记录Android Audio学习中的疑问

记录Android Audio学习中的疑问

简介

本篇博文,主要是记录博主在学习Audio时遇到的问题,不仅仅限于Android,许多问题有自己的理解,有些则挂着等待日后的理解!



Android领域


AudioPolicyManager中的mAvailableOutputDevices成员变量是什么?

答案:它的类型是DeviceVector,也就是一个设备集合,这个集合里面装的是DeviceDescriptor对象,这个对象是通过接卸audio_policy_configuration.xml里面的devicePort标签解析而来c++对象的,如下:

<devicePort tagName="Speaker" type="AUDIO_DEVICE_IN_AUX_DIGITAL" role="source"
                        address="Aux0_in">
  <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
          samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
</devicePort>

但是,不是每个devicePort转化的DeviceDescriptor都能加入到mAvailableOutputDevices成员上去,必须已经绑定的设备才行,也就是说Android硬件已经存在的audio设备才行,如下:

<attachedDevices>
    <item>Speaker</item>
</attachedDevices>

attachedDevices标签就是已经存在的设备,这个item标签和devicePort标签同时都有才能加入到mAvailableOutputDevices成员


IOProfile、AudioPort、AudioProfile中关于Dynamic动态的理解?

首先,虽然他们三个都有关于Dynamic关键字的方法,但是最终Dynamic相关函数都指向AudioProfile的Dynamic方法;

<mixPort name="usb_accessory output" role="source">  
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
        samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<profile name="" format="AUDIO_FORMAT_PCM_8_BIT"
		samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>

如上述摘取usb_audio_policy_configuration.xml文件的部分代码,IOProfile就是一个mixPort,AudioPort就是内部所有的profile标签,就是一个集合,AudioProfile就对应一个profile标签;这样理解Dynamic最终都执行AudioProfile都好理解;看看最终AudioProfile的isDynamic方法:

bool isDynamic() {
    
     
return mIsDynamicFormat || mIsDynamicChannels || mIsDynamicRate; 
}

只要format、channel和SampleRate任意一个的状态是dynamic就表明这个AudioProfile是Dynamic;而上面三个状态都是set设置进去的,那哪些地方又set这些变量呢?如下:
a. 在解析configuration文件时又设置

Return<AudioProfileTraits::Element> AudioProfileTraits::deserialize(const xmlNode *cur,
        PtrSerializingCtx /*serializingContext*/)
{
    
    
    std::string samplingRates = getXmlAttribute(cur, Attributes::samplingRates);
    std::string format = getXmlAttribute(cur, Attributes::format);
    std::string channels = getXmlAttribute(cur, Attributes::channelMasks);

    Element profile = new AudioProfile(formatFromString(format, gDynamicFormat),
            channelMasksFromString(channels, ","),
            samplingRatesFromString(samplingRates, ","));
	//gDynamicFormat=AUDIO_FORMAT_DEFAULT=0就是一个无效值
    profile->setDynamicFormat(profile->getFormat() == gDynamicFormat);
    profile->setDynamicChannels(profile->getChannels().isEmpty());
    profile->setDynamicRate(profile->getSampleRates().isEmpty());

    return profile;
}

当解析xml文件得到的sampleRate、channel为空,format等于0,他们的dynamic状态变量就是true;
b. 从hal层得到的参数

ssize_t AudioProfileVector::addProfileFromHal(const sp<AudioProfile> &profileToAdd)
{
    
    
     // Check valid profile to add:这个fomat是否有效,不能等于默认值
    if (!profileToAdd->hasValidFormat()) {
    
    
        return -1;
    }
    if (!profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
    
    
        FormatVector formats;
        formats.add(profileToAdd->getFormat());
        setFormats(FormatVector(formats));
        return 0;
    }
    if (!profileToAdd->hasValidChannels() && profileToAdd->hasValidRates()) {
    
    
        setSampleRatesFor(profileToAdd->getSampleRates(), profileToAdd->getFormat());
        return 0;
    }
    if (profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
    
    
        setChannelsFor(profileToAdd->getChannels(), profileToAdd->getFormat());
        return 0;
    }
    // Go through the list of profile to avoid duplicates 前明三个if说明format channel和sample都有效
    for (size_t profileIndex = 0; profileIndex < size(); profileIndex++) {
    
    
        const sp<AudioProfile> &profile = itemAt(profileIndex);
        if (profile->isValid() && profile == profileToAdd) {
    
    
            // Nothing to do
            return profileIndex;
        }
    }   
        //dynamic动态是否可以理解为,这个profileToAdd可以删除
    profileToAdd->setDynamicFormat(true); // set the format as dynamic to allow removal
    return add(profileToAdd);
}

代码执行到这里 profileToAdd->setDynamicFormat(true),标签这个profileToAdd的三个参数:format、channel和sampleRate都是有效的,叶会把他的formatDynamic设置为true;

总结

  1. 从xml解析出来的sampleRate、format和channel参数为空时dynamic为true
  2. 从hal层得到的完整有值的上面三个参数,也会把format设置为dynamic
  3. 只要一个参数的dynamic状态为true,那AudioProfile的Dynamic就为true
    最后,这个Dynamic属性表明这个AudioProfile是可以动态从AudioPort中remove掉的

open打开的ouput是什么?

status_t SwAudioOutputDescriptor::open(const audio_config_t *config,
                                       const DeviceVector &devices,
                                       audio_stream_type_t stream,
                                       audio_output_flags_t flags,
                                       audio_io_handle_t *output)

参数解释:
devices是集合,不过一般里面只有一个device,往下层调用时都是取的device的type类型,如AUDIO_DEVICE_OUT_EARPIECE、AUDIO_DEVICE_OUT_SPEAKER等等
flags:是各种状态量,一般是mixport标签里面的flag,如AUDIO_OUTPUT_FLAG_DIRECT等
函数解释:

  1. open函数会从AudioPolicyManager、AudioPolicyService、AudioFlinger依次调用,通过HIDL方式调用到hal层,hal层则会根据device所属的module,根据module的name去获取module对应的so;
  2. open在hal层实质就是调用的open_output_stream,相当于每个open就是打开一条stream;如下:
static int adev_open_output_stream(struct audio_hw_device *dev,
                                   audio_io_handle_t handle,
                                   audio_devices_t devices,
                                   audio_output_flags_t flags,
                                   struct audio_config *config,
                                   struct audio_stream_out **stream_out,
                                   const char *address)
{
    
    
struct stream_out *out;
out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
out->flags = flags;
//把上层传递过来的device保存在out流结构体中
 out->devices = devices;   
 out->handle = handle;
 out->dev = adev;
....
//省略中的代码也没有对device做处理
//但flags参数很重要,省略中很多代码都需要flag做判断进而不同的处理
.....
}

综上,open调用了hal的open_output_stream函数,devices保存到了stream_out结构体中,除此之外并没有做其它的逻辑,所以这里可以回答一个问题;

AudioPolicyManager中在initialize初始化中会把所有的device设备打开吗?

因为initialize初始化函数,会遍历所有的module、IOProfile,并且与mAvailableOutputDevices设备取交集,也就是说只要配置文件中的attachedDevices标签的设备都会被打开,也就是硬件上已经绑定到系统上的音频设备都会去调用open函数打开;

但是这个打开是有局性的,open只会调用到hal的open_output_stream为止,并没有继续往下kernel打开真正的设备业务逻辑,也是合理的,不可能系统上电后就打开所有的音频设备,都还没真正用设备,你现在打开岂不是耗费电量等资源;只有在往里面写音频数据时才会打开与kenel的连接,如下流程:
out_write() --> start_output_stream()

在start_output_stream中就会选择内核设备声卡号和设备号,如:pcmC0D1P 0号声卡的1号播放设备,那这个声卡设备选择的标准是什么呢?

选择声卡设备的标准

这里就要用到open_output_stream的device了,或者address等,不同产商可能不一样;因为不同的音频设备挂载的声卡不一样,假如speaker挂载在0号声卡的1号设备,那就会选择0号声卡1号设备

总结

open函数打开,是以stream为概念的流,程序结构中用stream标识,在open_output_stream并没有打开实际的设备,只是保存了相应的传入参数;在真正写入音频数据时才会调用alsa打开内核音频设备,选择声卡设备,写入数据


再次总结openOutput链路

在这里插入图片描述

  • 第一处红框,遍历profile实质就是遍历audio_conf里面的mixport标签,也就是数据流
  • 第二处红框,经历4个步骤下来后,可能两个不同的profile得到的device是同一个,如下:
<defaultOutputDevice>Speaker</defaultOutputDevice>

<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="deep_buffer" role="source"
	flags="AUDIO_OUTPUT_FLAG_DEEP_BUFFER">
	<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
		samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>

<route type="mix" sink="Speaker" sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/>

如上配置,流为primary out和deep_buffer的支持设备都包含Speaker,并且Speaker又是默认的输出设备,所以经历了第二个红框后输出的device是一样的,都会Speaker

  • 第三处红框,按照第二个红框的案例,不同的profile,虽然选择了相同的device–Speaker,但是都会向Hal打开属于自己的专属stream,并且创建两个PlaybackThread并保存

AudioTrack创建时底层构建的对象链路

如下图:
在这里插入图片描述
在PlaybackThread中,每一个Track对应应用端一个AudioTrack,所以PlaybackThread作为服务端,用集合来保存了众多Track,创建时用mTrack集合;

同理一个TrackClientDescriptor也对应一个应用端AudioTrack,但是它保存在AudioOutputDescriptor的mClients中;
以上两个集合中如何对应,就是靠唯一标识portId;

当业务开始时,最上层AudioTrack调用play开始播放音频时,会层层调用start()方法,在PlaybackThread::track的start方法时,会调用其addTrack_l()函数,将track加入到PlaybackThread的另一个集合mActiveTrack中,同时在TrackClientDescriptor中激活client的状态setClientActive;



alsa领域


tinyplay、tinypcminfo等工具编译及使用

别人写的很好了,直接跳转如何查看声卡、pcm设备以及tinyplay、tinymix、tinycap的使用查看吧!

猜你喜欢

转载自blog.csdn.net/jackzhouyu/article/details/129302415