关于ffmpeg(libav)解码视频最后丢帧的问题

其实最初不是为了解决这个问题而来的,是Peter兄给我的提示解决另一个问题却让我误打误撞解决了另外一个问题之后也把这个隐藏了很久的bug找到(之前总是有一些特别短的视频产生不知所措还以为是视频素材本身有问题呢),今天真是收获颇丰啊,对libav的理解更深。

一直以来我的程序架构是让读一帧av_read_frame,然后去尝试根据packet的type类型分别去decode video或者audio,然而这样总是在视频的结尾会有许多帧丢失的问题,我找过avplay代码中似乎没找到我想象中的那种读packet完毕后特殊处理的代码。

一下log是我设置将其avcodec_decode_video2函数调用前后输出的pts、dts值(注:Frame:为decode之后,测试视频为纯视频无音频的文件),从图中可以看出来,开头为了顺序正确,libav暂存了9个packet,读packet完毕后,我当然不能就直接不管了,还是要将libav暂存的那些帧取出来才行。


    综上几点我认为是在调用avcodec_decode_video2函数的时候,函数内会去发现这个packet尚不足以解码下一帧的时候就会暂存packet在内部队列中(我自己给他们取的名字,望能理解),这样问题就来了,等到整个视频文件都读取完了,剩下libav内部存的packet还有一大堆没处理,我的架构又是要求函数直接向视频索取一帧,因此就需要在读新packet完毕后还 单独调用decode video函数或者decode audio函数来取出剩余的AVFrame。

为了这些bug我破例无耻的使用了goto语句:

首先定义了类成员变量,初始化都为false:

bool            _no_packet;   //代表是否还有packet
bool            _end_video_frame;   //代表是否video中的队列取出完毕
bool            _end_audio_frame;   //代表是否audio中的队列取出完毕
AVPacket packet = {0};
while(true)
{
        auto auto_releaser = std::shared_ptr<AVPacket>(&packet, [](AVPacket* p) { av_free_packet(p); });
        int ret = av_read_frame(_context.get(), &packet);
        if ( ret != 0)
        {
            _no_packet = true;
            if(!_end_video_frame)
                goto loop_end_video;
            if(!_end_audio_frame)
                goto loop_end_audio;
            if(_end_video_frame && _end_audio_frame)
                return false;
        }
        if(packet.stream_index == vstream_index)
        {
loop_end_video:
            int frame_finished = true;
            if(avcodec_decode_video2(_vcodec, _decoded_frame, &frame_finished, &packet) < 0)
             {
// 。。。。do something。。。
             }
             if(frame_finished)
             {
//。。。。do something。。。
             }
             else
             {
                if(_no_packet)
                    _end_video_frame = true;
            }
       }
       else if (packet.stream_index == astream_index && (type & MEDIA_AUDIO) != 0)
        {
            loop_end_audio:
            int frame_finished;
            if (avcodec_decode_audio4(_acodec, _decoded_frame, &frame_finished, &packet) < 0)
             {
// 。。。。do something。。。
             }
             if(frame_finished)
             {
//。。。。do something。。。
             }
             else
             {
                if(_no_packet)
                    _end_audio_frame = true;
            }
       }
}

有了这些跳转,就能完美的在read_frame完之后还继续decode为我所用。希望能帮助到与我遇到了相同问题的人。

再次感谢Peter~

时间: 2024-08-28 20:48:51

关于ffmpeg(libav)解码视频最后丢帧的问题的相关文章

【学习ffmpeg】打开视频文件,帧分析,并bmp保存关键帧

http://www.tuicool.com/articles/jiUzua http://blog.csdn.net/code_future/article/details/8646717 主题 FFmpeg 任务:需要把一个视频文件的每一帧提取出来,每帧存储成单独的文件. 以前用Matlab处理过这个问题,可是感觉比较慢,而且最近正在逐步转向使用开源的东西.因此搜到ffmpeg这个好东西. ffmpeg可用来处理视频文件的提取和各种转换,跨平台,官网上有Linux,WINDOWS和MAC版本

Android 音视频深入 九 FFmpeg解码视频生成yuv文件(附源码下载)

项目地址,求star https://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg(MP4%E8%BD%ACyuv%EF%BC%89 这一次是将MP4解码出yuv文件出来,先介绍一波yuv文件 YUV是指亮度参量和色度参量分开表示的像素格式,而这样分开的好处就是不但可以避免相互干扰,还可以降低色度的采样率而不会对图像质量影响太大.YUV是一个比较笼统地说法,针对它的具体排列方式,可以分为很多种

视频丢帧(详解)

1.丢帧的出现 说起视频播放器大家都很熟悉了,覆盖各种平台,使用简单操作方面,但是视频播放器里面的原理却非常的复杂,牵扯到很多方面的知识点.今天我们来探讨一下当视频解码和渲染的总时间大于了视频指定的时间时,就会出现声音比画面快的情况,单个画面延后的时间在人眼不能察觉的范围内还是能接受的,但是如此累计起来就会造成这个延迟的加大,导致后面声话完全不同步,这是不能接受的,那么为了解决这种问题,视频“丢帧”就出现了. 2.视频播放原理 我们看到的视频其实就是一幅一幅的图片组成的,就和电影一样的原理,在很

ffmpeg 分析一个视频的基本结构组成_流_包_帧

分析视频流 stream[0] duration = 2019840 // 视频的时长时间戳,计算秒 2019840/12800 index = 0 // 流索引,这是一个视频流 nb_frames // 视频的总帧数 avg_frame_rate // 视频的帧数, num/den就是帧数 timebase = {1, 12800} // 视频流的时间基 first_dts = -1024 // 第一帧的dts cur_dts = 当前帧的dts codecpar codec_type = A

(转载)[FFmpeg]使用ffmpeg从各种视频文件中直接截取视频图片

你曾想过从一个视频文件中提取图片吗?在Linux下就可以,在这个教程中我将使用ffmpeg来从视频中获取图片. 什么是ffmpeg?What is ffmpeg? ffmpeg是一个非常有用的命令行程序,它可以用来转码媒体文件.它是领先的多媒体框架FFmpeg的一部分,其有很多功能,比如解码.编码.转码.混流.分离.转化为流.过滤以及播放几乎所有的由人和机器创建的媒体文件. 在这个框架中包含有各种工具,每一个用于完成特定的功能.例如,ffserver能够将多媒体文件转化为用于实时广播的流,ffp

(转)FFMPEG:H264解码-SDL显示(RGB32、RGB24、YUV420P、YUV422)

FFMpeg对视频文件进行解码的大致流程 1. 注册所有容器格式: av_register_all()2. 打开文件: av_open_input_file()3. 从文件中提取流信息: av_find_stream_info()4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO5. 查找对应的解码器: avcodec_find_decoder()6. 打开编解码器: avcodec_open()7. 为解码帧分配内存: avcodec_alloc_frame()8. 不停地从码

ffmpeg编解码详细过程(转载)

1. 注册所有容器格式和CODEC:av_register_all() 2. 打开文件:av_open_input_file() 3. 从文件中提取流信息:av_find_stream_info() 4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO 5. 查找对应的解码器:avcodec_find_decoder() 6. 打开编解码器:avcodec_open() 7. 为解码帧分配内存:avcodec_alloc_frame() 8. 不停地从码流中提取出帧数据:av_re

C#:获取视频某一帧的缩略图

读取方式:使用ffmpeg读取,所以需要先下载ffmpeg.网上资源有很多. 原理是通过ffmpeg执行一条命令获取视频某一帧的缩略图. 首先,需要获取视频的帧高度和帧宽度,这样获取的缩略图才不会变形. 获取视频的帧高度和帧宽度可以http://www.zhoumy.cn/?p=35通过查看. 获取到视频的帧高度和帧宽度后,还需要获取缩略图的高度和宽度,这是按比例缩放的. 比如你存放缩略图的区域的最大宽度和最大高度是100,缩略图的高度也应缩放到对应大小. 接下来通过执行一条命令就可以获取到视频

使用FFmpeg生成HLS视频

准备 1.  准备高质量音视频源文件. 2. 编译安装必要的软件 a. ffmpeg  b. nginx 3. 测试环境配置 修改nginx中的nginx.conf配置文件,当前以阿里云预装的CentOS为例: 修改命令:                vim /etc/nginx/conf.d/default.conf 直接编辑该文件,加入如下配置: #音频路径                location /audios{                    alias /share/w