在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通讯。stagefrightrecorder

图1 录音从Java层调到StagefrightRecorder的流程图
(1)手机启动时会启动进程/system/bin/mediaserver。该进程会把media相关服务注册到ServiceManager中,如MediaPlayerService。
(/frameworks/av/media/mediaserver/main_mediaserver.cpp)
(/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp)
204<spanstyle="white-space:pre"></span>voidMediaPlayerService::instantiate(){
205<spanstyle="white-space:pre"></span>defaultServiceManager()->addService(
206<spanstyle="white-space:pre"></span>String16("media.player"),newMediaPlayerService());
207<spanstyle="white-space:pre"></span>}
(2)应用层创建MediaRecorder实例:mMediaRecorder = new MediaRecorder(); 调用SDK中MediaRecorder.java
(frameworks\base\media\java\android\media\MediaRecorder.java)
98publicMediaRecorder(){
99
100Looperlooper;
101if((looper=Looper.myLooper())!=null){
102mEventHandler=newEventHandler(this,looper);
103}elseif((looper=Looper.getMainLooper())!=null){
104mEventHandler=newEventHandler(this,looper);
105}else{
106mEventHandler=null;
107}
108
109StringpackageName=ActivityThread.currentPackageName();
110/*Nativesetuprequiresaweakreferencetoourobject.
111*It'seasiertocreateitherethaninC.
112*/
113native_setup(newWeakReference<MediaRecorder>(this),packageName);
114}
通过JNI方式调用到framework层android_media_MediaRecorder.cpp。
(\frameworks\base\media\jni\android_media_MediaRecorder.cpp)
418staticvoid
419android_media_MediaRecorder_native_setup(JNIEnv*env,jobjectthiz,jobjectweak_this,
420jstringpackageName)
421{
422ALOGV("setup");
423
424sp<MediaRecorder>mr=newMediaRecorder();
425if(mr==NULL){
426jniThrowException(env,"java/lang/RuntimeException","Outofmemory");
427return;
428}
429if(mr->initCheck()!=NO_ERROR){
430jniThrowException(env,"java/lang/RuntimeException","Unabletoinitializemediarecorder");
431return;
432}
433
434//createnewlistenerandgiveittoMediaRecorder
435sp<JNIMediaRecorderListener>listener=newJNIMediaRecorderListener(env,thiz,weak_this);
436mr->setListener(listener);
437
438//ConvertclientnamejstringtoString16
439constchar16_t*rawClientName=env->GetStringChars(packageName,NULL);
440jsizerawClientNameLen=env->GetStringLength(packageName);
441String16clientName(rawClientName,rawClientNameLen);
442env->ReleaseStringChars(packageName,rawClientName);
443
444//passclientpackagenameforpermissionstracking
445mr->setClientName(clientName);
446
447setMediaRecorder(env,thiz,mr);
448}
(3)继而调用mediarecorder.cpp的构造函数,它首先会从ServiceManager中获得MediaPlayerService服务,然后通过服务来创建recorder。这个recorder就是录音的真实实例。
(frameworks\av\media\libmedia\mediarecorder.cpp)
617MediaRecorder::MediaRecorder():mSuceMediaSource(NULL)
618{
619ALOGV("constructor");
620
621constsp<IMediaPlayerService>&service(getMediaPlayerService());
622if(service!=NULL){
623mMediaRecorder=service->createMediaRecorder();
624}
625if(mMediaRecorder!=NULL){
626mCurrentState=MEDIA_RECORDER_IDLE;
627}
628
629
630doCleanUp();
631}
(4)通过getMediaPlayerService得到的service其实是BpMediaPlayerService,它和mediaserver进程中的BnMediaPlayerService是相对应的,共同负责进程间binder通信。BpMediaPlayerService中的createMediaRecorder其实是通过binder机制将CREATE_MEDIA_RECORDER消息发送出去。
(/frameworks/av/media/libmedia/IMediaPlayerService.cpp)
81virtualsp<IMediaRecorder>createMediaRecorder()
82{
8arceldata,reply;
84data.writeInteceToken(IMediaPlayerService::getInteceDescriptor());
85remote()->transact(CREATE_MEDIA_RECORDER,data,&reply);
86returnintece_cast<IMediaRecorder>(reply.readStrongBinder());
87}
(5)在BnMediaPlayerService中,通过onTransact()来处理接收到的消息,并返回结果。当接收消息中的code为CREATE_MEDIA_RECORDER时,调用MediaPlayerService 中的createMediaRecorder函数。在该函数中创建了一个MediaRecorderClient的实例,也就是说MediaPlayerService会为每个client应用进程创建一个相应的MediaRecorderClient的实例,来提供服务。
(/frameworks/av/media/libmedia/IMediaPlayerService.cpp)
210status_tBnMediaPlayerService::onTransact(
211uint32_tcode,constParcel&data,Parcel*reply,uint32_tflags)
212{
213switch(code){
214caseCREATE:{

215CHECK_INTERFACE(IMediaPlayerService,data,reply);
216sp<IMediaPlayerClient>client=
217intece_cast<IMediaPlayerClient>(data.readStrongBinder());
218intaudioSessionId=data.readInt32();
219sp<IMediaPlayer>player=create(client,audioSessionId);
220reply->writeStrongBinder(player->asBinder());
221returnNO_ERROR;
222}break;
…
262caseCREATE_MEDIA_RECORDER:{
263CHECK_INTERFACE(IMediaPlayerService,data,reply);
264sp<IMediaRecorder>recorder=createMediaRecorder();
265reply->writeStrongBinder(recorder->asBinder());
266returnNO_ERROR;
267}break;
…
337default:
338returnBBinder::onTransact(code,data,reply,flags);
339}
340}
(/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp)
231sp<IMediaRecorder> MediaPlayerService::createMediaRecorder()232{233 pid_t pid = IPCThreadState::self()->getCallingPid();234 sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);235 wp<MediaRecorderClient> w = recorder;236 Mutex::Autolock lock(mLock);237 mMediaRecorderClients.add(w);238 ALOGV("Create new media recorder client from pid %d", pid);239 return recorder;240}
(6)如此MediaRecorder.cpp就得到了一个recorder的实例,对它来说这个实例和本地的其他类的实例没什么用法上的区别,但其实这个实例是运行在另外一个进程中。实现这种假象的就是binder机制。在MediaRecorderClient的构造函数中,才会真正的创建StagefrightRecorder的具体实例,即真正的录制对象,使用的StageFright多媒体框架。在android 4.0以后只有StagefrightRecorder一个录制框架。stagefrightrecorder在2.2、2.3中还存在另外一个录制对象PVMediaRecorder,使用的是OpenCore框架实现录音或录像。
(/frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp)
303MediaRecorderClient::MediaRecorderClient(constsp<MediaPlayerService>&service,pid_tpid)
304{
305ALOGV("Clientconstructor");
306mPid=pid;
307mRecorder=newStagefrightRecorder;
308mMediaPlayerService=service;
309}
图2是录音创建实例的时序图

图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<MediaRecorder> mr = newMediaRecorder();
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.参考资料
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/tongxinshuyu/article-50207-1.html
有病的才是你
还说实力远超日本
小王子