【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 初始化

【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 初始化,第1张

MediaRecorder 用于录制音频和视频,录制控制基于一个简单的状态机。下面是典型的使用 camera2 API 录制,需要使用到的 MediaRecorder API。

MediaRecorder 录像流程系列文章:

【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 配置
【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 开始录制

    /**
     * 初始化MediaRecorder
     */
    private void initMediaRecorder(){
        mMediaRecorder = new MediaRecorder();
    }

    /**
     * 配置录制视频相关数据
     */
    private void configMediaRecorder(){
        File file = new File(getExternalCacheDir(),"demo.mp4");
        if (file.exists()){
            file.delete();
        }
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置音频来源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);//设置视频来源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//设置输出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//设置音频编码格式
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);//设置视频编码格式
        mMediaRecorder.setVideoEncodingBitRate(1920 * 1080 * 30 * 0.2);//设置比特率
        mMediaRecorder.setVideoFrameRate(30);//设置帧数
        mMediaRecorder.setVideoSize(1920, 1080);
        mMediaRecorder.setOutputFile(file.getAbsolutePath());
        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 开始录制视频
     */
    private void startRecorder(){
        mMediaRecorder.start();
    }

    /**
     * 停止录制视频
     */
    private void stopRecorder(){
        mMediaRecorder.stop();
        mMediaRecorder.reset();
    }
    
    ...
    mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    List<Surface> surfaces = new ArrayList<>();

    // Set up Surface for the camera preview
    Surface previewSurface = new Surface(texture);
    surfaces.add(previewSurface);
    mPreviewBuilder.addTarget(previewSurface);

    // Set up Surface for the MediaRecorder
    Surface recorderSurface = mMediaRecorder.getSurface();
    surfaces.add(recorderSurface);
    mPreviewBuilder.addTarget(recorderSurface);

    // Start a capture session
    // Once the session starts, we can update the UI and start recording
    mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {

        @Override
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
            ...   
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
            ...
        }
    }, mBackgroundHandler);

MediaRecorder 类在第一次初始化的时候会调用它的 static 块,其内部调用 System.loadLibrary(…) 加载了 libmedia_jni.so,然后调用 native_init() 进行 native 初始化。

frameworks/base/media/java/android/media/MediaRecorder.java

public class MediaRecorder implements AudioRouting,
                                      AudioRecordingMonitor,
                                      AudioRecordingMonitorClient,
                                      MicrophoneDirection
{
    static {
        System.loadLibrary("media_jni");
        native_init();
    }
    ......
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    private static native final void native_init();
    ......
}

libmedia_jni.so 这个共享库编译加载了下面这些 cpp 源文件。其中就包含 android_media_MediaRecorder.cpp,不难猜测出它的内部实现了 MediaRecorder.java 里面 jni 方法。

frameworks/base/media/jni/Android.bp

cc_library_shared {
    name: "libmedia_jni",

    srcs: [
        "android_media_ImageWriter.cpp",
        "android_media_ImageReader.cpp",
        "android_media_MediaCrypto.cpp",
        "android_media_MediaCodec.cpp",
        "android_media_MediaCodecList.cpp",
        "android_media_MediaDataSource.cpp",
        "android_media_MediaDescrambler.cpp",
        "android_media_MediaDrm.cpp",
        "android_media_MediaExtractor.cpp",
        "android_media_MediaHTTPConnection.cpp",
        "android_media_MediaMetadataRetriever.cpp",
        "android_media_MediaMuxer.cpp",
        "android_media_MediaPlayer.cpp",
        "android_media_MediaProfiles.cpp",
        "android_media_MediaRecorder.cpp",
        "android_media_MediaScanner.cpp",
        "android_media_MediaSync.cpp",
        "android_media_ResampleInputStream.cpp",
        "android_media_Streams.cpp",
        "android_media_SyncParams.cpp",
        "android_mtp_MtpDatabase.cpp",
        "android_mtp_MtpDevice.cpp",
        "android_mtp_MtpServer.cpp",
    ],
    ......
}
......

native_init(…) 对应的方法 native 实现是 android_media_MediaRecorder_native_init(…)。

此方法内部首先通过 jni API 获取 MediaRecorder Java 类对应的 jclass,接着获取 mNativeContext、mSurface 字段相应的 jfieldID,然后获取 postEventFromNative 对应的 jmethodID,它们最终缓存到 fields 全局变量中备用,最后将 ArrayList 的 add 方法以及其 jclass 缓存到全局变量 gArrayListFields 中。

frameworks/base/media/jni/android_media_MediaRecorder.cpp

struct fields_t {
    jfieldID    context;
    jfieldID    surface;

    jmethodID   post_event;
};
static fields_t fields;

struct ArrayListFields {
    jmethodID add;
    jclass classId;
};
static ArrayListFields gArrayListFields;
......
static void
android_media_MediaRecorder_native_init(JNIEnv *env)
{
    jclass clazz;

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

    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));
}
......
static const JNINativeMethod gMethods[] = {
    {"setCamera",            "(Landroid/hardware/Camera;)V",    (void *)android_media_MediaRecorder_setCamera},
    {"setVideoSource",       "(I)V",                            (void *)android_media_MediaRecorder_setVideoSource},
    {"setAudioSource",       "(I)V",                            (void *)android_media_MediaRecorder_setAudioSource},
    {"setOutputFormat",      "(I)V",                            (void *)android_media_MediaRecorder_setOutputFormat},
    {"setVideoEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setVideoEncoder},
    {"setAudioEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setAudioEncoder},
    {"setParameter",         "(Ljava/lang/String;)V",           (void *)android_media_MediaRecorder_setParameter},
    {"_setOutputFile",       "(Ljava/io/FileDescriptor;)V",     (void *)android_media_MediaRecorder_setOutputFileFD},
    {"_setNextOutputFile",   "(Ljava/io/FileDescriptor;)V",     (void *)android_media_MediaRecorder_setNextOutputFileFD},
    {"setVideoSize",         "(II)V",                           (void *)android_media_MediaRecorder_setVideoSize},
    {"setVideoFrameRate",    "(I)V",                            (void *)android_media_MediaRecorder_setVideoFrameRate},
    {"setMaxDuration",       "(I)V",                            (void *)android_media_MediaRecorder_setMaxDuration},
    {"setMaxFileSize",       "(J)V",                            (void *)android_media_MediaRecorder_setMaxFileSize},
    {"_prepare",             "()V",                             (void *)android_media_MediaRecorder_prepare},
    {"getSurface",           "()Landroid/view/Surface;",        (void *)android_media_MediaRecorder_getSurface},
    {"getMaxAmplitude",      "()I",                             (void *)android_media_MediaRecorder_native_getMaxAmplitude},
    {"start",                "()V",                             (void *)android_media_MediaRecorder_start},
    {"stop",                 "()V",                             (void *)android_media_MediaRecorder_stop},
    {"pause",                "()V",                             (void *)android_media_MediaRecorder_pause},
    {"resume",               "()V",                             (void *)android_media_MediaRecorder_resume},
    {"native_reset",         "()V",                             (void *)android_media_MediaRecorder_native_reset},
    {"release",              "()V",                             (void *)android_media_MediaRecorder_release},
    {"native_init",          "()V",                             (void *)android_media_MediaRecorder_native_init},
    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
                                                                (void *)android_media_MediaRecorder_native_setup},
    {"native_finalize",      "()V",                             (void *)android_media_MediaRecorder_native_finalize},
    {"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },

    {"native_getMetrics",    "()Landroid/os/PersistableBundle;", (void *)android_media_MediaRecorder_native_getMetrics},

    {"native_setInputDevice", "(I)Z",                           (void *)android_media_MediaRecorder_setInputDevice},
    {"native_getRoutedDeviceId", "()I",                         (void *)android_media_MediaRecorder_getRoutedDeviceId},
    {"native_enableDeviceCallback", "(Z)V",                     (void *)android_media_MediaRecorder_enableDeviceCallback},

    {"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones},
    {"native_getPortId", "()I", (void *)android_media_MediaRecord_getPortId},
    {"native_setPreferredMicrophoneDirection", "(I)I",
            (void *)android_media_MediaRecord_setPreferredMicrophoneDirection},
    {"native_setPreferredMicrophoneFieldDimension", "(F)I",
            (void *)android_media_MediaRecord_setPreferredMicrophoneFieldDimension},
};
......

现在来分析 MediaRecorder 构造函数干了些什么?

  1. 调用 Looper 类方法 myLooper() 获取 Looper 对象,如果调用线程和相应的 Looper 做了绑定,此处就会得到这个 Looper,接下来就调用 EventHandler 的构造器进行 EventHandler 初始化;
  2. 如果调用线程是主线程,那么调用 Looper 类方法 getMainLooper() 必然不为空,那么使用主线程 Looper 对象和 MediaRecorder 对象作为实参初始化 EventHandler 对象;
  3. 如果不存在 Looper 对象,那么就将 mEventHandler 这个 field 置为空;
  4. 调用 ActivityThread 类方法 currentPackageName() 获取包名,调用 ActivityThread 类方法 currentOpPackageName() 获取 *** 作包名(用于 android.app. AppOpsManager 的包名,这样应用的 *** 作管理器的 uid 验证将与该名称一起工作),然后将它们作为入参调用 native_setup(…) 进一步调到 JNI Native 初始化。

frameworks/base/media/java/android/media/MediaRecorder.java

public class MediaRecorder implements AudioRouting,
                                      AudioRecordingMonitor,
                                      AudioRecordingMonitorClient,
                                      MicrophoneDirection
{
    ......
     /**
     * 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();
        /* 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());
    }
    ......
}
  1. 创建 MediaRecorder native 对象。如果构造函数调用失败返回空,向 Java 层抛出 RuntimeException(内存不足)。调用 MediaRecorder 对象 initCheck() 检查构造期间是否发生错误,如果存在错误则向 Java 层抛出 RuntimeException(无法初始化 media recorder)。
  2. 创建 JNIMediaRecorderListener 对象,并给 MediaRecorder native 对象这个 listener;
  3. 转化客户端名称 jstring 到 String16,调用 MediaRecorder native 对象方法 setClientName(…) 传递客户端包名以进行权限跟踪;
  4. 调用 setMediaRecorder(…) 将 MediaRecorder native 对象保存在 MediaRecorder Java 对象中(实际是将 MediaRecorder native 对象地址转化为 jlong 保存在 mNativeContext field 中)。

frameworks/base/media/jni/android_media_MediaRecorder.cpp

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

    ScopedUtfChars opPackageNameStr(env, opPackageName);

    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);
}
  1. 调用 getMediaPlayerService() 获取指向 IMediaPlayerService 的指针去初始化局部强指针变量 service;
  2. 使用 binder 机制调用远端服务 createMediaRecorder(…) 方法获取指向 IMediaRecorder 的指针;
  3. 将 mCurrentState 当前状态置为 MEDIA_RECORDER_IDLE(空闲态);
  4. 调用 doCleanUp() 设置一些布尔值为 false。

frameworks/av/media/libmedia/mediarecorder.cpp

void MediaRecorder::doCleanUp()
{
    ALOGV("doCleanUp");
    mIsAudioSourceSet  = false;
    mIsVideoSourceSet  = false;
    mIsAudioEncoderSet = false;
    mIsVideoEncoderSet = false;
    mIsOutputFileSet   = false;
}
......
MediaRecorder::MediaRecorder(const String16& opPackageName) : mSurfaceMediaSource(NULL)
{
    ALOGV("constructor");

    const sp<IMediaPlayerService> service(getMediaPlayerService());
    if (service != NULL) {
        mMediaRecorder = service->createMediaRecorder(opPackageName);
    }
    if (mMediaRecorder != NULL) {
        mCurrentState = MEDIA_RECORDER_IDLE;
    }


    doCleanUp();
}

status_t MediaRecorder::initCheck()
{
    return mMediaRecorder != 0 ? NO_ERROR : NO_INIT;
}
  1. 调用 defaultServiceManager() 获取指向“大内总管”的单例 Binder 代理对象强指针;
  2. 将 media.player 字符串包装成 String16 类型作为入参调用 getService(…) 查询相应服务;
  3. 直到查到相应服务,否则休息 0.5s,继续下一轮轮训直到服务就绪;
  4. 调用 linkToDeath(…) 设置“死亡通知” DeathNotifier;
  5. 最后将 binder 对象强转为指向 IMediaPlayerService 的代理对象,并返回。

frameworks/av/media/libmedia/IMediaDeathNotifier.cpp

// establish binder interface to MediaPlayerService
/*static*/const sp<IMediaPlayerService>
IMediaDeathNotifier::getMediaPlayerService()
{
    ALOGV("getMediaPlayerService");
    Mutex::Autolock _l(sServiceLock);
    if (sMediaPlayerService == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16("media.player"));
            if (binder != 0) {
                break;
            }
            ALOGW("Media player service not published, waiting...");
            usleep(500000); // 0.5 s
        } while (true);

        if (sDeathNotifier == NULL) {
            sDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(sDeathNotifier);
        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
    }
    ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
    return sMediaPlayerService;
}

使用 binder 机制调用远端服务 createMediaRecorder(…) 方法,首先会从代理端 BpMediaPlayerService 发出请求,然后在服务端 BnMediaPlayerService onTransact(…) 处理对应的服务,MediaPlayerService 继承自 BnMediaPlayerService,因此 onTransact(…) 方法中调用 createMediaRecorder(…) 实际会调用 MediaPlayerService 中的 createMediaRecorder(…) 方法。

frameworks/av/media/libmedia/IMediaPlayerService.cpp

class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
public:
    ......
    virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeString16(opPackageName);
        remote()->transact(CREATE_MEDIA_RECORDER, data, &reply);
        return interface_cast<IMediaRecorder>(reply.readStrongBinder());
    }
    ......
}
......

status_t BnMediaPlayerService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch (code) {
        ......
        case CREATE_MEDIA_RECORDER: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            const String16 opPackageName = data.readString16();
            sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName);
            reply->writeStrongBinder(IInterface::asBinder(recorder));
            return NO_ERROR;
        } break;
        ......
    }
}
  1. 调用 getCallingPid() 获取调用进程 ID;
  2. 创建 MediaRecorderClient 对象并将其赋给 sp 局部变量 recorder;
  3. 使用 sp 局部变量 recorder 创建 wp 局部变量 w;
  4. 将 wp 局部变量 w 添加到 SortedVector< wp > 容器中;
  5. 返回指向 MediaRecorderClient 的 sp 指针,最终实际将其转化为指向 binder 代理对象的指针返回。

frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp

sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 &opPackageName)
{
    pid_t pid = IPCThreadState::self()->getCallingPid();
    sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);
    wp<MediaRecorderClient> w = recorder;
    Mutex::Autolock lock(mLock);
    mMediaRecorderClients.add(w);
    ALOGV("Create new media recorder client from pid %d", pid);
    return recorder;
}

MediaRecorderClient 构造器中创建了 StagefrightRecorder 对象。

frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp

MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid,
        const String16& opPackageName)
{
    ALOGV("Client constructor");
    mPid = pid;
    mRecorder = new StagefrightRecorder(opPackageName);
    mMediaPlayerService = service;
}

StagefrightRecorder 构造器中初始化了一系列字段,给它们赋了一些默认值。

frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp

StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName)
    : MediaRecorderBase(opPackageName),
      mWriter(NULL),
      mOutputFd(-1),
      mAudioSource((audio_source_t)AUDIO_SOURCE_CNT), // initialize with invalid value
      mVideoSource(VIDEO_SOURCE_LIST_END),
      mStarted(false),
      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
      mDeviceCallbackEnabled(false),
      mSelectedMicDirection(MIC_DIRECTION_UNSPECIFIED),
      mSelectedMicFieldDimension(MIC_FIELD_DIMENSION_NORMAL) {

    ALOGV("Constructor");

    mAnalyticsDirty = false;
    reset();
}
......

status_t StagefrightRecorder::reset() {
    ALOGV("reset");
    stop();

    // No audio or video source by default
    mAudioSource = (audio_source_t)AUDIO_SOURCE_CNT; // reset to invalid value
    mVideoSource = VIDEO_SOURCE_LIST_END;

    // Default parameters
    mOutputFormat  = OUTPUT_FORMAT_THREE_GPP;
    mAudioEncoder  = AUDIO_ENCODER_AMR_NB;
    mVideoEncoder  = VIDEO_ENCODER_DEFAULT;
    mVideoWidth    = 176;
    mVideoHeight   = 144;
    mFrameRate     = -1;
    mVideoBitRate  = 192000;
    mSampleRate    = 8000;
    mAudioChannels = 1;
    mAudioBitRate  = 12200;
    mInterleaveDurationUs = 0;
    mIFramesIntervalSec = 1;
    mAudioSourceNode = 0;
    mUse64BitFileOffset = false;
    mMovieTimeScale  = -1;
    mAudioTimeScale  = -1;
    mVideoTimeScale  = -1;
    mCameraId        = 0;
    mStartTimeOffsetMs = -1;
    mVideoEncoderProfile = -1;
    mVideoEncoderLevel   = -1;
    mMaxFileDurationUs = 0;
    mMaxFileSizeBytes = 0;
    mTrackEveryTimeDurationUs = 0;
    mCaptureFpsEnable = false;
    mCaptureFps = -1.0;
    mCameraSourceTimeLapse = NULL;
    mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
    mEncoderProfiles = MediaProfiles::getInstance();
    mRotationDegrees = 0;
    mLatitudex10000 = -3600000;
    mLongitudex10000 = -3600000;
    mTotalBitRate = 0;

    // tracking how long we recorded.
    mDurationRecordedUs = 0;
    mStartedRecordingUs = 0;
    mDurationPausedUs = 0;
    mNPauses = 0;

    mOutputFd = -1;

    return OK;
}

JNIMediaRecorderListener 构造函数中获取了 MediaRecorder jclass,然后分别将入参转化为 JNI 全局引用缓存到 JNIMediaRecorderListener 对象内部,mClass 缓存 MediaRecorder 对应的 jclass,mObject 缓存 MediaRecorder Java 对象的弱引用。这里使用弱引用注释已经做了解释:使用弱引用,以便 MediaRecorder 对象可以被垃圾收集,引用仅用作回调的代理。不难看出 JNIMediaRecorderListener 主要是为 native 回调 java 方法而设立的,更进一步来说是将事件发布到应用程序线程。

frameworks/base/media/jni/android_media_MediaRecorder.cpp

// ref-counted object for callbacks
class JNIMediaRecorderListener: public MediaRecorderListener
{
public:
    JNIMediaRecorderListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
    ~JNIMediaRecorderListener();
    void notify(int msg, int ext1, int ext2);
private:
    JNIMediaRecorderListener();
    jclass      mClass;     // Reference to MediaRecorder class
    jobject     mObject;    // Weak ref to MediaRecorder Java object to call on
};

JNIMediaRecorderListener::JNIMediaRecorderListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{

    // Hold onto the MediaRecorder class for use in calling the static method
    // that posts events to the application thread.
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        ALOGE("Can't find android/media/MediaRecorder");
        jniThrowException(env, "java/lang/Exception", NULL);
        return;
    }
    mClass = (jclass)env->NewGlobalRef(clazz);

    // We use a weak reference so the MediaRecorder object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    mObject  = env->NewGlobalRef(weak_thiz);
}

JNIMediaRecorderListener::~JNIMediaRecorderListener()
{
    // remove global references
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    env->DeleteGlobalRef(mObject);
    env->DeleteGlobalRef(mClass);
}

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

MediaRecorder setListener(…) 实现非常简单仅仅将 MediaRecorderListener 对象保存在了 mListener 这个 field 内,确切地说是多了一个强指正指向 MediaRecorderListener 对象。

frameworks/av/media/libmedia/mediarecorder.cpp

status_t MediaRecorder::setListener(const sp<MediaRecorderListener>& listener)
{
    ALOGV("setListener");
    Mutex::Autolock _l(mLock);
    mListener = listener;

    return NO_ERROR;
}

再来看 MediaRecorder setClientName(…) 方法。前面将 mCurrentState 当前状态置为 MEDIA_RECORDER_IDLE(空闲态),因此 isInvalidState 这个布尔值为 false,最后调用 binder 代理对象 Bp:BpMediaRecorder 中的 setClientName(…) 进一步处理。

frameworks/av/media/libmedia/mediarecorder.cpp

status_t MediaRecorder::setClientName(const String16& clientName)
{
    ALOGV("setClientName");
    if (mMediaRecorder == NULL) {
        ALOGE("media recorder is not initialized yet");
        return INVALID_OPERATION;
    }
    bool isInvalidState = (mCurrentState &
                           (MEDIA_RECORDER_PREPARED |
                            MEDIA_RECORDER_RECORDING |
                            MEDIA_RECORDER_ERROR));
    if (isInvalidState) {
        ALOGE("setClientName is called in an invalid state: %d", mCurrentState);
        return INVALID_OPERATION;
    }

    mMediaRecorder->setClientName(clientName);

    return NO_ERROR;
}

BpMediaRecorder 中的 setClientName(…) 方法最终经过 binder 调用传递“消息”到 BnMediaRecorder onTransact(…) 中。

frameworks/av/media/libmedia/IMediaRecorder.cpp

class BpMediaRecorder: public BpInterface<IMediaRecorder>
{
    ......
    status_t setClientName(const String16& clientName)
    {
        ALOGV("setClientName(%s)", String8(clientName).string());
        Parcel data, reply;
        data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
        data.writeString16(clientName);
        remote()->transact(SET_CLIENT_NAME, data, &reply);
        return reply.readInt32();
    }    
    ......
}
......

status_t BnMediaRecorder::onTransact(
                                     uint32_t code, const Parcel& data, Parcel* reply,
                                     uint32_t flags)
{
    switch (code) {
        ......
        case SET_CLIENT_NAME: {
            ALOGV("SET_CLIENT_NAME");
            CHECK_INTERFACE(IMediaRecorder, data, reply);
            reply->writeInt32(setClientName(data.readString16()));
            return NO_ERROR;
        }
        ......
    }
}

BnMediaRecorder onTransact(…) 中调用 setClientName(…) 真正实现,MediaRecorderClient 继承自 BnMediaRecorder,实际上具体实现就位于 MediaRecorderClient 内。

mRecorder 指向 StagefrightRecorder 对象,因此实际会调用到 StagefrightRecorder setClientName(…) 方法。

frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp

status_t MediaRecorderClient::setClientName(const String16& clientName) {
    ALOGV("setClientName(%s)", String8(clientName).string());
    Mutex::Autolock lock(mLock);
    if (mRecorder == NULL) {
        ALOGE("recorder is not initialized");
        return NO_INIT;
    }
    return mRecorder->setClientName(clientName);
}

StagefrightRecorder setClientName(…) 内部仅仅把入参保存在了 mClientName 这个 field 内。

frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp

status_t StagefrightRecorder::setClientName(const String16& clientName) {
    mClientName = clientName;

    return OK;
}

现在回过头来看 android_media_MediaRecorder_native_setup(…) 中调用的 setMediaRecorder(…) 方法。

  1. 调用 JNI 接口 GetLongField(…) 获取存放在 Java MediaRecorder mNativeContext 字段中的值;
  2. 将 MediaRecorder native 对象的引用计数调用 incStrong(…) 加一;
  3. 如果存在旧值,调用 decStrong(…) 将 MediaRecorder native 对象引用计数减一;
  4. 调用 JNI 接口 SetLongField(…) 将新的值设置到 Java MediaRecorder mNativeContext 字段上。

frameworks/base/media/jni/android_media_MediaRecorder.cpp

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);
    }
    env->SetLongField(thiz, fields.context, (jlong)recorder.get());
    return old;
}

总结一下大致流程,如下图:

参考资料

1.https://developer.android.google.cn/reference/android/media/MediaRecorder

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/891727.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-14
下一篇 2022-05-14

发表评论

登录后才能评论

评论列表(0条)

保存