新手学习FFmpeg - 调用API调整视频局部速率
通过修改setpts代码实现调整视频部分的播放速率。 完整代码可参考: https://andy-zhangtao.github.io/ffmpeg-examples/
在前面提到了PTS/DTS/Timestamp的关系,播放器在渲染视频时就是根据PTS来确定渲染和展示时间点的。 根据这个原理,我们就可以通过调整帧的PTS时间来实现视频加速/降速播放。
加速/降速的原理
我们都知道,当帧速率(frame rate)大于24时,也就是1秒播放24帧时,我们的视觉就会看到流程的视频。 在帧总量不变的情况下,如果将1/24变为1/48,那么在相同时间内多播放了一倍的帧,对于我们的视觉来说,就感觉播放速度加快了(因为本该20秒才能播放完的帧,在10秒内就播放完了,就相当加速了一倍)。同理,如果将1/24调整为1/12,就会看到慢动作。
FFmpeg提供了setpts滤镜可以实现调整pts的效果。 典型的用法如下:
ffmpeg -i ~/tmp/trailer.mp4 -filter:v "setpts=0.5*PTS" output.mp4
0.5*PTS表示将帧的PTS值乘以0.5后作为新的PTS值。 比如说: 帧A当前的PTS是4000(根据以前的知识,根据PTS和Time_base可以计算出渲染的时间点)。 假设对应的时间点是: 00:00:05, 现在将PTS调整为0.5*PTS就变成了2000,那么对应的渲染时间点就变成了: 00:00:02.5。这样就实现了加速播放。
同理,如果是2*PTS就是降速播放。
局部调整
setpts只能实现全部加速或者全部减速。 因为在其内部实现中,对每个帧都应用相同的计算规则,所以要么都调整要么都不调整。如果要实现局部调整,按照通用的解决方案,只能先切割视频,然后对单独视频进行加速/降速处理,然后再将视频连接起来。
但如果我们适当调整PTS值,也可以实现部分调整的效果。
- 问题分析
假设存在一段30s的视频,帧分布如下:
+------------------------------------------------------------------+
| F1 F2 F3 F4 F5 F6 F7 |
| |--------------|--------------|--------------|---> |
|Time 0 10 20 30 |
|PTS 0 100 200 250 300 350 400 |
+------------------------------------------------------------------+
F1 - F7表示7个I帧(30秒包含的帧比这个多多了,这里是为了方便描述问题)。 假设我们需要加速前15秒(后15秒播放速率不变)的视频,那么需要调整F1到F4(F4是第15秒时渲染的帧)如下:
+------------------------------------------------------------------+
| F1 F2 F3 F4 F5 F6 F7 |
| |--------------|--------------|--------------|---> |
|Time 0 10 20 30 |
|PTS 0 100 200 250 300 350 400 |
+------------------------------------------------------------------+
这样调整看似没问题,但仔细分析会发现在10s-20s之间会出现天窗,这是因为这段时间内的PTS没有任何帧需要渲染,直到第20秒的时候,才会开始继续渲染F5帧。显然这样不满足实际应用需求。
而发生问题的关键在于将F2-F4调整PTS之后,也需要实时调整F5-F7的PTS。 也就是正确的帧分布应该是下面的样子:
+------------------------------------------------------------------+
| F1 F2 F3 F4 F5 F6 F7 |
| |--------------|--------------|--------------|---> |
|Time 0 10 20 30 |
|PTS 0 100 200 250 300 350 400 |
+------------------------------------------------------------------+
F1-F4以一个速率播放,而F5-F7以另外一个速率播放。这样就实现了部分加速的效果。
- 代码实现
为了简化编码难度,我们以setpts的代码为基础进行修改。 在setpts代码中修改pts的代码是下面部分:
d = av_expr_eval(setpts->expr, setpts->var_values, NULL);
frame->pts = D2TS(d);
d是根据规则(0.5*PTS)计算出来的pts值. 然后将新的PTS赋值给当前帧,而后继续后面的编码处理。
所以在这里,我们做一些判断,为了简化其它无关步骤,先假设只修改前5秒的视频,所以需要先判断当前帧是否需要修改:
(frame->pts * av_q2d(inlink->time_base)) < 5.0
通过pts*time_base可以计算出当前时间点,通过这个判断,可以得出是否需要修改此帧的PTS值。 如果需要修改,那么仍然通过frame->pts = D2TS(d)来调整。 而处理不需要修改的帧才是重点,
按照上图所示意的PTS,F5应该继承F4调整前的PTS值,所以需要在调整F4之前需要保存旧的PTS。所以是下面的伪代码:
保存Old PTS
if (当前时间 < 0.5) {
计算新的PTS并赋值给当前帧
}else{
当前帧使用上个帧的PTS
}
将伪代码实现后如下:
oldPts = frame->pts;
if ((frame->pts * av_q2d(inlink->time_base)) < 5.0) {
frame->pts = D2TS(d);
setpts->_pts = frame->pts;
} else {
frame->pts = setpts->_pts;
}
setpts->_pts++;
完整代码可参考 isetpts中的代码。
新手学习FFmpeg - 调用API调整视频局部速率的更多相关文章
- 新手学习FFmpeg - 调用API完成视频的读取和输出
在写了几个avfilter之后,原本以为对ffmpeg应该算是入门了. 结果今天想对一个视频文件进行转码操作,才发现基本的视频读取,输出都搞不定. 痛定思痛,仔细研究了一下ffmpeg提供的examp ...
- 新手学习FFmpeg - 调用API完成两个视频的任意合并
本次尝试在视频A中的任意位置插入视频B. 在上一篇中,我们通过调整PTS可以实现视频的加减速.这只是对同一个视频的调转,本次我们尝试对多个视频进行合并处理. Concat如何运行 ffmpeg提供了一 ...
- 新手学习FFmpeg - 调用API完成录屏
调用FFMPEG Device API完成Mac录屏功能. 调用FFMPEG提供的API来完成录屏功能,大致的思路是: 打开输入设备. 打开输出设备. 从输入设备读取视频流,然后经过解码->编码 ...
- 新手学习FFmpeg - 调用API编写实现多次淡入淡出效果的滤镜
前面几篇文章聊了聊FFmpeg的基础知识,我也是接触FFmpeg不久,除了时间处理之外,很多高深(滤镜)操作都没接触到.在学习时间处理的时候,都是通过在ffmpeg目前提供的avfilter基础上面修 ...
- 新手学习FFmpeg - 调用API完成录屏并进行H.264编码
Screen Record H.264 目前在网络传输视频/音频流都一般会采用H.264进行编码,所以尝试调用FFMPEG API完成Mac录屏功能,同时编码为H.264格式. 在上一篇文章中,通过调 ...
- 新手学习FFmpeg - 调用API计算关键帧渲染时间点
通过简单的计算来,线上I帧在视频中出现的时间点. 完整代码请参考 https://andy-zhangtao.github.io/ffmpeg-examples/ 名词解释 首先需要明确以下名词概念: ...
- 新手学习FFmpeg - 通过API实现可控的Filter调用链
虽然通过声明[x][y]avfilter=a=x:b=y;avfilter=xxx的方式可以创建一个可用的Filter调用链,并且在绝大多数场合下这种方式都是靠谱和实用的. 但如果想精细化的管理AVF ...
- 新手学习FFmpeg - 通过API完成filter-complex功能
本篇尝试通过API实现Filter Graph功能. 源码请参看 https://andy-zhangtao.github.io/ffmpeg-examples/ FFmpeg提供了很多实用且强大的滤 ...
- 新手学习FFmpeg - 如何编写Kubernetes资源文件
Kubernetes API的使用方式 Kubernetes API属于声明式API编程, 它和常用的命令式编程有一些区别. 通俗的说,命令式编程是第一人称,我要做什么,我要怎么做. 操作系统最喜欢这 ...
随机推荐
- 腾讯物联TencentOS tiny上云初探
2017年中旬曾写过一篇关于物联网平台的文章<微软最完善,百度最“小气” 看微软阿里百度三大物联网云平台对比>.现在已经过去两年了,物联网的格局又发生了不少的变化.不过针对腾讯来说,其物联 ...
- JavaWeb无框架,借助反射采用精巧设计模式实现放微信PC聊天页面
本周开始在写仿写一个微信PC端的聊天页面,没有使用ssh.ssm等框架,采用JavaWeb.反射.MySQL.C3P0等技术.这里把其中和核心技术列出来请大家指教. 与传统JavaWeb项目的区别 传 ...
- Go_笔试题记录-不熟悉的
1.golang中没有隐藏的this指针,这句话的含义是() A. 方法施加的对象显式传递,没有被隐藏起来 B. golang沿袭了传统面向对象编程中的诸多概念,比如继承.虚函数和构造函数 C. go ...
- spring-boot-plus后台快速开发脚手架之代码生成器使用(十)
spring-boot-plus 代码生成 Generator 代码生成内容 spring-boot-plus在mybatis-plus基础上,新增param/vo等模板 拓展controller/s ...
- Java web部分-面试题
1.Tomcat的优化经验 答:去掉对web.xml的监视,把jsp提前编辑成Servlet. 有富余物理内存的情况,加大tomcat使用的jvm的内存 2.Servlet的生命周期 答:servle ...
- 使用spark dataSet 和rdd 解决 某个用户在某个地点待了多长时间
现有如下数据文件需要处理格式:CSV位置:hdfs://myhdfs/input.csv大小:100GB字段:用户ID,位置ID,开始时间,停留时长(分钟) 4行样例: UserA,LocationA ...
- 区间K大数
区间K大数 问题描述 给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个. 输入格式 第一行包含一个数n,表示序列长度. 第二行包含n个正整数,表示给定的序列. 第三个包含一个正整数m, ...
- Liunx学习总结(一)--初识 Liunx
liunx 系统可以说是目前为止份额最大的一个系统了,基本上大多数服务器上都可以看到它的身影.今天我们就来学习一下它. Liunx 简介 Linux 是一套免费使用和自由传播的类 Unix 操作系统, ...
- python 18 re模块
目录 re 模块 1. 正则表达式 2. 匹配模式 3. 常用方法 re 模块 1. 正则表达式 \w 匹配字母(包含中文)或数字或下划线 \W 匹配非字母(包含中文)或数字或下划线 \s 匹配任意的 ...
- CentOS -- RocketMQ 4.3.2 standalone Installation and Configuration
1 Download RockeMQ Package: You need to download it and put it to the OS Image. wget http://apache.c ...