android 多媒体文件之mp4分析(续)---base on jellybean(七)

 Sample Table Boxstbl

stbl”几乎是普通的MP4文件中最复杂的一个box了。sample是媒体数据存储的单位,存储在mediachunk中,chunksample的长度均可互不相同。chunk是几个sample的集合。“stbl”包含了关于tracksample所有时间和位置的信息,以及sample的编解码等信息。利用这个表,可以解释sample的时序、类型、大小以及在各自存储容器中的位置。“stbl”是一个container box,其子box包括:sample description boxstsd)、time to sample boxstts)、sample size boxstszstz2)、sample to chunk boxstsc)、chunk offset boxstcoco64)、composition time to sample boxctts)、sync sample boxstss)等。“stsd”必不可少,且至少包含一个条目,该box包含了data reference box进行sample数据检索的信息。没有“stsd”就无法计算media sample的存储位置。“stsd”包含了编码的信息,其存储的信息随媒体类型不同而不同。

if (chunk_type == FOURCC('s', 't', 'b', 'l')) {

                ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);

 

                if (mDataSource->flags()

                        & (DataSource::kWantsPrefetching

                            | DataSource::kIsCachingDataSource)) {

                    sp<MPEG4DataSource> cachedSource =

                        new MPEG4DataSource(mDataSource);

 

                    if (cachedSource->setCachedRange(*offset, chunk_size) == OK) {

                        mDataSource = cachedSource;

                    }

                }

 

                mLastTrack->sampleTable = new SampleTable(mDataSource);----创建sampletable,每个track对应一个sampletable

            }

Sample Description Box(stsd)

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

case FOURCC('s', 't', 's', 'd'):

        {

 

,…………………………….

            uint32_t entry_count = U32_AT(&buffer[4]);

 

            off64_t stop_offset = *offset + chunk_size;

            *offset = data_offset + 8;

 

            if (entry_count > 1) {----针对3GPP,有可能有多个entry_count,但目前我们每个track支持单类型的media

                // For 3GPP timed text, there could be multiple tx3g boxes contain

                // multiple text display formats. These formats will be used to

                // display the timed text.

                const char *mime;

                CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));

                if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {

                     ALOGV("Text track found");

                     for (uint32_t i = 0; i < entry_count; ++i) {

                     status_t err = parseChunk(offset, depth + 1);

                        if (err != OK) {

                            return err;

                        }

                     }

                    // For now we only support a single type of media per track.

                }

                else {

                     status_t err = mLastTrack->sampleTable->setSampleDescParams(entry_count, *offset, chunk_data_size);

                     if (err != OK) {

                         return ERROR_IO;

                     }

                     //视频的编码类型、宽高、长度,音频的声道、采样等信息

                     mHasVideo = true;

                     uint8_t avc1[86];//(avc1-avcc) which is fixed

                     if (mDataSource->readAt(*offset, avc1, sizeof(avc1)) < (ssize_t)sizeof(avc1)) {

                         return ERROR_IO;

                     }

                     uint32_t chunk_type = U32_AT(&avc1[4]);

                     uint16_t data_ref_index = U16_AT(&avc1[14]);

                     uint16_t width = U16_AT(&avc1[32]);

                     uint16_t height = U16_AT(&avc1[34]);

 

                     mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));

                     mLastTrack->meta->setInt32(kKeyWidth, width);

                     mLastTrack->meta->setInt32(kKeyHeight, height);

 

                     uint8_t *avcc;

                     uint32_t avccSize;

                     mLastTrack->sampleTable->getSampleDescAtIndex(1, &avcc, &avccSize);

                     mLastTrack->meta->setData(kKeyAVCC, kTypeAVCC, avcc, avccSize);

                     *offset = stop_offset;

                }

            } else {

                 for (uint32_t i = 0; i < entry_count; ++i) {

                     status_t err = parseChunk(offset, depth + 1);

                     if (err != OK) {

                        return err;

                     }

                 } // end of for

 

            }//end of entry count 1

 

            if (*offset != stop_offset) {

                return ERROR_MALFORMED;

            }

            break;

        }

Time To Sample Box(stts)

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

case FOURCC('s', 't', 't', 's'):

        {

            status_t err =

                mLastTrack->sampleTable->setTimeToSampleParams(---该方法在SampleTable.cpp,映射时间和sample序号

                        data_offset, chunk_data_size);

 

            if (err != OK) {

                return err;

            }

 

            *offset += chunk_size;

            break;

        }

 

 Sample Size Box(stsz)

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

case FOURCC('s', 't', 's', 'z'):

        case FOURCC('s', 't', 'z', '2'):

        {

            status_t err =

                mLastTrack->sampleTable->setSampleSizeParams(-----该方法在SampleTable.cpp,设置sample大小

                     chunk_type, data_offset, chunk_data_size);

 

            if (err != OK) {

                return err;

            }

 

            size_t max_size;

            err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);

 

            if (err != OK) {

                return err;

            }

 

            // Assume that a given buffer only contains at most 10 fragments,

            // each fragment originally prefixed with a 2 byte length will

            // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,

            // and thus will grow by 2 bytes per fragment.

            mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);

            *offset += chunk_size;

 

            // Calculate average frame rate.

            const char *mime;

            CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));

            if (!strncasecmp("video/", mime, 6)) {

                size_t nSamples = mLastTrack->sampleTable->countSamples();

                int64_t durationUs;

                if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) {

                    if (durationUs > 0) {

                        int32_t frameRate = (nSamples * 1000000LL +

                                    (durationUs >> 1)) / durationUs;

                        mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);

                    }

                }

            }

 

            break;

        }

Sample To Chunk Box(stsc)

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

case FOURCC('s', 't', 's', 'c'):

        {

            status_t err =

                mLastTrack->sampleTable->setSampleToChunkParams(该方法在SampleTable.cpp,映射samplechunk的关系,一个或多个sample组成一个chunk

 

                        data_offset, chunk_data_size);

 

            if (err != OK) {

                return err;

            }

 

            *offset += chunk_size;

            break;

        }

Sync Sample Box(stss)

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

如何查找关键帧呢?

1:确定给定时间的sample序号检查sync sample atom来发现这个sample序号之后的key frame

2:检查sample-to-chunk atom来发现对应该sample的chunk

3:从chunk offset atom中提取该chunk的偏移量

4:利用sample size atom找到sample在trunk内的偏移量和sample的大小

case FOURCC('s', 't', 's', 's'):

        {

            status_t err =

                mLastTrack->sampleTable->setSyncSampleParams(----设置关键帧

                        data_offset, chunk_data_size);

 

            if (err != OK) {

                return err;

            }

 

            *offset += chunk_size;

            break;

        }

 

Chunk Offset Box(stco)

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

case FOURCC('s', 't', 'c', 'o'):

        case FOURCC('c', 'o', '6', '4'):

        {

            status_t err =

                mLastTrack->sampleTable->setChunkOffsetParams(---设置chunk的偏移量

                        chunk_type, data_offset, chunk_data_size);

 

            if (err != OK) {

                return err;

            }

 

            *offset += chunk_size;

            break;

        }

Free Space Boxfreeskip

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

Meida Data Boxmdat

 box包含于文件层,可以有多个,也可以没有(当媒体数据全部为外部文件引用时),用来存储媒体数据。

下图为总的概括:

android 多媒体文件之mp4分析(续)---base on jellybean(七)_第1张图片

参考资料:http://mpeg.chiariglione.org/standards/mpeg-4/mpeg-4.htm

具体源码:frameworks/av/media/libstagefright/MPEG4Extractor.cpp

                      frameworks/av/media/libstagefright/sampletable.cpp

好了,MP4文件格式已经介绍完了,video recoder也会用到这些知识,望大家好好研究研究。

你可能感兴趣的:(android 多媒体文件之mp4分析(续)---base on jellybean(七))