FFMPEG实时解码网络视频流(回调方式)
2017-05-05 23:56
429 查看
原文: 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;
}
读取网络数据的回调函数:
解码视频帧:
总结:
这种方法的重点在于使用avformat_alloc_context创建一个自定义的Format Context,再使用avio_alloc_context为这个Format Context创建一个IO Context,在这IO Context中指定IO数据的回调函数,这样我们就可以在回调函数读取任何我们想送给解码的数据了。
具体还是要看源码啊!源码表达得最清楚,哈哈!
在上一篇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数据的回调函数,这样我们就可以在回调函数读取任何我们想送给解码的数据了。
具体还是要看源码啊!源码表达得最清楚,哈哈!
相关文章推荐
- 使用 ffmpeg 进行网络推流:拉流->解封装->解码->处理原始数据(音频、视频)->编码->编码->推流
- 使用ffmpeg-1.0内置RTMP协议实时解码H264视频流
- FFMPEG 实时解码网络H264码流,RTP封装
- ffmpeg系列之两种视频解码方式
- 移动视频监控(2)---原型开发---(音视频编解码多平台移植(for window/wince))ffmpeg --自由之路即是曲折之路。
- (转)利用VC++实现局域网实时视频传输(网络视频)
- 利用ffmpeg来进行视频解码的完整示例代码
- h.264 视频解码的一点小经验(ffmpeg)
- h.264 视频解码的一点小经验(ffmpeg)
- ffmpeg h264解码, 屏蔽因为网络丢包等各种原因导致的花屏帧
- 网络游戏与实时视频结合,拉动网游的进步
- 利用ffmpeg来进行视频解码的完整示例代码(H.264)
- FFmpeg 音视频编解码开源库
- Silverlight多人网络实时视频会议系统
- Silverlight多人网络实时视频会议系统
- ffmpeg对视频文件进行解码的大致流程
- FFMpeg对视频文件进行解码的大致流程
- [FFmpeg]疑问:QT上用的视频播放解码是基于FFmpeg的吗?
- ffmpeg对视频文件进行解码的大致流程
- 使用ffmpeg进行视频解码以及图像转换