【发布时间】:2017-02-17 03:18:48
【问题描述】:
如何正确地将 H264 字节流打包到 RTP 数据包中,以便接收带有 FFMPEG 的帧?
当我启动 FFMPEG 接收器时,它会产生很多类似这样的错误:
Invalid UE golomb code
[h264 @ 0xd63060] pps_id 3199971767 out of range
[h264 @ 0xd63060] slice type 32 too large at -1
[h264 @ 0xd63060] decode_slice_header error
[h264 @ 0xd63060] non-existing PPS 0 referenced
[h264 @ 0xd63060] decode_slice_header error
[h264 @ 0xd63060] no frame!
[h264 @ 0xd63060] decode_slice_header error
[h264 @ 0xd63060] Unknown NAL code: 0 (0 bits)
[h264 @ 0xd63060] no frame!
[h264 @ 0xd63060] non-existing PPS 0 referenced
这是我使用的 SDP 文件:
c=IN IP4 192.168.2.30
t=0 0
m=video 51372 RTP/AVP 96
a=rtpmap:96 H264/90000
a=recv only
pps_id 错误很奇怪,它好像在寻找下一个 PPS,但是找不到,虽然我尝试将 PPS 嵌入到每个 NALU 中。
我一直在阅读RFC 6184 并试图理解它。但是我感觉我还是不太明白H264和RTP是怎么交互的。目前,我正在尝试对来自相机的像素进行编码,并通过 RTP 通过网络传输 1920x1080 H264 编码帧,然后由 FFMPEG 接收并解码。我正在用 Java 组装 RTP 和 FU-A 标头,并在 NALU 对 MTU 来说太大时将它们分段。
我一直在 Wireshark 中密切关注流,这是我的第一个数据包的输出:
Real-Time Transport Protocol
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 .... = Extension: False
.... 0000 = Contributing source identifiers count: 0
1... .... = Marker: True
Payload type: DynamicRTP-Type-96 (96)
Sequence number: 0
Timestamp: 2727179012
Synchronization Source identifier: 0x00000000 (0)
H.264
NAL unit header or first byte of the payload
0... .... = F bit: No bit errors or other syntax violations
.00. .... = Nal_ref_idc (NRI): 0
...0 0000 = Type: Undefined (0)
H264 NAL Unit Payload
我不明白为什么第一个有效载荷的 NALU 类型为 0。不过,这是我的第二个数据包:
Real-Time Transport Protocol
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 .... = Extension: False
.... 0000 = Contributing source identifiers count: 0
0... .... = Marker: False
Payload type: DynamicRTP-Type-96 (96)
Sequence number: 1
Timestamp: 2727179019
Synchronization Source identifier: 0x00000000 (0)
H.264
FU identifier
0... .... = F bit: No bit errors or other syntax violations
.11. .... = Nal_ref_idc (NRI): 3
...1 1100 = Type: Fragmentation unit A (FU-A) (28)
FU Header
1... .... = Start bit: the first packet of FU-A picture
.0.. .... = End bit: Not the last packet of FU-A picture
..0. .... = Forbidden bit: 0
...0 0101 = Nal_unit_type: Coded slice of an IDR picture (5)
H264 NAL Unit Payload
0000 0000 0000 0000 0000 0000 0000 0001 0110 0101 1011 1000 0000 0100 0000 010. = first_mb_in_slice: 3000762881
.... ...1 = slice_type: P (P slice) (0)
0011 1... = pic_parameter_set_id: 6
所以我认为最后一个数据包是 I-Frame?这是开始和结束片段之间的片段:
Real-Time Transport Protocol
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 .... = Extension: False
.... 0000 = Contributing source identifiers count: 0
0... .... = Marker: False
Payload type: DynamicRTP-Type-96 (96)
Sequence number: 1
Timestamp: 2727179019
Synchronization Source identifier: 0x00000000 (0)
H.264
FU identifier
0... .... = F bit: No bit errors or other syntax violations
.11. .... = Nal_ref_idc (NRI): 3
...1 1100 = Type: Fragmentation unit A (FU-A) (28)
FU Header
0... .... = Start bit: Not the first packet of FU-A picture
.0.. .... = End bit: Not the last packet of FU-A picture
..0. .... = Forbidden bit: 0
...0 0101 = Nal_unit_type: Coded slice of an IDR picture (5)
当然这里是假定的 I-Frame 的最后一个数据包:
Real-Time Transport Protocol
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 .... = Extension: False
.... 0000 = Contributing source identifiers count: 0
1... .... = Marker: True
Payload type: DynamicRTP-Type-96 (96)
Sequence number: 1
Timestamp: 2727179019
Synchronization Source identifier: 0x00000000 (0)
H.264
FU identifier
0... .... = F bit: No bit errors or other syntax violations
.11. .... = Nal_ref_idc (NRI): 3
...1 1100 = Type: Fragmentation unit A (FU-A) (28)
FU Header
0... .... = Start bit: Not the first packet of FU-A picture
.1.. .... = End bit: the last packet of FU-A picture
..0. .... = Forbidden bit: 0
...0 0101 = Nal_unit_type: Coded slice of an IDR picture (5)
现在这是编码器给我的下一个字节的数据包:
Real-Time Transport Protocol
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 .... = Extension: False
.... 0000 = Contributing source identifiers count: 0
0... .... = Marker: False
Payload type: DynamicRTP-Type-96 (96)
Sequence number: 2
Timestamp: 2727179089
Synchronization Source identifier: 0x00000000 (0)
H.264
FU identifier
0... .... = F bit: No bit errors or other syntax violations
.11. .... = Nal_ref_idc (NRI): 3
...1 1100 = Type: Fragmentation unit A (FU-A) (28)
FU Header
1... .... = Start bit: the first packet of FU-A picture
.0.. .... = End bit: Not the last packet of FU-A picture
..0. .... = Forbidden bit: 0
...0 0001 = Nal_unit_type: Coded slice of a non-IDR picture (1)
H264 NAL Unit Payload
0000 0000 0000 0000 0000 0000 0000 0001 0110 0001 1110 0000 0010 0000 0001 100. = first_mb_in_slice: 2968522763
.... ...0 0111 .... = slice_type: B (B slice) (6)
.... 0001 110. .... = pic_parameter_set_id: 13
这部分让我很困惑,当相机静止时,编码器给了我越来越小的未定义类型的 NALU,我不完全确定为什么下面的数据包会作为一个完整的 NALU 发送到 FFMPEG。
Real-Time Transport Protocol
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 .... = Extension: False
.... 0000 = Contributing source identifiers count: 0
1... .... = Marker: True
Payload type: DynamicRTP-Type-96 (96)
Sequence number: 36
Timestamp: 2727180258
Synchronization Source identifier: 0x00000000 (0)
H.264
NAL unit header or first byte of the payload
0... .... = F bit: No bit errors or other syntax violations
.00. .... = Nal_ref_idc (NRI): 0
...0 0000 = Type: Undefined (0)
H264 NAL Unit Payload
我正在使用 Android MediaCodec 编码器,这里是我配置编码器的一些代码:
mediaCodec = MediaCodec.createByCodecName("OMX.Nvidia.h264.encoder");
mediaFormat = MediaFormat.createVideoFormat("video/avc", 1920, 1080);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1920 * 1080);
编码器是给我整个访问单元还是只给我 NALU?
这是我的逻辑:
- 如果帧大小大于 MTU,则帧将被分段。
- 当我发送起始 FU-A 标头时,我将起始位设置为 1。
- 当我发送帧的最后分段字节时,我将 RTP 标头中的标记位设置为 1,将 FU-A 标头中的结束分段位设置为 1。
- 开始和结束片段之间的 FU-A 标头将开始和结束位设置为 0。
- 标记始终设置为 0,最后一个数据包除外。
- 如果 NALU 可以放入 MTU,则发送整个帧。
- 每发送一个 NALU,我都会迭代 RTP 标头的序列号。
- 每发送一个 NALU,我都会为 RTP 标头获得一个新的时间戳。
- 在对 NALU 进行分片之前,我先保存 NALU 类型并将其插入到 FU-A 标头中
我觉得我已经接近了,但它显然不适用于任何 RTP 接收器。我感谢您对此事的任何想法或想法。
谢谢,
【问题讨论】:
-
I finally managed to work it out- 如果这解决了您的问题,那么您应该将其发布为答案(是的,回答自己的问题是完全有效的),以便其他人知道您的问题已解决。
标签: android h.264 packet rtp encoder