前言
接着《Media FrameWork简介》,我们在本篇将迎来具体的MediaPlayer的讲解。我们知道,一个最简单的播放器有播放、暂停、停止三个功能。那么,本篇将结合google官网给出的MediaPlayer状态图以及mediaplayer提供的基本接口,讲清楚这三个基本功能。
1. MediaPlayer概述
在了解一个新事物的时候,我个人习惯首先了解它的用法(它是用来干什么的)。在此基础上,再去了解它的整体结构(它是由什么东东组成的)。最后,结合这两者,详细分析,以求充分理解这个事物。
好,那么各位看官也明白了,我将先介绍MediaPlayer的用法,再介绍MediaPlayer的架构,最后结合两者,讲MediaPlayer是怎么播放起来的。
2. MediaPlayer的使用
刚刚开始的时候,我确实想直接给出MediaPlayer最简单用法的示例。比如:
// 初始化播放器
private void initMediaplayer() {
if (mMediaPlayer != null) {
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
}
mMediaPlayer = new MediaPlayer();
}
// 销毁音乐
private void destoryMusic() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
}
// 暂停播放
private void pauseMusic() {
if (mMediaPlayer.isPlaying()) {// 正在播放
mMediaPlayer.pause();// 暂停
} else {// 没有播放
mMediaPlayer.start();
}
}
// 停止播放
private void stopMusic() {
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
}
// 播放
private void playMusic() {
try {
/* 重置多媒体 */
mMediaPlayer.reset();
/* 读取媒体文件 */
mMediaPlayer.setDataSource(mFileName);
/* 准备播放 */
mMediaPlayer.prepare();
/* 开始播放 */
mMediaPlayer.start();
/* 是否单曲循环 */
mMediaPlayer.setLooping(false);
} catch (IOException e) {
e.printStackTrace();
}
}
但是,在某个漆黑的角落里,我突然发现了一张MediaPlayer状态机的图,如图1所示(Android Developers官网制作),简直如获至宝。特在此贴出来,作为讲解如何使用MediaPlayer的图示 ^ _ ^
2.1 初始化播放器
if (mMediaPlayer != null){
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
}
mMediaPlayer = new MediaPlayer();
- 如果有MediaPlayer的实例。那么,先reset -> Idle状态;再release -> End状态,以确保再次播放时,MediaPlayer处于正确的状态。
- 如果没有创建过MediaPlayer的实例,new 一个MediaPlayer实例。
2.2 播放
/* 重置多媒体 */
mMediaPlayer.reset();
/* 读取mp3文件 */
mMediaPlayer.setDataSource(mFileName);
/* 准备播放 */
mMediaPlayer.prepare();
/* 开始播放 */
mMediaPlayer.start();
/* 是否单曲循环 */
mMediaPlayer.setLooping(false);
- reset -> Idle状态;
- setDataSource("文件路径(本地或者网络)") -> Initialized状态;
- prepare -> Prepared状态;
- start -> Started状态;
- setLooping(false) && onCompletion invoked on OnCompletionListener -> PlaybackCompleted状态;
其他四个状态都一目了然。第五个状态是在1.没有设置单曲循环;2.播放完成状态通知,这两个条件下,才会到达PlaybackCompleted状态的。可以看到,如果调用strat,则又回到Started状态,并重头开始播放。
2.3 暂停
if (mMediaPlayer.isPlaying()) {// 正在播放
mMediaPlayer.pause();// 暂停
} else {// 没有播放
mMediaPlayer.start();
}
- 如果播放器正在播放,那么调用pause -> Paused状态
- 如果播放器此时正好在Paused状态,那么调用start -> Started状态
2.4 停止
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
如果有MediaPlayer播放的实例且播放状态是isPlaying,调用stop -> Stopped状态。
2.5 销毁
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
如果有MediaPlayer的实例,那么先stop -> Stopped状态; 然后release -> End状态。
最后小结下,当我们要使用MediaPlayer的时候,一般情况下要经过以下步骤
reset
:重置Mediaplayer
各种参数,进入Idle
状态;setDataSource
:带入文件的地址(有可能本地、有可能网络),获得Nuplayer的实例,进入Initialized
状态;prepare
:获取播放文件的各种信息(播放时长、比特率、编码信息等);start
:根据prepare
过程获得的信息,初始化解码器,初始化渲染器,操控输入、输出Buffer
进出解码器等,完成播放工作。
当然,还有stop
、reset
、pause
、release
等,相对于启动播放过程不是特别重要,留在以后再来补充,今后的篇章中,将着重讲2——3——4步骤。
3. MeidaPlayer三部曲
在讲三部曲之前,首先得获得MediaPlayer
的实例。让我们来看看frameworks/base/media/java/android/media/MediaPlayer.java
的部分代码吧。
首先是在静态代码段加载
media_jni
动态库(libmedia_jni.so
),并且调用本地方法native_init()
;
594 static {
595 System.loadLibrary("media_jni");
596 native_init();
597 }
来看一下native_init
做了什么事情(路径frameworks/base/media/jni/android_media_MediaPlayer.cpp
)。代码如下:
816// This function gets some field IDs, which in turn causes class initialization.
817// It is called from a static block in MediaPlayer, which won't run until the
818// first time an instance of this class is used.
819static void
820android_media_MediaPlayer_native_init(JNIEnv *env)
821{
822 jclass clazz;
823
824 clazz = env->FindClass("android/media/MediaPlayer");
825 if (clazz == NULL) {
826 return;
827 }
828
829 fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
830 if (fields.context == NULL) {
831 return;
832 }
833
834 fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
835 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
836 if (fields.post_event == NULL) {
837 return;
838 }
839
840 fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
841 if (fields.surface_texture == NULL) {
842 return;
843 }
844
845 env->DeleteLocalRef(clazz);
846
847 clazz = env->FindClass("android/net/ProxyInfo");
848 if (clazz == NULL) {
849 return;
850 }
851
852 fields.proxyConfigGetHost =
853 env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
854
855 fields.proxyConfigGetPort =
856 env->GetMethodID(clazz, "getPort", "()I");
857
858 fields.proxyConfigGetExclusionList =
859 env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
860
861 env->DeleteLocalRef(clazz);
862
863 gPlaybackParamsFields.init(env);
864 gSyncParamsFields.init(env);
865}
简单描述一下。
- 得到
MediaPlayer
对象。根据此对象拿到mNativeContext
、postEventFromNative
、mNativeSurfaceTexture
两个字段的ID,拿到方法postEventFromNative
的ID,之后销毁;- 得到
ProxyInfo
对象。根据此对象拿到getHost
、getPort
、getExclusionListAsString
三个方法的ID,之后销毁;- 初始化
Playback
的一些属性字段参数,初始化初始化Sync
的一些属性字段参数;
好,现在初始化完成了,各就各位了,我们开始创建MediaPlayer
吧。“小二!上Code!”
在上层的MediaPlayer.Java
的MediaPlayer
的构造函数中,有一个非常关键的点native_setup
。
867 static void
868 android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
869{
870 ALOGV("native_setup");
871 sp<MediaPlayer> mp = new MediaPlayer();
872 if (mp == NULL) {
873 jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
874 return;
875 }
876
877 // create new listener and give it to MediaPlayer
878 sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
879 mp->setListener(listener);
880
881 // Stow our new C++ MediaPlayer in an opaque field in the Java object.
882 setMediaPlayer(env, thiz, mp);
883}
- 首先是
new
了一个MediaPlayer
的实例,如果没有创建成功,抛出Out of memory
的异常,并返回;- 创建一个监听对象,并将此监听对象的注册到刚刚获得的
MediaPlayer
的实例中;- 将创建的
MediaPlayer
的对象的装在一个jobject
中(以便上层使用)。
好,至此我们获得到了native
层的MediaPlayer
的对象的实例了。接下来依照顺序java
->Jni
->native
看看三部曲。
3.1 setDataSource (以本地文件为例)
3.1.1 MediaPlayer.java
我还是从java这边开始看起。代码如下:
1090 private void setDataSource(String path, String[] keys, String[] values)
1091 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
1092 final Uri uri = Uri.parse(path);
1093 final String scheme = uri.getScheme();
1094 if ("file".equals(scheme)) {
1095 path = uri.getPath();
1096 } else if (scheme != null) {
1097 // handle non-file sources
1098 nativeSetDataSource(
1099 MediaHTTPService.createHttpServiceBinderIfNecessary(path),
1100 path,
1101 keys,
1102 values);
1103 return;
1104 }
1105
1106 final File file = new File(path);
1107 if (file.exists()) {
1108 FileInputStream is = new FileInputStream(file);
1109 FileDescriptor fd = is.getFD();
1110 setDataSource(fd);
1111 is.close();
1112 } else {
1113 throw new IOException("setDataSource failed.");
1114 }
1115 }
让我们简要分析下,在java层做了哪些事情吧。
- 根据传入的
path
,区分是本地文件还是网络播放文件;- 如果是网络文件的话,走
nativeSetDataSource
路线到Jni层;- 如果是本地文件的话,获取文件描述符,走
_setDataSource
路线到Jni层;
3.1.2 android_media_MediaPlayer.cpp
250static void
251android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
252{
253 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
254 if (mp == NULL ) {
255 jniThrowException(env, "java/lang/IllegalStateException", NULL);
256 return;
257 }
258
259 if (fileDescriptor == NULL) {
260 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
261 return;
262 }
263 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
264 ALOGV("setDataSourceFD: fd %d", fd);
265 process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
266}
- get到我们在
native_setup
中set的MediaPlayer
对象,若没有获取到,则抛出异常;- 拿到文件描述符
FD
;- 调用
process_media_player_call
,其中有个重要的参数是mp->setDataSource(fd, offset, length)
的返回值。
我们先来看看process_media_player_call
。
172static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
173{
174 if (exception == NULL) { // Don't throw exception. Instead, send an event.
175 if (opStatus != (status_t) OK) {
176 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
177 if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
178 }
179 } else { // Throw exception!
180 if ( opStatus == (status_t) INVALID_OPERATION ) {
181 jniThrowException(env, "java/lang/IllegalStateException", NULL);
182 } else if ( opStatus == (status_t) BAD_VALUE ) {
183 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
184 } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
185 jniThrowException(env, "java/lang/SecurityException", NULL);
186 } else if ( opStatus != (status_t) OK ) {
187 if (strlen(message) > 230) {
188 // if the message is too long, don't bother displaying the status code
189 jniThrowException( env, exception, message);
190 } else {
191 char msg[256];
192 // append the status code to the message
193 sprintf(msg, "%s: status=0x%X", message, opStatus);
194 jniThrowException( env, exception, msg);
195 }
196 }
197 }
198}
- 如果传入的
exception
为空(显然我们不走这个分支);
并且在mediaPlayer.cpp
中的setDataSource
失败了,拿到之前在native_setup
中创建的MediaPlayer
;
并且如果可以拿到的话,向java层通知MEDIA_ERROR
;- 正常情况下,我们一般走的是有传入
exception
的分支;
在此分支中,我们需要根据在MediaPlayer.cpp
中setDataSource
返回的status
状态来抛出对应的异常;
一般情况下,如果我们获取到的status == OK
的话,在这里是不做任何事情的。
3.1.3 MediaPlayer.cpp
229 status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
230 {
231 ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
232 status_t err = UNKNOWN_ERROR;
233 const sp<IMediaPlayerService> service(getMediaPlayerService());
234 if (service != 0) {
235 sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
236 if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
237 (NO_ERROR != player->setDataSource(fd, offset, length))) {
238 player.clear();
239 }
240 err = attachNewPlayer(player);
241 }
242 return err;
243 }
- 获取了名为“media.player”的binder服务(BpBinder),然后通过
interface_cast
拿到IMediaPlayerService
;- 调用
IMediaPlayerService
的BpMediaPlayerService::create
方法,通过remote->transact()
远程调用服务端,并用interface_cast
模版拿到IMediaPlayer
- 然后走到
IMediaPlayer
的类BpMediaPlayer::setDataSource
方法,通过remote->transact()
远程调用服务端,并返回服务端setDataSource
方法返回的状态;
好,流程到这里,相信有些童鞋已经一头雾水了(要了解的透彻,智能指针/JNI/Binder知识缺一不可,如不清楚,还请问问度娘,我以后有时间也会写的)。我们现在只要看看关键的地方就行了,毕竟我们的目的是了解MediaPlayer的框架嘛。
3.1.3.1 关键点1
MediaPlayer::setDataSource
中getMediaPlayerService()
,代码如下:
35 IMediaDeathNotifier::getMediaPlayerService()
36 {
37 ALOGV("getMediaPlayerService");
38 Mutex::Autolock _l(sServiceLock);
39 if (sMediaPlayerService == 0) {
40 sp<IServiceManager> sm = defaultServiceManager();
41 sp<IBinder> binder;
42 do {
43 binder = sm->getService(String16("media.player"));
44 if (binder != 0) {
45 break;
46 }
47 ALOGW("Media player service not published, waiting...");
48 usleep(500000); // 0.5 s
49 } while (true);
50
51 if (sDeathNotifier == NULL) {
52 sDeathNotifier = new DeathNotifier();
53 }
54 binder->linkToDeath(sDeathNotifier);
55 sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
56 }
57 ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
58 return sMediaPlayerService;
59 }
binder = sm->getService(String16("media.player"))
拿到名为“media.player”的bindersMediaPlayerService = interface_cast<IMediaPlayerService>(binder)
拿到IMediaPlayerService
(代理端)
3.1.3.2 关键点2
在frameworks/base/media/mediaserver/(多媒体服务的守护进程)其中只有一个源文件main_mediaserver.cpp
int main(int argc __unused, char **argv __unused)
{
signal(SIGPIPE, SIG_IGN);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm(defaultServiceManager());
ALOGI("ServiceManager: %p", sm.get());
InitializeIcuOrDie();
MediaPlayerService::instantiate();
ResourceManagerService::instantiate();
registerExtensions();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
- 这个函数就是启动了多媒体服务/相机服务/音频服务
- 重点在于
MediaPlayerService::instantiate()
;
我们来看看: /frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
中
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
- 这里看得很明显啦, 把名为"media.player"服务注册到
Service Manager
中, 这样我们在关键点1中就可以拿到这个服务啦.
3.1.3.3 关键点3
接下来看代理端调用的service->create(this, mAudioSessionId)
在本地端MediaPlayerService::creat
是如何实现的. 代码如下:
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId)
{
pid_t pid = IPCThreadState::self()->getCallingPid();
int32_t connId = android_atomic_inc(&mNextConnId);
sp<Client> c = new Client(
this, pid, connId, client, audioSessionId,
IPCThreadState::self()->getCallingUid());
ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
IPCThreadState::self()->getCallingUid());
wp<Client> w = c;
{
Mutex::Autolock lock(mLock);
mClients.add(w);
}
return c;
}
关键点在于:
- 拿到了
MediaPlayerService::Client
类的指针☆☆☆;- 加入到了
SortedVector
中
3.1.4 MediaPlayerService.cpp
真正本地端做事的setDataSource来了MediaPlayerService::Client::setDataSource
:
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);
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 playerType = MediaPlayerFactory::getPlayerType(this,
fd,
offset,
length);
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
// now set data source
setDataSource_post(p, p->setDataSource(fd, offset, length));
return mStatus;
}
简单来说, 在这个函数中做了以下几件事情:
- 从
MediaPlayerFactory::getPlayerType
中拿到对应的playerType
(当前7.0版本都是穿件的Nuplayer(这个后面会讲到));- 根据
playerType
, 通过setDataSource_pre
函数拿到MediaPlayerBase
;- 通过
setDataSource_post
函数传入MediaPlayerBase
对象, 继续完成setDataSource
的工作;
3.1.4.1 MediaPlayerFactory.cpp
现在来看看MediaPlayerFactory::getPlayerType
方法:
109 player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
110 int fd,
111 int64_t offset,
112 int64_t length) {
113 GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
114 }
80#define GET_PLAYER_TYPE_IMPL(a...) \
81 Mutex::Autolock lock_(&sLock); \
82 \
83 player_type ret = STAGEFRIGHT_PLAYER; \
84 float bestScore = 0.0; \
85 \
86 for (size_t i = 0; i < sFactoryMap.size(); ++i) { \
87 \
88 IFactory* v = sFactoryMap.valueAt(i); \
89 float thisScore; \
90 CHECK(v != NULL); \
91 thisScore = v->scoreFactory(a, bestScore); \
92 if (thisScore > bestScore) { \
93 ret = sFactoryMap.keyAt(i); \
94 bestScore = thisScore; \
95 } \
96 } \
97 \
98 if (0.0 == bestScore) { \
99 ret = getDefaultPlayerType(); \
100 } \
101 \
102 return ret;
- 从
sFactoryMap
中拿到事先在MediaPlayerService
的构造函数中注册好的mediaplayerFactory
(有NuPlayer
和TestPlayer
(此Player一般情况下只在userdebug和eng版本中猜有可能走到));- 根据参数对每个
mediaplayerFactory
打分, 获取到最高打分和对应的mediaplayerFactory
(绝大多数情况下都是NuPlayerFactory
);- 如果到最后
bestScore
还是0
, 那么使用默认的mediaplayerFactory
(NuplayerFactory).
3.1.4.2 setDataSource_pre
好,我们现在拿到了playerType, 可以进行setDataSource_pre了. 代码如下:
674sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
675 player_type playerType)
676{
677 ALOGV("player type = %d", playerType);
678
679 // create the right type of player
680 sp<MediaPlayerBase> p = createPlayer(playerType);
681 if (p == NULL) {
682 return p;
683 }
684
685 sp<IServiceManager> sm = defaultServiceManager();
686 sp<IBinder> binder = sm->getService(String16("media.extractor"));
687 mExtractorDeathListener = new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH);
688 binder->linkToDeath(mExtractorDeathListener);
689
690 binder = sm->getService(String16("media.codec"));
691 mCodecDeathListener = new ServiceDeathNotifier(binder, p, MEDIACODEC_PROCESS_DEATH);
692 binder->linkToDeath(mCodecDeathListener);
693
694 if (!p->hardwareOutput()) {
695 Mutex::Autolock l(mLock);
696 mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
697 mPid, mAudioAttributes);
698 static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
699 }
700
701 return p;
702}
- 根据传入的
playerType
, 按照MediaPlayerService::Client::creatPlayer
->MediaPlayerFactory::createPlayer
->NuplayerFactory::createPlayer
的顺序, 拿到NuPlayerDriver;- 拿到名为"media.extractor"的binder代理, 可与
MediaExtractor
那边打交道了;- 拿到名为"media.codec"的binder代理, 可与
MediaCodec
那边打交道了(IOMX接口);- 最后拿到
audioOutput
并返回指向NuplayerDriver
的指针.
3.1.4.3 setDataSource_post
最后, 我们要进行setDataSource_post了. 代码如下:
704 void MediaPlayerService::Client::setDataSource_post(
705 const sp<MediaPlayerBase>& p,
706 status_t status)
707 {
708 ALOGV(" setDataSource");
709 mStatus = status;
710 if (mStatus != OK) {
711 ALOGE(" error: %d", mStatus);
712 return;
713 }
714
715 // Set the re-transmission endpoint if one was chosen.
716 if (mRetransmitEndpointValid) {
717 mStatus = p->setRetransmitEndpoint(&mRetransmitEndpoint);
718 if (mStatus != NO_ERROR) {
719 ALOGE("setRetransmitEndpoint error: %d", mStatus);
720 }
721 }
722
723 if (mStatus == OK) {
724 mPlayer = p;
725 }
726 }
- 首先判断Nuplayer那边的的
setDataSource
完成的状态(关于这一块, 我将在下一篇具体说明), 如果状态正确, 往下走;- 在构造函数中,
mRetransmitEndpointValid = false
. 如果上层没有调用setRetransmitEndpoint
方法的话, 是不会走这个Flow流程的;- 最后将
NuplayerDriver
放入MediaPlayerService::Client
类的mPlayer
字段中, 以便后面调用;
3.1.5 NuplayerDriver.cpp
接下来, 我们来看看NuplayerDriver这边的setDataSource干了些啥?代码如下:
100 status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
101 ALOGV("setDataSource(%p) file(%d)", this, fd);
102 Mutex::Autolock autoLock(mLock);
103
104 if (mState != STATE_IDLE) {
105 return INVALID_OPERATION;
106 }
107
108 mState = STATE_SET_DATASOURCE_PENDING;
109
110 mPlayer->setDataSourceAsync(fd, offset, length);
111
112 while (mState == STATE_SET_DATASOURCE_PENDING) {
113 mCondition.wait(mLock);
114 }
115
116 return mAsyncResult;
117 }
mState = STATE_SET_DATASOURCE_PENDING
, 改变当前Nuplayer的状态(在构造函数中 :mState = STATE_IDLE
)- 调用
Nuplayer
的setDataSourceAsync
异步方法 (这个方法在NuPlayer.cpp
中, 做完后会notifyNuplayerDriver
, 并改变mState
状态);- 返回
mAsyncResult
;
3.1.6 Nuplayer.cpp
终于, 我们进军到了Nuplayer中, 发现了之前讲过的AMessage了, 让我们一起来看看Code吧 :
279 void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
280 sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
281
282 sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
283
284 sp<GenericSource> source =
285 new GenericSource(notify, mUIDValid, mUID);
286
287 status_t err = source->setDataSource(fd, offset, length);
288
289 if (err != OK) {
290 ALOGE("Failed to set data source!");
291 source = NULL;
292 }
293
294 msg->setObject("source", source);
295 msg->post();
296 }
new
了两个AMessage
, 一个msg
给自己的; 一个notify
给上面的NuplayerDriver
;- 调用
source
(GenericSource
)的setDataSource
方法;- 抛出
msg
, 在本类的onMessageReceive
中获取处理;
3.1.6.1 GenericSource构造函数
接下来, 看看new GenericSource
的时候所调用的构造函数. Code如下:
49 NuPlayer::GenericSource::GenericSource(
50 const sp<AMessage> ¬ify,
51 bool uidValid,
52 uid_t uid)
53 : Source(notify),
54 mAudioTimeUs(0),
55 mAudioLastDequeueTimeUs(0),
56 mVideoTimeUs(0),
57 mVideoLastDequeueTimeUs(0),
58 mFetchSubtitleDataGeneration(0),
59 mFetchTimedTextDataGeneration(0),
60 mDurationUs(-1ll),
61 mAudioIsVorbis(false),
62 mIsWidevine(false),
63 mIsSecure(false),
64 mIsStreaming(false),
65 mUIDValid(uidValid),
66 mUID(uid),
67 mFd(-1),
68 mDrmManagerClient(NULL),
69 mBitrate(-1ll),
70 mPendingReadBufferTypes(0) {
71 mBufferingMonitor = new BufferingMonitor(notify);
72 resetDataSource();
73 DataSource::RegisterDefaultSniffers();
74 }
- 在
Nuplayer::Source
这个AHandle中, 拿到Nuplayer
传递下来的AMessage :notify
;- 各种各样的, 与audio/video播放相关的参数相关的字段初始化;
mBufferingMonitor
字段拿到BufferingMonitor
(用来监视buffer
的状态);- 调用
resetDataSource
重置DataSource
相关的字段参数(一般是对一些字段进行置0
和NULL
的工作)RegisterDefaultSniffers
(在Android 8.0 code中,RegisterDefaultSniffers()
这个方法是在GenericSource::prepare
中抛出消息, 并且在接收方法onPrepareAsync
中的initFromDataSource
中的MediaExteactor::Creat
的CreateFromService
中调用的).
这个方法的作用是将各类Extractor的sniff
的函数指针放入到一个名为gSniffers
的List
中(在setDataSource
流程之后, 会依次遍历这些注册其中的sniff
函数进行打分来判断一个文件应该由那种Extractor
来解封装).
3.1.6.2 GenericSource::setDataSource
119 status_t NuPlayer::GenericSource::setDataSource(
120 int fd, int64_t offset, int64_t length) {
121 resetDataSource();
122
123 mFd = dup(fd);
124 mOffset = offset;
125 mLength = length;
126
127 // delay data source creation to prepareAsync() to avoid blocking
128 // the calling thread in setDataSource for any significant time.
129 return OK;
130 }
- 调用
resetDataSource()
做清理工作;- 获取①
mFd
文件描述符 ②offset
文件偏移量 ③length
文件长度;- 返回OK;
3.1.6.3 Nuplayer::onMessageReceive
在msg->post后, 回到Nuplayer
中的onMessageReceive
的Code如下:
483 case kWhatSetDataSource:
484 {
485 ALOGV("kWhatSetDataSource");
486
487 CHECK(mSource == NULL);
488
489 status_t err = OK;
490 sp<RefBase> obj;
491 CHECK(msg->findObject("source", &obj));
492 if (obj != NULL) {
493 Mutex::Autolock autoLock(mSourceLock);
494 mSource = static_cast<Source *>(obj.get());
495 } else {
496 err = UNKNOWN_ERROR;
497 }
498
499 CHECK(mDriver != NULL);
500 sp<NuPlayerDriver> driver = mDriver.promote();
501 if (driver != NULL) {
502 driver->notifySetDataSourceCompleted(err);
503 }
504 break;
505 }
- 做一系列的Check工作;
- 将
GenericSource
的指针转换为其父类的指针NuPlayer::Source
并赋值给mSource
;- 调用
NuPlayerDriver
的notifySetDataSourceCompleted
方法;
最后回到NuplayerDriver
的notifySetDataSourceCompleted
中, Code如下:
766 void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
767 Mutex::Autolock autoLock(mLock);
768
769 CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
770
771 mAsyncResult = err;
772 mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
773 mCondition.broadcast();
774 }
- check当前的装备是否为
STATE_SET_DATASOURCE_PENDING
;mAsyncResult = err
(这里值为OK
);mState
被赋值为STATE_UNPREPARED
;mCondition
广播下释放拿到的锁(意味着异步的setDataSource
已完成);
3.1.7 小结
至此, 我们完成了setDataSource
的工作. 具体的:
- 获得了
StageFright
平台之上的Player :NuPlayer
;- 获得了与
MediaExtractor
通信的权利;- 获得了与
MediaCodec
通信的权利;- 获得了Source :
GenericSource(本地文件)
;- 将各种Extractor注册(在
8.0
中, 移到了prepare
中进行);
3.2 prepare
在完成setDataSource后, 我们需要进行PrePare了. 按照惯例, 在本篇中我们还是从Java这一端开始吧. 时序图如下所示:
3.2.1 MediaPlayer.java
1183 public void prepare() throws IOException, IllegalStateException {
1184 _prepare();
1185 scanInternalSubtitleTracks();
1186 }
1187
1188 private native void _prepare() throws IOException, IllegalStateException;
- 调用JNI层的_prepare;
- 获取subtitle的默认track;
3.2.2 android_media_MediaPlayer.cpp
来看JNI层的具体code如下:
346 static void
347 android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
348 {
349 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
350 if (mp == NULL ) {
351 jniThrowException(env, "java/lang/IllegalStateException", NULL);
352 return;
353 }
354
355 // Handle the case where the display surface was set before the mp was
356 // initialized. We try again to make it stick.
357 sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
358 mp->setVideoSurfaceTexture(st);
359
360 process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
361 }
- 拿到指向MediaPlayer的强指针;
- 重新尝试与Display Surface的链接;
- 执行mp->prepare(), 通过返回值mPrepareStatus来尝试捕获异常;
3.2.3 mediaplayer.cpp
263 status_t MediaPlayer::prepare()
264 {
265 ALOGV("prepare");
266 Mutex::Autolock _l(mLock);
267 mLockThreadId = getThreadId();
268 if (mPrepareSync) {
269 mLockThreadId = 0;
270 return -EALREADY;
271 }
272 mPrepareSync = true;
273 status_t ret = prepareAsync_l();
274 if (ret != NO_ERROR) {
275 mLockThreadId = 0;
276 return ret;
277 }
278
279 if (mPrepareSync) {
280 mSignal.wait(mLock); // wait for prepare done
281 mPrepareSync = false;
282 }
283 ALOGV("prepare complete - status=%d", mPrepareStatus);
284 mLockThreadId = 0;
285 return mPrepareStatus;
286 }
- 调用prepareAsync_l()方法使用异步的prepare;
- mSignal.wait(mLock)等待拿到锁;
- 返回mPrepareStatus;
所以, 这里我们虽然调用了prepareAsync_l()
方法去异步完成prepare操作. 但在之后的Flow中, 还是在等待拿到锁, 所以本处讲解的prepare依然是同步操作.
※※※ 接下来看看prepareAsync_l()
※※※
244status_t MediaPlayer::prepareAsync_l()
245{
246 if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
247 if (mAudioAttributesParcel != NULL) {
248 mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
249 } else {
250 mPlayer->setAudioStreamType(mStreamType);
251 }
252 mCurrentState = MEDIA_PLAYER_PREPARING;
253 return mPlayer->prepareAsync();
254 }
255 ALOGE("prepareAsync called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());
256 return INVALID_OPERATION;
257}
- mPlayer->setAudioStreamType(mStreamType)设置player的Audio的Type为: "AUDIO_STREAM_MUSIC";
- 进入MEDIA_PLAYER_PREPARING状态;
- 调用MediaPlayerService::Client::prepareAsync()方法;
3.2.4 MediaPlayerService.cpp
967 status_t MediaPlayerService::Client::prepareAsync()
968 {
969 ALOGV("[%d] prepareAsync", mConnId);
970 sp<MediaPlayerBase> p = getPlayer();
971 if (p == 0) return UNKNOWN_ERROR;
972 status_t ret = p->prepareAsync();
973 #if CALLBACK_ANTAGONIZER
974 ALOGD("start Antagonizer");
975 if (ret == NO_ERROR) mAntagonizer->start();
976 #endif
977 return ret;
978 }
- 获得指向NuplayerDriver的指针p;
- 获取NuplayerDriver::prepareAsync的返回值ret, 并在最后返回;
3.2.5 NuplayerDriver.cpp
221 status_t NuPlayerDriver::prepareAsync() {
222 ALOGV("prepareAsync(%p)", this);
223 Mutex::Autolock autoLock(mLock);
224
225 switch (mState) {
226 case STATE_UNPREPARED:
227 mState = STATE_PREPARING;
228 mIsAsyncPrepare = true;
229 mPlayer->prepareAsync();
230 return OK;
231 case STATE_STOPPED:
232 // this is really just paused. handle as seek to start
233 mAtEOS = false;
234 mState = STATE_STOPPED_AND_PREPARING;
235 mIsAsyncPrepare = true;
236 mPlayer->seekToAsync(0, true /* needNotify */);
237 return OK;
238 default:
239 return INVALID_OPERATION;
240 };
241 }
在启播的过程中, 会走到STATE_UNPREPARED
的Flow中(因为在setDataSource
中调用notify
的时候, 改变了mState
的值)
- 调用
mPlayer
(在这里是NuPlayer
)的prepareAsync
;- 返回
OK
给MediaPlayerService::Client
的prepare
;
3.2.6 Nuplayer.cpp
3.2.6.1 NuPlayer::prepareAsync
void NuPlayer::prepareAsync() {
(new AMessage(kWhatPrepare, this))->post();
}
在
Nuplayer::prepareAsync()
中, 直接post
出消息;
3.2.6.2 NuPlayer::onMessageReceived
我们来看看NuPlayer::onMessageReceived
中做了什么事情, Code如下:
case kWhatPrepare:
{
mSource->prepareAsync();
break;
}
调用了
mSource
(GenericSource
)的prepareAsync
就结束了;
3.2.6.3 NuPlayer::GenericSource::prepareAsync
那么继续刨根问底, 来看看GenericSource::prepareAsync
干了什么, Code如下:
void NuPlayer::GenericSource::prepareAsync() {
if (mLooper == NULL) {
mLooper = new ALooper;
mLooper->setName("generic");
mLooper->start();
mLooper->registerHandler(this);
}
sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this);
msg->post();
}
- 在起播阶段,
mLooper
的值为NULL
, 所以会启动一个ALooper
, 他的名字为"generic", 并启动它;- 将
GenericSource
这个AHandler注册其中("generic"
的ALooper
);- 创建一个
AMessage
:msg
,post
出去;
3.2.6.4 NuPlayer::GenericSource::onMessageReceived
case kWhatPrepareAsync:
{
onPrepareAsync();
break;
}
直接调用
onPrepareAsync()
;
3.2.6.5 NuPlayer::GenericSource::onPrepareAsync
咱们终于找过真正在prepare
阶段"做事情的人"了!Code如下:
void NuPlayer::GenericSource::onPrepareAsync() {
// delayed data source creation
if (mDataSource == NULL) {
// set to false first, if the extractor
// comes back as secure, set it to true then.
mIsSecure = false;
if (!mUri.empty()) {
const char* uri = mUri.c_str();
String8 contentType;
mIsWidevine = !strncasecmp(uri, "widevine://", 11);
if (!strncasecmp("http://", uri, 7)
|| !strncasecmp("https://", uri, 8)
|| mIsWidevine) {
mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);
if (mHttpSource == NULL) {
ALOGE("Failed to create http source!");
notifyPreparedAndCleanup(UNKNOWN_ERROR);
return;
}
}
mDataSource = DataSource::CreateFromURI(
mHTTPService, uri, &mUriHeaders, &contentType,
static_cast<HTTPBase *>(mHttpSource.get()));
} else {
mIsWidevine = false;
mDataSource = new FileSource(mFd, mOffset, mLength);
mFd = -1;
}
if (mDataSource == NULL) {
ALOGE("Failed to create data source!");
notifyPreparedAndCleanup(UNKNOWN_ERROR);
return;
}
}
if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
}
// For widevine or other cached streaming cases, we need to wait for
// enough buffering before reporting prepared.
// Note that even when URL doesn't start with widevine://, mIsWidevine
// could still be set to true later, if the streaming or file source
// is sniffed to be widevine. We don't want to buffer for file source
// in that case, so must check the flag now.
mIsStreaming = (mIsWidevine || mCachedSource != NULL);
// init extractor from data source
status_t err = initFromDataSource();
if (err != OK) {
ALOGE("Failed to init from data source!");
notifyPreparedAndCleanup(err);
return;
}
if (mVideoTrack.mSource != NULL) {
sp<MetaData> meta = doGetFormatMeta(false /* audio */);
sp<AMessage> msg = new AMessage;
err = convertMetaDataToMessage(meta, &msg);
if(err != OK) {
notifyPreparedAndCleanup(err);
return;
}
notifyVideoSizeChanged(msg);
}
notifyFlagsChanged(
(mIsSecure ? FLAG_SECURE : 0)
| (mDecryptHandle != NULL ? FLAG_PROTECTED : 0)
| FLAG_CAN_PAUSE
| FLAG_CAN_SEEK_BACKWARD
| FLAG_CAN_SEEK_FORWARD
| FLAG_CAN_SEEK);
if (mIsSecure) {
// secure decoders must be instantiated before starting widevine source
sp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, this);
notifyInstantiateSecureDecoders(reply);
} else {
finishPrepareAsync();
}
}
- 在启播阶段, 字段
mDataSource
是NULL
的, 所以我们进入if (mDataSource == NULL)
;- 因为我们一直都是走的本地File播放流程, 所以我们会走
else
中的mDataSource = new FileSource(mFd, mOffset, mLength)
.;mIsStreaming = (mIsWidevine || mCachedSource != NULL);
这句是为了流媒体缓存buffer用的, 当buffer没有缓冲完毕的时候, 需要等待缓冲完毕后再notify prepare complete
;status_t err = initFromDataSource()
初始化媒体文件对应的extractor;if (mVideoTrack.mSource != NULL)
, 如果有Vedio, 则convertMetaDataToMessage
将MetaData信息装载到Message中, 并notifyVideoSizeChanged
(这个里面主要是将vidioTrack
的width
和height
通知到NuplayerDriver
显示宽高变化)notifyFlagsChanged
通知Flag变化了;- 最后, 走
finishPrepareAsync()
;
在这个里面, 我们需要看看initFromDataSource
的Code. 如下:
142 status_t NuPlayer::GenericSource::initFromDataSource() {
143 sp<IMediaExtractor> extractor;
144 String8 mimeType;
145 float confidence;
146 sp<AMessage> dummy;
147 bool isWidevineStreaming = false;
148
149 CHECK(mDataSource != NULL);
150
151 if (mIsWidevine) {
152 isWidevineStreaming = SniffWVM(
153 mDataSource, &mimeType, &confidence, &dummy);
154 if (!isWidevineStreaming ||
155 strcasecmp(
156 mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
157 ALOGE("unsupported widevine mime: %s", mimeType.string());
158 return UNKNOWN_ERROR;
159 }
160 } else if (mIsStreaming) {
161 if (!mDataSource->sniff(&mimeType, &confidence, &dummy)) {
162 return UNKNOWN_ERROR;
163 }
164 isWidevineStreaming = !strcasecmp(
165 mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM);
166 }
167
168 if (isWidevineStreaming) {
169 // we don't want cached source for widevine streaming.
170 mCachedSource.clear();
171 mDataSource = mHttpSource;
172 mWVMExtractor = new WVMExtractor(mDataSource);
173 mWVMExtractor->setAdaptiveStreamingMode(true);
174 if (mUIDValid) {
175 mWVMExtractor->setUID(mUID);
176 }
177 extractor = mWVMExtractor;
178 } else {
179 extractor = MediaExtractor::Create(mDataSource,
180 mimeType.isEmpty() ? NULL : mimeType.string());
181 }
182
183 if (extractor == NULL) {
184 return UNKNOWN_ERROR;
185 }
186
187 if (extractor->getDrmFlag()) {
188 checkDrmStatus(mDataSource);
189 }
190
191 mFileMeta = extractor->getMetaData();
192 if (mFileMeta != NULL) {
193 int64_t duration;
194 if (mFileMeta->findInt64(kKeyDuration, &duration)) {
195 mDurationUs = duration;
196 }
197
198 if (!mIsWidevine) {
199 // Check mime to see if we actually have a widevine source.
200 // If the data source is not URL-type (eg. file source), we
201 // won't be able to tell until now.
202 const char *fileMime;
203 if (mFileMeta->findCString(kKeyMIMEType, &fileMime)
204 && !strncasecmp(fileMime, "video/wvm", 9)) {
205 mIsWidevine = true;
206 }
207 }
208 }
209
210 int32_t totalBitrate = 0;
211
212 size_t numtracks = extractor->countTracks();
213 if (numtracks == 0) {
214 return UNKNOWN_ERROR;
215 }
216
217 for (size_t i = 0; i < numtracks; ++i) {
218 sp<IMediaSource> track = extractor->getTrack(i);
219 if (track == NULL) {
220 continue;
221 }
222
223 sp<MetaData> meta = extractor->getTrackMetaData(i);
224 if (meta == NULL) {
225 ALOGE("no metadata for track %zu", i);
226 return UNKNOWN_ERROR;
227 }
228
229 const char *mime;
230 CHECK(meta->findCString(kKeyMIMEType, &mime));
231
232 // Do the string compare immediately with "mime",
233 // we can't assume "mime" would stay valid after another
234 // extractor operation, some extractors might modify meta
235 // during getTrack() and make it invalid.
236 if (!strncasecmp(mime, "audio/", 6)) {
237 if (mAudioTrack.mSource == NULL) {
238 mAudioTrack.mIndex = i;
239 mAudioTrack.mSource = track;
240 mAudioTrack.mPackets =
241 new AnotherPacketSource(mAudioTrack.mSource->getFormat());
242
243 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
244 mAudioIsVorbis = true;
245 } else {
246 mAudioIsVorbis = false;
247 }
248 }
249 } else if (!strncasecmp(mime, "video/", 6)) {
250 if (mVideoTrack.mSource == NULL) {
251 mVideoTrack.mIndex = i;
252 mVideoTrack.mSource = track;
253 mVideoTrack.mPackets =
254 new AnotherPacketSource(mVideoTrack.mSource->getFormat());
255
256 // check if the source requires secure buffers
257 int32_t secure;
258 if (meta->findInt32(kKeyRequiresSecureBuffers, &secure)
259 && secure) {
260 mIsSecure = true;
261 if (mUIDValid) {
262 extractor->setUID(mUID);
263 }
264 }
265 }
266 }
267
268 mSources.push(track);
269 int64_t durationUs;
270 if (meta->findInt64(kKeyDuration, &durationUs)) {
271 if (durationUs > mDurationUs) {
272 mDurationUs = durationUs;
273 }
274 }
275
276 int32_t bitrate;
277 if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) {
278 totalBitrate += bitrate;
279 } else {
280 totalBitrate = -1;
281 }
282 }
283
284 if (mSources.size() == 0) {
285 ALOGE("b/23705695");
286 return UNKNOWN_ERROR;
287 }
288
289 mBitrate = totalBitrate;
290
291 return OK;
292 }
- 当我们播放的是本地文件的时候, 走的流程是
extractor = MediaExtractor::Create(mDataSource, mimeType.isEmpty() ? NULL : mimeType.string());
在这里, 我们拿到了文件对应的Extractor
;mFileMeta = extractor->getMetaData();
获得本地媒体文件的MetaData
信息放入字段mFileMeta
中;mDurationUs
字段获取到媒体文件的播放时间;if (!mIsWidevine)
中, 对video/wvm
类媒体文件, 置flagmIsWidevine
为true
;- 拿到一系列的与播放相关的数据
- 从
if (!strncasecmp(mime, "audio/", 6))
开始, 开始对mAudioTrack
和mVideoTrack
进行处理(具体是获取audio和video的), 并将指向这些track
的指针放入mSources
中;- 返回
OK
;
在onPrepareAsync()
最后阶段, finishPrepareAsync()
的Code如下:
469void NuPlayer::GenericSource::finishPrepareAsync() {
470 status_t err = startSources();
471 if (err != OK) {
472 ALOGE("Failed to init start data source!");
473 notifyPreparedAndCleanup(err);
474 return;
475 }
476
477 if (mIsStreaming) {
478 if (mBufferingMonitorLooper == NULL) {
479 mBufferingMonitor->prepare(mCachedSource, mWVMExtractor, mDurationUs, mBitrate,
480 mIsStreaming);
481
482 mBufferingMonitorLooper = new ALooper;
483 mBufferingMonitorLooper->setName("GSBMonitor");
484 mBufferingMonitorLooper->start();
485 mBufferingMonitorLooper->registerHandler(mBufferingMonitor);
486 }
487
488 mBufferingMonitor->ensureCacheIsFetching();
489 mBufferingMonitor->restartPollBuffering();
490 } else {
491 notifyPrepared();
492 }
493}
- 在
startSources
中, 会有这么两句(mAudioTrack.mSource != NULL && mAudioTrack.mSource->start() != OK)
和if (mVideoTrack.mSource != NULL && mVideoTrack.mSource->start() != OK)
. 其实在这里调用track
的mediaSource
的start
只是为了不让在异步prepare中已经缓存的buffer不在strat
的时候浪费掉而已, 并没有做什么实质性的事情;- 如果是
mIsStreaming
, 启动一个名为GSBMonitor
的线程Looper
来监视buffer
notifyPrepared
通知NuPlayerDriver
Prepare阶段完成;
3.3 start
在setDataSource
和Prepare
阶段结束后, 我们要正式开始播放啦. 按照惯例, 我们来看看从java
一端到Nuplayer
一端的时序图, 如下所示:
123123123
3.3.1 MediaPlayer.java
1210 public void start() throws IllegalStateException {
1211 baseStart();
1212 stayAwake(true);
1213 _start();
1214 }
1215
1216 private native void _start() throws IllegalStateException;
- 在
baseStart();
中, 我们设置了AudioPlaybackConfiguration
(audio播放相关);stayAwake(true);
的作用是让屏幕始终处于唤醒状态;- 调用JNI层的
native
方法_start()
;
3.3.2 android_media_MediaPlayer.cpp
380 static void
381 android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
382 {
383 ALOGV("start");
384 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
385 if (mp == NULL ) {
386 jniThrowException(env, "java/lang/IllegalStateException", NULL);
387 return;
388 }
389 process_media_player_call( env, thiz, mp->start(), NULL, NULL );
390 }
- 前面的流程都很熟悉了, 这里直接看到
mp->start()
, 我们接着往下看;
3.3.3 MediaPlayer.cpp
295 status_t MediaPlayer::start()
296 {
297 ALOGV("start");
298
299 status_t ret = NO_ERROR;
300 Mutex::Autolock _l(mLock);
301
302 mLockThreadId = getThreadId();
303
304 if (mCurrentState & MEDIA_PLAYER_STARTED) {
305 ret = NO_ERROR;
306 } else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED |
307 MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {
308 mPlayer->setLooping(mLoop);
309 mPlayer->setVolume(mLeftVolume, mRightVolume);
310 mPlayer->setAuxEffectSendLevel(mSendLevel);
311 mCurrentState = MEDIA_PLAYER_STARTED;
312 ret = mPlayer->start();
313 if (ret != NO_ERROR) {
314 mCurrentState = MEDIA_PLAYER_STATE_ERROR;
315 } else {
316 if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {
317 ALOGV("playback completed immediately following start()");
318 }
319 }
320 } else {
321 ALOGE("start called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());
322 ret = INVALID_OPERATION;
323 }
324
325 mLockThreadId = 0;
326
327 return ret;
328 }
- 在正常启播阶段, 我们会走
else if ( (mPlayer != 0)
这个流程;308 - 311 Lines
是远程调用服务端来设置声音相关的东西, 并将当前状态:mCurrentState
设置为:MEDIA_PLAYER_STARTED
;- 调用服务端的
start()
;
3.3.4 MediaPlayerService.cpp
980 status_t MediaPlayerService::Client::start()
981 {
982 ALOGV("[%d] start", mConnId);
983 sp<MediaPlayerBase> p = getPlayer();
984 if (p == 0) return UNKNOWN_ERROR;
985 p->setLooping(mLoop);
986 return p->start();
987 }
- 拿到指向
NupplayerDriver
的指针;- 调用
NupplayerDriver
的setLooping
方法;- 返回
NupplayerDriver
的start()
方法的返回结果;
3.3.5 NuPlayerDriver.cpp
243 status_t NuPlayerDriver::start() {
244 ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS);
245 Mutex::Autolock autoLock(mLock);
246 return start_l();
247 }
248
249 status_t NuPlayerDriver::start_l() {
250 switch (mState) {
251 case STATE_UNPREPARED:
252 {
253 status_t err = prepare_l();
254
255 if (err != OK) {
256 return err;
257 }
258
259 CHECK_EQ(mState, STATE_PREPARED);
260
261 // fall through
262 }
263
264 case STATE_PAUSED:
265 case STATE_STOPPED_AND_PREPARED:
266 case STATE_PREPARED:
267 {
268 mPlayer->start();
269
270 // fall through
271 }
272
273 case STATE_RUNNING:
274 {
275 if (mAtEOS) {
276 mPlayer->seekToAsync(0);
277 mAtEOS = false;
278 mPositionUs = -1;
279 }
280 break;
281 }
282
283 default:
284 return INVALID_OPERATION;
285 }
286
287 mState = STATE_RUNNING;
288
289 return OK;
290 }
- 在启播阶段, 我们走的是
case STATE_PREPARED:
. 然后执行mPlayer->start();
开始到Nuplayer
中执行逻辑;- 那么剩下的还有其他状态, 在此简单说明下:
2.1
STATE_UNPREPARED
, 在prepare
阶段, 执行prepare_l
;
2.2STATE_PAUSED
代表播放器点了pause键位, 依然从pause处开始执行strat()
;
2.3STATE_RUNNING
代表播放器正在播放, 如果达到了end os stream
, 则seek
到0, 重新开始播放;
- 最后,
mState
设置为STATE_RUNNING
;
3.3.6 Nuplayer.cpp
337 void NuPlayer::start() {
338 (new AMessage(kWhatStart, this))->post();
339 }
714 case kWhatStart:
715 {
716 ALOGV("kWhatStart");
717 if (mStarted) {
718 // do not resume yet if the source is still buffering
719 if (!mPausedForBuffering) {
720 onResume();
721 }
722 } else {
723 onStart();
724 }
725 mPausedByClient = false;
726 break;
727 }
- 在启播阶段
mStarted
字段为false
, 我们走onStart();
分支;mPausedByClient
字段设置为false
, 当上层调用pause
方法的时候, 该字段会被设置为true
;
3.3.6.1 onStart()
1317 void NuPlayer::onStart(int64_t startPositionUs) {
1318 if (!mSourceStarted) {
1319 mSourceStarted = true;
1320 mSource->start();
1321 }
1322 if (startPositionUs > 0) {
1323 performSeek(startPositionUs);
1324 if (mSource->getFormat(false /* audio */) == NULL) {
1325 return;
1326 }
1327 }
1328
1329 mOffloadAudio = false;
1330 mAudioEOS = false;
1331 mVideoEOS = false;
1332 mStarted = true;
1333 mPaused = false;
1334
1335 uint32_t flags = 0;
1336
1337 if (mSource->isRealTime()) {
1338 flags |= Renderer::FLAG_REAL_TIME;
1339 }
1340
1341 sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */);
1342 sp<MetaData> videoMeta = mSource->getFormatMeta(false /* audio */);
1343 if (audioMeta == NULL && videoMeta == NULL) {
1344 ALOGE("no metadata for either audio or video source");
1345 mSource->stop();
1346 mSourceStarted = false;
1347 notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_MALFORMED);
1348 return;
1349 }
1350 ALOGV_IF(audioMeta == NULL, "no metadata for audio source"); // video only stream
1351
1352 audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
1353 if (mAudioSink != NULL) {
1354 streamType = mAudioSink->getAudioStreamType();
1355 }
1356
1357 sp<AMessage> videoFormat = mSource->getFormat(false /* audio */);
1358
1359 mOffloadAudio =
1360 canOffloadStream(audioMeta, (videoFormat != NULL), mSource->isStreaming(), streamType)
1361 && (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
1362 if (mOffloadAudio) {
1363 flags |= Renderer::FLAG_OFFLOAD_AUDIO;
1364 }
1365
1366 sp<AMessage> notify = new AMessage(kWhatRendererNotify, this);
1367 ++mRendererGeneration;
1368 notify->setInt32("generation", mRendererGeneration);
1369 mRenderer = new Renderer(mAudioSink, notify, flags);
1370 mRendererLooper = new ALooper;
1371 mRendererLooper->setName("NuPlayerRenderer");
1372 mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
1373 mRendererLooper->registerHandler(mRenderer);
1374
1375 status_t err = mRenderer->setPlaybackSettings(mPlaybackSettings);
1376 if (err != OK) {
1377 mSource->stop();
1378 mSourceStarted = false;
1379 notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
1380 return;
1381 }
1382
1383 float rate = getFrameRate();
1384 if (rate > 0) {
1385 mRenderer->setVideoFrameRate(rate);
1386 }
1387
1388 if (mVideoDecoder != NULL) {
1389 mVideoDecoder->setRenderer(mRenderer);
1390 }
1391 if (mAudioDecoder != NULL) {
1392 mAudioDecoder->setRenderer(mRenderer);
1393 }
1394
1395 postScanSources();
1396 }
在onStart
中, 涉及到了一些decode和renderer的东西, 在本篇不细讲.
- 执行
GenericSource
中的start()
方法: 用extractor
去读取A/Vbuffer
, 并组装成ABuffer
放入一个List
中, 为后面的解码阶段做准备;- 设置
streamType
和videoFormat
mRendererLooper->setName("NuPlayerRenderer");
创建一个名为NuPlayerRenderer
的ALooper
(起一个新线程, 处理A/V Snyc问题)status_t err = mRenderer->setPlaybackSettings
设置Renderer
的Playback
设置;float rate = getFrameRate();
获得帧率;postScanSources();
创建decoder
, 处理解码相关工作;
4 总结
本篇讲了启播过程中, 从java到native的最基本的三个步骤. 分别是setDataSource
; prepare
和start
.
-
setDataSource
是用来获得原始数据的; -
prepare
是用来建立extractor
用来解析媒体文件, 进行A/V分离的; -
start
是用来将分离出来的audio
和video
数据组装成一帧, 向decoder
输出解码, 最后由renderer
做A/V Sync, 然后交给显示系统显示的.
因为时间和水平问题, 文中难免会有各种各样的错误, 欢迎各位看官提出意见和建议, 我将第一时间纠正, 感谢~