RTMP推流中的视频分辨率改变处理

问题

视频旋转时(分辨率发生改变时), 腾讯播放器不旋转

分析

RTMP协议只对数据进行转发, 音视频的封包是基于FLV进行打包的

 

RTMP VideoTag 根据 AVCPacketType 字段的不同,主要分为两种类型:

AVC sequence headerAVC 配置类型)

AVCPacketType = 0x00

用于发送 SPS/PPS 等解码器配置信息,通常在推流开始时发送一次,作为初始化解码器的关键数据。

 

AVC NALUAVC 帧数据类型)

AVCPacketType = 0x01

用于传输实际的 H.264 视频帧数据,如 I 帧(关键帧)或 P/B 帧(非关键帧)。 

但在实际传输中, RTMP VideoTag是可以包含多个 NALU 单元的,具体取决于封装格式和打包方式:

 


 

l 情况一:AVCC 格式(常见于 RTMP/MP4

 

每个 NALU 前有 长度字段(如 4 字节大端整数),表示该 NALU 的大小。

一个 VideoTag(即一帧数据)可以连续包含多个 NALU,例如:

SPS

PPS

IDR slice

SEI

这些 NALU 按顺序依次拼接,形成完整的视频帧数据。

 

 


 

l 情况二:Annex-B 格式(裸流或 TS

 

每个 NALU 以起始码 0x00 00 01 0x00 00 00 01 分隔。

多个 NALU 可以直接连续出现在同一个数据包中,无需额外封装结构。

 

当分辨率发生改变时,播放器可以从新的AVC sequence header感知,或者从AVC NALU感知,但前提是包含AVC NALU包含SPSPPS

原有使用我司封装的Librtmp推流

数据用的是Annex-B格式; AVC sequence header只发送了一次

如图所示,在开始推流时第一个数据包中,

1) AVC Sequence HeaderSPS内容为67 64 00 1f ac d9 40 50...

2) AVC NAUL Annex-B 格式, SPS内容为67 64 00 16 ac b4 05 01...

 

在该抓包数据中,搜索再无AVC Sequence Header, 即使SPS发生改变时:

 

发现AVC Sequence Header中的SPSAVC NAUL中额SPS内容不一致呢?

抓取另一个包,如下图所示:

 

1) AVC Sequence HeaderSPS内容为67 64 00 1f ac d9 40 50...

2) AVC NAUL Annex-B 格式, SPS内容为67 64 00 33 ac b4 02 20 1e ...

不难猜测,AVC Sequence HeaderSPS的内容,应该是封装库中,SPS的内容写死了,而未取参数传递的值。

 

播放器播放效果分析

VLC能够正确播放,且能够旋转, 这是因为VLC能够识别Annex-B格式,解析其中的SPS

腾讯播放器旋转不了,是因为其无法识别Annex-B格式, 只能从唯一的一个AVC Sequence Header来感知解码参数,当分辨率发生改变时也不变;

腾讯播放器播放竖屏视频源,开始就显示为横屏, 是因为AVC Sequence Header中的SPS被写死,未使用传递的SPS, 猜测由于使用Annex-B各种中包含了正确的SPS,所以解码器被重新初始化,解码未报错,但是画布的长宽是从AVC Sequence Header中获取的(1920*1080),所以是横屏

 

VLC能够播放, 阿里播放器能够播放, 腾讯最后一个免费的播放器不兼容

 

使用FFMPEG推流

VideoTag数据使用AVCC格式,且包含SPS; AVC sequence header只发送了一次

VideoTag采用Annex-B格式,包含SPS,且AVC Sequence Header中的SPS的内容一致 67 64 00 33 ac b4 02 20 1e 34

 

以下当分辨率发生改变时,AVC Sequence Header也未再发送

 

播放器播放效果分析

VLC能够正确播放,因为能解析AVCC格式,且其中包含SPS,能够再次初始化

腾讯播放器能够正确播放,因为能解析AVCC格式,且其中包含SPS,能够再次初始化

 

SRS-Librtmp

VideoTag数据使用AVCC格式,且包含SPS; AVC sequence header多次发送

开始时发送了一次AVC Sequence Header注意,后面没有紧跟I帧数据

 

下一个包才有I

SPS发生改变时,再次发送AVC Sequence Hader

当分辨率发生改变时,腾讯播放器能够正常播放,VLC仍会花屏

 

分析:

VLC 在接收 RTMP 流时,必须把 H.264 解码参数(SPS/PPS)先拿到才能正确解码后面的帧。在收到第一次AVC Sequence Header flv_packet() 会调用 flv_get_extradata() SPS/PPS 拷进 fmt->p_extra,随后 decoder_New() 用这份 extradata 初始化 H.264 解码器。

解码器一旦建立,后面再出现的 AVCPacketType == 0 包就被忽略了。

因此即使你中途把 SPS/PPS 改成别的参数,VLC 仍然用旧的 extradata 去解码,于是出现花屏/解码错误。

 

为什么 RTMP/FLV 不自动补发 SPS/PPS

 

RTMP 协议本身只是字节管道,它不缓存或重传解码配置。FLV 规范规定:

• AVCPacketType=0 的那一条(AVC sequence header)必须在每一路视频流的最前面发一次,后续只发 AVCPacketType=1 的裸 NALU

客户端(包括 VLC)通常只在这一条里解析 SPS/PPS之后就不再解析了。因此,后面如果 SPS/PPS 缺席,客户端不会回头去找。 新的腾讯播放器,ffplay会再次取AVE Sequence Header,来更新SPS/PPS

 

解决方式(任选其一)

在推流最开始发一条 AVC sequence headerAVCPacketType=0),里面放 SPS/PPS;以后每一帧都用 AVCPacketType=1,只发 IDR/P/B NALU

这样 VLC 一定能在首条拿到解码参数。

 

可以在每一个关键帧之前把 SPS/PPS 再发一次:

[VideoTagHeader: 0x17 0x01 00 00 00]

[NALU1: SPS]

[NALU2: PPS]

[NALU3: IDR]

 验证结果符合预期:在 RTMP 生态中,"每个 0x01 携带 SPS" 是实际可行的动态分辨率切换方案,而"重发 0x00" 存在兼容性问题。

 

posted @ 2026-07-24 15:52  CNHK19  阅读(18)  评论(0)    收藏  举报