admin管理员组

文章数量:1516870

前言

我们知道MediaPlayer播放的时候,最终会调到native层的MediaPlayerService中,在MediaPlayerService中会创建NuPlayer和TestPlayer,那么这俩Player是如何选择的呢?就涉及到了选择player的得分机制。

正文

先看下MediaPlayerService创建player的过程,首先MediaPlayerService在启动的时候

MediaPlayerService::MediaPlayerService(){ALOGV("MediaPlayerService created");
    mNextConnId =1;
    MediaPlayerFactory::registerBuiltinFactories();}

会调用到MediaPlayerFactory的registerBuiltinFactories,我们看下registerBuiltinFactories这个函数

void MediaPlayerFactory::registerBuiltinFactories(){
    Mutex::Autolock lock_(&sLock);//这是一个flage,标志此函数只能调用一次。if(sInitComplete)return;
    IFactory* factory =newNuPlayerFactory();if(registerFactory_l(factory, NU_PLAYER)!= OK)delete factory;
    factory =newTestPlayerFactory();if(registerFactory_l(factory, TEST_PLAYER)!= OK)delete factory;
    sInitComplete =true;}

分别new了两个factory,NuPlayerFactory和TestPlayerFactory,并分别调用了registerFactory_l,那么继续看下registerFactory_l

status_t MediaPlayerFactory::registerFactory_l(IFactory* factory,
                                               player_type type){if(NULL== factory){ALOGE("Failed to register MediaPlayerFactory of type %d, factory is"" NULL.", type);return BAD_VALUE;}if(sFactoryMap.indexOfKey(type)>=0){ALOGE("Failed to register MediaPlayerFactory of type %d, type is"" already registered.", type);return ALREADY_EXISTS;}if(sFactoryMap.add(type, factory)<0){ALOGE("Failed to register MediaPlayerFactory of type %d, failed to add"" to map.", type);return UNKNOWN_ERROR;}return OK;}

将NuPlayerFactory和TestPlayerFacory加入到容器sFactoryMap中。到此MediaPlayerService就初始化完成了。简单总结一下就是MediaPlayerService初始化的时候通过MediaPlayerFactory分别创建了NuPlayerFactory和TestPlayerFacory,并将两个factory加入到容器sFactoryMap中。
那么我们是在什么时候选取的player呢,其实是在我们SetDataSource的时候,我们知道MediaPlayer的java层Api暴露的SetDataSource的接口很多,我粗略看了下不管是hide还是systemapi的一共11个,其实这么多对应到native层就三个分别是

status_t MediaPlayer::setDataSource(const sp<IMediaHTTPService>&httpService,constchar*url,const KeyedVector<String8, String8>*headers)
status_t MediaPlayer::setDataSource(const sp<IMediaHTTPService>&httpService,constchar*url,const KeyedVector<String8, String8>*headers)
status_t MediaPlayer::setDataSource(const sp<IDataSource>&source)

这里简单列出了一个Media Player Service中的setDataSource的源码,其实不管哪个最终调用的都是MediaPlayerFactory的getPlayerType

player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,constchar* url){GET_PLAYER_TYPE_IMPL(client, url);}
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,int fd,int64_t offset,int64_t length){GET_PLAYER_TYPE_IMPL(client, fd, offset, length);}
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,const sp<IStreamSource>&source){GET_PLAYER_TYPE_IMPL(client, source);}
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,const sp<DataSource>&source){GET_PLAYER_TYPE_IMPL(client, source);}

选取PlayerType的时候代码如下

#define GET_PLAYER_TYPE_IMPL(a...)                      \
    Mutex::Autolock lock_(&sLock);                      \
                                                        \
    player_type ret = STAGEFRIGHT_PLAYER;               \
    float bestScore = 0.0;                              \
                                                        \
    for (size_t i = 0; i < sFactoryMap.size(); ++i) {   \
                                                        \
        IFactory* v = sFactoryMap.valueAt(i);           \
        float thisScore;                                \
        CHECK(v != NULL);                               \
        thisScore = v->scoreFactory(a, bestScore);      \
        if (thisScore > bestScore) {                    \
            ret = sFactoryMap.keyAt(i);                 \
            bestScore = thisScore;                      \
        }                                               \
    }                                                   \
                                                        \
    if (0.0 == bestScore) {                             \
        ret = getDefaultPlayerType();                   \
    }                                                   \
                                                        \
    return ret;

我们先看下playerType的定义

enum player_type {
    STAGEFRIGHT_PLAYER =3,
    NU_PLAYER =4,// Test players are available only in the 'test' and 'eng' builds.// The shared library with the test player is passed passed as an// argument to the 'test:' url in the setDataSource call.// 这个好像只是测试用的
    TEST_PLAYER =5,};

只有3个,1和2可能是早期的版本使用的,后来被移除了。STAGEFRIGHT_PLAYER这里基本也不用了,虽然这里playerType默认STAGEFRIGHT_PLAYER,但真正选择默认player的时候,是NU_PLAYER。我们继续看这个函数bestScore 是最终得分,thisScore是当前的分数,我们分析MediaPlayer初始化的时候知道了sFactoryMap其实只存储了两个值,分别是NU_PLAYER和TEST_PLAYER。那么我们就分别看下这俩fatory的scoreFactory

classNuPlayerFactory:public MediaPlayerFactory::IFactory {public:virtualfloatscoreFactory(const sp<IMediaPlayer>&/*client*/,constchar* url,float curScore){staticconstfloat kOurScore =0.8;if(kOurScore <= curScore)return0.0;if(!strncasecmp("http://", url,7)||!strncasecmp("https://", url,8)||!strncasecmp("file://", url,7)){
            size_t len =strlen(url);if(len >=5&&!strcasecmp(".m3u8",&url[len -5])){return kOurScore;}if(strstr(url,"m3u8")){return kOurScore;}if((len >=4&&!strcasecmp(".sdp",&url[len -4]))||strstr(url,".sdp?")){return kOurScore;}}if(!strncasecmp("rtsp://", url,7)){return kOurScore;}return0.0;}virtualfloatscoreFactory(const sp<IMediaPlayer>&/*client*/,const sp<IStreamSource>&/*source*/,float/*curScore*/){return1.0;}virtualfloatscoreFactory(const sp<IMediaPlayer>&/*client*/,const sp<DataSource>&/*source*/,float/*curScore*/){// Only NuPlayer supports setting a DataSource source directly.return1.0;}

对于url的的得分计算还算不错,大概根据url来处理,是0.8分还是0.0分的。对于剩下两种情况就比较敷衍了,直接得分1.0.我们在看下TestPlayerFactory 的得分机制

classTestPlayerFactory:public MediaPlayerFactory::IFactory {public:virtualfloatscoreFactory(const sp<IMediaPlayer>&/*client*/,constchar* url,float/*curScore*/){if(TestPlayerStub::canBeUsed(url)){return1.0;}return0.0;}

如果canBeUsed则得1.0分否则得0.0分,我们看下canBeUsed这个函数

/* static */bool TestPlayerStub::canBeUsed(constchar*url){returnisTestBuild()&&isTestUrl(url);}

其中isTestBuild

// @return true if the current build is 'eng' or 'test'.boolisTestBuild(){char prop[PROPERTY_VALUE_MAX]={'\0',};property_get(kBuildTypePropName, prop,"\0");returnstrcmp(prop, kEngBuild)==0||strcmp(prop, kTestBuild)==0;}

注释说的已经很清楚了,我们build的是一个eng或者test的版本,并且isTestUrl的时候我们就使用TestPlayer,那么什么样的url是testUrl呢

// @return true if the url scheme is 'test:'boolisTestUrl(constchar*url){return url &&strncmp(url, kTestUrlScheme,strlen(kTestUrlScheme))==0;}

原来是以’test:'这种开头的url。到此我们大概就明白了什么时候选取什么样的player了。
回到最初得分的那个函数里,如果我们thisScore > bestScore,那么最终得分bestScore = thisScore ,如果一顿操作猛如虎后,bestScore仍是0.0,那么就要使用getDefaultPlayerType()了。

static player_type getDefaultPlayerType(){return NU_PLAYER;}

感觉像是被耍了一样,直接return NU_PLAYER不好吗?为何还要在开头搞个player_type ret = STAGEFRIGHT_PLAYER,来吓唬人。
到此拿到player_type后,后面的就是创建对应的player了。这里就不在分析了。

总结

对于Android的早期版本关注不是很多,或许早期版本的player有多个以及复杂的得分机制。但是最近几个版本,包括我们此次分析的Android10.0.对于mediaPlayer的选取以及得分的机制都简单了很多,或许谷歌只是想提供基本的播放和测试,剩下的就让大家随便定制的。
总结下来,我们player的得分机制是在setDataSource的时候,通过MediaPlayerFactory选取对应的player_type 。最终创建需要的player。

本文标签: 的时候系统编程