解密FFmpeg播放状态控制内幕

版权声明:本文为博主原创文章,未经博主允许不得转载。

上一篇文章(http://my.oschina.NET/u/2336532/blog/400790)我们解决了在FFmpeg下如何处理H264和AAC的扩展数据,根据解出的NALU长度恢复了H264的起始码和AAC的ADTS头,这样一般来说播放是没有问题。本篇文章来谈谈如何实现基于FFmpeg的track mode控制,也就是如何用FFmpeg提供的功能来实现基本的seek、快进、快退。好了,废话少了,下面开始基于FFmpeg的track mode之旅。

FFmpeg提供了一个seek函数,原型如下:

int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,                   int flags);

参数说明:

s:操作上下文;

stream_index:基本流索引,表示当前的seek是针对哪个基本流,比如视频或者音频等等。

timestamp:要seek的时间点,以time_base或者AV_TIME_BASE为单位。

Flags:seek标志,可以设置为按字节,在按时间seek时取该点之前还是之后的关键帧,以及不按关键帧seek等,详细请参考FFmpeg的avformat.h说明。基于FFmpeg的所有track mode几乎都是用这个函数来直接或间接实现的。

  • 文件的seek功能实现

要转跳到视频100秒(100 000毫秒)处的第一个I帧(如果没有则向前找第一个):

av_seek_frame(pFormatCtx, vid_index, 100000*vid_time_scale/time_base, AVSEEK_FLAG_BACKWARD);

跳到音频80秒(80 000毫秒)处的帧(采样):

av_seek_frame(pFormatCtx, aud_index, 80000*aud_time_scale/ time_base, AVSEEK_FLAG_BACKWARD);

跳到文件开始的地方开始播放:

av_seek_frame(pFormatCtx, vid_index, 0, AVSEEK_FLAG_BACKWARD);

上面的time_scale、time_base都能通过流信息获取到,请参考前面的文章。有的文件不一定能seek成功,可以考虑在失败的情况下将AVSEEK_FLAG_BACKWARD改为AVSEEK_FLAG_ANY再次seek,不过seek到的视频帧可能不是I帧。

这个函数不管你当前在什么时间点上,都可以seek到任何合理位置。比如要实现在当前的基础上向后或向前跳转10秒,我们可以在av_read_frame函数拿到的包中含有当前时间戳的基础上增加或较少一个10000(换算成播放时间单位)再seek即可。所以这个函数可以用做进度的拖放、前进/后退一定时间、循环播放等功能。

  1. 快进快退

对于快进来说,一般解码器能实现2倍甚至再高倍速的播放,这种情况直接按照上一篇文章的基本播放流程就可以了。但对于4倍、8倍、16倍、32倍等高速播放,

一般不能像传统播放那样一帧一帧的送数据,不只是解码能力问题,数据读取也可能因为带宽不够跟不上,我们只能提取其中的I帧进行播放,将B帧和P帧丢掉。

快进时,通过当前数据包获得当前的时间PTS,将该PTS换算成时间再加上一小段时间,作为seek时间点向后找关键帧,此时flags可设置为AVSEEK_FLAG_FRAME。之后用av_read_frame获取到该关键帧。完成该帧解码显示后,再在该帧的PTS时间上增加一小段时间后seek,这样一直重复上述过程

快退时,通过当前数据包获得当前的时间PTS,将该PTS换算成时间再减去一小段时间,作为seek时间点向前找关键帧,此时flags可设置为AVSEEK_FLAG_BACKWARD。之后用av_read_frame获取到该关键帧。完成该帧解码显示后,再在该帧的PTS时间上减去一小段时间后seek,这样一直重复上述结果,

这样,我们通过以上对av_seek_frame函数的运用,即可完成文件playback的各种track mode实现,其实理解了这个函数后,你还会有很多其他办法来实现,这里只是提供了一种简单且占内存少的办法。在特定的情况下还可以先全部走一遍所有帧,并记录下全部的I帧的时间戳、帧编号、位置信息等需要的信息,然后直接从该表里面获取信息后进行seek和读取这些关键帧进行快速播放。

如需转载本文,请注明出处:http://my.oschina.net/u/2336532

谢谢合作!

时间: 2024-07-29 09:23:11

解密FFmpeg播放状态控制内幕的相关文章

解密FFmpeg播放track mode控制

上一篇文章(http://my.oschina.net/u/2336532/blog/400790)我们解决了在FFmpeg下如何处理H264和AAC的扩展数据,根据解出的NALU长度恢复了H264的起始码和AAC的ADTS头,这样一般来说播放是没有问题.本篇文章来谈谈如何实现基于FFmpeg的track mode控制,也就是如何用FFmpeg提供的功能来实现基本的seek.快进.快退.好了,废话少了,下面开始基于FFmpeg的track mode之旅. FFmpeg提供了一个seek函数,原型

apk支持html video控制 ,是播放状态就暂停,暂停状态就播放

apk支持html video控制 <!DOCTYPE html> <html> <body> <div style="text-align:center;"> <button onclick="playPause()">播放/暂停</button> <button onclick="makeBig()">大</button> <button o

FFMpeg的码率控制

mediaxyz是一位研究ffmpeg有三年的高人了,这几天一直在折腾ffmpeg中的x264,就是不知道该如何控制码率,主要是参数太多,也不知道该如何设置,在google上search了一下,这方面的介绍为0,那就找mediaxyz请教请教吧,这些可都是经验,非常宝贵! 以下是与mediaxyz在QQ上聊天的记录,只有一部分,因为QQ把之前的谈话删除了,但基本上精髓都可这里了. mediaxyz 23:40:26 你说的qsable是c->global_quality吧 Leon 23:40:

Java多线程(二)、线程的生命周期和状态控制(转)

Java多线程(二).线程的生命周期和状态控制 分类: javaSE综合知识点 2012-09-10 16:11 15937人阅读 评论(3) 收藏 举报 一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable). 注意:不能对已经启动的线程再次调用start()方法,否则会出现java.lang.IllegalThreadSt

mediaxyz访谈录:ffmpeg的码率控制

mediaxyz是一位研究ffmpeg有三年的高人了,这几天一直在折腾ffmpeg中的x264,就是不知道该如何控制码率,主要是参数太多,也不知道该如何设置,在 google上search了一下,这方面的介绍为0,那就找mediaxyz请教请教吧,这些可都是经验,非常宝贵! 以下是与mediaxyz在QQ上聊天的记录,只有一部分,因为QQ把之前的谈话删除了,但基本上精髓都可这里了. mediaxyz 23:40:26你说的qsable是c->global_quality吧 Leon 23:40:

Android多媒体播放之音乐播放状态和步骤

多媒体播放的状态图: 音乐播放状态:Idle--setDataSource()-->Initialized--prepare()-->Prepared--start()-->Started[-->stop()-->Stoped]--pause()-->Paused--start()-->Started... public class MainActivity extends Activity implements OnClickListener { private

WireMock之定义状态控制返回

WireMock之定义状态控制返回 定义操作习惯 可以定义一个场景,然后通过状态来控制每次请求返回的内容 基本语法,初始状态总是为Started XXX.inScenario("Scenario") .whenScenarioStateIs("Started") .willSetStateTo("NewStatus")); XXX.inScenario("Scenario") .whenScenarioStateIs(&quo

Win7 VS2015简单编译FFMPEG播放器FFPlay

Win平台简单编译FFPlay播放器,顺便纪念下雷霄骅,一年前刚学FFMPEG时还看过他的博客,昨晚再次搜FFMPEG文章时才知道人已经走了... 做成了视频放到B站 http://www.bilibili.com/video/av8644322/ C++版本也是可以编译的,只是要修改非常多的C语言转C++的强制类型转换,这个例子是C语言写的,改扩展名为cpp后,有不少cpp关键字的参数要修正,不过基本上花点时间就能解决,没什么难度. 现在主要问题是网上能找到的例子,包括FFPlay都是基于SD

关于C++ 输入输出流状态控制

关于这一点呢,是在做<C++primer >关联容器map的一道习题中发现这个蛋疼的问题的. 问题是这样的: 我想要将while循环条件设置为cin,这样就可以不断等待输入,普通的程序可以直接按下ctrl+z中止输入,麻烦在于这里有两层这样的while循环,而ctrl+z会一次性全部退出,导致无法实现想要的目的(在每个外层循环内部,可以输入有限量可中止的内层循环变量,也就是说第一次按下ctrl+z只停止内层的循环,等待进行下一次外层循环,然后再进入内层循环……) 1 #include<&