您的位置:首页 > 其它

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

2016-11-20 23:07 281 查看
最近用ffmpeg解码H264裸码流文件,发现解码总是少几帧。上网查了些资料,解决了。

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

代码如下:

[cpp] view
plain copy

 print?

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;  

}  

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

[cpp] view
plain copy

 print?

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和size,否则无法解码  

        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();  

            }  

            //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;  

            }  

            //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  

  

        av_packet_unref(&avpkt);  

    } // end of read frame  

  

    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 中午

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐