一个最简单的Media Playback过程如下,仅通过五个步骤就可以完成播放。

String url = "http://........"; // your URL hereMediaPlayer mediaPlayer = new MediaPlayer();mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mediaPlayer.setDataSource(url);mediaPlayer.prepare(); // might take long! (for buffering, etc)mediaPlayer.start();

初始化MediaPlayer

创建MediaPlayer对象,并完成Java层和Native层的初始化。

frameworks/base/media/java/android/media/MediaPlayer.javapublic class MediaPlayer implements SubtitleController.Listener{......    static {        System.loadLibrary("media_jni");        native_init(); // native 初始化    }    ......    public MediaPlayer() {        Looper looper;        if ((looper = Looper.myLooper()) != null) {            mEventHandler = new EventHandler(this, looper);        } else if ((looper = Looper.getMainLooper()) != null) {            mEventHandler = new EventHandler(this, looper);        } else {            mEventHandler = null;        }        mTimeProvider = new TimeProvider(this);        mOutOfBandSubtitleTracks = new Vector();        mOpenSubtitleSources = new Vector();        mInbandSubtitleTracks = new SubtitleTrack[0];        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);        mAppOps = IAppOpsService.Stub.asInterface(b);        /* Native setup requires a weak reference to our object.         * It's easier to create it here than in C++.         */        native_setup(new WeakReference(this)); // native层setup    }    .......

可以看到,MediaPlayer通过静态初始化的方式对native层进行初始化。MediaPlayer对媒体控制的动作都是在底层实现的,所以必须在MediaPlayer创建时优先初始化底层。

frameworks/base/media/jni/android_media_MediaPlayer.cppstatic voidandroid_media_MediaPlayer_native_init(JNIEnv *env)                                                                                                                                                          {    jclass clazz;    clazz = env->FindClass("android/media/MediaPlayer");    if (clazz == NULL) {        return;    }    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");    if (fields.context == NULL) {        return;    }    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");    if (fields.post_event == NULL) {        return;    }    fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");    if (fields.surface_texture == NULL) {        return;    }    clazz = env->FindClass("android/net/ProxyInfo");    if (clazz == NULL) {        return;    }    fields.proxyConfigGetHost =        env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");    fields.proxyConfigGetPort =        env->GetMethodID(clazz, "getPort", "()I");    fields.proxyConfigGetExclusionList =        env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");}

native_init()获取Java类MediaPlayer和ProxyInfo中的一些方法和变量。

  • 将native MediaPlayer的对象保存到Java MediaPlayer的mNativeContext中。
  • 获取Java MediaPlayer的postEventFromNative()方法,用于发送事件。
  • 将native video surface保存到Java MediaPlayer的mNativeSurfaceTexture中。
  • 获取Java ProxyInfo的getHost()、getPort()、getExclusionListAsString()方法。

之后在MediaPlayer的构造函数中初始化一些媒体播放的必要元素,像消息、时间、字幕、权限等。最后调用native_setup()对native层进行设置。

frameworks/base/media/jni/android_media_MediaPlayer.cppstatic void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this){    ALOGV("native_setup");    sp mp = new MediaPlayer();    if (mp == NULL) {        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");        return;    }        // create new listener and give it to MediaPlayer    sp listener = new JNIMediaPlayerListener(env, thiz, weak_this);    mp->setListener(listener);    // Stow our new C++ MediaPlayer in an opaque field in the Java object.    setMediaPlayer(env, thiz, mp);}

Setup过程完成一下工作。

  • 创建Native MeidaPlayer对象。
  • 创建Listener上报Native事件,这里调用postEventFromNative()。
  • 存储Native MediaPlayer对象到mNativeContext中,以便Java层使用。

设置声音系统

Android系统将定义了不同的Audio Stream类型,用来适用不同的应用场景。像STREAM_VOICE_CALL用于打电话场景,STREAM_SYSTEM用于系统声音,STREAM_RING用于电话铃声,STREAM_MUSIC用于播放音乐等。每种Stream类型都是独立的,有自己的状态和音量。在使用Player前需要选择一个Stream类型,音乐播放和视频播放时基本上采用的都是STREAM_MUSIC。
Java层的setAudioStreamType()直接调用的Native的setAudioStreamType(),而Native的这个方法也很简单,就是赋值给mStreamType。

frameworks/av/media/libmedia/mediaplayer.cppstatus_t MediaPlayer::setAudioStreamType(audio_stream_type_t type){    ALOGV("MediaPlayer::setAudioStreamType");    Mutex::Autolock _l(mLock);    if (mStreamType == type) return NO_ERROR;    if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED |                MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) {         // Can't change the stream type after prepare        ALOGE("setAudioStream called in state %d", mCurrentState);        return INVALID_OPERATION;    }       // cache    mStreamType = type;    return OK; }

mStreamType会在Player Prepare过程中被设置生效,这也就是为什么必须在Player开始前设置Stream类型。

frameworks/av/media/libmedia/mediaplayer.cppstatus_t MediaPlayer::prepareAsync_l(){    if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {        mPlayer->setAudioStreamType(mStreamType);        if (mAudioAttributesParcel != NULL) {            mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);        }        mCurrentState = MEDIA_PLAYER_PREPARING;        return mPlayer->prepareAsync();    }    ALOGE("prepareAsync called in state %d", mCurrentState);    return INVALID_OPERATION;}

上面的过程又远程调用了MediaPlayerService的setAudioStreamType(),真正的Audio系统设置就发生在这里。Audio系统比较复杂,这里不再展开。

frameworks/av/media/libmediaplayerservice/MediaPlayerService.cppstatus_t MediaPlayerService::Client::setAudioStreamType(audio_stream_type_t type){    ALOGV("[%d] setAudioStreamType(%d)", mConnId, type);    // TODO: for hardware output, call player instead    Mutex::Autolock l(mLock);    if (mAudioOutput != 0) mAudioOutput->setAudioStreamType(type);                                                                                                                                              return NO_ERROR;}

初始化底层播放器

Android MediaPlayer使用”工厂模式“来管理底层播放器。只要按MediaPlayerFactory
定义接口进行实现,并注册到sFactoryMap中,就可以调用自己的Player。原生Android提供了四个可用的播放器:NuPlayer、StagefrightPlayer、SonivoxPlayer和TestPlayer,厂商也可以增加客制化的播放器。在播放前需要根据评分来选择使用播放器,这个选择过程就是在setDataSource()中完成的。
Java层MediaPlayer实现了多个setDataSource(),但最终都是通过两个JNI接口调用到native中。

  • nativeSetDataSource:处理网络流媒体。
  • _setDataSource:处理本地的媒体文件。
frameworks/base/media/jni/android_media_MediaPlayer.cppstatic void android_media_MediaPlayer_setDataSourceAndHeaders(        JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,        jobjectArray keys, jobjectArray values) {                                                                                                                                                           sp mp = getMediaPlayer(env, thiz);......    sp httpService;    if (httpServiceBinderObj != NULL) {        sp binder = ibinderForJavaObject(env, httpServiceBinderObj);        httpService = interface_cast(binder);    }    status_t opStatus =        mp->setDataSource(                httpService,                pathStr,                headersVector.size() > 0? &headersVector : NULL);......}static voidandroid_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length){    sp mp = getMediaPlayer(env, thiz);......    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);    ALOGV("setDataSourceFD: fd %d", fd);    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );}

JNI中又调用Native层MediaPlayer的setDataSource()方法,最终的远程调用到MediaPlayerService的setDataSource()。

frameworks/av/media/libmediaplayerservice/MediaPlayerService.cppstatus_t MediaPlayerService::Client::setDataSource(        const sp &httpService,        const char *url,        const KeyedVector *headers){......if (strncmp(url, "content://", 10) == 0) {     // get a filedescriptor for the content Uri and    // pass it to the setDataSource(fd) method    String16 url16(url);    int fd = android::openContentProviderFile(url16);    if (fd < 0)     {        ALOGE("Couldn't open fd for %s", url);        return UNKNOWN_ERROR;    }    setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus    close(fd);    return mStatus;} else {    player_type playerType = MediaPlayerFactory::getPlayerType(this, url);    .....    sp p = setDataSource_pre(playerType);    if (p == NULL) {        return NO_INIT;    }    setDataSource_post(p, p->setDataSource(httpService, url, headers));    return mStatus;}}status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length){    ......    player_type playerType = MediaPlayerFactory::getPlayerType(this,                                                               fd,                                                               offset,                                                               length);    sp p = setDataSource_pre(playerType);    if (p == NULL) {        return NO_INIT;    }    // now set data source    setDataSource_post(p, p->setDataSource(fd, offset, length));    return mStatus;}status_t MediaPlayerService::Client::setDataSource(        const sp &source) {    // create the right type of player    player_type playerType = MediaPlayerFactory::getPlayerType(this, source);    sp p = setDataSource_pre(playerType);    if (p == NULL) {        return NO_INIT;    }    // now set data source    setDataSource_post(p, p->setDataSource(source));    return mStatus;}

setDataSource()虽然具体实现不同,但最主要的步骤都是相同的。

  • getPlayerType:选择合适的底层播放器。
  • setDataSource_pre:创建底层播放器。
  • setDataSource_post:调用底层播放器的setDataSource(),设置数据源。

播放器的选择过程也比较简单,就是根据参数寻找评分最高的播放器。每个播放器都会实现scoreFactory()方法,为自己打分。分值最高的播放器有最高优先级。

frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp#define GET_PLAYER_TYPE_IMPL(a...)                      \    Mutex::Autolock lock_(&sLock);                      \                                                        \    player_type ret = STAGEFRIGHT_PLAYER;               \    float bestScore = 0.0;                              \                                                        \    for (size_t i = 0; i < sFactoryMap.size(); ++i) {   \                                                        \        IFactory* v = sFactoryMap.valueAt(i);           \        float thisScore;                                \        CHECK(v != NULL);                               \        thisScore = v->scoreFactory(a, bestScore);      \        if (thisScore > bestScore) {                    \            ret = sFactoryMap.keyAt(i);                 \            bestScore = thisScore;                      \        }                                               \    }                                                   \                                                        \    if (0.0 == bestScore) {                             \        ret = getDefaultPlayerType();                   \    }                                                   \                                                        \    return ret;player_type MediaPlayerFactory::getPlayerType(const sp& client,                                              const char* url) {    GET_PLAYER_TYPE_IMPL(client, url);}player_type MediaPlayerFactory::getPlayerType(const sp& client,                                              int fd,                                               int64_t offset,                                              int64_t length) {    GET_PLAYER_TYPE_IMPL(client, fd, offset, length);}player_type MediaPlayerFactory::getPlayerType(const sp& client,                                              const sp &source) {    GET_PLAYER_TYPE_IMPL(client, source);}

以StagefrightPlayer为例,看一下scoreFactory()的实现。

frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cppclass StagefrightPlayerFactory :    public MediaPlayerFactory::IFactory {  public:    virtual float scoreFactory(const sp& /*client*/,                               int fd,                               int64_t offset,                               int64_t length,                               float /*curScore*/) {        if (legacyDrm()) {            sp source = new FileSource(dup(fd), offset, length);            String8 mimeType;            float confidence;            if (SniffWVM(source, &mimeType, &confidence, NULL /* format */)) {                return 1.0;            }        }        if (getDefaultPlayerType() == STAGEFRIGHT_PLAYER) {            char buf[20];            lseek(fd, offset, SEEK_SET);            read(fd, buf, sizeof(buf));            lseek(fd, offset, SEEK_SET);            uint32_t ident = *((uint32_t*)buf);            // Ogg vorbis?            if (ident == 0x5367674f) // 'OggS'                return 1.0;        }        return 0.0;    }    virtual float scoreFactory(const sp& /*client*/,                               const char* url,                               float /*curScore*/) {        if (legacyDrm() && !strncasecmp("widevine://", url, 11)) {            return 1.0;        }        return 0.0;    }......

可以看到,如果媒体为Widevine DRM格式,StagefrightPlayer会给出更高的打分。Ogg vorbis也会返回高分。

准备Player和播放

选择好底层播放器后,使用prepare()做播放前的准备,之后使用start()开始播发。这两个操作的调用流程类似,最终都是调用到底层播放器的相关接口。这里不分析播放器的具体操作,每个播放器的操作都不同,需要针对播放器做分析。调用流程如下图所示。
Android MediaPlayer Playback流程分析_第1张图片

更多相关文章

  1. Android Dialog触摸对话框外部让其消失的实现方法
  2. android 获取当前时间的方法
  3. Android ScrollView嵌套ViewPager不显示和出现空白部分 解决方法
  4. Android查询所有联系人和根据号码查询联系人方法
  5. Android系统信息查看方法
  6. Android裁剪图像实现方法示例
  7. Android中常用的bitmap处理方法

随机推荐

  1. NumPy进阶修炼|基础
  2. Python办公自动化|批量合并PDF,拿来就用
  3. 手把手教你使用Matplotlib|实战
  4. 动画:用动画给面试官解释 TCP 三次握手过
  5. 开始你的第一个机器学习项目|文末送书
  6. 玩转数据处理120题重制说明与下载
  7. Python两招轻松爬取美团评论
  8. 小鹿大学四年来给读者的一点碎碎念
  9. 朋友问他是否该跳槽了?我是这么跟他说的
  10. Python机器学习之旅|手把手带你探索IRIS数