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 previewSurface previewSurface = new Surface(texture);surfaces.add(previewSurface);mPreviewBuilder.addTarget(previewSurface);// Set up Surface for the MediaRecorderSurface recorderSurface = mMediaRecorder.getSurface();surfaces.add(recorderSurface);mPreviewBuilder.addTarget(recorderSurface);// Start a capture session// Once the session starts, we can update the UI and start recordingmCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {...   }@Overridepublic 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 MediaRecordersp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this);mr->setListener(listener);// Convert client name jstring to String16const 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 trackingmr->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 valuemVideoSource(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 defaultmAudioSource = (audio_source_t)AUDIO_SOURCE_CNT; // reset to invalid valuemVideoSource = VIDEO_SOURCE_LIST_END;// Default parametersmOutputFormat  = 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 classjobject     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 referencesJNIEnv *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

【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 初始化相关推荐

  1. 【Android 10 源码】深入理解 Omx 初始化

    MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...

  2. 【Android 10 源码】healthd 模块 BatteryService 初始化

    BatteryService 是在 SystemServer 中启动的,BatteryService 监控设备电池的充电状态和充电水平.当这些值改变时,这个服务会将这些新值广播给所有正在监听 ACTI ...

  3. 【Android 10 源码】深入理解 MediaCodec 组件分配

    MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...

  4. 【Android 10 源码】深入理解 software Codec2 服务启动

    MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...

  5. 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 配置

    MediaRecorder 录像配置主要涉及输出文件路径.音频来源.视频来源.输出格式.音频编码格式.视频编码格式.比特率.帧率和视频尺寸等. 我们假设视频输入源来自 Camera,Camera2 A ...

  6. 【Android 10 源码】healthd 模块 HAL 2.0 分析

    Android 9 引入了从 health@1.0 HAL 升级的主要版本 android.hardware.health HAL 2.0.这一新 HAL 具有以下优势: 框架代码和供应商代码之间的区 ...

  7. Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南

    Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南 实验环境 下载Android源码树 在pixel3上安装手机驱动版本 编译Android源码 Android ...

  8. 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 开始录制

    前面已经分析过 MediaRecorder 初始化和配置过程,接下来就可以真正进入录制流程了.现在不难得出这个结论:MediaRecorder 录制 Camera 的数据实际上是将预览数据经过 Med ...

  9. uboot 2021.10源码分析(启动流程)

    uboot版本:2021.10 平台:armv8  rk3399  eMMC 16G  LPDDR4 4G 本文主要基于uboot的执行流程进行分析而忽略了相关细节,从uboot的基本框架结构着手,新 ...

最新文章

  1. Docker容器入门-基本命令的使用
  2. FreeBSD 8.0候选版本RC3发布
  3. 不同浏览器 ajax,完整的 AJAX 写法(支持多浏览器)
  4. git 拉取远程分支到本地
  5. FZU 1914 Funny Positive Sequence
  6. git pull忽略指定文件_Git忽略提交规则
  7. 在html标签中写三元运算符,如何在剃须刀中使用三元运算符(特别是在HTML属性上)?...
  8. angularjs-控制form及radio,checkbox,select
  9. 虚拟linux系统首次登入,第一次在虚拟机启动我们的Linux系统
  10. 【正在完善】高级CSS特效解析其示范案例
  11. 精通JavaScript?关于JavaScript的内存与性能问题,你又了解多少呢?
  12. 基于单片机的触屏电机控制系统的设计
  13. 单位根检验urdf_怎样分析单位根检验结果
  14. 实时Javascript开发框架Clouda、Meteor、Firebase对比
  15. html外联式怎么设置,笔记《三》-html引用css的三种方式-内联,嵌入,外联
  16. dlopen failed: empty/missing DT_HASH in quot;libx.soquot; (built with --hash-style=gnu?)
  17. 微型计算机逻辑元件有哪些,目前普遍使用的微型计算机所采用的逻辑元件有哪些...
  18. pathlib.Path模块下的glob、rglob,glob模块下的glob、iglob
  19. Adblock Plus 3.2 Chrome插件下载
  20. 创建一个员工信息管理界面,页面如下

热门文章

  1. 标定学习笔记(六)-- Halcon手眼标定例程:Hand-eye-Calibration with a stationary cam
  2. 推荐国产神器Eolink!API优先,Eolink领先!
  3. 阿里云天池SQL训练营task6笔记
  4. Portapack ADS-B接收室外附近飞机信号
  5. Python Defensive Programming
  6. CSS如何使得img图片设置无色透明
  7. 一加6T手机Android10 root教程
  8. 找不到模块“./xxx.vue”或其相应的类型声明。ts(2307)
  9. 解决台式机开机后再连接视频线不显示的问题
  10. Web前端从入门到精通自学之路