指导7:快进快退

处理快进快退命令

现在我们来为我们的播放器加入一些快进和快退的功能,因为如果你不能全局搜索一部电影是很让人讨厌的。同时,这将告诉你av_seek_frame函数是多么容易使用。

我们将在电影播放中使用左方向键和右方向键来表示向后和向前一小段,使用向上和向下键来表示向前和向后一大段。这里一小段是10秒,一大段是60秒。所以我们需要设置我们的主循环来捕捉键盘事件。然而当我们捕捉到键盘事件后我们不能直接调用av_seek_frame函数。我们要主要的解码线程 decode_thread的循环中做这些。所以,我们要添加一些变量到大结构体中,用来包含新的跳转位置和一些跳转标志:

int seek_req;

int seek_flags;

int64_t seek_pos;

现在让我们在主循环中捕捉按键:

for(;;) {

double incr, pos;

SDL_WaitEvent(&event);

switch(event.type) {

case SDL_KEYDOWN:

switch(event.key.keysym.sym) {

case SDLK_LEFT:

incr = -10.0;

goto do_seek;

case SDLK_RIGHT:

incr = 10.0;

goto do_seek;

case SDLK_UP:

incr = 60.0;

goto do_seek;

case SDLK_DOWN:

incr = -60.0;

goto do_seek;

do_seek:

if(global_video_state) {

pos = get_master_clock(global_video_state);

pos += incr;

stream_seek(global_video_state,

(int64_t)(pos * AV_TIME_BASE), incr);

}

break;

default:

break;

}

break;

为了检测按键,我们先查了一下是否有SDL_KEYDOWN事件。然后我们使用event.key.keysym.sym来判断哪个按键被按下。一旦我们知道了如何来跳转,我们就来计算新的时间,方法为把增加的时间值加到从函数get_master_clock中得到的时间值上。然后我们调用 stream_seek函数来设置seek_pos等变量。我们把新的时间转换成为avcodec中的内部时间戳单位。在流中调用那个时间戳将使用帧而不是用秒来计算,公式为seconds = frames * time_base(fps)。默认的avcodec值为1,000,000fps(所以2秒的内部时间戳为2,000,000)。在后面我们来看一下为什么要把这个值进行一下转换。

这就是我们的stream_seek函数。请注意我们设置了一个标志为后退服务:

void stream_seek(VideoState *is, int64_t pos, int rel) {

if(!is->seek_req) {

is->seek_pos = pos;

is->seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0;

is->seek_req = 1;

}

}

现在让我们看一下如果在decode_thread中实现跳转。你会注意到我们已经在源文件中标记了一个叫做”seek stuff goes here”的部分。现在我们将把代码写在这里。

跳转是围绕着av_seek_frame函数的。这个函数用到了一个格式上下文,一个流,一个时间戳和一组标记来作为它的参数。这个函数将会跳转到你所给的时间戳的位置。时间戳的单位是你传递给函数的流的时基time_base。然而,你并不是必需要传给它一个流(流可以用-1来代替)。如果你这样做了,时基time_base将会是avcodec中的内部时间戳单位,或者是1000000fps。这就是为什么我们在设置seek_pos的时候会把位置乘以AV_TIME_BASER的原因。

但是,如果给av_seek_frame函数的stream参数传递传-1,你有时会在播放某些文件的时候遇到问题(比较少见),所以我们会取文件中的第一个流并且把它传递到av_seek_frame函数。不要忘记我们也要把时间戳timestamp的单位进行转化。

if(is->seek_req) {

int stream_index= -1;

int64_t seek_target = is->seek_pos;

if (is->videoStream >= 0) stream_index = is->videoStream;

else if(is->audioStream >= 0) stream_index = is->audioStream;

if(stream_index>=0){

seek_target= av_rescale_q(seek_target, AV_TIME_BASE_Q,

pFormatCtx->streams[stream_index]->time_base);

}

if(av_seek_frame(is->pFormatCtx, stream_index,

seek_target, is->seek_flags) < 0) {

fprintf(stderr, “%s: error while seeking\n”,

is->pFormatCtx->filename);

} else {

这里av_rescale_q(a,b,c)是用来把时间戳从一个时基调整到另外一个时基时候用的函数。它基本的动作是计算a*b/c,但是这个函数还是必需的,因为直接计算会有溢出的情况发生。AV_TIME_BASE_Q是AV_TIME_BASE作为分母后的版本。它们是很不相同的:AV_TIME_BASE * time_in_seconds = avcodec_timestamp而AV_TIME_BASE_Q * avcodec_timestamp = time_in_seconds(注意AV_TIME_BASE_Q实际上是一个AVRational对象,所以你必需使用avcodec中特定的q函数来处理它)。

清空我们的缓冲

我们已经正确设定了跳转位置,但是我们还没有结束。记住我们有一个堆放了很多包的队列。既然我们跳到了不同的位置,我们必需把队列中的内容清空否则电影是不会跳转的。不仅如此,avcodec也有它自己的内部缓冲,也需要每次被清空。

要实现这个,我们需要首先写一个函数来清空我们的包队列。然后我们需要一种命令声音和视频线程来清空avcodec内部缓冲的办法。我们可以在清空队列后把特定的包放入到队列中,然后当它们检测到特定的包的时候,它们就会把自己的内部缓冲清空。

让我们开始写清空函数。其实很简单的,所以我直接把代码写在下面:

static void packet_queue_flush(PacketQueue *q) {

AVPacketList *pkt, *pkt1;

SDL_LockMutex(q->mutex);

for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {

pkt1 = pkt->next;

av_free_packet(&pkt->pkt);

av_freep(&pkt);

}

q->last_pkt = NULL;

q->first_pkt = NULL;

q->nb_packets = 0;

q->size = 0;

SDL_UnlockMutex(q->mutex);

}

既然队列已经清空了,我们放入”清空包”。但是开始我们要定义和创建这个包:

AVPacket flush_pkt;

main() {

av_init_packet(&flush_pkt);

flush_pkt.data = “FLUSH”;

}

现在我们把这个包放到队列中:

} else {

if(is->audioStream >= 0) {

packet_queue_flush(&is->audioq);

packet_queue_put(&is->audioq, &flush_pkt);

}

if(is->videoStream >= 0) {

packet_queue_flush(&is->videoq);

packet_queue_put(&is->videoq, &flush_pkt);

}

}

is->seek_req = 0;

}

(这些代码片段是接着前面decode_thread中的代码片段的)我们也需要修改packet_queue_put函数才不至于直接简单复制了这个包:

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

AVPacketList *pkt1;

if(pkt != &flush_pkt && av_dup_packet(pkt) < 0) {

return -1;

}

然后在声音线程和视频线程中,我们在packet_queue_get后立即调用函数avcodec_flush_buffers:

if(packet_queue_get(&is->audioq, pkt, 1) < 0) {

return -1;

}

if(packet->data == flush_pkt.data) {

avcodec_flush_buffers(is->audio_st->codec);

continue;

}

上面的代码片段与视频线程中的一样,只要把”audio”换成”video”。

就这样,让我们编译我们的播放器:

gcc -o tutorial07 tutorial07.c -lavutil -lavformat -lavcodec -lz -lm`sdl-config –cflags –libs`

试一下!我们几乎已经都做完了;下次我们只要做一点小的改动就好了,那就是检测ffmpeg提供的小的软件缩放采样。

ffmpeg文档7:快进快退相关推荐

  1. ffmpeg播放器快进快退(七)

    指导7:快进快退 处理快进快退命令 现在我们来为我们的播放器加入一些快进和快退的功能,因为如果你不能全局搜索一部电影是很让人讨厌的.同时,这将告诉你av_seek_frame函数是多么容易使用. 我们 ...

  2. Android FFmpeg系列——7 实现快进/快退功能

    Android FFmpeg系列--0 编译.so库 Android FFmpeg系列--1 播放视频 Android FFmpeg系列--2 播放音频 Android FFmpeg系列--3 C多线 ...

  3. 十、FFmpeg视频播放之快进快退

    1.处理快进快退(seek)命令 本章我将给大家讲解怎么给我们的播放器添加快进.快退.定位功能,这也是几乎所有播放器都有的功能.为实现此功能,我们要用到av_seek_frame函数,这个函数非常简单 ...

  4. 如何实现视频的快进快退功能(整理)

    最近在研究视频的播放的快进快退功能,先把相关的调研结果整理一下,做个记录. 裸的H264码流,如果实现快进快退必须基于 I 帧才能实现:在播放前对整个码流进行统计,总共有多少帧,所有的 I 帧在什么位 ...

  5. vue项目视频实现键盘快进快退,音量调大小及监听播放事件

    直接上代码 <div style="padding-top:56.25%" ><video style="width:100%;height:672px ...

  6. Silverlight 5 beta新特性探索系列:9.视频快进快退和TextSearch对象对文字项查询

    本节讲诉两个新特性:一.在Silverlight 5中可以控制MediaElement对象播放的视频进行快进快退控制.二.在Silverlight 5中的文字项进行搜索查询. 一.对于MediaEle ...

  7. python 循环播放音乐_python gstreamer实现视频快进/快退/循环播放功能

    这篇文章主要介绍了python gstreamer 实现视频快进/快退/循环播放功能,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下 Gstreamer到底是个啥? ...

  8. 安卓平板倍速_推荐:安卓上本地音频播放器,可实现5秒快进快退和倍速播放...

    前一篇文章推荐过一个app,但那app没倍速播放功能.我又找到了一个更完美的app 安卓手机上我自己需要这样一款app,主要用来听电子书,知识音频等.需要能实现快进快退几秒钟的以及倍速播放功能.这功能 ...

  9. Java后端处理video快进快退播放以及断点续传的原理和代码

    video 快进快退的原理: 通过对所在服务器上的流媒体进行skip操作,然后再response的header里设置相应的Content-Range以及其他属性,来控制视频流的快进快退的功能. 断点续 ...

  10. ts, mp4文件快进快退(seek)原理

    最近用potplayer播放一些ts文件,seek(快进快退)发现会有卡顿问题,但是同一个文件用mp4转封装之后seek就很快很流畅了.所以抽空研究了ffplay 对mp4文件和ts文件的 seek ...

最新文章

  1. java long 对应mybati类型_修改 mybatis-generator 中数据库类型和 Java 类型的映射关系...
  2. 全球及中国微生物气溶胶采样器行业十四五”发展规划及运营前景研究报告2021年版
  3. 华为云部署html网页,手把手教你如何在华为云服务器上部署一个自己的弹幕网站!...
  4. 【Linux系统编程应用】 Linux系统中找不到设备/dev/fb0
  5. 对现有的所能找到的DDOS代码(攻击模块)做出一次分析----UDP篇
  6. 2021母婴行业洞察报告
  7. 安卓项目之微信公众好---初体验
  8. 如果云是水滴,Kubernetes就是水滴管理平台
  9. 图解TCPIP-以太网(物理层)
  10. 优化技巧:提前if判断帮助CPU分支预测
  11. python安装pygame教程_pygame 安装教程
  12. echar 数据显示在小圆点里
  13. 在平面国生活,会是怎样的体验?
  14. 手机上最好用的五笔输入法_远程输入法,用电脑键盘给手机打字,省蓝牙键盘钱了...
  15. 制造上云 佛山南海携手阿里云建创新中心
  16. 好强的谷歌插件,不用写代码就能爬虫!
  17. 区块链技术在司法行业的服务应用
  18. python闯关2-罗马数字编码
  19. 如何解决百度云下载慢的问题
  20. 乡村振兴战略下传统村落文化旅游设计 | 年度图书,踔厉奋发,勇毅前行

热门文章

  1. Google两步验证的工作原理
  2. Ubuntu 18.04 安装微信
  3. IOS 多个UIImageView 加载高清大图时内存管理
  4. backports移植rtlwifi驱动
  5. 关于远程访问tomcat问题的总结
  6. “一夜成名”需要多久?他花了20年!
  7. 数据迁移其实是很难的
  8. 【不积跬步,无以致千里】五个常用的Linux监控脚本代码
  9. 从哪儿摔倒,从哪儿爬起
  10. python 局部变量和全局变量 global