目录
[1. 核心比喻:快递包裹](#1. 核心比喻:快递包裹)
[2. 关键知识点详解](#2. 关键知识点详解)
[2.1. 容器 (Container) vs. 编码 (Encoding)](#2.1. 容器 (Container) vs. 编码 (Encoding))
[2.2. 流 (Stream) 与 解复用 (Demultiplexing)](#2.2. 流 (Stream) 与 解复用 (Demultiplexing))
[2.3. 核心参数 (Stream Parameters)](#2.3. 核心参数 (Stream Parameters))
[2.4. 时间基 (Time Base / time_base)](#2.4. 时间基 (Time Base / time_base))
[2.5. PTS / DTS](#2.5. PTS / DTS)
PTS (Presentation Timestamp / G/示时间戳)
DTS (Decoding Timestamp / 解码时间戳)
[🧩 一句话总览](#🧩 一句话总览)
[📦 容器(Container)](#📦 容器(Container))
[🎞 流(Stream)](#🎞 流(Stream))
[⚙️ 编码(Codec)](#⚙️ 编码(Codec))
[⏱ 时间戳(PTS / DTS)](#⏱ 时间戳(PTS / DTS))
[✅ 总结一句话](#✅ 总结一句话)
本文档详细解释了"阶段 I:本地播放与时钟同步"中第一周"容器与解复用"所涉及的核心知识点。理解这些概念是使用 FFmpeg 进行任何音视频开发(包括播放器、转码器、分析工具)的基石。
1. 核心比喻:快递包裹
理解音视频文件,最简单的比喻就是收发快递:
-
视频/音频裸数据(H.264, AAC): 这是你购买的商品,比如一件毛衣或一部手机。它们是真正有内容的东西。
-
编码 (Encoding): 为了方便运输,商品需要被压缩打包 。比如毛衣被抽真空,手机装在防震泡沫里。这就是 H.264 (视频编码) 或 AAC (音频编码) 所做的事情------它们是压缩原始音视频数据的算法。
-
容器 (Container - MP4, MKV): 这是快递盒子 。这个盒子里可以同时装下被压缩的毛衣(视频)和手机(音频),甚至还有一张说明书(字幕)。MP4 , MKV , FLV 就是这种"盒子",它们规定了如何把不同的数据"装"进去。
-
解复用 (Demuxing): 这就是你拆快递的过程。你打开盒子(容器),把里面的毛衣(视频流)和手机(音频流)分开,准备单独使用。
2. 关键知识点详解
2.1. 容器 (Container) vs. 编码 (Encoding)
这是最容易混淆,也最重要的概念。
容器 (Container)
-
作用: "打包"和"组织"。它是一个外壳,定义了如何将一个或多个编码后的数据流(视频、音频、字幕等)交织存储在一个单一的文件中。
-
它包含什么:
-
数据流 (Streams): 至少一个视频流和一个音频流。
-
元数据 (Metadata): 关于文件本身的信息(如标题、作者、封面图)和关于数据流的信息(如视频分辨率、音频采样率)。
-
索引/时间信息: 用于播放器快速定位(Seek)到特定时间点。
-
-
常见例子:
.mp4,.mkv(Matroska),.mov,.flv,.ts(MPEG-TS)。 -
FFmpeg 函数:
avformat_open_input就是用来"打开容器"的函数。avformat_...系列函数主要处理容器层面的操作(解复用)。
编码 (Encoding / Codec)
-
作用: "压缩"。它是一种算法(即 Co der-Decoder,编解码器),用于压缩原始的、巨大的音视频数据,使其变得足够小,易于存储和网络传输。
-
它做什么:
-
视频编码: 利用人眼的视觉特性(如对亮度比对色度敏感,对动态画面感知模糊等)和图像帧之间的相似性来移除冗余数据。
-
音频编码: 利用人耳的听觉特性(心理声学模型)来移除人耳听不到或不敏感的声音数据。
-
-
常见例子:
-
视频: H.264 (AVC), H.265 (HEVC), VP9, AV1。
-
音频: AAC, MP3, Opus, FLAC (无损)。
-
-
FFmpeg 函数:
avcodec_find_decoder是用来"查找解码器"的。avcodec_...系列函数主要处理编码层面的操作(解码)。
总结: 一个 .mp4 文件不是 一种视频格式。它是一个容器 ,里面可能 装着一个 H.264 编码的视频流和一个 AAC 编码的音频流。你的 MediaProbe 工具的任务之一,就是打开这个 mp4 盒子,然后告诉用户:"嘿,这个盒子里装的是 H.264 视频和 AAC 音频"。
2.2. 流 (Stream) 与 解复用 (Demultiplexing)
-
流 (Stream): 一个连续的数据序列,代表一种特定类型的内容。在一个媒体文件中:
-
通常有一个视频流 (
AVMEDIA_TYPE_VIDEO)。 -
通常有一个或多个音频流 (
AVMEDIA_TYPE_AUDIO),例如对应不同语言(国语、英语)或不同声道(立体声、5.1声道)。 -
可能有零个或多个字幕流 (
AVMEDIA_TYPE_SUBTITLE)。 -
可能有其他数据流 ,如封面图(
AV_DISPOSITION_ATTACHED_PIC)。
-
-
解复用 (Demuxing):
-
当你用
avformat_open_input打开一个文件时,FFmpeg 会读取文件的头部信息,识别出这是一个什么容器(如 MP4)。 -
当你调用
avformat_find_stream_info时,FFmpeg 会进一步读取文件开头的一小部分数据,分析并填充每个"流"的详细信息。 -
这些信息被存储在一个名为
AVFormatContext的结构体中,它里面有一个streams数组(AVStream**),数组的每个元素 (AVStream*) 就代表一个流。 -
你的
MediaProbe工具就是要遍历这个streams数组,提取并展示每个AVStream中的参数。
-
2.3. 核心参数 (Stream Parameters)
你的任务是列出这些参数,它们都可以在 AVStream 及其关联的 AVCodecParameters 结构中找到:
-
编解码器 (Codec):
codecpar->codec_id。例如AV_CODEC_ID_H264。 -
分辨率 (Resolution):
codecpar->width和codecpar->height。(仅视频) -
帧率 (Frame Rate):
avg_frame_rate。这是一个分数(AVRational),比如30000/1001(即 29.97 fps)。 -
采样率 (Sample Rate):
codecpar->sample_rate。例如 44100 Hz 或 48000 Hz。(仅音频) -
码率 (Bitrate):
codecpar->bit_rate。每秒的数据量,如 5000000 (bps) = 5000 (kbps)。 -
时长 (Duration):
duration。这是一个整数,单位是time_base。
2.4. 时间基 (Time Base / time_base)
这是 FFmpeg 中最重要、也最基础的概念。
-
是什么: 时间基(
time_base)是一个分数 (AVRational),它定义了时间戳的单位。 -
为什么需要它: 在媒体处理中,我们从不 使用浮点数(如 2.5 秒)来记录时间,因为这会产生累积的精度误差。相反,我们使用整数"滴答数"(ticks)来记录时间戳。
-
time_base就是 "1 滴答" 等于 "多少秒"。 -
公式:
时间(秒) = 时间戳(整数) * time_base -
示例:
-
一个流的
time_base是1/90000(一种常见的值)。 -
这个流中一个数据包的
pts(见下文)是180000。 -
那么这个数据包对应的G/示时间就是:
180000 * (1 / 90000) = 2.0秒。
-
-
注意: 不同的流(甚至容器本身)可能有不同的
time_base。AVStream中就有自己的time_base。在进行时间计算时,你必须使用对应流的time_base。 -
时长计算:
AVStream->duration是一个整数,单位是AVStream->time_base。-
总时长(秒) = stream->duration * av_q2d(stream->time_base) -
(
av_q2d是 FFmpeg 提供的将AVRational转换为double的辅助函数)
-
2.5. PTS / DTS
PTS 和 DTS 是存储在每个音视频数据包 (AVPacket) 上的时间戳 。它们都是整数,单位就是它们所属流的 time_base。
PTS (Presentation Timestamp / G/示时间戳)
-
含义: "这个数据包(解码后)应该在什么时候被G/示给用户"。
-
作用: 这是播放器用于音视频同步 的唯一 依据。播放器会努力确保在 PTS 时间点
T,G/示第N帧视频,并播放第M块音频。 -
对于音频流和没有 B 帧的视频流,PTS 总是单调递增的。
DTS (Decoding Timestamp / 解码时间戳)
-
含义: "这个数据包应该在什么时候被送入解码器"。
-
为什么需要它(B 帧的存在):
-
现代视频编码(如 H.264)为了追求高压缩率,引入了 B 帧(双向预测帧)。
-
B 帧需要参考它前面 和后面的帧才能解码。
-
例子:
-
G/示顺序 (PTS): I(1), B(2) , B(3), P(4)
-
为了解码
B(2)和B(3),解码器需要先有I(1)和P(4)。 -
因此,
P(4)必须在B(2)和B(3)之前被解码。 -
解码顺序 (DTS) 必须是: I(1), P(4) , B(2) , B(3)
-
-
-
总结:
-
数据包在文件中是按 DTS 顺序存储的。
-
解复用器 (
av_read_frame) 按 DTS 顺序把包读出来。 -
解码器按 DTS 顺序解码。
-
解码后,播放器根据 PTS 重新排序,然后在正确的时间G/示。
-
-
重要规则: 对于没有 B 帧的流(包括所有音频流),
DTS == PTS。
非常棒 👍
这里是一句一句、层层递进的简明总结,帮你彻底理清:
🧩 一句话总览
容器装流,流含编码数据,时间戳定义播放顺序。
📦 容器(Container)
-
像一个打包盒(MP4、MKV、FLV...)
-
作用:组织并存放多条媒体流(音频、视频、字幕)
-
提供:索引、时长、时间戳、同步信息
🎞 流(Stream)
-
是容器里的一条轨道:视频流、音频流、字幕流
-
每个流都有自己的参数(分辨率、采样率、编码方式)
-
FFmpeg 中对应结构:
AVStream
⚙️ 编码(Codec)
-
是"流"里的压缩格式:H.264、AAC、VP9 等
-
决定数据如何压缩与解码
-
FFmpeg 中对应结构:
AVCodec/AVCodecParameters
⏱ 时间戳(PTS / DTS)
-
用来标记每帧的时间位置
-
PTS:什么时候显示
-
DTS:什么时候解码
-
用
time_base把整数时间戳换算成秒,实现音视频同步
✅ 总结一句话
容器管理多流,流使用编码,时间戳用来同步播放。