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

最近用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);
//int frame = 0;
// av_read_fram返回下一帧,发生错误或文件结束返回<0
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; // to check
                }
            }
            if (rgbBuffer != NULL)
            {
                *rgbBuffer = convertToRgb();
                if (size != NULL)
                {
                    *size = m_picWidth * m_picHeight * 3; // 上面指定了rgb24,所以是w*h*3
                }
            }
            //printf("frame fmt: %d\n", m_picture->format);


            if (width != NULL)
            {
                *width = m_picWidth;
            }
            if (height != NULL)
            {
                *height = m_picHeight;
            }
            //printf("bit_rate: %d width: %d height:%d\n", m_avctx->bit_rate, m_avctx->width, m_avctx->height);
            return 1;
        } // end of got picture
        else
        {
            m_skippedFrame++;
            //debug("skipped count: %d\n", m_skippedFrame);
        }
    } // end of video stream

    av_free_packet(&avpkt);
} // end of read frame

return 0;

}

int CH264Decoder::getSkippedFrame(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 (m_skippedFrame-- > 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();
        }
        //printf("frame fmt: %d\n", m_picture->format);
        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;
    } // end of got picture

    av_free_packet(&avpkt);
} // end of read frame

return 0;

}

李迟 2015.12.12 中午