Проблема с декодированием видео H264 по RTP с помощью ffmpeg ( libavcodec)

Я устанавливаю profile_idc, level_idc, extradata и extradata_size для AvCodecContext с набором параметров-идентификатора профиля и sprop-параметра-набора SDP.

I разделить декодирование пакета кодированного слайса, SPS, PPS и NAL_IDR_SLICE:

Init:

uint8_t start_sequence [] = {0, 0, 1}; int size = recv (id_de_la_socket, (char *) rtpReceive, 65535,0);

Кодированный фрагмент:

char *z = new char[size-16+sizeof(start_sequence)];
    memcpy(z,&start_sequence,sizeof(start_sequence));
    memcpy(z+sizeof(start_sequence),rtpReceive+16,size-16);
    ConsumedBytes = avcodec_decode_video(codecContext,pFrame,&GotPicture,(uint8_t*)z,size-16+sizeof(start_sequence));
    delete z;

Результат: ConsumedBytes> 0 и GotPicture> 0 (часто)

SPS и PPS:

идентичны код. Результат: ConsumedBytes> 0 и GotPicture = 0

Я думаю, это нормально

Когда я нахожу новую пару SPS / PPS, я обновляю extradata и extrada_size с полезными нагрузками этого пакета и их размером.

NAL_IDR_SLICE:

Тип блока Nal: 28 => idr Кадр фрагментирован для этого. Я попробовал два способа декодирования

1) Я префикс первого фрагмента (без заголовка RTP) с последовательностью 0x000001 и отправил его в avcodec_decode_video. Затем я отправляю остальные фрагменты этой функции.

2) Я добавляю к первому фрагменту (без заголовка RTP) последовательность 0x000001 и объединяю остальные фрагменты с ней. Я отправляю этот буфер в декодер.

В обоих случаях у меня нет ошибки (ConsumedBytes> 0), но я не обнаруживаю ни одного кадра (GotPicture = 0) ...

В чем проблема?

9
задан bben 16 August 2010 в 13:49
поделиться

2 ответа

В RTP все I-кадры H264 (IDR) обычно фрагментированы. Когда вы получаете RTP, вы сначала должны пропустить заголовок (обычно первые 12 байтов), а затем перейти к блоку NAL (первый байт полезной нагрузки). Если NAL равен 28 (1C), это означает, что следующие полезные данные представляют один фрагмент IDR H264 (I-Frame) и что вам необходимо собрать их все, чтобы восстановить IDR H264 (I-Frame).

Фрагментация происходит из-за ограниченного MTU и гораздо большего IDR. Один фрагмент может выглядеть так:

Фрагмент, у которого START BIT = 1:

First byte:  [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS] 
Second byte: [ START BIT | END BIT | RESERVED BIT | 5 NAL UNIT BITS] 
Other bytes: [... IDR FRAGMENT DATA...]

Другие фрагменты:

First byte:  [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS]  
Other bytes: [... IDR FRAGMENT DATA...]

Чтобы восстановить IDR, вы должны собрать эту информацию:

int fragment_type = Data[0] & 0x1F;
int nal_type = Data[1] & 0x1F;
int start_bit = Data[1] & 0x80;
int end_bit = Data[1] & 0x40;

Если fragment_type == 28 то следующая за ним полезная нагрузка - это один фрагмент IDR. Следующая проверка устанавливается start_bit , если это так, то этот фрагмент является первым в последовательности. Вы используете его для восстановления байта NAL IDR, беря первые 3 бита из первого байта полезной нагрузки (3 БИТА NAL UNIT) и комбинируя их с последними 5 битами из второго байта полезной нагрузки (5 БИТОВ NAL UNIT) , чтобы получить такой байт [3 NAL UNIT BITS | 5 БИТОВ БЛОКА NAL] . Затем сначала запишите этот байт NAL в чистый буфер вместе со всеми последующими байтами из этого фрагмента. Не забудьте пропустить первый байт в последовательности, поскольку он не является частью IDR, а только идентифицирует фрагмент.

Если начальный_бит и конечный_бит равны 0, то просто запишите полезную нагрузку (пропуская первый байт полезной нагрузки, который идентифицирует фрагмент) в буфер.

Если start_bit равен 0, а end_bit равен 1, это означает, что это последний фрагмент, и вы просто записываете его полезную нагрузку (пропуская первый байт, который идентифицирует фрагмент) в буфер, и теперь вы восстановили свой IDR.

Если вам нужен какой-то код, просто спросите в комментарии, я отправлю его, но я думаю, что это довольно понятно, как это сделать ... =)

ОТНОСИТЕЛЬНО ДЕКОДИРОВКИ

Сегодня мне пришло в голову, почему вы получаете ошибку при декодировании IDR (я предполагал, что вы хорошо его восстановили). Как вы создаете запись конфигурации декодера AVC? Автоматизировано ли это в используемой вами библиотеке? Если нет, и вы не слышали об этом, продолжайте читать ...

AVCDCR определен, чтобы позволить декодерам быстро анализировать все данные, которые им нужны для декодирования видеопотока H264 (AVC). И данные следующие:

  • ProfileIDC
  • ProfileIOP
  • LevelIDC
  • SPS (Наборы параметров последовательности)
  • PPS (Наборы параметров изображения)

Все эти данные отправляются в сеансе RTSP в SDP под поля: идентификатор-уровня-профиля и наборы-параметров sprop .

ДЕКОДИРОВАНИЕ ИДЕНТИФИКАЦИИ УРОВНЯ ПРОФИЛЯ

Строка идентификатора уровня приоритетного файла делится на 3 подстроки, каждая длиной 2 символа:

[IDC ПРОФИЛЯ] [IOP ПРОФИЛЯ] [IDC УРОВНЯ]

Каждая подстрока представляет один байт в base16 ! Итак, если IDC профиля 28, это означает, что в base10 фактически 40. Позже вы будете использовать значения base10 для создания записи конфигурации декодера AVC.

ДЕКОДИРОВАНИЕ НАБОРОВ-ПАРАМЕТРОВ SPROP

Sprops обычно представляют собой 2 строки (может быть и больше), разделенных запятыми, и закодированные в base64 ! Вы можете декодировать их оба, но в этом нет необходимости. Ваша задача здесь - просто преобразовать их из строки base64 в массив байтов для дальнейшего использования. Теперь у вас есть двухбайтовые массивы, первый из которых использует SPS, второй - PPS.

СОЗДАНИЕ AVCDCR

Теперь у вас есть все необходимое для создания AVCDCR, вы начинаете с создания нового чистого буфера, а теперь записываете в него все в порядке, описанном здесь:

1 - Байт, имеющий значение 1 и представляет версию

2 - Байт IDC профиля

3 - Байт IOP Prifile

4 - Байт IDC уровня

5 - Байт со значением 0xFF (Google запись конфигурации декодера AVC для посмотрите, что это)

6 - Байт со значением 0xE1

7 - Короткий со значением длины массива SPS

8 - Массив байтов SPS

9 - Байт с количеством массивов PPS (вы можете их больше в наборе-параметров sprop)

10 - Короткое с длиной следующего массива PPS

11 - Массив PPS

ДЕКОДИРОВАНИЕ ВИДЕОПОТОКА

Теперь у вас есть массив байтов, который сообщает декодеру, как для декодирования видеопотока H264. Я считаю, что вам это нужно, если ваша библиотека сама не строит ее из SDP ...

25
ответ дан 4 December 2019 в 08:00
поделиться

Я не знаю об остальной части вашей реализации, но кажется вероятным, что "фрагменты", которые вы получаете, являются единицами NAL. Поэтому каждому из них может понадобиться стартовый код NALU (00 00 01 или 00 00 00 01), добавляемый при реконструкции битового потока перед отправкой в ffmpeg.

В любом случае, вы можете найти RFC для H264 RTP пакетизации полезным:

http://www.rfc-editor.org/rfc/rfc3984.txt

Надеюсь, это поможет!

1
ответ дан 4 December 2019 в 08:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: