一、背景: 发现网上能搜到的都是几年前的例子,现在Android版本都出到12了,几年前的例子以及不适用新版本了。于是就自己研究了下AudioRecord.java是如何调用到jni和C++层的AudioRecord.cpp。做一下记录为后来者避坑(PS:以前都不喜欢写博客,本人没时间也不太想分享)。改demo应该适用于Android8,Android9,Android10,Android11,Android12。或许过个几年到Androidxx又不适用了。
二、遇到比较有意思的坑: 程序运行没有报错但是录音没有声音,dump文件拉到电脑播放也是一条直线没有声音。后来分析是google基于安全 权限考虑做的限制。会根据uid,ui是否在前台等对AudioRecord进行静音。
解决方法有两个:
-
注释掉:void AudioFlinger::setRecordSilenced(audio_port_handle_t portId, bool silenced)函数静音代码。
-
使用 audio_source_t inputSource =AUDIO_SOURCE_HOTWORD;//AUDIO_SOURCE_MIC;
在老版本Android是使用AUDIO_SOURCE_MIC类型的。这里因为是c++ native没有ui界面等所以必须使用AUDIO_SOURCE_HOTWORD类型,否则 AudioRecord录音没有声音。
三、Demo源码: 例子有点乱,没有整理。AudioTrack是基于framework目录下AudioTrack共享内存例子修改的。
AudioRecord没有原生例子,只能自己研究了。
demo_test:
#define LOG_NDEBUG 0
#define LOG_TAG "demo_test"
#include <stdlib.h>
#include <stdio.h>
#include <cutils/properties.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
#include <media/AudioRecord.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <media/AidlConversion.h>
#include <utils/Log.h>
#include <fcntl.h>
#include "demo_test.h"
#define SOUND_LENGTH 18470
volatile bool g_bQuitAudioRecordThread = false;
int g_iNotificationPeriodInFrames = 8000/10;
void StartAudioRecordThread();
void StopAudioRecordThread();
void * inBuffer = NULL;
int myreadlen = 0;
int mywritelen = 0;
int bufferSizeInBytes = 1600;
#define SAMPLE_RATE 16000
// 这里必须使用 AUDIO_SOURCE_HOTWORD 否则录音无声,或者把 void AudioFlinger::setRecordSilenced(audio_port_handle_t portId, bool silenced)
//函数注释为空。
audio_source_t inputSource = AUDIO_SOURCE_HOTWORD;//AUDIO_SOURCE_HOTWORD;//AUDIO_SOURCE_MIC;
static const audio_attributes_t pAttributes = {
/* .content_type = */ AUDIO_CONTENT_TYPE_UNKNOWN, // 0
/* .usage = */ (audio_usage_t)AUDIO_USAGE_UNKNOWN, // 0
/* .source = */ inputSource, // 1
/* .flags = */ AUDIO_FLAG_MUTE_HAPTIC,// 0x00000800 //AUDIO_FLAG_NONE,
/* .tags = */ "demo_test"
};
static android::content::AttributionSourceState attributionSource;
namespace android {
void AudioRecordCallback(int event, void* user, void *info)
{
char *p;
if(user)
p = (char *)user;
if(event == android::AudioRecord::EVENT_NEW_POS)
{
ALOGD(" android::AudioRecord::EVENT_NEW_POS \n");
//if(g_iInSampleTime > g_iNotificationPeriodInFrames*100)
// g_bQuitAudioRecordThread = true;
}
else if (event == android::AudioRecord::EVENT_MORE_DATA)
{
ALOGD(" android::AudioRecord::EVENT_MORE_DATA..... \n");
android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info;
pBuff->size = 0;
}
else if (event == android::AudioRecord::EVENT_OVERRUN)
{
ALOGD(" EVENT_OVERRUN \n");
}
}
#if 1
static void AudioRecordThread()
{
audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; // 1
audio_channel_mask_t channelConfig = AUDIO_CHANNEL_IN_MONO;//AUDIO_CHANNEL_IN_MONO; //AUDIO_CHANNEL_IN_STEREO;
int sampleRateInHz = SAMPLE_RATE;
sp<AudioRecord> pAudioRecord = 0;
FILE * g_pAudioRecordFile = NULL;
char strAudioFile[] = "/sdcard/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
size_t minFrameCount = 0; // get from AudroRecord object
g_pAudioRecordFile = fopen(strAudioFile, "wb+");
iNbChannels = (channelConfig == AUDIO_CHANNEL_IN_STEREO) ? 2 : 1;
frameSize = iNbChannels * iBytesPerSample;
android::status_t status = AudioRecord::getMinFrameCount(
&minFrameCount, sampleRateInHz, audioFormat, channelConfig);
if(status != android::NO_ERROR)
{
ALOGD("%s AudioRecord.getMinFrameCount fail \n", __FUNCTION__);
}
// sampleRateInHz = 16000 minFrameCount = 768 iNbChannels = 2 frameSize = 4
ALOGD("sampleRateInHz = %d minFrameCount = %d iNbChannels = %d frameSize = %d ",
sampleRateInHz, (int)minFrameCount, iNbChannels, frameSize);
bufferSizeInBytes = (int)minFrameCount * frameSize;
//bufferSizeInBytes = 2*SOUND_LENGTH;
g_iNotificationPeriodInFrames = sampleRateInHz/10;
String16 opPackageName("demo_test");
uid_t uid = -1;
pid_t pid = -1;
attributionSource.packageName = VALUE_OR_FATAL(legacy2aidl_String16_string(opPackageName));
attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t((unsigned int)uid));
attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t((int)pid));
attributionSource.token = sp<BBinder>::make();
pAudioRecord = new AudioRecord(attributionSource);
if(NULL == pAudioRecord)
{
ALOGD(" create native AudioRecord failed! ");
}
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;// 0
audio_session_t sessionId = (audio_session_t)0;
int sharedAudioHistoryMs = 0;
status_t status2 =
pAudioRecord->set(pAttributes.source, sampleRateInHz,
audioFormat, // word length, PCM
channelConfig, 768,
AudioRecordCallback, // callback_t
NULL, // void* user
0, // notificationFrames,
true, // threadCanCallJava
sessionId, AudioRecord::TRANSFER_DEFAULT, flags, -1,
-1, // default uid, pid
&pAttributes, AUDIO_PORT_HANDLE_NONE, MIC_DIRECTION_UNSPECIFIED,
MIC_FIELD_DIMENSION_DEFAULT, sharedAudioHistoryMs);
if (status2 != NO_ERROR) {
ALOGE("Error creating AudioRecord instance: initialization check failed with status2 %d.",
status2);
}
pAudioRecord->setCallerName("demo_test");
if(pAudioRecord->initCheck() != android::NO_ERROR)
{
ALOGD("AudioTrack initCheck error!");
}
if(pAudioRecord->setPositionUpdatePeriod(g_iNotificationPeriodInFrames) != android::NO_ERROR)
{
ALOGD("AudioTrack setPositionUpdatePeriod error!");
}
if(pAudioRecord->start()!= android::NO_ERROR)
{
ALOGD("AudioTrack start error!");
}
int i = 0;
int readLen = 0;
while (!g_bQuitAudioRecordThread)
{
readLen = pAudioRecord->read((uint8_t*)inBuffer+i*readLen, 320/*bufferSizeInBytes*/, 1);
int writeResult = -1;
if(readLen > 0)
{
myreadlen += readLen;
if ( myreadlen > 7*SOUND_LENGTH ) {
ALOGD("pAudioRecord->read end");
break;
}
//iWriteDataCount += readLen;
if(NULL != g_pAudioRecordFile)
{
writeResult = fwrite((uint8_t*)inBuffer+i*readLen, 1, readLen, g_pAudioRecordFile);
if(writeResult < readLen)
{
ALOGE("Write Audio Record Stream error");
}
mywritelen += writeResult;
}
i++;
}
else
{
ALOGD("pAudioRecord->read readLen = 0");
}
}
ALOGD("myreadlen = %d mywritelen = %d\n", myreadlen, mywritelen);
if(pAudioRecord)
{
pAudioRecord->stop();
}
if(NULL != g_pAudioRecordFile)
{
fflush(g_pAudioRecordFile);
fclose(g_pAudioRecordFile);
g_pAudioRecordFile = NULL;
}
}
#endif
/************************************************************
*
* Constructor
*
************************************************************/
AudioTrackTest::AudioTrackTest(void) {
ALOGD("01 AudioTrackTest\n");
}
/************************************************************
*
*
************************************************************/
void AudioTrackTest::Execute(void) {
if (Test01() == 0) {
ALOGD("01 passed\n");
} else {
ALOGD("01 failed\n");
}
}
/************************************************************
*
* Shared memory test
*
************************************************************/
#define BUF_SZ 44100
int AudioTrackTest::Test01() {
bufferSizeInBytes = 10*SOUND_LENGTH;
inBuffer = malloc(bufferSizeInBytes);
if(inBuffer == NULL)
{
ALOGD("%s alloc mem failed \n", __FUNCTION__);
}
memset(inBuffer, 0, bufferSizeInBytes);
uint8_t* buf = (uint8_t*)inBuffer;
long sampleRate = SAMPLE_RATE;
size_t framecount = 16;
AudioRecordThread();
if (AudioTrack::getMinFrameCount(&framecount, AUDIO_STREAM_MUSIC, sampleRate) != NO_ERROR ) {
ALOGD("Error: cannot compute output frame count\n");
return -1;
}
ALOGD("framecount = %d \n", (int)framecount);
sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
sampleRate,
AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
AUDIO_CHANNEL_OUT_MONO, // AUDIO_CHANNEL_OUT_STEREO
0);
status_t status = track->initCheck();
if(status != NO_ERROR) {
track.clear();
ALOGD("Failed for initCheck()");
return -1;
}
// start play
ALOGD("start");
track->start();
for(int j = 0; j < 3; j++)
for(int i = 0; i <= 7*SOUND_LENGTH/320 ; i++){
track->write(buf+i*320, 320, 1);
}
ALOGD("stop");
track->stop();
if(inBuffer)
{
free(inBuffer);
inBuffer = NULL;
}
return 0;
}
/************************************************************
*
* main in name space
*
************************************************************/
int main() {
ProcessState::self()->startThreadPool();
AudioTrackTest *test;
test = new AudioTrackTest();
test->Execute();
delete test;
return 0;
}
}
/************************************************************
*
* global main
*
************************************************************/
int main() {
return android::main();
}
复制代码
demo_test.h
// Copyright 2008 The Android Open Source Project
#ifndef AUDIOTRACKTEST_H_
#define AUDIOTRACKTEST_H_
namespace android {
class AudioTrackTest{
public:
AudioTrackTest(void);
~AudioTrackTest() {};
void Execute(void);
int Test01();
};
};
#endif /*AUDIOTRACKTEST_H_*/
复制代码
Android.bp
cc_test {
name: "demo_test",
gtest: false,
srcs: ["demo_test.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
"libbinder",
"libhardware_legacy",
"libmedia",
"libaudioclient",
"libaudioclient_aidl_conversion",
],
header_libs: [
"libmediametrics_headers",
],
}
复制代码