Android 外部音频策略注册流程

1 篇文章 0 订阅
1 篇文章 0 订阅

目录

 

背景

外部音频焦点策略

外部音频路由策略


背景

Android P automovie版本支持对外部音频策略的注册,主要包括二部分:外部音频焦点策略 以及 外部音频路由策略。

原因可以直接看google官方说明,大意就是针对车载系统而言,音频焦点需求更复杂 同时 音频路由相比手机版而言更简洁(简洁非简单,车载版本上,很多针对音频路由的策略基本都用不上)。

外部音频焦点策略

车载版本上,由对应的Car模块,其中与音频相关的就是CarAudioService,其中setupDynamicRouting实现了注册(CarAudioService.java):

        AudioPolicy audioPolicy = getDynamicAudioPolicy();
        int r = mAudioManager.registerAudioPolicy(audioPolicy);
        if (r != AudioManager.SUCCESS) {
            throw new RuntimeException("registerAudioPolicy failed " + r);
        }
        mAudioPolicy = audioPolicy;

可以看到通过AudioManager来向系统注册策略,而其中创建的AudioPolicy对象,就包含了焦点以及路由策略。流程如下(CarAudioService.java):

//1. 创建policy build对象
AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);

//2. 注册外部音频路由策略
//2.1 创建Mix规则 build对象
AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder();
//2.2 注册路由规则,规则支持多种,见AudioMixingRule。这里采用usage匹配规则,意识是
//    根据应用播发音频时指定的usage/streamType来选择对应的输出设备。
mixingRuleBuilder.addRule(
                        new AudioAttributes.Builder().setUsage(usage).build(),
                        AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
//2.3 将mix规则与设备关联起来并创建Mix对象
//其中info为设备的相关信息,比如采样率、格式、通道数
AudioFormat mixFormat = new AudioFormat.Builder()
                    .setSampleRate(info.getSampleRate())
                    .setEncoding(info.getEncodingFormat())
                    .setChannelMask(info.getChannelCount())
                    .build();
//deviceinfo为具体的设备,RouteFlags表示为ROUTE_FLAG_RENDER表示对应输出,同一我们可以针对输入建立规则
AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build())
                        .setFormat(mixFormat)
                        .setDevice(info.getAudioDeviceInfo())
                        .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
                        .build();
//2.4 添加Mix规则,可以添加多个
builder.addMix(audioMix);

//3. 注册外部音频焦点策略
builder.setIsAudioFocusPolicy(true);
//mAudioPolicyFocusListener为AudioPolicy.AudioPolicyFocusListener实例
builder.setAudioPolicyFocusListener(mAudioPolicyFocusListener);

//4. 创建AudioPolicy对象
builder.build()

下面看registerAudioPolicy流程,通过AudioManager 实现(AudioManager.java):

//====================================================================
    // Audio policy
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public int registerAudioPolicy(@NonNull AudioPolicy policy) {
        ......
            //getConfig             路由策略Mix的封装
            //cb                    音频焦点回调对象
            //hasFocusListener      是否由焦点监听对象,与上文对应
            //isVolumeController    音量回调对象,即音量加、减、静音,有兴趣自己查看实现。
            String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
                    policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController());
            if (regId == null) {
                return ERROR;
            } else {
                //注册成功,设置状态
                policy.setRegistration(regId);
            }
            // successful registration
        ......
        return SUCCESS;
    }

AudioService实现(AudioService.java):

    public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
            boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
        //注册回调,当native Mix注册成功时会通知上层状态更新
        AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);

        ......
                //二次封装,实际注册地方
                AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener,isFocusPolicy, isVolumeController);
                //标准binder死亡监听
                pcb.asBinder().linkToDeath(app, 0/*flags*/);
                regId = app.getRegistrationId();
                //意味着支持多个策略。
                mAudioPolicies.put(pcb.asBinder(), app);
        ......
        return regId;
    }

下面是AudioPolicyProxy构造函数(AudioService.java):

AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token,
                boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
            ......
            //有外部焦点策略意味着mHasFocusListener不为空
            if (mHasFocusListener) {
                mMediaFocusControl.addFocusFollower(mPolicyCallback);
                // can only ever be true if there is a focus listener
                //只有当明确设置外部策略时才会采用外部焦点策略。
                if (isFocusPolicy) {
                    mIsFocusPolicy = true;
                    mMediaFocusControl.setFocusPolicy(mPolicyCallback);
                }
            }
            //音量控制回调
            if (mIsVolumeController) {
                setExtVolumeController(mPolicyCallback);
            }
            //开始注册Mix策略
            connectMixes();
        }

先看addFocusFollower,它只会被动通知焦点变化,而不会将焦点请求交给外部策略(MediaFocusControl.java):

private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();

    //addFocusFollower将监听加入mFocusFollowers集合
    void addFocusFollower(IAudioPolicyCallback ff) {
        ******
                mFocusFollowers.add(ff);
                notifyExtPolicyCurrentFocusAsync(ff);
        ******
    }

    //应用得到焦点通知
    void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
        for (IAudioPolicyCallback pcb : mFocusFollowers) {
            try {
                // oneway
                pcb.notifyAudioFocusGrant(afi, requestResult);
            } catch (RemoteException e) {
                Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
                        + pcb.asBinder(), e);
            }
        }
    }

    //应用失去焦点通知
    void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
        for (IAudioPolicyCallback pcb : mFocusFollowers) {
            try {
                // oneway
                pcb.notifyAudioFocusLoss(afi, wasDispatched);
            } catch (RemoteException e) {
                Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
                        + pcb.asBinder(), e);
            }
        }
    }
    
    

下面看setFocusPolicy,会将焦点赋值给本地mFocusPolicy对象(MediaFocusControl.java):

    void setFocusPolicy(IAudioPolicyCallback policy) {
        if (policy == null) {
            return;
        }
        synchronized (mAudioFocusLock) {
            mFocusPolicy = policy;
        }
    }

当应用申请焦点时,会判断mFocusPolicy是否为空,如果不为空,则通过外部焦点策略来实现逻辑判断(申请成功 or 失败)(MediaFocusControl.java):

    protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
            int sdk, boolean forceDuck) {
        ******
        synchronized(mAudioFocusLock) {
            ******
            final AudioFocusInfo afiForExtPolicy;
            if (mFocusPolicy != null) {
                // construct AudioFocusInfo as it will be communicated to audio focus policy
                afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(),
                        clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
                        flags, sdk);
            } else {
                afiForExtPolicy = null;
            }

            ******

            // external focus policy?
            if (mFocusPolicy != null) {
                //调用notifyExtFocusPolicyFocusRequest_syncAf通知应用请求
                if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) {
                    // stop handling focus request here as it is handled by external audio
                    // focus policy (return code will be handled in AudioManager)
                    return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY;
                } else {
                    // an error occured, client already dead, bail early
                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
                }
            }
        }
    }

    boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi,
            IAudioFocusDispatcher fd, IBinder cb) {
        ......
        try {
            //oneway
            //通过notifyAudioFocusRequest调用到外部焦点策略实现
            mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
            return true;
        } catch (RemoteException e) {
            Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
                    + mFocusPolicy.asBinder(), e);
        }
        return false;
    }

mFocusPolicy实现在AudioPolicy.java中,这里就补展开了。当外部焦点策略判断完成时,通过如下方式将结果告知给系统,系统在返回给应用(AudioManager.java):

    /**
     * @hide
     * Set the result to the audio focus request received through
     * {@link AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
     * @param afi the information about the focus requester
     * @param requestResult the result to the focus request to be passed to the requester
     * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
            @FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
        if (afi == null) {
            throw new IllegalArgumentException("Illegal null AudioFocusInfo");
        }
        if (ap == null) {
            throw new IllegalArgumentException("Illegal null AudioPolicy");
        }
        final IAudioService service = getService();
        try {
            service.setFocusRequestResultFromExtPolicy(afi, requestResult, ap.cb());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

需要注意的是,此流程有个时间限制,不能超过200ms,定义如下(AudioManager.java):

    /**
     * Timeout duration in ms when waiting on an external focus policy for the result for a
     * focus request
     */
    private static final int EXT_FOCUS_POLICY_TIMEOUT_MS = 200

 

外部音频路由策略

connectMixes简单调用AudioSystem.registerPolicyMixes(mMixes, true),它是native函数,实现在中,下面看registerPolicyMixes实现(android_media_AudioSystem.cpp):

static jint
android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz,
                                              jobject jMixesList, jboolean registration)
{
    ......

    status_t status;
    jint jStatus;
    jobject jAudioMix = NULL;
    Vector <AudioMix> mixes;
    //将Java的AudioMix转为native对象
    for (jint i = 0; i < numMixes; i++) {
        jAudioMix = env->GetObjectArrayElement(jMixes, i);
        if (!env->IsInstanceOf(jAudioMix, gAudioMixClass)) {
            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
            goto exit;
        }
        AudioMix mix;
        jStatus = convertAudioMixToNative(env, &mix, jAudioMix);
        env->DeleteLocalRef(jAudioMix);
        jAudioMix = NULL;
        if (jStatus != AUDIO_JAVA_SUCCESS) {
            goto exit;
        }
        mixes.add(mix);
    }
    ......
    //调用AudioSystem::registerPolicyMixes注册,注意此AudioSystem是native而非Java侧
    status = AudioSystem::registerPolicyMixes(mixes, registration);
    
    ......
    return jStatus;
}

通过convertAudioMixToNative函数将Java对象转为native对象,这里先看下AudioMix构造(AudioMix.java):

private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags,
            int deviceType, String deviceAddress) {
        //封装实际Rule对象
        mRule = rule;
        //音频 编码格式、采样率、通道数
        mFormat = format;
        /*
        * ROUTE_FLAG_RENDER 与 ROUTE_FLAG_LOOP_BACK 二选一,必须与mMixType对应
        */
        mRouteFlags = routeFlags;
        /*
        * AudioMix.MIX_TYPE_PLAYERS 与 AudioMix.MIX_TYPE_RECORDERS 二选一
        * 与Rule(AudioMixingRule#RULE_MATCH)相关:
        * players => RULE_MATCH_ATTRIBUTE_USAGE、RULE_MATCH_UID
        * recorders => RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET
        */
        mMixType = rule.getTargetMixType();
        //回调标识,仅内部使用
        mCallbackFlags = callbackFlags;
        //对应设备类型 AudioSystem.DEVICE_*
        mDeviceSystemType = deviceType;
        //对应设备地址
        mDeviceAddress = (deviceAddress == null) ? new String("") : deviceAddress;
    }

native构造如下,完全跟Java对应,这里就不详细说明(AudioPolicy.h):

class AudioMix {
public:
    // flag on an AudioMix indicating the activity on this mix (IDLE, MIXING)
    //   must be reported through the AudioPolicyClient interface
    static const uint32_t kCbFlagNotifyActivity = 0x1;

    AudioMix() {}
    AudioMix(Vector<AudioMixMatchCriterion> criteria, uint32_t mixType, audio_config_t format,
             uint32_t routeFlags, String8 registrationId, uint32_t flags) :
        mCriteria(criteria), mMixType(mixType), mFormat(format),
        mRouteFlags(routeFlags), mDeviceAddress(registrationId), mCbFlags(flags){}

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

    Vector<AudioMixMatchCriterion> mCriteria;
    uint32_t        mMixType;
    audio_config_t  mFormat;
    uint32_t        mRouteFlags;
    audio_devices_t mDeviceType;
    String8         mDeviceAddress;
    uint32_t        mCbFlags; // flags indicating which callbacks to use, see kCbFlag*
};

AudioSystem调用APS实现(AudioSystem.cpp):

status_t AudioSystem::registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;
    return aps->registerPolicyMixes(mixes, registration);
}

最终实现在APM中(AudioPolicyInterfaceImpl.cpp):

status_t AudioPolicyService::registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration)
{
    ......
    AutoCallerClear acc;
    //注意之前传递的值为true,表示注册
    if (registration) {
        return mAudioPolicyManager->registerPolicyMixes(mixes);
    } else {
        return mAudioPolicyManager->unregisterPolicyMixes(mixes);
    }
}

篇幅有限,AudioPolicyManager::registerPolicyMixes实现就不展开了,其本质就是通过循环,分别将LOOP_BACK、RENDER对应的AudioMix注册到mPolicyMixes对象中,方式如下:

//这里的address即设备地址,与之前AudioMix设置的deviceAddress基本一致,但可能会受AudioPatch影响,具体见代码
mPolicyMixes.registerMix(address, mixes[i], desc)

针对输出,会通过如下方式采用我们定义的路由策略:

//当输出设备状态发生变化时,保持策略同步更新,比如连接蓝牙耳机。
status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor>& devDesc,
                                                   audio_policy_dev_state_t state,
                                                   SortedVector<audio_io_handle_t>& outputs,
                                                   const String8& address)
{
    ******
                    addOutput(output, desc);
                    //当设备地址不为空时,判断是否有设置外部策略,如果有,则直接关联此策略
                    if (device_distinguishes_on_address(device) && address != "0") {
                        sp<AudioPolicyMix> policyMix;
                        if (mPolicyMixes.getAudioPolicyMix(address, policyMix) != NO_ERROR) {
                            ALOGE("checkOutputsForDevice() cannot find policy for address %s",
                                  address.string());
                        }
                        policyMix->setOutput(desc);
                        desc->mPolicyMix = policyMix;
                    }
    ******
}

//在播发音频时,拦截
status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
                                              audio_io_handle_t *output,
                                              audio_session_t session,
                                              audio_stream_type_t *stream,
                                              uid_t uid,
                                              const audio_config_t *config,
                                              audio_output_flags_t *flags,
                                              audio_port_handle_t *selectedDeviceId,
                                              audio_port_handle_t *portId)
{
    ......
    sp<SwAudioOutputDescriptor> desc;
    //优先通过mPolicyMixes获取对应arrt的设备
    if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) {
        ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
        if (!audio_has_proportional_frames(config->format)) {
            return BAD_VALUE;
        }
        *stream = streamTypefromAttributesInt(&attributes);
        *output = desc->mIoHandle;
        ALOGV("getOutputForAttr() returns output %d", *output);
        return NO_ERROR;
    }
    ......
}

针对输入:

//在录制音频时,拦截
status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
                                             audio_io_handle_t *input,
                                             audio_session_t session,
                                             uid_t uid,
                                             const audio_config_base_t *config,
                                             audio_input_flags_t flags,
                                             audio_port_handle_t *selectedDeviceId,
                                             input_type_t *inputType,
                                             audio_port_handle_t *portId)
{
    ......

    audio_devices_t device;
    //针对地址不为空的外部设备
    if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
            strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
        status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
        if (status != NO_ERROR) {
            goto error;
        }
        *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
        device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
        address = String8(attr->tags + strlen("addr="));
    } else {
        //一般来说,走这里,其它情况
        device = getDeviceAndMixForInputSource(inputSource, &policyMix);
    }
    ......
}

audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource,
                                                                  sp<AudioPolicyMix> *policyMix)
{
    audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
    audio_devices_t selectedDeviceFromMix =
           mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix);

    //如果有定义策略,直接返回
    if (selectedDeviceFromMix != AUDIO_DEVICE_NONE) {
        return selectedDeviceFromMix;
    }
    return getDeviceForInputSource(inputSource);
}

注意策略不一定需要通过Java注册,也可以直接native方式,比如现在有个需求是当插入USB扬声器时(当前前提是设备必须支持),指定的音源类型(比如Media)需要通过USB扬声器来播发,而其它的不受影响,则可以在添加如下代码增加动态策略(因为上层粗初始化时没有USB设备,所以不能直接通过上层方式来配置):

status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor>& devDesc,
                                                   audio_policy_dev_state_t state,
                                                   SortedVector<audio_io_handle_t>& outputs,
                                                   const String8& address)
{
    ******

                    //注意这里看必须与USB识别的设备类型匹配
                    } else if(FORCE_USB_SPEAKER && (device & AUDIO_DEVICE_OUT_USB_HEADSET)){
                        AudioMix audioMix;
                        audioMix.mMixType = MIX_TYPE_PLAYERS;//AudioMix.MIX_TYPE_PLAYERS
                        audioMix.mRouteFlags = MIX_ROUTE_FLAG_RENDER;//AudioMix.ROUTE_FLAG_RENDER;
                        audioMix.mDeviceType = desc->device();
                        audioMix.mDeviceAddress = address;
                        audioMix.mCbFlags = 0;//AudioMix.mCallbackFlags
                        audioMix.mFormat.sample_rate = desc->mSamplingRate;
                        audioMix.mFormat.channel_mask = desc->mChannelMask;
                        audioMix.mFormat.format = desc->mFormat;

                        AudioMixMatchCriterion criterion1;
                        criterion1.mRule = RULE_MATCH_ATTRIBUTE_USAGE;
                        criterion1.mValue.mUsage = AUDIO_USAGE_MEDIA;

                        AudioMixMatchCriterion criterion2;
                        criterion2.mRule = RULE_MATCH_ATTRIBUTE_USAGE;
                        criterion2.mValue.mUsage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;

                        audioMix.mCriteria.add(criterion1);
                        audioMix.mCriteria.add(criterion2);

                        if (mPolicyMixes.registerMix(address, audioMix, desc) == NO_ERROR) {
                            ALOGD("Success to registerMix for tbox output device");
                        }else{
                            ALOGD("Failed to registerMix for tbox output device");
                        }
                    }
    ******
}
                    

当然,当设备被移除时,也需要删除此策略,读者可以自行实现。

  • 7
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值