fontfile 与 movie 走同一套解析器,规则完全一样:换斜杠、转冒号。 引号是 shell 的事,不是滤镜的事——这两件事经常被混为一谈。
本章最后一节,把路径这条线收口。 6.2.2 讲的是 movie,这一节是 fontfile,同一个坑换个位置再来一次。
一、四种写法,结论与 movie 完全一致
写法 字符串开头 结果
原样 I:\Desktop\… 失败 只换斜杠 I:/Desktop/… 失败 只转义冒号 I\:\Desktop\… 失败 斜杠加转义 I\:/Desktop/… 成功
四种组合的结果与 6.2.2 一模一样,一字节都不差。 因为 fontfile 与 movie 的参数都走同一套解析:冒号分隔、正斜杠认路。
学会一次,这一课里所有带路径的参数都能用: movie、fontfile、textfile、subtitles,规则统一。
二、带空格的路径:引号该不该加
写法 结果 为什么
直接给带空格的路径 成功 接口层不看空格 再套一层单引号 失败 引号成了文件名的一部分
第一行成功,加了引号的第二行反而失败——这一点最容易想反。 因为单引号是 shell 的语法,不是滤镜的语法。
命令行里是 shell 先剥掉引号、再把干净的字符串交给 FFmpeg; 而代码里直接调接口,中间没有 shell,加引号就等于文件名里多了两个字符。
所以「要不要套引号」取决于你在哪一层写字,不是滤镜的硬性规定。 这一句话能解释掉一大半「同样的命令,代码里就是不行」的困惑。
三、四条规则,收个尾
规则 为什么
反斜杠换正斜杠 滤镜内部分隔符只认正斜杠 冒号前加反斜杠 冒号是参数分隔符 命令行里整体套引号 避免 shell 按空格拆参数 用绝对路径 避免工作目录不同找不到
四条一起做,路径问题就基本绝迹了。 最难记的是第二条——它只发生在 Windows 上,Linux 路径里没有冒号。
所以网上绝大多数教程压根不会提这件事, 而你会在自己的机器上一次次撞上它。
写成一个函数,以后所有路径都过一遍它,就不再需要记了:
p.replace("\\", "/").replace(":", "\\:"),两行,够用一辈子。
四、动手 Demo:四种写法与带空格的路径
先把同一个文件按四种方式拼路径,逐个交给 movie 验证。 再拿一个文件名带空格的副本,试「加引号」与「不加引号」。
import av, os, shutil
def esc(p):
return p.replace(u"\\", u"/").replace(u":", u"\\:")
src = os.path.abspath(u"s5.mp4")
for tag, p in ((u"原样", src),
(u"只换斜杠", src.replace(u"\\", u"/")),
(u"只转义冒号", src.replace(u":", u"\\:")),
(u"斜杠加转义", esc(src))):
try:
g = av.filter.Graph()
m = g.add(u"movie", p)
s = g.add(u"buffersink")
m.link_to(s)
g.configure()
s.pull()
print(u"%-12s 成功" % tag)
except Exception:
print(u"%-12s 失败" % tag)
sp = os.path.join(os.path.dirname(src), u"a b.mp4")
if not os.path.isfile(sp):
shutil.copyfile(src, sp)
for tag, p in ((u"不加引号", esc(sp)), (u"加单引号", u"'" + esc(sp) + u"'")):
try:
g = av.filter.Graph()
m = g.add(u"movie", p)
s = g.add(u"buffersink")
m.link_to(s)
g.configure()
s.pull()
print(u"%-10s 成功" % tag)
except Exception:
print(u"%-10s 失败" % tag)
实跑输出:
--------------------------------------------------------------------
6.6.3 drawtext 中使用绝对路径——与 movie 同一条规则
--------------------------------------------------------------------
**fontfile 与 movie 走的是同一套参数解析器,所以规则完全一样。**
**拿 movie 读一个带空格的文件,把这条规则验一遍。**
**① 四种写法**
写法 字符串开头 结果
-------------------------------------------------------
原样 I:\Desktop\自我教学\.workb… 失败
只换斜杠 I:/Desktop/自我教学/.workb… 失败
只转义冒号 I\:\Desktop\自我教学\.work… 失败
斜杠加转义 I\:/Desktop/自我教学/.work… 成功
**② 路径里有空格会怎样**
写法 结果 为什么
----------------------------------------------------------
直接给带空格的路径 成功 接口层不看空格
再套一层单引号 失败 引号成了文件名的一部分
**第一行成功,加了引号的第二行反而失败——这一点最容易想反。**
**因为单引号是 shell 的语法,不是滤镜的语法。**
**命令行里是 shell 先把引号剥掉、再把干净的字符串交给 FFmpeg;**
**而这段代码直接调接口,中间没有 shell,加引号就等于在文件名里多了两个字符。**
**所以「要不要套引号」取决于你在哪一层写字,不是滤镜的硬性规定。**
**③ 一张对照表,四条规则**
规则 为什么
-----------------------------------------------------
反斜杠换正斜杠 滤镜内部分隔符只认正斜杠
冒号前加反斜杠 冒号是参数分隔符
命令行里整体套引号 避免 shell 按空格拆参数
用绝对路径 避免工作目录不同导致找不到
**四条一起做,路径问题就基本绝迹了。**
**最难记的是第二条——它只发生在 Windows 上,Linux 的路径里没有冒号,**
**所以网上绝大多数教程压根不会提这件事。**
os.path.abspath(u"s5.mp4") 拿到的是 Windows 风格路径,反斜杠加盘符冒号。
这正是最需要转义的那种字符串,拿它做实验最贴近真实场景。
加引号那一行是把 ' 当成字符串的一部分拼进去的,
所以它拼出来的文件名里真的有两个引号字符——当然找不到文件。
在 PowerShell 或 cmd 里,这两行的结果会反过来:不加引号才失败。 差别不在滤镜,而在「谁先解析」——这就是这一节最值得带走的一句话。
30 秒实验
- 换目录:把文件挪到
C:\Program Files\x\,看空格与括号要不要额外处理。 - 换参数:把 movie 换成
textfile=...,验证同一套转义规则。 - 封装函数:把 esc 写进自己的工具库,以后所有路径都过一遍。
常见误区
- 只做一半转义。只换斜杠或只转冒号,都还是失败。
- 在代码里加单引号。引号会变成文件名的一部分,找不到文件。
- 以为命令行与代码行为一致。中间差了一个 shell,行为就可能相反。
自测三问(点开对答案)
1. fontfile 与 movie 的路径规则为什么一样? —— 它们都是滤镜参数,走同一套解析:冒号分隔、正斜杠认路。 2. 为什么在代码里加单引号会失败? —— 没有 shell 去剥引号,引号成了文件名的字符。 3. 两行转义函数怎么写? —— 先把反斜杠换成正斜杠,再把冒号换成「反斜杠加冒号」。- ☐ 第六章完,下一节 7.1 音视频特效简介