多流文件里挑一路看,写法就三种:类型、类型+序号。 选不到时不报错,直接打空——「没输出」本身就是答案。
3.3.5 讲怎么排版,这一节讲怎么限定范围。 3.4 起回到 ffmpeg 自己的选项,讲通用、视频、音频、字幕。
一、三种写法
选择语法很短,但有一个容易记错的地方:
写法 含义 例子
v 第一条视频流 -select_streams v a 第一条音频流 -select_streams a v:1 第二条视频流 -select_streams v:1
容易记错的是第三行:v:1 是「第二条视频」,不是「编号为 1 的流」。
序号是按同类数下来的,跟全局 index 无关。
这一点和 -map 0:v:1 是一致的:两处的第二段都是「同类里的第几条」。
所以从 ffprobe 转到 ffmpeg 时,这个序号可以直接搬。
二、选不到的时候不报错
这一条初学者最容易困惑:选了一个不存在的流,ffprobe 什么都不打,退出码还是 0。 不报错、不警告,就像命令跑成功了但没有结果。
情况 输出 退出码
选到了 正常内容 0 选不到 空 0 文件读不了 报错信息 1
所以「空输出」和「出错」是两回事,脚本里要分开判断: 先看退出码排除「读不了」,再看输出是否为空排除「选不到」。
这个设计有它的道理:ffprobe 是查询工具,「查不到」不算失败。 但对自动化来说就是个坑——空字符串往下传,可能在很远的地方才炸。
三、包层和帧层都能这么选
-select_streams 对三个 show_* 都有效:
配合的参数 效果 用途
-show_packets 只打这一路的包 看时间戳,不受另一路干扰 -show_frames 只打这一路的帧 数帧数,不用过滤 -show_streams 只打这一条流的档案 输出短,好读
不加这个参数时,两路的包会混在一起输出,靠 codec_type 那行才分得开。
所以看时间戳一定要加 -select_streams v,否则视频音频的 pts 混着看就乱了。
第三行是最实用的一处:多流文件的 -show_streams 输出很长,
只关心视频时加一个 v,输出立刻短一半。
四、动手 Demo:多流文件里挑一路看
用一个双流文件,把「三种选法各拿到什么」摆出来,并验证选不到时的行为。 最后统计两路的包数差多少。
import av
c = av.open(u"av3.mp4")
print(u"全部流:", [(s.index, s.type) for s in c.streams])
for tag in (u"video", u"audio"):
got = [s.index for s in c.streams if s.type == tag]
print(u"%s 共 %d 条,编号 %s" % (tag, len(got), got))
# 选不到的情况:只有 1 条视频,v:1 不存在
n = len(c.streams.video)
print(u"要视频第 2 条(v:1):%s" % (u"有" if n > 1 else u"没有"))
c.close()
# 两路的包数
c = av.open(u"av3.mp4")
nv = na = 0
for pk in c.demux():
if not pk.size:
continue
if pk.stream.type == u"video":
nv += 1
else:
na += 1
c.close()
print(u"视频包 %d,音频包 %d" % (nv, na))
实跑输出:
--------------------------------------------------------------------
ffprobe -select_streams——多流文件里挑一路看
--------------------------------------------------------------------
**① 选择语法就三种写法**
写法 含义 例子
-------------------------------------------------
v 第一条视频流 -select_streams v
a 第一条音频流 -select_streams a
v:1 第二条视频流 -select_streams v:1
**字母后面不加数字就是「第一条」,加了数字是按同类顺序数,不是按全局编号。**
**这个区别很关键:`v:1` 是「第二条视频」,不是「编号为 1 的流」。**
**② 本机这个双流文件,三种选法各拿到什么**
写成 想选什么 实际选到 存在吗
-----------------------------------------------
v 第一条视频 编号 0 有
a 第一条音频 编号 1 有
v:1 第二条视频 无 没有
a:1 第二条音频 无 没有
**本机这个文件只有 1 条视频、1 条音频,所以带 `:1` 的写法必然选不到东西。**
**选不到时 ffprobe 不报错,直接打空——所以「没输出」本身就是一种答案。**
**③ 包里也能这么选**
**`-show_packets -select_streams v` 只打视频包,`a` 只打音频包。**
**不加这个参数时两路混在一起输出,`grep codec_type` 才分得开。**
取哪一路 包数
---------------------
视频 25
音频 45
**两路包数差很多是正常的:视频一秒切 25 个,音频一秒才切十几二十个。**
**所以混着输出时,你会看到包的类型一条一条交替跳。**
第一行输出的 (index, type) 就是 Stream #0:x 那个编号和类型。
-select_streams v 选的是「第一条 video」,不一定 index=0——
有些文件音频在前,视频的 index 反而是 1。
最后一行两路包数差很多是正常的: 视频一秒切 25 个,音频一秒才切十几二十个(AAC 一帧 1024 个样本)。 所以混着输出时,包的类型会一条一条交替跳。
这里没有演示「选不到时的空输出」,因为要演示它得走命令行。
库这一层直接用 c.streams.video 取不到就是空列表,比命令行更直白。
30 秒实验
- 只看视频流:
ffprobe -show_streams -select_streams v 片子.mp4,输出立刻短一半。 - 只看视频包:
ffprobe -show_packets -select_streams v 片子.mp4,时间戳不乱。 - 试一次空:
ffprobe -show_streams -select_streams a 纯视频.mp4,看它是不是静悄悄地什么也不打。
常见误区
- 把
v:1当成「编号 1 的流」。它是「第二条视频」,跟全局 index 无关。 - 用「输出为空」判断文件坏了。可能只是没选到流,退出码仍是 0。要分开判断。
- 看时间戳不加
-select_streams v。视频音频的包混在一起,pts 不连续,看着像乱序。
自测三问(点开对答案)
1. `-select_streams v:1` 是什么意思? —— 第二条视频流。序号按同类数,跟全局 index 无关。 2. 选了一个不存在的流,ffprobe 会报错吗? —— 不会。它输出为空,退出码 0。所以脚本里要同时判断退出码和输出是否为空。 3. 为什么看时间戳一定要加 `-select_streams v`? —— 不加的话视频音频的包混着输出,两路的 pts 各按各的时间基算,看起来像乱序。 4. 一个文件里音频在视频之前,第一条视频的 index 是多少? —— 是 1。`-select_streams v` 选的是「第一条视频」,按类型数,不是按全局 index 数。- ☐ (本节完,下一节 3.4 通用选项)