【四】Android MediaPlayer整体架构源码分析 -【设置视频显示surface】

承接上一章节分析:【三】Android MediaPlayer整体架构源码分析 -【设置数据源】【Part 1】
完整系列章节分析:【一】Android MediaPlayer整体架构源码分析 -【初始化和创建】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

setDisplay方法实现流程分析
设置视频显示surface示例:

// 先获取SurfaceHolder
mSurfaceView = findViewById(R.id.surface_view);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
        
mMediaPlayer.setDisplay(mSurfaceHolder);

【后续有时间再详细分析关于SurfaceHolder/Surface/SurfaceTexture/SurfaceView相关源码实现】
简单总结:获取SurfaceView(图像显示)对象,调用其getHolder()方法获取当前SurfaceView中的Surface(图像数据)对应的SurfaceHolder(图像数据管理),SurfaceHolder开始对Surface进行控制管理。
SurfaceTexture(Android 3.0【API 11】):可以获取视频解码后的图像流进行其他渲染处理,但可以不需要显示出来,等处理完成后再将其发送给SurfaceView去显示。

根据java层MediaPlayer提供的显示视频实现方法可知,提供了两个java方法,和同一个native方法,如下:
setDisplay() 实现:
根据英文注释可知,不调用这两个java方法或者设置为null,都会造成视频播放时只有音频流被track播放。

// [android.media.MediaPlayer.java]

    /**
     * Sets the {@link SurfaceHolder} to use for displaying the video
     * portion of the media.
     *
     * Either a surface holder or surface must be set if a display or video sink
     * is needed.  Not calling this method or {@link #setSurface(Surface)}
     * when playing back a video will result in only the audio track being played.
     * A null surface holder or surface will result in only the audio track being
     * played.
     *
     * @param sh the SurfaceHolder to use for video display
     * @throws IllegalStateException if the internal player engine has not been
     * initialized or has been released.
     */
    public void setDisplay(SurfaceHolder sh) {
    	// 缓存该参数并获取其内部控制的Surface图像数据对象
        mSurfaceHolder = sh;
        Surface surface;
        if (sh != null) {
            surface = sh.getSurface();
        } else {
            surface = null;
        }
        // 设置video surface,调用jni层实现
        // 见下面的分析
        _setVideoSurface(surface);
        // 更新Surface屏幕显示
        // 内部执行的是SurfaceHolder的方法,因此放入以后相关章节分析,暂不分析
        updateSurfaceScreenOn();
    }

setSurface() 实现:
该方法功能和setDisplay()相似,但该方法不支持{@link #setScreenOnWhilePlaying(boolean)}设置的属性功能。
设置新Surface会覆盖此前设置的Surface。设置null会造成视频播放时只有音频流被track播放。

// [android.media.MediaPlayer.java]

    /**
     * Sets the {@link Surface} to be used as the sink for the video portion of
     * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but
     * does not support {@link #setScreenOnWhilePlaying(boolean)}.  Setting a
     * Surface will un-set any Surface or SurfaceHolder that was previously set.
     * A null surface will result in only the audio track being played.
     *
     * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps
     * returned from {@link SurfaceTexture#getTimestamp()} will have an
     * unspecified zero point.  These timestamps cannot be directly compared
     * between different media sources, different instances of the same media
     * source, or multiple runs of the same program.  The timestamp is normally
     * monotonically increasing and is unaffected by time-of-day adjustments,
     * but it is reset when the position is set.
     *
     * @param surface The {@link Surface} to be used for the video portion of
     * the media.
     * @throws IllegalStateException if the internal player engine has not been
     * initialized or has been released.
     */
    public void setSurface(Surface surface) {
        if (mScreenOnWhilePlaying && surface != null) {
            Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
        }
        // 不需要直接使用SurfaceHolder进行管控Surface,由应用调用层自己控制
        mSurfaceHolder = null;
        // 设置video surface,调用jni层实现
        // 见下面的分析
        _setVideoSurface(surface);
        // 更新Surface屏幕显示
        updateSurfaceScreenOn();
    }

_setVideoSurface() 实现:
native方法,用于设置Surface后更新SurfaceTexture。

// [android.media.MediaPlayer.java]

    /*
     * Update the MediaPlayer SurfaceTexture.
     * Call after setting a new display surface.
     */
    private native void _setVideoSurface(Surface surface);

有前面章节的分析流程,可知对应于jni层android_media_MediaPlayer.cpp中的对应方法:
JNI方法映射,省略其他代码

// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static const JNINativeMethod gMethods[] = {
    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface}
}

android_media_MediaPlayer_setVideoSurface()实现分析:

// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static void
android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
{
    setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
}

// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static void
setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
{
    // 获取native层此前创建缓存的MediaPlayer对象
    // 备注:该实现不再分析,请参考前面已分析章节
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        if (mediaPlayerMustBeAlive) {
        	// 若为true,则jni层抛出异常给java层去try-catch
            jniThrowException(env, "java/lang/IllegalStateException", NULL);
        }
        return;
    }

    // 减少(即释放清除)此前已设置缓存的Surface(引用)
    // 见第1小节分析
    decVideoSurfaceRef(env, thiz);

    // 获取新的Surface中的图形数据缓冲生产者对象【实际上是BpBinder代理对象】
    sp<IGraphicBufferProducer> new_st;
    if (jsurface) {
    	// 不为空时
    	
    	// 获取对应于java层Surface对象的native层Surface对象
    	// 见第2小节分析
        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
        if (surface != NULL) {
        	// 不为空时,获取IGraphicBufferProducer对象【实际上是BpBinder代理对象】
        	// 关于IGraphicBufferProducer相关实现会在后续Surface系列章节分析。
            new_st = surface->getIGraphicBufferProducer();
            if (new_st == NULL) {
            	// 获取失败,则表明当前Surface没有绑定一个SurfaceTexture对象
                jniThrowException(env, "java/lang/IllegalArgumentException",
                    "The surface does not have a binding SurfaceTexture!");
                return;
            }
            // 增加该对象的强引用计数,并将引用id绑定为 decVideoSurfaceRef 方法指针值,
            // 正对应于上面释放清除处理流程。
            // 备注:此处为什么还要单独调用智能指针增加强引用计数的方法的原因,
            // 在前面第一章节的【2.3.3】小节里面有解释过了:
            // 简单说就是为了将IGraphicBufferProducer对象裸指针直接放入全局变量中去缓存,
            // 因此必须要增加引用计数,否则sp将会释放该对象指针。
            new_st->incStrong((void*)decVideoSurfaceRef);
        } else {
        	// native Surface对象指针已被释放
            jniThrowException(env, "java/lang/IllegalArgumentException",
                    "The surface has been released");
            return;
        }
    }

    // 缓存新的Surface中的图形数据缓冲生产者对象【实际上是BpBinder代理对象】
    env->SetLongField(thiz, fields.surface_texture, (jlong)new_st.get());

	// 设置IGraphicBufferProducer对象给native层MediaPlayer对象
	// 见第3小节分析
	// 注:若MediaPlayer还未初始化则会返回失败。即该方法必须在setDataSource()调用之后进行设置,
	// 并且在 prepare/prepareAsync 两个方法中将会处理这个设置Surface失败的case,即会自动重新设置一次。
    // This will fail if the media player has not been initialized yet. This
    // can be the case if setDisplay() on MediaPlayer.java has been called
    // before setDataSource(). The redundant call to setVideoSurfaceTexture()
    // in prepare/prepareAsync covers for this case.
    mp->setVideoSurfaceTexture(new_st);
}

1、decVideoSurfaceRef(env, thiz) 实现分析:
减少(即清除)此前已设置缓存的Surface(引用)

// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static void
decVideoSurfaceRef(JNIEnv *env, jobject thiz)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        return;
    }

    // 获取(上一个Surface)此前缓存的IGraphicBufferProducer即BpBinder代理对象
    // 见下面的分析
    sp<IGraphicBufferProducer> old_st = getVideoSurfaceTexture(env, thiz);
    if (old_st != NULL) {
    	// 不为空时,减少其智能指针引用计数,其实际上就是清除该缓存的旧数据
        old_st->decStrong((void*)decVideoSurfaceRef);
    }
}

getVideoSurfaceTexture() 实现分析:
获取(上一个Surface)此前缓存的IGraphicBufferProducer即BpBinder代理对象

// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static sp<IGraphicBufferProducer>
getVideoSurfaceTexture(JNIEnv* env, jobject thiz) {
	// IGraphicBufferProducer对象指针(变量值)缓存在全局变量的该字段【fields.surface_texture】中。
	// 该字段代表的是 关联Java层MediaPlayer的 mNativeSurfaceTexture 字段值,请见本系列文字第一章节分析
    IGraphicBufferProducer * const p = (IGraphicBufferProducer*)env->GetLongField(thiz, fields.surface_texture);
    return sp<IGraphicBufferProducer>(p);
}

2、android_view_Surface_getSurface(env, jsurface) 实现分析:
获取对应于java层Surface对象的native层Surface对象。关于Surface相关实现会在后续Surface系列章节分析。

// [frameworks/base/core/jni/android_view_Surface.cpp]
sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) {
    sp<Surface> sur;
    
    // 看到此处代码,其实根据jni实现机制,可知是获取全局变量Surface类信息的【mLock】锁对象,
    // 该锁对象实际上就是对应于java层Surface对象中的mLock锁变量,其赋值代码为:
    // jclass clazz = FindClassOrDie(env, "android/view/Surface");
    // gSurfaceClassInfo.mLock = GetFieldIDOrDie(env, gSurfaceClassInfo.clazz, "mLock", "Ljava/lang/Object;");
    // Surface相关的实现源码分析将后续相关章节
    jobject lock = env->GetObjectField(surfaceObj,
            gSurfaceClassInfo.mLock);
    if (env->MonitorEnter(lock) == JNI_OK) {
    	// 获取java层对象中的锁成功时
    	// 获取该java层Surface对象字段【mNativeObject】中缓存的native层Surface对象。
    	// 其字段声明:gSurfaceClassInfo.mNativeObject = GetFieldIDOrDie(env, 
    	// gSurfaceClassInfo.clazz, "mNativeObject", "J");
    	// 其字段赋值是在java层调用native层相关方法进行创建的native层Surface对象。
    	// 【有多种创建方式,此处列举一种】如java层Surface构造函数中调用的native方法:
    	// nativeCreateFromSurfaceTexture()将会new一个native层Surface对象,
    	// 并缓存该值在java层Surface的[mNativeObject]字段中。
    	// 因此此处将会获取到该缓存的native Surface对象
        sur = reinterpret_cast<Surface *>(
                env->GetLongField(surfaceObj, gSurfaceClassInfo.mNativeObject));
        // 释放锁
        env->MonitorExit(lock);
    }
    // 删除释放本地创建的锁对象
    env->DeleteLocalRef(lock);
    // 返回该缓存的native Surface对象
    return sur;
}

3、mp->setVideoSurfaceTexture(new_st)实现分析:

// [frameworks/av/media/libmedia/mediaplayer.cpp]

status_t MediaPlayer::setVideoSurfaceTexture(
        const sp<IGraphicBufferProducer>& bufferProducer)
{
    ALOGV("setVideoSurfaceTexture");
    // 加锁处理
    Mutex::Autolock _l(mLock);
    // 必须先执行setDataSource,否则mPlayer为空
    if (mPlayer == 0) return NO_INIT;
    // 见下面分析
    return mPlayer->setVideoSurfaceTexture(bufferProducer);
}

mPlayer->setVideoSurfaceTexture(bufferProducer) 实现分析:
由前面章节分析可知,mPlayer是MediaPlayerService::Client对象在客户端这边的Bp端代理对象。
调用Client代理对象即BpMediaPlayer代理类的该方法实现

// [frameworks/av/media/libmedia/IMediaPlayer.cpp]

    // pass the buffered IGraphicBufferProducer to the media player service
    status_t setVideoSurfaceTexture(const sp<IGraphicBufferProducer>& bufferProducer)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        sp<IBinder> b(IInterface::asBinder(bufferProducer));
        data.writeStrongBinder(b);
        // 发起【SET_VIDEO_SURFACETEXTURE】事务码的Binder请求,并携带参数,然后等待应答执行状态码
        remote()->transact(SET_VIDEO_SURFACETEXTURE, data, &reply);
        // Bn实现端执行完毕后将返回该状态码,此处读取出来
        return reply.readInt32();
    }

然后通过Binder机制请求服务端BnMediaPlayer的onTransact()方法对应事务码【SET_VIDEO_SURFACETEXTURE】处理流程实现:

// [frameworks/av/media/libmedia/IMediaPlayer.cpp]
status_t BnMediaPlayer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{// ... 省略其他代码
    switch (code) {
        case SET_VIDEO_SURFACETEXTURE: {
        	// 检查是否客户端即Bp代理端是否有权限调用该接口
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            // 获取参数
            sp<IGraphicBufferProducer> bufferProducer =
                    interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
            // 调用BnMediaPlayer实现者子类该方法【setVideoSurfaceTexture】实现即Client类的该方法实现,见下面的分析
            // 然后将该方法的返回状态码写入应答reply对象中,以供调用端读取该值
            reply->writeInt32(setVideoSurfaceTexture(bufferProducer));
            return NO_ERROR;
        } break;
    }
}

setVideoSurfaceTexture(bufferProducer) 实现分析:

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
status_t MediaPlayerService::Client::setVideoSurfaceTexture(
        const sp<IGraphicBufferProducer>& bufferProducer)
{
    // mConnId该值如前面章节分析可知,记录的是当前Client的连接ID(唯一ID)
    ALOGV("[%d] setVideoSurfaceTexture(%p)", mConnId, bufferProducer.get());
    // 根据前面的分析可知,此处获取的是此前创建缓存的NuPlayerDriver对象
    sp<MediaPlayerBase> p = getPlayer();
    // 若未创建则失败
    if (p == 0) return UNKNOWN_ERROR;

    // 将当前传入的IGraphicBufferProducer对象转换为其父类IBinder对象
    sp<IBinder> binder(IInterface::asBinder(bufferProducer));
    // 然后进行比较是否与当前Client已设置缓存的IGraphicBufferProducer对象相同,
    // 此处比较的是智能指针内部IGraphicBufferProducer对象指针值。
    if (mConnectedWindowBinder == binder) {
    	// 已设置过该对象,则直接返回成功
        return OK;
    }

    // 创建android native层window对象即子类Surface对象
    sp<ANativeWindow> anw;
    if (bufferProducer != NULL) {
    	// 创建Surface对象
        anw = new Surface(bufferProducer, true /* controlledByApp */);
        // native window连接新的window,并传入新window对象指针
        // 见3.1小节分析
        status_t err = nativeWindowConnect(anw.get(), "setVideoSurfaceTexture");

        if (err != OK) {
            ALOGE("setVideoSurfaceTexture failed: %d", err);
            // Note that we must do the reset before disconnecting from the ANW.
            // Otherwise queue/dequeue calls could be made on the disconnected
            // ANW, which may result in errors.
            // 警告:必须在断开连接window之前先reset()操作,否则会造成错误
            // reset() 处理流程放在后续【reset处理章节中统一分析】
            reset();

            Mutex::Autolock lock(mLock);
            // 加锁,连接新window错误时,执行断开连接native层window
            // 见3.2小节分析
            disconnectNativeWindow_l();

            return err;
        }
    }

    // 此处标注:必须在断开连接旧window之前,设置新window给下一层player,
    // 注意该值可以为空,即不显示,否则会造成错误
    // Note that we must set the player's new GraphicBufferProducer before
    // disconnecting the old one.  Otherwise queue/dequeue calls could be made
    // on the disconnected ANW, which may result in errors.
    // 由前面章节分析可知,p为NuPlayerDriver对象,调用其该方法
    // 见3.3小节分析
    status_t err = p->setVideoSurfaceTexture(bufferProducer);

	// 加锁,连接新window之后,执行断开连接native层window
    mLock.lock();
    disconnectNativeWindow_l();

    if (err == OK) {
    	// 连接新window成功时,缓存新window相关值,解除锁
        mConnectedWindow = anw;
        mConnectedWindowBinder = binder;
        mLock.unlock();
    } else {
    	// 失败时,解除锁
        mLock.unlock();
        // 断开新window连接,见上面已有分析
        status_t err = nativeWindowDisconnect(
                anw.get(), "disconnectNativeWindow");

        if (err != OK) {
            ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
                    strerror(-err), err);
        }
    }

    return err;
}

3.1、nativeWindowConnect(anw.get(), “setVideoSurfaceTexture”)实现分析:

// [frameworks/av/media/libstagefright/SurfaceUtils.cpp]
status_t nativeWindowConnect(ANativeWindow *surface, const char *reason) {
    ALOGD("connecting to surface %p, reason %s", surface, reason);

    // NATIVE_WINDOW_API_MEDIA 表示当前native window渲染的数据是通过stagefright经过解码模块得到的视频渲染数据,
    // 其是一个枚举,另外还有多种方式提供视频渲染数据,比如OpenGL ES、CPU、Camera
    // 见下面的分析
    status_t err = native_window_api_connect(surface, NATIVE_WINDOW_API_MEDIA);
    ALOGE_IF(err != OK, "Failed to connect to surface %p, err %d", surface, err);

    return err;
}

native_window_api_connect(surface, NATIVE_WINDOW_API_MEDIA)实现分析:

// [native/libs/nativewindow/include/system/window.h]
/*
 * native_window_api_connect(..., int api)
 * connects an API to this window. only one API can be connected at a time.
 * Returns -EINVAL if for some reason the window cannot be connected, which
 * can happen if it's connected to some other API.
 */
static inline int native_window_api_connect(
        struct ANativeWindow* window, int api)
{	
	// 调用自身方法,见下面的分析
    return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
}

window->perform() 实现分析:
由下面英文注释可知,该方法只是个方法指针,指向该类型参数和返回值即函数描述类型相同方法的一个指针,其是用于保持功能扩展时向后兼容。并且要求不要直接调用该方法,使用helper帮助方法即上面的static方法【native_window_api_connect】。并且该方法是其子类实现的即Surface实现【hook_perform()】。关于Surface相关实现源码分析请见后续相关系列章节。此处暂不展开分析了。

// [native/libs/nativewindow/include/system/window.h]
    /*
     * hook used to perform various operations on the surface.
     * (*perform)() is a generic mechanism to add functionality to
     * ANativeWindow while keeping backward binary compatibility.
     *
     * DO NOT CALL THIS HOOK DIRECTLY.  Instead, use the helper functions
     * defined below.
     *
     * (*perform)() returns -ENOENT if the 'what' parameter is not supported
     * by the surface's implementation.
     *
     * See above for a list of valid operations, such as
     * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
     */
    int     (*perform)(struct ANativeWindow* window,
                int operation, ... );

3.2、disconnectNativeWindow_l()实现分析:

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
void MediaPlayerService::Client::disconnectNativeWindow_l() {
	// 该值为缓存的本次新设置成功的native window对象【可能为空,也可能为旧window对象比如新设置失败时】
    if (mConnectedWindow != NULL) {
    	// 执行断开连接
    	// 见下面的分析
        status_t err = nativeWindowDisconnect(
                mConnectedWindow.get(), "disconnectNativeWindow");

        if (err != OK) {
            ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
                    strerror(-err), err);
        }
    }
    mConnectedWindow.clear();
}

nativeWindowDisconnect() 实现:

// [frameworks/av/media/libstagefright/SurfaceUtils.cpp]
status_t nativeWindowDisconnect(ANativeWindow *surface, const char *reason) {
    ALOGD("disconnecting from surface %p, reason %s", surface, reason);

    status_t err = native_window_api_disconnect(surface, NATIVE_WINDOW_API_MEDIA);
    ALOGE_IF(err != OK, "Failed to disconnect from surface %p, err %d", surface, err);

    return err;
}

native_window_api_disconnect实现分析:

// [native/libs/nativewindow/include/system/window.h]
/*
 * native_window_api_disconnect(..., int api)
 * disconnect the API from this window.
 * An error is returned if for instance the window wasn't connected in the
 * first place.
 */
static inline int native_window_api_disconnect(
        struct ANativeWindow* window, int api)
{	
	// 到此处我们可以知道,window断开连接和连接操作都是同一个方法,只是该参数【NATIVE_WINDOW_API_DISCONNECT】不同而已
    return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api);
}

关于Surface相关实现源码分析请见后续相关系列章节。此处暂不展开分析了。

3.3、p->setVideoSurfaceTexture(bufferProducer)实现分析:

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
status_t NuPlayerDriver::setVideoSurfaceTexture(
        const sp<IGraphicBufferProducer> &bufferProducer) {
    ALOGV("setVideoSurfaceTexture(%p)", this);
    Mutex::Autolock autoLock(mLock);

	// 判断上次是否正在设置Surface流程中,若是则返回无效错误码。
    if (mSetSurfaceInProgress) {
        return INVALID_OPERATION;
    }

    switch (mState) {
    	// 正在执行setDataSource流程中
        case STATE_SET_DATASOURCE_PENDING:
        // 正在执行reset流程中
        case STATE_RESET_IN_PROGRESS:
        	// 则返回无效
            return INVALID_OPERATION;

        default:
            break;
    }

    // 标记正在设置Surface流程中
    mSetSurfaceInProgress = true;

    // 异步执行调用NuPlayer该方法
    // 见下面的分析
    mPlayer->setVideoSurfaceTextureAsync(bufferProducer);

    // 此处为同步上面异步执行的结果处理,
    // 即需要等待下一层player处理结果结束通知后才唤醒此处
    while (mSetSurfaceInProgress) {
        mCondition.wait(mLock);
    }

    // 等待上面异步执行完毕后唤醒此处开始依次返回上层调用端
    return OK;
}

mPlayer->setVideoSurfaceTextureAsync(bufferProducer)实现分析:
AHandler/ALooper机制实现源码分析请参考我另一章节实现
Android native层媒体通信架构AHandler/ALooper机制实现源码分析

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::setVideoSurfaceTextureAsync(
        const sp<IGraphicBufferProducer> &bufferProducer) {
    // 发送【kWhatSetVideoSurface】事件消息给NuPlayer自身接收处理
    // AHandler/ALooper机制实现源码分析请参考我另一章节实现
    sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);

    // 可以设置为空,即不需要视频渲染
    if (bufferProducer == NULL) {
        msg->setObject("surface", NULL);
    } else {
    	// 不为空时,再次创建一个新Surface对象指针并作为"surface"字段的参数值
        msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));
    }

	// 发送该消息
    msg->post();
}

【kWhatSetVideoSurface】事件消息处理:

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatSetVideoSurface:
        {

            // 获取传入的Surface对象指针参数,并强转
            sp<RefBase> obj;
            CHECK(msg->findObject("surface", &obj));
            sp<Surface> surface = static_cast<Surface *>(obj.get());

            ALOGD("onSetVideoSurface(%p, %s video decoder)",
                    surface.get(),
                    (mSource != NULL && mStarted && mSource->getFormat(false /* audio */) != NULL
                            && mVideoDecoder != NULL) ? "have" : "no");

            // Need to check mStarted before calling mSource->getFormat because NuPlayer might
            // be in preparing state and it could take long time.
            // When mStarted is true, mSource must have been set.
            // 判断条件:数据源mSource为空 或 mStarted为false,或getFormat获取视频格式信息数据对象不存在,
            // 或视频解码器对象不为空且解码器设置Surface成功【该情况是视频已正在播放中时进行的设置】,则进入该条件
            if (mSource == NULL || !mStarted || mSource->getFormat(false /* audio */) == NULL
                    // NOTE: mVideoDecoder's mSurface is always non-null
                    || (mVideoDecoder != NULL && mVideoDecoder->setVideoSurface(surface) == OK)) {
                // 执行设置Surface
                // 见下面的分析
                performSetSurface(surface);
                break;
            }
			
			// 目前暂不分析下面的流程【后续流程会分析到】,下面的流程是上面条件都不满足时进入此处,
			// 即例如正在播放过程中,mVideoDecoder->setVideoSurface设置失败了则会进入下面的处理流程,
			// mVideoDecoder->setVideoSurface设置流程后续章节会分析到的

            mDeferredActions.push_back(
                    new FlushDecoderAction(
                            (obj != NULL ? FLUSH_CMD_FLUSH : FLUSH_CMD_NONE) /* audio */,
                                           FLUSH_CMD_SHUTDOWN /* video */));

            mDeferredActions.push_back(new SetSurfaceAction(surface));

            if (obj != NULL) {
                if (mStarted) {
                    // Issue a seek to refresh the video screen only if started otherwise
                    // the extractor may not yet be started and will assert.
                    // If the video decoder is not set (perhaps audio only in this case)
                    // do not perform a seek as it is not needed.
                    int64_t currentPositionUs = 0;
                    if (getCurrentPosition(&currentPositionUs) == OK) {
                        mDeferredActions.push_back(
                                new SeekAction(currentPositionUs,
                                        MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC /* mode */));
                    }
                }

                // If there is a new surface texture, instantiate decoders
                // again if possible.
                mDeferredActions.push_back(
                        new SimpleAction(&NuPlayer::performScanSources));

                // After a flush without shutdown, decoder is paused.
                // Don't resume it until source seek is done, otherwise it could
                // start pulling stale data too soon.
                mDeferredActions.push_back(
                        new ResumeDecoderAction(false /* needNotify */));
            }

            processDeferredActions();
            break;
        }
	}
}

performSetSurface(surface) 实现分析:

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::performSetSurface(const sp<Surface> &surface) {
    ALOGV("performSetSurface");

    // 缓存该值
    mSurface = surface;

    // 设置视频缩放模式:
    // 默认为[NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW]
    // 即视频帧buffer匹配native window大小的缩放即以window大小来缩放
    // 见下面的分析
    // XXX - ignore error from setVideoScalingMode for now
    setVideoScalingMode(mVideoScalingMode);

    if (mDriver != NULL) {
        sp<NuPlayerDriver> driver = mDriver.promote();
        // 若NuPlayerDriver没有释放掉,则调用该方法通知设置Surface流程完成
        // 见下面的分析
        if (driver != NULL) {
            driver->notifySetSurfaceComplete();
        }
    }

setVideoScalingMode(mVideoScalingMode)实现分析:

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
status_t NuPlayer::setVideoScalingMode(int32_t mode) {
	// 缓存该值
    mVideoScalingMode = mode;
    if (mSurface != NULL) {
    	// 其实有前面的分析,我们可以知道该调用肯定会执行Surface自身的该功能
    	// 见下面的分析
        status_t ret = native_window_set_scaling_mode(mSurface.get(), mVideoScalingMode);
        if (ret != OK) {
            ALOGE("Failed to set scaling mode (%d): %s",
                -ret, strerror(-ret));
            return ret;
        }
    }
    return OK;
}

native_window_set_scaling_mode实现分析:

// [native/libs/nativewindow/include/system/window.h]
/*
 * native_window_set_scaling_mode(..., int mode)
 * All buffers queued after this call will be associated with the scaling mode
 * specified.
 */
static inline int native_window_set_scaling_mode(
        struct ANativeWindow* window,
        int mode)
{
	// 由此可知,和前面已分析流程一样,只是传入的参数不同,该部分放入后续Surface系列章节分析
    return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE,
            mode);
}

driver->notifySetSurfaceComplete()实现分析:

void NuPlayerDriver::notifySetSurfaceComplete() {
    ALOGV("notifySetSurfaceComplete(%p)", this);
    Mutex::Autolock autoLock(mLock);

    // 设置该标志位为false
    CHECK(mSetSurfaceInProgress);
    mSetSurfaceInProgress = false;
	
	// 并唤醒上面setSurface流程方法在该条件变量上的wait事件
	// 备注:这里使用了broadcast来广播通知该条件变量上所有等待的线程,
	// 因为该条件变量可能在其它方法中也wait了
    mCondition.broadcast();
}

本章节内容分析结束,请查看下一章节

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值