承接上一章节分析:【三】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(¤tPositionUs) == 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();
}
本章节内容分析结束,请查看下一章节