🎬 第三章 FFmpeg三大常用工具及应用选项详解
📄 一句话概括

多流文件里挑一路看,写法就三种:类型、类型+序号。 选不到时不报错,直接打空——「没输出」本身就是答案。

📝 本节和前面几节的关系

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 秒实验

  1. 只看视频流:ffprobe -show_streams -select_streams v 片子.mp4,输出立刻短一半。
  2. 只看视频包:ffprobe -show_packets -select_streams v 片子.mp4,时间戳不乱。
  3. 试一次空:ffprobe -show_streams -select_streams a 纯视频.mp4,看它是不是静悄悄地什么也不打。

常见误区

  1. 把 v:1 当成「编号 1 的流」。它是「第二条视频」,跟全局 index 无关。
  2. 用「输出为空」判断文件坏了。可能只是没选到流,退出码仍是 0。要分开判断。
  3. 看时间戳不加 -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 数。

相关笔记