从不完整的视频文件中提取元数据

npg*_*all 9 mp4 mpeg ffmpeg avi matroska

谁能告诉我元数据以常见的视频文件格式存储在哪里?如果它位于文件的开头,或分散在整个文件中.

我正在使用包含大量视频文件的远程对象存储,我想从这些文件中提取元数据,特别是视频持续时间和视频尺寸,而不将整个文件内容流式传输到本地计算机.

我希望这些元数据将存储在文件的前X个字节中,因此我可以从头开始而不是整个文件获取字节范围,将此部分文件数据传递给ffprobe.

出于测试目的,我创建了一个22MB的MP4文件,并使用以下命令仅向ffprobe提供前1MB的数据:

head -c1024K '2013-07-04 12.20.07.mp4' | ffprobe -
Run Code Online (Sandbox Code Playgroud)

它打印:

avprobe version 0.8.6-4:0.8.6-0ubuntu0.12.04.1, Copyright (c) 2007-2013 the Libav developers
  built on Apr  2 2013 17:02:36 with gcc 4.6.3
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1a6b7a0] stream 0, offset 0x10beab: partial file
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'pipe:':
  Metadata:
    major_brand     : isom
    minor_version   : 0
    compatible_brands: isom3gp4
    creation_time   : 1947-07-04 11:20:07
  Duration: 00:00:09.84, start: 0.000000, bitrate: N/A
    Stream #0.0(eng): Video: h264 (High), yuv420p, 1920x1080, 20028 kb/s, PAR 65536:65536 DAR 16:9, 29.99 fps, 30 tbr, 90k tbn, 180k tbc
    Metadata:
      creation_time   : 1947-07-04 11:20:07
    Stream #0.1(eng): Audio: aac, 48000 Hz, stereo, s16, 189 kb/s
    Metadata:
      creation_time   : 1947-07-04 11:20:07
Run Code Online (Sandbox Code Playgroud)

所以我看到第一个1MB足以提取视频持续时间9.84秒和视频尺寸1920x1080,即使ffprobe打印了关于检测部分文件的警告.如果我提供的不到1MB,则完全失败.

这种方法是否适用于其他常见的视频文件格式,以便可靠地提取元数据,或者在整个文件中使用任何常见格式分散元数据?

我知道容器格式的概念,并且可以使用各种编解码器来表示这些容器内的音频/视频数据.我不熟悉细节.所以我想这个问题可能适用于容器+编解码器的常见组合?提前致谢.

npg*_*all 11

在经过大量挖掘MP4,3GP和AVI的规格之后,可以回答我自己的问题......

AVI

根据AVI文件格式规范,元数据是AVI文件的开头.

视频持续时间不会逐字存储在AVI文件中,而是以dwMicroSecPerFrame x dwTotalFrames计算(以微秒为单位).

在规范的行之间进行读取,似乎可以直接从AVI文件中的偏移量读取许多元数据项而无需进行解析.但规范没有明确提及这些偏移,因此使用这个经验法则可能存在风险.

偏移32:dwMicroSecPerFrame,偏移48:dwTotalFrames,偏移64:dwWidth,偏移68:dwHeight.

因此对于AVI,可以仅使用文件的前X个字节来提取此元数据.

MP4,3GP(3GPP),3G2(3GPP2)

所有这些文件格式都基于ISO基础媒体文件格式,称为ISO/IEC 14496-12(MPEG-4 Part 12).

此格式允许将元数据存储在文件中的任何位置,但实际上它将在开始或结束时,因为原始捕获的音频/视频数据在中间连续保存.(但例外情况是,"碎片化"的MP4文件很少见.)

只有在开始时存储元数据的文件才能通过渐进式下载播放,但是由捕获设备或解码器来支持.

AFAICT这意味着要从这些文件中提取元数据,只需要文件的前X个字节,并且可以从该信息确定可能还需要最后的X个字节.但是不需要中间的字节.