前言
上一篇我们分析了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方法中的逻辑步骤:
- 获取之前创建好的C++层的MediaPlayer对象。
- 清除之前缓存的IGraphicBufferProducer对象,这里使用到了Android系统中频繁使用的强指针计数器,这里其实就是减少该对象的指针计数。
- 调用android_view_Surface_getSurface方法,通过Java层的Surface对象,获取对应的native层的Surface对象。
- 通过native层的Surface对象,获取新的IGraphicBufferProducer对象,并缓存到强指针计数器中。
- 将new_st的指针赋值到Java层的MediaPlayer.java的mNativeSurfaceTexture。
- 将缓冲的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中,通过播放器的解码逻辑,最终将视频图像数据渲染到对应的播放控件上,完成视频的播放。