Android多媒体框架--01:从开机到MediaServer的注册过程

"本文转载自:[yanbixing123]的Android MultiMedia框架完全解析 - 从开机到MediaServer的注册过程"

1.概述

  MediaPlayer是Android中的一个多媒体播放类,通过它可以控制音视频流或本地音视频资源的播放过程。下面是MediaPlayer播放音视频的大致流程:

MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
    @Override
    public void onCompletion(MediaPlayer mp){
        mediaPlayer.release();
        mediaPlayer = null;
    }
});

mediaPlayer.setDataSource("abc.mp3"); 
mediaPlayer.setDisplay();
mediaPlayer.prepare();
mediaPlayer.start();

我们就按照这个流程来一步一步分析整个播放流程。

2.MediaServer的启动流程

  Android是基于Linux内核的,而在Linux中,启动的第一个进程就是init进程,其他进程都是init进程的子进程。在init进程的启动过程中,会解析Linux的配置脚本init.rc文件。根据init.rc文件的内容,init进程会装载Android的文件系统,创建系统目录,启动Android系统重要的守护进程,这些进程包括USB守护进程,adb守护进程,vold守护进程等。

  同时,init进程还会启动MediaServer(多媒体服务),ServiceManager(Binder服务管家)等重要服务。init进程还会孵化出Zygote进程,Zygote进程是Android系统的首个Java进程,Zygote是所有Java进程的父进程,如图所示:

image.png

(1)在Android 10.0中,mediaserver服务的启动脚本在system/core/rootdir/init.zygote64.rc文件中:

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

(2)其中命令:onrestart restart media,会执行frameworks/av/media/mediaserver/mediaserver.rc文件中的mediaserver的启动脚本:

service media /system/bin/mediaserver
    class main
    user media
    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
    ioprio rt 4
    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

(3)mediaserver启动后,会把media相关的一些服务添加到ServiceManager中,其中就有MediaPlayerService,ResourceManagerService等等。mediaserver可以理解为所有有关Media相关的服务器,它为app所提供服务。

  那么再来看看MediaPlayerService在Android Framework中所处的位置:

image.png

这个mediaserver的核心文件就是frameworks/av/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());
    AIcu_initializeIcuOrDie();
    MediaPlayerService::instantiate();
    ResourceManagerService::instantiate();
    registerExtensions();
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

其中:MediaPlayerService::instantiate(),就是MediaPlayerService的初始化代码,它位于frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中:

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}

向ServiceManager中注册了一个实名Binder:media.player。

3.MediaPlayer的创建过程

  在App中,我们执行了:MediaPlayer mediaPlayer = new MediaPlayer();

  那么就来看看这个过程,在 frameworks/base/media/java/android/MediaPlayer.java中:

    public MediaPlayer() {
        super(new AudioAttributes.Builder().build(),
                AudioPlaybackConfiguration.PLAYER_TYPE_JAM_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);
        mOpenSubtitleSources = new Vector<InputStream>();

        /* 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));

        baseRegisterPlayer();
    }

在它的构造函数中,将java中创建的MediaPlayer通过弱引用传递给JNI层,而在它的构造函数之前,MediaPlayer类有一段静态代码块,加载了media_jni.so库,用于JNI相关的初始化:

    static {
        System.loadLibrary("media_jni");
        native_init();
    }

    private static native final void native_init();

这里调用了本地方法native_init(),它的实现位于 frameworks/base/media/jni/android_media_MediaPlayer.cpp中,但是,并不能单纯通过名字直接找到这个native_init()方法,它通过一个结构体数组做了映射:

static const JNINativeMethod gMethods[] = {
    {
        "nativeSetDataSource",
        "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
        "[Ljava/lang/String;)V",
        (void *)android_media_MediaPlayer_setDataSourceAndHeaders
    },

    {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
    {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
...
    {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},
    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
...
};

这个结构体数组几乎映射了所有MediaPlayer类的方法,以后有类似的方法可以通过这个数组找到,下面继续看native_init()方法:

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

    env->DeleteLocalRef(clazz);

    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;");

    env->DeleteLocalRef(clazz);
......
}

这里native_init()函数就执行完毕了,它设置了一些java层的方法。然后就是MediaServer的构造函数中的native_setup()函数,它同样位于这个文件中:

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

这里创建了一个C++层的MediaPlayer,还设置了一些Listener回调,这个模式和Android的Looper机制差不多,都是java层一个Looper,C++层也有一个Looper。

通过上面的步骤,就发现,从java层想要生成一个MediaPlayer,最终会在C++层中生成一个MediaPlayer()类,至此,MediaPlayer的构造就完成了。

4.SetDataSource的过程

  在Android App中:mediaPlayer.setDataSource("abc.mp3");
  继续在frameworks/base/media/jni/android_media_MediaPlayer.cpp文件中通过那个映射数组找到对应的方法(我们以播放本地文件为例),所以对应的方法就是:

static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    if (fileDescriptor == NULL) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return;
    }
    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." );
}

首先获取C++层中刚创建的MediaPlayer,然后调用process_media_player_call()来执行MediaPlayer的setDataSource函数并检查返回状态(process_media_player_call()函数这里就不展开了,可以自己看,主要是做了一些错误和异常检测工作,然后notify出去相应的错误状态)。

具体MediaPlayer的setDataSource函数做了什么工作,我们下节再分析,这里只需要知道,java层的setDataSource方法最终调用了C++层MediaPlayer类的setDataSource方法就行。

5.SetDisplay的过程

  在Android App中:mediaPlayer.setDisplay();
  在frameworks/base/media/java/android/MediaPlayer.java中:

    public void setDisplay(SurfaceHolder sh) {
        mSurfaceHolder = sh;
        Surface surface;
        if (sh != null) {
            surface = sh.getSurface();
        } else {
            surface = null;
        }
        _setVideoSurface(surface);
        updateSurfaceScreenOn();
    }

private native void _setVideoSurface(Surface surface);

最终会调用到jni层的android_media_MediaPlayer_setVideoSurface()函数,同样在frameworks/base/media/jni/android_media_MediaPlayer.cpp中:

static void
android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
{
    setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
}

static void
setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        if (mediaPlayerMustBeAlive) {
            jniThrowException(env, "java/lang/IllegalStateException", NULL);
        }
        return;
    }

    decVideoSurfaceRef(env, thiz);

    sp<IGraphicBufferProducer> new_st;
    if (jsurface) {//获取java层的surface
        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
        if (surface != NULL) {
            new_st = surface->getIGraphicBufferProducer();//获取IGraphicBufuferProducer
            if (new_st == NULL) {
                jniThrowException(env, "java/lang/IllegalArgumentException",
                    "The surface does not have a binding SurfaceTexture!");
                return;
            }
            new_st->incStrong((void*)decVideoSurfaceRef);
        } else {
            jniThrowException(env, "java/lang/IllegalArgumentException",
                    "The surface has been released");
            return;
        }
    }

    env->SetLongField(thiz, fields.surface_texture, (jlong)new_st.get());

    // This will fail if the media player has not been initialized yet. This
    // can be the case if setDisplay() on MediaPlayer.java has been called
    // before setDataSource(). The redundant call to setVideoSurfaceTexture()
    // in prepare/prepareAsync covers for this case.
    mp->setVideoSurfaceTexture(new_st);
}

这里主要是对图像现实的surface进行保存,然后将旧的IGraphicBufferProducer强引用减一,再获得新的IGraphicBufferProducer,最终会调用C++层的MediaPlayer的setVideoSurfaceTexture将它设置进去。

  IGraphicBufferProducer是SurfaceFlinger中的内容,一个UI完全现实到display的过程,SurfaceFlinger扮演着重要的角色,但是它的职责是"Flinger",即把所有应用程序最终的绘图结果进行“混合叠图”,然后统一绘制到物理屏幕上。在这个绘图过程中,需要BufferQueue的参与,它是每个应用程序“一对一”的辅导老师,知道着UI程序的“画板申请”,“作画流程”等一系列细节,同时BufferQueue也是IGraphicBufferProducer的服务端,app通过IGraphicBufferProducer来与BufferQueue沟通。

6.prepare的过程

  在Android App中:mediaPlayer.prepare();
  在frameworks/base/media/java/android/MediaPlayer.java中:

    public void prepare() throws IOException, IllegalStateException {
        _prepare();
        scanInternalSubtitleTracks();

        // DrmInfo, if any, has been resolved by now.
        synchronized (mDrmLock) {
            mDrmInfoResolved = true;
        }
    }

    private native void _prepare() throws IOException, IllegalStateException;

最终会调用到jni层的android_media_MediaPlayer_prepare()函数,同样在frameworks/base/media/jni/android_media_MediaPlayer.cpp中:

static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    // Handle the case where the display surface was set before the mp was
    // initialized. We try again to make it stick.
    sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
    mp->setVideoSurfaceTexture(st);

    process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
}

最终会调用C++层的MediaPlayer的prepare()。

7.start的过程

  在Android App中:mediaPlayer.start();
  在frameworks/base/media/java/android/MediaPlayer.java中:

    public void start() throws IllegalStateException {
        //FIXME use lambda to pass startImpl to superclass
        final int delay = getStartDelayMs();
        if (delay == 0) {
            startImpl();
        } else {
            new Thread() {
                public void run() {
                    try {
                        Thread.sleep(delay);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    baseSetStartDelayMs(0);
                    try {
                        startImpl();
                    } catch (IllegalStateException e) {
                        // fail silently for a state exception when it is happening after
                        // a delayed start, as the player state could have changed between the
                        // call to start() and the execution of startImpl()
                    }
                }
            }.start();
        }
    }

    private void startImpl() {
        baseStart();
        stayAwake(true);
        _start();
    }

    private native void _start() throws IllegalStateException;

最终会调用到jni层的android_media_MediaPlayer_start()函数,同样在frameworks/base/media/jni/android_media_MediaPlayer.cpp中:

static void
android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
{
    ALOGV("start");
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }
    process_media_player_call( env, thiz, mp->start(), NULL, NULL );
}

最终会调用C++层的MediaPlayer的start()。

8.小结

  本节只是分析了APP层与C++层MediaPlayer的构建过程,让大家对于这个播放流程有个大致的认识和理解。

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