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 构造函数干了些什么?
- 调用 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 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);
}
- 调用 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 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(…) 方法。
- 调用 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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)