• 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数据的回调函数,这样我们就可以在回调函数读取任何我们想送给解码的数据了。

    具体还是要看源码啊!源码表达得最清楚,哈哈!

  • 相关阅读:
    电脑性能提升三
    电脑实用小技巧
    自定义windows开机声音
    电脑性能提升一
    rpm和yum软件管理
    Linux网络技术管理及进程管理
    RAID磁盘阵列及CentOS7系统启动流程
    Linux磁盘管理及LVM讲解
    Linux计划任务及压缩归档
    Android Studio 常用快捷键及常用设置
  • 原文地址:https://www.cnblogs.com/lidabo/p/15684660.html
Copyright © 2020-2023  润新知