Android音视频框架探索(三):系统播放器MediaPlayer的创建流程

0. 前言

在前面的文章《Android音视频学习(一):MediaPlayer》中,我们对Android系统提供的音视频组件MediaPlayer的应用层接口,主要状态转换和使用流程有了一个大概的了解。今天我们将更进一步,深入Android源码讨论MediaPlayer的主体架构。本文主要介绍MediaPlayer从App到其底层NuPlayer实现的创建过程。

1. MediaPlayer的代码结构

首先介绍一下MediaPlayer的代码结构。在实现上MediaPlayer也是类似C/S架构形式,App作为使用者,即客户端,主要使用java的MediaPlayer接口来控制播放功能,经过JNI,其底层的C++对象通过Binder与MediaServer进行通信。

而系统的MediaServer进程则是服务端,其中运行的MediaPlayerService对象是系统层面的MediaPlayer的总管对象,系统内当前所有的MediaPlayer会由其来管理。当收到客户端的Binder请求时,MediaPlayerService会创建具体的MediaPlayerService::Client,为当前的App进行服务。在层次图上这里结构有一些代码上的封装,但MediaPlayerService::Client可以看作具体持有和使用播放器的核心NuPlayer

MediaPlayer的java层API在安卓源码目录的frameworks/base/media/java/android/media/MediaPlayer.java下。
MediaPlayerService的代码在frameworks/av/media/libmediaplayerservice/MediaPlayerService.h NuPlayer的代码则在frameworks/av/media/libmediaplayerservice/nuplayer/

2. 创建流程源码探究

2.1 构造方法

首先从MediaPlayer的构造方法看起。

2.1.1 主要流程

MediaPlayer的构造方法主要流程如下,主要是初始化,创建native层的C++对象MediaPlayer(继承自MediaPlayerClient, IMediaPlayerClient Binder)为其设置一个listener,用于java层对象收到回调。

2.1.2 源码走读

java 复制代码
// MediaPlayer最主要的构造方法
// Context是App的上下文对象
// sessionId是AudioSessionID, 用于音频系统,需要创建一个会话id,如果设置为AudioSystem.AUDIO_SESSION_ALLOCATE (0), 则会由底层自动生成一个唯一id
private MediaPlayer(Context context, int sessionId) {
    super(new AudioAttributes.Builder().build(),
            AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);

    // 这里是创建EventHandler,并将自身引用传入,用于响应时间
    // looper是事件系统的事件循环,代表着事件监听者的线程。
    Looper looper;
    if ((looper = Looper.myLooper()) != null) {
        // 如果当前线程有事件循环,则EventHandler交给本线程监听
        mEventHandler = new EventHandler(this, looper);
    } else if ((looper = Looper.getMainLooper()) != null) {
        // 否则交由主线程的事件循环监听。
        mEventHandler = new EventHandler(this, looper);
    } else {
        mEventHandler = null;
    }

    // 初始化一个计时器,可以为MediaPlayer提供当前时间,设置定时任务等行管操作
    mTimeProvider = new TimeProvider(this);
    // 字幕流输入,不是很重要
    mOpenSubtitleSources = new Vector<InputStream>();

    // AttributionSource用于追踪应用的权限,经常被用于不同应用之间确认权限信息,这里主要是用于native层确认权限信息。
    AttributionSource attributionSource =
            context == null ? AttributionSource.myAttributionSource()
                    : context.getAttributionSource();
    // set the package name to empty if it was null
    if (attributionSource.getPackageName() == null) {
        attributionSource = attributionSource.withPackageName("");
    }

    // 这里其实就是将attributionSource内容,序列化为Parcel结构,用于Binder通信
    try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
        // 主流程!调用native_setup完成native层的创建工作
        native_setup(new WeakReference<>(this), attributionSourceState.getParcel(),
                resolvePlaybackSessionId(context, sessionId));
    }
    // 调用基类的方法,实例化完成后向AudioService设置一下audioSessionid和当前player的信息
    baseRegisterPlayer(getAudioSessionId());
}

native_setup()是一个native方法,它会通过JNI调用到frameworks/base/media/jni/android_media_MediaPlayer.cpp下的android_media_MediaPlayer_native_setup()方法。

C++ 复制代码
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
                                       jobject jAttributionSource,
                                       jint jAudioSessionId)
{
    ALOGV("native_setup");
    // 获取Parcel,反序列化并通过其构造C++层的attributionSource对象
    Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
    android::content::AttributionSourceState attributionSource;
    attributionSource.readFromParcel(parcel);
    // 创建C++层的MediaPlayer
    sp<MediaPlayer> mp = sp<MediaPlayer>::make(
        attributionSource, static_cast<audio_session_t>(jAudioSessionId));
    if (mp == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }

    // 创建一个JNI层的listener,用于触发MediaPlayer的回调。
    // 这里JNIMediaPlayerListener会在env里创建MediaPlayer的全局jclass引用,以及weak_this的全局引用,方便从其他线程触发回调。
    // listener会交给C++的mp保留,事件触发时回调给jni层。
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);

    // 将C++的mp对象设置给java对象,会将原始指针保存在java对象的mNativeContext成员中
    setMediaPlayer(env, thiz, mp);
}

让我们来看看C++层的MediaPlayer在创建时做了什么,这里就进入了frameworks/av/media/libmedia/mediaplayer.cpp的MediaPlayer::MediaPlayer()方法。

C++ 复制代码
// 构造方法
MediaPlayer::MediaPlayer(const AttributionSourceState& attributionSource,
    const audio_session_t sessionId) : mAttributionSource(attributionSource)
{
    // 以下都可以看作是状态的初始化,一开始没办法搞明白每个成员的意义
    // 但是不要紧,我们在具体功能中才能去理解,尽量先从大意上有个认识就行
    ALOGV("constructor");
    mListener = NULL;             // JNI监听者
    mCookie = NULL;
    mStreamType = AUDIO_STREAM_MUSIC;
    mAudioAttributesParcel = NULL;
    mCurrentPosition = -1;
    mCurrentSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
    mSeekPosition = -1;
    mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
    mCurrentState = MEDIA_PLAYER_IDLE;       // MediaPlayer的状态,构造后进入idle
    mPrepareSync = false;
    mPrepareStatus = NO_ERROR;
    mLoop = false;
    mLeftVolume = mRightVolume = 1.0;
    mVideoWidth = mVideoHeight = 0;
    mLockThreadId = 0;
    // 如果audioSeesionid 传入的是AUDIO_SESSION_ALLOCATE (0), 那么底层具体的id则会自行生成一个唯一id
    if (sessionId == AUDIO_SESSION_ALLOCATE) {
        mAudioSessionId = static_cast<audio_session_t>(
            AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION));
    } else {
        mAudioSessionId = sessionId;
    }
    AudioSystem::acquireAudioSessionId(mAudioSessionId, (pid_t)-1, (uid_t)-1); // always in client.
    mSendLevel = 0;
    mRetransmitEndpointValid = false;
}

// JNI层在构造之后调用的setListener(), 只是对象赋值而已。
status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener)
{
    ALOGV("setListener");
    Mutex::Autolock _l(mLock);
    mListener = listener;
    return NO_ERROR;
}

2.2 setDataSource()

setDataSource()方法为MediaPlayer设置需要播放的输入媒体源,在实现上它还包含了创建底层播放器核心NuPlayer的任务。

2.2.1 主要流程

App调用setDataSource()的主要任务是为了给播放器设置输入参数,如远程文件url、本地文件路径信息等。

而在setDataSource()这个方法内具体还会通过MediaPlayerService创建底层播放器实现NuPlayer。主要的流程如下图所示:

2.2.2 源码走读

2.2.2.1 从Java层到JNI

setDataSource()方法有很多种参数输入的版本,我们从最简单的版本看起:

java 复制代码
// 输入是String的版本,也可以是本地文件,也可以是远程url
public void setDataSource(String path)
        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
    setDataSource(path, null, null);
}

继而会调用三参数版本的同名方法。

java 复制代码
// 注意这里输入的headers和cookies都为null
private void setDataSource(String path, Map<String, String> headers, List<HttpCookie> cookies)
        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
{
    String[] keys = null;
    String[] values = null;

    if (headers != null) {
        keys = new String[headers.size()];
        values = new String[headers.size()];

        int i = 0;
        for (Map.Entry<String, String> entry: headers.entrySet()) {
            keys[i] = entry.getKey();
            values[i] = entry.getValue();
            ++i;
        }
    }
    // 所以这里相当于调用setDataSource(path, null, null, null);
    setDataSource(path, keys, values, cookies);
}

然后会调用4参数的版本:

java 复制代码
private void setDataSource(String path, String[] keys, String[] values,
        List<HttpCookie> cookies)
        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
    // 解析url和对应的协议
    final Uri uri = Uri.parse(path);
    final String scheme = uri.getScheme();
    if ("file".equals(scheme)) {
        path = uri.getPath();
    } else if (scheme != null) {
        // 如果是非本地文件资源,会走这里
        nativeSetDataSource(
            MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
            path,
            keys,
            values);
        return;
    }

    // 如果是本地文件,打开走文件描述符fd版本的输入
    final File file = new File(path);
    try (FileInputStream is = new FileInputStream(file)) {
        setDataSource(is.getFD());
    }
}

我们先看看输入是文件的版本:

java 复制代码
// 直接设置offest为0,length为MAX,调参数为(fd, offset, length)的版本
public void setDataSource(FileDescriptor fd)
        throws IOException, IllegalArgumentException, IllegalStateException {
    // intentionally less than LONG_MAX
    setDataSource(fd, 0, 0x7ffffffffffffffL);
}

public void setDataSource(FileDescriptor fd, long offset, long length)
        throws IOException, IllegalArgumentException, IllegalStateException {
    try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {
        if (modernFd == null) {
            _setDataSource(fd, offset, length);
        } else {
            _setDataSource(modernFd.getFileDescriptor(), offset, length);
        }
    } catch (IOException e) {
        Log.w(TAG, "Ignoring IO error while setting data source", e);
    }
}

之后调用_setDataSource(),也是一个native方法:

java 复制代码
private native void _setDataSource(FileDescriptor fd, long offset, long length)
        throws IOException, IllegalArgumentException, IllegalStateException;

对应的JNI层的方法:

C++ 复制代码
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    // 从java对象的mNativeContext成员中拿到C++对象mediaplayer的指针
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    if (fileDescriptor == NULL) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return;
    }
    // 从java对象中获取文件描述符fd
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    ALOGV("setDataSourceFD: fd %d", fd);
    // 注意,这里套了两层
    // 首先是调用C++层的方法,mp->setDataSource()
    // 然后根据其返回结果,调用process_media_player_call()对异常进行处理。
    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
2.2.2.2 通过MediaPlayerService来创建MediaPlayerService::Client

然后来看C++层对象的setDataSource():

C++ 复制代码
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
    status_t err = UNKNOWN_ERROR;
    // 获得mediaPlayerService(的Binder代理)对象
    const sp<IMediaPlayerService> service(getMediaPlayerService());
    if (service != 0) {
        // 调用service的create方法,创建IMediaPlayer Binder对象(实体在MediaPlayerService进程)
        sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mAttributionSource));
        // doSetRetransmitEndpoint是有调用setRetransmitEndpoint()后才会生效,功能是"转发XX至对应IP?"(先忽略,不影响主流程,一般也不会生效)
        // 具体调用IMediaPlayer的setDataSource方法来完成功能
        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
            (NO_ERROR != player->setDataSource(fd, offset, length))) {
            player.clear();
        }
        // 保留新创建的IMediaPlayer引用,会存放在mPlayer字段中
        err = attachNewPlayer(player);
    }
    return err;
}

看一下MediaPlayerServicecreate方法:

C++ 复制代码
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
        audio_session_t audioSessionId, const AttributionSourceState& attributionSource)
{
    // -- 创建一个单调递增的connect id
    int32_t connId = android_atomic_inc(&mNextConnId);
    // -- 创建AttributionSourceState
    AttributionSourceState verifiedAttributionSource = attributionSource;
    verifiedAttributionSource.pid = VALUE_OR_FATAL(
        legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
    verifiedAttributionSource.uid = VALUE_OR_FATAL(
        legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));

    // 创建MediaPlayer(实体)
    sp<Client> c = new Client(
            this, verifiedAttributionSource, connId, client, audioSessionId);

    ALOGV("Create new client(%d) from %s, ", connId,
        verifiedAttributionSource.toString().c_str());

    // 弱引用加入到mClients列表中
    wp<Client> w = c;
    {
        Mutex::Autolock lock(mLock);
        mClients.add(w);
    }
    return c;
}
2.2.2.3 创建具体的NuPlayer

MediaPlayerService::create()创建一个MediaPlayerService::Client后,所调用MediaPlayerService::ClientsetDataSource方法的具体实现:

c++ 复制代码
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld",
            fd, nameForFd(fd).c_str(), (long long) offset, (long long) length);
    // 拿到fd对应的文件状态,判断参数offset和length是否有效
    struct stat sb;
    int ret = fstat(fd, &sb);
    if (ret != 0) {
        ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
        return UNKNOWN_ERROR;
    }

    ALOGV("st_dev  = %llu", static_cast<unsigned long long>(sb.st_dev));
    ALOGV("st_mode = %u", sb.st_mode);
    ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));
    ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));
    ALOGV("st_size = %llu", static_cast<unsigned long long>(sb.st_size));

    if (offset >= sb.st_size) {
        ALOGE("offset error");
        return UNKNOWN_ERROR;
    }
    if (offset + length > sb.st_size) {
        length = sb.st_size - offset;
        ALOGV("calculated length = %lld", (long long)length);
    }

    // 根据输入判断Player实现的具体type,这里有Stagefright和NuPlayer,在Androd6.0之后主要使用Nuplayer
    // 在最新的Android代码中,这里player_type虽然有STAGEFRIGHT_PLAYER,但实际上的工厂类Factory等,都不再包含Stagefright的代码,所以这里默认创建的是NuPlayer
    player_type playerType = MediaPlayerFactory::getPlayerType(this,
                                                               fd,
                                                               offset,
                                                               length);
                                                               
    sp<MediaPlayerBase> p = setDataSource_pre(playerType);
    if (p == NULL) {
        return NO_INIT;
    }

    // now set data source
    return mStatus = setDataSource_post(p, p->setDataSource(fd, offset, length));
}

具体看看setDataSource_pre(),里面会创建NuPlayer示例:

c++ 复制代码
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType)
{
    ALOGV("player type = %d", playerType);

    // 这里会创建NuPlayer
    sp<MediaPlayerBase> p = createPlayer(playerType);
    if (p == NULL) {
        return p;
    }

    // 创建几个Service的状态监听,在进程挂掉后会触发回调
    std::vector<DeathNotifier> deathNotifiers;

    // Listen to death of media.extractor service
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->getService(String16("media.extractor"));
    if (binder == NULL) {
        ALOGE("extractor service not available");
        return NULL;
    }
    deathNotifiers.emplace_back(
            binder, [l = wp<MediaPlayerBase>(p)]() {
        sp<MediaPlayerBase> listener = l.promote();
        if (listener) {
            ALOGI("media.extractor died. Sending death notification.");
            listener->sendEvent(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
                                MEDIAEXTRACTOR_PROCESS_DEATH);
        } else {
            ALOGW("media.extractor died without a death handler.");
        }
    });

    {
        using ::android::hidl::base::V1_0::IBase;

        // Listen to death of OMX service
        {
            sp<IBase> base = ::android::hardware::media::omx::V1_0::
                    IOmx::getService();
            if (base == nullptr) {
                ALOGD("OMX service is not available");
            } else {
                deathNotifiers.emplace_back(
                        base, [l = wp<MediaPlayerBase>(p)]() {
                    sp<MediaPlayerBase> listener = l.promote();
                    if (listener) {
                        ALOGI("OMX service died. "
                              "Sending death notification.");
                        listener->sendEvent(
                                MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
                                MEDIACODEC_PROCESS_DEATH);
                    } else {
                        ALOGW("OMX service died without a death handler.");
                    }
                });
            }
        }

        // Listen to death of Codec2 services
        {
            for (std::shared_ptr<Codec2Client> const& client :
                    Codec2Client::CreateFromAllServices()) {
                sp<IBase> hidlBase = client->getHidlBase();
                ::ndk::SpAIBinder aidlBase = client->getAidlBase();
                auto onBinderDied = [l = wp<MediaPlayerBase>(p),
                                     name = std::string(client->getServiceName())]() {
                    sp<MediaPlayerBase> listener = l.promote();
                    if (listener) {
                        ALOGI("Codec2 service \"%s\" died. "
                              "Sending death notification.",
                              name.c_str());
                        listener->sendEvent(
                                MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
                                MEDIACODEC_PROCESS_DEATH);
                    } else {
                        ALOGW("Codec2 service \"%s\" died "
                              "without a death handler.",
                              name.c_str());
                    }
                };
                if (hidlBase) {
                    deathNotifiers.emplace_back(hidlBase, onBinderDied);
                } else if (aidlBase.get() != nullptr) {
                    deathNotifiers.emplace_back(aidlBase, onBinderDied);
                }
            }
        }
    }

    Mutex::Autolock lock(mLock);

    mDeathNotifiers.clear();
    mDeathNotifiers.swap(deathNotifiers);
    mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);

    if (!p->hardwareOutput()) {
        mAudioOutput = new AudioOutput(mAudioSessionId, mAttributionSource,
                mAudioAttributes, mAudioDeviceUpdatedListener);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
    }

    return p;
}

createPlayer()方法里面会调用MediaPlayerFactory的静态方法来创建具体的播放器NuPlayer

C++ 复制代码
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
    // 这里getPlayer就是返回的mPlayer,第一次创建时为nullptr
    sp<MediaPlayerBase> p = getPlayer();
    if ((p != NULL) && (p->playerType() != playerType)) {
        ALOGV("delete player");
        p.clear();
    }
    if (p == NULL) {
        // 通过MediaPlayerFactory来创建
        p = MediaPlayerFactory::createPlayer(playerType, mListener,
            VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mAttributionSource.pid)));
    }

    if (p != NULL) {
        p->setUID(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mAttributionSource.uid)));
    }

    return p;
}
2.2.2.4 MediaPlayerFactory创建NuPlayer的具体过程

继续上文,具体是调用MediaPlayerFactory::createPlayer()

C++ 复制代码
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
        player_type playerType,
        const sp<MediaPlayerBase::Listener> &listener,
        pid_t pid) {
    sp<MediaPlayerBase> p;
    IFactory* factory;
    status_t init_result;
    Mutex::Autolock lock_(&sLock);

    // 根据sFactoryMap中注册的type,这里选到目前只有NuPlayerFactory 
    // sFactoryMap的注册逻辑可以参考frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp中的内容
    if (sFactoryMap.indexOfKey(playerType) < 0) {
        ALOGE("Failed to create player object of type %d, no registered"
              " factory", playerType);
        return p;
    }

    // 一定是NuPlayerFactory
    factory = sFactoryMap.valueFor(playerType);
    CHECK(NULL != factory);
    // 调用对应的创建方法
    p = factory->createPlayer(pid);

    if (p == NULL) {
        ALOGE("Failed to create player object of type %d, create failed",
               playerType);
        return p;
    }

    init_result = p->initCheck();
    if (init_result == NO_ERROR) {
        p->setNotifyCallback(listener);
    } else {
        ALOGE("Failed to create player object of type %d, initCheck failed"
              " (res = %d)", playerType, init_result);
        p.clear();
    }

    return p;
}

NuPlayer::createPlayer()里面则是创建了一个NuPlayerDriver

C++ 复制代码
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
  public:
    // ...
    virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
        ALOGV(" create NuPlayer");
        return new NuPlayerDriver(pid);
    }
};
2.2.2.5 NuPlayerDriver及NuPlayer的构造过程

这个NuPlayerDriver继承自MediaPlayerInterface,它其中包含一个成员mPlayer,具体指向NuPlayer,在构造函数中会一起创建。

C++ 复制代码
// struct NuPlayerDriver : public MediaPlayerInterface

NuPlayerDriver::NuPlayerDriver(pid_t pid)
    : mState(STATE_IDLE),
      mIsAsyncPrepare(false),
      mAsyncResult(UNKNOWN_ERROR),
      mSetSurfaceInProgress(false),
      mDurationUs(-1),
      mPositionUs(-1),
      mSeekInProgress(false),
      mPlayingTimeUs(0),
      mRebufferingTimeUs(0),
      mRebufferingEvents(0),
      mRebufferingAtExit(false),
      mLooper(new ALooper),                     // 会创建一个ALooper, 事件循环
      mMediaClock(new MediaClock),
      mPlayer(new NuPlayer(pid, mMediaClock)),  // 创建新的mPlayer
      mPlayerFlags(0),
      mCachedPlayerIId(PLAYER_PIID_INVALID),
      mMetricsItem(NULL),
      mClientUid(-1),
      mAtEOS(false),
      mLooping(false),
      mAutoLoop(false) {
    ALOGD("NuPlayerDriver(%p) created, clientPid(%d)", this, pid);
    mLooper->setName("NuPlayerDriver Looper");

    // 初始化时钟, 这里时钟也是基于单独运行在一个新线程上的新的ALooper,在init()中创建
    mMediaClock->init();                        

    // set up an analytics record
    mMetricsItem = mediametrics::Item::create(kKeyPlayer);
    
    // 启动事件循环
    mLooper->start(
            false, /* runOnCallingThread */
            true,  /* canCallJava */
            PRIORITY_AUDIO);

    // 事件处理者,mPlayer 
    mLooper->registerHandler(mPlayer);

    // NuPlayer初始化
    mPlayer->init(this);
}

时钟在创建时也会创建一个单独的ALooper:

C++ 复制代码
MediaClock::MediaClock()
    : mAnchorTimeMediaUs(-1),
      mAnchorTimeRealUs(-1),
      mMaxTimeMediaUs(INT64_MAX),
      mStartingTimeMediaUs(-1),
      mPlaybackRate(1.0),
      mGeneration(0) {
    mLooper = new ALooper;
    mLooper->setName("MediaClock");
    mLooper->start(false /* runOnCallingThread */,
                   false /* canCallJava */,
                   ANDROID_PRIORITY_AUDIO);
}

void MediaClock::init() {
    mLooper->registerHandler(this);
}
2.2.2.6 MediaPlayerService::Client::setDataSource的后续调用

在2.2.2.3节最后,通过setDataSource_pre()MediaPlayerService::Client创建了具体的NuPlayerDriver实例,之后会调用其setDataSource()方法,里面会调用NuPlayersetDataSourceAsync()方法。

C++ 复制代码
status_t NuPlayerDriver::setDataSource(
        const sp<IMediaHTTPService> &httpService,
        const char *url,
        const KeyedVector<String8, String8> *headers) {
    ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str());
    ATRACE_BEGIN(StringPrintf("setDataSource(%p)", this).c_str());
    Mutex::Autolock autoLock(mLock);

    if (mState != STATE_IDLE) {
        ATRACE_END();
        return INVALID_OPERATION;
    }

    mState = STATE_SET_DATASOURCE_PENDING;

    // 调用NuPlayer的setDataSourceAsync()方法
    mPlayer->setDataSourceAsync(httpService, url, headers);

    // 同步等待上面的方法完成
    while (mState == STATE_SET_DATASOURCE_PENDING) {
        mCondition.wait(mLock);
    }
    ATRACE_END();

    return mAsyncResult;
}

NuPlayerSetDataSource具体做了什么,我们在后续文章中进行解析。

之后MediaPlayerService::Client会调用setDataSource_post()方法。

C++ 复制代码
status_t MediaPlayerService::Client::setDataSource_post(
        const sp<MediaPlayerBase>& p,
        status_t status)
{
    ALOGV(" setDataSource");
    if (status != OK) {
        ALOGE("  error: %d", status);
        return status;
    }

    // 设置转发端口?不是很重要
    if (mRetransmitEndpointValid) {
        status = p->setRetransmitEndpoint(&mRetransmitEndpoint);
        if (status != NO_ERROR) {
            ALOGE("setRetransmitEndpoint error: %d", status);
        }
    }

    if (status == OK) {
        Mutex::Autolock lock(mLock);
        // 将NuPlayerDriver保留引用
        mPlayer = p;
    }
    return status;
}

3. 后记(碎碎念)

说实话,这篇博文鸽了两个多月,除了之前工作上的任务繁忙了一点,其他原因还是得反思一下,就是步子迈大了,把控不住就容易犯懒,降低动力。

我原本预期是想用一篇文章来介绍和讲解MediaPlayer的主要结构和接口,但是当我这么处理时,我现在回头来想,其实中间过程很难推动。有以下原因:

  1. 标题开太大,但是自身储备不足,整体结构难以把控。在这种情况下做一个整体的输出,其实耗费心力会很大,慢慢会降低动力。保持动力还是得快速输出。
  2. 涉及内容太多的话,如果代码走读讲解,文章会很臃肿,容易偏离主题。
  3. 有些内容单独讲解效果会更好,比如包括setDisplay()等方法,涉及到部分Android系统的渲染框架,那么补充一些渲染方面组件的知识,单独再写一篇内容,那样组织起来会更好。

所以写文章也好,干活也好,今后出现这种推进效率很低的情况的时候,我得及时思考一下,是否我当前的任务目标拆解的不够细致。出现这种情况时,我得将目标更进一步拆分,一点一点攻克。

4. 参考资料

  1. 《Android MultiMedia框架完全解析 - MediaPlayer的C/S架构与Binder机制实现》
  2. 《# Android MultiMedia框架完全解析 - setDataSource继续分析》
  3. 《Android音视频开发》:何俊林
相关推荐
阿巴斯甜9 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker9 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952710 小时前
Andorid Google 登录接入文档
android
黄林晴12 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android