h264 inside AVI、MP4 和 "Raw" h264 流。不同格式的 NAL 单元(或 ffmpeg 错误)
h264 inside AVI, MP4 and "Raw" h264 streams. Different format of NAL units (or ffmpeg bug)
TL;DR: 我想从 AVI/MP4 文件中读取原始 h264 流,甚至 broken/incomplete.
几乎所有关于h264的文档都告诉我它由NAL数据包组成。好的。几乎所有地方都告诉我数据包应该以 00 00 01
或 00 00 00 01
这样的签名开头。例如,,
The format of H.264 is that it’s made up of NAL Units, each starting
with a start prefix of three bytes with the values 0x00, 0x00, 0x01
and each unit has a different type depending on the value of the 4th
byte right after these 3 starting bytes. One NAL Unit IS NOT one frame
in the video, each frame is made up of a number of NAL Units.
好的。
我下载了 random_youtube_video.mp4 并从中删除了一帧:
ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.avi
并得到:
红色部分 - 这是 AVI 容器的一部分,其他 - 实际数据。
如您所见,这里我有 00 00 24 A9
而不是 00 00 00 01
此 AVI 文件播放完美
我对 mp4 容器做同样的事情:
如您所见,这里的字节数完全相同。
这个 MP4 文件播放完美
我尝试去除原始数据:
ffmpeg -i pic.avi -c copy pic.h264
此文件无法在 VLC 中播放,甚至生成此文件的 ffmpeg 也无法解析它:
我下载了 mp4 流分析器并得到:
MP4Box
告诉我:
Cannot find H264 start code
Error importing pic.h264: BitStream Not Compliant
学习 h264 的内部原理非常困难,因为什么都不会。
所以,我有问题:
- mp4 中的实际数据是什么?
- 解码该数据我必须阅读的内容(我的意思是不同的附件)
- 如何从这个 "broken" 原始流中读取流并获取解码图像(即使使用 ffmpeg)?
更新:
ffmpeg 中似乎有错误:
当我进行双重转换时:
ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.mp4
ffmpeg pic.mp4 -c copy pic.h264
但是当我直接转换文件时:
ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.h264
我有 NAL 签名和一个额外的 NAL 单元。其他字节相同(选中)。
这是错误?
更新
不,这不是错误,您必须使用选项 -bsf h264_mp4toannexb 将流保存为 "Annex B" 格式(带前缀)
"I want to read raw h264 streams from AVI files, even broken/incomplete."
"Almost everywhere told to me that the packet should start with a signature like :
00 00 01
or 00 00 00 01
"
"...As you can see, here I have 00 00 24 A9
instead of 00 00 00 01
"
您的 H264 是 AVCC 格式,这意味着它使用数据 sizes(而不是数据 start codes)。只有 Annex-B 会将您提到的签名作为起始代码。
您寻找帧,而不是通过寻找开始代码,而是您只是跳过帧大小以达到(请求的)帧的最终正确偏移...
AVI 处理:
读取大小(四)字节(32 位整数,Little Endian)。
提取下一个字节,最大为 size 数量。
这是您的 H.264 帧(AVCC 格式),解码字节以查看图像。
要转换为 Annex-B,请尝试用 00 00 00 01
.[=40 替换 H.264 frame bytes 的前 4 个字节=]
考虑您显示的 AVI 字节(参见 first 图片):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 4C 49 53 54 BA 24 00 00 6D 6F 76 69 ....LISTº$..movi
30 30 64 63 AD 24 00 00 00 00 24 A9 65 88 84 27 00dc.$....$©eˆ„'
C7 11 FE B3 C7 83 08 00 08 2A 7B 6E 59 B5 71 E1 Ç.þ³Çƒ...*{nYµqá
E3 9C 0E 73 E7 10 50 00 18 E9 25 F7 AA 7D 9C 30 ãœ.sç.P..é%÷ª}œ0
E6 2F 0F 20 00 3A 64 AA CA 5E 4F CA FF AE 20 04 æ/. .:dªÊ^OÊÿ® .
07 81 40 00 48 00 0A 28 71 21 84 48 06 18 90 0C ..@.H..(q!„H....
31 14 57 9E 7A CD 63 A0 E0 9B 96 69 C5 18 AE F2 1.WžzÍc à›–iÅ.®ò
E6 07 02 29 01 20 10 70 A1 0F 8C BC 73 F0 78 FA æ..). .p¡.Œ¼sðxú
9E 1D E1 C2 BF 8C 62 CE CE AC 14 5A A4 E1 45 44 ž.ῌbÎά.Z¤áED
38 38 85 DB 12 57 3E F6 E0 FB AE 03 04 21 62 8D 88…Û.W>öàû®..!b.
F6 F1 1E 37 1C A2 FF 75 1C F1 02 66 0C 92 07 06 öñ.7.¢ÿu.ñ.f.’..
15 7C 90 15 6F 7D FC BD 13 1E 2B 0C 14 3C 0C 00 .|..o}ü½..+..<..
B0 EA 6F 53 B4 98 D7 80 7A 68 3E 34 69 20 D2 FA °êoS´˜×€zh>4i Òú
F0 91 FC 75 C6 00 01 18 C0 00 3B 9A C5 E2 7D BF ð‘üuÆ...À.;šÅâ}¿
一些解释:
忽略前导多个 00
字节。
4C 49 53 54 D6 3C 00 00 6D 6F 76 69
包括 30 30 64 63
= AVI "List" header.
AD 24 00 00
== decimal 9389
是AVI自身的H264 item的大小(必须读入Little Endian)。
注意 AVI 字节包括...
- item 的注释总 size (AD 24 00 00
... or reverse对于 Little Endian:00 00 24 AD
)
- 后跟 item 数据 (00 00 24 A9 65 88 84 27 ... etc ... C5 E2 7D BF
).
这个 size 包括 AVI 的 4 个字节size”条目 + 的预期字节长度item 自己的字节数。可以简单写成:
AVI_Item_Size = ( 4 + item_H264_Frame.length );
AVI 中的 H.264 视频帧字节 :
接下来是item数据,即H.264视频帧。由于 formats/bytes 布局的巧合,它也包含 data 的 size 的 4 字节条目(因为你的 H264 在AVCC 格式,如果它是 Annex-B 那么你会在这里看到起始代码字节而不是大小字节)。
与 AVI 字节不同,这些 H264 size 字节以 Big Endian 格式写入。
00 00 24 A9
= 此视频帧的字节大小(而不是起始代码:00 00 00 01
)。
65 88 84 27 C7 11 FE B3 C7
= H.264 关键帧(始终从 X5 开始,其中 X
值基于其他设置)。
记住在四个大小字节(甚至是起始码)之后如果后面跟着...
- byte
X5
= 关键帧 (IDR),示例字节 65
.
- byte
X1
= P 或 B 帧,示例字节 41
.
- byte
X6
= SEI(补充增强信息)。
- byte
X7
= SPS(序列参数集)。
- byte
X8
= PPS(图片参数集).
- bytes
00 00 00 X9
= 访问单元定界符。
如果您在 AVI 文件中搜索完全相同的字节,则可以找到 H.264。查看 third 图片,这些是您的 H.264 字节(它们被剪切并粘贴到 AVI 容器中)。
有时一个帧被分割成不同的 NAL 单元。因此,如果您提取一个关键帧并且它只显示 1/2 或 1/3 而不是完整图像,只需抓取下一个或两个 NAL 并 re-try 解码。
TL;DR: 我想从 AVI/MP4 文件中读取原始 h264 流,甚至 broken/incomplete.
几乎所有关于h264的文档都告诉我它由NAL数据包组成。好的。几乎所有地方都告诉我数据包应该以 00 00 01
或 00 00 00 01
这样的签名开头。例如,,
The format of H.264 is that it’s made up of NAL Units, each starting with a start prefix of three bytes with the values 0x00, 0x00, 0x01 and each unit has a different type depending on the value of the 4th byte right after these 3 starting bytes. One NAL Unit IS NOT one frame in the video, each frame is made up of a number of NAL Units.
好的。
我下载了 random_youtube_video.mp4 并从中删除了一帧:
ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.avi
并得到:
00 00 24 A9
而不是 00 00 00 01
此 AVI 文件播放完美
我对 mp4 容器做同样的事情:
如您所见,这里的字节数完全相同。 这个 MP4 文件播放完美
我尝试去除原始数据:
ffmpeg -i pic.avi -c copy pic.h264
此文件无法在 VLC 中播放,甚至生成此文件的 ffmpeg 也无法解析它:
我下载了 mp4 流分析器并得到:
MP4Box
告诉我:
Cannot find H264 start code
Error importing pic.h264: BitStream Not Compliant
学习 h264 的内部原理非常困难,因为什么都不会。
所以,我有问题:
- mp4 中的实际数据是什么?
- 解码该数据我必须阅读的内容(我的意思是不同的附件)
- 如何从这个 "broken" 原始流中读取流并获取解码图像(即使使用 ffmpeg)?
更新:
ffmpeg 中似乎有错误:
当我进行双重转换时:
ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.mp4
ffmpeg pic.mp4 -c copy pic.h264
但是当我直接转换文件时:
ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.h264
我有 NAL 签名和一个额外的 NAL 单元。其他字节相同(选中)。
这是错误?
更新
不,这不是错误,您必须使用选项 -bsf h264_mp4toannexb 将流保存为 "Annex B" 格式(带前缀)
"I want to read raw h264 streams from AVI files, even broken/incomplete."
"Almost everywhere told to me that the packet should start with a signature like :
00 00 01
or00 00 00 01
""...As you can see, here I have
00 00 24 A9
instead of00 00 00 01
"
您的 H264 是 AVCC 格式,这意味着它使用数据 sizes(而不是数据 start codes)。只有 Annex-B 会将您提到的签名作为起始代码。
您寻找帧,而不是通过寻找开始代码,而是您只是跳过帧大小以达到(请求的)帧的最终正确偏移...
AVI 处理:
读取大小(四)字节(32 位整数,Little Endian)。
提取下一个字节,最大为 size 数量。
这是您的 H.264 帧(AVCC 格式),解码字节以查看图像。
要转换为 Annex-B,请尝试用
00 00 00 01
.[=40 替换 H.264 frame bytes 的前 4 个字节=]
考虑您显示的 AVI 字节(参见 first 图片):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 4C 49 53 54 BA 24 00 00 6D 6F 76 69 ....LISTº$..movi
30 30 64 63 AD 24 00 00 00 00 24 A9 65 88 84 27 00dc.$....$©eˆ„'
C7 11 FE B3 C7 83 08 00 08 2A 7B 6E 59 B5 71 E1 Ç.þ³Çƒ...*{nYµqá
E3 9C 0E 73 E7 10 50 00 18 E9 25 F7 AA 7D 9C 30 ãœ.sç.P..é%÷ª}œ0
E6 2F 0F 20 00 3A 64 AA CA 5E 4F CA FF AE 20 04 æ/. .:dªÊ^OÊÿ® .
07 81 40 00 48 00 0A 28 71 21 84 48 06 18 90 0C ..@.H..(q!„H....
31 14 57 9E 7A CD 63 A0 E0 9B 96 69 C5 18 AE F2 1.WžzÍc à›–iÅ.®ò
E6 07 02 29 01 20 10 70 A1 0F 8C BC 73 F0 78 FA æ..). .p¡.Œ¼sðxú
9E 1D E1 C2 BF 8C 62 CE CE AC 14 5A A4 E1 45 44 ž.ῌbÎά.Z¤áED
38 38 85 DB 12 57 3E F6 E0 FB AE 03 04 21 62 8D 88…Û.W>öàû®..!b.
F6 F1 1E 37 1C A2 FF 75 1C F1 02 66 0C 92 07 06 öñ.7.¢ÿu.ñ.f.’..
15 7C 90 15 6F 7D FC BD 13 1E 2B 0C 14 3C 0C 00 .|..o}ü½..+..<..
B0 EA 6F 53 B4 98 D7 80 7A 68 3E 34 69 20 D2 FA °êoS´˜×€zh>4i Òú
F0 91 FC 75 C6 00 01 18 C0 00 3B 9A C5 E2 7D BF ð‘üuÆ...À.;šÅâ}¿
一些解释:
忽略前导多个
00
字节。4C 49 53 54 D6 3C 00 00 6D 6F 76 69
包括30 30 64 63
= AVI "List" header.AD 24 00 00
== decimal9389
是AVI自身的H264 item的大小(必须读入Little Endian)。
注意 AVI 字节包括...
- item 的注释总 size (AD 24 00 00
... or reverse对于 Little Endian:00 00 24 AD
)
- 后跟 item 数据 (00 00 24 A9 65 88 84 27 ... etc ... C5 E2 7D BF
).
这个 size 包括 AVI 的 4 个字节size”条目 + 的预期字节长度item 自己的字节数。可以简单写成:
AVI_Item_Size = ( 4 + item_H264_Frame.length );
AVI 中的 H.264 视频帧字节 :
接下来是item数据,即H.264视频帧。由于 formats/bytes 布局的巧合,它也包含 data 的 size 的 4 字节条目(因为你的 H264 在AVCC 格式,如果它是 Annex-B 那么你会在这里看到起始代码字节而不是大小字节)。
与 AVI 字节不同,这些 H264 size 字节以 Big Endian 格式写入。
00 00 24 A9
= 此视频帧的字节大小(而不是起始代码:00 00 00 01
)。65 88 84 27 C7 11 FE B3 C7
= H.264 关键帧(始终从 X5 开始,其中X
值基于其他设置)。记住在四个大小字节(甚至是起始码)之后如果后面跟着...
- byte
X5
= 关键帧 (IDR),示例字节65
. - byte
X1
= P 或 B 帧,示例字节41
. - byte
X6
= SEI(补充增强信息)。 - byte
X7
= SPS(序列参数集)。 - byte
X8
= PPS(图片参数集). - bytes
00 00 00 X9
= 访问单元定界符。
- byte
如果您在 AVI 文件中搜索完全相同的字节,则可以找到 H.264。查看 third 图片,这些是您的 H.264 字节(它们被剪切并粘贴到 AVI 容器中)。
有时一个帧被分割成不同的 NAL 单元。因此,如果您提取一个关键帧并且它只显示 1/2 或 1/3 而不是完整图像,只需抓取下一个或两个 NAL 并 re-try 解码。