Как упаковать Android MediaCodec в кодировке H264 в RTP-пакеты

Как правильно упаковать поток байтов 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. В настоящее время я пытаюсь кодировать пиксели с камеры и транслировать кадры 1920x1080 H264 через RTP по сети, где они затем принимаются FFMPEG и декодируются. Я собираю заголовки RTP и FU-A в Java и фрагментирую 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-кадра:

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. Я ценю любые мысли или идеи по этому вопросу.

Спасибо,

1 ответ

Мне наконец удалось разобраться, мои пакеты не были настроены должным образом.

  • Я должен повторить порядковый номер для пакета.
  • Я должен установить метку времени для NALU, а не для пакета.
  • Я должен удалить префикс NALU 00 00 01 ** отправка байтов после индекса 4.
  • Побитовые операции в моих заголовках были неверными.

Я даже могу запустить FFmpeg в середине потока, и это работает!

Другие вопросы по тегам