Окружающая среда:
У меня есть IP-камера, которая способна передавать данные по RTP в кодированном формате H.264. Этот необработанный поток записывается из сети Ethernet. С этими данными я должен работать.
Цель:
В конце концов, я хочу иметь файл *.mp4, который я могу играть с обычными медиаплеерами (например, VLC или Windows MP).
Что я сделал до сих пор:
Я беру данные необработанных потоков, которые у меня есть, и разбираю их. Поскольку данные были переданы через RTP, мне необходимо позаботиться о байтах NAL, SPS и PPS.
1. Напишите исходный файл
Сначала я определяю тип каждого кадра, полученного по Ethernet. Для этого я разбираю первые два байта каждой полезной нагрузки RTP, поэтому я могу получить бит 8 NAL, бит типа фрагмента и стартовый, зарезервированный и конечный бит. В полезной нагрузке они расположены следующим образом:
Byte 1: [ 3 NAL Unit Bits | 5 Fragment Type Bits]
Byte 2: [Start Bit | Reserved Bit | End Bit | 5 NAL Unit Bits]
Из этого я могу определить:
- Начало и конец Видеокадр → Начальный бит и конечный бит
- Тип полезной нагрузки → 5 битов типа фрагмента
- Байт блока NAL
Типы фрагментов, которые необходимы в моем случае:
Fragment Type 7 = SPS
Fragment Type 8 = PPS
Fragment Type 28 = Video Fragment
Байт NAL создается путем объединения бит блока NAL из байтов 1 и 2.
Теперь, в зависимости от типа фрагментации, я делаю следующее:
SPS/PPS:
- Запишите префикс NAL (
0x00 0x00 0x01
), а затем данные SPS или PPS
Фрагментация с помощью стартового бита
- Запись префикса NAL
- Write NAL Unit Byte
- Запись оставшихся необработанных данных
Фрагментация без стартового бита
- Запись исходных данных
Это означает, что мой сырой файл выглядит примерно так:
[NAL Prefix][SPS][NAL Prefix][PPS][NAL Prefix][NAL Unit Byte][Raw Video Data][Raw Video Data]....[NAL Prefix][NAL Unit Byte][Raw Video Data]...
Для каждого PPS и SPS я нахожу в своих потоковых данных, я просто пишу префикс NAL (0x00 0x00 0x01), а затем сам SPS/PPS.
Теперь я не могу воспроизвести эти данные с помощью какого-либо медиаплеера, что приводит меня к:
2. Преобразовать файл
Так как я не хотел много работать с кодеками, я просто пошел использовать существующее приложение → FFmpeg. Это я вызываю с такими параметрами:
ffmpeg.exe -f h264 -i <RawInputFile> -vcodec copy -r 25 <OutPutFilename>.mp4
-f h264
: Это должно сказать ffmpeg У меня есть кодированный поток h264
-vcodec copy
: Цитата из manpage:
Force video codec to codec. Use the "copy" special value to tell that the raw codec data must be copied as is.
-r 25
: устанавливает частоту кадров до 25 FPS.
Когда я вызываю ffmpeg с этими параметрами, я получаю файл .mp4, который я могу играть с VLC и Windows MP, поэтому он действительно работает. Но файл теперь немного отличается от моего сырого файла.
Это приводит меня к моему вопросу:
Что я на самом деле делал?
Моя проблема заключается не в том, что она не работает. Я просто хочу/должен знать, что я на самом деле сделал с вызовом ffmpeg. У меня был необработанный файл H264, в котором я не мог играть . После использования FFmpeg я может воспроизвести его.
Существуют следующие различия между исходным исходным файлом (который я написал) и тем, который был написан FFmpeg:
- Заголовок: файл FFmpeg имеет около 0x30 байт заголовка
- Нижний колонтитул: файл FFmpeg также имеет нижний колонтитул
- Измененный префикс и 2 новых байта:
В то время как новый видеофрагмент из исходного файла начинался как
[NAL Prefix][NAL Unit Byte][Raw Video Data]
в новом файле выглядит так:
[0x00 0x00][2 "Random" Bytes][NAL Unit Byte][Raw Video Data].....[0x00 0x00[2 other "Random" Bytes][NAL Unit Byte][Raw Video Data]...
Я понимаю, что для видеопотока нужен формат контейнера (исправьте меня, если я ошибаюсь, но я полагаю, что за него отвечают новый верхний и нижний колонтитулы). Но почему это фактически меняет некоторые байты в необработанных данных? Это не может быть некоторое декодирование, поскольку сам поток должен быть декодирован игроком, а не ffmpeg.
Как вы можете видеть, мне больше не нужно новое решение для моей проблемы, поэтому я могу объяснить это самостоятельно. Что делает ffmpeg на самом деле? И почему это изменяет некоторые байты в видеоданных?