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

发布于 2022-4-19 09:46
浏览
2收藏

作者:润和软件 赵海鹏
OpenHarmony3.1 Release版本新增按键音量调节功能,如下图:
OpenHarmony3.1 按下音量按键后Audio service发生的哪些事-开源基础软件社区
音量调节通俗的理解为按下按键,触发中断,中断处理函数中完成音量的调节。

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

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

在Audio子系统中,AudioPolicy进程加载了多模子系统,并订阅的按键事件处理。根据按键的操作,触发音量调节接口,并通过PulseAudio调用HDI的接口,HDI接口通过Audio Dirver Model完成音量的调节。
OpenHarmony3.1 按下音量按键后Audio service发生的哪些事-开源基础软件社区
下文将展开描述各个模块具体的执行流程

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与多模输入的简单关系图
OpenHarmony3.1 按下音量按键后Audio service发生的哪些事-开源基础软件社区

多模输入与AudioService如何联系

  1. 结合音频组件架构图,AudioPolicy主要完成Audio设备、音量等管理工作。
C++
AudioPolicyServer完成依赖模块的加载,音量按键的调节与多模输入模块相关。

void AudioPolicyServer::OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId)

{

    switch (systemAbilityId) {

        case MULTIMODAL_INPUT_SERVICE_ID: // 3101

            ......

            SubscribeKeyEvents();

            break;

            ......

    }

}

OpenHarmony3.1 按下音量按键后Audio service发生的哪些事-开源基础软件社区

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;

}

上述类之间的关系见图,如下:
OpenHarmony3.1 按下音量按键后Audio service发生的哪些事-开源基础软件社区

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)。
::: hljs-center

::: hljs-center

附录

:::

1.https://gitee.com/openharmony/multimedia_audio_standard/blob/master/README_zh.md

2.https://gitee.com/openharmony/drivers_peripheral/blob/master/input/README_zh.md

3.https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-peripherals-audio-des.md

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-4-19 09:46:47修改
3
收藏 2
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐