【问题标题】:How to pack Android MediaCodec encoded H264 into RTP packets如何将 Android MediaCodec 编码的 H264 打包成 RTP 数据包
【发布时间】: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


【解决方案1】:

我终于设法解决了,我的数据包配置不正确。

  • 我必须迭代每个数据包的序列号。
  • 我必须为每个 NALU 而不是每个数据包设置时间戳。
  • 我必须去掉 00 00 01 ** 在索引 4 之后发送字节的 NALU 前缀。
  • 我的标头中的按位运算不正确。

我什至可以在流的中间启动 FFmpeg 并且它可以工作!

【讨论】:

    猜你喜欢
    • 2015-04-30
    • 2011-12-04
    • 1970-01-01
    • 1970-01-01
    • 2014-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    相关资源
    最近更新 更多