FFmpeg中转场滤镜xfade的时间参数(duration和offset)与算法解读

本文详细介绍了FFmpeg中的xfade转场滤镜的工作原理及应用方法,包括A场与B场的概念、offset和duration参数的具体含义及其计算方式,并通过实例展示了不同场景下视频合成的效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文出处:http://blog.youkuaiyun.com/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.youkuaiyun.com/leixiaohua1020/article/details/44279329,2015-03-16.
[2]刘歧.刘歧:FFmpeg Filter深度应用[EB/OL].https://blog.youkuaiyun.com/vn9PLgZvnPs1522s82g/article/details/81977602,2018-08-22.

### FFmpeg xfade 转场效果实现指南 `xfade` 是 FFmpeg 提供的一种滤波器,用于在两个输入流之间创建平滑的过渡效果。这种滤波器可以应用于视频剪辑中的景切换,使画面转换更加自然流畅。 #### 基本语法 以下是 `xfade` 的基本语法结构: ```bash ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex "[0:v][1:v]xfade=transition=<type>:duration=<seconds>:offset=<seconds>[out]" -map "[out]" output.mp4 ``` - **input1.mp4 input2.mp4**: 表示参转场的两个输入文件。 - **transition**: 定义使用的转场类型,支持多种内置效果[^3]。 - **duration**: 设置转场持续时间(单位为秒)。 - **offset**: 指定第二个视频片段何时开始播放(相对于第一个视频的时间偏移量)。 #### 支持的转场类型 `xfade` 提供了丰富的转场样式,常见的有以下几种: | 类型名称 | 描述 | |----------------|--------------------------| | crossfade | 渐隐渐显 | | fadeblack | 黑屏淡入淡出 | |fadewhite | 白屏淡入淡出 | |wipeleft | 左向擦除 | |wiperight | 右向擦除 | 更多类型的列表可以通过查阅官方文档获取更多信息[^3]。 #### 示例代码 下面是一些具体的例子展示如何应用不同的转场效果。 ##### 示例 1: 使用交叉淡化 (crossfade) 此示例演示了如何让两个视频片段通过交叉淡化的形式进行衔接。 ```bash ffmpeg -i video1.mp4 -i video2.mp4 -filter_complex "[0:v][1:v]xfade=transition=crossfade:duration=2:offset=5[v]" -map "[v]" output.mp4 ``` 在此命令中,`video1.mp4` 将在其第 5 秒处开始 `video2.mp4` 执行为期 2 秒的交叉淡化操作[^3]。 ##### 示例 2: 使用黑屏淡入淡出 (fadeblack) 如果希望在两个视频间插入一段短暂的全黑过渡,则可选用 `fadeblack` 效果。 ```bash ffmpeg -i clipA.mp4 -i clipB.mp4 -filter_complex "[0:v][1:v]xfade=transition=fadeblack:duration=1:offset=7[outv]" -map "[outv]" result.mp4 ``` 这里设置了从 `clipA.mp4` 到 `clipB.mp4` 的 1 秒黑色过渡,并且该过程发生在总时长的第 7 秒位置[^3]。 #### 注意事项 当尝试复杂组合或者多个连续转场时,请注意计算好各部分之间的相对时间关系以及可能产生的同步问题。另外,某些特定硬件加速配置下可能会遇到性能瓶颈或兼容性挑战,在实际部署前建议充分测试目标环境下的表现情况。 ---
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值