ANDROID音频系统散记之一:A2dpAudioInterface

http://blog.csdn.net/sepnic/article/details/6801591

 

写在之前


本来有打算写写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。


  1. AudioFlinger::AudioFlinger()  
  2.     : BnAudioFlinger(),  
  3.         mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)  
  4. {  
  5.     mHardwareStatus = AUDIO_HW_IDLE;  
  6.   
  7.     mAudioHardware = AudioHardwareInterface::create();  
  8.     ......  
AudioFlinger::AudioFlinger()
    : BnAudioFlinger(),
        mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
{
    mHardwareStatus = AUDIO_HW_IDLE;

    mAudioHardware = AudioHardwareInterface::create();
    ......

再看AudioHardwareInterface::create():

  1. AudioHardwareInterface* AudioHardwareInterface::create()  
  2. {  
  3.     /* 
  4.      * FIXME: This code needs to instantiate the correct audio device 
  5.      * interface. For now - we use compile-time switches. 
  6.      */  
  7.     AudioHardwareInterface* hw = 0;  
  8.     char value[PROPERTY_VALUE_MAX];  
  9.   
  10. #ifdef GENERIC_AUDIO   
  11.     hw = new AudioHardwareGeneric();  
  12. #else   
  13.     // if running in emulation - use the emulator driver   
  14.     if (property_get("ro.kernel.qemu", value, 0)) {  
  15.         LOGD("Running in emulation - using generic audio driver");  
  16.         hw = new AudioHardwareGeneric();  
  17.     }  
  18.     else {  
  19.         LOGV("Creating Vendor Specific AudioHardware");  
  20.         hw = createAudioHardware();  
  21.     }  
  22. #endif   
  23.     if (hw->initCheck() != NO_ERROR) {  
  24.         LOGW("Using stubbed audio hardware. No sound will be produced.");  
  25.         delete hw;  
  26.         hw = new AudioHardwareStub();  
  27.     }  
  28.       
  29. #ifdef WITH_A2DP   
  30.     hw = new A2dpAudioInterface(hw);  
  31. #endif   
  32.   
  33. #ifdef ENABLE_AUDIO_DUMP   
  34.     // This code adds a record of buffers in a file to write calls made by AudioFlinger.   
  35.     // It replaces the current AudioHardwareInterface object by an intermediate one which   
  36.     // will record buffers in a file (after sending them to hardware) for testing purpose.   
  37.     // This feature is enabled by defining symbol ENABLE_AUDIO_DUMP.   
  38.     // The output file is set with setParameters("test_cmd_file_name=<name>"). Pause are not recorded in the file.   
  39.     LOGV("opening PCM dump interface");  
  40.     hw = new AudioDumpInterface(hw);    // replace interface   
  41. #endif   
  42.     return hw;  
  43. }  
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设备接口。例如需要打开一个音频输出流时:

  1. AudioStreamOut* A2dpAudioInterface::openOutputStream(  
  2.         uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)  
  3. {  
  4.     if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {  
  5.         LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);  
  6.         return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);  
  7.     }  
  8.   
  9.     status_t err = 0;  
  10.   
  11.     // only one output stream allowed   
  12.     if (mOutput) {  
  13.         if (status)  
  14.             *status = -1;  
  15.         return NULL;  
  16.     }  
  17.   
  18.     // create new output stream   
  19.     A2dpAudioStreamOut* out = new A2dpAudioStreamOut();  
  20.     if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {  
  21.         mOutput = out;  
  22.         mOutput->setBluetoothEnabled(mBluetoothEnabled);  
  23.         mOutput->setSuspended(mSuspended);  
  24.     } else {  
  25.         delete out;  
  26.     }  
  27.   
  28.     if (status)  
  29.         *status = err;  
  30.     return mOutput;  
  31. }  
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,仅仅只有几个接口:

  1. int a2dp_init(int rate, int channels, a2dpData* dataPtr);  
  2. void a2dp_set_sink(a2dpData data, const char* address);  
  3. int a2dp_write(a2dpData data, const void* buffer, int count);  
  4. int a2dp_stop(a2dpData data);  
  5. 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时:

  1. ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)  
  2. {  
  3.     Mutex::Autolock lock(mLock);  
  4.   
  5.     size_t remaining = bytes;  
  6.     status_t status = -1;  
  7.   
  8.     if (!mBluetoothEnabled || mClosing || mSuspended) {  
  9.         LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \  
  10.                mBluetoothEnabled %d, mClosing %d, mSuspended %d",  
  11.                 mBluetoothEnabled, mClosing, mSuspended);  
  12.         goto Error;  
  13.     }  
  14.   
  15.     status = init();  
  16.     if (status < 0)  
  17.         goto Error;  
  18.   
  19.     while (remaining > 0) {  
  20.         status = a2dp_write(mData, buffer, remaining);  
  21.         if (status <= 0) {  
  22.             LOGE("a2dp_write failed err: %d\n", status);  
  23.             goto Error;  
  24.         }  
  25.         remaining -= status;  
  26.         buffer = ((char *)buffer) + status;  
  27.     }  
  28.   
  29.     mStandby = false;  
  30.   
  31.     return bytes;  
  32.   
  33. Error:  
  34.     // Simulate audio output timing in case of error   
  35.     usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);  
  36.   
  37.     return status;  
  38. }  
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()调用,下面简要介绍音频数据从上层到底层硬件设备的传输流向过程。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值