Media Player簡介

前言

接著《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的圖示 ^ _ ^


圖1 mediaplayer_state_diagram.gif

2.1 初始化播放器

if (mMediaPlayer != null){  
    mMediaPlayer.reset();  
    mMediaPlayer.release();  
    mMediaPlayer = null;  
}  
mMediaPlayer = new MediaPlayer();
  1. 如果有MediaPlayer的實例。那么,先reset -> Idle狀態(tài);再release -> End狀態(tài),以確保再次播放時,MediaPlayer處于正確的狀態(tài)。
  2. 如果沒有創(chuàng)建過MediaPlayer的實例,new 一個MediaPlayer實例。

2.2 播放

/* 重置多媒體 */  
mMediaPlayer.reset();  
/* 讀取mp3文件 */  
mMediaPlayer.setDataSource(mFileName);  
/* 準備播放 */  
mMediaPlayer.prepare();  
/* 開始播放 */  
mMediaPlayer.start();  
/* 是否單曲循環(huán) */  
mMediaPlayer.setLooping(false); 
  1. reset -> Idle狀態(tài);
  2. setDataSource("文件路徑(本地或者網(wǎng)絡(luò))") -> Initialized狀態(tài);
  3. prepare -> Prepared狀態(tài);
  4. start -> Started狀態(tài);
  5. 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();  
}  
  1. 如果播放器正在播放,那么調(diào)用pause -> Paused狀態(tài)
  2. 如果播放器此時正好在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)過以下步驟

  1. reset:重置Mediaplayer各種參數(shù),進入Idle狀態(tài);
  2. setDataSource:帶入文件的地址(有可能本地、有可能網(wǎng)絡(luò)),獲得Nuplayer的實例,進入Initialized狀態(tài);
  3. prepare:獲取播放文件的各種信息(播放時長、比特率、編碼信息等);
  4. start:根據(jù)prepare過程獲得的信息,初始化解碼器,初始化渲染器,操控輸入、輸出Buffer進出解碼器等,完成播放工作。

當然,還有stop、reset、pauserelease等,相對于啟動播放過程不是特別重要,留在以后再來補充,今后的篇章中,將著重講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}

簡單描述一下。

  1. 得到MediaPlayer對象。根據(jù)此對象拿到mNativeContextpostEventFromNative、mNativeSurfaceTexture兩個字段的ID,拿到方法postEventFromNative的ID,之后銷毀;
  2. 得到ProxyInfo對象。根據(jù)此對象拿到getHostgetPort、getExclusionListAsString三個方法的ID,之后銷毀;
  3. 初始化Playback的一些屬性字段參數(shù),初始化初始化Sync的一些屬性字段參數(shù);

好,現(xiàn)在初始化完成了,各就各位了,我們開始創(chuàng)建MediaPlayer 吧?!靶《?!上Code!”
在上層的MediaPlayer.JavaMediaPlayer的構(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}
  1. 首先是new了一個MediaPlayer的實例,如果沒有創(chuàng)建成功,拋出Out of memory的異常,并返回;
  2. 創(chuàng)建一個監(jiān)聽對象,并將此監(jiān)聽對象的注冊到剛剛獲得的MediaPlayer的實例中;
  3. 將創(chuàng)建的MediaPlayer的對象的裝在一個jobject中(以便上層使用)。
    好,至此我們獲得到了native層的MediaPlayer的對象的實例了。接下來依照順序java -> Jni -> native看看三部曲。

3.1 setDataSource (以本地文件為例)

圖2 setDataSource.png

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層做了哪些事情吧。

  1. 根據(jù)傳入的path,區(qū)分是本地文件還是網(wǎng)絡(luò)播放文件;
  2. 如果是網(wǎng)絡(luò)文件的話,走nativeSetDataSource路線到Jni層;
  3. 如果是本地文件的話,獲取文件描述符,走_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}
  1. get到我們在native_setup中set的MediaPlayer對象,若沒有獲取到,則拋出異常;
  2. 拿到文件描述符FD
  3. 調(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}
  1. 如果傳入的exception為空(顯然我們不走這個分支);
    并且在mediaPlayer.cpp中的setDataSource失敗了,拿到之前在native_setup中創(chuàng)建的MediaPlayer;
    并且如果可以拿到的話,向java層通知MEDIA_ERROR;
  2. 正常情況下,我們一般走的是有傳入exception的分支;
    在此分支中,我們需要根據(jù)在MediaPlayer.cppsetDataSource返回的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 }
  1. 獲取了名為“media.player”的binder服務(wù)(BpBinder),然后通過interface_cast拿到IMediaPlayerService;
  2. 調(diào)用IMediaPlayerServiceBpMediaPlayerService::create方法,通過remote->transact()遠程調(diào)用服務(wù)端,并用interface_cast模版拿到IMediaPlayer
  3. 然后走到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::setDataSourcegetMediaPlayerService(),代碼如下:

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 }
  1. binder = sm->getService(String16("media.player"))拿到名為“media.player”的binder
  2. sMediaPlayerService = 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();
}

  1. 這個函數(shù)就是啟動了多媒體服務(wù)/相機服務(wù)/音頻服務(wù)
  2. 重點在于 MediaPlayerService::instantiate();

我們來看看: /frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}
  1. 這里看得很明顯啦, 把名為"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)鍵點在于:

  1. 拿到了MediaPlayerService::Client類的指針☆☆☆;
  2. 加入到了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ù)中做了以下幾件事情:

  1. MediaPlayerFactory::getPlayerType中拿到對應(yīng)的playerType(當前7.0版本都是穿件的Nuplayer(這個后面會講到));
  2. 根據(jù)playerType, 通過setDataSource_pre函數(shù)拿到MediaPlayerBase;
  3. 通過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;
  1. sFactoryMap中拿到事先在MediaPlayerService的構(gòu)造函數(shù)中注冊好的mediaplayerFactory(有NuPlayerTestPlayer(此Player一般情況下只在userdebug和eng版本中猜有可能走到));
  2. 根據(jù)參數(shù)對每個mediaplayerFactory打分, 獲取到最高打分和對應(yīng)的mediaplayerFactory(絕大多數(shù)情況下都是NuPlayerFactory);
  3. 如果到最后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}
  1. 根據(jù)傳入的playerType, 按照MediaPlayerService::Client::creatPlayer -> MediaPlayerFactory::createPlayer -> NuplayerFactory::createPlayer的順序, 拿到NuPlayerDriver;
  2. 拿到名為"media.extractor"的binder代理, 可與MediaExtractor那邊打交道了;
  3. 拿到名為"media.codec"的binder代理, 可與MediaCodec那邊打交道了(IOMX接口);
  4. 最后拿到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 }
  1. 首先判斷Nuplayer那邊的的setDataSource完成的狀態(tài)(關(guān)于這一塊, 我將在下一篇具體說明), 如果狀態(tài)正確, 往下走;
  2. 在構(gòu)造函數(shù)中, mRetransmitEndpointValid = false. 如果上層沒有調(diào)用setRetransmitEndpoint方法的話, 是不會走這個Flow流程的;
  3. 最后將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 }
  1. mState = STATE_SET_DATASOURCE_PENDING, 改變當前Nuplayer的狀態(tài)(在構(gòu)造函數(shù)中 : mState = STATE_IDLE)
  2. 調(diào)用NuplayersetDataSourceAsync異步方法 (這個方法在NuPlayer.cpp中, 做完后會notify NuplayerDriver, 并改變mState狀態(tài));
  3. 返回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 } 
  1. new了兩個AMessage, 一個msg給自己的; 一個notify給上面的NuplayerDriver;
  2. 調(diào)用source(GenericSource)的setDataSource方法;
  3. 拋出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> &notify,
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 }
  1. Nuplayer::Source這個AHandle中, 拿到Nuplayer傳遞下來的AMessage : notify;
  2. 各種各樣的, 與audio/video播放相關(guān)的參數(shù)相關(guān)的字段初始化;
  3. mBufferingMonitor字段拿到BufferingMonitor(用來監(jiān)視buffer的狀態(tài));
  4. 調(diào)用resetDataSource重置DataSource相關(guān)的字段參數(shù)(一般是對一些字段進行置0NULL的工作)
  5. RegisterDefaultSniffers(在Android 8.0 code中, RegisterDefaultSniffers()這個方法是在GenericSource::prepare中拋出消息, 并且在接收方法onPrepareAsync中的initFromDataSource中的MediaExteactor::CreatCreateFromService中調(diào)用的).
    這個方法的作用是將各類Extractor的sniff的函數(shù)指針放入到一個名為gSniffersList中(在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 }
  1. 調(diào)用resetDataSource()做清理工作;
  2. 獲取①mFd文件描述符 ②offset文件偏移量 ③length文件長度;
  3. 返回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        }
  1. 做一系列的Check工作;
  2. GenericSource的指針轉(zhuǎn)換為其父類的指針NuPlayer::Source并賦值給mSource;
  3. 調(diào)用NuPlayerDrivernotifySetDataSourceCompleted方法;

最后回到NuplayerDrivernotifySetDataSourceCompleted中, 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 }
  1. check當前的裝備是否為STATE_SET_DATASOURCE_PENDING;
  2. mAsyncResult = err(這里值為OK);
  3. mState被賦值為STATE_UNPREPARED;
  4. mCondition廣播下釋放拿到的鎖(意味著異步的setDataSource已完成);

3.1.7 小結(jié)

至此, 我們完成了setDataSource的工作. 具體的:

  1. 獲得了StageFright平臺之上的Player : NuPlayer;
  2. 獲得了與MediaExtractor通信的權(quán)利;
  3. 獲得了與MediaCodec通信的權(quán)利;
  4. 獲得了Source : GenericSource(本地文件);
  5. 將各種Extractor注冊(在8.0中, 移到了prepare中進行);

3.2 prepare

在完成setDataSource后, 我們需要進行PrePare了. 按照慣例, 在本篇中我們還是從Java這一端開始吧. 時序圖如下所示:

Prepare.png

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;
  1. 調(diào)用JNI層的_prepare;
  2. 獲取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 }
  1. 拿到指向MediaPlayer的強指針;
  2. 重新嘗試與Display Surface的鏈接;
  3. 執(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 }
  1. 調(diào)用prepareAsync_l()方法使用異步的prepare;
  2. mSignal.wait(mLock)等待拿到鎖;
  3. 返回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}
  1. mPlayer->setAudioStreamType(mStreamType)設(shè)置player的Audio的Type為: "AUDIO_STREAM_MUSIC";
  2. 進入MEDIA_PLAYER_PREPARING狀態(tài);
  3. 調(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 }
  1. 獲得指向NuplayerDriver的指針p;
  2. 獲取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的值)

  1. 調(diào)用mPlayer(在這里是NuPlayer)的prepareAsync;
  2. 返回OKMediaPlayerService::Clientprepare;

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();
}
  1. 在起播階段, mLooper的值為NULL, 所以會啟動一個ALooper, 他的名字為"generic", 并啟動它;
  2. GenericSource這個AHandler注冊其中("generic"ALooper);
  3. 創(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();
    }
}
  1. 在啟播階段, 字段mDataSourceNULL的, 所以我們進入if (mDataSource == NULL);
  2. 因為我們一直都是走的本地File播放流程, 所以我們會走else中的mDataSource = new FileSource(mFd, mOffset, mLength).;
  3. mIsStreaming = (mIsWidevine || mCachedSource != NULL);這句是為了流媒體緩存buffer用的, 當buffer沒有緩沖完畢的時候, 需要等待緩沖完畢后再notify prepare complete;
  4. status_t err = initFromDataSource()初始化媒體文件對應(yīng)的extractor;
  5. if (mVideoTrack.mSource != NULL), 如果有Vedio, 則convertMetaDataToMessage將MetaData信息裝載到Message中, 并notifyVideoSizeChanged(這個里面主要是將vidioTrackwidthheight通知到NuplayerDriver顯示寬高變化)
  6. notifyFlagsChanged通知Flag變化了;
  7. 最后, 走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 }
  1. 當我們播放的是本地文件的時候, 走的流程是extractor = MediaExtractor::Create(mDataSource, mimeType.isEmpty() ? NULL : mimeType.string());在這里, 我們拿到了文件對應(yīng)的Extractor;
  2. mFileMeta = extractor->getMetaData();獲得本地媒體文件的MetaData信息放入字段mFileMeta中;
  3. mDurationUs字段獲取到媒體文件的播放時間;
  4. if (!mIsWidevine)中, 對video/wvm類媒體文件, 置flag mIsWidevinetrue;
  5. 拿到一系列的與播放相關(guān)的數(shù)據(jù)
  6. if (!strncasecmp(mime, "audio/", 6))開始, 開始對mAudioTrackmVideoTrack進行處理(具體是獲取audio和video的), 并將指向這些track的指針放入mSources中;
  7. 返回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}
  1. startSources中, 會有這么兩句(mAudioTrack.mSource != NULL && mAudioTrack.mSource->start() != OK)if (mVideoTrack.mSource != NULL && mVideoTrack.mSource->start() != OK). 其實在這里調(diào)用trackmediaSourcestart只是為了不讓在異步prepare中已經(jīng)緩存的buffer不在strat的時候浪費掉而已, 并沒有做什么實質(zhì)性的事情;
  2. 如果是mIsStreaming, 啟動一個名為GSBMonitor的線程Looper來監(jiān)視buffer
  3. notifyPrepared通知NuPlayerDriverPrepare階段完成;

3.3 start

setDataSourcePrepare階段結(jié)束后, 我們要正式開始播放啦. 按照慣例, 我們來看看從java一端到Nuplayer一端的時序圖, 如下所示:

start.png

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;
  1. baseStart();中, 我們設(shè)置了AudioPlaybackConfiguration(audio播放相關(guān));
  2. stayAwake(true);的作用是讓屏幕始終處于喚醒狀態(tài);
  3. 調(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 }
  1. 前面的流程都很熟悉了, 這里直接看到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 }
  1. 在正常啟播階段, 我們會走else if ( (mPlayer != 0)這個流程;
  2. 308 - 311 Lines是遠程調(diào)用服務(wù)端來設(shè)置聲音相關(guān)的東西, 并將當前狀態(tài): mCurrentState 設(shè)置為: MEDIA_PLAYER_STARTED;
  3. 調(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 }
  1. 拿到指向NupplayerDriver的指針;
  2. 調(diào)用NupplayerDriversetLooping方法;
  3. 返回NupplayerDriverstart()方法的返回結(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 }
  1. 在啟播階段, 我們走的是case STATE_PREPARED:. 然后執(zhí)行mPlayer->start();開始到Nuplayer中執(zhí)行邏輯;
  2. 那么剩下的還有其他狀態(tài), 在此簡單說明下:

2.1 STATE_UNPREPARED, 在prepare階段, 執(zhí)行prepare_l;
2.2 STATE_PAUSED代表播放器點了pause鍵位, 依然從pause處開始執(zhí)行strat();
2.3STATE_RUNNING代表播放器正在播放, 如果達到了end os stream, 則seek到0, 重新開始播放;

  1. 最后, 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        }
  1. 在啟播階段mStarted字段為false, 我們走onStart();分支;
  2. 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的東西, 在本篇不細講.

  1. 執(zhí)行GenericSource中的start()方法: 用extractor去讀取A/V buffer, 并組裝成ABuffer放入一個List中, 為后面的解碼階段做準備;
  2. 設(shè)置streamTypevideoFormat
  3. mRendererLooper->setName("NuPlayerRenderer");創(chuàng)建一個名為NuPlayerRendererALooper(起一個新線程, 處理A/V Snyc問題)
  4. status_t err = mRenderer->setPlaybackSettings設(shè)置RendererPlayback設(shè)置;
  5. float rate = getFrameRate();獲得幀率;
  6. postScanSources();創(chuàng)建decoder, 處理解碼相關(guān)工作;

4 總結(jié)

本篇講了啟播過程中, 從java到native的最基本的三個步驟. 分別是setDataSource; preparestart.

  1. setDataSource是用來獲得原始數(shù)據(jù)的;
  2. prepare是用來建立extractor用來解析媒體文件, 進行A/V分離的;
  3. start是用來將分離出來的audiovideo數(shù)據(jù)組裝成一幀, 向decoder輸出解碼, 最后由renderer做A/V Sync, 然后交給顯示系統(tǒng)顯示的.

因為時間和水平問題, 文中難免會有各種各樣的錯誤, 歡迎各位看官提出意見和建議, 我將第一時間糾正, 感謝~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Media Playback Android多媒體框架包涵了對播放多種通用媒體的類型的支持,所以你可以很容易的集成...
    VegetableAD閱讀 977評論 0 0
  • 最近項目需要用到 MediaPlayer + SurfaceView 來播放短視頻,回憶了一下之前的做法寫了一下,...
    Arnold_J閱讀 1,365評論 0 5
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 7,295評論 0 17
  • 本人初學Android,最近做了一個實現(xiàn)安卓簡單音樂播放功能的播放器,收獲不少,于是便記錄下來自己的思路與知識總結(jié)...
    落日柳風閱讀 19,437評論 2 41
  • 紅燈 綠燈 行走 暫停 轉(zhuǎn)彎 一個路口 另一個路口 再一個路口 紅花 藍天 白襯衫 微笑臉 擦肩而過
    淺淺而歸閱讀 131評論 0 0

友情鏈接更多精彩內(nèi)容