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

上面的函数已经统计了缓存起来的帧总数,下面根据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和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 中午

Python中解码H264流通常需要使用`ffmpeg`库,因为`ffmpeg`是一个非常强大的多媒体处理工具,它可以处理几乎所有的视频和音频格式,包括H264。在Python中,我们可以通过`subprocess`模块来调用`ffmpeg`命令行工具,或者使用封装了`ffmpeg`的Python库,比如`python-ffmpeg`模块,来实现这一功能。 使用`python-ffmpeg`解码H264流,可以通过以下步骤进行: 1. 首先需要安装`ffmpeg`和`python-ffmpeg`模块。 2. 使用`python-ffmpeg`打开H264流。 3. 初始化解码器并设置相应的编解码参数。 4. 读取H264流的数据包,并将其送入解码器。 5. 解码数据包以获取解码后的。 6. 将解码后的数据进行处理,例如显示或保存。 下面是一个简单的示例代码框架,用于展示如何在Python中使用`python-ffmpeg`库解码H264流: ```python import ffmpeg # 打开H264流文件 input_stream = ffmpeg.input('input.h264') # 输出解码后的视频流 output_stream = input_stream.output('pipe:', format='rawvideo', pix_fmt='bgr24') # 执行解码过程 ffprobe = ffmpeg.probe('input.h264') stream = next((s for s in ffprobe['streams'] if s['codec_type'] == 'video'), None) width = int(stream['width']) height = int(stream['height']) ffmpeg.run(output_stream) # 读取解码后的数据 # 注意:这里需要根据实际情况读取数据,并进行相应的处理 ``` 这段代码仅作为一个框架示例,实际上还需要根据具体需求进行细致的处理,例如分配输出缓冲区、处理解码出错的情况等。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值