范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

MP4格式优化短视频播放体验必须先了解它

  (本文基本逻辑:MP4 封装格式概览   重要 Box 具体信息介绍   实战中对 MP4 Box 信息的使用)
  MP4 也称为  MPEG-4 第 14 部分 ,是继承 MPEG-4 第 12 部分的 ISO 基础媒体文件格式并略作扩展而来,定义于标准 ISO/IEC 14496-14 中,是一种标准的数字多媒体容器格式。
  在现在互联网使用的视频中,MP4 是最常见的格式之一,尤其是短视频。 如果我们要对短视频的播放体验做优化,了解 MP4 的格式是非常必要的。  所以本文我们将介绍一下如下内容: MP4 格式概览 File Type Box(ftyp) 介绍 Movie Box(moov) 及其重要子 Box 的介绍 Media Data Box(mdat) 介绍 实战解析案例: moov 和 MP4 视频的秒开   :  moov Box 位置对 MP4 秒开的影响。 MP4 视频的预加载 :最少加载多少数据可以渲染出 MP4 视频首帧。 1、MP4 格式概览
  MP4 文件的数据都是封装在一个又一个名为 Box 的单元中。  一个 MP4 文件由若干个 Box/FullBox 组成,每个 Box 包含了 Header 和 Data。FullBox 是 Box 的扩展,其包含的 Header 增加了 version(8bits) 和 flags(24bits) 部分。Header 部分包含了 size(32bits) 和 type(32bits) 部分。size 用于描述整个 Box 的长度,type 用于描述 Box 的类型。当 size 为 0 时,表示这是文件中最后一个 Box;当 size 为 1 时,表示 Box 长度需要更多 bits 来描述,这时在后面会定义一个 64bits 的 largesize 来描述 Box 的长度。当 type 是 uuid 时,代表 Box 中的数据是用户自定义扩展类型。
  Box 的数据格式定义: aligned(8) class Box (unsigned int(32) boxtype, optional unsigned int(8)[16] extended_type) {     unsigned int(32) size;     unsigned int(32) type = boxtype;     if (size==1) {        unsigned int(64) largesize;    } else if (size==0) {        // box extends to end of file    }     if (boxtype==‘uuid’) {             unsigned int(8)[16] usertype = extended_type;         }}
  FullBox 的数据格式定义: aligned(8) class FullBox(unsigned int(32) boxtype, unsigned int(8) v, bit(24) f) extends Box(boxtype) {    unsigned int(8) version = v;    bit(24) flags = f; }
  下图是对 Box/FullBox 结构的描述:
  Box 有不同的类型,有着不同的数据结构,Box 中还可以包含其他 Box。Box 的类型详见下表(其中  *   表示当父 Box 存在时,则必须包含该 Box):
  在众多类型的 Box 中,最常见的第一层级 Box 有 3 个,分别是  ftyp  、 moov  、 mdat  。
  下面就着重介绍和分析一下这几个 Box 以及他们包含的子 Box。
  C++音视频学习资料免费获取方法:关注音视频开发T哥  ,点击「链接」 即可免费获取2023年最新 C++音视频开发进阶独家免费学习大礼包!
  2、File Type Box(ftyp) 解析
  ftyp,即 File Type Box,包含文件的类型、版本、兼容信息等。在一个 MP4 文件中,该 Box 有且只有一个,并且需要尽可能放在文件最开始的位置,除非有必要的固定长度的文件签名信息 Box 可以放在该 Box 前面,其他非固定长度的 Box 数据都必须放在它后面。
  ftyp Box 的数据格式定义: aligned(8) class FileTypeBox extends Box(‘ftyp’)      {         unsigned int(32) major_brand;         unsigned int(32) minor_version;         unsigned int(32) compatible_brands[]; // to end of the box     }
  下面是一个 MP4 文件的 ftyp Box 的 16 进制示例数据:
  按照上文所述,前 4 个字节  00 00 00 20   表示该 Box 的 size,即 32 字节。接着 4 个字节  66 74 79 70   是该 Box 的 type,即  ftyp  。接下来 4 个字节  69 73 6F 6D   是主 brand,表示该文件所遵循的标准规格,这里是  isom  ,即遵循 ISO Base Media File Format。接下来的 4 个字节  00 00 02 00   表示的是这个 Box 格式的版本号。接下来的 16 个字节则是兼容的 compatible brands,即该文件兼容的其他标准规格,这里是  isom  、 iso2  、 avc1  、 mp41  。
  虽然这个 Box 是 MP4 文件所必须的,但是通常我们并不太关注这里的信息,所以这里不再多讲。 3、Movie Box(moov) 解析
  moov,即 Movie Box,包含文件中所有媒体数据的宏观描述信息。实际的音视频数据都存储在 mdat 中,那么多的数据,我们怎么确定每一帧数据的位置呢,这就需要解析 moov 中的数据来得到实际音视频数据的索引。
  moov Box 可以说是 MP4 文件中最重要的 Box,一般播放器的实现都需要读取到 moov 的数据才能开始播放流程。
  moov Box 是一个 container box,所以它的数据格式定义比较简单: aligned(8) class MovieBox extends Box(‘moov’) { }
  moov 通常包含 1 个 mvhd 和若干个 trak。 3.1、Movie Header Box(moov/mvhd)
  mvhd,Movie Header Box,包含与具体媒体数据无关,但与整体播放相关的信息,比如 timescale、duration 等信息。
  mvhd Box 的数据格式定义: aligned(8) class MovieHeaderBox extends FullBox(‘mvhd’, version, 0) {      if (version==1) {          unsigned int(64) creation_time;          unsigned int(64) modification_time;          unsigned int(32) timescale;          unsigned int(64) duration;      } else { *// version==0*          unsigned int(32) creation_time;          unsigned int(32) modification_time;          unsigned int(32) timescale;          unsigned int(32) duration;      }      template int(32) rate = 0x00010000; *// typically 1.0*      template int(16) volume = 0x0100; *// typically, full volume*      const bit(16) reserved = 0;      const unsigned int(32)[2] reserved = 0;      template int(32)[9] matrix = { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };      *// Unity matrix*      bit(32)[6] pre_defined = 0;      unsigned int(32) next_track_ID;  }
  下面是一些字段的含义: version:该 Box 的版本号。 creation_time:创建时间。 modification_time:最新修改时间。 timescale:表示 1s 的时长被切割的单元数,它体现了时间的精度,后面表示时长的字段均以 timescale 定义的精度来计量时间。比如,timescale 是 1000,则表示 1s 的时间被切割为 1000 个单元,每个单元即 1/1000s,即 1ms,也就是说该视频的计时精度是毫秒。 duration:这个值是从后面 trak 的时长派生来的,表示视频时长,以 timescale 定义的精度计量。比如,timescale 是 1000,duration 是 29782,则表示时长是  29782 * 1 / 1000   秒,即 29.782 秒。 rate:表示播放速率,是以 16.16 形式的定点数表示。比如, 0x00010000   表示播放速率是 1.0,对应的是正常的播放速率。 volume:表示音量,是以 8.8 形式的定点数表示。比如, 0x0100   表示音量是 1.0,对应最大音量。 matrix:表示视频的图形变换矩阵数据。这里的默认值是  { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }  。 next_track_ID:下一条 track 的 ID,这个值不能为 0。一般,对于一个 MP4 文件来说这个值是现有的最大 track ID 加 1。比如,一个 MP4 包含音频和视频两个 track,对应的 ID 分别为 1 和 2,那么 next_track_ID 一般就为 3。 3.2、Track Box(moov/trak)
  trak,即 Track Box。一个 trak Box 包含一个单独轨道的信息。每个 trak 都是独立的,包含自己的空域和时域信息。每个 trak 包含一个 tkhd 和 mdia。有两种类型的 trak Box:media track 和 hint track。media track 包含媒体轨道的信息,一个文件至少会包含一个 media track 类型的 Box;hint track 包含用于流媒体协议的打包信息。
  trak Box 是一个 container box,所以它的数据格式定义是: aligned(8) class TrackBox extends Box(‘trak’) {  }3.3、Track Header Box(moov/trak/tkhd)
  tkhd,即 Track Header Box,包含该轨道的创建时间、标识该轨道 的 ID、轨道的播放时长、音量、宽高等信息。
  tkhd Box 的数据格式定义: aligned(8) class TrackHeaderBox extends FullBox(‘tkhd’, version, flags){      if (version==1) {          unsigned int(64) creation_time;          unsigned int(64) modification_time;          unsigned int(32) track_ID;          const unsigned int(32) reserved = 0;          unsigned int(64) duration;      } else { *// version==0*          unsigned int(32) creation_time;          unsigned int(32) modification_time;          unsigned int(32) track_ID;          const unsigned int(32) reserved = 0;          unsigned int(32) duration;      }      const unsigned int(32)[2] reserved = 0;      template int(16) layer = 0;      template int(16) alternate_group = 0;      template int(16) volume = {if track_is_audio 0x0100 else 0};      const unsigned int(16) reserved = 0;      template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };      *// unity matrix*      unsigned int(32) width;      unsigned int(32) height;  }
  下面是一些字段的含义: version:该 Box 的版本号。这里是 0 或 1。 flags:有这些定义值。 creation_time:创建时间。 modification_time:最近修改时间。 track_ID:当前 track 的 ID。非 0,并且不可重复。 duration:当前 track 的时长。使用的 timescale 是来自于 Movie Header Box(mvhd)。 layer:视频轨道的前后排序。数值越小则越靠近观看者。通常这个值默认为 0。 alternate_group:可替代的备份数据组。如果为 0,表示当前 track 没有备份的 track 数据;非 0,则表示可能存在 group 号相同的 track 数据作为备份。当然一个 group 可能只有一个 track。 volume:表示音量,是以 8.8 形式的定点数表示。比如, 0x0100   表示音量是 1.0,对应最大音量。只对音频轨道有效。可以多音频轨道组合,并复用 mvhd 的音量;也可以不同的音频轨道设置不同的音量。 matrix:表示视频的图形变换矩阵数据。这里的默认值是  { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }  。 width & height:表示视频轨道尺寸,各自是以 16.16 形式的定点数表示。这个尺寸不用和图像 sample 的尺寸一致,所有图像 sample 在做矩阵变换前都会缩放处理为该尺寸。 3.4、Media Box(moov/trak/mdia)
  mdia,即 Media Box。包含声明当前轨道信息的所有对象。这个 Box 下面包含众多类型的子 Box。
  mdia Box 是一个 container box,所以它的数据格式定义是: aligned(8) class MediaBox extends Box(‘mdia’){}3.5、Media Header Box(moov/trak/mdia/mdhd)
  mdhd,Media Header Box。
  mdhd Box 的数据格式定义: aligned(8) class MediaHeaderBox extends FullBox(‘mdhd’, version, 0) {     if (version==1) {         unsigned int(64) creation_time;         unsigned int(64) modification_time;         unsigned int(32) timescale;         unsigned int(64) duration;     } else { // version==0         unsigned int(32) creation_time;         unsigned int(32) modification_time;         unsigned int(32) timescale;         unsigned int(32) duration;     }     bit(1) pad = 0;     unsigned int(5)[3] language; // ISO-639-2/T language code     unsigned int(16) pre_defined = 0; }
  下面是一些字段的含义: version:该 Box 的版本号。这里是 0 或 1。 creation_time:创建时间。 modification_time:最近修改时间。 timescale:表示 1s 的时长被切割的单元数,它体现了时间的精度。 duration:表示当前媒体数据时长,以 timescale 定义的精度计量。 pad:占位符。 language:该字段总长为 15bit,通常是和 pad 字段组合成为 2 字节的长度。 pre_defined:无作用,默认 0。 3.6、Handler Reference Box(moov/trak/mdia/hdlr)
  hdlr,Handler Reference Box,表示该 track 数据的处理方式,对应的类型包括:Video Track、Audio Track 或者 Hint Track。这个 Box 可以作为 mdia 的子 Box ,也可以作为 meta 的子 Box。
  hdlr Box 的数据格式定义: aligned(8) class HandlerBox extends FullBox(‘hdlr’, version = 0, 0) {     unsigned int(32) pre_defined = 0;     unsigned int(32) handler_type;     const unsigned int(32)[3] reserved = 0;     string name; }
  下面是一些字段的含义: handler_type:当作为 mdia 的子 Box 时,该值有下面几种: vide:Video track。 soun:Audio track。 hint:Hint track。 name:这个主要是写名字方便人阅读的。 3.7、Media Information Box(moov/trak/mdia/minf)
  minf,即 Media Information Box,包含了描述该 trak 中媒体数据的所有特征信息。其中 minf 下 最重要的 Box 是 stbl。
  minf Box 是一个 container box,所以它的数据格式定义是: aligned(8) class MediaInformationBox extends Box(‘minf’) { }3.8、Sample Table Box(moov/trak/mdia/minf/stbl)
  stbl,即 Sample Table Box,是包含媒体数据信息最多的 Box,也是最复杂的 Box。主要包含了时间和媒体采样数据的索引表,使用这部分数据可以按照时间检索出采样数据的位置、类型(是否 I 帧)、大小、实际偏移位置。
  如果当前 track 不包含数据,那么 stbl Box 不需要包含任何子 Box。反之,Sample Description(stsd)、Sample Size(stsz)、Sample To Chunk(stsc)、Chunk Offset(stco) 这些子 Box 是必须包含的。此外,Sync Sample(stss) 这个 Box 虽然是可选的,但也比较重要,它是关键帧的序号表,如果没有这个 Box 则表示所有采样都是关键帧。
  stbl Box 是一个 container box,所以它的数据格式定义是 aligned(8) class SampleTableBox extends Box(‘stbl’) { }3.9、Sample Description Box(moov/trak/mdia/minf/stbl/stsd)
  stsd,即 Sample Description Box,这里主要包含了采样数据的细节信息,包括编码类型以及解码需要的各种初始化数据信息。
  stsd 由于需要兼顾 video track、audio track、hint track 各种类型,所以它的数据格式定义比较复杂一些: aligned(8) abstract class SampleEntry (unsigned int(32) format) extends Box(format) {     const unsigned int(8)[6] reserved = 0;     unsigned int(16) data_reference_index; }   class HintSampleEntry() extends SampleEntry (protocol) {     unsigned int(8) data []; }  // Visual Sequences class VisualSampleEntry(codingname) extends SampleEntry (codingname) {     unsigned int(16) pre_defined = 0;     const unsigned int(16) reserved = 0;     unsigned int(32)[3] pre_defined = 0;     unsigned int(16) width;     unsigned int(16) height;     template unsigned int(32) horizresolution = 0x00480000; // 72 dpi     template unsigned int(32) vertresolution = 0x00480000; // 72 dpi     const unsigned int(32) reserved = 0;     template unsigned int(16) frame_count = 1;     string[32] compressorname;     template unsigned int(16) depth = 0x0018;     int(16) pre_defined = -1; }   // Audio Sequences class AudioSampleEntry(codingname) extends SampleEntry (codingname) {     const unsigned int(32)[2] reserved = 0;     template unsigned int(16) channelcount = 2;     template unsigned int(16) samplesize = 16;     unsigned int(16) pre_defined = 0;     const unsigned int(16) reserved = 0 ;     template unsigned int(32) samplerate = {timescale of media}<<16; }   aligned(8) class SampleDescriptionBox (unsigned int(32) handler_type) extends FullBox("stsd", 0, 0){     int i ;     unsigned int(32) entry_count;     for (i = 1 ; i <= entry_count ; i++){         switch (handler_type){             case ‘soun’: // for audio tracks                 AudioSampleEntry();                 break;             case ‘vide’: // for video tracks                 VisualSampleEntry();                 break;             case ‘hint’: // Hint track                 HintSampleEntry();                 break;         }     } }
  一些字段的含义: entry_count:下面条目表中条目的数量。 SampleEntry:对应的 sample 条目。 data_reference_index:利用这个索引可以检索与当前 sample description 关联的数据。数据引用存储在 Data Reference Box(dref)。 ChannelCount:音频通道数,值为 1(mono,单声道) 或 2(stereo,立体音)。 SampleSize:音频采样大小,默认大小是 16bits。 SampleRate:音频采样率,以 16.16 定点数。通常是 44100.0、48000.0 等。 resolution:图像分辨率,以 16.16 定点数表示。单位是 pixels-per-inch。 frame_count:每个 sample 包含的帧数。一般是 1 个 sample 对应 1 帧。 Compressorname:一个用于信息展示的名字。 depth:图像位深。比如,0x0018 表示不带 alpha 值的颜色位深。 width & height:当前 sample 的最大尺寸,单位是像素。
  下面是两个示例数据:
  视频 track 对应的 stsd Box:
  我们看到上面还在  avcC   条目里包含了视频的 SPS 和 PPS 的信息,这些都是视频解码需要的信息。SPS 和 PPS 是 H.264 流中的元信息,在 MP4 文件中单独存放在  avcC   中。转换的时候,还需要将 SPS 和 PPS 提取出来,添加上 0x00000001,放在 H.264 视频流的开始位置。
  对于 H.265,其元信息在  hvcC   类型 Box 中。
  音频 track 对应的 stsd Box:
  3.10、Time To Sample Box(moov/trak/mdia/minf/stbl/stts(ctts))
  Time To Sample Box 分为 Decoding Time To Sample Box(stts) 和 Composition Time To Sample Box(ctts) 两种。
  stts 包含的信息是相邻两帧的解码间隔时间。
  stts Box 的数据格式定义: aligned(8) class TimeToSampleBox extends FullBox(’stts’, version = 0, 0) {     unsigned int(32) entry_count;     int i;     for (i = 0; i < entry_count; i++) {         unsigned int(32) sample_count;         unsigned int(32) sample_delta;     } }
  一些字段的含义: entry_count:记录条目的数量。 sample_count:记录连续相同 delta 的条目数量。 sample_delta:记录以 timescale 为精度的时间长度。
  ctts 包含的信息是 decoding time 和 composition time 的差值。
  ctts Box 的数据格式定义: aligned(8) class CompositionOffsetBox extends FullBox(‘ctts’, version = 0, 0) {     unsigned int(32) entry_count;     int i;     for (i=0; i < entry_count; i++) {         unsigned int(32) sample_count;         unsigned int(32) sample_offset;     } }entry_count:记录条目的数量。 sample_count:记录连续相同 offset 的条目数量。 sample_offset:记录以 timescale 为精度的时间长度。
  下面是一个示例:
  表中有一序列的 I、P、B 帧,他们是按照解码时间排列的。表中给出了各帧的 Decoding Time(DT) 和 Composition Time(CT),并据此计算出了对应的 Decode delta 和 Composition offset。
  下面则是与上面对应的,在 stts 存储的 Decode delta 的信息。因为,连续的 14 个 sample 的 Decode delta 都是 10,所以一条数据即可记录。DT 和 stts 表数据的计算公式: DT(n+1) = DT(n) + STTS(n)  。
  下面是对应的在 ctts 存储的 Composition offset 的信息。CT、DT 和 ctts 表数据的计算公式: CT(n) = DT(n) + CTTS(n)  。
  Time To Sample Box 中记录的时间信息,通常可以用来通过时间做 seek 操作。 3.11、Sample Size Box(moov/trak/mdia/minf/stbl/stsz)
  stsz,即 Sample Size Boxe,包含每个 Sample 的大小。
  stsz 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;         }     } }
  一些字段的含义: sample_size:如果所有的 sample 都是一样的大小,那么这个字段的值就对应这个大小。如果 sample 的大小各自不同,那么这个字段的值就是 0,这时候每个 sample 的大小存储在 sample size table 中。 sample_count:表示 sample 的数量。 entry_size:表示 sample size table 中每个 sample 的大小。
  下图是一个视频 track 的 stsz 的示例:
  3.12、Chunk Offset Box(moov/trak/mdia/minf/stbl/stco(co64))
  stco,即 Chunk Offset Box,每个 Chunk 的偏移。这个偏移是相对文件初始位置的偏移。所以这里需要注意的一点是,当修改 mdat Box 之前其他 Box 的信息时,会影响到 Chunk Offset,这里的记录则需要做对应的更新。如果视频文件较大,Offset 用 32 位表示不下,就用 co64 Box 通过 64 位来表示。
  在 MP4 文件中,Chunk 是最小的基本单位,而不是 Sample。一个 Chunk 里可以包含单个或多个 Sample。这里是为了优化数据的 I/O 读取效率。
  stco(co64) Box 的数据格式定义: aligned(8) class ChunkOffsetBox extends FullBox(‘stco’, version = 0, 0) {     unsigned int(32) entry_count;     for (i=1; i <= entry_count; i++) {         unsigned int(32) chunk_offset;     } }  aligned(8) class ChunkLargeOffsetBox extends FullBox(‘co64’, version = 0, 0) {     unsigned int(32) entry_count;     for (i = 1; i <= entry_count; i++) {         unsigned int(64) chunk_offset;     } }
  一些字段的含义: entry_count:表示下面的条目表的条目数量。其实也就是 Chunk 的数量。 chunk_offset:每个 Chunk 的偏移量。
  下图是一个视频 track 的 stco 的示例:
  3.13、Sample To Chunk Box(moov/trak/mdia/minf/stbl/stsc)
  stsc,即 Sample To Chunk Box,包含 Sample 和 Chunk 的映射关系。媒体数据的 sample 采样在文件中是以 chunk 的形式组装起来的。这个 Box 可以用来找到包含 sample 的 chunk,以及 chunk 的位置和描述信息。
  stsc Box 的数据格式定义: aligned(8) class SampleToChunkBox extends FullBox(‘stsc’, version = 0, 0) {     unsigned int(32) entry_count;     for (i = 1; i <= entry_count; i++) {         unsigned int(32) first_chunk;         unsigned int(32) samples_per_chunk;         unsigned int(32) sample_description_index;     } }
  一些字段的含义: entry_count:表示后面表中的条目数量。 first_chunk:每个条目开始的 chunk 的位置。这个字段理解起来有点复杂。每个条目可能对应着 1 个或多个 chunk,而这些 chunk 共享后面的 samples_per_chunk 和 sample_description_index 字段。其中的第一个 chunk 则是这里的 first_chunk。 samples_per_chunk:每个 chunk 中包含的 sample 的数量。 sample_description_index:每个 sample 的描述,一般默认为 1。
  下面是一个视频 track 的 stsc 的示例:
  可以看到这里的 entry table 里面只有 2 条数据。这里与上面 stco 示例的视频是同一个视频,stco 的信息显示,该视频 track 有 743 个 chunk。那么这里的 stsc 的 entry table 的数据则表示,第  [1, 2-1]   个 chunk 都包含了 2 个 sample,第  [2, last=743]   个 chunk 都包含了 1 个 sample。所以,总共是  (1 * 2) + (743 - 2 + 1) * 1 = 744   个 sample。这个数量刚好和 stsz 示例中显示的 sample 数量对的上。 3.14、Sync Sample Box(moov/trak/mdia/minf/stbl/stss)
  stss,即 Sync Sample Box,包含可随机访问的 Sample 序号列表,一般可以认为是关键帧序号列表。
  stss Box 的数据格式定义: 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;     } }
  一些字段的含义: entry_count:下面条目表的数量。如果是 0 则表示视频流中没有可以随机访问的位置。 sample_number:可随机访问的 sample 的序号,一般可以认为是关键帧的序号。
  下面是一个视频 track 的 stss 的示例:
  4、Media Data Box(mdat) 解析
  mdat,即 Media Data Box,包含具体的媒体数据,即实际的音视频数据。比如我们需要的第一帧图片的数据就存放在这个 Box 里。这个 Box 中的数据是没有结构的,所以依赖于 moov 中的信息来进行索引。
  另外,值得注意的是 mdat Box 在一个 MP4 文件中不是必须的,可以没有。因为 MP4 是支持将媒体数据放在其他文件中,并通过 moov 中的信息来索引。
  mdat Box 的数据格式定义: aligned(8) class MediaDataBox extends Box(‘mdat’) {     bit(8) data[]; }5、实战解析5.1、moov 和 MP4 视频的秒开
  了解了 MP4 的文件结构后,我们知道了 moov 这个 Box 包含着对视频来说非常重要的索引信息,所以一般播放器需要拿到这些信息才能完成解码器的初始化,开启播放流程。而 moov Box 在文件中的位置是可以随意放置的。它可以放在包含视频实际数据的 mdat Box 前面,也可以放在其后面。一般来讲,如果不做特别设置,moov 会放在 mdat 后面,因为从正常的处理流程上来讲,当所有的音视频数据都处理完成后,才能确定对应的宏观信息和索引信息,这时候才能确定 moov 的信息。
  当 moov 放在 mdat 后面时,我们修改视频中 moov/udta 中的用户自定义信息时,不会对 mdat 的 Chunk Offset 造成影响,这样就不需要更新 stco 里的数据,编辑效率较高。但是,当从网络读取和播放 MP4 文件时就需要等待较长时间,直到播放器获取到 moov 的数据后才能初始化解码器并开始播放。
  当 moov 放在 mdat 前面时,则与上述情况相反,这时候从网络读取和播放 MP4 文件时,就可以较快获取到 moov 的数据并开始播放。所以一般来说, 对于通过网络播放 MP4 视频的场景,都建议将视频处理为 moov 前置。
  我们可以使用 FFmpeg 将一个 moov 后置的 MP4 处理为前置: ffmpeg -i slow_play.mp4 -movflags faststart fast_play.mp4
  在实际开发中,我们通过手机编辑视频时,要么是自己拍摄的,要么是从相册选择的。
  在 iOS 中,可以通过系统提供 AVAssetExportSession 来导出视频,其中有一个  shouldOptimizeForNetworkUse   接口可以用来支持 fast start。一般在自己拍摄视频的场景可以这样来做。 /* indicates that the output file should be optimized for network use, e.g. that a QuickTime movie file should support "fast start" */ @property (nonatomic) BOOL shouldOptimizeForNetworkUse; 复制代码
  如果只是从相册选择的视频,我们可以借由 AVAssetExportSession 来对视频重新打包来实现 moov 前置。当然,我们也可以自己写一段代码,单纯地针对 MP4 文件来完成 moov 前置的操作。
  在实际应用场景中,我们还遇到过由于视频 moov 后置并且视频太大(分辨率 1920x1080,码率 5 Mbps,帧率 25 fps)导致在手机浏览器播放失败的情况。如下图:
  遇到报错: Client closed connection before receiving entire response
  这种情况可能是由于 moov 后置导致浏览器播放内核需要加载的数据太大而触发某些限制导致了主动断开连接。 5.2、MP4 视频的预加载
  在视频相关的业务实现中,有很多需要我们关注的用户体验点, 视频迅速开播 和 无黑屏 是其中两个常见的关注点。
  对视频进行预加载是提升这两个体验点的技术方案之一。那么现在问题来了:预加载多少数据比较合适呢?
  在 iOS 中,系统提供的 AVPlayer 有一个特性,即使没有开始播放,当 AVPlayer 加载到足够的数据后,它会把视频的第一帧显示出来,就像是视频的封面一样。结合这个特性,如果我们能够预加载一定的数据量保证 AVPlayer 刚好能把视频首帧渲染出来,这样就能确保视频能迅速开播,并且用户一打开视频就能看到画面,实现了无黑屏。那么对于 AVPlayer 来说这个需要的数据量是多少呢?
  通过我们对 AVPlayer 的反复试验,我们发现:AVPlayer 拿到第一个关键帧的 sample 数据即可渲染出首帧画面。基于上面我们对 MP4 文件结构的介绍,我们其实可以找出这个数据量的算法: 1)找到 moov 中的视频对应的 track,从中找出 Sync Sample Box(moov/trak/mdia/minf/stbl/stss),找出第一个 sync sample 的序号  x  。 2)从 Sample To Chunk Box(moov/trak/mdia/minf/stbl/stsc) 中找出序号为 x 的 sample 所在的 chunk 的序号  k  ,以及该 chunk 中在 sample x 之前的其他 sample 的数量  m  。 3)结合 Sample Size Box(moov/trak/mdia/minf/stbl/stsz) 中的 sample size table 找出 sample x 的 size 以及其在它前面的 m 个 sample 的 size,计算这些 size 求和得到  s  。 4)在 Chunk Offset Box(moov/trak/mdia/minf/stbl/stco(co64)) 中找到序号为 k 的 chunk 的 offset 值  t  。 5)需要加载的数据量则为: t + s  。
  我们还可以通过下面的命令: ffprobe -show_frames -select_streams v -skip_frame nokey -show_entries frame=pict_type,pkt_pos,pkt_size,media_type -i 
  找到第一帧视频 I 帧的 pkt_pos 和 pkt_size,二者相加就是从文件开始位置取得第一帧视频 I 帧所需要的字节数。 本文参考
  1)ISO/IEC 14496-14
  www.iso.org/obp/ui/#iso…
  2)ISO Base Media File Format mpeg.chiariglione.org/standards/m…
  (通过上文的介绍,我们了解了 MP4 视频封装格式中重要的 Box 类型及其包含的信息,也看到了实战中可以用这些信息来做什么。我们将在后面继续探讨其他常见的媒体封装格式,敬请期待) - 完 -
  作者:关键帧Keyframe 链接:https://juejin.cn/post/7196511671626498108 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  #音视频开发# #程序员# #MP3MP4#

2022年冠军基金万家黄海持仓报告2022年主动权益基金排行榜万家黄海勇夺冠亚军。黄海一共三支基金,分别夺得第一(万家宏观择时多策略混合)第二(万家新利灵活配置混合)第四(万家精选混合A),区间回报率48。5643大展宏兔开新局!中联重科超10亿元设备交付海内外红网时刻新闻1月30日讯(通讯员李家宇)1月29日,中联重科全国各园区隆重举办了以极致竞未来为主题的开门红活动。当天,中联重科总价值超10亿元的各系列产品从中联智慧产业城麓谷泉塘望世界最大金佛被当泥像遗弃200年,工人搬运时摔了一下露出金身众所周知,佛教在泰国非常盛行。在泰国全部人口中,佛教徒占据了大约百分之九十五。因为佛教是泰国的国教,所以泰国的佛教徒在国内的地位很高,无论在哪一个社会阶层,佛教徒都掌握着一定的话语比亚迪再次提速,15。9亿元拿下紫光在建芯片基地,扩充半导体产能比亚迪再次提速,花费15。9亿元,收购紫光成都存储器制造基地。根据西南联合产权交易所官网上的公示信息显示,去年9月份挂牌的成都市双流区黄甲街道综保横路168号成都空港国芯科技有限公刘吉惠州2023年GDP计划增长7以上视频加载中广东召开全省高质量发展大会,为广东经济高质量发展描绘奋进蓝图。惠州市委书记刘吉表示,省委省政府开年第一天召开这次高质量发展大会,发出了大抓实体经济加快高质量发展的动员令。绝味食品难顶鸭力餐饮巨头新兵纷纷抢食,预计2022年利润不足3亿本文来源时代周报作者叶曼至卤味一哥的鸭脖生意越来越难做。1月29日,绝味食品(603517。SH)发布业绩预告,预计2022年归属于上市公司股东的净利润2。2亿元2。6亿元,与上年茂名房地产交易2022年12月报化州商品房均价同比涨7日前,茂名市住房和城乡建设局发布茂名市2022年12月房地产交易信息月报。数据显示,原本属于开发商冲业绩的年末期,成交数据未达理想。具体数据上,2022年12月商品房成交面积同比环四川这两县或将抱团,助推广元发展说到四川的城市,可能很多人对于广元这个名字十分陌生,的确,相比于我们熟知的那些城市,广元这座城市在四川省内的存在感并不是那么强烈。但这并不代表广元不重要,这里自古就是进入蜀地的重要22年ETF账户收益总结2022年,是非常艰难的一年,基本面上经历俄乌战争疫情的严控(如上海静默)以及突然放开美国打压及美联储多次加息。行情的走势基本是一边倒然后反转再反转,市场情绪跌宕起伏也演绎到极致。苏宁易购预计2022年亏损超94亿元,持续调整业务结构1月30日晚间,苏宁易购发布2022年业绩预告。预告显示,苏宁易购2022年GMV规模预计1113亿元,预计实现营业收入约730亿元,同比下降约47。4。2022年归属于上市公司股20句洗涤心灵的文案,每一句都是震撼至极的王炸一真正的内心强大,就是活在自己的世界里,而不是活在别人的眼中和嘴上。人生在世,无非是笑笑别人,然后再让别人笑笑自己。二生活总是让我们遍体鳞伤,但到最后,那些受伤的地方一定会变成我们
她46岁自驾317进藏,遇见灵魂伴侣。她拯救了他,最后也杀死了他都说此生必须去一次西藏,让那神圣的阳光让灵魂得到升华,让那纯净的雪域让灵魂得到净化。自驾318你会被美丽壮观的自然景观吸引,你会感叹大自然的鬼斧神工。而你若是走一走川藏北线317,为什么不建议早餐吃大米粥?提醒不只大米粥,这两种或也要少吃早饭对于人体的重要性,想必大家都知道,一日之计在于晨,一顿有营养的早餐能够为身体补充足够的营养物质,同时也为身体提供足够的能量来维持机体的运转和活动,有精力去面对一天的工作和学习。男子频繁头晕,脑梗离世,医生4个症状已经提醒过你孙先生今年五十岁,一直在他家附近当保安。一周前,老张在工作的时候总是经常头晕,以为只是睡眠不足引起的,没注意。结果几天后,老张直接因为头晕晕倒。被送到医院的时候,老张已经没有了生命记者重庆队走投无路了食堂供应都成了问题!已停训休假根据足球报记者鲁蜜的消息,中超重庆队在结束了今天的训练之后,全队已经进入了停训休假的状态,原因是一线队的运营经费已经严重匮乏。鲁蜜表示这一次是真的走投无路了,之前不是说河北拖欠水电库里轰4054仍无缘今日最佳!对不起!你碰上NBA怪兽了今日NBA赛场有11组对决,有不少球员打出了耀眼表现,赛后NBAFantasy根据球员表现评出了今日5佳球员。勇士的库里上榜,在勇士vs公牛的强强对决中,库里打出了让对手绝望的表现严惩于德豪!替阿的江出头用肩故意撞裁判,他或遭篮协停赛重罚北京时间11月12日,CBA常规赛继续进行,新疆男篮以100109不敌广厦男篮,遭遇三连败,而且在这场比赛中,新疆队主帅阿的江因为不满裁判员的判罚,被连吹两次技术犯规驱逐出场,当比高圆圆敢穿!上半身少女下半身熟女,网友真羡慕赵又廷高圆圆是网友熟悉的演员和模特,现在看到高圆圆代言的产品比较多,但其实高圆圆也是通过拍摄大量商业广告进入娱乐圈的。1996年被广告公司发掘,1997年出演爱辣汤,2005年在青红中饰被曝和郭碧婷婚变后,向佐本人亲自实锤表示暂时分开比较好很荣幸各位看官能够浏览完这篇文章,小编给大家比心啦,你的评论和点赞就是对小编最大的鼓励哦,爱你们!点开本文的读者朋友们,麻烦你们看完后能够给个关注,谢谢!提到郭碧婷,很多人都会想到世界上的第一台电动机,究竟是谁发明的?1731年7月的一次惊雷闪电之后,英国威克菲尔德的一名商人偶然发现他的新刀叉钢针竟然有了磁性。1751年,美国物理学家富兰克林偶然发现莱顿瓶(一种能贮电的装置)放电之后,附近的缝纫美国打造世界首艘载人太空站,重达76吨,运行171天就废弃了?从祖辈第一次开始仰望星空之后,我们对于宇宙的探索就从未止步,对宇宙的探索其实也包含了我们对自身发展的探索。随着科技的进步,人类也从最开始的肉眼观测天象变化来推断一些天文现象,到如今红米Note9Pro跌至清仓价,120Hz骁龙750G1亿像素,想买的要尽快可以说现如今在1K2K元价位的千元机中,红米Redmi手机是大部分人的首选机型。在全球10大热卖机型中,红米凭借Note9系列全球销量近5000万台,已先后两次成功入局,可见该系列