MediaRecorder 流程

534 阅读4分钟

MediaRecorder 流程

本文已初始化流程和setAudioSource方法流程为示例,来描述MediaRecorder的工作流程。

一、初始化流程

步骤1、加载jni库,初始化native

该部分代码是静态代码,执行时间在构造方法之前,首先是加载动态库,然后JNI中拿JAVA层的类名和类属性ID为后续JNI访问和操作JAVA做准备,该部分代码不是操作对象,因为构造函数还没有执行到。涉及的类包括:
(1) base\media\java\android\media\MediaRecorder.java
(2) base\media\jni\android_meiia_MediaRecorder.cpp

1.1 加载动态库

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

1.2 native_init

// 该部分代码是静态代码,JNI获取类的属性ID,JNI层以静态的形式存储这些属性ID
// 
                                                                        --------wuchunyuan
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaRecorder, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaRecorder_native_init(JNIEnv *env)
{
    jclass clazz;

    clazz = env->FindClass("android/media/MediaRecorder");
    if (clazz == NULL) {
        return;
    }

    //此处context存放的是mNativeContext的ID,实际mNativeContext赋值在native_setup中,
    //context用于存放mediaRecorder.cpp文件中的MediaRecorde类的对象
    //env->SetLongField(thiz, fields.context, (jlong)recorder.get());
    //JAVA层的MediaRecorder有native层MediaRecorder的指针的值,通过该值C++可以转换回C++中的MediaRecorder对象
                                                                    --------wuchunyuan
    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    if (fields.context == NULL) {
        return;
    }

    fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
    if (fields.surface == NULL) {
        return;
    }

    jclass surface = env->FindClass("android/view/Surface");
    if (surface == NULL) {
        return;
    }

    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    if (fields.post_event == NULL) {
        return;
    }

    clazz = env->FindClass("java/util/ArrayList");
    if (clazz == NULL) {
        return;
    }
    gArrayListFields.add = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");
    gArrayListFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
}
  • 关联知识a:JNI调用JAVA中的方法
Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld2(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorld2_methodID=env->GetMethodID(clazz,"helloWorld2","(java/lang/String;)V");
    if(helloWorld2_methodID==NULL) return;
    const char *msg="hello world";
    jstring jmsg=env->NewStringUTF(msg);
    
    //JNI调用JAVA中的方法,此时是通过反射调用,java中的方法并不需要加native关键字                                       
                                                                  --------wuchunyuan
    env->CallVoidMethod(thiz,helloWorld2_methodID,jmsg);
}
  • 关联知识b: JNI给JAVA属性赋值
env->SetLongField(thiz, fields.context, (jlong)recorder.get());

步骤2、创建MediaRecorder对象

该部分将创建Java层和Native层MediaRecorder对象,且建立与MediaRecorderClient的进程通信。涉及的类包括:
(1) base\media\java\android\media\MediaRecorder.java
(2) base\media\jni\android_meiia_MediaRecorder.cpp

2.1 创建JAVA层MediaRecorder

//创建JAVA层MediaRecorder对象,并将弱引用传递给native_setup
/**
     * Default constructor.
     */
    public MediaRecorder() {
        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;
        }

        mChannelCount = 1;
        String packageName = ActivityThread.currentPackageName();
        
        // 这里将JAVA层MediaRecorder对象的弱引用传递给NATIVE层,
        // 主要用于NATIVE层回调信息给JAVA层
                                                                          --------wuchunyuan
        /* Native setup requires a weak reference to our object.
         * It's easier to create it here than in C++.
         */
        native_setup(new WeakReference<MediaRecorder>(this), packageName,
                ActivityThread.currentOpPackageName());
    }
JAVA MediaRecorder向JNI传递弱的引用用于native回调,比如native调用JAVA静态方法postEventFromNative,方法中带了具体的JAVA对象的弱引用,明确了由哪个对象处理。
//c++
//mObject: JAVA层传递给JNI的MediaRecorder 弱引用
//postEventFromNative: JAVA中的静态方法的方法名
void JNIMediaRecorderListener::notify(int msg, int ext1, int ext2)
{
    ALOGV("JNIMediaRecorderListener::notify");

    JNIEnv *env = AndroidRuntime::getJNIEnv();
    env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, NULL);
}
//JAVA
/**
     * Called from native code when an interesting event happens.  This method
     * just uses the EventHandler system to post the event back to the main app thread.
     * We use a weak reference to the original MediaRecorder object so that the native
     * code is safe from the object disappearing from underneath it.  (This is
     * the cookie passed to native_setup().)
     */
    private static void postEventFromNative(Object mediarecorder_ref,
                                            int what, int arg1, int arg2, Object obj)
    {
        MediaRecorder mr = (MediaRecorder)((WeakReference)mediarecorder_ref).get();
        if (mr == null) {
            return;
        }

        if (mr.mEventHandler != null) {
            Message m = mr.mEventHandler.obtainMessage(what, arg1, arg2, obj);
            mr.mEventHandler.sendMessage(m);
        }
    }

2.2 native_setup

static void
android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
                                         jstring packageName, jstring opPackageName)
{
    ALOGV("setup");

    ScopedUtfChars opPackageNameStr(env, opPackageName);
    //创建C++的MediaRecorder对象                                               --------wuchunyuan
    sp<MediaRecorder> mr = new MediaRecorder(String16(opPackageNameStr.c_str()));
    if (mr == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }
    if (mr->initCheck() != NO_ERROR) {
        jniThrowException(env, "java/lang/RuntimeException", "Unable to initialize media recorder");
        return;
    }

    // create new listener and give it to MediaRecorder
    sp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this);
    mr->setListener(listener);

    // Convert client name jstring to String16
    const char16_t *rawClientName = reinterpret_cast<const char16_t*>(
        env->GetStringChars(packageName, NULL));
    jsize rawClientNameLen = env->GetStringLength(packageName);
    String16 clientName(rawClientName, rawClientNameLen);
    env->ReleaseStringChars(packageName,
                            reinterpret_cast<const jchar*>(rawClientName));

    // pass client package name for permissions tracking
    mr->setClientName(clientName);

    setMediaRecorder(env, thiz, mr);
}
static sp<MediaRecorder> setMediaRecorder(JNIEnv* env, jobject thiz, const sp<MediaRecorder>& recorder)
{
    Mutex::Autolock l(sLock);
    sp<MediaRecorder> old = (MediaRecorder*)env->GetLongField(thiz, fields.context);
    if (recorder.get()) {
        recorder->incStrong(thiz);
    }
    if (old != 0) {
        old->decStrong(thiz);
    }
    //将native层的MediaRecorder对象的指针数值保存在JAVA层的MediaRecorder中
    env->SetLongField(thiz, fields.context, (jlong)recorder.get());
    return old;
}

步骤3、C++ 层MediaRecorder对象创建具体流程

介绍C++ 层MediaRecorder类的创建

#include <media/mediarecorder.h>
sp<MediaRecorder> mr = new MediaRecorder(String16(opPackageNameStr.c_str()));

BpBinder没有重载queryLocalInterface,BnBinder重载queryLocalInterface,返回this

//实现asInterface函数
    android::sp<ICameraClient> ICameraClient::asInterface(
            const android::sp<android::IBinder>& obj)
    {
        android::sp<ICameraClient> intr; 
        if (obj != NULL) {
            intr = static_cast<ICameraClient*>(
                //queryLocalInterface是在IBinder中定义的,默认返回NULL,但在BBinder的子类BnInterface中,重载了该方法,返回this,而BpBinder没有重载,使用IBinder的默认实现,返回NULL
                obj->queryLocalInterface( 
                        ICameraClient::descriptor).get()); 
            if (intr == NULL) {
                //构建INTERFACE的Bp端代理对象
                intr = new BpCameraClient(obj);
            }
        }
        return intr;
    }

参考文章

1、Anroid MediaRecorder 架构详解