侧边栏
首页代码

手机投屏音频流转在无系统权限中的应用

背景

手机投屏处理音频流转问题 中介绍了反射android.media.AudioSystem类的setDeviceConnectionState方法来达到音频流转方案,此方案是基于系统权限的,也就是说具有系统签名并且拥有android:sharedUserId="android.uid.system",如果没有这个权限咋整?

因公司需要把之前的投屏项目脱离系统权限并准备上Google Play,所以不能在拥有系统权限了,因此之前投屏项目的好的功能需要能适配就适配不能的只能去掉,所以投屏分成了两个app:一个app是没有任何系统权限的,另外一个app则是拥有签名权限的,没有系统权限的app将来是要上Google Play的,拥有签名权限的内置在自己手机里,因此把上面实现流转的功能放在了拥有签名权限app里,两个app之间通过AIDL进程间通信。

替代的API

当我把相关音频流转API放入到拥有签名权限app里时去执行时,虽然反射执行方法没啥问题,但结果就是没有效果,此时细心的可以发现日志有一句打印:

经过分析源码可以发现android.media.AudioSystem类的setDeviceConnectionState方法成功反射执行了,但是在调用C++层就出现了问题:

看来没有system uid权限还是不行的,于是我就在系统源码里各种寻找其他方案。

最终在我不懈努力终于被我找到了AudioManager类的setWiredDeviceConnectionState
setWiredDeviceConnectionState

哇,感觉发现了新大陆,这个api比android.media.AudioSystem类的setDeviceConnectionState方法好用多了,不用费那么大的劲进行反射,而且Android13及以下的api参数基本上是一致的,直接调用就行了。但是这里我们通过源码的api方法上面可以发现:此方法是需要android.Manifest.permission.MODIFY_AUDIO_ROUTING权限的,而且这个权限只能是系统签名的app能使用,所以还是得把这个api得调用放入到拥有签名权限的app中。

public class AudioUtils {
    private static final String TAG = AudioUtils.class.getSimpleName();
    public static void setWiredDeviceConnectionState(Context context,int state){
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        audioManager.setWiredDeviceConnectionState(AudioManager.DEVICE_OUT_REMOTE_SUBMIX,state,"","");
    }
}

音频卡顿问题

当实现了后音频可以正常进程流转,但是又一个新问题,就是流转后得音频播放出来是卡顿的,一开始我以为是我的Sink端有问题,直到我在Source端把录音保存本地后,发现是音频录取有问题。

于是又是排查一番发现是调用setWiredDeviceConnectionState方法传入的AudioManager.DEVICE_OUT_REMOTE_SUBMIX参数问题,最终发现可以使用AudioManager.DEVICE_OUT_WIRED_HEADPHONE来替代,表示有线耳机输出
DEVICE_OUT_WIRED_HEADPHONE

posted @ 2023-05-24 17:02  咸鱼Jay  阅读(156)  评论(0编辑  收藏  举报
页脚HTML代码