天天看點

android聲音通道的切換

在進行通道切換時,為什麼會在原通道上設定一回在去設定新的通道

一、Application framework

在Application framework層級是app層的code,是通過android.media提供的API來與audio硬體進行互動動作,這部分的代碼是通過audio JNI來調用native代碼進而達到影響硬體的效果;

二、JNI

JNI部分的代碼是位于 frameworks/base/core/jni/和frameworks/base/media/jni 目錄下的;

三、Native framework

四、Binder IPC

Binder IPC通信是跨程序通信的手段,audio的這部分代碼位于frameworks/av/media/libmedia目錄下,并且命名都是以I開頭的;

五、Media Server

Audio Service是隸屬Media Server的,其代碼位于 frameworks/av/services/audioflinger,它是真正的與HAL層的實作進行互動的;

六、HAL

HAL層定義了Audio Service調用的标準接口,不同的硬體必須根據自己的情況來實作這個接口來讓硬體在android中正常的工作,是以可以在不影響應用層系統調用的情況下,更換不同的硬體。大大減少了系統耦合性;

七、Kernel Driver

Audio驅動是與硬體進行互動,并且實作HAL層的接口供上層正常調用,這裡,廠商可以選擇ALSA,OSS以及自定義的音頻驅動; (NOTE:如果選擇ALSA,android建議使用 external/tinyalsa目錄下的實作); 接下來就來說說通話時音頻通道的切換,但是往下看之前必須知道,對于Audio Path的切換,android有一政策管理器來幫我們配置設定好輸入輸出的裝置,比如當手機播放音樂時,從Speaker播放出來,這時候插入耳機的話會從耳機裝置輸出;但是有時候我們想要自己去指定的話,就是我們接下來要說的了; 我們在通話時,要是開免提,實際上也就是Audio Path切換到了Speaker,也就是外方喇叭;代碼中的話調用一個函數即可,這是強制切換audio Path,不遵從系統的配置設定:

中間過程簡單不說,最終是調用到了JNI,android_media_AudioSystem中的android_media_AudioSystem_setForceUse()函數,來看下其具體實作:

android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config)
{
    return check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <audio_policy_force_use_t>(usage),
                                                           static_cast <audio_policy_forced_cfg_t>(config)));
}
           

顯而易見,它是調用了AudioSystem.cpp的setForceUse()函數,check_AudioSystem_Command()不說,重點看看audio_policy_force_use_t和audio_policy_forced_cfg_t這兩個結構體:

audio_policy_force_use_t 說明的是目前的Audio環境

audio_policy_forced_cfg_t 表示audio的輸入輸出裝置

它們是專門為setForceUse所用的;

/* usages used for audio_policy->set_force_use() */ 
typedef enum { 
AUDIO_POLICY_FORCE_FOR_COMMUNICATION, //表示的是通話過程中 
AUDIO_POLICY_FORCE_FOR_MEDIA, //媒體
AUDIO_POLICY_FORCE_FOR_RECORD, //錄音
AUDIO_POLICY_FORCE_FOR_DOCK, 
AUDIO_POLICY_FORCE_FOR_SYSTEM, 
AUDIO_POLICY_FORCE_USE_CNT, 
AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - , 
} audio_policy_force_use_t; 

 /* device categories used for audio_policy->set_force_use() */ 
typedef enum {
    AUDIO_POLICY_FORCE_NONE, 
    AUDIO_POLICY_FORCE_SPEAKER, 
    AUDIO_POLICY_FORCE_HEADPHONES, 
    AUDIO_POLICY_FORCE_BT_SCO, 
    AUDIO_POLICY_FORCE_BT_A2DP, 
    AUDIO_POLICY_FORCE_WIRED_ACCESSORY, 
    AUDIO_POLICY_FORCE_BT_CAR_DOCK, 
    AUDIO_POLICY_FORCE_BT_DESK_DOCK, 
    AUDIO_POLICY_FORCE_ANALOG_DOCK, 
    AUDIO_POLICY_FORCE_DIGITAL_DOCK, 
    AUDIO_POLICY_FORCE_NO_BT_A2DP, 
    /* A2DP sink is not preferred to speaker or wired HS */   
    AUDIO_POLICY_FORCE_SYSTEM_ENFORCED, 
    AUDIO_POLICY_FORCE_CFG_CNT, 
    AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE, 
} audio_policy_forced_cfg_t; 
           

這時候我們就應該知道,當我想要在通話時打開Speaker,傳遞的參數就是usage和config分别是AUDIO_POLICY_FORCE_FOR_COMMUNICATION和AUDIO_POLICY_FORCE_SPEAKER了,這兩個參數從上層一直到底層,還是很簡單的;

接着往下看就是調用的AudioSystem.cpp的setForceUse()函數了:

status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) 
{ 
    SLOGE("setForceUse() usage = %d, config = %d" ,usage , config);
    if (aps == ) return PERMISSION_DENIED; return aps->setForceUse(usage, config); 
} 
           

get_audio_policy_service()函數不做過多解釋,就是通過Native的ServiceManager來擷取audio policy的Service代理對象,進而實作與audio policy的程序間通訊;

....... 
 binder = sm->getService(String16("media.audio_policy"));
           

接下來就是調用frameworks/av/services/audioflinger/AudioPolicyService.cpp的setForceUse()函數了;

status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) 
{ 
    if (mpAudioPolicy == NULL) { 
        return NO_INIT; 
    } 
    if (!settingsAllowed()) { 
        return PERMISSION_DENIED; 
    } 
    if (usage <  || usage >= AUDIO_POLICY_FORCE_USE_CNT) { 
        return BAD_VALUE; 
    } 
    if (config <  || config >= AUDIO_POLICY_FORCE_CFG_CNT) { 
        return BAD_VALUE; 
    } 
    Mutex::Autolock _l(mLock); 
    mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config); 
    return NO_ERROR; 
} 
           

這個mpAudioPolicy是什麼呢?它的set_force_use函數在哪裡實作呢?這兩個問題需要了解就OK了; 首先mpAudioPolicy它是一個指針,在AudioServicePolicy.cpp的構造函數中被指派,來看看其指派過程:

...... 
const struct hw_module_t *module; 
...... 
rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module); 
...... 
rc = audio_policy_dev_open(module, &mpAudioPolicyDev); 
...... 
rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev,&aps_ops,this, &mpAudioPolicy);
 ...... 
           

首先AUDIO_POLICY_HARDWARE_MODULE_ID值是:

其次module是一個指針,指向的是一個hw_module_t結構體類型,它的作用是調用系統的哪個audio policy module,這個module可以是原始的,也可以由廠商自定義的

typedef struct hw_module_t { 
/** tag must be initialized to HARDWARE_MODULE_TAG */ 
    uint32_t tag; 
    uint16_t module_api_version; 
    #define version_major module_api_version 
    uint16_t hal_api_version; 
    #define version_minor hal_api_version 
    /** Identifier of module */ 
    const char *id; 
    const char *name; 
    const char *author; 
    /** Modules methods */ 
    struct hw_module_methods_t* methods; 
    /** module's dso */ 
    void* dso; 
    /** padding to 128 bytes, reserved for future use */ 
    uint32_t reserved[-]; 
} hw_module_t; 
           

再來看看是如何給module指派的: hardware.c

int hw_get_module(const char *id, const struct hw_module_t **module) 
{ 
    return hw_get_module_by_class(id, NULL, module); 
} 
           

看看hw_get_module_by_class方法的實作: hardware.c

int hw_get_module_by_class(const char *class_id, const char *inst, const struct hw_module_t **module) { 
    int status; 
    int i; const struct hw_module_t *hmi = NULL; 
    char prop[PATH_MAX]; char path[PATH_MAX]; 
    char name[PATH_MAX]; 
    if (inst) 
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
        strlcpy(name, class_id, PATH_MAX);
    /* * Here we rely on the fact that calling dlopen multiple times on
       * the same .so will simply increment a refcount (and not load
       * a new copy of the library).
       * We also assume that dlopen() is thread-safe. 
    */ 

    /* Loop through the configuration variants looking for a module */
    for (i= ; i<HAL_VARIANT_KEYS_COUNT+ ; i++)
    {
        if (i < HAL_VARIANT_KEYS_COUNT) { 
            if (property_get(variant_keys[i], prop, NULL) == ) { 
                continue; 
            } 
            snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH2, name, prop); 
            if (access(path, R_OK) == )
                break;
            snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH1, name, prop);
            if (access(path, R_OK) == )
                break;
        }
        else { 
            snprintf(path, sizeof(path), "%s/%s.default.so", HAL_LIBRARY_PATH1, name);
            if (access(path, R_OK) == )
                break; 
            } 
    } 
    status = -ENOENT; 
    if (i < HAL_VARIANT_KEYS_COUNT+) { 
    /* load the module, if this fails, we're doomed, and we should not try
     * to load a different variant.
     */ 
    status = load(class_id, path, module); 
    } 
    return status; 
} 
           

方法是找到指定的庫檔案并且加載;不做詳細介紹;這裡會得到audio_policy.default.so;這個庫正是編譯hardware/libhardware_legacy/audio出來的; 再跳回到AudioPolicyService的構造函數中來;接下來 :

java

它調用的是legacy_ap_dev_open()函數,不做詳細介紹:audio_policy_hal.cpp

static int legacy_ap_dev_open(const hw_module_t* module, const char* name, hw_device_t** device) 
{ 
    struct legacy_ap_device *dev; 
    if (strcmp(name, AUDIO_POLICY_INTERFACE) != ) 
        return -EINVAL; 
    dev = (struct legacy_ap_device *)calloc(, sizeof(*dev)); 
    if (!dev) return -ENOMEM; 
    dev->device.common.tag = HARDWARE_DEVICE_TAG;
    dev->device.common.version = ;
    dev->device.common.module = const_cast<hw_module_t*>(module);
    dev->device.common.close = legacy_ap_dev_close;
    dev->device.create_audio_policy = create_legacy_ap;
    dev->device.destroy_audio_policy = destroy_legacy_ap;
    *device = &dev->device.common; return ;
} 
           

create_audio_policy()中的aps_ops參數指針代表的是,它是AudioPolicyService與外界互動的接口:

struct audio_policy_service_ops aps_ops = { 
    open_output : aps_open_output, 
    open_duplicate_output : aps_open_dup_output, 
    close_output : aps_close_output,
    suspend_output : aps_suspend_output,
    restore_output : aps_restore_output,
    open_input : aps_open_input,
    close_input : aps_close_input,
    set_stream_volume : aps_set_stream_volume,
    set_stream_output : aps_set_stream_output,
    set_parameters : aps_set_parameters,
    get_parameters : aps_get_parameters,
    start_tone : aps_start_tone,
    stop_tone : aps_stop_tone,
    set_voice_volume : aps_set_voice_volume,
    move_effects : aps_move_effects,
    load_hw_module : aps_load_hw_module,
    open_output_on_module : aps_open_output_on_module,
    open_input_on_module : aps_open_input_on_module,
}; 
           

知道了這些,接下來看create_audio_policy(): create_audio_policy()這個函數作用是建立一個使用者自定義的policy_hal子產品的接口,因為我們使用的是qcom的晶片,qcom有自己的一套,android原生有自己的一套,就依照原生的來看吧;其實都是差不多的; 剛剛上面分析的legacy_ap_dev_open()函數有這樣一句:

...... 
dev->device.create_audio_policy = create_legacy_ap; 
...... 
           

那這樣我們就來看看其create_legacy_ap()函數吧;我們隻需要關注的是其中的那麼幾小段:

static int create_legacy_ap(const struct audio_policy_device *device, struct audio_policy_service_ops *aps_ops, void *service, struct audio_policy **ap)
{ 
    struct legacy_audio_policy *lap;
    ......
    lap = (struct legacy_audio_policy *)calloc(, sizeof(*lap));
    ......
    lap->policy.set_force_use = ap_set_force_use;
    ......
    lap->service = service;
    lap->aps_ops = aps_ops;
    lap->service_client = new AudioPolicyCompatClient(aps_ops, service);
    ......
    lap->apm = createAudioPolicyManager(lap->service_client);
    ......
    *ap = &lap->policy;
    ......
}
           

就這樣,AudioPolicyService.cpp的set_force_use()函數就調用到了這裡: audio_policy_hal.cpp

/* force using a specific device category for the specified usage */ 
 static void ap_set_force_use(struct audio_policy *pol, audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)
 {
     struct legacy_audio_policy *lap = to_lap(pol);
     lap->apm->setForceUse((AudioSystem::force_use)usage, (AudioSystem::forced_config)config);
 }
           

從之前的create_legacy_ap()函數我們知道apm的由來,

java lap->apm = createAudioPolicyManager(lap->service_client); 
           

createAudioPolicyManager()函數定義在AudioPolicyInterface.h接口中;

extern "C" 
AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface); 
           

而這個createAudioPolicyManager()由硬體廠商實作,傳回其AudioPolicyManager;qcom的實作是在AudioPolicyManagerALSA.cpp中;再往下不做具體分析了,主要是根據不同的政策來切換不同的Output和input裝置以及其他一些操作;如果想進一步分析的話,還需要關注AudioPolicyManagerBase.cpp; 其實準确的總結起來是AudioPolicyService是一個殼子,這個殼子的重要關鍵就是audio_policy,真正的實作可以由廠商來自己實作,當然android也有,就是AudioPolicyManagerDefault

繼續閱讀