前言
接著《Media FrameWork簡介》,我們在本篇將迎來具體的MediaPlayer的講解。我們知道,一個最簡單的播放器有播放、暫停、停止三個功能。那么,本篇將結(jié)合google官網(wǎng)給出的MediaPlayer狀態(tài)圖以及mediaplayer提供的基本接口,講清楚這三個基本功能。
1. MediaPlayer概述
在了解一個新事物的時候,我個人習慣首先了解它的用法(它是用來干什么的)。在此基礎(chǔ)上,再去了解它的整體結(jié)構(gòu)(它是由什么東東組成的)。最后,結(jié)合這兩者,詳細分析,以求充分理解這個事物。
好,那么各位看官也明白了,我將先介紹MediaPlayer的用法,再介紹MediaPlayer的架構(gòu),最后結(jié)合兩者,講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();
/* 是否單曲循環(huán) */
mMediaPlayer.setLooping(false);
} catch (IOException e) {
e.printStackTrace();
}
}
但是,在某個漆黑的角落里,我突然發(fā)現(xiàn)了一張MediaPlayer狀態(tài)機的圖,如圖1所示(Android Developers官網(wǎng)制作),簡直如獲至寶。特在此貼出來,作為講解如何使用MediaPlayer的圖示 ^ _ ^

2.1 初始化播放器
if (mMediaPlayer != null){
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
}
mMediaPlayer = new MediaPlayer();
- 如果有MediaPlayer的實例。那么,先reset -> Idle狀態(tài);再release -> End狀態(tài),以確保再次播放時,MediaPlayer處于正確的狀態(tài)。
- 如果沒有創(chuàng)建過MediaPlayer的實例,new 一個MediaPlayer實例。
2.2 播放
/* 重置多媒體 */
mMediaPlayer.reset();
/* 讀取mp3文件 */
mMediaPlayer.setDataSource(mFileName);
/* 準備播放 */
mMediaPlayer.prepare();
/* 開始播放 */
mMediaPlayer.start();
/* 是否單曲循環(huán) */
mMediaPlayer.setLooping(false);
- reset -> Idle狀態(tài);
- setDataSource("文件路徑(本地或者網(wǎng)絡(luò))") -> Initialized狀態(tài);
- prepare -> Prepared狀態(tài);
- start -> Started狀態(tài);
- setLooping(false) && onCompletion invoked on OnCompletionListener -> PlaybackCompleted狀態(tài);
其他四個狀態(tài)都一目了然。第五個狀態(tài)是在1.沒有設(shè)置單曲循環(huán);2.播放完成狀態(tài)通知,這兩個條件下,才會到達PlaybackCompleted狀態(tài)的??梢钥吹?,如果調(diào)用strat,則又回到Started狀態(tài),并重頭開始播放。
2.3 暫停
if (mMediaPlayer.isPlaying()) {// 正在播放
mMediaPlayer.pause();// 暫停
} else {// 沒有播放
mMediaPlayer.start();
}
- 如果播放器正在播放,那么調(diào)用pause -> Paused狀態(tài)
- 如果播放器此時正好在Paused狀態(tài),那么調(diào)用start -> Started狀態(tài)
2.4 停止
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
如果有MediaPlayer播放的實例且播放狀態(tài)是isPlaying,調(diào)用stop -> Stopped狀態(tài)。
2.5 銷毀
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
如果有MediaPlayer的實例,那么先stop -> Stopped狀態(tài); 然后release -> End狀態(tài)。
最后小結(jié)下,當我們要使用MediaPlayer的時候,一般情況下要經(jīng)過以下步驟
reset:重置Mediaplayer各種參數(shù),進入Idle狀態(tài);setDataSource:帶入文件的地址(有可能本地、有可能網(wǎng)絡(luò)),獲得Nuplayer的實例,進入Initialized狀態(tài);prepare:獲取播放文件的各種信息(播放時長、比特率、編碼信息等);start:根據(jù)prepare過程獲得的信息,初始化解碼器,初始化渲染器,操控輸入、輸出Buffer進出解碼器等,完成播放工作。
當然,還有stop、reset、pause、release等,相對于啟動播放過程不是特別重要,留在以后再來補充,今后的篇章中,將著重講2——3——4步驟。
3. MeidaPlayer三部曲
在講三部曲之前,首先得獲得MediaPlayer的實例。讓我們來看看frameworks/base/media/java/android/media/MediaPlayer.java的部分代碼吧。
首先是在靜態(tài)代碼段加載
media_jni動態(tài)庫(libmedia_jni.so),并且調(diào)用本地方法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對象。根據(jù)此對象拿到mNativeContext、postEventFromNative、mNativeSurfaceTexture兩個字段的ID,拿到方法postEventFromNative的ID,之后銷毀;- 得到
ProxyInfo對象。根據(jù)此對象拿到getHost、getPort、getExclusionListAsString三個方法的ID,之后銷毀;- 初始化
Playback的一些屬性字段參數(shù),初始化初始化Sync的一些屬性字段參數(shù);
好,現(xiàn)在初始化完成了,各就各位了,我們開始創(chuàng)建MediaPlayer 吧?!靶《?!上Code!”
在上層的MediaPlayer.Java的MediaPlayer的構(gòu)造函數(shù)中,有一個非常關(guān)鍵的點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的實例,如果沒有創(chuàng)建成功,拋出Out of memory的異常,并返回;- 創(chuàng)建一個監(jiān)聽對象,并將此監(jiān)聽對象的注冊到剛剛獲得的
MediaPlayer的實例中;- 將創(chuàng)建的
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層做了哪些事情吧。
- 根據(jù)傳入的
path,區(qū)分是本地文件還是網(wǎng)絡(luò)播放文件;- 如果是網(wǎng)絡(luò)文件的話,走
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;- 調(diào)用
process_media_player_call,其中有個重要的參數(shù)是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中創(chuàng)建的MediaPlayer;
并且如果可以拿到的話,向java層通知MEDIA_ERROR;- 正常情況下,我們一般走的是有傳入
exception的分支;
在此分支中,我們需要根據(jù)在MediaPlayer.cpp中setDataSource返回的status狀態(tài)來拋出對應(yīng)的異常;
一般情況下,如果我們獲取到的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服務(wù)(BpBinder),然后通過
interface_cast拿到IMediaPlayerService;- 調(diào)用
IMediaPlayerService的BpMediaPlayerService::create方法,通過remote->transact()遠程調(diào)用服務(wù)端,并用interface_cast模版拿到IMediaPlayer- 然后走到
IMediaPlayer的類BpMediaPlayer::setDataSource方法,通過remote->transact()遠程調(diào)用服務(wù)端,并返回服務(wù)端setDataSource方法返回的狀態(tài);
好,流程到這里,相信有些童鞋已經(jīng)一頭霧水了(要了解的透徹,智能指針/JNI/Binder知識缺一不可,如不清楚,還請問問度娘,我以后有時間也會寫的)。我們現(xiàn)在只要看看關(guān)鍵的地方就行了,畢竟我們的目的是了解MediaPlayer的框架嘛。
3.1.3.1 關(guān)鍵點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 關(guān)鍵點2
在frameworks/base/media/mediaserver/(多媒體服務(wù)的守護進程)其中只有一個源文件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();
}
- 這個函數(shù)就是啟動了多媒體服務(wù)/相機服務(wù)/音頻服務(wù)
- 重點在于
MediaPlayerService::instantiate();
我們來看看: /frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
- 這里看得很明顯啦, 把名為"media.player"服務(wù)注冊到
Service Manager中, 這樣我們在關(guān)鍵點1中就可以拿到這個服務(wù)啦.
3.1.3.3 關(guān)鍵點3
接下來看代理端調(diào)用的service->create(this, mAudioSessionId)在本地端MediaPlayerService::creat是如何實現(xiàn)的. 代碼如下:
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;
}
關(guān)鍵點在于:
- 拿到了
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;
}
簡單來說, 在這個函數(shù)中做了以下幾件事情:
- 從
MediaPlayerFactory::getPlayerType中拿到對應(yīng)的playerType(當前7.0版本都是穿件的Nuplayer(這個后面會講到));- 根據(jù)
playerType, 通過setDataSource_pre函數(shù)拿到MediaPlayerBase;- 通過
setDataSource_post函數(shù)傳入MediaPlayerBase對象, 繼續(xù)完成setDataSource的工作;
3.1.4.1 MediaPlayerFactory.cpp
現(xiàn)在來看看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的構(gòu)造函數(shù)中注冊好的mediaplayerFactory(有NuPlayer和TestPlayer(此Player一般情況下只在userdebug和eng版本中猜有可能走到));- 根據(jù)參數(shù)對每個
mediaplayerFactory打分, 獲取到最高打分和對應(yīng)的mediaplayerFactory(絕大多數(shù)情況下都是NuPlayerFactory);- 如果到最后
bestScore還是0, 那么使用默認的mediaplayerFactory(NuplayerFactory).
3.1.4.2 setDataSource_pre
好,我們現(xiàn)在拿到了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}
- 根據(jù)傳入的
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完成的狀態(tài)(關(guān)于這一塊, 我將在下一篇具體說明), 如果狀態(tài)正確, 往下走;- 在構(gòu)造函數(shù)中,
mRetransmitEndpointValid = false. 如果上層沒有調(diào)用setRetransmitEndpoint方法的話, 是不會走這個Flow流程的;- 最后將
NuplayerDriver放入MediaPlayerService::Client類的mPlayer字段中, 以便后面調(diào)用;
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的狀態(tài)(在構(gòu)造函數(shù)中 :mState = STATE_IDLE)- 調(diào)用
Nuplayer的setDataSourceAsync異步方法 (這個方法在NuPlayer.cpp中, 做完后會notifyNuplayerDriver, 并改變mState狀態(tài));- 返回
mAsyncResult;
3.1.6 Nuplayer.cpp
終于, 我們進軍到了Nuplayer中, 發(fā)現(xiàn)了之前講過的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;- 調(diào)用
source(GenericSource)的setDataSource方法;- 拋出
msg, 在本類的onMessageReceive中獲取處理;
3.1.6.1 GenericSource構(gòu)造函數(shù)
接下來, 看看new GenericSource的時候所調(diào)用的構(gòu)造函數(shù). 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播放相關(guān)的參數(shù)相關(guān)的字段初始化;
mBufferingMonitor字段拿到BufferingMonitor(用來監(jiān)視buffer的狀態(tài));- 調(diào)用
resetDataSource重置DataSource相關(guān)的字段參數(shù)(一般是對一些字段進行置0和NULL的工作)RegisterDefaultSniffers(在Android 8.0 code中,RegisterDefaultSniffers()這個方法是在GenericSource::prepare中拋出消息, 并且在接收方法onPrepareAsync中的initFromDataSource中的MediaExteactor::Creat的CreateFromService中調(diào)用的).
這個方法的作用是將各類Extractor的sniff的函數(shù)指針放入到一個名為gSniffers的List中(在setDataSource流程之后, 會依次遍歷這些注冊其中的sniff函數(shù)進行打分來判斷一個文件應(yīng)該由那種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 }
- 調(diào)用
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的指針轉(zhuǎn)換為其父類的指針NuPlayer::Source并賦值給mSource;- 調(diào)用
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 小結(jié)
至此, 我們完成了setDataSource的工作. 具體的:
- 獲得了
StageFright平臺之上的Player :NuPlayer;- 獲得了與
MediaExtractor通信的權(quán)利;- 獲得了與
MediaCodec通信的權(quán)利;- 獲得了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;
- 調(diào)用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的鏈接;
- 執(zhí)行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 }
- 調(diào)用prepareAsync_l()方法使用異步的prepare;
- mSignal.wait(mLock)等待拿到鎖;
- 返回mPrepareStatus;
所以, 這里我們雖然調(diào)用了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)設(shè)置player的Audio的Type為: "AUDIO_STREAM_MUSIC";
- 進入MEDIA_PLAYER_PREPARING狀態(tài);
- 調(diào)用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中調(diào)用notify的時候, 改變了mState的值)
- 調(diào)用
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;
}
調(diào)用了
mSource(GenericSource)的prepareAsync就結(jié)束了;
3.2.6.3 NuPlayer::GenericSource::prepareAsync
那么繼續(xù)刨根問底, 來看看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);- 創(chuàng)建一個
AMessage:msg,post出去;
3.2.6.4 NuPlayer::GenericSource::onMessageReceived
case kWhatPrepareAsync:
{
onPrepareAsync();
break;
}
直接調(diào)用
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()初始化媒體文件對應(yīng)的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());在這里, 我們拿到了文件對應(yīng)的Extractor;mFileMeta = extractor->getMetaData();獲得本地媒體文件的MetaData信息放入字段mFileMeta中;mDurationUs字段獲取到媒體文件的播放時間;if (!mIsWidevine)中, 對video/wvm類媒體文件, 置flagmIsWidevine為true;- 拿到一系列的與播放相關(guān)的數(shù)據(jù)
- 從
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). 其實在這里調(diào)用track的mediaSource的start只是為了不讓在異步prepare中已經(jīng)緩存的buffer不在strat的時候浪費掉而已, 并沒有做什么實質(zhì)性的事情;- 如果是
mIsStreaming, 啟動一個名為GSBMonitor的線程Looper來監(jiān)視buffernotifyPrepared通知NuPlayerDriverPrepare階段完成;
3.3 start
在setDataSource和Prepare階段結(jié)束后, 我們要正式開始播放啦. 按照慣例, 我們來看看從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();中, 我們設(shè)置了AudioPlaybackConfiguration(audio播放相關(guān));stayAwake(true);的作用是讓屏幕始終處于喚醒狀態(tài);- 調(diào)用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是遠程調(diào)用服務(wù)端來設(shè)置聲音相關(guān)的東西, 并將當前狀態(tài):mCurrentState設(shè)置為:MEDIA_PLAYER_STARTED;- 調(diào)用服務(wù)端的
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的指針;- 調(diào)用
NupplayerDriver的setLooping方法;- 返回
NupplayerDriver的start()方法的返回結(jié)果;
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:. 然后執(zhí)行mPlayer->start();開始到Nuplayer中執(zhí)行邏輯;- 那么剩下的還有其他狀態(tài), 在此簡單說明下:
2.1
STATE_UNPREPARED, 在prepare階段, 執(zhí)行prepare_l;
2.2STATE_PAUSED代表播放器點了pause鍵位, 依然從pause處開始執(zhí)行strat();
2.3STATE_RUNNING代表播放器正在播放, 如果達到了end os stream, 則seek到0, 重新開始播放;
- 最后,
mState設(shè)置為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字段設(shè)置為false, 當上層調(diào)用pause方法的時候, 該字段會被設(shè)置為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的東西, 在本篇不細講.
- 執(zhí)行
GenericSource中的start()方法: 用extractor去讀取A/Vbuffer, 并組裝成ABuffer放入一個List中, 為后面的解碼階段做準備;- 設(shè)置
streamType和videoFormatmRendererLooper->setName("NuPlayerRenderer");創(chuàng)建一個名為NuPlayerRenderer的ALooper(起一個新線程, 處理A/V Snyc問題)status_t err = mRenderer->setPlaybackSettings設(shè)置Renderer的Playback設(shè)置;float rate = getFrameRate();獲得幀率;postScanSources();創(chuàng)建decoder, 處理解碼相關(guān)工作;
4 總結(jié)
本篇講了啟播過程中, 從java到native的最基本的三個步驟. 分別是setDataSource; prepare和start.
-
setDataSource是用來獲得原始數(shù)據(jù)的; -
prepare是用來建立extractor用來解析媒體文件, 進行A/V分離的; -
start是用來將分離出來的audio和video數(shù)據(jù)組裝成一幀, 向decoder輸出解碼, 最后由renderer做A/V Sync, 然后交給顯示系統(tǒng)顯示的.
因為時間和水平問題, 文中難免會有各種各樣的錯誤, 歡迎各位看官提出意見和建議, 我將第一時間糾正, 感謝~