Android Media Framework(4): 支持格式的扩展

燕昊东
2023-12-01

Android Media Framework 框架的层次:

Java层:frameworks/base/media/java/android/media/MediaPlayer.java
JNI本地调用:frameworks/base/media/jni/android_media_MediaPlayer.cpp
libmedia多媒体底层库:frameworks/base/media/libmedia/mediaplayer.cpp
libmediaplayer多媒体服务部分:frameworks/base/media/libmediaplayerservice/MediaPlayerService.cpp, StagefrightPlayer.cpp
Stagefright框架:frameworks/base/media/libstagefright/AwesomePlayer.cpp, OMX

整个Media框架的核心是AwesomePlayer,Awesomeplayer中对应的数据结构主要有DataSource, MediaExtractor, MediaSource。其中:

DataSource主要负责提供原始数据
MediaSource负责提供demux后的数据(即实际的audio 或者 video数据包)
MediaExtractor则负责中间的过程,即将从DataSource得到的原始数据解析成解码器需要的es数据,并通过MediaSource的接口输出。

Android本身支持的文件格式有限,硬件厂商有时出于运营策略也会限制部分格式支持,被限制的部分格式需要收费等,于是公司就有自己扩展支持格式的需求。
我们先看AwesomePlayer工作的3个大的步骤:

探测文件类型,根据文件类型创建对应的提取器(MediaExtractor);
进入/libstagefright/omx/SoftOMXPlugin.cpp读取kComponents数组,此为现有系统支持的解码器列表;
读取/etc/media_codec.xml文件,根据已探测的文件类型获取需要的解码器名称,再对比kComponents得到实际的解码器。

我们具体的来细看每一个步骤:
1) 探测文件类型,根据文件类型创建对应的提取器(MediaExtractor)
在AwesomePlayer的构造函数中由DataSource注册sniff探测文件类型:

AwesomePlayer::AwesomePlayer(){  
    ...
    DataSource::RegisterDefaultSniffers();  
    ...
}  

RegisterDefaultSniffers的实现:

void DataSource::RegisterDefaultSniffers() {
    RegisterSniffer(SniffMPEG4);  
    RegisterSniffer(SniffFragmentedMP4);  
    RegisterSniffer(SniffMatroska);  
    RegisterSniffer(SniffOgg);  
    RegisterSniffer(SniffWAV);  
    RegisterSniffer(SniffFLAC);  
    RegisterSniffer(SniffAMR);  
    RegisterSniffer(SniffMPEG2TS);  
    RegisterSniffer(SniffMP3);  
    RegisterSniffer(SniffAAC);  
    RegisterSniffer(SniffMPEG2PS);  
    RegisterSniffer(SniffWVM);  
  
    char value[PROPERTY_VALUE_MAX];  
    if (property_get("drm.service.enabled", value, NULL)  
            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {  
        RegisterSniffer(SniffDRM);  
    }  
}
// static
void DataSource::RegisterSniffer(SnifferFunc func) {
    Mutex::Autolock autoLock(gSnifferMutex);
 
    for (List<SnifferFunc>::iterator it = gSniffers.begin();
         it != gSniffers.end(); ++it) {
        if (*it == func) {
            return;
        }
    }
 
    gSniffers.push_back(func);
}

从代码可以看出RegisterDefaultSniffers的主要作用既是注册Sniffer函数将所有的sniffer函数都挂在全局链表gSniffers中。sniffer函数的主要作用就是用于探测文件的类型,每种类型的媒体文件都对应一个sniffer函数。这里从代码可以看出原生的android播放器支持的格式还比较少。
AwesomePlayer中extractor 创建流程:

在setDataSource的最后,会调用setDataSource_l(dataSource),将datasource和对应的extractor对应起来。

status_t AwesomePlayer::setDataSource_l(  
        const sp<DataSource> &dataSource) {  
    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);  
   
    if (extractor == NULL) {  
        return UNKNOWN_ERROR;  
    }  
   
    if (extractor->getDrmFlag()) {  
        checkDrmStatus(dataSource);  
    }  
   
    return setDataSource_l(extractor);  
}  

MediaExtractor::Create(), 这里通过MediaExtractor::Create创建extractor:

sp<MediaExtractor> MediaExtractor::Create(  
        const sp<DataSource> &source, const char *mime) {  
    sp<AMessage> meta;  
   
    String8 tmp;  
    if (mime == NULL) {  
        float confidence;  
        if (!source->sniff(&tmp, &confidence, &meta)) {  
            ALOGV("FAILED to autodetect media content.");  
   
            return NULL;  
        }  
   
        mime = tmp.string();  
        ALOGV("Autodetected media content as '%s' with confidence %.2f",  
             mime, confidence);  
    } 

主要是将gSniffers链表中的每种格式的函数调用一遍,选取最高的confidence作为选中的文件格式。

MediaExtractor *ret = NULL;
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime, “audio/mp4”)) {
int fragmented = 0;
if (meta != NULL && meta->findInt32(“fragmented”, &fragmented) && fragmented) {
ret = new FragmentedMP4Extractor(source);
} else {
ret = new MPEG4Extractor(source);
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
ret = new MP3Extractor(source, meta);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
ret = new AMRExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {
ret = new FLACExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
ret = new WAVExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
ret = new OggExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
ret = new MatroskaExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
ret = new MPEG2TSExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {
// Return now. WVExtractor should not have the DrmFlag set in the block below.
return new WVMExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
ret = new AACExtractor(source, meta);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
ret = new MPEG2PSExtractor(source);
}

if (ret != NULL) {  
   if (isDrm) {  
       ret->setDrmFlag(true);  
   } else {  
       ret->setDrmFlag(false);  
   }  
}  

return ret;  

}

成功的通过sniff函数确定了文件的格式之后,就可以构造对应的extractor对象。

2)进入/libstagefright/omx/SoftOMXPlugin.cpp读取kComponents数组,此为现有系统支持的解码器列表
AwesomePlayer通过OMXClient::connect()得到OMX的实例,在构造OMX对象的过程中又调用了OMXMaster的构造函数创建OMXMaster的对象。

status_t OMXClient::connect() {  
    sp<</span>IServiceManager> sm = defaultServiceManager();  
    sp<</span>IBinder> binder = sm->getService(String16("media.player"));  
    sp<</span>IMediaPlayerService> service = interface_cast<</span>IMediaPlayerService>(binder);  
    
    CHECK(service.get() != NULL);  
    
    mOMX = service->getOMX();  
    CHECK(mOMX.get() != NULL);  
    
    if (!mOMX->livesLocally(NULL , getpid())) {  
        ALOGI("Using client-side OMX mux.");  
        mOMX = new MuxOMX(mOMX);  
    }  
    
    return OK;  
}  
OMX::OMX()  
    : mMaster(new OMXMaster),  
      mNodeCounter(0) {  
}  

在OMXMaster构造函数中有addPlugin()函数创建SoftOMXPlugin对象

OMXMaster::OMXMaster()  
    : mVendorLibHandle(NULL) {  
    addVendorPlugin();  
    addPlugin(new SoftOMXPlugin);  
}  

addPlugin()函数的实现在之前的文章中已经给出,主要是将enumerateComponents枚举出来的各种解码器名字存放在成员变量mPluginByComponentName中,类型为 KeyedVector。这样就读取了kComponents数组。

3)读取/etc/media_codec.xml文件,根据已探测的文件类型获取需要的解码器名称,再对比kComponents得到实际的解码器
AwesomePlayer构造函数结束后,在setDataSource之后会调用prepare方法,其实现中会调用initAudioDecoder和initVideoDecoder来构造解码器实例。

status_t AwesomePlayer::initVideoDecoder()
{ 
    mVideoSource = OMXCodec::Create(
            mClient.interface(), 
            mVideoTrack->getFormat(), 
            false, 
            mVideoTrack);
}
sp<</span>MediaSource> OMXCodec::Create(*)  
{  
    
        findMatchingCodecs(  
            mime, createEncoder, matchComponentName, flags, &matchingCodecs);  
    
        sp<</span>OMXCodecObserver> observer = new OMXCodecObserver;  
        IOMX::node_id node = 0;  
    
        status_t err = omx->allocateNode(componentName, observer, &node);  
    
        sp<</span>OMXCodec> codec = new OMXCodec(  
                    omx, node, quirks, flags,  
                    createEncoder, mime, componentName,  
                    source, nativeWindow);  
    
        observer->setCodec(codec);  
    
        err = codec->configureCodec(meta);  
    
}  

findMatchingCodecs()的实现:

void OMXCodec::findMatchingCodecs(
const char *mime,
bool createEncoder, const char *matchComponentName,
uint32_t flags,
Vector<CodecNameAndQuirks> *matchingCodecs) {
matchingCodecs->clear();

const MediaCodecList *list = MediaCodecList::getInstance();  
if (list == NULL) {  
    return;  
}  

size_t index = 0;  
for (;;) {  
    ssize_t matchIndex =  
        list->findCodecByType(mime, createEncoder, index);  

    if (matchIndex <</span> 0) {  
        break;  
    }  

    index = matchIndex + 1;  

    const char *componentName = list->getCodecName(matchIndex);  

    // If a specific codec is requested, skip the non-matching ones.  
    if (matchComponentName && strcmp(componentName, matchComponentName)) {  
        continue;  
    }  

    // When requesting software-only codecs, only push software codecs  
    // When requesting hardware-only codecs, only push hardware codecs  
    // When there is request neither for software-only nor for  
    // hardware-only codecs, push all codecs  
    if (((flags & kSoftwareCodecsOnly) &&   IsSoftwareCodec(componentName)) ||  
        ((flags & kHardwareCodecsOnly) &&  !IsSoftwareCodec(componentName)) ||  
        (!(flags & (kSoftwareCodecsOnly | kHardwareCodecsOnly)))) {  

        ssize_t index = matchingCodecs->add();  
        CodecNameAndQuirks *entry = &matchingCodecs->editItemAt(index);  
        entry->mName = String8(componentName);  
        entry->mQuirks = getComponentQuirks(list, matchIndex);  

        ALOGV("matching '%s' quirks 0xx",  
              entry->mName.string(), entry->mQuirks);  
    }  
}  

if (flags & kPreferSoftwareCodecs) {  
    matchingCodecs->sort(CompareSoftwareCodecsFirst);  
}  

}

从代码可以看到主要就是从MediaCodecList找到与传入的matchComponentName对应的解码器名称,MediaCodecList就是从/etc/media_codecs.xml解析出支持的解码器名称并匹配出对应的解码器名称。
我的总结(猜想,不是很明白):
AwesomePlayer首先探测文件格式类型,由文件格式类型可以得到mime和matchComponentName,由mime创建出对应的提取器(MediaExtractor);然后读取支持的格式列表kComponents数组作为预备;最后由matchComponentName在MediaCodecList(media_codec.xml)中找到需要的解码器名称,根据这个解码器名称到kComponents中查询并创建实际的解码器。

于是,可以得出结论:

要扩展对文件格式的支持,那么这三个地方都要相应扩展:

第一,扩展相应的提取器(MediaExtractor);
第二,扩展media_codec.xml,matchComponentName在这里边找到需要的解码器名称;
第三,扩展支持的解码器列表kComponents。

 类似资料: