【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 初始化
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 构造函数干了些什么?
- 调用 Looper 类方法 myLooper() 获取 Looper 对象,如果调用线程和相应的 Looper 做了绑定,此处就会得到这个 Looper,接下来就调用 EventHandler 的构造器进行 EventHandler 初始化;
- 如果调用线程是主线程,那么调用 Looper 类方法 getMainLooper() 必然不为空,那么使用主线程 Looper 对象和 MediaRecorder 对象作为实参初始化 EventHandler 对象;
- 如果不存在 Looper 对象,那么就将 mEventHandler 这个 field 置为空;
- 调用 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());}......
}
- 创建 MediaRecorder native 对象。如果构造函数调用失败返回空,向 Java 层抛出 RuntimeException(内存不足)。调用 MediaRecorder 对象 initCheck() 检查构造期间是否发生错误,如果存在错误则向 Java 层抛出 RuntimeException(无法初始化 media recorder)。
- 创建 JNIMediaRecorderListener 对象,并给 MediaRecorder native 对象这个 listener;
- 转化客户端名称 jstring 到 String16,调用 MediaRecorder native 对象方法 setClientName(…) 传递客户端包名以进行权限跟踪;
- 调用 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);
}
- 调用 getMediaPlayerService() 获取指向 IMediaPlayerService 的指针去初始化局部强指针变量 service;
- 使用 binder 机制调用远端服务 createMediaRecorder(…) 方法获取指向 IMediaRecorder 的指针;
- 将 mCurrentState 当前状态置为 MEDIA_RECORDER_IDLE(空闲态);
- 调用 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;
}
- 调用 defaultServiceManager() 获取指向“大内总管”的单例 Binder 代理对象强指针;
- 将 media.player 字符串包装成 String16 类型作为入参调用 getService(…) 查询相应服务;
- 直到查到相应服务,否则休息 0.5s,继续下一轮轮训直到服务就绪;
- 调用 linkToDeath(…) 设置“死亡通知” DeathNotifier;
- 最后将 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;......}
}
- 调用 getCallingPid() 获取调用进程 ID;
- 创建 MediaRecorderClient 对象并将其赋给 sp 局部变量 recorder;
- 使用 sp 局部变量 recorder 创建 wp 局部变量 w;
- 将 wp 局部变量 w 添加到 SortedVector< wp > 容器中;
- 返回指向 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(…) 方法。
- 调用 JNI 接口 GetLongField(…) 获取存放在 Java MediaRecorder mNativeContext 字段中的值;
- 将 MediaRecorder native 对象的引用计数调用 incStrong(…) 加一;
- 如果存在旧值,调用 decStrong(…) 将 MediaRecorder native 对象引用计数减一;
- 调用 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 初始化相关推荐
- 【Android 10 源码】深入理解 Omx 初始化
MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...
- 【Android 10 源码】healthd 模块 BatteryService 初始化
BatteryService 是在 SystemServer 中启动的,BatteryService 监控设备电池的充电状态和充电水平.当这些值改变时,这个服务会将这些新值广播给所有正在监听 ACTI ...
- 【Android 10 源码】深入理解 MediaCodec 组件分配
MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...
- 【Android 10 源码】深入理解 software Codec2 服务启动
MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...
- 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 配置
MediaRecorder 录像配置主要涉及输出文件路径.音频来源.视频来源.输出格式.音频编码格式.视频编码格式.比特率.帧率和视频尺寸等. 我们假设视频输入源来自 Camera,Camera2 A ...
- 【Android 10 源码】healthd 模块 HAL 2.0 分析
Android 9 引入了从 health@1.0 HAL 升级的主要版本 android.hardware.health HAL 2.0.这一新 HAL 具有以下优势: 框架代码和供应商代码之间的区 ...
- Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南
Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南 实验环境 下载Android源码树 在pixel3上安装手机驱动版本 编译Android源码 Android ...
- 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 开始录制
前面已经分析过 MediaRecorder 初始化和配置过程,接下来就可以真正进入录制流程了.现在不难得出这个结论:MediaRecorder 录制 Camera 的数据实际上是将预览数据经过 Med ...
- uboot 2021.10源码分析(启动流程)
uboot版本:2021.10 平台:armv8 rk3399 eMMC 16G LPDDR4 4G 本文主要基于uboot的执行流程进行分析而忽略了相关细节,从uboot的基本框架结构着手,新 ...
最新文章
- Docker容器入门-基本命令的使用
- FreeBSD 8.0候选版本RC3发布
- 不同浏览器 ajax,完整的 AJAX 写法(支持多浏览器)
- git 拉取远程分支到本地
- FZU 1914 Funny Positive Sequence
- git pull忽略指定文件_Git忽略提交规则
- 在html标签中写三元运算符,如何在剃须刀中使用三元运算符(特别是在HTML属性上)?...
- angularjs-控制form及radio,checkbox,select
- 虚拟linux系统首次登入,第一次在虚拟机启动我们的Linux系统
- 【正在完善】高级CSS特效解析其示范案例
- 精通JavaScript?关于JavaScript的内存与性能问题,你又了解多少呢?
- 基于单片机的触屏电机控制系统的设计
- 单位根检验urdf_怎样分析单位根检验结果
- 实时Javascript开发框架Clouda、Meteor、Firebase对比
- html外联式怎么设置,笔记《三》-html引用css的三种方式-内联,嵌入,外联
- dlopen failed: empty/missing DT_HASH in quot;libx.soquot; (built with --hash-style=gnu?)
- 微型计算机逻辑元件有哪些,目前普遍使用的微型计算机所采用的逻辑元件有哪些...
- pathlib.Path模块下的glob、rglob,glob模块下的glob、iglob
- Adblock Plus 3.2 Chrome插件下载
- 创建一个员工信息管理界面,页面如下
热门文章
- 标定学习笔记(六)-- Halcon手眼标定例程:Hand-eye-Calibration with a stationary cam
- 推荐国产神器Eolink!API优先,Eolink领先!
- 阿里云天池SQL训练营task6笔记
- Portapack ADS-B接收室外附近飞机信号
- Python Defensive Programming
- CSS如何使得img图片设置无色透明
- 一加6T手机Android10 root教程
- 找不到模块“./xxx.vue”或其相应的类型声明。ts(2307)
- 解决台式机开机后再连接视频线不显示的问题
- Web前端从入门到精通自学之路