6

OpenHarmony3.1 按下音量按键后Audio service发生的哪些事

 2 years ago
source link: https://os.51cto.com/article/707091.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
OpenHarmony3.1 按下音量按键后Audio service发生的哪些事-51CTO.COM
OpenHarmony3.1 按下音量按键后Audio service发生的哪些事
作者:赵海鹏 2022-04-21 11:55:06
在Audio子系统中,AudioPolicy进程加载了多模子系统,并订阅的按键事件处理。根据按键的操作,触发音量调节接口,并通过PulseAudio调用HDI的接口,HDI接口通过Audio Dirver Model完成音量的调节。

87cedc203f5a902c38f93336b42b92f6780fea.png

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

OpenHarmony3.1 Release版本新增按键音量调节功能,如下图:

c252f6b716a1bf5a18547796c12be83722f34a.png

音量调节通俗的理解为按下按键,触发中断,中断处理函数中完成音量的调节。

那么在OpenHarmony Audio Service中是怎么完成的呢?

1.按键的注册,按键作为input模块,被多模输入子系统管理。

在Audio子系统中,AudioPolicy进程加载了多模子系统,并订阅的按键事件处理。根据按键的操作,触发音量调节接口,并通过PulseAudio调用HDI的接口,HDI接口通过Audio Dirver Model完成音量的调节。

5361cd9355f74310ebb682fb82ca2227557873.png

下文将展开描述各个模块具体的执行流程。

Key输入与多模输入

1.KEY的注册。

KEY信息配置与节点配置,见vendor/hihope/rk3568/hdf_config/khdf/input/input_config.hcs,通过RegisterKeyDevice将Vol+/Vol-注册到HDF_KEY模块中,由input统一管理。

C
keyConfig {
            keyList = ["power", "VolUp", "VolDown", "Up", "Down", "Left", "Right"];
            keyInfoList {
                key1 {
                    match_attr = "key_device0";
                    /* 0:touch 1:key 2:keyboard 3:mouse 4:button 5:crown 6:encoder */
                    inputType = 1;
                    keyName = "power";
                    gpioNum = 1;
                    irqFlag = 3;
                    debounceTime = 80;
                }
                key2 {
                    keyName = "volUp";
                    gpioNum = 31;
                    irqFlag = 1;
                    debounceTime = 80;
                }
                key3 {
                    keyName = "volDown";
                    gpioNum = 32;
                    irqFlag = 1;
                    debounceTime = 80;
                }
            }
        }

在openHarmony中KEY与多模输入的简单关系图。

a9f637626d3e4d0fa85284e62a38bd9c7be575.png

多模输入与AudioService如何联系

结合音频组件架构图,AudioPolicy主要完成Audio设备、音量等管理工作。

++
AudioPolicyServer完成依赖模块的加载,音量按键的调节与多模输入模块相关。
void AudioPolicyServer::OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId)
{
    switch (systemAbilityId) {
        case MULTIMODAL_INPUT_SERVICE_ID: // 3101
            ......
            SubscribeKeyEvents();
            break;
            ......
    }
}

772b27f31476501eb0a910b273cee3b22d2f27.png

2.SubscribeKeyEvents接口完成Vol+/Vol-按键事件的订阅,当前Input事件触发后会执行对应的动作。

C++
void AudioPolicyServer::SubscribeKeyEvents()
{
    // 按键信息配置
    MMI::InputManager *im = MMI::InputManager::GetInstance();
    std::set<int32_t> preKeys;
    std::shared_ptr<OHOS::MMI::KeyOption> keyOption_down = std::make_shared<OHOS::MMI::KeyOption>();
    keyOption_down->SetPreKeys(preKeys);
    keyOption_down->SetFinalKey(OHOS::MMI::KeyEvent::KEYCODE_VOLUME_DOWN);
    keyOption_down->SetFinalKeyDown(true);
    keyOption_down->SetFinalKeyDownDuration(0);
    // key触发的事件
    im->SubscribeKeyEvent(keyOption_down, [=](std::shared_ptr<MMI::KeyEvent> keyEventCallBack) {
        std::lock_guard<std::mutex> lock(volumeKeyEventMutex_);
        AudioStreamType streamInFocus = GetStreamInFocus();
        if (streamInFocus == AudioStreamType::STREAM_DEFAULT) {
            streamInFocus = AudioStreamType::STREAM_MUSIC;
        }
        // 获取当前音量
        float currentVolume = GetStreamVolume(streamInFocus);
        if (ConvertVolumeToInt(currentVolume) <= MIN_VOLUME_LEVEL) {
            for (auto it = volumeChangeCbsMap_.begin(); it != volumeChangeCbsMap_.end(); ++it) {
                std::shared_ptr<VolumeKeyEventCallback> volumeChangeCb = it->second;
                if (volumeChangeCb == nullptr) {
                    MEDIA_ERR_LOG("volumeChangeCb: nullptr for client : %{public}d", it->first);
                    continue;
                }
                volumeChangeCb->OnVolumeKeyEvent(streamInFocus, MIN_VOLUME_LEVEL, true);
            }
            return;
        }
        // 设置音量
        SetStreamVolume(streamInFocus, currentVolume-GetVolumeFactor(), true);
    });

Audio Service如何进行音量设置

1.接着上述订阅事件,AudioPolicyServer通过调用AudioPolicyService类,AudioPolicyService继而调用AudioAdapterManager::SetStreamVolume。

此接口中,通过完成WriteVolumeToKvStore音量值的保存(kv:Key,value),继而调用AudioServiceAdapter::mAudioServiceAdapter->SetVolume(streamType, volume);进行音量设置。

2.AudioServiceAdapter::SetVolume接口。

PulseAudio作为Audio服务,SetVolume通过调用PulseAudio的接口完成音量设置。

C++
int32_t PulseAudioServiceAdapterImpl::SetVolume(AudioStreamType streamType, float volume)
{
    ......
    pa_threaded_mainloop_lock(mMainLoop);
    pa_operation *operation = pa_context_get_sink_input_info_list(mContext,
        PulseAudioServiceAdapterImpl::PaGetSinkInputInfoVolumeCb, reinterpret_cast<void*>(userData.get()));
   ......
    return SUCCESS;
}

此接口中PaGetSinkInputInfoVolumeCb作为回调函数,通过pa_context_set_sink_input_volume接口完成音量设置的具体工作。

C++
void PulseAudioServiceAdapterImpl::PaGetSinkInputInfoVolumeCb(pa_context *c, const pa_sink_input_info *i, int       eol,void *userdata)
{
  ......
  pa_cvolume cv = i->volume;
  uint32_t volume = pa_sw_volume_from_linear(vol);
  pa_cvolume_set(&cv, i->channel_map.channels, volume);
  pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &cv, NULL, NULL));
  ......
  return;
}

上述类之间的关系见图,如下:

296af7128cea87ff10e450a297dc7488fa2ba3.png

pulseaudio如何与HDI接口对接

1.module-hdi-sink/module-hdi-source(foundation/multimedia/audio_standard/frameworks/native/pulseaudio/src/modules/hdi)作为PulseAudio的module,系统启动过程被audiopolicy进程加载启动。

2.renderer_sink_adapter(foundation/multimedia/audio_standard/frameworks/native/audiorenderer/src/renderer_sink_adapter.c) 作为hdi-sink和renderer之间的适配层,完成接口封装与定义。

C++
struct RendererSinkAdapter {
    int32_t (*RendererSinkInit)(const SinkAttr *attr);
    void (*RendererSinkDeInit)(void);
    int32_t (*RendererSinkStart)(void);
    int32_t (*RendererSinkStop)(void);
    int32_t (*RendererRenderFrame)(char *data, uint64_t len, uint64_t *writeLen);
    int32_t (*RendererSinkSetVolume)(float left, float right);
    int32_t (*RendererSinkGetLatency)(uint32_t *latency);
};

其中RendererSinkSetVolume接口完成音量设置,LoadSinkAdapter接口根据不同的设备类型,给出不同的接口实现。

C++
int32_t LoadSinkAdapter(const char *device, struct RendererSinkAdapter **sinkAdapter)
{
    ......
    if (!strcmp(device, g_deviceClassPrimary)) {
        MEDIA_INFO_LOG("%{public}s: primary device", __func__);
        ......
        adapter->RendererSinkSetVolume = AudioRendererSinkSetVolume;
        ......
        g_deviceClass = CLASS_TYPE_PRIMARY;
    } else if (!strcmp(device, g_deviceClassA2Dp)) {
        MEDIA_INFO_LOG("%{public}s: a2dp device", __func__);
        ......
        adapter->RendererSinkSetVolume = BluetoothRendererSinkSetVolume;
        ......
        g_deviceClass = CLASS_TYPE_A2DP;
    } else {
        MEDIA_ERR_LOG("%{public}s: Device not supported", __func__);
        free(adapter);
        return ERROR;
    }
    ......
}

3.RendererSinkSetVolume如何与HDI接口对接。

以primary device为例,RendererSinkSetVolume对应的实现为AudioRendererSinkSetVolume,调用g_audioRendrSinkInstance->SetVolume,即AudioRendererSink::SetVolume接口。

C++
int32_t AudioRendererSink::SetVolume(float left, float right)
{
    ......
    ret = audioRender_->volume.SetVolume(reinterpret_cast<AudioHandle>(audioRender_), volume);
    if (ret) {
        MEDIA_ERR_LOG("AudioRendererSink::Set volume failed!");
    }
    return ret;
}

audioRender的定义为struct AudioRender *audioRender,AudioRender即为HDI接口,其定义参考(drivers/peripheral/audio/interfaces/include/audio_render.h)。

HDI如何完成音量设置,可以参考Audio HDI的测试用例,本文将不描述(*drivers/peripheral/audio/test/systemtest/hdi/render/src/audio_hdirender_volume_test.cpp)。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

71eea7105a1cf9982d2996c42d853b97bd50ef.jpg


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK