1

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

String url = "http://........"; // your URL here
MediaPlayer 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.java

public 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<SubtitleTrack>();
        mOpenSubtitleSources = new Vector<InputStream>();
        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<MediaPlayer>(this)); // native层setup
    }
    .......

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

frameworks/base/media/jni/android_media_MediaPlayer.cpp

static void
android_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.cpp

static void 
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    ALOGV("native_setup");
    sp<MediaPlayer> mp = new MediaPlayer();
    if (mp == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }    

    // create new listener and give it to MediaPlayer
    sp<JNIMediaPlayerListener> 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.cpp

status_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.cpp

status_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.cpp

status_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.cpp

static void 
android_media_MediaPlayer_setDataSourceAndHeaders(
        JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,
        jobjectArray keys, jobjectArray values) {                                                                                                                                                           

sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
......
    sp<IMediaHTTPService> httpService;
    if (httpServiceBinderObj != NULL) {
        sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
        httpService = interface_cast<IMediaHTTPService>(binder);
    }

    status_t opStatus =
        mp->setDataSource(
                httpService,
                pathStr,
                headersVector.size() > 0? &headersVector : NULL);
......
}

static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    sp<MediaPlayer> 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.cpp

status_t MediaPlayerService::Client::setDataSource(
        const sp<IMediaHTTPService> &httpService,
        const char *url,
        const KeyedVector<String8, String8> *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<MediaPlayerBase> 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<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;
}

status_t MediaPlayerService::Client::setDataSource(
        const sp<IStreamSource> &source) {
    // create the right type of player
    player_type playerType = MediaPlayerFactory::getPlayerType(this, source);
    sp<MediaPlayerBase> 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<IMediaPlayer>& client,
                                              const char* url) {
    GET_PLAYER_TYPE_IMPL(client, url);
}

player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
                                              int fd, 
                                              int64_t offset,
                                              int64_t length) {
    GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
}

player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
                                              const sp<IStreamSource> &source) {
    GET_PLAYER_TYPE_IMPL(client, source);
}

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

frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp

class StagefrightPlayerFactory :
    public MediaPlayerFactory::IFactory {
  public:
    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
                               int fd,
                               int64_t offset,
                               int64_t length,
                               float /*curScore*/) {
        if (legacyDrm()) {
            sp<DataSource> 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<IMediaPlayer>& /*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()开始播发。这两个操作的调用流程类似,最终都是调用到底层播放器的相关接口。这里不分析播放器的具体操作,每个播放器的操作都不同,需要针对播放器做分析。调用流程如下图所示。
图片描述


戈壁老王
143 声望64 粉丝

做为一个不称职的老年码农,一直疏忽整理笔记,开博记录一下,用来丰富老年生活,