背景描述:
此篇是对《Android音频框架之一 详解audioPolicy流程及HAL驱动加载》、《Android音频框架之二 用户录音启动流程源码走读》 和
《Android音频框架之三 用户录音启动流程源码走读 startRecord》的延续内容;本篇描述是 android7.1 源码中,如果增加 TinyAlsaHAL
的源码,并配置 Android 系统启动和加载库项目,移植 tinyalsa 至 hikey960-Androi7.1 平台,以完成这个系列博文所实现的功能。
Android 7.0 引入了一种新的音频政策配置文件格式 (XML),用于描述音频拓扑,Android 7.0 弃用了 audio_policy.conf,并增加了
对使用 XML 文件格式来定义音频拓扑的支持,这种文件格式更通俗易懂,具有多种编辑和解析工具,并且足够灵活,可以描述复杂的音频拓扑。
第一步 配置 audio_policy_configuration.xml 模式有效
1.1> Android7.1-RK3288 配置方法:
开启 USE_XML_AUDIO_POLICY_CONF 功能,并把配置文件内容拷贝至系统文件系统
@ device/rockchip/rk3288/device.mk
## config audio policy by ljb
USE_XML_AUDIO_POLICY_CONF := 1
PRODUCT_COPY_FILES += frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml:system/etc/audio_policy_configuration.xml\
frameworks/av/services/audiopolicy/config/a2dp_audio_policy_configuration.xml:system/etc/a2dp_audio_policy_configuration.xml\
frameworks/av/services/audiopolicy/config/usb_audio_policy_configuration.xml:system/etc/usb_audio_policy_configuration.xml\
frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:system/etc/r_submix_audio_policy_configuration.xml\
frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:system/etc/audio_policy_volumes.xml\
frameworks/av/services/audiopolicy/config/default_volume_tables.xml:system/etc/default_volume_tables.xml
添加库文件及调试工具的支持
@ device/rockchip/common/device.mk
# audio lib
PRODUCT_PACKAGES += \
audio_policy.$(TARGET_BOARD_HARDWARE) \
audio.primary.$(TARGET_BOARD_HARDWARE) \
audio.r_submix.default\
libaudioroute
# audio lib
PRODUCT_PACKAGES += \
libasound \
alsa.default \
acoustics.default \
libtinyalsa \
tinymix \
tinyplay \
tinycap \
tinypcminfo
1.2> Android8.1-hikey960 配置方法:
@ device/linaro/hikey/hikey960/device-hikey960.mk
增加如下配置内容
# Build HiKey960 HDMI audio HAL. Experimental only may not work. FIXME
USE_XML_AUDIO_POLICY_CONF := 1
PRODUCT_COPY_FILES += frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml:system/etc/audio_policy_configuration.xml\
frameworks/av/services/audiopolicy/config/a2dp_audio_policy_configuration.xml:system/etc/a2dp_audio_policy_configuration.xml\
frameworks/av/services/audiopolicy/config/usb_audio_policy_configuration.xml:system/etc/usb_audio_policy_configuration.xml\
frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:system/etc/r_submix_audio_policy_configuration.xml\
frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:system/etc/audio_policy_volumes.xml\
frameworks/av/services/audiopolicy/config/default_volume_tables.xml:system/etc/default_volume_tables.xml
# audio.primary configuration
PRODUCT_PACKAGES += audio.primary.$(TARGET_BOARD_PLATFORM)\
audio.r_submix.default
# TinyAlsaHAL configuration
PRODUCT_PACKAGES += \
libasound \
libaudioroute \
alsa.default \
acoustics.default \
libtinyalsa \
tinymix \
tinyplay \
tinycap \
tinypcminfo
第二处 修改 BOARD_USES_ALSA_AUDIO 有效
@ device/linaro/hikey/BoardConfigCommon.mk
#BOARD_USES_GENERIC_AUDIO := true # 关闭 GENERIC_AUDIO
BOARD_USES_ALSA_AUDIO := true # 开启 ALSA_AUDIO
第二步 修改音频配置文件
用文件夹中audio_policy_configuration.xml文件,把此文件覆盖
@ frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml
文件。
配置文件内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
<!-- version section contains a “version” tag in the form “major.minor” e.g version=”1.0” -->
<!-- Global configuration Decalaration -->
<globalConfiguration speaker_drc_enabled="true"/>
<modules>
<!-- Primary Audio HAL -->
<module name="primary" halVersion="2.5">
<attachedDevices>
<item>Speaker</item>
<item>Remote Submix Out</item>
<item>Built-In Mic</item>
<item>Remote Submix In</item>
</attachedDevices>
<defaultOutputDevice>Speaker</defaultOutputDevice>
<!-- “mixPorts”: listing all output and input streams exposed by the audio HAL -->
<mixPorts>
<mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="primary input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
<!-- r_submix -->
<mixPort name="r_submix output" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="r_submix input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="44100,48000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
</mixPorts>
<!-- “devicePorts”: a list of device descriptors for all input and output devices
accessible via this module. -->
<devicePorts>
<!-- Output devices declaration, i.e. Sink DEVICE PORT -->
<devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER" address="">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<gains>
<gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT"
minValueMB="-8400"
maxValueMB="4000"
defaultValueMB="0"
stepValueMB="100"/>
</gains>
</devicePort>
<devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="44100,48000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</devicePort>
<!-- add remote deviceport by ljb -->
<devicePort tagName="Remote Submix Out" role="sink" type="AUDIO_DEVICE_OUT_REMOTE_SUBMIX">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</devicePort>
<devicePort tagName="Remote Submix In" type="AUDIO_DEVICE_IN_REMOTE_SUBMIX" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="44100,48000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</devicePort>
</devicePorts>
<!-- route declaration, i.e. list all available sources for a given sink -->
<routes>
<route type="mix" sink="Speaker"
sources="primary output"/>
<route type="mix" sink="primary input"
sources="Built-In Mic"/>
<!-- r_submix route -->
<route type="mix" sink="Remote Submix Out"
sources="r_submix output"/>
<route type="mix" sink="r_submix input"
sources="Remote Submix In"/>
</routes>
</module>
</modules>
<!-- End of Modules section -->
<!-- Volume section -->
<xi:include href="audio_policy_volumes.xml"/>
<xi:include href="default_volume_tables.xml"/>
<!-- End of Volume section -->
</audioPolicyConfiguration>
第三步 移植TinyAlsaHAL 声卡驱动
在安卓系统源码中,把 device/linaro/hikey/audio 路径下文件夹内容清除掉,
把此文件夹下 audio.tar.bz2 文件解压至 device/linaro/hikey/ 路径下。
audio文件夹中 Android.mk 配置内容如下:
# Copyright (C) 2012 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_SRC_FILES := \
audio_setting.c \
audio_bitstream.c \
audio_hw.c \
alsa_route.c \
alsa_mixer.c \
voice_preprocess.c \
audio_hw_hdmi.c
LOCAL_C_INCLUDES += \
external/tinyalsa/include \
$(call include-path-for, audio-utils) \
$(call include-path-for, audio-route) \
$(call include-path-for, speex)
LOCAL_CFLAGS := -Wno-unused-parameter
ifeq ($(AUD_VOICE_CONFIG),voice_support)
LOCAL_CFLAGS += -DVOICE_SUPPORT
endif
LOCAL_CFLAGS += -Wno-error
LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libaudioroute libhardware_legacy libspeexresampler
LOCAL_STATIC_LIBRARIES := libspeex
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -Wno-error
LOCAL_SRC_FILES:= amix.c alsa_mixer.c
LOCAL_MODULE:= amix
LOCAL_PROPRIETARY_MODULE := true
LOCAL_SHARED_LIBRARIES := liblog libc libcutils
LOCAL_MODULE_TAGS:= debug
include $(BUILD_EXECUTABLE)
此 TinyAlsaHAL 中的 audio_hw.c 文件中;
此源码在RK3288平台上实现TinyAlsaHAL基础上修改如下:
@ hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
static int start_input_stream(struct stream_in *in)
{
struct audio_device *adev = in->dev;
int ret = 0;
//ALOGV("Debug_dump_info: %s : %d , in->device: 0x%04x \n ", __func__, __LINE__, in->device);
ALOGD("Debug_dump_info: %s : %d enter process", __func__, __LINE__ );
in_dump(in, 0);
route_pcm_open(getRouteFromDevice(in->device | AUDIO_DEVICE_BIT_IN));
if(in->input_source == 0x08){ //> 修改对 Remote_submix 类型支持
ALOGD("Debug_dump_info: %s : %d ,PCM_CARD:%d PCM_DEVICE:%d PCM_IN:%d ", __func__, __LINE__, PCM_CARD, PCM_DEVICE_SCO, PCM_IN);
in->pcm = pcm_open(PCM_CARD, PCM_DEVICE_SCO, PCM_IN, in->config);
}else{
ALOGD("Debug_dump_info: %s : %d ,PCM_CARD:%d PCM_DEVICE:%d PCM_IN:%d ", __func__, __LINE__, PCM_CARD, PCM_DEVICE, PCM_IN);
in->pcm = pcm_open(PCM_CARD, PCM_DEVICE, PCM_IN, in->config);
}
if (in->pcm && !pcm_is_ready(in->pcm)) {
ALOGE("Debug_dump_info: pcm_open() failed: %s", pcm_get_error(in->pcm));
pcm_close(in->pcm);
return -ENOMEM;
}
/* if no supported sample rate is available, use the resampler */
if (in->resampler)
in->resampler->reset(in->resampler);
in->frames_in = 0;
adev->input_source = in->input_source;
adev->in_device = in->device;
adev->in_channel_mask = in->channel_mask;
ALOGD("Debug_dump_info: %s : %d , sourceType: %4x \n ", __func__, __LINE__, in->input_source);
if (in->device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)
start_bt_sco(adev);
/* initialize volume ramp */
in->ramp_frames = (CAPTURE_START_RAMP_MS * in->requested_rate) / 1000;
in->ramp_step = (uint16_t)(USHRT_MAX / in->ramp_frames);
in->ramp_vol = 0;;
ALOGD("Debug_dump_info: %s : %d exit process", __func__, __LINE__ );
return 0;
}
笔者在源码中修改对 Remote_submix 输入源类型支持,对应的声卡和PCM_DEVICE如源码中描述,读者如不是很了解对应关系,
请参考《ubuntu 20 使用命令行 snd-aloop 实现内录音、录制音乐播放器的音频》 博文内容,snd-aloop声卡驱动配置文件中,
已经指定此 PCM_DEVICE 为 1 时,通过 snd-aloop驱动获取的音频数据,就是系统播放音频内容。
第四步 修改源码
修改 @frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp 文件内容如下:
status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
pid_t pid,
uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_input_flags_t flags,
audio_port_handle_t selectedDeviceId)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
... //省略部分代码
{
Mutex::Autolock _l(mLock);
// the audio_in_acoustics_t parameter is ignored by get_input()
status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid,
samplingRate, format, channelMask,
flags, selectedDeviceId,
&inputType);
audioPolicyEffects = mAudioPolicyEffects;
ALOGD("Debug_dump_info: %s,%d Status:%d ", __func__, __LINE__, status);
if (status == NO_ERROR) {
// enforce permission (if any) required for each type of input
switch (inputType) {
case AudioPolicyInterface::API_INPUT_LEGACY:
break;
case AudioPolicyInterface::API_INPUT_TELEPHONY_RX:
// FIXME: use the same permission as for remote submix for now.
case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:
if (!captureAudioOutputAllowed(pid, uid)) {
//> close this code by ljb 2022-3-14,屏蔽下面两条代码
//ALOGE("Debug_dump_info: getInputForAttr() permission denied: capture not allowed");
//status = PERMISSION_DENIED;
}
break;
case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:
if (!modifyAudioRoutingAllowed()) {
ALOGE("Debug_dump_info: getInputForAttr() permission denied: modify audio routing not allowed");
status = PERMISSION_DENIED;
}
break;
case AudioPolicyInterface::API_INPUT_INVALID:
default:
LOG_ALWAYS_FATAL("getInputForAttr() encountered an invalid input type %d",
(int)inputType);
break;
}
ALOGD("Debug_dump_info: %s,%d Status:%d ", __func__, __LINE__, status);
}
if (status != NO_ERROR) {
ALOGD("Debug_dump_info: %s,%d Status:%d ", __func__, __LINE__, status);
if (status == PERMISSION_DENIED) {
mAudioPolicyManager->releaseInput(*input, session);
}
return status;
}
}
此源码仅屏蔽权限授权部分,其他未做修改。
case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:
if (!captureAudioOutputAllowed(pid, uid)) {
//> close this code by ljb 2022-3-14,屏蔽下面两条代码
//ALOGE("Debug_dump_info: getInputForAttr() permission denied: capture not allowed");
//status = PERMISSION_DENIED;
}
break;
第五步 编译验证
在 android8.1 源码中,设备选择 hikey960 来验证此部分功能, Android 源码编译成功,
因笔者的安卓8.1镜像是运行在容器中,实际测试情况待时间方便会分享那部分内容。
在 android7.1 系统 rk3288 平台笔者已经成功实现此功能,第一步 中相关修改就是基于
实验成功基础上。