上一篇 MediaExtractor 笔记中我们学习了 extractor 以及 source 调用的层次结构,这一节我们会看一看部分的实现细节。
getFormat 是 IMediaSource 的一个方法,调用它可以获得指定 track 的格式信息。格式信息在 extractor 中被称为 MetaData,里面存储有编码格式,视频的宽高,时长,音频的通道数,采样率等等信息,有的存储有解码所需要的 csd 信息。
在高版本的 Android 中,格式信息在 extractor 中一般以 AMediaFormat 的形式存储(例如 mp4 extractor),AMediaFormat 内部实际就是一个 AMessage,信息以 key-value 的形式存储,key 是字符串;有的会以 MetaData 的形式存储(例如 mpeg2 ts extractor),MetaData 也是以 key-value 的形式存储信息,但是它的 key 是int类型,存储的信息会更多一点:
bool MetaDataBase::setData(uint32_t key, uint32_t type, const void *data, size_t size);
可以看到出了 key-value 外还多了 type 和 size 信息。
之所以要这么设计是因为 format 需要通过 binder 跨进程传递给 mediaserver,传递的时候我们需要知道被传递内容的大小信息,而 type 大概是用做特殊的标记。
status_t MediaTrackCUnwrapper::getFormat(MetaDataBase& format) {
sp<AMessage> msg = new AMessage();
AMediaFormat *tmpFormat = AMediaFormat_fromMsg(&msg);
media_status_t ret = wrapper->getFormat(wrapper->data, tmpFormat);
sp<MetaData> newMeta = new MetaData();
convertMessageToMetaData(msg, newMeta);
delete tmpFormat;
format = *newMeta;
return reverse_translate_error(ret);
}
从以上代码片我们可以知道,内部的 getFormat 方法传出参数为 AMediaFormat,也就是说无论内部 format 以何种形式存储,最终会转换为 AMediaFormat,以 MPEG2TSSource 为例,MetaData 会先被转换为 AMessage,再转换为 AMediaFormat 回传:
media_status_t MPEG2TSSource::getFormat(AMediaFormat *meta) {
sp<MetaData> implMeta = mImpl->getFormat();
sp<AMessage> msg;
convertMetaDataToMessage(implMeta, &msg);
copyAMessageToAMediaFormat(meta, msg);
return AMEDIA_OK;
}
跨进程传递时 AMediaFormat 又要被转换为 MetaData,因为 MetaData 中有 writeToParcel 和 createFromParcel( 方法,很方便就完成 Parcel 的封装和解封装。
其实 AMessage 也有 writeToParcel 和 FromParcel,可以很轻松的让 AMessage 在进程间传递,但是这两个方法不能用于 vendor 和 apex。
上面说可以将 MetaData 和 AMessage 相互转换,但是由于它们的 key 的类型并不一样,所以一定有对照关系,这里我们要看 Utils.cpp 这边的几个 pair,pair 中没有的对照关系在 convertMessageToMetaData 和 convertMetaDataToMessage 中会指定转换关系。
如果我们需要自定义一些 Format 信息,那么必须要在 convertMessageToMetaData 和 convertMetaDataToMessage 实现转换关系,否则 extractor parse 出的信息将无法通过 binder 传递,mediaserver 端也无法解析到自定义的 format。
MetaData 所用的 key 位于 MetaDataBase.h,以 enum 的形式提供,如何使用可以参考 Utils.cpp 。
AMediaFormat 使用的 key 位于 NdkMediaFormat.cpp ,以字符串的形式提供,AMediaFormat 如何使用同样参考 Utils.cpp 。
看代码我们可以发现基础实现 MediaExtractor 和 MediaTrack 分别继承于 MediaExtractorPluginHelper
和 MediaTrackHelper
,这两个基类的声明位于 MediaExtractorPluginHelper.h,除了这两个类外,还有非常重要的 MediaBufferHelper、MediaBufferGroupHelper、DataSourceHelper。
往上一层,与之相对应的 C 风格的 api 声明位于 MediaExtractorPluginApi.h,创建了 MediaExtractorPluginHelper 和 MediaTrackHelper 对象后,会将其封装到 CMediaExtractor
和 CMediaTrack
供上层使用。wrap 方法在 MediaExtractorPluginHelper.h 中有提供,对 Helper 提供的 api 进行了封装。
再往上就到了 MediaExtractor
和 MediaTrack
,它们两个是基类提供了对外接口,对应的实现是 MediaExtractorCUnwrapper 和 MediaTrackCUnwrapper 。它们内部封装了对 CMediaExtractor
和 CMediaTrack
的调用以及对返回结果的处理。
最外层是binder封装,这里就不细看了。
看到这里我们大致就可以了解,binder 调用的是 MediaExtractor 和 MediaTrack 提供的标准接口,它们对内部依赖进行了抽象,使其依赖于 CMediaExtractor 和 CMediaTrack,而不是依赖于具体的实现,具体实现封装在CMediaExtractor 和 CMediaTrack中。我觉得这里CMediaExtractor -> MediaExtractorPluginHelper 所用到的设计模式应该算是外观模式。
我并不是很理解为什么要设计中间层 CMediaExtractor 和 CMediaTrack,拿掉它们同样可以实现抽象与多态,如果有大神理解可以指点我一下。