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

Android Media Framework 框架的层次:

  1. Java层:frameworks/base/media/java/android/media/MediaPlayer.java
  2. JNI本地调用:frameworks/base/media/jni/android_media_MediaPlayer.cpp
  3. libmedia多媒体底层库:frameworks/base/media/libmedia/mediaplayer.cpp
  4. libmediaplayer多媒体服务部分:frameworks/base/media/libmediaplayerservice/MediaPlayerService.cpp, StagefrightPlayer.cpp
  5. 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个大的步骤:

  1. 探测文件类型,根据文件类型创建对应的提取器(MediaExtractor);
  2. 进入/libstagefright/omx/SoftOMXPlugin.cpp读取kComponents数组,此为现有系统支持的解码器列表;
  3. 读取/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::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) {  
    sp 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::Create(  
        const sp &source, const char *mime) {  
    sp 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<IServiceManager> sm = defaultServiceManager();  
    sp<IBinder> binder = sm->getService(String16("media.player"));  
    sp<IMediaPlayerService> service = interface_cast<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<MediaSource> OMXCodec::Create(*)  
{  
    
        findMatchingCodecs(  
            mime, createEncoder, matchComponentName, flags, &matchingCodecs);  
    
        sp<OMXCodecObserver> observer = new OMXCodecObserver;  
        IOMX::node_id node = 0;  
    
        status_t err = omx->allocateNode(componentName, observer, &node);  
    
        sp<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 < 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。

你可能感兴趣的:(Android Media Framework(4): 支持格式的扩展)