AndroidMediaRecorder架构详解
1. 简介
在android中录制音频有两种方式,MediaRecorder和AudioRecord。两者的区别如下:
(1) MediaRecorder
简单方便,不需要理会中间录制过程,结束录制后可以直接得到音频文件进行播放;录制的音频文件是经过压缩的,需要设置编码器;录制的音频文件可以用系统自带的播放器播放。
(2) AudioRecorder
在声音录制过程中,可以处理采集的声音数据,如降噪、合成等。过程为一段一段进行录制然后得到数据分别进行处理。录制的是PCM格式的音频文件,需要用AudioTrack来播放,AudioTrack更接近底层。
本文主要详解MediaRecorder架构,从上层调到StagefrightRecorder的流程以及应用层录音接口调用的流程。(注:本次分析基于android4.4.2源码)
2. 应用层录音接口调用流程
(1) 调用new MediaRecorder()构造函数得到实例。
(2) 调用setOutputFormat()设定媒体文件的输出格式。
(3) 调用setAudioSource()设定音频的录入源以及调用setAudioEncoder()设定音频的编码方式。
(4) 调用setOutputFile()设定记录的媒体文件保存的路径。
(5) 调用prepare()准备录制。
(6) 调用start()开始录制。
(7) 记录完成后,调用stop()停止录制。
3. 应用层调到StagefrightRecorder的流程
如图1所示,MediaRecorder在运行时,可以分成Client和Server两个部分,它们分别在两个进程中运行,它们之间使用Binder机制实现IPC通讯。
图1 录音从Java层调到StagefrightRecorder的流程图
(1) 手机启动时会启动进程/system/bin/mediaserver。该进程会把media相关服务注册到ServiceManager中,如MediaPlayerService。
(/frameworks/av/media/mediaserver/main_mediaserver.cpp)
124 sp proc(ProcessState::self());
125 sp sm = defaultServiceManager();
126 ALOGI("ServiceManager: %p", sm.get());
127 AudioFlinger::instantiate();
128 MediaPlayerService::instantiate();
129 CameraService::instantiate();
130 AudioPolicyService::instantiate();
131 registerExtensions();
132 ProcessState::self()->startThreadPool();
133 IPCThreadState::self()->joinThreadPool();
(/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp)
204 void MediaPlayerService::instantiate() {
205 defaultServiceManager()->addService(
206 String16("media.player"), new MediaPlayerService());
207 }
(2) 应用层创建MediaRecorder实例:mMediaRecorder = new MediaRecorder(); 调用SDK中MediaRecorder.java
(frameworks\base\media\java\android\media\MediaRecorder.java)
98 public MediaRecorder() {
99
100 Looper looper;
101 if ((looper = Looper.myLooper()) != null) {
102 mEventHandler = new EventHandler(this, looper);
103 } else if ((looper = Looper.getMainLooper()) != null) {
104 mEventHandler = new EventHandler(this, looper);
105 } else {
106 mEventHandler = null;
107 }
108
109 String packageName = ActivityThread.currentPackageName();
110 /* Native setup requires a weak reference to our object.
111 * It's easier to create it here than in C++.
112 */
113 native_setup(new WeakReference(this), packageName);
114 }
通过JNI方式调用到framework层android_media_MediaRecorder.cpp。
(\frameworks\base\media\jni\android_media_MediaRecorder.cpp)
418static void
419android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
420 jstring packageName)
421{
422 ALOGV("setup");
423
424 sp mr = new MediaRecorder();
425 if (mr == NULL) {
426 jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
427 return;
428 }
429 if (mr->initCheck() != NO_ERROR) {
430 jniThrowException(env, "java/lang/RuntimeException", "Unable to initialize media recorder");
431 return;
432 }
433
434 // create new listener and give it to MediaRecorder
435 sp listener = new JNIMediaRecorderListener(env, thiz, weak_this);
436 mr->setListener(listener);
437
438 // Convert client name jstring to String16
439 const char16_t *rawClientName = env->GetStringChars(packageName, NULL);
440 jsize rawClientNameLen = env->GetStringLength(packageName);
441 String16 clientName(rawClientName, rawClientNameLen);
442 env->ReleaseStringChars(packageName, rawClientName);
443
444 // pass client package name for permissions tracking
445 mr->setClientName(clientName);
446
447 setMediaRecorder(env, thiz, mr);
448}
(3) 继而调用mediarecorder.cpp的构造函数,它首先会从ServiceManager中获得MediaPlayerService服务,然后通过服务来创建recorder。这个recorder就是录音的真实实例。
(frameworks\av\media\libmedia\mediarecorder.cpp)
617MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL)
618{
619 ALOGV("constructor");
620
621 const sp& service(getMediaPlayerService());
622 if (service != NULL) {
623 mMediaRecorder = service->createMediaRecorder();
624 }
625 if (mMediaRecorder != NULL) {
626 mCurrentState = MEDIA_RECORDER_IDLE;
627 }
628
629
630 doCleanUp();
631}
(4) 通过getMediaPlayerService得到的service其实是BpMediaPlayerService,它和mediaserver进程中的BnMediaPlayerService是相对应的,共同负责进程间binder通信。BpMediaPlayerService中的createMediaRecorder其实是通过binder机制将CREATE_MEDIA_RECORDER消息发送出去。
(/frameworks/av/media/libmedia/IMediaPlayerService.cpp)
81 virtual sp createMediaRecorder()
82 {
83 Parcel data, reply;
84 data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
85 remote()->transact(CREATE_MEDIA_RECORDER, data, &reply);
86 return interface_cast(reply.readStrongBinder());
87 }
(5) 在BnMediaPlayerService中,通过onTransact()来处理接收到的消息,并返回结果。当接收消息中的code为CREATE_MEDIA_RECORDER时,调用MediaPlayerService 中的createMediaRecorder函数。在该函数中创建了一个MediaRecorderClient的实例,也就是说MediaPlayerService会为每个client应用进程创建一个相应的MediaRecorderClient的实例,来提供服务。
(/frameworks/av/media/libmedia/IMediaPlayerService.cpp)
210status_t BnMediaPlayerService::onTransact(
211 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
212{
213 switch (code) {
214 case CREATE: {
215 CHECK_INTERFACE(IMediaPlayerService, data, reply);
216 sp client =
217 interface_cast(data.readStrongBinder());
218 int audioSessionId = data.readInt32();
219 sp player = create(client, audioSessionId);
220 reply->writeStrongBinder(player->asBinder());
221 return NO_ERROR;
222 } break;
…
262 case CREATE_MEDIA_RECORDER: {
263 CHECK_INTERFACE(IMediaPlayerService, data, reply);
264 sp recorder = createMediaRecorder();
265 reply->writeStrongBinder(recorder->asBinder());
266 return NO_ERROR;
267 } break;
…
337 default:
338 return BBinder::onTransact(code, data, reply, flags);
339 }
340}
(/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp)
231sp
(6) 如此MediaRecorder.cpp就得到了一个recorder的实例,对它来说这个实例和本地的其他类的实例没什么用法上的区别,但其实这个实例是运行在另外一个进程中。实现这种假象的就是binder机制。在MediaRecorderClient的构造函数中,才会真正的创建StagefrightRecorder的具体实例,即真正的录制对象,使用的StageFright多媒体框架。在android 4.0以后只有StagefrightRecorder一个录制框架。在2.2、2.3中还存在另外一个录制对象PVMediaRecorder,使用的是OpenCore框架实现录音或录像。
(/frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp)
303MediaRecorderClient::MediaRecorderClient(const sp& service, pid_t pid)
304{
305 ALOGV("Client constructor");
306 mPid = pid;
307 mRecorder = new StagefrightRecorder;
308 mMediaPlayerService = service;
309}
图2 录音创建实例的时序图
4. Application Framework层与libraries层录音函数对应关系
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mMediaRecorder.setOutputFile(recordFile.getAbsolutePath());
mMediaRecorder.prepare();
mMediaRecorder.start();
mMediaRecorder.stop();
mMediaRecorder.release();
sp
mr->setAudioSource(1); //MIC = 1
mr->setOutputFormat(0); //DEFAULT = 0;
mr->setAudioEncoder(0);//DEFAULT = 0;
mr->setOutputFile(fos.getFD(), 0, 0);
mr->prepare();
mr->start();
mr->stop();
mr->release();
5. 总结
通过对androidmediarecorder架构的详解,了解各模块调用的流程,不仅可以实现在应用层调用录音接口进行录音,还能直接调用libraries层录音接口函数进行C层录音。同时,若需要监控录音,则可以Hook系统进程/system/bin/mediaserver的ioctl函数,从而拦截该进程的binder通信过程,通过解析binder通信数据包,就可以监控到手机中所有录音软件的录音行为。
6. 参考资料
[1] Android录音MediaRecorder/AudioRecorder相关总结:
http://www.360doc.com/content/12/0919/19/10764837_237084032.shtml
[2] MediaRecorder和AudioRecord的区别和联系:
http://blog.csdn.net/ameyume/article/details/7885744
[3] MediaRecorder类介绍:http://blog.csdn.net/mark_dev/article/details/7249415
[4] AndroidXRef:http://androidxref.com/