h264在AVI,MP4和" Raw" h264流。不同格式的NAL单位(或ffmpeg bug)

时间:2017-10-06 08:41:18

标签: c video ffmpeg h.264 specifications

TL; DR:我想从AVI / MP4文件中读取原始h264流,甚至是已损坏/不完整。

几乎所有关于h264的文档都告诉我它包含NAL数据包。好的。几乎每个地方都告诉我,数据包应该以{{1​​}}或00 00 01这样的签名开头。例如,https://stackoverflow.com/a/18638298/8167678https://stackoverflow.com/a/17625537/8167678

  

H.264的格式是它由NAL单元组成,每个单元都是从NAL单元开始的   具有三个字节的起始前缀,值为0x00,0x00,0x01   并且每个单元具有不同的类型,具体取决于第4个的值   在这3个起始字节之后的字节。一个NAL单元不是一个帧   在视频中,每个帧由许多NAL单元组成。

好。

我下载了random_youtube_video.mp4并从中删除了一帧:

00 00 00 01

得到了: hexdump of AVI 红色部分 - 这是AVI容器的一部分,其他 - 实际数据。 如您所见,我在ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.avi而不是00 00 24 A9

此AVI文件播放完美

我为mp4容器做同样的事情: hexdump of mp4

如您所见,这里的字节完全相同。 此MP4文件播放完美

我尝试删除原始数据: 00 00 00 01 Raw data

此文件无法在VLC中播放,甚至生成此文件的ffmpeg也无法解析它: ffmpeg error

我下载了mp4流分析器,得到了: Analysis

ffmpeg -i pic.avi -c copy pic.h264告诉我:

MP4Box

当没有任何作用时,很难学习h264的内部结构。

所以,我有疑问

  1. mp4中的实际数据是什么?
  2. 我必须阅读解码数据(我的意思是不同的附件)
  3. 如何读取流并获取解码图像(即使使用ffmpeg)来自这个"破坏"原始流?
  4. 更新

    ffmpeg似乎有错误:

    当我进行双重转换时:

     Cannot find H264 start code
     Error importing pic.h264: BitStream Not Compliant
    

    enter image description here

    但是当我直接转换文件时:

    ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.mp4 ffmpeg pic.mp4 -c copy pic.h264 with NALs

    我有NAL签名和一个额外的NAL单位。其他字节相同(选中)。

    这是错误吗?

    更新

    不是,这不是错误,U必须使用选项-bsf h264_mp4toannexb将流保存为"附件B"格式(带前缀)

1 个答案:

答案 0 :(得分:8)

  

"我想从AVI文件中读取原始的h264流,即使已损坏/不完整。"

     

"几乎每个地方都告诉我,数据包应该以如下签名开头:
00 00 0100 00 00 01"

     

" ...正如您所看到的,我在这里00 00 24 A9代替00 00 00 01"

您的H264采用AVCC格式,这意味着它使用数据尺寸(而不是数据启动代码)。只有附件B才能将您提到的签名作为起始代码。

你寻找帧,而不是通过寻找起始码,而是你只是跳过帧大小来达到(请求的)帧的最终正确偏移...

AVI处理:

  • 读取大小(四个)字节(32位整数,Little Endian)。

  • 提取下一个字节,直到大小金额。

  • 这是您的H.264帧(采用AVCC格式),解码字节以查看图像。

  • 要转换为附件B,请尝试用00 00 00 01替换H.264 帧字节的前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&#34;列表&#34;报头中。

  • AD 24 00 00 ==十进制9389是AVI自己的H264项目大小(在Little Endian中读取)。

请注意,AVI字节包括...
- 项目的总尺寸AD 24 00 00 ...或Little Endian反向:{{ 1}})
- 后跟 item 数据(00 00 24 AD)。

大小既是AVI的四个字节字节本身,也是项目自身字节的长度。或者简单地说:
00 00 24 A9 65 88 84 27 ... etc ... C5 E2 7D BF

AVI中的H.264视频帧字节

接下来是数据,即H.264 视频帧。由于格式/字节布局完全重合,它也为数据大小保留了一个4字节的条目(因为你的H264是AVCC格式,如果它是附件B然后你会看到这里的起始码字节而不是大小字节)。与AVI不同,此大小以Little Endian格式编写。

  • AVI_Item_Size = ( 4 + item_H264_Frame.length ); =此视频帧的字节大小(而不是起始码:00 00 24 A9)。

  • 00 00 00 01 = H.264 关键帧(始终从X5开始,其中65 88 84 27 C7 11 FE B3 C7值基于其他设置)。

  • 请记住四个大小字节(甚至是起始码),如果后面跟着......

    • byte X = keyframe(IDR),示例字节X5
    • byte 65 = P或B帧,示例字节X1
    • byte 41 = SEI(补充增强信息)。
    • byte X6 = SPS(序列参数集)。
    • byte X7 = PPS(图片参数集)。
    • bytes X8 =访问单位分隔符。

如果在AVI文件中搜索完全相同的字节,则可以找到H.264。请参阅third图片,这些是您的H.264字节(它们被剪切并粘贴到AVI容器中)。

有时框架被切成不同的NAL单元。因此,如果您提取关键帧并且它仅显示1/2或1/3而不是完整图像,则只需抓住下一个或两个NAL并重新尝试解码。