【多媒体封装格式详解】---MP4【4】

来源:互联网 发布:淘宝自动发货订单 编辑:程序博客网 时间:2024/06/05 20:28

前面介绍过的几种格式flv、mkv、asf等。他们音视频的数据包一般都是按照文件的顺序交叉安放。你解析完头部信息后。剩下的一般就按照文件顺序一个数据包一个数据包的解析就行了。但是MP4完全不是这种概念。他的媒体信息和数据是分开存放的。就是你想获得数据之前必须要解析出每个帧数据所有的位置。mp4存放这个帧信息的是放在stbl这个box里。而真实的数据放在mdat中。接下来就讲讲stbl与mdat的对应关系。

Sample Table Box(stbl)

来一张典型的stbl结构图:

常见子box

stts:    Decoding Time to Sample Box 时间戳和Sample映射表
stsd:   Sample Description Box
stsz, stz2: Sample Size Boxes 每个Sample大小的表。
stsc: Sample to chunk 的映射表。
‘stco’, ‘co64’: Chunk位置偏移表
stss:关键帧index。

1.解析stsd可获得coding类型视频宽高、音频samplesize、channelcount这些和解码器有关信息。

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 u entry_count ; i++){switch (handler_type){case ‘soun’: // for audio tracksAudioSampleEntry();break;case ‘vide’: // for video tracksVisualSampleEntry();break;case ‘hint’: // Hint trackHintSampleEntry();break;}}}

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 Sequencesclass 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 dpitemplate unsigned int(32) vertresolution = 0x00480000; // 72 dpiconst 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 Sequencesclass 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;}


2.解析stsz box 可以获得一个sample size的表

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 u sample_count; i++) {unsigned int(32) entry_size;}}}
3.解析stts 

aligned(8) class TimeToSampleBoxextends 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;}}

4.解析stsc 还原Sample 与chunk的映射表 



Sample 是存储的最基本单元,mp4把Sample 存在chunk中。chunk的长度、chunk的大小、chunk中Sample的数量及大小都是不定的。

通过解析这部分box来还原这个映射表。

aligned(8) class SampleToChunkBoxextends FullBox(‘stsc’, version = 0, 0) {unsigned int(32) entry_count;for (i=1; i u entry_count; i++) {unsigned int(32) first_chunk;unsigned int(32) samples_per_chunk;unsigned int(32) sample_description_index;}}

每个entry 表示着一组数据,entry_count 表示这数量。这一组其实是相同类型的chunk。

first_chunk 表示 这一组相同类型的chunk中 的第一个chunk数。

这些chunk 中包含的Sample 数量,即samples_per_chunk 是一致的。

每个Sample 可以通过sample_description_index 去stsd box 找到描述信息。

看ffmpeg中mov_read_stsc() 它把这些数据放在一个结构体数组中备用。

static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom){    AVStream *st;    MOVStreamContext *sc;    unsigned int i, entries;    if (c->fc->nb_streams < 1)        return 0;    st = c->fc->streams[c->fc->nb_streams-1];    sc = st->priv_data;    avio_r8(pb); /* version */    avio_rb24(pb); /* flags */    entries = avio_rb32(pb);    av_dlog(c->fc, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries);    if (!entries)        return 0;    if (entries >= UINT_MAX / sizeof(*sc->stsc_data))        return AVERROR_INVALIDDATA;    sc->stsc_data = av_malloc(entries * sizeof(*sc->stsc_data));    if (!sc->stsc_data)        return AVERROR(ENOMEM);    for (i = 0; i < entries && !pb->eof_reached; i++) {        sc->stsc_data[i].first = avio_rb32(pb);        sc->stsc_data[i].count = avio_rb32(pb);        sc->stsc_data[i].id = avio_rb32(pb);    }    sc->stsc_count = i;    if (pb->eof_reached)        return AVERROR_EOF;    return 0;}
在获得完整的映射表,我们还需要chunk总个数信息。这些信息放在‘stco’, ‘co64’中。

5.解析‘stco’, ‘co64’

“stco”定义了每个thunk在媒体流中的位置。位置有两种可能,32位的和64位的,后者对非常大的电影很有用。

32位

aligned(8) class ChunkOffsetBoxextends FullBox(‘stco’, version = 0, 0) {unsigned int(32) entry_count;for (i=1; i u entry_count; i++) {unsigned int(32) chunk_offset;}}

64位

aligned(8) class ChunkLargeOffsetBoxextends FullBox(‘co64’, version = 0, 0) {unsigned int(32) entry_count;for (i=1; i u entry_count; i++) {unsigned int(64) chunk_offset;}}
从这个box我们就可以获得 chunk 的总数量,entry_count


1.解析‘stco’, ‘co64’我们有了chunk 表,知道了chunk 的总数及每个chunk所在文件的位置。

2.解析stsc 配合着上面的chunk表,我们就能弄个Sample与chunk的关系表。我们也就能获得每个Sample的位置信息。

3.配合上面的stts 时间表和解码器信息等。搞出ES流已经不成问题了。

4.想获得关键帧的index,需要解析stss’

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











原创粉丝点击