您的位置:首页 > 移动开发 > Android开发

关于android电话录音问题的详细分析

2013-06-26 09:59 211 查看

作者:老猫

一直以来都是在网络上看别人的文章,老老实实的做潜水员,今天一时兴起,写点东西,希望对大家有所帮助,不要再走同样的弯路。

本文是关于Android下录音问题的分析,网络上都说Android录音时记录下的语音信号都是混音器的信号。但是都没有给出详细说明为什么是这样。

我们知道Android下进行电话录音的代码很简单:

大致流程如下:

recorder = new MediaRecorder();

//这里mode可以设置为 VOICE_UPLINK|VOICE_DOWNLINK|VOICE_CALL

recorder.setAudioSource(mode);

recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);

recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);

recorder.setOutputFile(recFile.getAbsolutePath());

//准备录音

recorder.prepare();

//启动录音

recorder.start();

//停止录音

recorder.stop();

MediaRecorder.AudioSource中定义了以下常量可以用于recorder.setAudioSource

这里和电话录音相关的有3个常量

Voice_call 录制上行线路和下行线路

Voice_uplink 录制上行线路,应该是对方的语音

Voice_downlink 录制下行线路,应该是我方的语音

网络上关于java层如何调用native代码的介绍很多,这里只做简单介绍。JAVA中MediaRecorder的方法会掉用本地C++代码,这些代码编译后为libmedia.so,再通过进程间通信机制Binder和MediaServer通信,MediaServer收到请求后,把这些请求转发给opencore。

以下是Android的媒体库框架图,从网络上下载的。

从上图可以看出,客户端调用的本地代码位于libmedia.so中,媒体服务进程调用的代码位于libmediaplayerservice.so中。libmediaplayerservice.so再调用底层的libopencoreplayer.so完成具体功能。

以下通过代码介绍媒体服务进程如何转发请求到opencore中。关于客户端mediarecorder如何与媒体服务进程交互请搜索网络,这方面文章很多,这里就不多介绍。

总而言之,客户端的一个mediarecorder对象和服务器端的MediaRecorderClient对象对应,客户端通过mediarecorder发送的请求,通过进程间通信机制最终都会发送到服务端的MediaRecorderClient类中。我们来看下内部类client的声明,代码位于frameworks\base\media\libmediaplayerservice\MediaRecorderClient.h

class MediaRecorderClient : public BnMediaRecorder

{

public:

virtual status_t setCamera(const sp<ICamera>& camera);

virtual status_t setPreviewSurface(const sp<ISurface>& surface);

virtual status_t setVideoSource(int vs);

virtual status_t setAudioSource(int as);

virtual status_t setOutputFormat(int of);

virtual status_t setVideoEncoder(int ve);

virtual status_t setAudioEncoder(int ae);

virtual status_t setOutputFile(const char* path);

virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);

virtual status_t setVideoSize(int width, int height);

virtual status_t setVideoFrameRate(int frames_per_second);

virtual status_t setParameters(const String8& params);

virtual status_t setListener(const sp<IMediaPlayerClient>& listener);

virtual status_t prepare();

virtual status_t getMaxAmplitude(int* max);

virtual status_t start();

virtual status_t stop();

virtual status_t reset();

virtual status_t init();

virtual status_t close();

virtual status_t release();

。。。

}

可以看到,大部分客户端方法在MediaRecorderClient中都有对应方法。这样当我们调用客户端的recorder.start();时,最后会调用到MediaRecorderClient类中的start方法。

status_t MediaRecorderClient::start()

{

LOGV("start");

Mutex::Autolock lock(mLock);

if (mRecorder == NULL) {

LOGE("recorder is not initialized");

return NO_INIT;

}

return mRecorder->start(); //转发给mRecorder

}

//这里的mRecorder是在MediaRecorderClient构造函数中创建的。

MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid)

{

。。。

#ifndef NO_OPENCORE

{

//创建了PVMediaRecorder用于录音

mRecorder = new PVMediaRecorder();

}

#else

{

mRecorder = NULL;

}

#endif

mMediaPlayerService = service;

}

其他的调用也是一样,所有的请求基本都转发给了PVMediaRecorder,这个PVMediaRecorder就是opencore中的对应的录音的类。

这样,我们就直接进入opencore分析,先看看PVMediaRecorder的声明,代码位于frameworks\base\include\media\PVMediaRecorder.h,可以看到,客户端的方法在这里基本都有对应的方法。

class PVMediaRecorder : public MediaRecorderBase {

public:

PVMediaRecorder();

virtual ~PVMediaRecorder();

virtual status_t init();

virtual status_t setAudioSource(audio_source as);

virtual status_t setVideoSource(video_source vs);

virtual status_t setOutputFormat(output_format of);

virtual status_t setAudioEncoder(audio_encoder ae);

virtual status_t setVideoEncoder(video_encoder ve);

virtual status_t setVideoSize(int width, int height);

virtual status_t setVideoFrameRate(int frames_per_second);

virtual status_t setCamera(const sp<ICamera>& camera);

virtual status_t setPreviewSurface(const sp<ISurface>& surface);

virtual status_t setOutputFile(const char *path);

virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);

virtual status_t setParameters(const String8& params);

virtual status_t setListener(const sp<IMediaPlayerClient>& listener);

virtual status_t prepare();

virtual status_t start();

virtual status_t stop();

virtual status_t close();

virtual status_t reset();

virtual status_t getMaxAmplitude(int *max);

private:

status_t doStop();

AuthorDriverWrapper* mAuthorDriverWrapper;

PVMediaRecorder(const PVMediaRecorder &);

PVMediaRecorder &operator=(const PVMediaRecorder &);

};

Opencore是一个第3方的库,体系比较复杂,关于opencore的详细资料请参阅android源代码树下的external\opencore\doc,网络上也有这方面资料,不过不全。

总而言之,Opencore提供了一个多媒体开发框架,要使用Opencore进行多媒体应用,开发人员应该在顶层提供包装接口,在底层提供硬件接口,Opencore提供中间层功能。接收顶层发送的请求,经过处理后,最终交给底层硬件完成任务。在android系统上,顶层和底层代码都位于目录external\opencore\android下,其中external\opencore\android\author目录下是关于录音部分的代码。Opencore其他子目录下是原生代码。

以下是通过逆向编译后得到的关于录音部分的类模型。这里我们只关注主要部分。

PVMediaRecorder类收到的请求都会转发给AuthorDriverWrapper类,AuthorDriverWrapper类收到请求后又会转发给AuthorDriver类,这样,我们只要关注AuthorDriver类就可以了。

AuthorDriver中定义了如下的方法:

void handleInit(author_command *ac);

//##ModelId=4DE0871D000D

void handleSetAudioSource(set_audio_source_command *ac);

//##ModelId=4DE0871D0010

void handleSetCamera(set_camera_command *ac);

//##ModelId=4DE0871D0015

void handleSetVideoSource(set_video_source_command *ac);

//##ModelId=4DE0871D001C

void handleSetOutputFormat(set_output_format_command *ac);

//##ModelId=4DE0871D0021

void handleSetAudioEncoder(set_audio_encoder_command *ac);

//##ModelId=4DE0871D0024

void handleSetVideoEncoder(set_video_encoder_command *ac);

//##ModelId=4DE0871D0029

void handleSetVideoSize(set_video_size_command *ac);

//##ModelId=4DE0871D002E

void handleSetVideoFrameRate(set_video_frame_rate_command *ac);

//##ModelId=4DE0871D0031

void handleSetPreviewSurface(set_preview_surface_command *ac);

//##ModelId=4DE0871D0035

void handleSetOutputFile(set_output_file_command *ac);

//##ModelId=4DE0871D003A

void handleSetParameters(set_parameters_command *ac);

//##ModelId=4DE0871D003D

void handlePrepare(author_command *ac);

//##ModelId=4DE0871D0042

void handleStart(author_command *ac);

//##ModelId=4DE0871D0046

void handleStop(author_command *ac);

//##ModelId=4DE0871D004A

void handleReset(author_command *ac);

//##ModelId=4DE0871D004E

void handleClose(author_command *ac);

//##ModelId=4DE0871D0052

void handleQuit(author_command *ac);

其中每个方法对应于客户端一个请求的处理,这里需要注意的是opencore使用了事件调度机制,这种调度机制在opencore的大部分类中都出现,了解这种机制有助于我们分析代码。简单来说,opencore的大部分类收到一个请求后,会把该请求包装成1个命令对象,然后添加到命令对象队列中,在通过调度对这个命令对象进行处理。通常接收请求的方法名和处理请求的方法名字都有对应关系。比如:

status_t PVMediaRecorder::start()

{

LOGV("start");

if (mAuthorDriverWrapper == NULL) {

LOGE("author driver wrapper is not initialized yet");

return UNKNOWN_ERROR;

}

//把请求包装成命令

author_command *ac = new author_command(AUTHOR_START);

if (ac == NULL) {

LOGE("failed to construct an author command");

return UNKNOWN_ERROR;

}

//调用mAuthorDriverWrapper的enqueueCommand方法

return mAuthorDriverWrapper->enqueueCommand(ac, 0, 0);

}

status_t AuthorDriverWrapper::enqueueCommand(author_command *ac, media_completion_f comp, void *cookie)

{

if (mAuthorDriver) {

//再转发给mAuthorDriver

return mAuthorDriver->enqueueCommand(ac, comp, cookie);

}

return NO_INIT;

}

status_t AuthorDriver::enqueueCommand(author_command *ac, media_completion_f comp, void *cookie)

{

。。。

//把命令请求添加到命令请求队列中

mCommandQueue.push_front(ac);

。。。

}

//在opencore调度线程中调用

void AuthorDriver::Run()

{

。。。

//调用handleStart处理客户的start请求

case AUTHOR_START: handleStart(ac); break;

。。。

}

这样当客户端调用recorder.start();时,这个请求通过层层转发会调用AuthorDriver的handleStart方法,其他请求也一样,后面就不再列出。

void AuthorDriver::handleStart(author_command *ac)

{

LOGV("handleStart");

int error = 0;

//调用opencore的引擎的start方法

OSCL_TRY(error, mAuthor->Start(ac));

OSCL_FIRST_CATCH_ANY(error, commandFailed(ac));

}

mAuthor成员的初始化在AuthorDriver::authorThread()方法中完成。流程图如下,图中Client对象对应于我们的AuthorDriver对象。

PVAuthorEngine类定义在文件external\opencore\engines\author\src\pvauthorengine.h中

其中引擎的start方法定义如下

OSCL_EXPORT_REF PVCommandId PVAuthorEngine::Start(const OsclAny* aContextData)

{

PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,

(0, "PVAuthorEngine::Start: aContextData=0x%x", aContextData));

PVEngineCommand cmd(PVAE_CMD_START, iCommandId, (OsclAny*)aContextData);

Dispatch(cmd);

return iCommandId++;

}

经过调度机制由DoStart方法处理该请求

PVMFStatus PVAuthorEngine::DoStart(PVEngineCommand& aCmd)

{

PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVAuthorEngine::DoStart"));

OSCL_UNUSED_ARG(aCmd);

if (GetPVAEState() != PVAE_STATE_INITIALIZED)

{

return PVMFErrInvalidState;

}

iNodeUtil.Start(iComposerNodes);

if (iEncoderNodes.size() > 0)

iNodeUtil.Start(iEncoderNodes);

//调用PVAuthorEngineNodeUtility的start方法完成请求

iNodeUtil.Start(iDataSourceNodes);

return PVMFPending;

}

PVMFStatus PVAuthorEngineNodeUtility::Start(const PVAENodeContainerVector& aNodes, OsclAny* aContext)

{

。。。

PVAENodeUtilCmd cmd;

PVMFStatus status = cmd.Construct(PVAENU_CMD_START, aNodes, aContext);

。。。又是调度机制,由DoStart处理

return AddCmdToQueue(cmd);

}

PVMFStatus PVAuthorEngineNodeUtility::DoStart(const PVAENodeUtilCmd& aCmd)

{

。。。

for (uint32 i = 0; i < aCmd.iNodes.size(); i++)

{

nodeContainer = aCmd.iNodes[i];

nodeContainer->iNode->Start(nodeContainer->iSessionId, aCmd.iContext);

}

);

。。。。

}

注意到这里面调用了nodeContainer->iNode->Start(nodeContainer->iSessionId, aCmd.iContext);

这个iNode是在调用start函数前面的函数时保存到opencore的引擎中的,具体分析过程比较复杂,这里就不详细列出。

总而言之,inode指向PvmfMediaInputNode对象,代码位于external\opencore\nodes\pvmediainputnode\src\pvmf_media_input_node.h

OSCL_EXPORT_REF PVMFCommandId PvmfMediaInputNode::Start(PVMFSessionId s, const OsclAny* aContext)

{

PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,

(0, "PvmfMediaInputNode::Start() called"));

PvmfMediaInputNodeCmd cmd;

cmd.PvmfMediaInputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_START, aContext);

return QueueCommandL(cmd);

}

通过调度机制又调用了DoStart方法

PVMFStatus PvmfMediaInputNode::DoStart(PvmfMediaInputNodeCmd& aCmd)

{

。。。

//Start the MIO

PVMFStatus status = SendMioRequest(aCmd, EStart);

。。。

}

////////////////////////////////////////////////////////////////////////////

PVMFStatus PvmfMediaInputNode::SendMioRequest(PvmfMediaInputNodeCmd& aCmd, EMioRequest aRequest)

{

。。。

case EStart:

{

OSCL_TRY(err, iMediaIOCmdId = iMediaIOControl->Start(););

。。。

}

调用iMediaIOControl的start方法,这个iMediaIOControl成员是在创建该节点时候传递近来的,具体分析过程就不说了,太长。总之,iMediaIOControl指向AndroidAudioInput类,还记得我们说过,使用opencore要提供底层接口吗,这个就是android提供的底层接口,代码位于external\opencore\android\author\android_audio_input.h

PVMFCommandId AndroidAudioInput::Start(const OsclAny* aContext)

{

LOGV("Start");

if(iState != STATE_INITIALIZED && iState != STATE_PAUSED)

{

LOGE("Start: Invalid state (%d)", iState);

OSCL_LEAVE(OsclErrInvalidState);

return -1;

}

return AddCmdToQueue(AI_CMD_START, aContext);

}

通过调度机制由DoStart完成

////////////////////////////////////////////////////////////////////////////

PVMFStatus AndroidAudioInput::DoStart()

{

OsclThread AudioInput_Thread;

//创建一个线程,线程入口函数start_audin_thread_func

OsclProcStatus::eOsclProcError ret = AudioInput_Thread.Create(

(TOsclThreadFuncPtr)start_audin_thread_func, 0,

(TOsclThreadFuncArg)this, Start_on_creation);

。。。

int AndroidAudioInput::start_audin_thread_func(TOsclThreadFuncArg arg)

{

prctl(PR_SET_NAME, (unsigned long) "audio in", 0, 0, 0);

sp<AndroidAudioInput> obj = (AndroidAudioInput *)arg;

//调用audin_thread_func函数

return obj->audin_thread_func();

}

//注意这里创建了一个AudioRecord来完成实际的录音底层工作

int AndroidAudioInput::audin_thread_func() {

// setup audio record session

//最后调用的是AudioRecord类完成音频录制

LOGV("create AudioRecord %p", this);

AudioRecord

* record = new AudioRecord(

iAudioSource, iAudioSamplingRate,

android::AudioSystem::PCM_16_BIT,

(iAudioNumChannels > 1) ? AudioSystem::CHANNEL_IN_STEREO : AudioSystem::CHANNEL_IN_MONO,

4*kBufferSize/iAudioNumChannels/sizeof(int16), flags);

。。。。。

好了,绕了一整圈,我们知道opencore实际上并不负责底层录音的设置,最终工作是由AudioRecord来完成的,那我们来分析AudioRecord是如何完成录音工作的。

AudioRecord类声明在文件frameworks\base\media\libmedia\AudioRecord.h中,是audioflinger的一部分。先看下构造函数

AudioRecord::AudioRecord(

int inputSource,

uint32_t sampleRate,

int format,

uint32_t channels,

int frameCount,

uint32_t flags,

callback_t cbf,

void* user,

int notificationFrames)

: mStatus(NO_INIT)

{

//调用set方法

mStatus = set(inputSource, sampleRate, format, channels,

frameCount, flags, cbf, user, notificationFrames);

}

status_t AudioRecord::set(

int inputSource,

uint32_t sampleRate,

int format,

uint32_t channels,

int frameCount,

uint32_t flags,

callback_t cbf,

void* user,

int notificationFrames,

bool threadCanCallJava)

{

// input实际上是录音线程句柄,调用AudioSystem::getInput

audio_io_handle_t input = AudioSystem::getInput(inputSource,

sampleRate, format, channels, (AudioSystem::audio_in_acoustics)flags);

}

10、audiosystem.cpp

audio_io_handle_t AudioSystem::getInput(int inputSource,

uint32_t samplingRate,

uint32_t format,

uint32_t channels,

audio_in_acoustics acoustics)

{

const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();

if (aps == 0) return 0;

return aps->getInput(inputSource, samplingRate, format, channels, acoustics);

}

调用的是AudioPolicyService的getInput,代码位于frameworks\base\libs\audioflinger\AudioPolicyService.cpp

audio_io_handle_t AudioPolicyService::getInput(int inputSource,

uint32_t samplingRate,

uint32_t format,

uint32_t channels,

AudioSystem::audio_in_acoustics acoustics)

{

if (mpPolicyManager == NULL) {

return 0;

}

Mutex::Autolock _l(mLock);

//调用audioPolicyManagerbase的getinput

return mpPolicyManager->getInput(inputSource, samplingRate, format, channels, acoustics);

}

audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource,

uint32_t samplingRate,

uint32_t format,

uint32_t channels,

AudioSystem::audio_in_acoustics acoustics)

{

audio_io_handle_t input = 0;

// case AUDIO_SOURCE_VOICE_UPLINK:case AUDIO_SOURCE_VOICE_DOWNLINK:case AUDIO_SOURCE_VOICE_CALL:device = AudioSystem::DEVICE_IN_VOICE_CALL;

// 对于电话录音,返回的是DEVICE_IN_VOICE_CALL

uint32_t device = getDeviceForInputSource(inputSource);

// 设置选择的录音通道,如果是电话录音,设置相应得通道标志

switch(inputSource) {

case AUDIO_SOURCE_VOICE_UPLINK:

channels = AudioSystem::CHANNEL_IN_VOICE_UPLINK;

break;

case AUDIO_SOURCE_VOICE_DOWNLINK:

channels = AudioSystem::CHANNEL_IN_VOICE_DNLINK;

break;

case AUDIO_SOURCE_VOICE_CALL:

channels = (AudioSystem::CHANNEL_IN_VOICE_UPLINK | AudioSystem::CHANNEL_IN_VOICE_DNLINK);

break;

default:

break;

}

//调用AudioPolicyService的openInput

input = mpClientInterface->openInput(&inputDesc->mDevice,

&inputDesc->mSamplingRate,

&inputDesc->mFormat,

&inputDesc->mChannels,

inputDesc->mAcoustics);

return input;

}

audio_io_handle_t AudioPolicyService::openInput(uint32_t *pDevices,

uint32_t *pSamplingRate,

uint32_t *pFormat,

uint32_t *pChannels,

uint32_t acoustics)

{

sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();

if (af == 0) {

LOGW("openInput() could not get AudioFlinger");

return 0;

}

//调用AudioFlinger的openInput

return af->openInput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, acoustics);

}

AudioFlinger类定义在frameworks\base\libs\audioflinger\AudioFlinger.cpp中

int AudioFlinger::openInput(uint32_t *pDevices,

uint32_t *pSamplingRate,

uint32_t *pFormat,

uint32_t *pChannels,

uint32_t acoustics)

//打开录音输入流,对于电话录音参数,这里调用肯定失败

AudioStreamIn *input = mAudioHardware->openInputStream(*pDevices,

(int *)&format,

&channels,

&samplingRate,

&status,

(AudioSystem::audio_in_acoustics)acoustics);

//错误的话会返回,使用单通道模式重新打开

input = mAudioHardware->openInputStream(*pDevices,

(int *)&format,

&channels,

&samplingRate,

&status,

(AudioSystem::audio_in_acoustics)acoustics);

上面的mAudioHardware指向AudioHardware

AudioStreamIn* AudioHardware::openInputStream(

uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustic_flags)

{

AudioStreamInMSM72xx* in = new AudioStreamInMSM72xx();

//对硬件进行设置

status_t lStatus = in->set(this, devices, format, channels, sampleRate, acoustic_flags);

status_t AudioHardware::AudioStreamInMSM72xx::set(

AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate,

AudioSystem::audio_in_acoustics acoustic_flags)

{

。。。。

//不支持AUDIO_SOURCE_VOICE_UPLINK | AUDIO_SOURCE_VOICE_DOWNLINK

//什么样的音频硬件支持?

////如果通道不是CHANNEL_IN_MONO或者CHANNEL_IN_STEREO,把通道设置成单通道

//返回错误,还记得在AudioFlinger::openInput中检测到错误会重新调用吗?

//重新调用时使用AUDIO_HW_IN_CHANNELS,

// #define AUDIO_HW_IN_CHANNELS (AudioSystem::CHANNEL_IN_MONO)

//实际上就是单通道,然后在af的openinput中重新调用,就成功了

if (pChannels == 0 || (*pChannels != AudioSystem::CHANNEL_IN_MONO &&

*pChannels != AudioSystem::CHANNEL_IN_STEREO)) {

*pChannels = AUDIO_HW_IN_CHANNELS;

return BAD_VALUE;

}

分析到这里终于知道了,当我们设置录音方式为

Voice_call 录制上行线路和下行线路

Voice_uplink 录制上行线路,应该是对方的语音

Voice_downlink 录制下行线路,应该是我方的语音

第1次打开设备时肯定失败,失败后android把通道标志设置为CHANNEL_IN_MONO,然后调用成功,实际上此时使用的应该是混音器录音。

希望大家不要再犯相同的错误,谢谢!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: