ffmpeg解码H264缺少帧的解决办法

最近用ffmpeg解码H264裸码流文件,发现解码总是少几帧。上网查了些资料,解决了。

当使用avcodec_decode_video2时,如果第三个参数的值为1,则表示完成一帧的解码,如果为0,表示没有解码完成。此时需要计算未解码的帧数,以便再次调用avcodec_decode_video2函数。如下getFrame函数,当解码成功一帧时返回,如果没有解码,则累加。另外实现getSkippedFrame函数,将之前未解码的数据再次解码。

代码如下:

[cpp]  view plain  copy
 print ?
  1. int CH264Decoder::getFrame(unsigned char** yuvBuffer, unsigned char** rgbBuffer, int* size, int* width, int* height)  
  2. {  
  3.     int got_picture = 0;    // 找到帧标志  
  4.     int len = 0;  
  5.     AVPacket avpkt;  
  6.   
  7.     av_init_packet(&avpkt);  
  8.     //int frame = 0;  
  9.     // av_read_fram返回下一帧,发生错误或文件结束返回<0  
  10.     while (av_read_frame(m_fmtctx, &avpkt) >= 0)  
  11.     {  
  12.         // 解码视频流  
  13.         if (avpkt.stream_index == m_videoStream)  
  14.         {  
  15.             len = avcodec_decode_video2(m_avctx, m_picture, &got_picture, &avpkt);  
  16.             if (len < 0)  
  17.             {  
  18.                 debug("error while decoding frame.\n");  
  19.                 return -1;  
  20.             }  
  21.             if (got_picture)  
  22.             {  
  23.                 m_picWidth  = m_avctx->width;  
  24.                 m_picHeight = m_avctx->height;  
  25.                 // 传出原始数据指针,由于内部已经申请了,不用再开辟数据  
  26.                 if (yuvBuffer != NULL)  
  27.                 {  
  28.                     *yuvBuffer = m_picture->data[0];  
  29.                     if (size != NULL)  
  30.                     {  
  31.                         *size = len; // to check  
  32.                     }  
  33.                 }  
  34.                 if (rgbBuffer != NULL)  
  35.                 {  
  36.                     *rgbBuffer = convertToRgb();  
  37.                     if (size != NULL)  
  38.                     {  
  39.                         *size = m_picWidth * m_picHeight * 3; // 上面指定了rgb24,所以是w*h*3  
  40.                     }  
  41.                 }  
  42.                 //printf("frame fmt: %d\n", m_picture->format);  
  43.   
  44.   
  45.                 if (width != NULL)  
  46.                 {  
  47.                     *width = m_picWidth;  
  48.                 }  
  49.                 if (height != NULL)  
  50.                 {  
  51.                     *height = m_picHeight;  
  52.                 }  
  53.                 //printf("bit_rate: %d width: %d height:%d\n", m_avctx->bit_rate, m_avctx->width, m_avctx->height);  
  54.                 return 1;  
  55.             } // end of got picture  
  56.             else  
  57.             {  
  58.                 m_skippedFrame++;  
  59.                 //debug("skipped count: %d\n", m_skippedFrame);  
  60.             }  
  61.         } // end of video stream  
  62.   
  63.         av_free_packet(&avpkt);  
  64.     } // end of read frame  
  65.   
  66.     return 0;  
  67. }  

上面的函数已经统计了缓存起来的帧总数,下面根据m_skippedFrame的值再调用avcodec_decode_video2解码。

[cpp]  view plain  copy
 print ?
  1. int CH264Decoder::getSkippedFrame(unsigned char** yuvBuffer, unsigned char** rgbBuffer, int* size, int* width, int* height)  
  2. {  
  3.     int got_picture = 0;    // 找到帧标志  
  4.     int len = 0;  
  5.     AVPacket avpkt;  
  6.   
  7.     memset(&avpkt, '\0'sizeof(AVPacket));  
  8.     av_init_packet(&avpkt);  
  9.   
  10.     // 是否还有缓存的帧  
  11.     while (m_skippedFrame-- > 0)  
  12.     {  
  13.         // 注:avpkt要清空data和size,否则无法解码  
  14.         avpkt.data = NULL;  
  15.         avpkt.size = 0;  
  16.         // 解码视频流  
  17.         len = avcodec_decode_video2(m_avctx, m_picture, &got_picture, &avpkt);  
  18.         if (len < 0)  
  19.         {  
  20.             debug("error while decoding frame.\n");  
  21.             return -1;  
  22.         }  
  23.         if (got_picture)  
  24.         {  
  25.             // 传出原始数据指针,由于内部已经申请了,不用再开辟数据  
  26.             if (yuvBuffer != NULL)  
  27.             {  
  28.                 *yuvBuffer = m_picture->data[0];  
  29.             }  
  30.             if (rgbBuffer != NULL)  
  31.             {  
  32.                 *rgbBuffer = convertToRgb();  
  33.             }  
  34.             //printf("frame fmt: %d\n", m_picture->format);  
  35.             if (size != NULL)  
  36.             {  
  37.                 *size = len;  
  38.             }  
  39.             m_picWidth  = m_avctx->width;  
  40.             m_picHeight = m_avctx->height;  
  41.             if (width != NULL)  
  42.             {  
  43.                 *width = m_picWidth;  
  44.             }  
  45.             if (height != NULL)  
  46.             {  
  47.                 *height = m_picHeight;  
  48.             }  
  49.             //printf("bit_rate: %d width: %d height:%d\n", m_avctx->bit_rate, m_avctx->width, m_avctx->height);  
  50.             return 1;  
  51.         } // end of got picture  
  52.   
  53.         av_packet_unref(&avpkt);  
  54.     } // end of read frame  
  55.   
  56.     return 0;  
  57. }  

2016.4.23 周日的补充:

注意,在调用avcodec_decode_video2对缓存的帧解码时,一定要将avpkt.data置为NULL,并将avpkt.size置为0,否则会解码不成功。在调用av_init_packet前,也要手工对AVPacket结构体进行清零操作。直到最后在VS环境使用该类时才发现这个问题。在解码函数avcodec_decode_video2注释中有如下说明:

 * @note Codecs which have the CODEC_CAP_DELAY capability set have a delay
 * between input and output, these need to be fed with avpkt->data=NULL,
 * avpkt->size=0 at the end to return the remaining frames.



李迟 2015.12.12 中午

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用FFmpeg解码H.264视频,你可以使用以下步骤: 1. 首先,需要初始化FFmpeg库。你可以使用`av_register_all()`函数注册所有的FFmpeg组件。 2. 打开输入文件并创建AVFormatContext对象,表示输入的媒体文件。可以使用`avformat_open_input()`函数打开视频文件。 3. 检索流信息,使用`avformat_find_stream_info()`函数获取流信息。 4. 获取视频流的索引。可以使用`av_find_best_stream()`函数或者遍历`AVFormatContext`中的`streams`数组,判断其中的流类型是否为视频流(`AVMEDIA_TYPE_VIDEO`)来确定视频流的索引。 5. 查找并打开解码器。可以使用`avcodec_find_decoder()`函数根据视频流的编码ID查找解码器,然后使用`avcodec_open2()`函数打开解码器。 6. 分配AVPacket对象和AVFrame对象。AVPacket用于存储解码前的压缩数据,AVFrame用于存储解码后的像素数据。 7. 循环读取压缩数据并进行解码。使用`av_read_frame()`函数读取一压缩数据(AVPacket),然后使用`avcodec_send_packet()`函数将AVPacket发送到解码器进行解码,最后使用`avcodec_receive_frame()`函数接收解码后的数据(AVFrame)。 8. 处理解码后的数据。可以将解码后的数据用于渲染、处理等操作。 9. 释放资源。最后,记得释放分配的内存,包括AVPacket对象、AVFrame对象、解码器上下文和AVFormatContext对象等。 这只是一个基本的流程示例,你可以根据实际需求进行调整和扩展。请注意,H.264视频可能使用不同的配置参数和包装方式,你可能需要根据具体情况进行解码器的配置和设置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值