Android音视频——NuPlayer框架

简介: Android音视频——NuPlayer框架

如标题所说,接下来讲的是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 作为播放器,其中涉及数据解析、解码、渲染等过程。


下图为结构关系图


image.pngNuPlayer 主要用于处理流媒体播放,自然会涉及通过不同流媒体协议传输过来的数据,并有对应的解析和处理逻辑,下面看看NuPlayer的类关系图image.pngAndroid层的多媒体框架,有多层实现,甚至有跨进程的调用。


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;
}
//部分省略
目录
相关文章
|
2月前
|
SQL 缓存 安全
Android ORM 框架之 greenDAO
Android ORM 框架之 greenDAO
84 0
|
2月前
|
JSON 前端开发 Android开发
Android MVI框架搭建与使用(上)
Android MVI框架搭建与使用(上)
144 0
|
2月前
|
监控 Unix 应用服务中间件
Android-音视频学习系列-(八)基于-Nginx-搭建(rtmp、http)直播服务器
Android-音视频学习系列-(八)基于-Nginx-搭建(rtmp、http)直播服务器
|
28天前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
29天前
|
JavaScript Java Android开发
kotlin安卓在Jetpack Compose 框架下跨组件通讯EventBus
**EventBus** 是一个Android事件总线库,简化组件间通信。要使用它,首先在Gradle中添加依赖`implementation &#39;org.greenrobot:eventbus:3.3.1&#39;`。然后,可选地定义事件类如`MessageEvent`。在活动或Fragment的`onCreate`中注册订阅者,在`onDestroy`中反注册。通过`@Subscribe`注解方法处理事件,如`onMessageEvent`。发送事件使用`EventBus.getDefault().post()`。
|
29天前
|
JavaScript 前端开发 Android开发
kotlin安卓在Jetpack Compose 框架下使用webview , 网页中的JavaScript代码如何与native交互
在Jetpack Compose中使用Kotlin创建Webview组件,设置JavaScript交互:`@Composable`函数`ComposableWebView`加载网页并启用JavaScript。通过`addJavascriptInterface`添加`WebAppInterface`类,允许JavaScript调用Android方法如播放音频。当页面加载完成时,执行`onWebViewReady`回调。
|
1月前
|
缓存 Android开发 Kotlin
【安卓app开发】kotlin Jetpack Compose框架 | 先用OKhttp下载远程音频文件再使用ExoPlayer播放
使用 Kotlin 的 Jetpack Compose 开发安卓应用时,可以结合 OkHttp 下载远程音频文件和 ExoPlayer 进行播放。在 `build.gradle` 添加相关依赖后,示例代码展示了如何下载音频并用 ExoPlayer 播放。代码包括添加依赖、下载文件、播放文件及简单的 Compose UI。注意,示例未包含完整错误处理和资源释放,实际应用需补充这些内容。
|
1月前
|
前端开发 JavaScript 测试技术
|
2月前
|
数据库 Android开发
Android数据库框架-GreenDao入门,2024年最新flutter 页面跳转动画
Android数据库框架-GreenDao入门,2024年最新flutter 页面跳转动画
Android数据库框架-GreenDao入门,2024年最新flutter 页面跳转动画
|
2月前
|
开发框架 架构师 安全
android快速开发框架,建议收藏
android快速开发框架,建议收藏