MP4学习(三)ts-mp4源码阅读(1)介绍

ts-mp4源码阅读



    为了加深对mp4文件格式的理解,接下来介绍一个开源的MP4解析器ts-mp4,下载地址:https://github.com/portl4t/ts-mp4。把工程下载下来之后,进入src目录就可以看见源代码了,代码量不是很多。


ts-mp4支持的box类型


// 支持的box类型
typedef enum {
    MP4_TRAK_ATOM = 0,
    MP4_TKHD_ATOM,
    MP4_MDIA_ATOM,
    MP4_MDHD_ATOM,
    MP4_HDLR_ATOM,
    MP4_MINF_ATOM,
    MP4_VMHD_ATOM,
    MP4_SMHD_ATOM,
    MP4_DINF_ATOM,
    MP4_STBL_ATOM,
    MP4_STSD_ATOM,
    MP4_STTS_ATOM,
    MP4_STTS_DATA,
    MP4_STSS_ATOM,
    MP4_STSS_DATA,
    MP4_CTTS_ATOM,
    MP4_CTTS_DATA,
    MP4_STSC_ATOM,
    MP4_STSC_CHUNK,
    MP4_STSC_DATA,
    MP4_STSZ_ATOM,
    MP4_STSZ_DATA,
    MP4_STCO_ATOM,
    MP4_STCO_DATA,
    MP4_CO64_ATOM,
    MP4_CO64_DATA,
    MP4_LAST_ATOM = MP4_CO64_DATA
} TSMp4AtomID;


常见的MP4结构图






container box的定义


    由于container box是由子box构成的,因此在代码实现中,这些container box不存在结构体,由函数指针数组来表示!



MP4容器


    MP4本身就是一个容器

/* 
** MP4容器定义,包含:
** ftyp、moov、mdat
*/
static mp4_atom_handler mp4_atoms[] = {
    { "ftyp", &Mp4Meta::mp4_read_ftyp_atom },
    { "moov", &Mp4Meta::mp4_read_moov_atom },
    { "mdat", &Mp4Meta::mp4_read_mdat_atom },
    { NULL, NULL }
};



moov box的定义

    moov box(Movie Box)
    该box包含了文件媒体的metadata信息,“moov”是一个container box,具体内容信息由子box诠释。同File Type Box一样,该box有且只有一个,且只被包含在文件层。一般情况下,“moov”会紧随“ftyp”出现。
    一般情况下,“moov”中会包含1个“mvhd”和若干个“trak”。其中“mvhd”为header box,一般作为“moov”的第一个子box出现(对于其他container box来说,header box都应作为首个子box出现)。“trak”包含了一个track的相关信息,是一个container box

/*
** moov box的定义,包含:
** mvhd、trak(cmov无意义)
*/
static mp4_atom_handler mp4_moov_atoms[] = {
    { "mvhd", &Mp4Meta::mp4_read_mvhd_atom },
    { "trak", &Mp4Meta::mp4_read_trak_atom },
    { "cmov", &Mp4Meta::mp4_read_cmov_atom },
    { NULL, NULL }
};



trak box的定义

    trak box(Track Box)
    它是moov box的子box,也是一个container box,其子box包含了该track的媒体数据引用和描述(hint track除外)。一个MP4文件中的媒体可以包含多个track,且至少有一个track,这些track之间彼此独立,有自己的时间和空间信息。“trak”必须包含一个“tkhd”和一个“mdia”,此外还有很多可选的box。其中“tkhd”为track header box,“mdia”为media box,该box是一个包含一些track媒体数据信息box的container box

/*
** trak box的定义,包含:
** tkhd、mdia
*/
static mp4_atom_handler mp4_trak_atoms[] = {
    { "tkhd", &Mp4Meta::mp4_read_tkhd_atom },
    { "mdia", &Mp4Meta::mp4_read_mdia_atom },
    { NULL, NULL }
};


mdia box的定义

    mdia box(Media Box)
    “mdia”也是个container box,其子box的结构和种类比较复杂。
    总体来说,“mdia”定义了track媒体类型以及sample数据,描述sample信息。一般“mdia”包含一个“mdhd”,一个“hdlr”和一个“minf”,其中“mdhd”为media header box,“hdlr”为handler reference box,“minf”为media information box。

/*
** mdia box的定义,包含:
** mdhd、hdlr、minf等box
*/
static mp4_atom_handler mp4_mdia_atoms[] = {
    { "mdhd", &Mp4Meta::mp4_read_mdhd_atom },
    { "hdlr", &Mp4Meta::mp4_read_hdlr_atom },
    { "minf", &Mp4Meta::mp4_read_minf_atom },
    { NULL, NULL }
};


minf box的定义

    minf box(Media Information Box)
    它是mdia box的子box。“minf”存储了解释track媒体数据的handler-specific信息,media handler用这些信息将媒体时间映射到媒体数据并进行处理。“minf”中的信息格式和内容与媒体类型以及解释媒体数据的media handler密切相关,其他media handler不知道如何解释这些信息。“minf”是一个container box,其实际内容由子box说明。
    一般情况下,“minf”包含一个header box,一个“dinf”和一个“stbl”,其中,header box根据track type(即media handler type)分为“vmhd”、“smhd”、“hmhd”和“nmhd”,“dinf”为data information box,“stbl”为sample table box。

/*
** minf box的定义,它包含:
** vmhd、smhd、dinf、stbl等box
*/
static mp4_atom_handler mp4_minf_atoms[] = {
    { "vmhd", &Mp4Meta::mp4_read_vmhd_atom },
    { "smhd", &Mp4Meta::mp4_read_smhd_atom },
    { "dinf", &Mp4Meta::mp4_read_dinf_atom },
    { "stbl", &Mp4Meta::mp4_read_stbl_atom },
    { NULL, NULL }
};


stbl box的定义

    stbl box(Sample Table Box)
    它是minf box的子box。“stbl”几乎是普通的MP4文件中最复杂的一个box了,首先需要回忆一下sample的概念。sample是媒体数据存储的单位,存储在media的chunk中,chunk和sample的长度均可互不相同。
    “stbl”包含了关于track中sample所有时间和位置的信息,以及sample的编解码等信息。利用这个表,可以解释sample的时序、类型、大小以及在各自存储容器中的位置。“stbl”是一个container box,其子box包括:sample description box(stsd)、time to sample box(stts)、sample size box(stsz或stz2)、sample to chunk box(stsc)、chunk offset box(stco或co64)、composition time to sample box(ctts)、sync sample box(stss)等

/*
** trak box的定义,包含:
** stsd、stts、stss、ctts、stsc、stsz、stco、co64等box
*/
static mp4_atom_handler mp4_stbl_atoms[] = {
    { "stsd", &Mp4Meta::mp4_read_stsd_atom },
    { "stts", &Mp4Meta::mp4_read_stts_atom },
    { "stss", &Mp4Meta::mp4_read_stss_atom },
    { "ctts", &Mp4Meta::mp4_read_ctts_atom },
    { "stsc", &Mp4Meta::mp4_read_stsc_atom },
    { "stsz", &Mp4Meta::mp4_read_stsz_atom },
    { "stco", &Mp4Meta::mp4_read_stco_atom },
    { "co64", &Mp4Meta::mp4_read_co64_atom },
    { NULL, NULL }
};


普通box


box头部

// box头部
typedef struct {
    u_char    size[4]; // box的大小
    u_char    name[4]; // box的类型
} mp4_atom_header;


mvhd box

    mvhd box(Movie Header Box)
    它是moov box的第一个子box

// mvhd box
typedef struct {
    u_char    size[4]; // 大小
    u_char    name[4]; // 名字
    u_char    version[1]; // box版本,0或1,一般为0
    u_char    flags[3]; // 标识
    u_char    creation_time[4]; // 创建时间(相对于UTC时间1904-01-01零点的秒数)
    u_char    modification_time[4]; // 修改时间
    u_char    timescale[4]; // 文件媒体在1秒时间内的刻度值,可以理解为1秒长度的时间单元数
    u_char    duration[4]; // 该track的时间长度,用duration和time scale值可以计算track时长
    u_char    rate[4]; // 推荐播放速率
    u_char    volume[2]; // 与rate类似,表示音量
    u_char    reserved[10]; // 保留位
    u_char    matrix[36]; // 视频变换矩阵
    u_char    preview_time[4]; // 下面四个成员表示pre-defined
    u_char    preview_duration[4];
    u_char    poster_time[4];
    u_char    selection_time[4];
    u_char    selection_duration[4];
    u_char    current_time[4];
    u_char    next_track_id[4]; // 下一个track使用的id号
} mp4_mvhd_atom;


tkhd box

    tkhd box(Track Header Box)
    它是trak box的第一个子box

// tkhd box
typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
	
	/*
		flags标志位
	    按位或操作结果值,预定义如下:
		0x000001 track_enabled,否则该track不被播放;
		0x000002 track_in_movie,表示该track在播放中被引用;
		0x000004 track_in_preview,表示该track在预览时被引用。
		一般该值为7,如果一个媒体所有track均未设置track_in_movie和track_in_preview,
		将被理解为所有track均设置了这两项;对于hint track,该值为0
	*/
    u_char    flags[3];
    u_char    creation_time[4];
    u_char    modification_time[4];
    u_char    track_id[4]; // id号,不能重复且不能为0
    u_char    reserved1[4];
    u_char    duration[4]; // track的时间长度
    u_char    reserved2[8];
    u_char    layer[2]; // 视频层,默认为0,值小的在上层
    u_char    group[2]; // track分组信息,默认为0表示该track未与其他track有群组关系
    u_char    volume[2]; // 音量
    u_char    reverved3[2];
    u_char    matrix[36]; // 视频变换矩阵
    u_char    width[4]; // 宽
    u_char    heigth[4]; // 高
} mp4_tkhd_atom;


mdhd box

    mdhd box(Media Header Box)
    它是mdia box的第一个子box

// mdhd box
typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    creation_time[4];
    u_char    modification_time[4];
    u_char    timescale[4];
    u_char    duration[4];
    u_char    language[2]; // 媒体语言码
    u_char    quality[2];
} mp4_mdhd_atom;


stsd box

    stsd box(Sample Description Box)
    它是stbl box的子box。“stsd”必不可少,且至少包含一个条目,该box包含了data reference box进行sample数据检索的信息。没有“stsd”就无法计算media sample的存储位置。“stsd”包含了编码的信息,其存储的信息随媒体类型不同而不同box header和version字段后会有一个entry count字段,根据entry的个数,每个entry会有type信息,如“vide”、“sund”等,根据type不同sample description会提供不同的信息,例如对于video track,会有“VisualSampleEntry”类型信息,对于audio track会有“AudioSampleEntry”类型信息。
    视频的编码类型、宽高、长度,音频的声道、采样等信息都会出现在这个box中。

// stsd box
typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    entries[4]; // entry的个数,每个entry里面会有type信息

    u_char    media_size[4];
    u_char    media_name[4];
} mp4_stsd_atom;


stts box

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

// stts box
typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    entries[4];
} mp4_stts_atom;


    stts box中entry


typedef struct {
    u_char    count[4];
    u_char    duration[4];
} mp4_stts_entry;


stss box

    stss box(Sync Sample Box)
    它是stbl box的子box。stss”确定media中的关键帧。对于压缩媒体数据,关键帧是一系列压缩序列的开始帧,其解压缩 时不依赖以前的帧,而后续帧的解压缩将依赖于这个关键帧。“stss”可以非常紧凑的标记媒体内的随机存取点,它包含一个sample序号表,表内的每一 项严格按照sample的序号排列,说明了媒体中的哪一个sample是关键帧。如果此表不存在,说明每一个sample都是一个关键帧,是一个随机存取 点。

// stss box
typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    entries[4];
} mp4_stss_atom;



ctts box


typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    entries[4];
} mp4_ctts_atom;


    ctts box中entry


// ctts box中entry
typedef struct {
    u_char    count[4];
    u_char    offset[4];
} mp4_ctts_entry;


stsc box

    stsc box(Sample To Chunk Box)
    它是stbl box的子box。用chunk组织sample可以方便优化数据获取,一个thunk包含一个或多个sample。“stsc”中用一个表描述了sample与chunk的映射关系,查看这张表就可以找到包含指定sample的thunk,从而找到这个sample。

// stsc box
typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    entries[4];
} mp4_stsc_atom;


    stsc box中entry


// stsc box中entry
typedef struct {
    u_char    chunk[4];
    u_char    samples[4];
    u_char    id[4];
} mp4_stsc_entry;


stsz box

    stsz box(Sample Size Box)
    它是stbl box的子box。“stsz” 定义了每个sample的大小,包含了媒体中全部sample的数目和一张给出每个sample大小的表。这个box相对来说体积是比较大的。

// stsz box
typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    uniform_size[4];
    u_char    entries[4];
} mp4_stsz_atom;


    stsz box中的entry


// stsz box中的entry
typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    entries[4];
} mp4_stco_atom;


co64 box


typedef struct {
    u_char    size[4];
    u_char    name[4];
    u_char    version[1];
    u_char    flags[3];
    u_char    entries[4];
} mp4_co64_atom;


你可能感兴趣的:(视频容器MP4)