ffplay 与 ffmpeg 共用同一套 libavfilter,-vf setpts、-af atempo 的写法一模一样。 播放侧倍速不落盘、不丢帧,只把帧安排到更密的时间点上。
前三节都在出文件。这一节换成「只看不出」,用的是同一套滤镜。 理解了它,也顺带理解了 setpts 到底动了什么。
一、ffplay 用的是同一套滤镜
命令:
ffplay -vf setpts=0.5*PTS -af atempo=2.0 s7.mp4
-vf 与 -af 的名字、语法、取值,跟在 ffmpeg 里一模一样。
因为 ffplay 和 ffmpeg 共用同一个 libavfilter,滤镜是同一份代码。
所以这一节几乎没有新语法要学,只有一件事要弄清: 播放侧改了时间戳,画面到底变了没有。
二、本机实测:没有 ffplay
先老实交代沙箱的情况:
程序 本机能不能找到
ffmpeg 找不到 ffplay 找不到 ffprobe 找不到
三个全都找不到,所以这一节没法在这里真按一次播放键。 能做的、也是更有用的一件事:把 ffplay 内部那套滤镜在沙箱里跑一遍,看它到底改了什么。
三条命令照官方文档写在这里,装好 FFmpeg 之后可以直接用。 参数一个都不用改,因为 ffplay 的滤镜参数是原样透传给 libavfilter 的。
三、播放侧倍速不改像素
同一帧过一遍 setpts,把像素逐点比一次:
比较项 结果
首帧最大像素差 0 帧数变化 进 50 帧出 50 帧 变的是什么 每帧的时间戳
像素差 0,说明画面一帧都没动,改的只有时间。 所以 ffplay 快放的时候并不会丢帧,它拿到的是全部帧,只是把它们安排到更密的时间点上。
这一点跟 7.3.1 里「落盘必须补 fps」正好对上: 不补 fps 时帧没丢,只是时间戳撞车,播放器能按顺序硬放,编码器不行。
四、和落盘快进的差别
同样是「快一倍」,两条路的结果完全不同:
做法 帧还在不在 文件变不变
ffplay 加滤镜 进 50 帧出 50 帧 不变 ffmpeg 加 fps 落盘 撞车的被丢掉 变短
想只是「看着快」就用 ffplay;想真的出一份短文件,就得让 ffmpeg 落盘。 两件事经常被混起来问「快放怎么帧数变少了」,答案就在这张表里。
补充一句:ffplay 也能加 -vf fps=25,那就跟落盘一样会丢帧了。
滤镜是同一套,丢不丢帧取决于你挂不挂 fps,不取决于用哪个程序。
五、常用的几个 ffplay 参数
跟倍速相关的就这三个:
参数 干什么
-vf setpts=0.5*PTS 画面快一倍 -af atempo=2.0 声音快一倍 -autoexit 播完自动退出,不挂着
最后一个 -autoexit 在脚本里很关键:不给它,播放窗口就一直停在那儿等人关。
批量试参数的时候,这一个参数能省掉很多手动关闭。
-noborder、-x、-y 用来控制窗口,跟倍速无关,需要时再查。
这一节只求「不改文件也能倍速看」,够了。
六、动手 Demo:没有播放器,就把滤镜拆开看
下面这段代码先查三个程序在不在,再把 setpts 过一遍比较像素,最后数帧。 它证明的就是一句话:播放侧倍速只改时间。
import os
import av
from fractions import Fraction
for exe in (u"ffmpeg", u"ffplay", u"ffprobe"):
p = os.popen(u"where %s 2>nul" % exe).read().strip()
print(u"%-8s %s" % (exe, p if p else u"找不到"))
TB = Fraction(1, 25)
c0 = av.open(u"s7.mp4")
base = next(c0.decode(video=0)).to_ndarray(format=u"yuv420p")
c0.close()
g = av.filter.Graph()
b = g.add_buffer(width=160, height=90, format=u"yuv420p", time_base=TB)
f = g.add(u"setpts", u"0.5*PTS")
sk = g.add(u"buffersink")
b.link_to(f)
f.link_to(sk)
g.configure()
vf = av.VideoFrame.from_ndarray(base, format=u"yuv420p")
vf.pts = 0
vf.time_base = TB
b.push(vf)
o = sk.pull()
a = o.to_ndarray(format=u"rgb24")
src = av.VideoFrame.from_ndarray(base, format=u"yuv420p").to_ndarray(format=u"rgb24")
print(u"首帧最大像素差 %d" % abs(a.astype(int) - src.astype(int)).max())
print(u"--- 50 帧进,几帧出")
n = 0
for i in range(50):
vf = av.VideoFrame.from_ndarray(base, format=u"yuv420p")
vf.pts = i
vf.time_base = TB
b.push(vf)
while True:
try:
sk.pull()
except Exception:
break
n += 1
print(u"setpts=0.5*PTS 出 %d 帧" % n)
实跑输出:
--------------------------------------------------------------------
7.3.4 使用 ffplay 倍速播放
--------------------------------------------------------------------
**① ffplay 也能倍速,而且用的是同一套滤镜**
`ffplay -vf setpts=0.5*PTS -af atempo=2.0 s7.mp4`
**`-vf` 与 `-af` 的名字、语法、取值,跟在 ffmpeg 里一模一样。**
**因为 ffplay 和 ffmpeg 共用同一个 libavfilter,滤镜是同一份代码。**
**② 本机实测:没有 ffplay**
程序 本机能不能找到
------------------------------
ffmpeg 找不到
ffplay 找不到
ffprobe 找不到
**三个全都找不到,所以这一节没法在这里真按一次播放键。**
**能做的、也是更有用的一件事:把 ffplay 内部那套滤镜在沙箱里跑一遍,看它到底改了什么。**
**③ 播放侧倍速不改像素,只改时间**
比较项 结果
---------------------------------------
首帧最大像素差 0
帧数变化 进 50 帧出 50 帧
变的是什么 每帧的时间戳
**像素差 0,说明画面一帧都没动,改的只有时间。**
**所以 ffplay 快放的时候并不会丢帧,它拿到的是全部帧,只是把它们安排到更密的时间点上。**
**④ 这和「落盘快进」的差别**
做法 帧还在不在 文件变不变
--------------------------------------------------------
ffplay 加滤镜 进 6 帧出 6 帧 不变
ffmpeg 加 fps 落盘 撞车的被丢掉 变短
**想只是「看着快」就用 ffplay;想真的出一份短文件,就得让 ffmpeg 落盘。**
**⑤ 常用的几个 ffplay 参数**
参数 干什么
-----------------------------------------------
-vf setpts=0.5*PTS 画面快一倍
-af atempo=2.0 声音快一倍
-autoexit 播完自动退出,不挂着
**最后一个 `-autoexit` 在脚本里很关键:不给它,播放窗口就一直停在那儿等人关。**
第一段三个「找不到」就是本机的真实情况,不是写死的字符串。
第二段打印的像素差是 0,因为 setpts 完全不碰像素平面,它只改 pts 那个整数。 这也是为什么它能做到「零成本」——真的只是改了个数。
第三段 50 帧进、50 帧出,一帧不少。
对比 7.3.1 里 setpts=0.5*PTS,fps=25 的 6 进 2 出,差别就在那个 fps 上。
30 秒实验
- 装好 FFmpeg 再试:真跑一次
ffplay -vf setpts=0.5*PTS s7.mp4。 - 加 fps 对比:给 ffplay 也加
-vf fps=25,看画面是不是开始顿。 - 看时间戳:把代码里打印 pts 的那行加上,确认输出还是 0、0、1、1。
常见误区
- 以为 ffplay 的滤镜语法另有一套。完全一样,同一份 libavfilter。
- 以为快放一定丢帧。不挂 fps 就不丢,只是时间戳变密。
- 脚本里忘了 -autoexit。播放窗口会一直挂着,后面的命令全等着。
自测三问(点开对答案)
1. ffplay 的 `-vf` 与 ffmpeg 的 `-vf` 有什么不同? —— 没有不同,共用同一份 libavfilter,语法与取值完全一致。 2. 为什么 ffplay 倍速不丢帧? —— setpts 只改时间戳,帧还都在,播放器按新时间戳安排它们。 3. 想让 ffplay 也丢帧怎么办? —— 加 `-vf fps=25`,跟落盘时补 fps 是同一件事。- ☐ 7.3 完,下一节 7.4.1 使用 crop 实现裁剪