您的位置:首页 > 移动开发 > Android开发

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

2016-10-03 01:22 423 查看
Sample Table Boxstbl
“stbl”几乎是普通的MP4文件中最复杂的一个box了。sample是媒体数据存储的单位,存储在media的chunk中,chunk和sample的长度均可互不相同。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)等。“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(*offse
df3d
t, 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,映射sample和chunk的关系,一个或多个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 Box(free或skip)

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

Meida Data Box(mdat)

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

下图为总的概括:



参考资料: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也会用到这些知识,望大家好好研究研究。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: