如标题所说,接下来讲的是NuPlayer,不知道对这个,大家了解多少呢。
Android2.3时引入流媒体框架,而流媒体框架的核心是NuPlayer。在之前的版本中一般认为Local
Playback就用Stagefrightplayer+Awesomeplayer,流媒体用NuPlayer。Android4.0之后HttpLive和RTSP协议开始使用NuPlayer播放器,Android5.0(L版本)之后本地播放也开始使用NuPlayer播放器。
Android7.0(N版本)则完全去掉了Awesomeplayer。
通俗点说,NuPlayer是AOSP中提供的多媒体播放框架,能够支持本地文件、HTTP(HLS)、RTSP等协议的播放,通常支持H.264、H.265/HEVC、AAC编码格式,支持MP4、MPEG-TS封装。
在实现上NuPlayer和Awesomeplayer不同,NuPlayer基于StagefrightPlayer的基础类构建,利用了更底层的ALooper/AHandler机制来异步地处理请求,ALooper列队消息请求,AHandler中去处理,所以有更少的Mutex/Lock在NuPlayer中。Awesomeplayer中利用了omxcodec而NuPlayer中利用了Acodec。
结构
NuPlayer 是从 MediaPlayerFactory构造出来的实例 NuPlayerFactory产生的,其结构关系图 如图5-1所示。
MediaPlayerFactory 通过工厂模式创建 StagefrightFactory 和 NuPlayerFactory,然后通过 NuPlayerFactory 创建 NuPlayerDriver,接着通过 NuPlayerDriver 构建一个 NuPlayer,NuPlayer 作为播放器,其中涉及数据解析、解码、渲染等过程。
下图为结构关系图
NuPlayer 主要用于处理流媒体播放,自然会涉及通过不同流媒体协议传输过来的数据,并有对应的解析和处理逻辑,下面看看NuPlayer的类关系图Android层的多媒体框架,有多层实现,甚至有跨进程的调用。
NuPlayer::Source:解析模块(parser,功能类似FFmpeg的avformat)。其接口与MediaExtractor和MediaSource组合的接口差不多,同时提供了用于快速定位的seekTo接口。
NuPlayer::Decoder:解码模块(decoder,功能类似FFmpeg的avcodec),封装了用于AVC、AAC解码的接口,通过ACodec实现解码(包含OMX硬解码和软解码)。
NuPlayer::Render:渲染模块(render,功能类似声卡驱动和显卡驱动),主要用于音视频渲染和同步,与NativeWindow有关。
在接下来的文章呢,也会详细讲解下这三个模块。
构造
NuPlayer 的构建呢,是在上层调用 setDataSource函数后,到达 MediaPlayerService中的 setDataSource函数,通过getPlayerType函数获取播放器类型。
首先说一下播放器的类型枚举
在**/frameworks/av/include/media/MediaPlayerInterface.h**录下。
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, };
status_t MediaPlayerService::Client::setDataSource( const sp<IStreamSource> &source) { // create the right type of player player_type playerType = MediaPlayerFactory::getPlayerType(this, source); sp<MediaPlayerBase> p = setDataSource_pre(playerType); if (p == NULL) { return NO_INIT; } // now set data source setDataSource_post(p, p->setDataSource(source)); return mStatus; }
现在再看一下 getPlayerType 方法
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client, const sp<IStreamSource> &source) { GET_PLAYER_TYPE_IMPL(client, source); }
接下来再往下可以看到一个宏函数,简单介绍一下 #define 的作用
在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。
在C或C++语言中,“宏”分为有参数和无参数两种。
#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;
这个宏函数的标示遍历map中存放的播放器工厂类,调用 scoreFactory 可以得到播放器的播放能力。这时候根据前面 StagefrightPlayerFactory 中的 if判断逻辑,thisScore>bestScore条 件不成立,所以得到 thisScore是0.0,而如果是NuPlayer,默认就会有一个0.8的值,所以返回的 ret 就是 NuPlayerFactory 对象。如果得到的值是 0.0,就会进入 getDefaultPlayerType函 数,代码如下:
static player_type getDefaultPlayerType() { char value[PROPERTY_VALUE_MAX]; if (property_get("media.stagefright.use-awesome", value, NULL) && (!strcmp("1", value) || !strcasecmp("true", value))) { return STAGEFRIGHT_PLAYER; } return NU_PLAYER; }
也就是如果设置了 property 是 media.stagefright.use-awesome,才会走到 StagefrightPlayer- Factory, 默认是 NuPlayerFactory。可见 Google 已经逐步替换掉 StagefrightPlayer 而使用 NuPlayer了。
针对 StagefrightPlayerFactory 会创建 StagefrightPlayer,而针对 NuPlayerFactory 不会直接创 建NuPlayer,而是在NuPlayerDriver的构造函数中创建一个NuPlayerDriver:
NuPlayerDriver::NuPlayerDriver(pid_t pid) : mState(STATE_IDLE), mIsAsyncPrepare(false), mAsyncResult(UNKNOWN_ERROR), mSetSurfaceInProgress(false), mDurationUs(-1), mPositionUs(-1), mSeekInProgress(false), mLooper(new ALooper), mPlayerFlags(0), mAtEOS(false), mLooping(false), mAutoLoop(false), mStartupSeekTimeUs(-1) { ALOGV("NuPlayerDriver(%p)", this); mLooper->setName("NuPlayerDriver Looper"); mLooper->start( false, /* runOnCallingThread */ true, /* canCallJava */ PRIORITY_AUDIO); mPlayer = new NuPlayer(pid); mLooper->registerHandler(mPlayer); mPlayer->setDriver(this); }
NuPlayer 继承自 AHandler,并且引入了 AMessage,通过 ALooper 来处理消息,如NuPlayerDriver 调用 NuPlayer 的 prepareAsync 函数:
void NuPlayer::prepareAsync() {
(new AMessage(kWhatPrepare, this))->post();
}
void NuPlayer::onMessageReceived(const sp<AMessage> &msg)( switch (msg->what()){ //省略部分代码 case kWhatPrepare: mSource->prepareAsync(); break; } //部分省略