Media Player简介

前言

接着《Media FrameWork简介》,我们在本篇将迎来具体的MediaPlayer的讲解。我们知道,一个最简单的播放器有播放、暂停、停止三个功能。那么,本篇将结合google官网给出的MediaPlayer状态图以及mediaplayer提供的基本接口,讲清楚这三个基本功能。

1. MediaPlayer概述

在了解一个新事物的时候,我个人习惯首先了解它的用法(它是用来干什么的)。在此基础上,再去了解它的整体结构(它是由什么东东组成的)。最后,结合这两者,详细分析,以求充分理解这个事物。
好,那么各位看官也明白了,我将先介绍MediaPlayer的用法,再介绍MediaPlayer的架构,最后结合两者,讲MediaPlayer是怎么播放起来的。

2. MediaPlayer的使用

刚刚开始的时候,我确实想直接给出MediaPlayer最简单用法的示例。比如:

// 初始化播放器  
    private void initMediaplayer() {  
        if (mMediaPlayer != null) {  
            mMediaPlayer.reset();  
            mMediaPlayer.release();  
            mMediaPlayer = null;  
        }  
        mMediaPlayer = new MediaPlayer();  
    }  
  
    // 销毁音乐  
    private void destoryMusic() {  
        if (mMediaPlayer != null) {  
            mMediaPlayer.stop();  
            mMediaPlayer.release();  
            mMediaPlayer = null;  
        }  
    }  
  
    // 暂停播放  
    private void pauseMusic() {  
        if (mMediaPlayer.isPlaying()) {// 正在播放  
            mMediaPlayer.pause();// 暂停  
        } else {// 没有播放  
            mMediaPlayer.start();  
        }  
    }  
  
    // 停止播放  
    private void stopMusic() {  
        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {  
            mMediaPlayer.stop();  
        }  
    }  

   // 播放  
    private void playMusic() {  
        try {  
            /* 重置多媒体 */  
            mMediaPlayer.reset();  
            /* 读取媒体文件 */  
            mMediaPlayer.setDataSource(mFileName);  
            /* 准备播放 */  
            mMediaPlayer.prepare();  
            /* 开始播放 */  
            mMediaPlayer.start();  
            /* 是否单曲循环 */  
            mMediaPlayer.setLooping(false);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  

但是,在某个漆黑的角落里,我突然发现了一张MediaPlayer状态机的图,如图1所示(Android Developers官网制作),简直如获至宝。特在此贴出来,作为讲解如何使用MediaPlayer的图示 ^ _ ^


图1 mediaplayer_state_diagram.gif

2.1 初始化播放器

if (mMediaPlayer != null){  
    mMediaPlayer.reset();  
    mMediaPlayer.release();  
    mMediaPlayer = null;  
}  
mMediaPlayer = new MediaPlayer();
  1. 如果有MediaPlayer的实例。那么,先reset -> Idle状态;再release -> End状态,以确保再次播放时,MediaPlayer处于正确的状态。
  2. 如果没有创建过MediaPlayer的实例,new 一个MediaPlayer实例。

2.2 播放

/* 重置多媒体 */  
mMediaPlayer.reset();  
/* 读取mp3文件 */  
mMediaPlayer.setDataSource(mFileName);  
/* 准备播放 */  
mMediaPlayer.prepare();  
/* 开始播放 */  
mMediaPlayer.start();  
/* 是否单曲循环 */  
mMediaPlayer.setLooping(false); 
  1. reset -> Idle状态;
  2. setDataSource("文件路径(本地或者网络)") -> Initialized状态;
  3. prepare -> Prepared状态;
  4. start -> Started状态;
  5. setLooping(false) && onCompletion invoked on OnCompletionListener -> PlaybackCompleted状态;

其他四个状态都一目了然。第五个状态是在1.没有设置单曲循环;2.播放完成状态通知,这两个条件下,才会到达PlaybackCompleted状态的。可以看到,如果调用strat,则又回到Started状态,并重头开始播放。

2.3 暂停

if (mMediaPlayer.isPlaying()) {// 正在播放  
    mMediaPlayer.pause();// 暂停  
    } else {// 没有播放  
    mMediaPlayer.start();  
}  
  1. 如果播放器正在播放,那么调用pause -> Paused状态
  2. 如果播放器此时正好在Paused状态,那么调用start -> Started状态

2.4 停止

if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {  
    mMediaPlayer.stop();  
}  

如果有MediaPlayer播放的实例且播放状态是isPlaying,调用stop -> Stopped状态。

2.5 销毁

if (mMediaPlayer != null) {  
    mMediaPlayer.stop();  
    mMediaPlayer.release();  
    mMediaPlayer = null;  
}  

如果有MediaPlayer的实例,那么先stop -> Stopped状态; 然后release -> End状态。

最后小结下,当我们要使用MediaPlayer的时候,一般情况下要经过以下步骤

  1. reset:重置Mediaplayer各种参数,进入Idle状态;
  2. setDataSource:带入文件的地址(有可能本地、有可能网络),获得Nuplayer的实例,进入Initialized状态;
  3. prepare:获取播放文件的各种信息(播放时长、比特率、编码信息等);
  4. start:根据prepare过程获得的信息,初始化解码器,初始化渲染器,操控输入、输出Buffer进出解码器等,完成播放工作。

当然,还有stopresetpauserelease等,相对于启动播放过程不是特别重要,留在以后再来补充,今后的篇章中,将着重讲2——3——4步骤。

3. MeidaPlayer三部曲

在讲三部曲之前,首先得获得MediaPlayer的实例。让我们来看看frameworks/base/media/java/android/media/MediaPlayer.java的部分代码吧。

首先是在静态代码段加载media_jni动态库(libmedia_jni.so),并且调用本地方法native_init()

594    static {
595        System.loadLibrary("media_jni");
596        native_init();
597    }

来看一下native_init做了什么事情(路径frameworks/base/media/jni/android_media_MediaPlayer.cpp)。代码如下:

816// This function gets some field IDs, which in turn causes class initialization.
817// It is called from a static block in MediaPlayer, which won't run until the
818// first time an instance of this class is used.
819static void
820android_media_MediaPlayer_native_init(JNIEnv *env)
821{
822    jclass clazz;
823
824    clazz = env->FindClass("android/media/MediaPlayer");
825    if (clazz == NULL) {
826        return;
827    }
828
829    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
830    if (fields.context == NULL) {
831        return;
832    }
833
834    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
835                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
836    if (fields.post_event == NULL) {
837        return;
838    }
839
840    fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
841    if (fields.surface_texture == NULL) {
842        return;
843    }
844
845    env->DeleteLocalRef(clazz);
846
847    clazz = env->FindClass("android/net/ProxyInfo");
848    if (clazz == NULL) {
849        return;
850    }
851
852    fields.proxyConfigGetHost =
853        env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
854
855    fields.proxyConfigGetPort =
856        env->GetMethodID(clazz, "getPort", "()I");
857
858    fields.proxyConfigGetExclusionList =
859        env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
860
861    env->DeleteLocalRef(clazz);
862
863    gPlaybackParamsFields.init(env);
864    gSyncParamsFields.init(env);
865}

简单描述一下。

  1. 得到MediaPlayer对象。根据此对象拿到mNativeContextpostEventFromNativemNativeSurfaceTexture两个字段的ID,拿到方法postEventFromNative的ID,之后销毁;
  2. 得到ProxyInfo对象。根据此对象拿到getHostgetPortgetExclusionListAsString三个方法的ID,之后销毁;
  3. 初始化Playback的一些属性字段参数,初始化初始化Sync的一些属性字段参数;

好,现在初始化完成了,各就各位了,我们开始创建MediaPlayer 吧。“小二!上Code!”
在上层的MediaPlayer.JavaMediaPlayer的构造函数中,有一个非常关键的点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的实例,如果没有创建成功,抛出Out of memory的异常,并返回;
  2. 创建一个监听对象,并将此监听对象的注册到刚刚获得的MediaPlayer的实例中;
  3. 将创建的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. 根据传入的path,区分是本地文件还是网络播放文件;
  2. 如果是网络文件的话,走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. 调用process_media_player_call,其中有个重要的参数是mp->setDataSource(fd, offset, length)的返回值。

我们先来看看process_media_player_call

172static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
173{
174    if (exception == NULL) {  // Don't throw exception. Instead, send an event.
175        if (opStatus != (status_t) OK) {
176            sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
177            if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
178        }
179    } else {  // Throw exception!
180        if ( opStatus == (status_t) INVALID_OPERATION ) {
181            jniThrowException(env, "java/lang/IllegalStateException", NULL);
182        } else if ( opStatus == (status_t) BAD_VALUE ) {
183            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
184        } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
185            jniThrowException(env, "java/lang/SecurityException", NULL);
186        } else if ( opStatus != (status_t) OK ) {
187            if (strlen(message) > 230) {
188               // if the message is too long, don't bother displaying the status code
189               jniThrowException( env, exception, message);
190            } else {
191               char msg[256];
192                // append the status code to the message
193               sprintf(msg, "%s: status=0x%X", message, opStatus);
194               jniThrowException( env, exception, msg);
195            }
196        }
197    }
198}
  1. 如果传入的exception为空(显然我们不走这个分支);
    并且在mediaPlayer.cpp中的setDataSource失败了,拿到之前在native_setup中创建的MediaPlayer
    并且如果可以拿到的话,向java层通知MEDIA_ERROR
  2. 正常情况下,我们一般走的是有传入exception的分支;
    在此分支中,我们需要根据在MediaPlayer.cppsetDataSource返回的status状态来抛出对应的异常;
    一般情况下,如果我们获取到的status == OK的话,在这里是不做任何事情的。

3.1.3 MediaPlayer.cpp

229 status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
230 {
231    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
232    status_t err = UNKNOWN_ERROR;
233    const sp<IMediaPlayerService> service(getMediaPlayerService());
234    if (service != 0) {
235        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
236        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
237            (NO_ERROR != player->setDataSource(fd, offset, length))) {
238            player.clear();
239        }
240        err = attachNewPlayer(player);
241    }
242    return err;
243 }
  1. 获取了名为“media.player”的binder服务(BpBinder),然后通过interface_cast拿到IMediaPlayerService
  2. 调用IMediaPlayerServiceBpMediaPlayerService::create方法,通过remote->transact()远程调用服务端,并用interface_cast模版拿到IMediaPlayer
  3. 然后走到IMediaPlayer的类BpMediaPlayer::setDataSource方法,通过remote->transact()远程调用服务端,并返回服务端setDataSource方法返回的状态;

好,流程到这里,相信有些童鞋已经一头雾水了(要了解的透彻,智能指针/JNI/Binder知识缺一不可,如不清楚,还请问问度娘,我以后有时间也会写的)。我们现在只要看看关键的地方就行了,毕竟我们的目的是了解MediaPlayer的框架嘛。

3.1.3.1 关键点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 关键点2

在frameworks/base/media/mediaserver/(多媒体服务的守护进程)其中只有一个源文件main_mediaserver.cpp

int main(int argc __unused, char **argv __unused)
{
    signal(SIGPIPE, SIG_IGN);

    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm(defaultServiceManager());
    ALOGI("ServiceManager: %p", sm.get());
    InitializeIcuOrDie();
    MediaPlayerService::instantiate();
    ResourceManagerService::instantiate();
    registerExtensions();
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

  1. 这个函数就是启动了多媒体服务/相机服务/音频服务
  2. 重点在于 MediaPlayerService::instantiate();

我们来看看: /frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}
  1. 这里看得很明显啦, 把名为"media.player"服务注册到Service Manager中, 这样我们在关键点1中就可以拿到这个服务啦.

3.1.3.3 关键点3

接下来看代理端调用的service->create(this, mAudioSessionId)在本地端MediaPlayerService::creat是如何实现的. 代码如下:

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
        audio_session_t audioSessionId)
{
    pid_t pid = IPCThreadState::self()->getCallingPid();
    int32_t connId = android_atomic_inc(&mNextConnId);

    sp<Client> c = new Client(
            this, pid, connId, client, audioSessionId,
            IPCThreadState::self()->getCallingUid());

    ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
         IPCThreadState::self()->getCallingUid());

    wp<Client> w = c;
    {
        Mutex::Autolock lock(mLock);
        mClients.add(w);
    }
    return c;
}

关键点在于:

  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;
}

简单来说, 在这个函数中做了以下几件事情:

  1. MediaPlayerFactory::getPlayerType中拿到对应的playerType(当前7.0版本都是穿件的Nuplayer(这个后面会讲到));
  2. 根据playerType, 通过setDataSource_pre函数拿到MediaPlayerBase;
  3. 通过setDataSource_post函数传入MediaPlayerBase对象, 继续完成setDataSource的工作;

3.1.4.1 MediaPlayerFactory.cpp

现在来看看MediaPlayerFactory::getPlayerType方法:

109 player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
110                                              int fd,
111                                              int64_t offset,
112                                              int64_t length) {
113    GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
114 }

80#define GET_PLAYER_TYPE_IMPL(a...)                      \
81    Mutex::Autolock lock_(&sLock);                      \
82                                                        \
83    player_type ret = STAGEFRIGHT_PLAYER;               \
84    float bestScore = 0.0;                              \
85                                                        \
86    for (size_t i = 0; i < sFactoryMap.size(); ++i) {   \
87                                                        \
88        IFactory* v = sFactoryMap.valueAt(i);           \
89        float thisScore;                                \
90        CHECK(v != NULL);                               \
91        thisScore = v->scoreFactory(a, bestScore);      \
92        if (thisScore > bestScore) {                    \
93            ret = sFactoryMap.keyAt(i);                 \
94            bestScore = thisScore;                      \
95        }                                               \
96    }                                                   \
97                                                        \
98    if (0.0 == bestScore) {                             \
99        ret = getDefaultPlayerType();                   \
100   }                                                   \
101                                                       \
102    return ret;
  1. sFactoryMap中拿到事先在MediaPlayerService的构造函数中注册好的mediaplayerFactory(有NuPlayerTestPlayer(此Player一般情况下只在userdebug和eng版本中猜有可能走到));
  2. 根据参数对每个mediaplayerFactory打分, 获取到最高打分和对应的mediaplayerFactory(绝大多数情况下都是NuPlayerFactory);
  3. 如果到最后bestScore还是0, 那么使用默认的mediaplayerFactory(NuplayerFactory).

3.1.4.2 setDataSource_pre

好,我们现在拿到了playerType, 可以进行setDataSource_pre了. 代码如下:

674sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
675        player_type playerType)
676{
677    ALOGV("player type = %d", playerType);
678
679    // create the right type of player
680    sp<MediaPlayerBase> p = createPlayer(playerType);
681    if (p == NULL) {
682        return p;
683    }
684
685    sp<IServiceManager> sm = defaultServiceManager();
686    sp<IBinder> binder = sm->getService(String16("media.extractor"));
687    mExtractorDeathListener = new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH);
688    binder->linkToDeath(mExtractorDeathListener);
689
690    binder = sm->getService(String16("media.codec"));
691    mCodecDeathListener = new ServiceDeathNotifier(binder, p, MEDIACODEC_PROCESS_DEATH);
692    binder->linkToDeath(mCodecDeathListener);
693
694    if (!p->hardwareOutput()) {
695        Mutex::Autolock l(mLock);
696        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
697                mPid, mAudioAttributes);
698        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
699    }
700
701    return p;
702}
  1. 根据传入的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完成的状态(关于这一块, 我将在下一篇具体说明), 如果状态正确, 往下走;
  2. 在构造函数中, mRetransmitEndpointValid = false. 如果上层没有调用setRetransmitEndpoint方法的话, 是不会走这个Flow流程的;
  3. 最后将NuplayerDriver放入MediaPlayerService::Client类的mPlayer字段中, 以便后面调用;

3.1.5 NuplayerDriver.cpp

接下来, 我们来看看NuplayerDriver这边的setDataSource干了些啥?代码如下:

100 status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
101    ALOGV("setDataSource(%p) file(%d)", this, fd);
102    Mutex::Autolock autoLock(mLock);
103
104    if (mState != STATE_IDLE) {
105        return INVALID_OPERATION;
106    }
107
108    mState = STATE_SET_DATASOURCE_PENDING;
109
110    mPlayer->setDataSourceAsync(fd, offset, length);
111
112    while (mState == STATE_SET_DATASOURCE_PENDING) {
113        mCondition.wait(mLock);
114    }
115
116    return mAsyncResult;
117 }
  1. mState = STATE_SET_DATASOURCE_PENDING, 改变当前Nuplayer的状态(在构造函数中 : mState = STATE_IDLE)
  2. 调用NuplayersetDataSourceAsync异步方法 (这个方法在NuPlayer.cpp中, 做完后会notify NuplayerDriver, 并改变mState状态);
  3. 返回mAsyncResult;

3.1.6 Nuplayer.cpp

终于, 我们进军到了Nuplayer中, 发现了之前讲过的AMessage了, 让我们一起来看看Code吧 :

279 void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
280    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
281
282    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
283
284    sp<GenericSource> source =
285            new GenericSource(notify, mUIDValid, mUID);
286
287    status_t err = source->setDataSource(fd, offset, length);
288
289    if (err != OK) {
290        ALOGE("Failed to set data source!");
291        source = NULL;
292    }
293
294    msg->setObject("source", source);
295    msg->post();
296 } 
  1. new了两个AMessage, 一个msg给自己的; 一个notify给上面的NuplayerDriver;
  2. 调用source(GenericSource)的setDataSource方法;
  3. 抛出msg, 在本类的onMessageReceive中获取处理;

3.1.6.1 GenericSource构造函数

接下来, 看看new GenericSource的时候所调用的构造函数. 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播放相关的参数相关的字段初始化;
  3. mBufferingMonitor字段拿到BufferingMonitor(用来监视buffer的状态);
  4. 调用resetDataSource重置DataSource相关的字段参数(一般是对一些字段进行置0NULL的工作)
  5. RegisterDefaultSniffers(在Android 8.0 code中, RegisterDefaultSniffers()这个方法是在GenericSource::prepare中抛出消息, 并且在接收方法onPrepareAsync中的initFromDataSource中的MediaExteactor::CreatCreateFromService中调用的).
    这个方法的作用是将各类Extractor的sniff的函数指针放入到一个名为gSniffersList中(在setDataSource流程之后, 会依次遍历这些注册其中的sniff函数进行打分来判断一个文件应该由那种Extractor来解封装).

3.1.6.2 GenericSource::setDataSource

119 status_t NuPlayer::GenericSource::setDataSource(
120        int fd, int64_t offset, int64_t length) {
121    resetDataSource();
122
123    mFd = dup(fd);
124    mOffset = offset;
125    mLength = length;
126
127    // delay data source creation to prepareAsync() to avoid blocking
128    // the calling thread in setDataSource for any significant time.
129    return OK;
130 }
  1. 调用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的指针转换为其父类的指针NuPlayer::Source并赋值给mSource;
  3. 调用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 小结

至此, 我们完成了setDataSource的工作. 具体的:

  1. 获得了StageFright平台之上的Player : NuPlayer;
  2. 获得了与MediaExtractor通信的权利;
  3. 获得了与MediaCodec通信的权利;
  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. 调用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. 执行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. 调用prepareAsync_l()方法使用异步的prepare;
  2. mSignal.wait(mLock)等待拿到锁;
  3. 返回mPrepareStatus;

所以, 这里我们虽然调用了prepareAsync_l()方法去异步完成prepare操作. 但在之后的Flow中, 还是在等待拿到锁, 所以本处讲解的prepare依然是同步操作.

※※※ 接下来看看prepareAsync_l() ※※※

244status_t MediaPlayer::prepareAsync_l()
245{
246    if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
247        if (mAudioAttributesParcel != NULL) {
248            mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
249        } else {
250            mPlayer->setAudioStreamType(mStreamType);
251        }
252        mCurrentState = MEDIA_PLAYER_PREPARING;
253        return mPlayer->prepareAsync();
254    }
255    ALOGE("prepareAsync called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());
256    return INVALID_OPERATION;
257}
  1. mPlayer->setAudioStreamType(mStreamType)设置player的Audio的Type为: "AUDIO_STREAM_MUSIC";
  2. 进入MEDIA_PLAYER_PREPARING状态;
  3. 调用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中调用notify的时候, 改变了mState的值)

  1. 调用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;
        }

调用了mSource(GenericSource)的prepareAsync就结束了;

3.2.6.3 NuPlayer::GenericSource::prepareAsync

那么继续刨根问底, 来看看GenericSource::prepareAsync干了什么, Code如下:

void NuPlayer::GenericSource::prepareAsync() {
    if (mLooper == NULL) {
        mLooper = new ALooper;
        mLooper->setName("generic");
        mLooper->start();

        mLooper->registerHandler(this);
    }

    sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this);
    msg->post();
}
  1. 在起播阶段, mLooper的值为NULL, 所以会启动一个ALooper, 他的名字为"generic", 并启动它;
  2. GenericSource这个AHandler注册其中("generic"ALooper);
  3. 创建一个AMessage: msg, post出去;

3.2.6.4 NuPlayer::GenericSource::onMessageReceived

      case kWhatPrepareAsync:
      {
          onPrepareAsync();
          break;
      }

直接调用onPrepareAsync();

3.2.6.5 NuPlayer::GenericSource::onPrepareAsync

咱们终于找过真正在prepare阶段"做事情的人"了!Code如下:

void NuPlayer::GenericSource::onPrepareAsync() {
    // delayed data source creation
    if (mDataSource == NULL) {
        // set to false first, if the extractor
        // comes back as secure, set it to true then.
        mIsSecure = false;

        if (!mUri.empty()) {
            const char* uri = mUri.c_str();
            String8 contentType;
            mIsWidevine = !strncasecmp(uri, "widevine://", 11);

            if (!strncasecmp("http://", uri, 7)
                    || !strncasecmp("https://", uri, 8)
                    || mIsWidevine) {
                mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);
                if (mHttpSource == NULL) {
                    ALOGE("Failed to create http source!");
                    notifyPreparedAndCleanup(UNKNOWN_ERROR);
                    return;
                }
            }

            mDataSource = DataSource::CreateFromURI(
                   mHTTPService, uri, &mUriHeaders, &contentType,
                   static_cast<HTTPBase *>(mHttpSource.get()));
        } else {
            mIsWidevine = false;

            mDataSource = new FileSource(mFd, mOffset, mLength);
            mFd = -1;
        }

        if (mDataSource == NULL) {
            ALOGE("Failed to create data source!");
            notifyPreparedAndCleanup(UNKNOWN_ERROR);
            return;
        }
    }

    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
        mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
    }

    // For widevine or other cached streaming cases, we need to wait for
    // enough buffering before reporting prepared.
    // Note that even when URL doesn't start with widevine://, mIsWidevine
    // could still be set to true later, if the streaming or file source
    // is sniffed to be widevine. We don't want to buffer for file source
    // in that case, so must check the flag now.
    mIsStreaming = (mIsWidevine || mCachedSource != NULL);

    // init extractor from data source
    status_t err = initFromDataSource();

    if (err != OK) {
        ALOGE("Failed to init from data source!");
        notifyPreparedAndCleanup(err);
        return;
    }

    if (mVideoTrack.mSource != NULL) {
        sp<MetaData> meta = doGetFormatMeta(false /* audio */);
        sp<AMessage> msg = new AMessage;
        err = convertMetaDataToMessage(meta, &msg);
        if(err != OK) {
            notifyPreparedAndCleanup(err);
            return;
        }
        notifyVideoSizeChanged(msg);
    }

    notifyFlagsChanged(
            (mIsSecure ? FLAG_SECURE : 0)
            | (mDecryptHandle != NULL ? FLAG_PROTECTED : 0)
            | FLAG_CAN_PAUSE
            | FLAG_CAN_SEEK_BACKWARD
            | FLAG_CAN_SEEK_FORWARD
            | FLAG_CAN_SEEK);

    if (mIsSecure) {
        // secure decoders must be instantiated before starting widevine source
        sp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, this);
        notifyInstantiateSecureDecoders(reply);
    } else {
        finishPrepareAsync();
    }
}
  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()初始化媒体文件对应的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());在这里, 我们拿到了文件对应的Extractor;
  2. mFileMeta = extractor->getMetaData();获得本地媒体文件的MetaData信息放入字段mFileMeta中;
  3. mDurationUs字段获取到媒体文件的播放时间;
  4. if (!mIsWidevine)中, 对video/wvm类媒体文件, 置flag mIsWidevinetrue;
  5. 拿到一系列的与播放相关的数据
  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). 其实在这里调用trackmediaSourcestart只是为了不让在异步prepare中已经缓存的buffer不在strat的时候浪费掉而已, 并没有做什么实质性的事情;
  2. 如果是mIsStreaming, 启动一个名为GSBMonitor的线程Looper来监视buffer
  3. notifyPrepared通知NuPlayerDriverPrepare阶段完成;

3.3 start

setDataSourcePrepare阶段结束后, 我们要正式开始播放啦. 按照惯例, 我们来看看从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();中, 我们设置了AudioPlaybackConfiguration(audio播放相关);
  2. stayAwake(true);的作用是让屏幕始终处于唤醒状态;
  3. 调用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是远程调用服务端来设置声音相关的东西, 并将当前状态: mCurrentState 设置为: MEDIA_PLAYER_STARTED;
  3. 调用服务端的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. 调用NupplayerDriversetLooping方法;
  3. 返回NupplayerDriverstart()方法的返回结果;

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:. 然后执行mPlayer->start();开始到Nuplayer中执行逻辑;
  2. 那么剩下的还有其他状态, 在此简单说明下:

2.1 STATE_UNPREPARED, 在prepare阶段, 执行prepare_l;
2.2 STATE_PAUSED代表播放器点了pause键位, 依然从pause处开始执行strat();
2.3STATE_RUNNING代表播放器正在播放, 如果达到了end os stream, 则seek到0, 重新开始播放;

  1. 最后, mState设置为STATE_RUNNING;

3.3.6 Nuplayer.cpp

337 void NuPlayer::start() {
338    (new AMessage(kWhatStart, this))->post();
339 }

714        case kWhatStart:
715        {
716            ALOGV("kWhatStart");
717            if (mStarted) {
718                // do not resume yet if the source is still buffering
719                if (!mPausedForBuffering) {
720                    onResume();
721                }
722            } else {
723                onStart();
724            }
725            mPausedByClient = false;
726            break;
727        }
  1. 在启播阶段mStarted字段为false, 我们走onStart();分支;
  2. mPausedByClient字段设置为false, 当上层调用pause方法的时候, 该字段会被设置为true;

3.3.6.1 onStart()

1317 void NuPlayer::onStart(int64_t startPositionUs) {
1318    if (!mSourceStarted) {
1319        mSourceStarted = true;
1320        mSource->start();
1321    }
1322    if (startPositionUs > 0) {
1323        performSeek(startPositionUs);
1324        if (mSource->getFormat(false /* audio */) == NULL) {
1325            return;
1326        }
1327    }
1328
1329    mOffloadAudio = false;
1330    mAudioEOS = false;
1331    mVideoEOS = false;
1332    mStarted = true;
1333    mPaused = false;
1334
1335    uint32_t flags = 0;
1336
1337    if (mSource->isRealTime()) {
1338        flags |= Renderer::FLAG_REAL_TIME;
1339    }
1340
1341    sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */);
1342    sp<MetaData> videoMeta = mSource->getFormatMeta(false /* audio */);
1343    if (audioMeta == NULL && videoMeta == NULL) {
1344        ALOGE("no metadata for either audio or video source");
1345        mSource->stop();
1346        mSourceStarted = false;
1347        notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_MALFORMED);
1348        return;
1349    }
1350    ALOGV_IF(audioMeta == NULL, "no metadata for audio source");  // video only stream
1351
1352    audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
1353    if (mAudioSink != NULL) {
1354        streamType = mAudioSink->getAudioStreamType();
1355    }
1356
1357    sp<AMessage> videoFormat = mSource->getFormat(false /* audio */);
1358
1359    mOffloadAudio =
1360        canOffloadStream(audioMeta, (videoFormat != NULL), mSource->isStreaming(), streamType)
1361                && (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
1362    if (mOffloadAudio) {
1363        flags |= Renderer::FLAG_OFFLOAD_AUDIO;
1364    }
1365
1366    sp<AMessage> notify = new AMessage(kWhatRendererNotify, this);
1367    ++mRendererGeneration;
1368    notify->setInt32("generation", mRendererGeneration);
1369    mRenderer = new Renderer(mAudioSink, notify, flags);
1370    mRendererLooper = new ALooper;
1371    mRendererLooper->setName("NuPlayerRenderer");
1372    mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
1373    mRendererLooper->registerHandler(mRenderer);
1374
1375    status_t err = mRenderer->setPlaybackSettings(mPlaybackSettings);
1376    if (err != OK) {
1377        mSource->stop();
1378        mSourceStarted = false;
1379        notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
1380        return;
1381    }
1382
1383    float rate = getFrameRate();
1384    if (rate > 0) {
1385        mRenderer->setVideoFrameRate(rate);
1386    }
1387
1388    if (mVideoDecoder != NULL) {
1389        mVideoDecoder->setRenderer(mRenderer);
1390    }
1391    if (mAudioDecoder != NULL) {
1392        mAudioDecoder->setRenderer(mRenderer);
1393    }
1394
1395    postScanSources();
1396 }

onStart中, 涉及到了一些decode和renderer的东西, 在本篇不细讲.

  1. 执行GenericSource中的start()方法: 用extractor去读取A/V buffer, 并组装成ABuffer放入一个List中, 为后面的解码阶段做准备;
  2. 设置streamTypevideoFormat
  3. mRendererLooper->setName("NuPlayerRenderer");创建一个名为NuPlayerRendererALooper(起一个新线程, 处理A/V Snyc问题)
  4. status_t err = mRenderer->setPlaybackSettings设置RendererPlayback设置;
  5. float rate = getFrameRate();获得帧率;
  6. postScanSources();创建decoder, 处理解码相关工作;

4 总结

本篇讲了启播过程中, 从java到native的最基本的三个步骤. 分别是setDataSource; preparestart.

  1. setDataSource是用来获得原始数据的;
  2. prepare是用来建立extractor用来解析媒体文件, 进行A/V分离的;
  3. start是用来将分离出来的audiovideo数据组装成一帧, 向decoder输出解码, 最后由renderer做A/V Sync, 然后交给显示系统显示的.

因为时间和水平问题, 文中难免会有各种各样的错误, 欢迎各位看官提出意见和建议, 我将第一时间纠正, 感谢~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • Media Playback Android多媒体框架包涵了对播放多种通用媒体的类型的支持,所以你可以很容易的集成...
    VegetableAD阅读 868评论 0 0
  • 最近项目需要用到 MediaPlayer + SurfaceView 来播放短视频,回忆了一下之前的做法写了一下,...
    Arnold_J阅读 1,195评论 0 5
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,358评论 0 17
  • 本人初学Android,最近做了一个实现安卓简单音乐播放功能的播放器,收获不少,于是便记录下来自己的思路与知识总结...
    落日柳风阅读 19,080评论 2 41
  • 红灯 绿灯 行走 暂停 转弯 一个路口 另一个路口 再一个路口 红花 蓝天 白衬衫 微笑脸 擦肩而过
    浅浅而归阅读 111评论 0 0