Android应用程序很少会用到Android Native C++ 层中AudioRecord来录制PCM,但也不是完全没有需求,至少这么做可以在不懂C++的程序员面前装装逼!
直接在Native C++层录制和处理PCM数据可以避免数据拷贝到虚拟机,再由虚拟机拷回到Native C++层;虽然这个消耗笔者认为应该不大,但是没有具体分析过虚拟机,不清楚,有知道的朋友请告诉一下。
本文描述了在Android Native C++ 层中使用AudioRecord录制PCM音频的一般方法,希望对有此需求的童鞋有所帮助。
可以看到AudioRecord位于android名字空间中,继承于RefBase,这个基类是智能指针引用计数用的,而
直接在Native C++层录制和处理PCM数据可以避免数据拷贝到虚拟机,再由虚拟机拷回到Native C++层;虽然这个消耗笔者认为应该不大,但是没有具体分析过虚拟机,不清楚,有知道的朋友请告诉一下。
本文描述了在Android Native C++ 层中使用AudioRecord录制PCM音频的一般方法,希望对有此需求的童鞋有所帮助。
C++层的AudioRecord类位于源码目录\frameworks\av\include\media\AudioRecord.h中,定义如下:
namespace android {
class audio_track_cblk_t;
// ----------------------------------------------------------------------------
class AudioRecord : virtual public RefBase
{
public:
static const int DEFAULT_SAMPLE_RATE = 8000;
...
} //end of calss
} //end of namespace android
可以看到AudioRecord位于android名字空间中,继承于RefBase,这个基类是智能指针引用计数用的,而
class AudioRecord : virtual public RefBase
虚继承是为了解决菱形继承问题,让从不同的路径继承过来的同一个类在子类内存中只有一个拷贝。以下是具体代码:
#include <media/AudioRecord.h>
//==============================================
// Audio Record Defination
//==============================================
static pthread_t g_AudioRecordThread;
static pthread_t * g_AudioRecordThreadPtr = NULL;
volatile bool g_bQuitAudioRecordThread = false;
volatile int g_iInSampleTime = 0;
int g_iNotificationPeriodInFrames = 8000/10;
// g_iNotificationPeriodInFrames should be change when sample rate changes.
static void * AudioRecordThread( void *inArg );
void StartAudioRecordThread();
void StopAudioRecordThread();
void AudioRecordCallback(int event, void* user, void *info)
{
if(event == android::AudioRecord::EVENT_NEW_POS)
{
g_iInSampleTime += g_iNotificationPeriodInFrames;
//if(g_iInSampleTime > g_iNotificationPeriodInFrames*100)
// g_bQuitAudioRecordThread = true;
}
else if (event == android::AudioRecord::EVENT_MORE_DATA)
{
android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info;
pBuff->size = 0;
}
else if (event == android::AudioRecord::EVENT_OVERRUN)
{
LOGE(" EVENT_OVERRUN \n");
}
}
static void * AudioRecordThread( void *inArg )
{
uint64_t inHostTime = 0;
void * inBuffer = NULL;
audio_source_t inputSource = AUDIO_SOURCE_MIC;
audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT;
audio_channel_mask_t channelConfig = AUDIO_CHANNEL_IN_MONO; //AUDIO_CHANNEL_IN_STEREO;
int bufferSizeInBytes = 1600;
int sampleRateInHz = 8000;
android::AudioRecord * pAudioRecord = NULL;
FILE * g_pAudioRecordFile = NULL;
char strAudioFile[] = "/mnt/sdcard/external_sd/AudioRecordFile.pcm";
int iNbChannels = 1; // 1 channel for mono, 2 channel for streo
int iBytesPerSample = 2; // 16bits pcm, 2Bytes
int frameSize = 0; // frameSize = iNbChannels * iBytesPerSample
int minFrameCount = 0; // get from AudroRecord object
int iWriteDataCount = 0; // how many data are there write to file
// log the thread id for debug info
LOGD("%s Thread ID = %d \n", __FUNCTION__, pthread_self());
g_iInSampleTime = 0;
g_pAudioRecordFile = fopen(strAudioFile, "wb+");
iNbChannels = (channelConfig == AUDIO_CHANNEL_IN_STEREO) ? 2 : 1;
frameSize = iNbChannels * iBytesPerSample;
android::status_t status = android::AudioRecord::getMinFrameCount(
&minFrameCount, sampleRateInHz, audioFormat, channelConfig);
if(status != android::NO_ERROR)
{
LOGE("%s AudioRecord.getMinFrameCount fail \n", __FUNCTION__);
goto exit ;
}
LOGE("sampleRateInHz = %d minFrameCount = %d iNbChannels = %d frameSize = %d ",
sampleRateInHz, minFrameCount, iNbChannels, frameSize);
bufferSizeInBytes = minFrameCount * frameSize;
inBuffer = malloc(bufferSizeInBytes);
if(inBuffer == NULL)
{
LOGE("%s alloc mem failed \n", __FUNCTION__);
goto exit ;
}
g_iNotificationPeriodInFrames = sampleRateInHz/10;
pAudioRecord = new android::AudioRecord();
if(NULL == pAudioRecord)
{
LOGE(" create native AudioRecord failed! ");
goto exit;
}
pAudioRecord->set( inputSource,
sampleRateInHz,
audioFormat,
channelConfig,
0,
AudioRecordCallback,
NULL,
0,
true,
0);
if(pAudioRecord->initCheck() != android::NO_ERROR)
{
LOGE("AudioTrack initCheck error!");
goto exit;
}
if(pAudioRecord->setPositionUpdatePeriod(g_iNotificationPeriodInFrames) != android::NO_ERROR)
{
LOGE("AudioTrack setPositionUpdatePeriod error!");
goto exit;
}
if(pAudioRecord->start()!= android::NO_ERROR)
{
LOGE("AudioTrack start error!");
goto exit;
}
while (!g_bQuitAudioRecordThread)
{
inHostTime = UpTicks();
int readLen = pAudioRecord->read(inBuffer, bufferSizeInBytes);
int writeResult = -1;
if(readLen > 0)
{
iWriteDataCount += readLen;
if(NULL != g_pAudioRecordFile)
{
writeResult = fwrite(inBuffer, 1, readLen, g_pAudioRecordFile);
if(writeResult < readLen)
{
LOGE("Write Audio Record Stream error");
}
}
// write PCM data to file or other stream,implement it yourself
writeResult = WriteAudioData(
g_iInSampleTime,
inHostTime,
inBuffer,
readLen);
//LOGD("readLen = %d writeResult = %d iWriteDataCount = %d", readLen, writeResult, iWriteDataCount);
}
else
{
LOGE("pAudioRecord->read readLen = 0");
}
}
exit:
if(NULL != g_pAudioRecordFile)
{
fflush(g_pAudioRecordFile);
fclose(g_pAudioRecordFile);
g_pAudioRecordFile = NULL;
}
if(pAudioRecord)
{
pAudioRecord->stop();
delete pAudioRecord;
pAudioRecord == NULL;
}
if(inBuffer)
{
free(inBuffer);
inBuffer = NULL;
}
LOGD("%s Thread ID = %d quit\n", __FUNCTION__, pthread_self());
return NULL;
}
呕心沥血所作,有用请点赞!
版权声明:本文为Romantic_Energy原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。