如标题所说,接下来讲的是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;
}
//部分省略