视频编码知识
数据类型谁负责生成存储位置作用SPS/PPS编码器芯片 (RKMPP)码流头部 / Extradata告诉解码器如何初始化硬件IDR 帧编码器芯片 (RKMPP)关键帧 NALU视频流的即时刷新点DTSFFmpeg 封装层AVPacket确定解码器什么时候“拆包”PTS你的代码逻辑确定画面什么时候“上屏”在代码中打印pkt->pts。如果发现pts。
1. IDR 帧与普通 I 帧的区别
所有的 IDR 帧都是 I 帧,但不是所有的 I 帧都是 IDR 帧。
-
普通 I 帧:是一个全帧压缩图像,不依赖其他帧就能解码。但是,在它之后的 P 帧或 B 帧可以跨过这个 I 帧去参考它之前的帧。
-
IDR 帧:它会“清空”解码器的参考帧队列。这意味着,IDR 帧之后的任何帧都绝对不能参考该 IDR 帧之前的任何帧。
2. IDR 帧的两个核心作用
A. 解码器的“重置键”
当解码器遇到 IDR 帧时,它知道之前所有的参考数据都不再需要了。这防止了错误无限传递。如果视频流因为网络抖动丢包了,画面会花屏,直到下一个 IDR 帧 出现,画面才会恢复正常。
B. 随机访问与切入点
视频流的“快进”或“重新连接”只能从 IDR 帧开始。
-
为什么你的拉流端有时报
no one is publishing或timeout? 如果推流端发送数据的频率太低,或者 IDR 帧间隔(GOP)设置得太大(比如 10 秒才一个),拉流端在接入时可能要等很久才能拿到第一个 IDR 帧和对应的 SPS/PPS,从而导致连接超时断开。
ffmpeg -re -i input.mp4 -c:v libx264 -g 30 -force_key_frames "expr:gte(t,n_forced*1)" -f rtsp rtsp://...
-
-g 30:每 30 帧设一个 GOP。 -
force_key_frames:强制每秒产生一个关键帧(IDR 帧)。
以下是关于视频传输基础知识的详细讲解:
3. 时间戳:PTS 与 DTS
视频帧在传输过程中并不是简单地“发一帧播一帧”,因为为了压缩效率,视频帧的解码顺序和显示顺序往往是不一致的。
-
PTS (Presentation Time Stamp, 显示时间戳):
-
定义:告诉播放器这一帧应该在什么时间点展示给用户看。
-
作用:确保音视频同步(AV Sync)。播放器会对比音频 PTS 和视频 PTS,决定是否需要跳帧或等待。
-
-
DTS (Decoding Time Stamp, 解码时间戳):
-
定义:告诉解码器这一帧应该在什么时间点进行解码。
-
必要性:在使用 B 帧(双向预测帧)时,解码器必须先解码后面的 P 帧才能解码前面的 B 帧,因此 DTS 会早于 PTS。
-
4. 时间基 (Time Base)
时间戳(PTS/DTS)本身通常不是以“秒”为单位的整数,而是一个刻度数。要转换成秒,必须配合 Time Base。
-
概念:
时间(秒) = PTS × Time Base。 -
示例:如果系统的
Time Base是1/90000(这是 RTSP/封装格式中常见的频率),而某一帧的PTS是90000,那么它的显示时间就是90000 * (1/90000) = 1秒。 -
在 RK3588 开发中的意义:如果你在代码中直接把
1, 2, 3赋值给 PTS 却发现推流失败,通常是因为你的Time Base设置得很大(如 90000),导致服务器认为你的帧间隔只有几万分之一秒,从而判定为异常。
5. NALU:视频流的“原子”单位
H.264/H.265 的数据流是由一个个 NALU (Network Abstraction Layer Unit) 组成的。
-
起始码 (Start Code):在 Annex B 格式中,每个 NALU 以
00 00 00 01或00 00 01开头。 -
NALU 类型:
-
SPS (序列参数集):包含分辨率、配置等级等全局信息。
-
PPS (图像参数集):包含具体图像的解码参数。
-
IDR 帧:即即时解码刷新帧,是解码的起点。
-
SEI:补充增强信息,有时用于传输元数据(如摄像头旋转角度)。
-
6. 视频帧类型:GOP 结构
视频压缩通过 GOP (Group of Pictures) 来实现,一个 GOP 是指两个 I 帧之间的连续帧序列。
-
I 帧 (Intra-coded):全帧压缩,类似一张 JPEG 图片,解码不依赖其他帧。
-
P 帧 (Predictive-coded):前向预测帧,只记录与前一帧的差异。
-
B 帧 (Bi-predictive):双向预测帧,参考前后两帧。注意:实时通信(如你的拼接项目)通常会禁用 B 帧来降低延迟。
7. 视频传输的“来龙去脉”(封装与协议)
视频压缩好了,但它只是一堆乱糟糟的数学数据。要传输它,需要分层处理:
第一步:NALU (原子层)
将压缩后的数据切成一个个小包。
-
SPS/PPS:说明书,告诉解码器分辨率是多少。
-
IDR 帧:关键点,告诉解码器从这里可以开始工作。
第二步:容器 (Container)
把视频包和音频包像“包饺子”一样裹在一起。
-
常见格式:MP4, MKV, FLV, TS。
第三步:协议 (Protocol) —— 你的 RTSP 报错就在这
容器是静态的,协议是动态的。
-
RTSP/RTMP:相当于“物流快递”。它负责握手、建立连接、控制播放/暂停。
-
PTS/DTS:相当于包上的“快递单号”。
-
DTS (解码时间戳):告诉解码器什么时候拆包。
-
PTS (显示时间戳):告诉屏幕什么时候把画贴出来。
-
8 pts dts的时间戳时干嘛的 按时间播放什么意思
在视频传输中,PTS 和 DTS 的存在是因为视频并不是按照“排队领盒饭”的简单顺序处理的。为了让视频体积更小,人们发明了复杂的“跳跃式”参考机制,这导致了解码顺序和显示顺序的脱节。
1. 为什么要分 PTS 和 DTS?
视频帧分为 I、P、B 三种类型。问题的核心在于 B 帧(双向参考帧)。
-
DTS (Decoding Time Stamp):解码时间戳。告诉解码器什么时候该把这一帧数据“拆包”并还原成图像像素。
-
PTS (Presentation Time Stamp):显示时间戳。告诉播放器什么时候该把这一帧图像“贴到屏幕上”给用户看。
为什么它们不一样? 想象你要组装一个模型,B 零件需要参考 A 零件和 C 零件。那么:
-
你必须先拿到 A 和 C(解码顺序/DTS)。
-
即使你先拿到了 C,你也得等 B 组装好后,按顺序摆放 A -> B -> C(显示顺序/PTS)。
在没有 B 帧的情况下(比如很多低延迟监控流),DTS 和 PTS 通常是相等的。但只要有 B 帧,DTS 就会比 PTS 提前,因为解码器必须“未雨绸缪”,先解码后面的参考帧。
2. “按时间播放”是什么意思?
“按时间播放”本质上是**将视频帧从“数学刻度”还原为“人类感知时间”**的过程。
视频文件里存储的 PTS 并不是“00:00:01”这样的文字,而是一个计数器数值(类似于时钟的滴答数)。
A. 时间基 (Time Base) 的概念
由于计算机处理速度很快,秒这个单位太大了。视频系统会把 1 秒切成很多份,这就是 Time Base(时间基)。
-
常见的 RTSP 时间基是
1/90000。 -
这意味着 1 秒钟被切成了 90,000 份。
B. 还原公式
播放器看到的逻辑是:
播放时间 (秒)=PTS×Time Base
举个例子: 如果一帧视频的 PTS 是 180000,时间基是 1/90000。 播放器计算:180000×(1/90000)=2.0。 那么播放器就会等系统时钟走到第 2.0 秒的那一刻,把这帧画面刷到屏幕上。
在视频开发的整条流水线中,这些关键的元数据(SPS/PPS, PTS/DTS)是由不同的环节逐层添加上去的。
我们可以把这个过程看作一个“物流工厂”:
1. 谁添加了 SPS 和 PPS?(配置说明书)
这是由 编码器 (Encoder) 产生的。
-
在你之前的命令中:当你调用
-c:v h264_rkmpp时,瑞芯微的硬件编码器芯片在初始化时,会根据你设置的分辨率(1920x1080)和码率(4M)计算出这套“说明书”。 -
传输过程:编码器通常只在最开始生成一次。由于你后来添加了
-bsf:v dump_extra,FFmpeg 的比特流过滤器会在每一个 IDR 帧前强行把这些数据“复印”一份插进去。
2. 谁添加了 PTS 和 DTS?(物流单号)
这个数据的来源分为两个阶段:
阶段 A:采集端(初稿)
-
来源:由 采集设备(摄像头/V4L2) 或 封装库 添加。
-
逻辑:当你从
/dev/video75读取数据时,内核驱动会给每一帧打上一个“系统时间戳”,告诉 FFmpeg 这一帧是什么时候拍下来的。
阶段 B:推流端(校订)
-
来源:由 你的代码逻辑 或 FFmpeg 容器层 (Muxer) 重新计算。
-
逻辑:
-
解码后:原始像素(YUV)本身是不带 PTS 的,PTS 存在于
AVPacket(压缩包)里。当你解码成AVFrame后,FFmpeg 会把 PTS 传递给这个图像。 -
编码后:当你把拼接后的图像送入编码器,你需要给编码器输出的新包重新分配 PTS。
-
封装时:当你调用
av_interleaved_write_frame时,FFmpeg 会根据输出流的 时间基 (Time Base)(比如 90kHz)将你的秒数转换成最终的数值。
-
3. 为什么你的程序会报错 encode_write_fail?
这通常是因为在 阶段 B 出了问题:
-
你是“搬运工”还是“发货员”?
-
如果你的代码只是简单地把从摄像头读到的 PTS 丢给推流端,而没有考虑多个摄像头之间的同步或者拼接处理造成的延迟,PTS 就会出现重叠或倒流。
-
关键: 推流服务器(如 MediaMTX)要求收到的 PTS 必须是严格递增的。如果你给出的单号(PTS)是乱的,发货程序就会报错
fail。
-
4. 总结:数据流转表
| 数据类型 | 谁负责生成 | 存储位置 | 作用 |
|---|---|---|---|
| SPS/PPS | 编码器芯片 (RKMPP) | 码流头部 / Extradata | 告诉解码器如何初始化硬件 |
| IDR 帧 | 编码器芯片 (RKMPP) | 关键帧 NALU | 视频流的即时刷新点 |
| DTS | FFmpeg 封装层 | AVPacket | 确定解码器什么时候“拆包” |
| PTS | 你的代码逻辑 | AVPacket / AVFrame | 确定画面什么时候“上屏” |
你的下一步工作: 在代码中打印 pkt->pts。如果发现 pts 的值没有按照固定间隔(例如 30fps 应该是每帧增加 3000 左右,视 Time Base 而定)稳定增长,或者出现了变小的情况,你就找到了 encode_write_fail 的真凶。
idr是指 i帧 p b帧吗
简单来说:IDR 帧属于 I 帧的一种,但它比普通的 I 帧更“绝”。
为了理解它们的关系,我们可以把视频流看作一列火车,每一节车厢就是一帧:
1. I、P、B 帧:视频压缩的三剑客
这是按压缩方式分类的三种基本帧类型:
-
I 帧 (Intra-coded picture,帧内编码帧):
-
特点:完整的图像。它不参考任何其他帧,自己就能解压出一张图片。
-
形象理解:像一张独立的 JPEG 照片。
-
-
P 帧 (Predictive-coded picture,前向预测编码帧):
-
特点:不完整。它只记录与前一帧相比发生的变化。
-
形象理解:只记录“动了的地方”,背景没动就不记。
-
-
B 帧 (Bi-directionally predicted picture,双向预测编码帧):
-
特点:最省空间。它参考前面和后面的帧来推算自己。
-
形象理解:根据过去和未来来猜现在的样子
-
更多推荐




所有评论(0)