FrameSync帧同步—参数
作者:罗上文,微信:Loken1,公众号:FFmpeg弦外之音
FrameSync 里面有 3 个参数来满足不同的同步需求,如下:

本文我们来研究一下这些参数的功能在代码里是如何实现的。
eof_action 参数
eof_action 参数的作用是定义 第二个流结束时候 的处理逻辑,它有 3 个值。
repeat,重复上一帧的数据,也就是第二个流结束之后,后面一直用最后一帧来跟第一个流的帧进行overlay操作。endall,结束滤波处理,当第二个流结束的时候,overlay 滤波处理就要立即结束。pass,跳过滤波处理,也就是当第二个流结束的时候,后面不再进行overlay滤波操作了,直接把 main 帧传递给下一级 滤镜处理。
其实 这些参数的功能都是通过两个字段来实现的,分别是 FFFrameSyncIn::before、 FFFrameSyncIn::after。
FFFrameSyncIn::before 代表输入流开始前的操作,FFFrameSyncIn::after 代表输入流结束后的操作。
其实 before、after 这两个字段有些值我也不太懂,我没有找到对应的命令来演示某些值的功能,所以我只讲我懂的那部分。
当 eof_action 等于 repeat 的时候,第二个流的 FFFrameSyncIn::after 就是 EXT_INFINITY,这也是默认值。是在初始化的时候赋值的,如下:

当第二个流结束的时候,consume_from_fifos() 函数就从第二个流里面读不到 AVFrame 了,他就会跑到 framesync_inject_status() 里面,如下:

当 framesync_inject_status() 里面发现 after 是 EXT_INFINITY 的时候,它就会把 第二个流的 pts_next 设为最大值 INT64_MAX。
这样后面在 framesync_advance() 循环的时候,第一个流的 pts_next 总是最小的了。这样第二个流就不会把 frame_next 移到 frame 里,一直使用的都是最后一帧数据来进行 overlay 操作。
当 eof_action 等于 endall ,其实就是 opt_shortest 等于 1,这两个参数实现的是同一个功能,你可以理解为别名。代码如下:

也就是说下面两条命令是等价的。
ffmpeg -eof_action endall ...省略...
ffmpeg -shortest ...省略...
当 opt_shortest 等于 1 的时候,他就会把所有输入流的 FFFrameSyncIn::after 设置为 EXT_STOP,如下:
if (fs->opt_shortest) {
for (i = 0; i < fs->nb_in; i++)
fs->in[i].after = EXT_STOP;
}
然后在 framesync_advance() 里面,当某个输入流结束了,也就是 state 等于 STATE_EOF,就会执行 framesync_eof(),如下:

framesync_eof() 里面的重点是 设置了下级 AVFIlterLink 的状态为 AVERROR_EOF,如下:
ff_outlink_set_status(fs->parent->outputs[0], AVERROR_EOF, AV_NOPTS_VALUE);
这实际上是把 overlay 滤波输出的状态设置为 AVERROR_EOF,但是并不代表整个 Graph 的滤波处理都要结束,只是 overlay 这个环节结束了。
因为有些命令可能比较复杂,他可能有一个跟 overlay 同级的滤镜Y,如下:

endall 这个参数他结束的只是他对应的滤镜的滤波处理,并不是整个 Graph 的滤波处理。
如上图,虽然 overlay 结束了,但是滤镜 Y 还没结束,Y 还可以继续传递数据给 滤镜 Z 进行滤波处理。
当 eof_action 等于 pass 的时候,其实就是 opt_repeatlast 等于 0,这两个参数实现的是同一个功能,你可以理解为别名。代码如下:

也就是说下面两条命令是等价的。
ffmpeg -eof_action pass ...省略...
ffmpeg -norepeatlast ...省略...
当 opt_repeatlast 等于 0 的时候,他就会把所有输入流的 FFFrameSyncIn::after 设置为 EXT_NULL,如下:
if (!fs->opt_repeatlast) {
for (i = 1; i < fs->nb_in; i++) {
fs->in[i].after = EXT_NULL;
fs->in[i].sync = 0;
}
}
EXT_NULL 代表其余的流可以在缺少了该流的情况下执行滤波处理。
在 framesync_inject_status() 里面发现 after 是 EXT_NULL 的时候,它就不会把 第二个流的 pts_next 设为最大值 INT64_MAX。
这样会导致第二个流的 FFFrameSyncIn::frame 后面被清空,再导致后面再 do_blend() 里面拿不到第二帧数据,然后就会直接传递 mainpic 帧给下一级的滤镜。

这就是 eof_action 等于 pass 的代码实现逻辑。
其实 overlay.c 里面也有跟 framesync.c 一样的参数,如下:

虽然 FrameSync 帧同步在其他滤镜里面也有使用,例如:vf_alphamerge.c,vf_bm3d.c,vf_displace.c 等等。
但是我看了一下,只有 overlay.c 有 eof_action、shortest、repeatlast 这些帧同步参数,其他滤镜是没有的,无法配置这些参数的。
所以我个人认为这 3 个参数,是 overlay 滤镜专属的。
