环境:
我有一个 IP 摄像机,它能够通过 RTP 以 H.264 编码格式传输数据。此原始流是从以太网记录的。有了这些数据,我必须工作。
目标:
最后,我想要一个 *.mp4 文件,我可以使用常见的媒体播放器(如 VLC 或 Windows MP)播放它。
到目前为止我做了什么:
我获取我拥有的原始流数据并对其进行解析。由于数据是通过 RTP 传输的,我需要处理 NAL 字节、SPS 和 PPS。
1.写一个原始文件
首先,我确定通过以太网接收的每个帧的类型。为此,我解析每个 RTP Payload 的前两个字节,因此我可以获得 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 字节是通过将字节 1 和 2 中的 NAL 单元位放在一起创建的。
现在根据碎片类型,我执行以下操作:
SPS/PPS:
- 写入 NAL 前缀 (
0x00 0x00 0x01
),然后写入 SPS 或 PPS 数据
带起始位的分片
- 写 NAL 前缀
- 写入 NAL 单元字节
- 写入剩余的原始数据
没有起始位的分片
- 写入原始数据
这意味着我的原始文件看起来像这样:
[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
:从手册页引用:
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 实际上做了什么?为什么它会改变视频数据中的一些字节?