Android MediaPlayer 源码分析(三) —— MediaPlayer设置视频图像控件

964 阅读4分钟

前言

上一篇我们分析了MediaPlayer设置数据源,以及与MediaPlayerService关联,那么在完成以上步骤之后,就需要MediaPlayer设置一个播放视频时的显示载体,简单来说就是设置视频图像控件。

设置视频图像控件

Android中提供了多种视频图像控件,SurfaceView、GLSurfaceView、TextureView等,关于视频图像控件与普通控件的区别,以及SurfaceView与TextureView的区别,这些都不是本文的讨论重点,我们只来具体分析这些控件的使用。

MediaPlayer + SurfaceView

    SurfaceView surfaceView = new SurfaceView(context);
    mediaPlayer.setDisplay(surfaceView.getHolder());

MediaPlayer + TextureView

    TextureView textureView = new TextureView(context);
    Surface surface = new Surface(textureView.getSurfaceTexture());
    mediaPlayer.setSurface(surface);

以上两种控件,分别使用到了setDisplay和setSurface方法,下面逐一进行分析:

// frameworks/base/media/java/android/media/MediaPlayer.java

    public void setDisplay(SurfaceHolder sh) {
        mSurfaceHolder = sh;
        Surface surface;
        if (sh != null) {
            //获取Surface对象
            surface = sh.getSurface();
        } else {
            surface = null;
        }
        //调用native方法 _setVideoSurface
        _setVideoSurface(surface);
        updateSurfaceScreenOn();
    }  
    
    public void setSurface(Surface surface) {
        if (mScreenOnWhilePlaying && surface != null) {
            Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
        }
        mSurfaceHolder = null;
        //调用native方法 _setVideoSurface
        _setVideoSurface(surface);
        updateSurfaceScreenOn();
    }
    
    ...
    
    private native void _setVideoSurface(Surface surface);

从上面源码可以看出,不管是setDisplay还是setSurface,都调用到了native方法 _setVideoSurface。

经过JNI动态注册方法,_setVideoSurface调用到android_media_MediaPlayer_setVideoSurface方法中,而android_media_MediaPlayer_setVideoSurface最终调用到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 */);
}

static void
setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
{
    //获取之前创建好的C++层的MediaPlayer对象
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        if (mediaPlayerMustBeAlive) {
            //抛出异常给Java层
            jniThrowException(env, "java/lang/IllegalStateException", NULL);
        }
        return;
    }
    
    //清除之前缓存的IGraphicBufferProducer对象
    decVideoSurfaceRef(env, thiz);

    sp<IGraphicBufferProducer> new_st;
    if (jsurface) {
        //通过Java层的Surface对象,获取对应的native层的Surface对象
        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
        if (surface != NULL) {
            //通过native层的Surface对象,获取新的IGraphicBufferProducer对象
            new_st = surface->getIGraphicBufferProducer();
            if (new_st == NULL) {
                //抛出异常给Java层
                jniThrowException(env, "java/lang/IllegalArgumentException",
                    "The surface does not have a binding SurfaceTexture!");
                return;
            }
            //缓存新的IGraphicBufferProducer对象new_st
            new_st->incStrong((void*)decVideoSurfaceRef);
        } else {
            jniThrowException(env, "java/lang/IllegalArgumentException",
                    "The surface has been released");
            return;
        }
    }
    
    //将new_st的指针赋值到Java层的MediaPlayer.java的mNativeSurfaceTexture
    env->SetLongField(thiz, fields.surface_texture, (jlong)new_st.get());

    //将缓冲的IGraphicBufferProducer传递给媒体播放器服务MediaPlayerService
    mp->setVideoSurfaceTexture(new_st);
}

我们来分析setVideoSurface方法中的逻辑步骤:

  1. 获取之前创建好的C++层的MediaPlayer对象。
  2. 清除之前缓存的IGraphicBufferProducer对象,这里使用到了Android系统中频繁使用的强指针计数器,这里其实就是减少该对象的指针计数。
  3. 调用android_view_Surface_getSurface方法,通过Java层的Surface对象,获取对应的native层的Surface对象。
  4. 通过native层的Surface对象,获取新的IGraphicBufferProducer对象,并缓存到强指针计数器中。
  5. 将new_st的指针赋值到Java层的MediaPlayer.java的mNativeSurfaceTexture。
  6. 将缓冲的IGraphicBufferProducer传递给媒体播放器服务。

从上面的步骤我们可以看到,不管是创建、获取还是缓存,几乎所有的逻辑都是围绕着IGraphicBufferProducer对象展开,那么这个IGraphicBufferProducer对象究竟代表什么呢?

图像渲染模型

IGraphicBufferProducer本质上是binder机制中的接口代理类,它对应的是Android图像系统的SurfaceFlinger中的一个BufferQueueLayer的Surface。简单来说,就是Android图像系统中一套生产者-消费者模型架构的生产者,具体对应关系如下:

  • IGraphicBufferProducer(生产者)—— Surface
  • IGraphicBufferConsumer(消费者)—— SurfaceFlinger
  • BufferQueue(buffer队列)

当我们最终将IGraphicBufferProducer对象,传递给MediaPlayerService后,MediaPlayerService中的解码器会通过IGraphicBufferProducer对象(Surface的接口代理类),将一帧一帧的视频原始数据,添加到BufferQueue队列中,SurfaceFlinger再从队列中取出每一帧数据,并最终渲染到视频图像控件对应的画面中。

所以,这里的最后一步,就是把IGraphicBufferProducer对象传递给媒体播放器服务MediaPlayerService。

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

status_t MediaPlayer::setVideoSurfaceTexture(
        const sp<IGraphicBufferProducer>& bufferProducer)
{
    ALOGV("setVideoSurfaceTexture");
    Mutex::Autolock _l(mLock);
    if (mPlayer == 0) return NO_INIT;
    //将缓冲的IGraphicBufferProducer传递给媒体播放器服务MediaPlayerService
    return mPlayer->setVideoSurfaceTexture(bufferProducer);
}

从上一篇文章,我们知道这个mPlayer对象,就是服务端播放器代理对象,也就是说,最终IGraphicBufferProducer对象被传递给了MediaPlayerService的播放器,并由播放器将每一帧视频数据通过IGraphicBufferProducer对象,传递给SurfaceFlinger,并最终渲染到屏幕上。

关于MediaPlayerService和播放器源码,我们会再开一个系列专门分析,这里就不再展开。

小结

当我们创建视频图像控件时,就是在向Android的图像系统申请一块渲染内存,在MediaPlayer中它的关联关系如下:

SurfaceView/TextureView —> Surface.java —> Surface.cpp —> IGraphicBufferProducer.h

当MediaPlayer调用setDisplay或setSurface来设置视频图像控件时,就是将对应的IGraphicBufferProducer对象传递到MediaPlayerService中,通过播放器的解码逻辑,最终将视频图像数据渲染到对应的播放控件上,完成视频的播放。