最近用ffmpeg解码H264裸码流文件,发现解码总是少几帧。上网查了些资料,解决了。
当使用avcodec_decode_video2时,如果第三个参数的值为1,则表示完成一帧的解码,如果为0,表示没有解码完成。此时需要计算未解码的帧数,以便再次调用avcodec_decode_video2函数。如下getFrame函数,当解码成功一帧时返回,如果没有解码,则累加。另外实现getSkippedFrame函数,将之前未解码的数据再次解码。
代码如下:
- int CH264Decoder::getFrame(unsigned char** yuvBuffer, unsigned char** rgbBuffer, int* size, int* width, int* height)
- {
- int got_picture = 0;
- int len = 0;
- AVPacket avpkt;
-
- av_init_packet(&avpkt);
-
-
- while (av_read_frame(m_fmtctx, &avpkt) >= 0)
- {
-
- if (avpkt.stream_index == m_videoStream)
- {
- len = avcodec_decode_video2(m_avctx, m_picture, &got_picture, &avpkt);
- if (len < 0)
- {
- debug("error while decoding frame.\n");
- return -1;
- }
- if (got_picture)
- {
- m_picWidth = m_avctx->width;
- m_picHeight = m_avctx->height;
-
- if (yuvBuffer != NULL)
- {
- *yuvBuffer = m_picture->data[0];
- if (size != NULL)
- {
- *size = len;
- }
- }
- if (rgbBuffer != NULL)
- {
- *rgbBuffer = convertToRgb();
- if (size != NULL)
- {
- *size = m_picWidth * m_picHeight * 3;
- }
- }
-
-
-
- if (width != NULL)
- {
- *width = m_picWidth;
- }
- if (height != NULL)
- {
- *height = m_picHeight;
- }
-
- return 1;
- }
- else
- {
- m_skippedFrame++;
-
- }
- }
-
- av_free_packet(&avpkt);
- }
-
- return 0;
- }
上面的函数已经统计了缓存起来的帧总数,下面根据m_skippedFrame的值再调用avcodec_decode_video2解码。
- int CH264Decoder::getSkippedFrame(unsigned char** yuvBuffer, unsigned char** rgbBuffer, int* size, int* width, int* height)
- {
- int got_picture = 0;
- int len = 0;
- AVPacket avpkt;
-
- memset(&avpkt, '\0', sizeof(AVPacket));
- av_init_packet(&avpkt);
-
-
- while (m_skippedFrame-- > 0)
- {
-
- avpkt.data = NULL;
- avpkt.size = 0;
-
- len = avcodec_decode_video2(m_avctx, m_picture, &got_picture, &avpkt);
- if (len < 0)
- {
- debug("error while decoding frame.\n");
- return -1;
- }
- if (got_picture)
- {
-
- if (yuvBuffer != NULL)
- {
- *yuvBuffer = m_picture->data[0];
- }
- if (rgbBuffer != NULL)
- {
- *rgbBuffer = convertToRgb();
- }
-
- if (size != NULL)
- {
- *size = len;
- }
- m_picWidth = m_avctx->width;
- m_picHeight = m_avctx->height;
- if (width != NULL)
- {
- *width = m_picWidth;
- }
- if (height != NULL)
- {
- *height = m_picHeight;
- }
-
- return 1;
- }
-
- av_packet_unref(&avpkt);
- }
-
- return 0;
- }
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 中午