使用os.execvp调用ffmpeg时,长filter_complex字符串无法被正确识别
我在编写Python脚本调用ffmpeg拼接多个视频片段时遇到了异常:给filter_complex参数的字符串加引号后代码能正常工作,但不加引号时,ffmpeg会把整个滤镜字符串拆成多个参数解析。
正常工作的代码
inputs = sorted(list(askopenfilenames())) n = len(inputs) filter = [] for i in range(n): filter.append("[{}:v]".format(i)) filter.append("[{}:a]".format(i)) filter.append("concat={}:v=1:a=1".format(n)) filter.append("[v]") filter.append("[a]") filter = " ".join(filter) fargs = zip(itertools.repeat('-i'), inputs) fargs = itertools.chain( ["ffmpeg"], itertools.chain.from_iterable(fargs), ["-filter_complex", '"{}"'.format(filter), "-vsync", "vfr", "-map", "[v]", "-map", "[a]"], ["-c:v", "libx264", "-crf", "{}".format(quality)], ["-c:a", "aac", "-b:a", "192k"], [out] ) os.execvp("ffmpeg", list(fargs))
异常情况的代码片段
fargs = itertools.chain( ["ffmpeg", "-loglevel", "debug"], itertools.chain.from_iterable(fargs), #["-filter_complex", '"{}"'.format(filter), "-vsync", "vfr", "-map", "[v]", "-map", "[a]"], ["-filter_complex", filter, "-vsync", "vfr", "-map", "[v]", "-map", "[a]"], ["-c:v", "libx264", "-crf", "{}".format(quality)], ["-c:a", "aac", "-b:a", "192k"], [out] )
ffmpeg错误日志
Reading option '-filter_complex' ... matched as option 'filter_complex' (create a complex filtergraph) with argument '[0:v]'.
Reading option '[0:a]' ... matched as output url.
Reading option '[1:v]' ... matched as output url.
Reading option '[1:a]' ... matched as output url.
Reading option '[2:v]' ... matched as output url.
Reading option '[2:a]' ... matched as output url.
Reading option 'concat=3:v=1:a=1' ... matched as output url.
Reading option '[v]' ... matched as output url.
Reading option '[a]' ... matched as output url.
参数列表打印结果
['ffmpeg', '-loglevel', 'debug', '-i', 'a.mp4', '-i', 'b.mp4', '-i', 'c.mp4', '-filter_complex', '[0:v] [0:a] [1:v] [1:a] [2:v] [2:a] concat=3:v=1:a=1 [v] [a]', '-vsync', 'vfr', '-map', '[v]', '-map', '[a]', '-c:v', 'libx264', '-crf', '20', '-c:a', 'aac', '-b:a', '192k', 'asdf.mp4']
原因解析
问题出在两个点:
- ffmpeg参数解析逻辑:ffmpeg对
filter_complex的参数解析会特殊处理内部的空格,即使通过os.execvp传递的是单个字符串参数,ffmpeg仍会把字符串内的空格当作参数分隔符,将原本的滤镜图拆分成多个独立参数。 - 滤镜图语法错误:你用空格分隔滤镜图的元素,这不符合ffmpeg滤镜图的规范语法——滤镜元素之间应该用逗号分隔,而非空格。
加引号能临时解决问题,是因为ffmpeg会将引号内部的内容视为完整的单个参数,忽略内部空格,但这属于临时 workaround,并非规范用法。
正确解决方法
修改滤镜字符串的构建逻辑,用逗号代替空格拼接元素:
filter = [] for i in range(n): filter.append("[{}:v]".format(i)) filter.append("[{}:a]".format(i)) filter.append("concat={}:v=1:a=1".format(n)) filter.append("[v]") filter.append("[a]") # 用逗号替代空格拼接滤镜元素 filter = ",".join(filter)
之后直接传递filter参数即可,无需添加引号:
["-filter_complex", filter, "-vsync", "vfr", "-map", "[v]", "-map", "[a]"],
这样ffmpeg就能正确识别整个滤镜图为单个参数,不会拆分内部元素,同时符合ffmpeg的语法规范。
内容的提问来源于stack exchange,提问作者incertia

