Android 录音实现追踪(Android 7.1)

未完待续

  • 最初的梦想
  • 冰山上面的部分
  • AudioRecord代码追踪
  • 代码位置
  • 追踪中的一些工具,技巧
    • tinycap
    • 列出alsa识别到的声卡

最初的梦想

哈哈哈哈哈哈,我就是想了解下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()
   }
}

AudioRecord代码追踪

  • 第一步的builder模式最重要的是创建了java层的AudioRecord对象
// 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());
    }
}
  • AudioRecord构造时最重要的是调用native的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层了,对于jni层代码的位置,有个6的不行的办法,google直接搜索java层类名称加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;
}
  • AudioSystem里面的神奇东西, 闹了半天,AudioSystem是个傀儡,实际就调到AudioFlinger或者AudioPolicyService里面去了,这个和java中的用法一样的。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.h中关于audio_unique_id_use_taudio_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

tinycap是alsa的用户空间的帮助工具,源码参见external/tinyalsa/tinycap.c,可以用这个快速验证硬件是否正常,driver是否正常,Android的AudioFlinger中会加载hal的实现,在s905x这个平台上是使用alsa来完成声音的回放、录制、音量控制等的硬件控制的。

列出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

你可能感兴趣的:(Audio,Android)