未完待续
哈哈哈哈哈哈,我就是想了解下Android上录音是怎么实现的,写了个简单的录音demo,一路跟下去,瞅瞅这家伙都干了些啥。基于Android 7.1, s905x 平台。
按照官方我Android大文档写了下面的录音代码,只打log不干事也是厉害。
// 这个方法运行在子线程,要不那个死循环不得把应用搞ANR了。
private fun record() {
val minSize = AudioRecord.getMinBufferSize(48000,
AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT)
var record: AudioRecord? = null
try {
record = AudioRecord.Builder()
.setAudioSource(MediaRecorder.AudioSource.DEFAULT)
.setAudioFormat(AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(48000)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build())
.setBufferSizeInBytes(minSize * 2)
.build()
record.startRecording()
val buffer = ShortArray(minSize)
while (true) {
Log.d(TAG, "read ....")
val read = record.read(buffer, 0, minSize)
Log.d(TAG, "read $read bytes data...")
}
} catch (e: Throwable) {
e.printStackTrace()
} finally {
record?.stop()
}
}
// frameworks/base/media/java/android/media/AudioRecord.java
public AudioRecord build() throws UnsupportedOperationException {
// 前面都是一堆的参数构造和校验,略过
try {
// If the buffer size is not specified,
// use a single frame for the buffer size and let the
// native code figure out the minimum buffer size.
if (mBufferSizeInBytes == 0) {
mBufferSizeInBytes = mFormat.getChannelCount()
* mFormat.getBytesPerSample(mFormat.getEncoding());
}
final AudioRecord record = new AudioRecord(
mAttributes, mFormat, mBufferSizeInBytes, mSessionId);
if (record.getState() == STATE_UNINITIALIZED) {
// release is not necessary
throw new UnsupportedOperationException("Cannot create AudioRecord");
}
return record;
} catch (IllegalArgumentException e) {
throw new UnsupportedOperationException(e.getMessage());
}
}
native_setup
来初始化c++层的对象,进行真正的录音初始化。// frameworks/base/media/java/android/media/AudioRecord.java
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int sessionId) throws IllegalArgumentException {
......
int initResult = native_setup( new WeakReference(this),
mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
mAudioFormat, mNativeBufferSizeInBytes,
session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
......
jni
关键字,找到网址是android.googlesource.com
的就是了。jni层的android_media_AudioRecord_setup
中会创建c++层的AudioRecord
对象// frameworks/base/core/jni/android_media_AudioRecord.cpp
// 底下有jni方法和native方法的对应表
{"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
(void *)android_media_AudioRecord_setup},
// 本体出现
static jint
android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
jlong nativeRecordInJavaObj)
{
// 前面都是乱七八糟的参数校验
...
// create an uninitialized AudioRecord object
lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
...
// AudioRecord构造方法也会调用set
const status_t status = lpRecorder->set(paa->source,
sampleRateInHertz,
format, // word length, PCM
localChanMask,
frameCount,
recorderCallback,// callback_t
lpCallbackData,// void* user
0, // notificationFrames,
true, // threadCanCallJava
sessionId,
AudioRecord::TRANSFER_DEFAULT,
flags,
-1, -1, // default uid, pid
paa);
AudioRecord.cpp
构造方法就只干了一件事set
,至于那是干啥的,进去瞅瞅。(c++的语法不熟啊,恩恩,有朝一日看的烦死它,我大概也就学会了)// frameworks/av/media/libmedia/AudioRecord.cpp
mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
uid, pid, pAttributes);
set
里面可就神奇了,主要就创建了IAudioRecord
实例,这家伙一看就是个aidl的角色。// frameworks/av/media/libmedia/AudioRecord.cpp
......
// 对于我们的音频获取一个session id,这个AudioSystem是什么鬼,瞅瞅去。
if (sessionId == AUDIO_SESSION_ALLOCATE) {
mSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
} else {
mSessionId = sessionId;
}
ALOGV("set(): mSessionId %d", mSessionId);
......
// 创建了一个线程,搞毛的,一会儿瞅瞅
if (cbf != NULL) {
mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava);
mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
// thread begins in paused state, and will not reference us until start()
}
// 这可就是重点了,敲黑板,方法名字加个l,那明显是lock的意思。
// create the IAudioRecord
status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);
if (status != NO_ERROR) {
if (mAudioRecordThread != 0) {
mAudioRecordThread->requestExit(); // see comment in AudioRecord.h
mAudioRecordThread->requestExitAndWait();
mAudioRecordThread.clear();
}
return status;
}
system/media/audio/include/system/audio.h
包含了Android 中对于声音的一些常量定义,影响到Android上层,aps, audio-hal等多个地方。类似java中对于抽象接口的声明和常量的声明。// 这就是上面的那个newAudioUniqueId,调到AudioFlinger里去了。
audio_unique_id_t AudioSystem::newAudioUniqueId(audio_unique_id_use_t use)
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return AUDIO_UNIQUE_ID_ALLOCATE;
return af->newAudioUniqueId(use);
}
// 熟悉的配方,从ServiceManager中通过名称获取server的binder对象。
// establish binder interface to AudioFlinger service
const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
{
sp<IAudioFlinger> af;
sp<AudioFlingerClient> afc;
{
Mutex::Autolock _l(gLock);
if (gAudioFlinger == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.audio_flinger"));
if (binder != 0)
break;
ALOGW("AudioFlinger not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (gAudioFlingerClient == NULL) {
gAudioFlingerClient = new AudioFlingerClient();
} else {
if (gAudioErrorCallback) {
gAudioErrorCallback(NO_ERROR);
}
}
binder->linkToDeath(gAudioFlingerClient);
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0);
afc = gAudioFlingerClient;
}
af = gAudioFlinger;
}
if (afc != 0) {
af->registerClient(afc);
}
return af;
}
// AudioPolicyService 和上面那个一样
// establish binder interface to AudioPolicy service
const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service()
audio_unique_id_use_t
和audio_unique_id_t
的定义,感觉就像是java中的接口。// system/media/audio/include/system/audio.h
/* a unique ID allocated by AudioFlinger for use as an audio_io_handle_t, audio_session_t, * effect ID (int), audio_module_handle_t, and audio_patch_handle_t. * Audio port IDs (audio_port_handle_t) are allocated by AudioPolicy * in a different namespace than AudioFlinger unique IDs. */
typedef int audio_unique_id_t;
/* Possible uses for an audio_unique_id_t */
typedef enum {
AUDIO_UNIQUE_ID_USE_UNSPECIFIED = 0,
AUDIO_UNIQUE_ID_USE_SESSION = 1, // for allocated sessions, not special AUDIO_SESSION_*
AUDIO_UNIQUE_ID_USE_MODULE = 2,
AUDIO_UNIQUE_ID_USE_EFFECT = 3,
AUDIO_UNIQUE_ID_USE_PATCH = 4,
AUDIO_UNIQUE_ID_USE_OUTPUT = 5,
AUDIO_UNIQUE_ID_USE_INPUT = 6,
// 7 is available
AUDIO_UNIQUE_ID_USE_MAX = 8, // must be a power-of-two
AUDIO_UNIQUE_ID_USE_MASK = AUDIO_UNIQUE_ID_USE_MAX - 1
} audio_unique_id_use_t;
AudioFlinger
中关于newAudioUniqueId
方法的实现// frameworks/av/services/audioflinger/AudioFlinger.cpp
// AudioFlinger的aidl的声明在
// frameworks/av/include/media/IAudioFlinger.h
// Binder方法的实现,这里只做校验,真实实现是在nextUniqueId中
audio_unique_id_t AudioFlinger::newAudioUniqueId(audio_unique_id_use_t use)
{
// This is a binder API, so a malicious client could pass in a bad parameter.
// Check for that before calling the internal API nextUniqueId().
if ((unsigned) use >= (unsigned) AUDIO_UNIQUE_ID_USE_MAX) {
ALOGE("newAudioUniqueId invalid use %d", use);
return AUDIO_UNIQUE_ID_ALLOCATE;
}
return nextUniqueId(use);
}
// 真正的实现在这里,哈哈哈哈
audio_unique_id_t AudioFlinger::nextUniqueId(audio_unique_id_use_t use)
{
// This is the internal API, so it is OK to assert on bad parameter.
LOG_ALWAYS_FATAL_IF((unsigned) use >= (unsigned) AUDIO_UNIQUE_ID_USE_MAX);
const int maxRetries = use == AUDIO_UNIQUE_ID_USE_SESSION ? 3 : 1;
for (int retry = 0; retry < maxRetries; retry++) {
// The cast allows wraparound from max positive to min negative instead of abort
// 这个和java中的cas一个套路呀,都是原子操作
// audio_unique_id_t包含两部分(2进制模式表示): xxxxxx... 后面3位是usage,前面(左边)的是真正的id,每次+1,因为是从8开始,所以每次+8,也就是AUDIO_UNIQUE_ID_USE_MAX。
uint32_t base = (uint32_t) atomic_fetch_add_explicit(&mNextUniqueIds[use],
(uint_fast32_t) AUDIO_UNIQUE_ID_USE_MAX, memory_order_acq_rel);
ALOG_ASSERT(audio_unique_id_get_use(base) == AUDIO_UNIQUE_ID_USE_UNSPECIFIED);
// allow wrap by skipping 0 and -1 for session ids
if (!(base == 0 || base == (~0u & ~AUDIO_UNIQUE_ID_USE_MASK))) {
ALOGW_IF(retry != 0, "unique ID overflow for use %d", use);
return (audio_unique_id_t) (base | use);
}
}
// We have no way of recovering from wraparound
LOG_ALWAYS_FATAL("unique ID overflow for use %d", use);
// TODO Use a floor after wraparound. This may need a mutex.
}
AudioRecord
中的 openRecord_l()// frameworks/av/media/libmedia/AudioRecord.cpp
// must be called with mLock held
status_t AudioRecord::openRecord_l(const Modulo &epoch, const String16& opPackageName)
{
// 获取AudioFlinger实例(binder代理)
const sp& audioFlinger = AudioSystem::get_audio_flinger();
if (audioFlinger == 0) {
ALOGE("Could not get audioflinger");
return NO_INIT;
}
......
audio_io_handle_t input;
// mFlags (not mOrigFlags) is modified depending on whether fast request is accepted.
// After fast request is denied, we will request again if IAudioRecord is re-created.
status_t status;
// Not a conventional loop, but a retry loop for at most two iterations total.
// Try first maybe with FAST flag then try again without FAST flag if that fails.
// Exits loop normally via a return at the bottom, or with error via a break.
// The sp<> references will be dropped when re-entering scope.
// The lack of indentation is deliberate, to reduce code churn and ease merges.
for (;;) {
status = AudioSystem::getInputForAttr(&mAttributes, &input,
mSessionId,
// FIXME compare to AudioTrack
mClientPid,
mClientUid,
mSampleRate, mFormat, mChannelMask,
mFlags, mSelectedDeviceId);
//
sp record = audioFlinger->openRecord(input,
mSampleRate,
mFormat,
mChannelMask,
opPackageName,
&temp,
&flags,
mClientPid,
tid,
mClientUid,
&mSessionId,
¬ificationFrames,
iMem,
bufferMem,
&status);
.......
}
}
AudioSystem::getInputForAttr
的实现,走到IAudioPolicyService里头去了。// frameworks/av/media/libmedia/AudioSystem.cpp
status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
pid_t pid,
uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_input_flags_t flags,
audio_port_handle_t selectedDeviceId)
{
const sp& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
return aps->getInputForAttr(
attr, input, session, pid, uid,
samplingRate, format, channelMask, flags, selectedDeviceId);
}
AudioPolicyService
这家伙不单纯呀,里面好复杂呀android.media.AudioRecord
: frameworks/base/media/java/android/media/android_media_AudioRecord.cpp
: frameworks/base/core/jni/AudioRecord.h
: frameworks/av/include/media/AudioRecord.cpp
: frameworks/av/media/libmedia/AudioSystem.cpp
: frameworks/av/media/libmedia/tinycap是alsa的用户空间的帮助工具,源码参见external/tinyalsa/tinycap.c
,可以用这个快速验证硬件是否正常,driver是否正常,Android的AudioFlinger
中会加载hal的实现,在s905x这个平台上是使用alsa来完成声音的回放、录制、音量控制等的硬件控制的。
cat /proc/asound/cards
0 [AMLM8AUDIO ]: AML-M8AUDIO - AML-M8AUDIO
AML-M8AUDIO
1 [Audio ]: USB-Audio - J-Make USB Audio
J-Make J-Make USB Audio at usb-xhci-hcd.0.auto-2.4, full speed