Android 13 - Media框架(8)- MediaExtractor(2)

上一篇 MediaExtractor 笔记中我们学习了 extractor 以及 source 调用的层次结构,这一节我们会看一看部分的实现细节。

1、getFormat

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 。

2、MediaExtractor 与 MediaTrack(MediaSource) 设计结构

看代码我们可以发现基础实现 MediaExtractor 和 MediaTrack 分别继承于 MediaExtractorPluginHelperMediaTrackHelper,这两个基类的声明位于 MediaExtractorPluginHelper.h,除了这两个类外,还有非常重要的 MediaBufferHelper、MediaBufferGroupHelper、DataSourceHelper。

往上一层,与之相对应的 C 风格的 api 声明位于 MediaExtractorPluginApi.h,创建了 MediaExtractorPluginHelper 和 MediaTrackHelper 对象后,会将其封装到 CMediaExtractorCMediaTrack 供上层使用。wrap 方法在 MediaExtractorPluginHelper.h 中有提供,对 Helper 提供的 api 进行了封装。

再往上就到了 MediaExtractor MediaTrack,它们两个是基类提供了对外接口,对应的实现是 MediaExtractorCUnwrapper 和 MediaTrackCUnwrapper 。它们内部封装了对 CMediaExtractorCMediaTrack 的调用以及对返回结果的处理。

最外层是binder封装,这里就不细看了。

Android 13 - Media框架(8)- MediaExtractor(2)_第1张图片

看到这里我们大致就可以了解,binder 调用的是 MediaExtractor 和 MediaTrack 提供的标准接口,它们对内部依赖进行了抽象,使其依赖于 CMediaExtractor 和 CMediaTrack,而不是依赖于具体的实现,具体实现封装在CMediaExtractor 和 CMediaTrack中。我觉得这里CMediaExtractor -> MediaExtractorPluginHelper 所用到的设计模式应该算是外观模式。

我并不是很理解为什么要设计中间层 CMediaExtractor 和 CMediaTrack,拿掉它们同样可以实现抽象与多态,如果有大神理解可以指点我一下。

你可能感兴趣的:(Android,Media,android,Framework,Media,多媒体,c++)