audio动态策略剖析之AudioPolicy,AudioMix,AudioMixingRule深入剖析

背景:

在音频子系统课程中讲解过AudioTrack获取对应播放Device,常规情况都在

frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp

代码中的getDevicesForStrategyInt方法中进行匹配判断获取。
传统路由方式这种设备路由方式选着device,其实对于正常手机等普通使用来说也是完全够用的。但是明显也发现出它的明显缺点,就是对于设备路由方式都是靠代码静态编码确定的,要修改的话只能靠修改Engine.cpp的源码才可以完成,灵活性太差,不容易扩展。

随着车机设备的引入及手机需求的不断增加,安卓就新引入了AudioPolicy来实现动态路由策略,
本文将重点介绍AudioPolicy这部分内容。

剖析java层面的AudioPolicy,AudioMix,AudioMixingRule

AudioPolicy一般主要是用于动态设备路由策略,从传统默认的代码写死固定策略中有了一个很好的动态扩展,大大增加了音频设备灵活选择。

AudioPolicy是设备路由策略的扩展接口,当有人创建了AudioPolicy对象,并注册给AudioPolicyService时,AudioPolicyService就会优先使用这个路由策略,而不会使用默认的硬编码策略。
AudioPolicy相关的源码是在/frameworks/base/media/java/android/media/audiopolicy/目录中。

在这里插入图片描述

这里主要研究一下AudioPolicy,AudioPolicyConfig,AudioMix,AudioMixingRule。

本想寻找关于这个的AudioPolicy等官方文档解释,但是代码注释中也没有啥解释,半天没找到,最后通过git进行查找,最后确实查找到了一个官方权威解释。

Author: Jean-Michel Trivi <jmtrivi@google.com>
Date:   Tue Jul 1 09:48:46 2014 -0700

    Define audio policy, mixes, and mixing rules
    
    An AudioMixingRule is a collection of AudioAttributes and match/exclude
       rules.
    An AudioMix is defined by its AudioMixingRule, AudioFormat and
       routing flag.
    An AudioPolicyConfig is a collection of AudioMix and is
       parcellable.
    An AudioPolicy has an AudioPolicyConfig, and
       can be registered/unregistered through AudioManager.
    
    bug 16009464
    

补充上面没说到的AudioPolicy类的作用,主要是提供对音频路由和音频焦点的访问管理权限。

/**
 * @hide
 * AudioPolicy provides access to the management of audio routing and audio focus.
 */
@SystemApi
public class AudioPolicy {

这几个类的关系,这里找到了一个非常好网络图片:
在这里插入图片描述

上面是AudioPolicy在Java层中的数据结构类图。所有的数据结构都保存在AudioPolicyConfig对象中,一个AudioPolicyConfig对象代表一个播放路由定制策略。

在AudioPolicyConfig对象中,会包含多个AudioMix对象。每个AudioMix对象代表的是一个播放设备的选择策略。所以,每个AudioMix对象只包含一个DeviceAddress的字符串。

每个播放Device对应一个匹配策略对象–AudioMixingRule,在AudioMixingRule中,可以包含多条匹配规则–AudioMixingRule.AudioMixMatchCriterion对象。

目前系统中支持的种类:


    /**
     * A rule requiring the usage information of the {@link AudioAttributes} to match.
     * This mixing rule can be added with {@link Builder#addRule(AudioAttributes, int)} or
     * {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of
     * {@link AudioAttributes}.
     */
    public static final int RULE_MATCH_ATTRIBUTE_USAGE = 0x1;
    /**
     * A rule requiring the capture preset information of the {@link AudioAttributes} to match.
     * This mixing rule can be added with {@link Builder#addRule(AudioAttributes, int)} or
     * {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of
     * {@link AudioAttributes}.
     */
    public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 0x1 << 1;
    /**
     * A rule requiring the UID of the audio stream to match that specified.
     * This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where the Object
     * parameter is an instance of {@link java.lang.Integer}.
     */
    public static final int RULE_MATCH_UID = 0x1 << 2;
    /**
     * A rule requiring the userId of the audio stream to match that specified.
     * This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where the Object
     * parameter is an instance of {@link java.lang.Integer}.
     */
    public static final int RULE_MATCH_USERID = 0x1 << 3;
    /**
     * A rule requiring the audio session id of the audio stream to match that specified.
     * This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where Object
     * parameter is an instance of {@link java.lang.Integer}.
     * @see android.media.AudioTrack.Builder#setSessionId
     */
    public static final int RULE_MATCH_AUDIO_SESSION_ID = 0x1 << 4;

    private final static int RULE_EXCLUSION_MASK = 0x8000;
    /**
     * @hide
     * A rule requiring the usage information of the {@link AudioAttributes} to differ.
     */
    public static final int RULE_EXCLUDE_ATTRIBUTE_USAGE =
            RULE_EXCLUSION_MASK | RULE_MATCH_ATTRIBUTE_USAGE;
    /**
     * @hide
     * A rule requiring the capture preset information of the {@link AudioAttributes} to differ.
     */
    public static final int RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET =
            RULE_EXCLUSION_MASK | RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET;
    /**
     * @hide
     * A rule requiring the UID information to differ.
     */
    public static final int RULE_EXCLUDE_UID =
            RULE_EXCLUSION_MASK | RULE_MATCH_UID;

    /**
     * @hide
     * A rule requiring the userId information to differ.
     */
    public static final int RULE_EXCLUDE_USERID =
            RULE_EXCLUSION_MASK | RULE_MATCH_USERID;

    /**
     * @hide
     * A rule requiring the audio session id information to differ.
     */
    public static final int RULE_EXCLUDE_AUDIO_SESSION_ID =
            RULE_EXCLUSION_MASK | RULE_MATCH_AUDIO_SESSION_ID;

主要介绍以下几个常用的:

RULE_MATCH_ATTRIBUTE_USAGE:通过AudioAttributes的Usage进行匹配。比如当一个AudioTrack在播放时,指定的Usage是USAGE_MEDIA类型,而Device1也定义了USAGE_MEDIA类型的匹配规则时,AudioPolicyManager就会选择Device1来播放这个AudioTrack的声音。

RULE_EXCLUDE_ATTRIBUTE_USAGE:通过AudioAttributes的Usage进行排除。排除的意思是指Device1不支持USAGE_MEDIA类型的声音播放。

RULE_MATCH_UID:通过APP的UID进行匹配。在Android中,每个3rd APP的UID都不一样。即当指定UID的APP进行声音播放时,AudioPolicyManager就会选择Device1来输出。

RULE_EXCLUDE_UID:通过APP的UID进行排除。意思是Device1不能播放指定UID的APP下发的声音。

RULE_MATCH_USERID:通过UserId进行匹配。上面提到的镜像播放(mirroringDevice)就是通过这个规则来实现的。意思是当指定UserId中的任何APP播放声音时,AudioPolicyManager都会选择Device1来输出。
RULE_EXCLUDE_USERID:通过UserId进行排除。意思是Device1不能播放指定UserId下的所有APP下发的声音。

native层面的AudioPolicy

上面只是AudioPolicy在java层介绍,但是java层面的AudioPolicy只是一些定义构造AudioMix,最后都是会转换成c++类型,而且需要通过跨进程方式传递到audioserver中的AudioPolicyManager中。

一般是系统App定义构造好AudioPolicy即AudioMix对象,然后通过AudioManager接口跨进程到system_server的AudioService,AudioService通过AudioSystem向AudioPolicyManager中注册AudioPolicy数据时,AudioSystem就会将Java层的数据结构转换成C++层的数据结构,注意只需要转换传递核心的AudioMix对象既可以。
在这里插入图片描述转换核心方法:
frameworks/base/core/jni/android_media_AudioSystem.cpp

static jint
android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz,
                                              jobject jMixesList, jboolean registration)
{
//省略
    status_t status;
    Vector <AudioMix> mixes;
    for (jint i = 0; i < numMixes; i++) {
        ScopedLocalRef<jobject> jAudioMix(env, env->GetObjectArrayElement(jMixes, i));
        if (!env->IsInstanceOf(jAudioMix.get(), gAudioMixClass)) {
            return AUDIO_JAVA_BAD_VALUE;
        }
        AudioMix mix;
        //java层面的AudioMix转c++
        if (jint jStatus = convertAudioMixToNative(env, &mix, jAudioMix.get());
            jStatus != AUDIO_JAVA_SUCCESS) {
            return jStatus;
        }
        mixes.add(mix);
    }

    ALOGV("AudioSystem::registerPolicyMixes numMixes %d registration %d", numMixes, registration);
    status = AudioSystem::registerPolicyMixes(mixes, registration);
    ALOGV("AudioSystem::registerPolicyMixes() returned %d", status);

    return nativeToJavaStatus(status);
}

看看转换方法convertAudioMixToNative,其实就是对java对象变量读取赋值

static jint convertAudioMixToNative(JNIEnv *env, AudioMix *nAudioMix, const jobject jAudioMix) {
    nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType);
    nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags);
    nAudioMix->mDeviceType =
            static_cast<audio_devices_t>(env->GetIntField(jAudioMix, gAudioMixFields.mDeviceType));

    jstring jDeviceAddress =
            static_cast<jstring>(env->GetObjectField(jAudioMix, gAudioMixFields.mDeviceAddress));
    const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL);
    nAudioMix->mDeviceAddress = String8(nDeviceAddress);
    env->ReleaseStringUTFChars(jDeviceAddress, nDeviceAddress);
    env->DeleteLocalRef(jDeviceAddress);

    nAudioMix->mCbFlags = env->GetIntField(jAudioMix, gAudioMixFields.mCallbackFlags);

    jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
    javaAudioFormatToNativeAudioConfig(env, &nAudioMix->mFormat, jFormat, false /*isInput*/);
    env->DeleteLocalRef(jFormat);

    jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule);
    nAudioMix->mAllowPrivilegedMediaPlaybackCapture =
            env->GetBooleanField(jRule, gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture);
    nAudioMix->mVoiceCommunicationCaptureAllowed =
            env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed);

    jobject jToken = env->GetObjectField(jAudioMix, gAudioMixFields.mToken);

    std::unique_ptr<AIBinder, decltype(&AIBinder_decStrong)>
            aiBinder(AIBinder_fromJavaBinder(env, jToken), &AIBinder_decStrong);
    nAudioMix->mToken = AIBinder_toPlatformBinder(aiBinder.get());

    nAudioMix->mVirtualDeviceId = env->GetIntField(jAudioMix, gAudioMixFields.mVirtualDeviceId);
    jint status = convertAudioMixingRuleToNative(env, jRule, &(nAudioMix->mCriteria));

    env->DeleteLocalRef(jRule);

    return status;
}

那么看看AudioMix在c++层面的情况:
frameworks/av/media/libaudioclient/include/media/AudioPolicy.h

class AudioMixMatchCriterion {
public:
    AudioMixMatchCriterion() {}
    AudioMixMatchCriterion(audio_usage_t usage, audio_source_t source, uint32_t rule);

    status_t readFromParcel(Parcel *parcel);
    status_t writeToParcel(Parcel *parcel) const;

    bool isExcludeCriterion() const;
    union {
        audio_usage_t   mUsage;
        audio_source_t  mSource;
        uid_t           mUid;
        int        mUserId;
        audio_session_t  mAudioSessionId;
    } mValue;
    uint32_t        mRule;
};

class AudioMix {
};

它们的对应关系是:

C++层的AudioMix对应的是Java层的AudioMix.java。
C++层的AudioMixMatchCriterion对应的是Java层的AudioMixingRule.AudioMixMatchCriterion。
C++层的mDeviceAddress字符串对应的是Java层的mDeviceAddress字符串。

AudioPolicyManager为了将播放Device与outputStream进行关联,定义了AudioPolicyMix类来继承扩展AudioMix类。在AudioPolicyMix类定义了成员变量SwAudioOutputDescriptor mOutput,用于表示此Device关联的outputStream。同时,定义了AudioPolicyMixCollection集合来保存所有的AudioPolicyMix对象。

剩下的AudioPolicyManager中如何使用这个AudioMix进行路由策略选择Device就下一篇文章再看,本文最重要讲解清楚动态路由的java,c++层面的AudioPolicy,AudioMix等类的作业和转换传递到audioserver进程的AudioPolicyManager中。

原文地址:https://mp.weixin.qq.com/s/fIXFvqMdp2J2PHermWL6Jw

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值