🎬 第六章 FFmpeg命令行实现图片水印及文字跑马灯
📄 一句话概括

Windows 绝对路径直接塞进 movie 一定失败:盘符的冒号会被当成参数分隔符。 必须「反斜杠换正斜杠 + 冒号前加反斜杠」,两步一起做。

📝 本节和前面几节的关系

上一节用的是相对路径,所以没碰上这个坑。 一旦换成绝对路径——也就是实际做项目时的常态——就得处理它。

一、四种写法,只有一种能过

同一个文件,四种写法各试一次:

写法 送进去的字符串开头 结果


原样 I:\Desktop\… 失败 只换斜杠 I:/Desktop/… 失败 只转义冒号 I\:\Desktop\… 失败 斜杠加转义 I\:/Desktop/… 成功

失败的那三个报的都是 No such file or directory, 看起来像「文件不存在」,其实文件好好的。

二、为什么偏偏是冒号

滤镜的参数用冒号分隔:movie=文件名:loop=0:seek_point=1。 解析器从左往右扫,见到冒号就断。

I: 里那个冒号被当成了「参数到此结束」, 于是它拿着一个光秃秃的 I 去找文件,当然找不到。

转义成 I\: 之后,解析器才知道「这个冒号是路径的一部分」。 反斜杠也得换成正斜杠——这是 FFmpeg 自己的解析,不是 Windows 的 API。

两个坑叠在一起,所以必须同时处理。只做一步,还是失败。

三、相对路径为什么反而没事

在素材所在目录里写 movie=s5.mp4,一次就过。 因为字符串里没有冒号,解析器不会误断。

这就解释了「为什么照着别人的命令抄就是不行」: 写命令的人在 Linux 上,那里根本没有盘符,路径里也没有冒号。

换到 Windows,同一行命令凭空多出一个必须转义的字符。 而绝大多数教程压根不会提这件事。

四、一条能背下来的口诀

「先换斜杠,再转冒号,外面套单引号。」

前两步是滤镜的要求,第三步是命令行(shell)的要求,别混为一谈。 路径里有空格时,不套引号,空格之后的半截会被 shell 当成另一个参数。

在 Python 里直接调接口就没有 shell 这一层,加引号反而错—— 引号会变成文件名的两个字符。这一点在 6.6.3 还会再验一次。

五、动手 Demo:四种写法逐个试

把同一个文件的路径按四种方式拼出来,逐个交给 movie。 然后再切到文件所在目录,用相对路径试一次做对照。

import av, os

src = os.path.abspath(u"s5.mp4")
cand = [
    (u"原样", src),
    (u"只换斜杠", src.replace(u"\\", u"/")),
    (u"只转义冒号", src.replace(u":", u"\\:")),
    (u"斜杠加转义", src.replace(u"\\", u"/").replace(u":", u"\\:")),
]

for tag, p in cand:
    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 as e:
        print(u"%-12s 失败 %s" % (tag, str(e)[:34]))

old = os.getcwd()
os.chdir(os.path.dirname(src))
try:
    g = av.filter.Graph()
    m = g.add(u"movie", u"s5.mp4")
    s = g.add(u"buffersink")
    m.link_to(s)
    g.configure()
    s.pull()
    print(u"相对路径 s5.mp4 成功")
finally:
    os.chdir(old)

实跑输出:

--------------------------------------------------------------------
6.2.2 movie 里的绝对路径——冒号必须转义
--------------------------------------------------------------------

**同一个文件,四种写法,全试一遍**

  写法           送进去的字符串开头                  结果
  -----------------------------------------------------------
  原样           I:\Desktop\自我教学\.workbuddy…     失败
  只换斜杠       I:/Desktop/自我教学/.workbuddy…     失败
  只转义冒号     I\:\Desktop\自我教学\.workbudd…     失败
  斜杠加转义     I\:/Desktop/自我教学/.workbudd…     成功

**只有第四种成功,而且失败的那三个连「文件不存在」都不说清楚——**
**它们报的是一句笼统的 `No such file or directory`,让人以为是路径本身错了。**

**② 为什么偏偏是冒号**

**滤镜的参数用冒号分隔:`movie=文件名:loop=0:seek_point=1`。**
**解析器从头扫,见到冒号就断——`I:` 里的那个冒号被当成了「参数结束」。**
**于是它拿着 `I` 去找文件,当然找不到。转义成 `I\:` 才告诉它「这是路径的一部分」。**

**反斜杠也必须换成正斜杠:这里是 FFmpeg 自己的解析,不是 Windows 的 API,**
**它只认正斜杠。两个坑叠在一起,就必须同时处理。**

**③ 相对路径为什么反而没事**

  写法            结果     为什么
  -----------------------------------------------------
  s5.mp4          成功     不含冒号,解析器不会误断
  I:\…\s5.mp4     失败     盘符冒号被当成参数分隔

**这就是为什么很多人「照着别人命令抄就是不行」:**
**写命令的人在 Linux 上,那里根本没有盘符,路径里也没有冒号。**
**换到 Windows,同一行命令就多出一个必须转义的字符。**

**④ 一条能背下来的口诀**

**「先换斜杠,再转冒号,外面套单引号。」**

**第三步在命令行里尤其重要:`movie='I\:/a b/logo.png'`,**
**路径带空格时不套引号,空格之后的半截会被当成另一个参数。**

三次失败的报错里都有 No such file or directory,很容易被误导。 记住一句话:路径明明对却报「找不到」,先怀疑转义。

os.path.abspath 拿到的是 Windows 风格的反斜杠路径。 Python 里写这段要留神转义:u"\\\\" 是「两个字符」的反斜杠,别被自己绕晕。

最后那段 os.chdir 只是为了证明「相对路径能过」,真实项目里别这么写。 切工作目录会让程序的其他部分也受影响,用一个函数做完就够了。

30 秒实验

  1. 再试一次:把冒号转义写成 \: 会不会被 Python 吃掉?用 print(p) 看真实字符串。
  2. 带空格的目录:把文件放进 C:\a b\,看命令行与代码两种场景的差别。
  3. 换个滤镜:把 movie 换成 subtitles 或 movie,验证规则是否通用。

常见误区

  1. 只换斜杠不转冒号。报「找不到文件」,其实文件在那儿。
  2. 只转冒号不换斜杠。反斜杠在滤镜参数里不是路径分隔符。
  3. 以为加单引号就更安全。在代码里加引号反而会把文件名弄错。
自测三问(点开对答案) 1. 盘符的冒号为什么会被吃掉? —— 滤镜用冒号分隔参数,`I:` 的冒号被当成「参数到此结束」。 2. 正确的两步是哪两步? —— 反斜杠全部换成正斜杠;冒号前加一个反斜杠。 3. 为什么 Linux 教程从不提这件事? —— Linux 路径里没有盘符,也就没有冒号需要转义。

相关笔记