1. 程式人生 > >Android Framework學習筆記 -- Audio調節音量流程

Android Framework學習筆記 -- Audio調節音量流程

Audio調節音量流程

流程圖 Audio音量調節是一級一級調節,而且分不同的流型別,如響鈴,通話,多媒體等。不同的裝置(藍芽裝置)的設定方法有所區別。

sdk的api,設定相應流的音量。不同的流index的範圍不一樣

//--->frameworks/base/media/java/android/media/AudioManager.java
public void setStreamVolume(int streamType, int index, int flags) {
    IAudioService service = getService();
    try {
        service.setStreamVolume(streamType, index, flags, 
            getContext().getOpPackageName());
    } catch (RemoteException e) {
        Log.e(TAG, "Dead object in setStreamVolume", e);
    }
}

java層Service實現,volume的調節的實現是用state模式來實現,可能需要原子性或不同的模式下調節音量的操作不同。

//--->frameworks/base/services/core/java/com/android/server/audio/AudioService.java
private void setStreamVolume(int streamType, int index, int flags, 
    String callingPackage,String caller, int uid) {

    ...(檢查引數)
    ...(轉換引數)

    // 獲取裝置
    final int device = getDeviceForStream(streamType);
    ...(特殊處理a2dp)
    ...(檢查uid,實體按鍵調節音量需要判斷當前使用者?)

    synchronized (mSafeMediaVolumeState) {
        mPendingVolumeCommand = null;
        oldIndex = streamState.getIndex(device);
        index = rescaleIndex(index * 10, streamType, streamTypeAlias);

        ...(特殊處理a2dp)
        ...(特殊處理HDMI)
        ...(設定一些標誌位,如標記一些不可調節音量的裝置)

        //檢查當前是否可設定音量
        if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
            // 不可以則生成PendingCommand,等待合適的時機
            mVolumeController.postDisplaySafeVolumeWarning(flags);
            mPendingVolumeCommand = new StreamVolumeCommand(
                streamType, index, flags, device);
        } else {
            // 設定音量
            onSetStreamVolume(streamType, index, flags, device, 
                caller);
            index = mStreamStates[streamType].getIndex(device);
        }
    }

    // 傳送更新音量資訊
    sendVolumeUpdate(streamType, oldIndex, index, flags);
}

private void onSetStreamVolume(int streamType, int index, int flags, 
    int device,String caller) {
    final int stream = mStreamVolumeAlias[streamType];
    // 設定音量
    setStreamVolumeInt(stream, index, device, false, caller);

    ...(判斷音量是否為0,調節模式(靜音或響鈴))
    mStreamStates[stream].mute(index == 0);
}

private void setStreamVolumeInt(int streamType,int index,int device,
    boolean force,String caller) {
    VolumeStreamState streamState = mStreamStates[streamType];
    if (streamState.setIndex(index, device, caller) || force) {
        // Post message to set system volume (it in turn will post a message
        // to persist).
        sendMsg(mAudioHandler,MSG_SET_DEVICE_VOLUME,SENDMSG_QUEUE,device,
            0,streamState,0);
    }
}

  @Override
public void handleMessage(Message msg) {
    ...
    switch (msg.what) {
        case MSG_SET_DEVICE_VOLUME:
            setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
            break;
        ...
    }
    ...
}

private void setDeviceVolume(VolumeStreamState streamState, int device) {

    synchronized (VolumeStreamState.class) {
        // 設定音量
        streamState.applyDeviceVolume_syncVSS(device);

        ...(Apply change to all streams using this one as alias)
    }

    // Post a persist volume msg
    sendMsg(mAudioHandler,MSG_PERSIST_VOLUME,SENDMSG_QUEUE,device,0,
        streamState,PERSIST_DELAY);
}

public void applyDeviceVolume_syncVSS(int device) {
    int index;
    if (mIsMuted) {
        index = 0;
    } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 
        && mAvrcpAbsVolSupported) || ((device & mFullVolumeDevices) != 0)) {
        index = (mIndexMax + 5)/10;
    } else {
        index = (getIndex(device) + 5)/10;
    }
    AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}

AudioSystem的setStreamVolumeIndex是個native函式,從這裡到跳到c++程式碼

//--->frameworks/base/media/java/android/media/AudioSystem.java
public static native int setStreamVolumeIndex(int stream, int index,
    int device);

jni的程式碼沒做處理,直接轉發給c++層的AudioSystem

//--->frameworks/base/core/jni/android_media_AudioSystem.cpp
static JNINativeMethod gMethods[] = { ...
 {"setStreamVolumeIndex","(III)I",   (void *)android_media_AudioSystem_setStreamVolumeIndex},
...};

static jint android_media_AudioSystem_setStreamVolumeIndex(
    JNIEnv *env,jobject thiz,jint stream,jint index,jint device)
{
    return (jint) check_AudioSystem_Command(AudioSystem::setStreamVolumeIndex(
        static_cast <audio_stream_type_t>(stream),index,(audio_devices_t)device));
}

AudioSystem又踢給AudioPolicyService(這裡是binder通訊,從這裡跳到服務端處理)

//--->frameworks/av/media/libmedia/AudioSystem.cpp
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
    int index,audio_devices_t device)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;
    return aps->setStreamVolumeIndex(stream, index, device);
}

AudioPolicyService做了些許可權和引數檢查,轉發給AudioPolicyManager

//---frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
    int index,audio_devices_t device)
{
    if (mAudioPolicyManager == NULL) return NO_INIT;
    if (!settingsAllowed()) return PERMISSION_DENIED;
    if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) return BAD_VALUE;
    Mutex::Autolock _l(mLock);
    return mAudioPolicyManager->setStreamVolumeIndex(stream,index,device);
}

AudioPolicyManager的處理比較複雜,主要是包括了音訊策略的判斷

//--->frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
    int index,audio_devices_t device)
{

    ...(檢查音量及裝置是否為audio裝置)
    ...(策略判斷)
    if ((device != AUDIO_DEVICE_OUT_DEFAULT) && 
        (device & (strategyDevice | accessibilityDevice)) == 0) {
        return NO_ERROR;
    }

    ...(設定每個輸出裝置的音量)
    status_t volStatus = checkAndSetVolume(stream, index, desc, curDevice);
    ...
}

status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,int index,
    const sp<AudioOutputDescriptor>& outputDesc,audio_devices_t device,int delayMs,bool force)
{
    ...(do not change actual stream volume if the stream is muted)
    ...(do not change in call volume if bluetooth is connected and vice versa)

    // 聲音等級與真正引數的轉換
    float volumeDb = computeVolume(stream, index, device);  
    // 設定輸出裝置的聲音
    outputDesc->setVolume(volumeDb, stream, device, delayMs, force);

    // 設定通話的音量??
    if (stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) {
            ...
            mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
            ...
        }
    }
    return NO_ERROR;
}

AudioOutputDescriptor是音訊裝置描述符,outputDesc是SwAudioOutputDescriptor型別。

//--->/frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
 bool AudioOutputDescriptor::setVolume(float volume,audio_stream_type_t stream,
    audio_devices_t device __unused,uint32_t delayMs,bool force)
{
    // We actually change the volume if:
    // - the float value returned by computeVolume() changed
    // - the force flag is set
    if (volume != mCurVolume[stream] || force) {
        ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs);
        mCurVolume[stream] = volume;
        return true;
    }
    return false;
}

bool SwAudioOutputDescriptor::setVolume(float volume,audio_stream_type_t stream,
    audio_devices_t device,uint32_t delayMs,bool force)
{
    bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, 
        delayMs, force);

    if (changed) {
        // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
        // enabled
        float volume = Volume::DbToAmpl(mCurVolume[stream]);
        if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
            mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume,
                mIoHandle, delayMs);
        }
        mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
    }
    return changed;
}

AudioOutputDescriptor的mClientInterface是AudioPolicyService,所以會轉到AudioPolicyService的setStreamVolume
AudioPolicyService非同步執行這個操作,最後會轉到AudioSystem的setStreamVolume。

//--->frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
int AudioPolicyService::setStreamVolume(audio_stream_type_t stream,float volume,
    audio_io_handle_t output,int delayMs)
{

    return (int)mAudioCommandThread->volumeCommand(stream, volume,output, delayMs);
}

status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream,
    float volume,audio_io_handle_t output,int delayMs)
{
    ...(封裝了一下data跟command)
    return sendCommand(command, delayMs);
}

status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs) {...(一些命令佇列的操作)}

// 處理函式
bool AudioPolicyService::AudioCommandThread::threadLoop()
{
    ...
    while (!exitPending())
    {
        ...
        switch (command->mCommand) {
        ...
        case SET_VOLUME: 
            ...(Lock)
            VolumeData *data = (VolumeData *)command->mParam.get();
            command->mStatus = AudioSystem::setStreamVolume(data->mStream,
                data->mVolume,data->mIO);
        break;
        ...
    }
}

AudioSystem又轉到AudioFlinger

//--->frameworks/av/media/libmedia/AudioSystem.cpp 
status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,
    audio_io_handle_t output)
{
    ...(許可權引數檢查)
    af->setStreamVolume(stream, value, output);
    return NO_ERROR;
}

AudioFlinger會去獲取output對應的PlaybackThread並設定PlaybackThread的音量,如果output == AUDIO_IO_HANDLE_NONE,則設定所有PlaybackThread的音量。

//--->frameworks/av/services/audioflinger/AudioFlinger.cpp
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
        audio_io_handle_t output)
{
    ...(許可權檢查)
    ...(流型別檢查)

    AutoMutex lock(mLock);
    ...(獲取對應裝置的PlaybackTread)

    // ???
    mStreamTypes[stream].volume = value;

    if (thread == NULL) {   // output == AUDIO_IO_HANDLE_NONE
        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
            mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
        }
    } else {
        thread->setStreamVolume(stream, value);
    }

    return NO_ERROR;
}

PlaybackThread設定mStreamTypes的volume。並喚醒PlaybackThread執行緒

void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
    Mutex::Autolock _l(mLock);
    mStreamTypes[stream].volume = value;
    broadcast_l();
}

不同型別的Thread貌似有不同使用方法 MixerThread是在prepareTracks_l裡使用,最後會設定AudioMixer的引數

//--->frameworks/av/services/audioflinger/Threads.cpp
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
    Vector< sp<Track> > *tracksToRemove) {
    ...
    // FastTrack
    track->mCachedVolume = masterVolume * mStreamTypes[track->streamType()].volume;

    ...
    // NormalTrack
    // 這裡涉及到了左右聲道的音量的計算
    // compute volume for this track
    uint32_t vl, vr;       // in U8.24 integer format
    float vlf, vrf, vaf;   // in [0.0, 1.0] float format
    float typeVolume = mStreamTypes[track->streamType()].volume;
    float v = masterVolume * typeVolume;
    ...
    //計算完設定混音器的引數
    mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
    mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
    mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);
    ...
}

// 最後會呼叫到mAudioMixer的setVolumeRampVariables
static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,
    int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,
    float *pSetVolume, float *pPrevVolume, float *pVolumeInc){...}

DirectOutputThread在processVolume_l裡使用(processVolume_l在prepareTracks_l中被呼叫)
processVolume_l直接設定了輸出裝置的volume

//--->frameworks/av/services/audioflinger/Threads.cpp   
void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack) {
    ...
    float typeVolume = mStreamTypes[track->streamType()].volume;
    float v = mMasterVolume * typeVolume;
    ...(一系列的設定)
    if (mOutput->stream->set_volume) {
        mOutput->stream->set_volume(mOutput->stream, left, right);
    }
}

還有其他的幾種Thread都是上面兩種Thread的子類,處理方式是一致的

相關推薦

Android Framework學習筆記 -- Audio調節音量流程

Audio調節音量流程 Audio音量調節是一級一級調節,而且分不同的流型別,如響鈴,通話,多媒體等。不同的裝置(藍芽裝置)的設定方法有所區別。 sdk的api,設定相應流的音量。不同的流index的範圍不一樣 //--->frameworks/b

Android Framework學習(四)之Launcher啟動流程解析

在之前的部落格中,我們學習了init程序、Zygote程序和SyetemServer程序的啟動過程,我們知道SystemServer程序主要用於啟動系統的各種服務,二者其中就包含了負責啟動Launcher的服務,LauncherAppService,本篇部落格我

Game Framework學習筆記(2):初識流程(Procedure)

Game Framework學習筆記的第二篇:流程 流程是貫穿遊戲執行時整個生命週期的有限狀態機。 之前的文章: Game Framework學習筆記(1):初識Game Framework 流程介紹 在介紹流程之前,我們得先了解什麼是有限狀態機。 有限狀態機

Android usb學習筆記:Android AOA協議Android流程總結

背景 上篇文章中我們瞭解了嵌入式裝置端將Android手機設定為accessory模式的流程以及嵌入式裝置端接收和傳送資料的流程,本文將對應介紹Android端accessory模式被啟用的過程,以及接下來如何與嵌入式裝置端進行通訊。本文的原始碼下載地址:ht

Android:日常學習筆記(7)———探究UI開發(1)

tac calling repl action its 內容 schema lesson try Android:日常學習筆記(7)———探究UI開發(1) 常用控件的使用方法 TextView 說明:TextView是安卓中最為簡單的一個控件,常用來在界面上顯示一段文本信

Android:日常學習筆記(7)———探究UI開發(4)

this 活動 eal enc panel .html http 中間 編寫 Android:日常學習筆記(7)———探究UI開發(4) UI概述 View 和 ViewGrou   Android 應用中的所有用戶界面元素都是使用 View 和 ViewGroup 對象

Android:日常學習筆記(9)———探究廣播機制

ora rri enabled cas 管理 encoding protect 其他 acc Android:日常學習筆記(9)———探究廣播機制 引入廣播機制 Andorid廣播機制   廣播是任何應用均可接收的消息。系統將針對系統事件(例如:系統啟動或設備開始充電時)傳

Android:日常學習筆記(10)———使用LitePal操作數據庫

分享 數據 turn find netstat price 彈出 category 模式 Android:日常學習筆記(10)———使用LitePal操作數據庫 引入LitePal 什麽是LitePal   LitePal是一款開源的Android數據庫框架,采用了對象關系

Entity Framework學習筆記——EF簡介(一篇文章告訴你什麽是EF)

比較 編程 ast 定義 .aspx b2c 文件創建 發送 ase Entity Framework是以ADO.NET為基礎,面向數據的“實體框架”。以下簡稱EF。 它利用了抽象化數據結構的方式,將每個數據庫對象都轉換成應用程序對象 (entity),

Entity Framework學習筆記——edmx文件

tinc metadata glob 兩個類 color empty 完成 文件中 文件的 上文簡單介紹了一下Entity FrameWork,這裏說一下EF的核心——edmx文件。 在VisualStudio中建立edmx文件(此例環境為Visual

quasar-framework學習筆記

quasar quasar-framework ui 框架 vue官方網站: http://quasar-framework.org/ 一、安裝 1、 安裝quasar-cli npm install -g quasar-cli 2、 使用quasar starter kits

Linux學習筆記之內核啟動流程與模塊機制

oid img 相關 call rootfs _exit alt 執行 分模塊 本文旨在簡單的介紹一下Linux的啟動流程與模塊機制: Linux啟動的C入口位於/Linux.2.6.22.6/init/main.c::start_kernel() 下圖簡要的描述了一下內核

MYSQL進階學習筆記三:MySQL流程控制語句!(視頻序號:進階_7-10)

sls @age 分享 流程 null set oop 默認 soft 知識點四:MySQL流程控制語句(7-10) 選擇語句:   (IF ELSE ELSE IF CASE 分支)IFNULL函數 IF語法: 語法規則:

Java學習筆記七:Java的流程控制語句之switch

獎品 出現 字符型 -s png 結束 ase 選項 分享圖片 Java條件語句之 switch   當需要對選項進行等值判斷時,使用 switch 語句更加簡潔明了。例如:根據考試分數,給予前四名不同的獎品。第一名,獎勵筆記本一臺;第二名,獎勵 IPAD 2 一個

Java學習筆記八:Java的流程控制語句之循環語句

AI 介紹 可見 while 兩個 流程控制 邏輯運算 ava 表示 Java的流程控制語句之循環語句 一:Java循環語句之 while;   生活中,有些時候為了完成任務,需要重復的進行某些動作。如參加 10000 米長跑,需要繞 400 米的賽道反復的跑 25

SharePoint framework學習筆記——實現bootstrap輪播效果

ots += [] http true ble work erb sequence 要實現的效果:根據不同列表名查詢不同的輪播數據 數據從SharePoint站點查詢出 項目目錄: 站點數據: 1. 創建spfx項目:詳見SharePoint framewor

Android核心學習筆記

0、android系統啟動 《Android系統啟動流程 -- bootloader》 《The Android boot process from power on》 《Android 啟動過程介紹》 《Android培訓班(86)核心執行之前的載入程式》 這是一系列

Go語言學習筆記(四) 流程控制

程式設計語言的流程控制語句,用於設定計算執行的次序,建立程式的邏輯結構。可以說,流程控制語句是整個程式的骨架。從根本上講,流程控制只是為了控制程式語句的執行順序,一般需要與各種條件配合,因此,在各種流程中,會加入條件判斷語句。流程控制語句一般起以下3個作用:   選擇,即根據

Android ToolBar學習筆記

前言 開發中經常遇到頂部導航欄的需求,5.0 之後Google為了統一設計風格,默認了ToolBar這個控制元件作為統一頂部欄,並且還支援了不少的動畫和各種設定,但是!有關ToolBar 的theme,Menu,click有不少的坑,今天來系統的學習一下。 內容 基於需求來學習。

Robot Framework學習筆記:List變數

用法: @{Val3}   Set Variable  1   2   3 或者: @{listVal3}  Create List   3   2  1