FFMPEG实时解码网络视频流(回调方式)

来源:互联网 发布:南通农村商业银行软件 编辑:程序博客网 时间:2024/05/16 08:24

原文: http://blog.csdn.net/leixiaohua1020/article/details/12980423

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

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


1 0
原创粉丝点击