关于android电话录音问题的详细分析
作者:老猫
一直以来都是在网络上看别人的文章,老老实实的做潜水员,今天一时兴起,写点东西,希望对大家有所帮助,不要再走同样的弯路。
本文是关于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,然后调用成功,实际上此时使用的应该是混音器录音。
希望大家不要再犯相同的错误,谢谢!!