🎬 第四章 Qt播放音视频及Multimedia多媒体模块
📄 一句话概括

Qt Multimedia 自己不实现解码,它是一层「统一接口 + 各平台的后端插件」。 所以「能不能播」取决于后端和系统,不取决于 Qt。

📝 本节和前面几节的关系

第三章是自己动手:解码线程、QImage、定时器,每一步都看得见。 这一章反过来——一行 setSource 就完事,代价是你看不见里面。 这一节先把这层的结构摊开, 不然「为什么这个文件在我机器上能播、在客户机器上不能」永远解释不清。

一、三个层次:接口、插件、系统

从下往上看,一共三层:

层次 是什么 典型名字
应用接口 你写代码直接用的类 QMediaPlayer、QAudioOutput、QVideoWidget
后端插件 Qt 内部的可替换实现 windowsmediafoundation、gstreamer、ffmpeg
系统媒体栈 操作系统提供的解码与输出 WMF、AVFoundation、GStreamer

中间那层是理解一切的关键。 它意味着 QMediaPlayer 是一个「空壳」:你调 play(),它转手交给插件,插件再转手交给系统。

所以「Qt 支持什么格式」这个问法本身就不对。 正确的问法是「这台机器的后端支持什么格式」。

Qt 6.5 起情况有个变化:官方给 Multimedia 加了 FFmpeg 后端, 可以用环境变量 QT_MEDIA_BACKEND=ffmpeg 选它。 这条路把解码器打进 Qt 自己的二进制里,跟系统脱钩, 也就是绕开了第一层讲的「看系统」这个限制。但默认仍然走平台后端, 所以「默认情况下看系统」这句话依然成立。

二、后端决定能力,不是 Qt 决定能力

同一个 QMediaPlayer,换个平台就是换了一套底层:

平台 Qt 5 的后端 Qt 6 的后端
Windows DirectShow,或改用 WMF WMF(Media Foundation)
macOS AVFoundation AVFoundation
Linux GStreamer GStreamer

Qt 5 那格的「或改用 WMF」是个环境变量: QT_MULTIMEDIA_PREFERRED_PLUGINS=windowsmediafoundation。 这个细节说明「后端是可换的」不是空话,只是换法不统一、也不在公开 API 里。

Qt 6 把这层统一了:只留 WMF。 DirectShow 那套 API 微软自己都不再维护了, Qt 6 干脆砍掉——代价是有些在 DirectShow 下能播的老格式,到 Qt 6 就播不了了。

最典型的例子是 HEVC:Windows 10 / 11 自带的解码器里没有 HEVC, 要装「HEVC 视频扩展」(商店里那个)。 没装的话,QMediaPlayer 拿到的就是 InvalidMedia,而且错误信息只有一句「无法播放」, 没有任何线索。

这就是 Demo 里那张表的用处:同一个编码, FFmpeg 那条路「编译进来就有」,QMediaPlayer 那条路「要系统支持」。 差距全在这两句话里。

三、三条路的分工

做播放器一共三条路,没有哪条最好,只有哪条适合:

维度 Qt Multimedia LibVLC FFmpeg 自建
代码量 三行 二十行 几百行
格式覆盖 看系统 几乎全覆盖 你编多少有多少
能否取到单帧 较难,走 QVideoSink 回调可取 最直接
自定义渲染 基本不行 可以 完全可控
依赖体积 随 Qt 一起 几十 MB 只挑需要的库
部署成本 零 要带 dll 要带一堆 dll

第三行「能否取到单帧」是最该先问的一句。 要做视频分析——人脸、车牌、AI 推理、截图——都必须能拿到一帧的像素。QMediaPlayer 在这件事上一直很别扭,Qt 6 的 QVideoSink 算是补上了,但拿到的是 QVideoFrame,还要自己做格式转换,比 FFmpeg 那套绕。

所以选型的判断顺序是: 只放常规格式、要零部署成本 → Qt Multimedia。 要格式全覆盖、要能取帧、不想自己搭解码 → LibVLC。 要逐帧处理、要接 GPU、要做滤镜 → FFmpeg 自建。

最常见的翻车是第一种:内部工具用 QMediaPlayer 两周做完, 交给客户之后收到「这个文件放不了」的投诉, 而且在你机器上复现不了——因为你机器上装着 HEVC 扩展,客户机器上没装。

四、动手 Demo:QMediaPlayer 能不能播,看后端而不是看 Qt

这段把三平台的后端列出来,再查本机 FFmpeg 那边的解码器清单,对照「系统自带支持吗」。 两张表合起来就是一句话:同一个编码,两条路的能力来源完全不同。

import av

# FFmpeg 这条路:解码器是编译进来的
for name in ("h264", "hevc", "aac", "mp3"):
    print(name, "在" if name in av.codecs_available else "不在")

# QMediaPlayer 这条路:能力等于系统后端
# 代码里查不出来,只能看平台文档
backend = {
    "Windows": "WMF(Media Foundation)",
    "macOS":   "AVFoundation",
    "Linux":   "GStreamer",
}
for k, v in backend.items():
    print(k, v)

实跑输出:

--------------------------------------------------------------------
QMediaPlayer 能不能播,看后端而不是看 Qt
--------------------------------------------------------------------
**`QMediaPlayer` 只是一个接口,真正干活的是「后端插件」。**
**所以在 Windows 上它能不能放 HEVC,取决于系统装没装 HEVC 扩展。**

**四个平台的后端各不相同**:

  平台        Qt 5 的后端           Qt 6 的后端
  -------------------------------------------------------------
  Windows     DirectShow 或 WMF     WMF(Media Foundation)
  macOS       AVFoundation          AVFoundation
  Linux       GStreamer             GStreamer

**「看后端」这句话的具体含义**:Qt 自己不实现解码,它把活转给系统。
**所以「这台机器能不能播」= 「这台机器的系统解码器支不支持」。**

**本机(PyAV/FFmpeg 那条路)能用的解码器里,四个常见的在不在**:

  编码      本机 FFmpeg     Windows 系统自带
  ----------------------------------------------
  H.264     在              支持
  HEVC      在              需装扩展
  AAC       在              支持
  MP3       在              支持

**这张表说明「两条路的差距在哪」**:
  **FFmpeg 这条路**:解码器编译进来就有,**跟系统无关**。
  **QMediaPlayer 这条路**:能力等于系统,**换机器就变**。

**所以选型的判断很简单**:
  **内部工具、格式固定** → QMediaPlayer,代码三行,部署零成本。
  **给客户装机、格式不确定** → LibVLC 或自建 FFmpeg 链路,
  **否则你会收到「某个文件放不了」的投诉,而且在你机器上复现不了。**

上面那张四行表是本机查出来的:h264、hevc、aac、mp3 四个解码器全在, 因为 PyAV 带的是完整的 FFmpeg 库。

下面那张四行表是「查不出来」的——这是本节最该记住的一点: QMediaPlayer 没有提供「你支持哪些格式」的接口。你只能 try:设一个 media, 看 mediaStatus 是不是 InvalidMedia。

所以「支持格式列表」这种功能,在 Qt Multimedia 上做不干净。 LibVLC 有 libvlc_media_player_get_tracks 之类的接口, FFmpeg 那边更是随时能查 avcodec_find_decoder。这个差异,就是「底层自主权」的直接后果。

最后那句「看平台文档」不是偷懒:WMF 支持的格式取决于系统装了什么解码器, 而系统装了什么,取决于这台机器之前装过什么软件。 这就是「在我机器上能播」这类问题的根源。

30 秒实验

  1. 在 Windows 上把一个 HEVC 文件交给 QMediaPlayer,看 mediaStatus 是什么、 errorString 说了什么。
  2. 算一下:LibVLC 的 dll 加起来几十 MB,Qt 的 Multimedia 插件才几 MB,差距换来了什么?
  3. 判断一下:你的项目需要「取一帧做截图」,三条路里该选哪条?

常见误区

  1. 以为 QMediaPlayer 自带解码器。 它只是接口,真正干活的是后端插件与系统栈。 换台机器能力就可能变。
  2. 用 supportedMimeTypes() 判断能不能播某个本地文件。 它给的是 MIME 列表, 不等于「这个具体文件的编码能解」。要真去设一次 media 看状态。
  3. 在 Qt 6 上照抄 Qt 5 的代码。 setMedia(QMediaContent(...)) 没了, setVolume() 搬到了 QAudioOutput,QMediaPlaylist 整个删了。
  4. 把「播不了」归因于 Qt 版本或者 bug。 九成是缺系统解码器(HEVC 扩展最典型), 先装扩展或者换后端再判断。
自测三问(点开对答案) 1. `QMediaPlayer` 为什么在不同平台上支持的格式不一样? —— 因为它不实现解码,把活转给了后端插件与系统媒体栈:Windows 是 WMF、macOS 是 AVFoundation、 Linux 是 GStreamer。 2. Qt 6.5 之后多了哪条路可以绕开系统解码器? —— 官方新增的 FFmpeg 后端,用 `QT_MEDIA_BACKEND=ffmpeg` 选中; 解码器打进 Qt 自己的二进制,跟系统脱钩。 3. 三条路的选型,第一句该问什么? —— 「将来需不需要拿到单帧图像」。需要就基本只能在 LibVLC 回调和 FFmpeg 自建里选。

框架讲清了,下面三节是三个具体的东西:一个播放器、一个音乐播放器、一个视频播放器。 先说最小的那个——它只有三步。

相关笔记