FFMPEG實時解碼網絡視頻流(回調方式)


在上一篇FFMPEG實時解碼網絡視頻流中使用av_parser_parse2來組合數據包,判斷是否已經得到一幀數據,但如果多媒體流中混合音頻和視頻,這種方法似乎走不通。

下面使用另一種方法實現,先初始化:


int CTcpH264Dlg::InitDecode()
{
    av_register_all();
    av_init_packet(&m_avpkt);
 
    m_pFmtCtx = avformat_alloc_context();
    if(m_pFmtCtx == NULL){
        TRACE("avformat_alloc_context failed!\n");
        return -1;
    }
 
    m_pIOBuf = (unsigned char*)av_malloc(32768);
    if(m_pIOBuf == NULL){
        TRACE("av_malloc failed!\n");
        return -1;
    }
 
    m_pIOCtx = avio_alloc_context(m_pIOBuf, 32768, 0, this, ReadNetPacket, NULL, NULL);
    m_pFmtCtx->pb = m_pIOCtx;
 
    m_codec = avcodec_find_decoder(CODEC_ID_H264);
    if(!m_codec){
        TRACE(_T("Codec not found\n"));
        return -1;
    }
    m_pCodecCtx = avcodec_alloc_context3(m_codec);
    if(!m_pCodecCtx){
        TRACE(_T("Could not allocate video codec context\n"));
        return -1;
    }
 
    m_pCodecParserCtx=av_parser_init(AV_CODEC_ID_H264);
    if (!m_pCodecParserCtx){
        TRACE(_T("Could not allocate video parser context\n"));
        return -1;
    }
 
    if(m_codec->capabilities&CODEC_CAP_TRUNCATED)
        m_pCodecCtx->flags|= CODEC_FLAG_TRUNCATED; 
 
    if (avcodec_open2(m_pCodecCtx, m_codec, NULL) < 0) {
        TRACE(_T("Could not open codec\n"));
        return -1;
    }
 
    m_picture = av_frame_alloc();
    m_pFrameRGB = av_frame_alloc();
    if(!m_picture || !m_pFrameRGB){
        TRACE(_T("Could not allocate video frame\n"));
        return -1;
    }
 
    m_PicBytes = 0;
    m_PicBuf = NULL;
    m_pImgCtx = NULL;
 
    return 0;
}


讀取網絡數據的回調函數:

int CTcpH264Dlg::ReadNetPacket(void *opaque, uint8_t *buf, int buf_size)
{
    CTcpH264Dlg* pDlg = (CTcpH264Dlg*)opaque;
    return pDlg->_ReadNetPacket(buf, buf_size);
}
 
int CTcpH264Dlg::_ReadNetPacket(uint8_t *buf, int buf_size)
{
    if(m_sock == INVALID_SOCKET){
        return 0;
    }
 
    int ret = recv(m_sock, (char*)buf, buf_size, 0);
    if(ret == 0){
        closesocket(m_sock);
        m_sock = INVALID_SOCKET;
        return 0;
    }
 
    TRACE(_T("Read network packet len=%d\n"), ret);
 
    return ret;
}
分離音頻/視頻流:
void CTcpH264Dlg::DemuxerWorker()
{
    int ret, i;
    int vid_idx, aud_idx;
    AVPacket pkt;
 
    if((ret = avformat_open_input(&m_pFmtCtx, "", 0, 0)) < 0){
        TRACE(_T("Could not open input file!\n"));
        return ;
    }
 
    if((ret = avformat_find_stream_info(m_pFmtCtx, 0)) < 0){
        TRACE(_T("Failed to retrieve input stream information"));
        return ;
    }
 
    for(i=0; i<m_pFmtCtx->nb_streams; i++){
        if(m_pFmtCtx->streams[i]->codec->codec_type    == AVMEDIA_TYPE_VIDEO){
            vid_idx = i;
        }else if(m_pFmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
            aud_idx = i;
        }
    }
 
    while(av_read_frame(m_pFmtCtx, &pkt) >= 0)
    {
        if(pkt.stream_index == vid_idx)
        {
            TRACE(_T("Get a video frame!\n"));
            DecodeVideoFrame(&pkt);
        }
        else if(pkt.stream_index == aud_idx)
        {
            TRACE(_T("Get a audio frame!\n"));
        }
 
        av_free_packet(&pkt);
    }
 
    avformat_close_input(&m_pFmtCtx);
}

解碼視頻幀:
int CTcpH264Dlg::DecodeVideoFrame(AVPacket* pPkt)
{
    int len, got;
    len = avcodec_decode_video2(m_pCodecCtx, m_picture, &got, pPkt);
    if(len < 0){
        TRACE(_T("Error while decoding video frame\n"));
        return -1;
    }
 
    if(got){
        if(m_PicBytes == 0){
            m_PicBytes = avpicture_get_size(PIX_FMT_BGR24, m_pCodecCtx->width, m_pCodecCtx->height);
            m_PicBuf = new uint8_t[m_PicBytes];
            avpicture_fill((AVPicture *)m_pFrameRGB, m_PicBuf, PIX_FMT_BGR24,
                m_pCodecCtx->width, m_pCodecCtx->height);
        }
 
        if(!m_pImgCtx){
            m_pImgCtx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt, m_pCodecCtx->width, m_pCodecCtx->height, PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);
        }
 
        m_picture->data[0] += m_picture->linesize[0]*(m_pCodecCtx->height-1);
        m_picture->linesize[0] *= -1;                      
        m_picture->data[1] += m_picture->linesize[1]*(m_pCodecCtx->height/2-1);
        m_picture->linesize[1] *= -1;
        m_picture->data[2] += m_picture->linesize[2]*(m_pCodecCtx->height/2-1);
        m_picture->linesize[2] *= -1;
        sws_scale(m_pImgCtx, (const uint8_t* const*)m_picture->data, m_picture->linesize, 0, m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize); 
 
        DisplayPicture(m_pFrameRGB->data[0], m_pCodecCtx->width, m_pCodecCtx->height);
    }
 
    return 0;
}
視頻幀圖片顯示:
void CTcpH264Dlg::DisplayPicture(uint8_t* data, int width, int height)
{
    //TRACE(_T("Display a picture\n"));
    CRect rc;
    CWnd* PlayWnd = GetDlgItem(IDC_PLAYER);
    HDC hdc = PlayWnd->GetDC()->GetSafeHdc();
    GetClientRect(&rc);
 
    init_bm_head(width, height);
 
    DrawDibDraw(m_DrawDib,
        hdc,
        rc.left,
        rc.top,
        -1,            // don't stretch
        -1,
        &m_bm_info.bmiHeader, 
        (void*)data, 
        0, 
        0, 
        width, 
        height, 
        0);
}


總結:
這種方法的重點在於使用avformat_alloc_context創建一個自定義的Format Context,再使用avio_alloc_context為這個Format Context創建一個IO Context,在這IO Context中指定IO數據的回調函數,這樣我們就可以在回調函數讀取任何我們想送給解碼的數據了。

具體還是要看源碼啊!源碼表達得最清楚,哈哈!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM