Анализ потока PS GB28181: инкапсуляция/отправка пакетов/прием группового пакета/анализ
Анализ потока PS GB28181: инкапсуляция/отправка пакетов/прием группового пакета/анализ

1. Формат потоковой передачи PS

Формат потока RTP, требуемый GB28181

Во-первых, давайте посмотрим на формат потока PS I-кадра. Здесь необходимо отметить, что заголовок PES должен быть добавлен перед SPS и PPS. Как показано на рисунке ниже, зеленая часть — это полученные нами данные чистого потока H.264, которые необходимо разделить на три сегмента и добавить с заголовком PES впереди. Этот момент не подробно описан в стандарте GB28181, и его можно увидеть только путем анализа потока Hikvision IPC.

В обычных обстоятельствах кадр IDR очень велик и превышает предел длины полезной нагрузки RTP (1400 байт), поэтому указанный выше I-кадр необходимо разделить на несколько пакетов и отправить по RTP несколько раз. Структура первого пакета показана на рисунке выше. После второго пакета структура RTP намного проще.

Вышеупомянутое относится к I-кадру. По сравнению с ним формат кадра P/B действительно прост, поскольку он не имеет ни SYS, PSM, ни SPS, PPS:

Размер кадра P/B обычно не превышает 1400 байт. Если он превышает 1400 байт, его также необходимо разделить на несколько пакетов данных RTP для передачи. Вторая структура пакета RTP для части, превышающей 1400:

1). Инкапсуляция ключевых кадров видео. RTP + PS header + PS system header + PS system Map + PES header +h264 data

2) Инкапсуляция неключевых кадров видео. RTP +PS header + PES header + h264 data

3). Инкапсуляция аудиокадра: RTP + PES header + G711

2. Поток PS на стороне сервера получает и анализирует пакеты.

Взяв Hikvision DS-IPC-B12H2-I в качестве примера для оптимизации

1. rtp over udp

80 60 00 00 00 00 00 00 00 00 04 00 00 00 01 ba 44 f0 4f 69 64 01 02 5f 03 fe ff ff 00 01 11 0c 00 00 01 bb 00 12 81 2f 81 04 e1 7f e0 e0 80 c0 c0 08 bd e0 80 bf e0 80 00 00 01 bc 00 5e f8 ff 00 24 40 0e 48 4b 01 00 14 14 40 16 6b bf 00 ff ff ff 41 12 48 4b 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 30 1b e0 00 1c 42 0e 07 10 10 ea 05 00 02 d0 11 30 00 00 1c 21 2a 0a 7f ff 00 00 07 08 1f fe a0 5a 90 c0 00 0c 43 0a 01 40 fe 00 7d 03 03 e8 03 ff f6 53 94 03 00 00 01 e0 00 1e 8c 80 08 21 3c 13 da 59 ff ff fc 00 00 00 01 67 4d 00 1f 96 35 40 a0 0b 74 dc 04 04 04 08 00 00 01 e0 00 0e 8c 00 03 ff ff fc 00 00 00 01 68 ee 3c 80 00 00 01 e0 00 0e 8c 00 02 ff fc 00 00 00 01 06 e5 01 d5 80 00 00 01 e0 c2 06 8c 00 05 ff ff ff ff f8 00 00 00 01 65 b8 00 00 0a 77 80 00 00 4f e7 e5 34 0f f3 41 4b b9 58 a9 4e 4c f4 ea 04 0e 32 a5 f9 51 df cc 4c b8 99 f2 cf 16 3e 32 19 ed 86 df 05 6b fc 21 5e 0f 87 90 20 c3 16 02 03 73 0f a3 d2 9b 52 1b b1 a7 7c b4 61 6d d9 aa f4 5d 34 f6 49 d4 f6 72 af b6 c7 11 c0 ff 3d 1b fd e3 5d 41 db 32 3a c7 9f f4 f2 c0 99 e6

Анализ данных, это I-кадр

rtp header: 80 60 00 00 00 00 00 00 00 00 04 00

—— 12-байтовое видео фиксированной длины

ps header: 00 00 01 ba ...

——00 00 01 ba 44 f0 4f 69 64 01 02 5f 03 fe ff ff 00 01 11 0c Первые 14 байт фиксированы, первые 14 байт 0xfe & 0x07 = 0x0e То есть позже он будет расширен до 6 байт.

ps system header: 00 00 01 bb ...

—— Следующие 00 12 — это длина, то есть длина системного заголовка ps = 4+18 байт.

ps system map 00 00 01 bc ...

—— Следующие 00 5e — это длина, то есть длина системной карты ps = 4+94 байта

pes header: 00 00 01 e0/c0 ...

——e0 — видео, c0 — аудио, а следующие 00 — 1e — длина, которая представляет собой длину заголовка pes (4+30 байт), а остальное — данные полезной нагрузки ps.

Язык кода:javascript
копировать
ps payload SPS 00 00 00 01 67 ...

ps payload PPS 00 00 00 01 68 ...

ps payload I   00 00 00 01 65 ...

ps payload P   00 00 00 01 61/41 ...

Камера Hikvision, PS payloadНачальный идентификатор00 00 00 01 61,У некоторых производителей есть00 00 00 01 41,После пятого байта & 0x1F = 1, это правильный PS идентификатор начала полезной нагрузки

кроме того:

0x000001BD личные данные,такой же0x000001E0 Пропустить напрямую.

Для конкретного формата PS вы можете обратиться к другой информации в Интернете.

Кроме того, если данные содержат 0x000001, они будут экранированы в соответствии с протоколом h264 и станут 0x00000301. Включает в себя 3 побега 0x000001 -> 0x00000301 0x000002 -> 0x00000302 0x000003 -> 0x00000303

Процесс получения и разбора udp--->rtp--->ps--->h264

Язык кода:javascript
копировать
while ((pack = sess.GetNextPacket()) != NULL) { 
  loaddata = pack->GetPayloadData(); 
  len = pack->GetPayloadLength(); /* payload type: ps */
  if(pack->GetPayloadType() == 96) { /*the last packet*/
      if(pack->HasMarker()) {
        if(pos + len < PS_BUFFER_SIZE){ 
          memcpy(&buff[pos],loaddata,len); 
          printf("!!! GetPayload len = %ld !!!! \n ",pos+len); 
          size_t r = ps_demuxer_input(ps, buff, pos+len); 
          pos = 0; 
          lasttime = nowtime; 
        } 
      } else { 
      if(pos + len < PS_BUFFER_SIZE){ 
        memcpy(&buff[pos],loaddata,len); 
        pos = pos + len; 
      } 
    } 
  } else { 
    printf("!!! GetPayloadType = %d !!!! \n ",pack->GetPayloadType()); 
  } sess.DeletePacket(pack); 
}

Заполните проанализированное видео h264 и аудио g711 в очередь для обработки.

vedio

Язык кода:javascript
копировать
/* SPS frame 00 00 00 01 67 PPS frame 00 00 00 01 68 I frame 00 00 00 01 65 P slice 00 00 00 01 41/61 */
if(FindStartCode(p)) {
  data_t d; 
  if(rb_numitems(task->buffer) < DATA_ITEM_NMAX){ 
    if(task->pos < ITEM_BUFFER_SIZE) {
      memcpy(d.buf,task->buf, task->pos); 
      d.size = task->pos; d.type = H264; 
      rb_put(task->buffer, &d); 
      fflush(stdout); 
    } 
  } 
  task->pos = 0; 
  if((task->pos + bytes) < TASK_BUFFER_SIZE){ 
    memcpy(&(task->buf[task->pos]),p,bytes); 
    task->pos = task->pos + bytes; 
  } 
} else { 
  if((task->pos + bytes) < TASK_BUFFER_SIZE){
    memcpy(&(task->buf[task->pos]),p,bytes); 
    task->pos = task->pos + bytes;
  } 
}

audio

Язык кода:javascript
копировать
data_t d; 
if (rb_numitems(task->buffer) < DATA_ITEM_NMAX) { 
  if(bytes < ITEM_BUFFER_SIZE){ 
    memcpy(d.buf, p, bytes); 
    d.size = bytes; 
    d.type = G711A; 
    rb_put(task->buffer, &d); 
    fflush(stdout); 
  } 
}

Анализ кадров данных h264 nalu может относиться к обработке live555

Ссылка: H264VideoRTPSink.cpp.

Язык кода:javascript
копировать
Boolean H264VideoRTPSource::processSpecialHeader(BufferedPacket* packet, unsigned& resultSpecialHeaderSize) { 
  unsigned char* headerStart = packet->data(); 
  unsigned packetSize = packet->dataSize(); 
  unsigned numBytesToSkip; // Check the 'nal_unit_type' for special 'aggregation' or 'fragmentation' packets:
  if (packetSize < 1) 
    return False; 
    fCurPacketNALUnitType = (headerStart[0]&0x1F); 
    switch (fCurPacketNALUnitType) { 
    case 24: { // STAP-A
      numBytesToSkip = 1; // discard the type byte
      break; } 
    case 25:
    case 26: 
    case 27: { // STAP-B, MTAP16, or MTAP24
      numBytesToSkip = 3; // discard the type byte, and the initial DON
    break; 
    } 
    case 28: 
    case 29: { // // FU-A or FU-B // For these NALUs, the first two bytes are the FU indicator and the FU header. // If the start bit is set, we reconstruct the original NAL header into byte 1:
      if (packetSize < 2) 
        return False; 
      unsigned char startBit = headerStart[1]&0x80; 
      unsigned char endBit = headerStart[1]&0x40; 
      if (startBit) { 
        fCurrentPacketBeginsFrame = True; 
        headerStart[1] = (headerStart[0]&0xE0)|(headerStart[1]&0x1F); 
        numBytesToSkip = 1; 
        } else { // The start bit is not set, so we skip both the FU indicator and header:
        
        fCurrentPacketBeginsFrame = False; 
        numBytesToSkip = 2; 
      } 
    fCurrentPacketCompletesFrame = (endBit != 0);
    break; 
  } 
  default: { // This packet contains one complete NAL unit:

  fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame = True; 
  numBytesToSkip = 0; 
  break; 
  } 
} resultSpecialHeaderSize = numBytesToSkip; return True; }

H264VideoRTPSink.cpp

Язык кода:javascript
копировать
H264VideoRTPSink* H264VideoRTPSink ::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, char const* sPropParameterSetsStr) {
    u_int8_t* sps = NULL; 
    unsigned spsSize = 0; 
    u_int8_t* pps = NULL; 
    unsigned ppsSize = 0; 
    unsigned numSPropRecords; 
    SPropRecord* sPropRecords = parseSPropParameterSets(sPropParameterSetsStr, numSPropRecords); 
    for (unsigned i = 0; i < numSPropRecords; ++i) { 
      if (sPropRecords[i].sPropLength == 0)
        continue; // bad data
      u_int8_t nal_unit_type = (sPropRecords[i].sPropBytes[0])&0x1F;
      if (nal_unit_type == 7/*SPS*/) { 
        sps = sPropRecords[i].sPropBytes; 
        spsSize = sPropRecords[i].sPropLength; 
      } else if (nal_unit_type == 8/*PPS*/) {
        pps = sPropRecords[i].sPropBytes; 
        ppsSize = sPropRecords[i].sPropLength; 
      } 
    } 
    H264VideoRTPSink* result = new H264VideoRTPSink(env, RTPgs, rtpPayloadFormat, sps, spsSize, pps, ppsSize); 
    delete[] sPropRecords; 
    return result; 
}

Или h264bitstream также имеет анализ h264 nalu

https://www.cnblogs.com/dong1/p/10149980.html

Разделение атрибутов кадра

SPS: 0x67 header & 0x1F = 7

PPS: 0x68 header & 0x1F = 8

SEI: 0x66 header & 0x1F = 6

I Frame: 0x65 header & 0x1F = 5

P Frame: 0x41 header & 0x1F = 1

Анализ h264 nalu не по теме, я кстати об этом упомянул. Основной упор здесь делается на анализ потока ps.

2. rtp over tcp

05 84 80 60 6d ee 00 b5 62 60 00 00 a5 f6

00 00 01 ba 44 76 55 85 74 01 02 5f 03 fe ff ff 00 00 86 24 00 00 01 e0 21 ba 8c 80 0a 21 1d 95 61 5d ff ff ff ff f8 00 00 00 01 61 e0 40 00 59 13 ff 01 23 44 a1 02 38 33 0f 99 df 89 95 01 9e 6d 31 00 2a 8f 05 a5 fb 96 67 38 b8 7f c5 73 bb 25 b6 96 3d 0c 15 0e a4 ed 95 30 6b 43 35 51 9a 04 a1 89 26 6a 6a fc 64 c6 44 37 2a 32 6d 16 12 41 83 53 42 d7 66 e3 51 6b 8e bc 8f 40 73 2a 22 9d a0 d7 b9 c1 ed f8 a5 14 91 2d 8e 90 07 0e b4 2e 4a 0e cb 03 4b 73 f4 1a 49 0a d3 1f bb 72 c5 28 13 b7 9b 35 a0 18 3a c0 91 73 99 1d 4c dd 3b fd eb ce 8e 73 79 34 8a 05 6d 98 d6 a9 20 d3 43 44 d9 b3 cd be 5b f6 74 86 f4 67 26 2f a1 be fb 5c 2c aa 81 4d 51 85 06 c7 65 82 52 47 05 5b ae 76 93

анализ данных, Это кадр P

Длина: 05 84

rtp header 80 60 6d ee 00 b5 62 60 00 00 a5 f6

ps header 00 00 01 ba ...

pes header 00 00 01 e0 ...

ps payload P 00 00 00 01 61/41 ...

В режиме RTP через TCP имеется больше полей длины, чем в режиме RTP через UDP. Информация о длине может использоваться для группировки пакетов для формирования полного пакета TCP. Полный пакет TCP без информации о длине является пакетом RTP.

Поскольку нижний уровень TCP оптимизирует распаковку и привязку пакетов, уровень приложения требует специальной обработки. Вы можете обратиться к режиму TCP в jrtplib. Библиотека jrtplib уже выполнила распаковку и привязку пакетов.

Процесс получения и разбора tcp--->rtp--->ps--->h264

Предоставьте пример кода: пример RTP через TCP/UDP в jrtplib.

https://www.cnblogs.com/dong1/p/12179996.html

3. Инкапсуляция и отправка потока PS на стороне устройства.

Теперь, когда данные, полученные от устройства, полностью проанализированы, как их по очереди упаковать, фрагментировать и отправить?

Самое хлопотное это упаковка ps, можно скопировать ffmpeg-4.1/doc/examples/muxing.c

Aac+h264 (также поддерживаются некоторые другие форматы аудио и видео) можно напрямую инкапсулировать в поток PS.

Для запечатанного ps buf следуйте формату FU-A и разрежьте его на фрагменты каждые 1400 байт. Добавьте 12-байтовый rtp-заголовок перед каждым фрагментом. Установите для последнего фрагмента значение «Маркер» и отправьте его.

В режиме rtp over tcp перед заголовком rtp необходимо добавить 2 байта, поэтому заголовок rtp over tcp составляет 14 байт, а заголовок rtp over udp — 12 байт.

Подробную информацию о различных типах шардинга см. в rfc3984: https://datatracker.ietf.org/doc/rfc3984/.

Оригинальный текст: https://my.oschina.net/u/4293666/blog/3494983

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose