写在之前
本来有打算写写Android音频系统的,但是仔细研究了如下链接的三篇文章,果断中断了我的想法。毫不夸张来说,这是我看过的最好的阐述Android音频系统的文章了,简练精辟,将音频系统各个方面的重要的脉络都描述出来了。有这三篇文章,理解Android音频系统何止加快了10倍。
Android Audio System 之一:AudioTrack如何与AudioFlinger交换音频数据
Android Audio System 之二:AudioFlinger
Android Audio System 之三: AudioPolicyService 和 AudioPolicyManager
A2dpAudioInterface
Android音频系统有两大服务:一是AudioFlinger,二是AudioPolicyService。AudioFlinger负责向下访问AudioHardwareInterface,实现音频PCM数据的混音/输入/输出,实现音量调节;AudioPolicyService负责音频输入输出设备的连接状态,音频策略调度即音频设备(如本地CODEC、Bluetooth A2DP、Headset)的切换策略(注意它只是负责策略,真正的切换操作是在AudioFlinger中的openOutput,毕竟AudioFlinger负责操作底层音频硬件)。AudioPolicyService在以后的章节详细分析,这里主要探讨A2DP-Audio是如何注册到AudioFlinger中,并简要提及音频PCM数据流向。
好的平台软件应有这样的一个抽象层:向下提供一套固定的接口,不同的硬件设备根据这些接口实现各自的方法,然后注册到这个抽象层中去。这样对于上层应用而言并没有任何区别,因为上层只需调用抽象层接口就行了,不管底层硬件的差异性。AudioFlinger就是这样的一个抽象层,无论底层是ALSA设备还是BluetoothHeadset,上层都只会看到AudioFlinger的接口。至于何时切换到ALSA设备何时切换到BluetoothHeadset,这就属于音频策略调度范畴了即AudioPolicyService。
- AudioFlinger::AudioFlinger()
- : BnAudioFlinger(),
- mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
- {
- mHardwareStatus = AUDIO_HW_IDLE;
-
- mAudioHardware = AudioHardwareInterface::create();
- ......
AudioFlinger::AudioFlinger() : BnAudioFlinger(), mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1) { mHardwareStatus = AUDIO_HW_IDLE; mAudioHardware = AudioHardwareInterface::create(); ......再看AudioHardwareInterface::create():
- AudioHardwareInterface* AudioHardwareInterface::create()
- {
-
-
-
-
- AudioHardwareInterface* hw = 0;
- char value[PROPERTY_VALUE_MAX];
-
- #ifdef GENERIC_AUDIO
- hw = new AudioHardwareGeneric();
- #else
-
- if (property_get("ro.kernel.qemu", value, 0)) {
- LOGD("Running in emulation - using generic audio driver");
- hw = new AudioHardwareGeneric();
- }
- else {
- LOGV("Creating Vendor Specific AudioHardware");
- hw = createAudioHardware();
- }
- #endif
- if (hw->initCheck() != NO_ERROR) {
- LOGW("Using stubbed audio hardware. No sound will be produced.");
- delete hw;
- hw = new AudioHardwareStub();
- }
-
- #ifdef WITH_A2DP
- hw = new A2dpAudioInterface(hw);
- #endif
-
- #ifdef ENABLE_AUDIO_DUMP
-
-
-
-
-
- LOGV("opening PCM dump interface");
- hw = new AudioDumpInterface(hw);
- #endif
- return hw;
- }
AudioHardwareInterface* AudioHardwareInterface::create() { /* * FIXME: This code needs to instantiate the correct audio device * interface. For now - we use compile-time switches. */ AudioHardwareInterface* hw = 0; char value[PROPERTY_VALUE_MAX]; #ifdef GENERIC_AUDIO hw = new AudioHardwareGeneric(); #else // if running in emulation - use the emulator driver if (property_get("ro.kernel.qemu", value, 0)) { LOGD("Running in emulation - using generic audio driver"); hw = new AudioHardwareGeneric(); } else { LOGV("Creating Vendor Specific AudioHardware"); hw = createAudioHardware(); } #endif if (hw->initCheck() != NO_ERROR) { LOGW("Using stubbed audio hardware. No sound will be produced."); delete hw; hw = new AudioHardwareStub(); } #ifdef WITH_A2DP hw = new A2dpAudioInterface(hw); #endif #ifdef ENABLE_AUDIO_DUMP // This code adds a record of buffers in a file to write calls made by AudioFlinger. // It replaces the current AudioHardwareInterface object by an intermediate one which // will record buffers in a file (after sending them to hardware) for testing purpose. // This feature is enabled by defining symbol ENABLE_AUDIO_DUMP. // The output file is set with setParameters("test_cmd_file_name=<name>"). Pause are not recorded in the file. LOGV("opening PCM dump interface"); hw = new AudioDumpInterface(hw); // replace interface #endif return hw; }这个函数我在 ANDROID2.3音频系统HAL有简要的分析,现在我们接着往下看看A2DP的注册:
hw = new A2dpAudioInterface(hw);
注意红色部分hw,为什么A2dpAudioInterface还需要createAudioHardware()打开的 AudioHardwareInterface(我们假设这是ALSA设备接口)呢?如我们所知,BluetoothA2DP与ALSA设备并不走同一套接口,因此Android的设计者就把ALSA设备接口扔到A2DP接口里面管理了。这又是如何管理呢?简单来说,就是根据上层传下来的参数 devices,判断devices是否是DEVICE_OUT_BLUETOOTH_A2DP,如果是则走A2DP接口,如果不是则走ALSA设备接口。例如需要打开一个音频输出流时:
- AudioStreamOut* A2dpAudioInterface::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
- {
- if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
- LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
- return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
- }
-
- status_t err = 0;
-
-
- if (mOutput) {
- if (status)
- *status = -1;
- return NULL;
- }
-
-
- A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
- if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
- mOutput = out;
- mOutput->setBluetoothEnabled(mBluetoothEnabled);
- mOutput->setSuspended(mSuspended);
- } else {
- delete out;
- }
-
- if (status)
- *status = err;
- return mOutput;
- }
AudioStreamOut* A2dpAudioInterface::openOutputStream( uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices); return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status); } status_t err = 0; // only one output stream allowed if (mOutput) { if (status) *status = -1; return NULL; } // create new output stream A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) { mOutput = out; mOutput->setBluetoothEnabled(mBluetoothEnabled); mOutput->setSuspended(mSuspended); } else { delete out; } if (status) *status = err; return mOutput; }当上层传下来的devices不属于A2DP设备时,则return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);其中mHardwareInterface保存的是ALSA的hw。否则A2dpAudioStreamOut* out = new A2dpAudioStreamOut();为A2DP打开一个音频输出流。
liba2dp
到了A2dpAudioInterface这层,就是访问BlueZ的音频操作接口了,主要是external\bluetooth\bluez \audio\liba2dp.c。liba2dp.c代码或许很复杂,我也没有深入了解过,但是接口却非常简单易用。看liba2dp.h,仅仅只有几个接口:
- int a2dp_init(int rate, int channels, a2dpData* dataPtr);
- void a2dp_set_sink(a2dpData data, const char* address);
- int a2dp_write(a2dpData data, const void* buffer, int count);
- int a2dp_stop(a2dpData data);
- void a2dp_cleanup(a2dpData data);
int a2dp_init(int rate, int channels, a2dpData* dataPtr); void a2dp_set_sink(a2dpData data, const char* address); int a2dp_write(a2dpData data, const void* buffer, int count); int a2dp_stop(a2dpData data); void a2dp_cleanup(a2dpData data);a2dp_init:根据传入来的采样率rate,声道数 channels初始化一个a2dpData;
a2dp_set_sink:绑定一个蓝牙地址address到a2dpData上;
a2dp_write:往a2dp写入音频PCM数据;
a2dp_stop:停止a2dp播放。
例如,每当有音频PCM数据需要送入Bluetooth时:
- ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
- {
- Mutex::Autolock lock(mLock);
-
- size_t remaining = bytes;
- status_t status = -1;
-
- if (!mBluetoothEnabled || mClosing || mSuspended) {
- LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
- mBluetoothEnabled %d, mClosing %d, mSuspended %d",
- mBluetoothEnabled, mClosing, mSuspended);
- goto Error;
- }
-
- status = init();
- if (status < 0)
- goto Error;
-
- while (remaining > 0) {
- status = a2dp_write(mData, buffer, remaining);
- if (status <= 0) {
- LOGE("a2dp_write failed err: %d\n", status);
- goto Error;
- }
- remaining -= status;
- buffer = ((char *)buffer) + status;
- }
-
- mStandby = false;
-
- return bytes;
-
- Error:
-
- usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);
-
- return status;
- }
ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) { Mutex::Autolock lock(mLock); size_t remaining = bytes; status_t status = -1; if (!mBluetoothEnabled || mClosing || mSuspended) { LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \ mBluetoothEnabled %d, mClosing %d, mSuspended %d", mBluetoothEnabled, mClosing, mSuspended); goto Error; } status = init(); if (status < 0) goto Error; while (remaining > 0) { status = a2dp_write(mData, buffer, remaining); if (status <= 0) { LOGE("a2dp_write failed err: %d\n", status); goto Error; } remaining -= status; buffer = ((char *)buffer) + status; } mStandby = false; return bytes; Error: // Simulate audio output timing in case of error usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000); return status; }核心语句:status = a2dp_write(mData, buffer, remaining); 只需要传入音频数据的首地址和大小就行了。
该函数在AudioFlinger::MixerThread::threadLoop()调用,下面简要介绍音频数据从上层到底层硬件设备的传输流向过程。