QuickTime And Mp4

1. 历史

MOV 格式是 QuickTime 的一种容器,不光包括视频音频,还可以包含Java,脚本,Skin,图片等,是一种很复杂的封装格式。(以前苹果网站上还有专门用 MOV 做的电影主题网站,还可以把游戏封装到 MOV 里面)MP4 是把 MOV 格式中音频视频部分提取出来标准化,也可以装一些简单的脚本,复杂程度远不及 MOV。可以说 MP4 只是 MOV 的一个标准化了的子集。IE
M4V 以前是苹果有 DRM 版权保护的 MP4 格式,后来就变乱了,没那么严格叫 M4V。苹果把自己的 MOV 格式精简下来,提交给 ISO 标准化方案,诞生了 MP4 格式。所以苹果都兼容 MOV,MP4 ,本来就是自己一家的。mp4就不解释了, 封装mpeg4系列编码的通用iso容器,m4v是苹果开发的容器 可以理解成apple版mp4 和mp4十分接近 文件结构也和mp4基本相同 通常可以互改后缀不产生问题 区别在于m4v支持DRM保护 和AC3音频 而mp4好像不支持ac3音频 DRM上m4v好像也有点特别的地方,mov就和上面2个明显不同了 一般mov文件由专业软件的输出编码器编码产生 支持很多mpeg4系列外的专业编码 比如 RGB YUV PNG/TIFF序列 DV5P ProRes等 音频则支持LPCM/ADPCM ac3 DTS等mp4不支持的编码 mov的文件结果和mp4也有很多不同 mov不是iso容器 mp4的一些标准是基于mov的 容器的历史比mp4早 可以理解为apple版avi
​ 至于为什么3者都由quicktime解码原因很简单 mp4/m4v的一部分标准基于mov 他们都属于quicktime文件 专业软件导入窗口可以观察一下
mp4 m4v mov都在quicktime files文件类别 在大型视频软件中 一般它们都要调动quicktime解码

1.2 mp4文件格式解析

基本术语

  • box

    由唯一类型标识符和长度定义的面向对象的构建
    
  • container box

    用来容纳一组相关的box的box,container box通常都不是fullbox
    
  • chunk

    同一轨道的一组连续的采样
    
  • hint chunk

    不包含媒体数据,但包含了将一个或多个轨打包到流频道的指示
    
  • media data box

    用来容纳实体数据的box
    
  • movie box

    子box定义了元数据的容器box
    
  • sample

    与某个时间戳相关联的所有数据,video sample就是一帧视频,或者一组连续视频帧,audio sample就是一段连续的压缩音频
    
  • sample description

    定义和描述轨中的采样的格式的结构
    
  • sample table

    指明sample时序和物理布局的表
    
  • track

    按照时间排序的相关的采样,对于媒体数据来说,track表示一个视频或者音频序列
    

2 容器格式介绍

这里贴一张基本格式图

QuickTime And Mp4_第1张图片
mp4格式低清图描述.png

2.1 box结构

一个box基本结构是由box header和box里面包含的数据组成,box header 定义了大小和类型,下面对box header字段用c语言进行伪代码的描述,注意,在数据结构中定义的largesize是条件存在的.也就是可能存在也可能不存在,具体是否存在根据相关的条件进行确定

struct BoxHeader {
  uint32 size;
  uint32 type;
  if (size ==1) {
    uint64 largesize;
  }
}

Full Box Header 定义

struct FullBox { 
  uint32 size;
  uint32 type;
  if (size == 1) {
    uint64 largesize;
  }
  version:8;
  flags:24; // share a int with version.
}

2.2 各种常见的box的分析

**ftype**

file type box:表明文件的位置,只能包含在文件层,不能被其他box相包含,一般是在文件的开始位置,包含一个32位的major brand.一个32位的minor version.一个以32为单位的compatible数组,这些都是用来指示文件应用级别的信息, 使用c语言作为伪代码描述的格式如下

align(8) struct FileTypeBox { -> 'ftyp'
  BoxHeader header;
  uint32 major_brand;
  uint32 minor_version;
  int32[] compatible_brands[];
}

Major_brand : mp41,mp42,isom等

minor_version:是major brand的次版本标示

compatible_brands :是一个 list.一直到这个

FileTypeBox 长度是24个字节

**mdat**

这个box位于文件层次,它可以有多个,也可以一个都没有(当媒体数据全部都是外部文件引用的时),用来存储媒体数据,数据直接跟在box type字段后面,它的结构由metadata来描述,metadata通过文件中的绝对偏移来引用媒体数据,它在标准文件中通过媒体数据的绝对偏移来引用数据

align(8) struct MediaDataBox {
   -> "mdat"
     BoxHeader header;
         int8 data[];
}

**free**

free box 中的内容是无关紧要的,可以被忽略,该box被删除后,不会对播放产生任何影响,它的type域可以是free或者skip

align(8) struct FreeBox {
   -> "free" or "skip"
     BoxHeader header;
     int8 data[];
}

**moov**

movie box,用来存放媒体的metadata信息,其内容信息由子box诠释,该box有且只有一个并且包含在文件层,一般情况下moov box会紧随着ftype box出现,但也有放在文件末尾的,定义如下

struct MovieBox {
   -> "moov"
     BoxHeader header;
}

**mvhd**

用来存放文件的总体信息,比如时常和创建时间,独立于媒体并且与整个播放相关,mvhd box在标准文档中定义如下

struct MovieHeaderBox {
  -> "mvhd"
  FullBoxHeader header;
  if (version ==1) {
    unsigned int64 creation_time;
    unsigned int64 modification_time;
    unsigned int32 timescale;
    unsigned int64 duration;
  }
  else {
    unsigned int32 creation_time;
    unsigned int32 modification_time;
    unsigned int32 timescale;
    unsigned int32 duration;
  }
  int32 rate = 0x00010000;
  int16 volume = 0x0100;
  const reserved:16 = 0;
  const unsigned int32[2] reserved = 0;
  int32 matrix[9] ={0x0001000,0,0,0,0x00010000,0,0,0,0x40000000};
  bit(32)[6] pre_defined = 0;
  unsigned int32 next_track_ID;
}

对于mvhd的一些字段的解释

字段 含义
version 取数值0或者1
create_time 用来制定创建的时间,单位相对于UTC时间1904-01-01的秒数
modification_time 用来指定最后修改时间
timescale 用来指定文件媒体在1s时间内的刻度值,一秒时间对应的单元数量
duration 用来指定该track的时间长度,以timescale为单位
rate 用来指定推荐播放速率,高16位和低16位分别是小数点整数和小数部分
volume 用来指定推荐的音量,与rate类似,8.8格式
matrix 用来指定视频变换矩阵
Next_track_ID 用来指定下一个track使用的id号

**trak**

trak box 是一个container box.它的子box包含了该track的媒体数据引用和描述(hint track除外)。一个mp4文件中的媒体可以包含多个track.但是至少有一个track.这些track之间彼此独立,有自己的时间和空间信息,trak box必须包含一个tkhd bo和mdia box.

align(8) struct TrackBox {
  -> "trak"
    HeaderBox header;
}

**tkhd**

包含了该track的特性和总体信息,比如时常,宽高等等,tkhd box 在标准文档中的定义如下

align(8) struct TrackHeaderBox {
  -> 'tkhd'
    FullBoxHeader header;
  if (version ==1) {
    unsigned int64 creation_time;
    unsigned int64 modification_time;
    unsigned int32 track_ID;
    const unsigned int32 reseverd = 0;
    unsigned int64 duration;
  }
  else {
    unsigned int32 creation_time;
    unsigned int32 modificaition_time;
    unsigned int32 track_ID;
    const unsigned int32 reserved = 0;
    unsigned int32 duration;
  }
  const unsigned int32 reserverd[2] = 0;
  int16 layer =0;
  int16 alternate_group = 0;
  int16 volume = if(track_is_audio 0x0100) else 0;
  const unsigned int16 reserved = 0;
  int32 matrix[9] = {0x00010000,0,0,0,0x00010000,0,0,0,0x40000000};
  unsigned int32 width;
  unsigned int32 height;
}

下面是对一些字段的解释

字段 含义
creation_time 创建时间
modification_time 修改时间
track_ID 指定track的id号,不能重复并且不能为0
reserverd 保留位
duration 指定track的时间长度
reserved 保留
layer 指定视频的层,值小的在上面
alternate_group 指定track分组信息,默认为0表示该track没有与其他track有群组关系
volume 8.8 (1.0表示最大音量)
reserved 保留位
matrix 指定视频变换矩阵
width 宽高16.16格式,与sample描述中的实际画面大小比值
height 宽高16.16格式,用来指定高

注意:上面的格式在使用程序验证的阶段可能需要修改之后才能够使用

**mdia**

包含整个track的媒体信息,比如媒体类型和sample信息,它在标准文档中的定义如下.这是一个容器的容器

aligned(8) struct MediaBox {
   -> 'mdia'
}

mdia包含的孩子容器

**mdhd**

包含了该track的总体信息,mdhd和tkhd内容大概是相同的,tkhd通常是对指定的track设定相关属性和内容,而mdhd是针对于独立的media来设置的,一般情况下二者相同

align(8) class MediaHeaderBox {
    FullBox -> 'mdhd',version,0
      if (version == 1) {
        unsigned int64 creation_time;
        unsigned int64 modification_time;
        unsigned int32 timescale;
        unsigned int64 duration;
      }
      else {
        unsigned int32 creation_time;
        unsigned int32 modification_time;
        unsigned int32 timescale;
        unsigned int32 duration;
      }
      bit1 pad = 0;
      unsigned int(5)[3] language; // 这里有一点出入,16bit可能都是语言
      unsigned int16 pre_defined = 0; // 这里是质量
}

**hdlr**

解释了媒体播放过程中的相关信息,该box也可以被包含在meta box(meta)中,它在标准文档中的定义如下

aligned(8) struct HandlerBox {
    FullBox header; -> 'hdlr',0,0
      unsigned int32 component_type = 0;
        unsigned int32 component_subtype = 0;
      unsigned int32 component_manufacture = 0;
        unsigned int32 component_flags = 0;
      unsigned int32 component_flags_mask = 0;
      unsigned int32 component_name;
}

component_subtype ->在media box中,有四个可以进行取的值

"vide" - video track

"soun" - audio track

"hint" - hint track

**minf**

Media Information Box, minf box包括了所有描述该track中的媒体信息的对象,信息存储在其子box中,它在标准文档中的定义如下

align(8) struct MediaInfomationBox {
  Box header -> "minf";
}

**vmhd**

用在视频track中,包含当前track的视频描述信息(如视频编码信息等等),它的定义是

align(8) struct VideoMediaHeaderBox {
   FullBox header = 'vmhd',version = 0,1;
   unsigned int16 graphicsmode =0;
   unsigned int16[3] opcolor = {0,0,0};
}

数据解释

名称 解释
graphicsmode 视频合成模式,为0时拷贝原始图像,否则与opcolor进行合成
opcolor {red,green,blue}

**smhd**

smhd用在音频track中,包含当前track的音频描述信息(如编码格式等信息)

align(8) class SoundMediaHeaderBox {
  FullBox header -> 'smhd',0,0
    int16 balance = 0; // 立体声音平衡,[8.8格式] -1.0表示全部在左声道,1.0表示全部在右声道 
    const unsigned int16 reserved = 0;
}

**dinf**

dinf box 解释如何定位媒体信息,是一个container box.dinf box 一般包含一个dref box.也就是data reference box.

**dref**

dref box 是用来设置当前box描述信息的data_entry.dref box下会包含若干个"url"或者"urn",这些box组成一个表,用来定位track数据,简单的说,track可以被分成若干段,每一段都可以根据"url"或者"urn"指向的地址来获取数据,sample描述中会用这些片段的序号,将这些片段组成一个完整的track.一般情况下,当数据被完全包含在文件中的时候,"url"和"urn"的定位字符串是空的

align(8) struct DataEntryUrlBox(bit(24) flags) {
   FullBox -> 'url', version,flags;
   string location;
}
align(8) struct DataEntryUrnBox(bit(24) flags) {
  FullBox -> 'urn',version,flags;
  String name;
  String location;
}
align8 struct DataReferenceBox {
  FullBox header -> 'dref',0,0;
  unsigned int32 entry_count;
  for (i = 0;i<=entry_count;i++) {
    DataEntryBox(entry_version,entry_flags) data_entry
  }
    
}

entry_flags:它的数值不是确定的,但是有一个特殊的值,0x000001用来表示当前media和moov包含的数据一致

**stbl**

stbl: stbl box几乎是普通的mp4文件中最复杂的一个box.sample是媒体数据存储的单位,储存在media的chunk中,chunk和sample的长度可以互不相同,如下图所示

stbl包含了关于track中sample所有时间和位置的信息,以及sample的编解码等信息,利用这个表,可以解释sample的时序,类型,大小,以及在各自存储容器中的位置,stbl是一个container box,其子box包括:

  1. sample description box(stsd)
  2. time to sample box(stts)
  3. sample size box(stsz stz2)
  4. sample to chunk box(stsc)
  5. chunk offset box(stco,co64)
  6. composition time to sample box(ctts)
  7. sync sample box(stss)

其中stsd必不可少,其中至少包含了一个条目,该box包含了data reference box 进行sample数据检索的信息,没有"stsd"就没有办法计算media sample的存储位置,stsd包含了编码的信息,其存储的信息信息用来计算media sample的存储位置,这个存储的信息随着媒体类型的不同而不同

aligned(8) struct SampleTableBox {
  Box header -> 'stbl';
}

**stsd**

box header和version字段后字段后面会有一个entry_count字段,根据entry的个数,每个entry会有type信息,如'vide','sund'等,根据type不同,sample description会提供不同的描述,对于video track,会有VisualSampleEntry等类型信息,对于audio track会有AudioSampleEntry类型信息,视频的编码类型,宽高,长度,音频的声道,采样等信息都会出现在这个box中

abstract class SampleEntry {
  Box header -> format;
  const unsigned int8[6] reserved = 0;
  unsigned int16 data_reference_index;
}
class SampleDescriptionBox {
  FullBox header -> 'stsd',version,0;
  unsigned int32 entry_count;
  for (int i=0;i

**ctts**

class CompositionOffsetBox {
  FullBox header 'ctts',version,0;
  unsigned int32 entry_count;
  if (version == 0) {
    for (int i=0;i

**stts**

stts box存储了sample的duration,描述了sample时序的映射方法,我们通过它可以找到任何时间的sample.stts box 可以包含一个压缩的表来映射时间和sample序号,用其他的表来提供每个sample的长度和指针,表中每个条目提供了在同一个时间偏移量里面连续的sample编号,以及sample中的偏移量,递增这些偏移量,就可以建立一个完整的time to sample表

struct TimeToSampleBox {
  FullBox header 'stts',0,0;
  unsigned int32 entry_count;
  for (int i=0;i

**stsz**

stsz定义了每个sample的大小,包含了媒体中全部sample的数目和一张给出每个sample大小的表,这个box相对来说,体积是比较大的.

aligned(8) class SampleSizeBox extends FullBox(‘stsz’, version = 0, 0) 
{
    unsigned int(32) sample_size;
    unsigned int(32) sample_count;
    if (sample_size==0) 
    {
        for (i=1; i <= sample_count; i++) 
        {
            unsigned int(32) entry_size;
        }
    }
}

ps 由于对于一些字段的理解仍然不是很顺利,因此这里就不去列举出某些字段的含义了
stss:
“stss”确定media中的关键帧。对于压缩媒体数据,关键帧是一系列压缩序列的开始帧,其解压缩时不依赖以前的帧,而后续帧的解压缩将依赖于这个关键帧。“stss”可以非常紧凑的标记媒体内的随机存取点,它包含一个sample序号表,表内的每一项严格按照sample的序号排列,说明了媒体中的哪一个sample是关键帧。如果此表不存在,说明每一个sample都是一个关键帧,是一个随机存取点。它在标准文档中的定义为

aligned(8) class SyncSampleBox extends FullBox(‘stss’, version = 0, 0) 
{
    unsigned int(32) entry_count;
    int i;
    for (i=0; i < entry_count; i++) 
    {
        unsigned int(32) sample_number;
    }
}

stco:“stco”定义了每个chunk在媒体流中的位置,sample的偏移可以根据其他box推算出来。位置有两种可能,32位的和64位的,后者对非常大的电影很有用。在一个表中只会有一种可能,这个位置是在整个文件中的,而不是在任何box中的,这样做就可以直接在文件中找到媒体数据,而不用解释box。需要注意的是一旦前面的box有了任何改变,这张表都要重新建立,因为位置信息已经改变了。它在标准文档中的定义为:

根据上面的数据画一个图处理

Section 2 这里使用了另外的方式对某些item进行了解释

// 之后需要对笔记进行整理

Movie Profile Atom

*注意*:profile atoms在QuickTime文件格式中已弃用。 以下信息旨在记录包含profile atoms的现有内容,不应用于新开发

movie profile atom总结了movie的特征和复杂性,例如所需的编解码器和最大比特率,以帮助播放器应用或设备快速确定他们是否具有播放movie的必要资源

movie的特征通常包括movie的最大视频和音频比特率,音频和视频编解码器类型的列表,movie的视频尺寸以及任何适用的MPEG-4配置文件和级别。 这是也可以通过更详细地检查movie文件的内容而获得的所有信息。 此摘要旨在允许应用程序或设备快速确定他们是否可以播放movie。 它不打算作为movie中其他地方找不到的信息的容器,也就是说它只是作为其他信息的一个 子集,方便可以快速地寻找到信息

Syntax

aligned(8) class ProfileAtom
    extends FullAtom('prfl') {
    unsigned int(32) feature-record-count;
    for (i=1; i

可能采用的数值有

Brand Code Description Profile Parent[父节点]
0x20202020 mvbr 最大视频比特率vbr Movie/Video Track
0x20202020 avvb 平均视频比特率 Movie/Video Track
0x20202020 mabr 最大音频比特率 Movie/Track
0x20202020 avab 平均音频比特率 Movie/Sound Track
0x20202020 vfmt 视频编码格式 Movie/Video Track
0x20202020 afmt 音频编码格式 Movie/Video Track
0x20202020 m4vp MPEG-4 Video Profile Movie/Video Track
0x20202020 m4vo MPEG-4 Video Object Type Movie/Video Track
0x20202020 mp4a MPEG-4 Audio Codec Movie/Sound Track
0x20202020 mvsz 最大视频尺寸in Movie Movie
0x20202020 tvsz 最大视频尺寸in Track Movie/Video Track
0x20202020 vfps 单个轨道最大视频帧率 Movie/Video Track
0x20202020 tafr 单个轨道平均视频帧率 Movie/Video Track
0x20202020 vvfp 可变视频帧率指示 Movie/Video Track
0x20202020 ausr Sample Entry音频采样率 Movie/Sound Track
0x20202020 avbr 可变音频比特率指示 Movie/Sound Track
0x20202020 achc 音channel个数 Movie/Sound Track

Maximum Video Bit Rate

  • Containing profile atom

    Track (video), movie

  • Reserved

    0x00000000

  • part-ID

    0x20202020 (universal feature)

  • feature-code

    'mvbr'

  • feature-value

    Unsigned int(32) indicating maximum video bit rate in bits per second

这里的数值可能比实际的比特率要大,因此不可以将这个解释成实际的比特率

这里的数值最低的要求是和实际的比特率相等,或者大于它

更加详细的解释参考

Profile Atom Guidelines

Color Table Atoms

Color table atoms定义了在仅支持256种颜色的设备上显示movie的首选颜色列表。 该列表最多可以包含256种颜色。 这些可选atoms类型值为'ctab'。 color table atom包含Macintosh颜色表数据结构

[图片上传失败...(image-66be71-1577339732284)]

color table atom包含以下数据元素。

  • Size
    32位整数,指定此color table atom中的字节数。
  • Type
    标识atom类型的32位整数; 此字段必须设置为“ctab”。
  • Color table seed
    必须设置为0的32位整数。
  • Color table flags
    必须设置为0x8000的16位整数。
  • Color table size
    一个16位整数,指示后面的color array中的颜色数。 这是一个零相对值; 将此字段设置为0表示数组中有一种颜色。
  • Color array
    颜色数组。 每种颜色由四个无符号的16位整数组成。 第一个整数必须设置为0,第二个是红色值,第三个是绿色值,第四个是蓝色值。

User Data Atoms

user data atoms允许您定义和存储与QuickTime对象相关联的数据,例如movie 'moov',track 'trak'或media 'mdia'。这也包括QuickTime寻找的信息,例如版权信息或movie是否应该循环,以及由应用程序提供的任意信息 - QuickTime完全忽略的。

其直接父结点是movie atom的user data atom包含与movie作为整体相关的数据。其父结点是轨道atom的user data atom包含与该特定轨道相关的信息。 QuickTime movie文件可以包含许多user data atoms,但是只允许一个user data atom作为任何给定movie atom或轨道atom的直接子节点。

user data atom的atom类型为“udta”。在user data atom内的是描述每条用户数据的atoms列表。用户数据提供了一种扩展存储在QuickTime movie中的信息的简单方法。例如,user data atom可以存储movie的窗口位置,回放特性或创建信息。

本节介绍QuickTime可识别的 data atoms。您可以创建自己的应用程序可识别的新的data atom类型。应用程序应忽略它们不能识别的任何data atom类型

QuickTime And Mp4_第2张图片
userdataatom布局结构.gif

Track Atoms

track atom定义了movie的单个轨道。 movie可能由一个或多个曲目(tracks)组成。 每个轨道独立于movie中的其他轨道并且携带其自己的时间和空间信息。 每个轨道atom包含其相关联的媒体atom。

轨道专门用于以下目的:

  • 包含媒体数据引用和说明(媒体轨道)。
  • 要包含修饰符轨道(补间等)。
  • 包含流协议的包化信息(提示轨道)。 提示轨道可以包含对媒体样本数据或媒体样本数据的副本的引用。 有关提示轨道的详细信息,请参阅Hint Media。

*图 2-6* 显示了track atom的布局。 轨道atom的atom类型值为'trak'。 轨道atom需要track header atom('tkhd')和media atom('mdia')。 其他子atom是可选的,并且可以包括track clipping atom('clip'),track matte atom('matt'),edit atom('edts'),track reference atom('tref'),track load settings atom('load'),track input map atom('imap')和user data atom('udta')

QuickTime And Mp4_第3张图片
TrackAtom布局.png

// 为了流程起见,这里的笔记暂时就不多继续总结了
[^-> ""]: 在使用伪代码描述相关的数据结构的时候,会发现在文件的开头通过这种形式,指出了该结构的type名称,这是这种方式在本文章中的默认规定

你可能感兴趣的:(QuickTime And Mp4)