FFmpeg中转场滤镜xfade的时间参数(duration和offset)与算法解读
Posted Jack_Chai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg中转场滤镜xfade的时间参数(duration和offset)与算法解读相关的知识,希望对你有一定的参考价值。
本文出处:http://blog.csdn.net/chaijunkun/article/details/111579466,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。
xfade转场滤镜小科普
最近在研究音视频合成的相关功能,现已有两个视频剪辑。拼合成一个文件显然用concat可以完成,但是过渡生硬,而xfade滤镜可以很方便实现更加缓和的场景切换。
xfade滤镜在FFmpeg 4.3中才出现,因此低于此版本的FFmpeg都无法使用该滤镜。
在xfade转场滤镜中,有这么几个名词:A场、B场,持续时长和起始位置。
A场:按照时间顺序,转场前的画面叫做A场(下图中蓝色画面);
B场:按照时间顺序,转场后的画面叫做B场(下图中青色画面);
起始位置:offset,指转场效果在A场输入源的何处开始起作用,
持续时长:duration,指转场特效持续的时长。
举个例子:
|<--offset
|<--duration-->|
AAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBBB
应用后的输出
|<--duration-->|
AAAAAAABABABABABABABABBBBBBBBBBBB
关于时间的疑问
在官方FFmpeg官方文档中,关于的duration和offset的描述比较简单:
duration
Set cross fade duration in seconds. Default duration is 1 second.
offset
Set cross fade start relative to first input stream in seconds. Default offset is 0.
通过文档可知,这两个参数都是以“秒”为单位的。如果编程实现自动化生成带转场视频,秒级单位很显然不如毫秒更为方便(当然可以使用小数来精确到毫秒,但在程序中,某些时候整数处理要比小数处理容易)。那么参数有办法使用毫秒吗?
另外,转场后视频的精确时间如何计算?
查看源码
作为滤镜,xfade的源码所在位置:
https://github.com/FFmpeg/FFmpeg/blob/master/libavfilter/vf_xfade.c
来看下该滤镜的参数定义部分:
static const AVOption xfade_options[] =
...
"duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, .i64=1000000, 0, 60000000, FLAGS ,
"offset", "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, .i64=0, INT64_MIN, INT64_MAX, FLAGS ,
...
;
可以看到,duration和offset参数的类型,都是AV_OPT_TYPE_DURATION,因此和其他滤镜中关于时间的参数一样,均支持形如:5s,300ms,2:28这样的格式。最终经过parseutils.c中的av_parse_time处理后,统一转换为64位的基于微秒(microsecond)的整数数据。
在视音频编码中,码流的时钟同步是通过pts(Presentation TimeStamps)来实现的。例如使用ffprobe命令查看码流信息:
"time_base": "1/12800",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 269824,
"duration": "21.080000",
pts(start_pts,duration_ts)为64位整数类型。
对应的,在呈现时还有一个时间基(time_base),表示一个pts展示的时长,单位:秒。
上面的例子如果将时间基取倒数,也可以解释为1秒钟会包含12800个pts。
因此可以得知:
duration(单位:秒) = duration_ts * time_base
21.08 = 269824 * 1 / 12800
由于不同码流可以采用自己不同的时间基。如何将输入的duration和offset精确对应到码流的某个时间点呢?
FFmpeg引入了一个系统的标准时间基,刚才讲到,处理时间相关的参数时,会统一转换为以微秒为单位的整数数据。
因此系统标准时间基为:AV_TIME_BASE_Q(在avutil.h中定义),值为:1/1,000,000,即1秒钟包含一百万个pts。
那么时间到目标流的pts算法就是一个等比例缩放的关系(滤镜源码在config_output中使用了av_rescale_q):
duration * AV_TIME_BASE_Q = target_pts * target_time_base
即:
target_pts = (duration * AV_TIME_BASE_Q) / target_time_base
在上述计算中,涉及到了除法。当除不尽时,最后结果将针对小数点后1位进行四舍五入,取整。
转场后视频的精确时间如何计算?
正常的例子
假设有如下两个文件:
video_a.mp4:
"time_base": "1/12800",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 65024,
"duration": "5.080000"
video_b.mp4
"time_base": "1/12800",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 197120,
"duration": "15.400000"
使用如下命令转场:
ffmpeg -i video_a.mp4 -i video_b.mp4 -filter_complex "[0:v][1:v]xfade=transition=fade:offset=5s:duration=80ms" -c copy trans.mp4
可以看到,输出视频的信息为:
"time_base": "1/12800",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 261120,
"duration": "20.400000"
指定的offset为5s,转换为内部的微秒为 5,000,000,综合时间基等比例缩放到video_a的pts:
(5,000,000 * (1 / 1,000,000)) / (1 / 12800)
即offset映射到的pts为:64,000
此值小于video_a的duration_ts(即:65024),说明滤镜将会被激活。
从offset开始,其实在A场景的底层,B场景已经开始播放了。
计算输出视频的总duration_ts时,由于B场景的时间基与A场景一致,因此直接累加B的duration_ts即可:
total_duration_ts = offset + b_duration_ts
= 64,000 + 197,120
= 261,120
可以看到,结果与上述生成的视频结果一致。
两个时间基不一样的场景如何转场
xfade滤镜在输出时,将会参考A场视频的时间基参数。若B场的时间基与A场不同,在计算合成后的视频长度时,
应当对B场的时间基进行统一化,映射到与A场景同样的时间基:
b_new_duration_ts = b_old_duration_ts * b_old_time_base / a_time_base
一些特殊例子
指定的offset超过了A场的总时长
当offset超过了A场的总时长时,xfade滤镜没有机会启用,因此经过此滤镜的输出流仍然是A场内容,B场内容将会被忽略。
指定的offset没有超过A场总时长
从offset开始算起,此时又有两种可能:
加上duration后,超过了A场的总时长和小于等于总时长;
无论是哪种情况,只要offset位于A场视频中,滤镜就会被激活。
offset+duration超过A场总时长
|<--duration-->
AAAAAAAA
BBBBBBBBBBBBBBBBB
特效的目标是明确的,就是A场转B场。
以淡出滤镜为例,就是将A场的全部像素RGBA数据中的Alpha通道从1降到0。
假设duration为2秒, 那么就是在这2秒中均匀地递减Alpha值,每一次减多少都是事先算好的。
因此在每一帧经过滤镜时,滤镜只是按照之前的规划对像素进行调整。
A场没有数据了,或者duration超过了,则自动将A场内容屏蔽掉。
视频总长度:
total_pts = a_offset_pts + b_duration_ts
offset+duration小于等于A场总时长
|<--duration-->|
AAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBB
这种情况下,滤镜已经完成了转场通能,超出部分会被截掉。
视频总长度:
total_pts = a_offset_pts + b_duration_ts
参考文献:
[1]雷霄骅.FFmpeg源代码简单分析:结构体成员管理系统-AVOption[EB/OL].https://blog.csdn.net/leixiaohua1020/article/details/44279329,2015-03-16.
[2]刘歧.刘歧:FFmpeg Filter深度应用[EB/OL].https://blog.csdn.net/vn9PLgZvnPs1522s82g/article/details/81977602,2018-08-22.
以上是关于FFmpeg中转场滤镜xfade的时间参数(duration和offset)与算法解读的主要内容,如果未能解决你的问题,请参考以下文章