侧边栏
首页代码

手机投屏处理音频流转问题

背景

公司项目中有手机投屏平板,声音默认是在手机端播放,也可以切换到平板上播放,这里使用的是反射android.media.AudioSystem类的setDeviceConnectionState方法实现的,这个在低于Android13的版本是没有问题的,然而到了Android13手机上确出现了问题,经过对比Android12和Android13发现,android.media.AudioSystem类的setDeviceConnectionState方法实现有差别

问题分析

Android12的源码

先看一下Android12的android.media.AudioSystem类的setDeviceConnectionState方法

  • device:音频输出设备,因为是录制的是系统声音,所以是AudioSystem.DEVICE_OUT_REMOTE_SUBMIX
  • state:流转状态,0:手机端,1:非手机端
  • device_address:设备地址,可以传空
  • device_name:设备名称,可以传空
  • codecFormat:编码格式,这里直接传0

反射Android12的代码如下:

public static void setDeviceConnectionStateTablet() {
	LogUtils.i(TAG, "set Connection State to Tablet");
	try {
		Class<?> audioSystem = Class.forName("android.media.AudioSystem");
		Method setDeviceConnectionState = audioSystem.getDeclaredMethod("setDeviceConnectionState",
				int.class, int.class, String.class, String.class, int.class);
		setDeviceConnectionState.setAccessible(true);
		setDeviceConnectionState.invoke(audioSystem, 0x8000, 1, "", "", 0);
	} catch (ClassNotFoundException
			| NoSuchMethodException
			| InvocationTargetException
			| IllegalAccessException e) {
		LogUtils.e(TAG, "SET CONNECTION STATE Error1: " + e.getCause());
	}
}

public static void setDeviceConnectionStatePhone() {
	LogUtils.i(TAG, "set Connection State to Phone" );
	try {
		Class<?> audioSystem = Class.forName("android.media.AudioSystem");
		Method setDeviceConnectionState = audioSystem.getDeclaredMethod("setDeviceConnectionState",
				int.class, int.class, String.class, String.class, int.class);
		setDeviceConnectionState.setAccessible(true);
		setDeviceConnectionState.invoke(audioSystem, 0x8000, 0, "", "", 0);
	} catch (ClassNotFoundException
			| NoSuchMethodException
			| InvocationTargetException
			| IllegalAccessException e) {
		LogUtils.e(TAG, "SET CONNECTION STATE Error3: " + e.getCause());
	}
}

Android13的源码

在看一下Android13的android.media.AudioSystem类的setDeviceConnectionState方法

这里参数跟Android12的是有所改变的,因此原来的反射代码会有问题。
这里的setDeviceConnectionState方法有两个方法重载,可以看到这两个重载方法里都没有音频输出设备的参数,因此我们可以先看看AudioDeviceAttributes是啥?我们继续看AudioDeviceAttributes的源码可以发现其构造函数有5个,其中有两个的参数就有音频输出设备和设备地址,那么我们完全可以使用这两个构造函数进行反射创建它的实例。

解决问题

知道原因了,就可以做一下适配:

public static void setDeviceConnectionStateTablet() {
	LogUtils.i(TAG, "set Connection State to Tablet");
	try {
		Class<?> audioSystem = Class.forName("android.media.AudioSystem");
		if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) {
			Class<?> audioDeviceAttributesClass = Class.forName("android.media.AudioDeviceAttributes");
			Constructor<?> constructor = audioDeviceAttributesClass.getDeclaredConstructor(int.class, String.class);
			constructor.setAccessible(true);
			Object audioDeviceAttributes = constructor.newInstance(0x8000,"");

			Method setDeviceConnectionState = audioSystem.getDeclaredMethod("setDeviceConnectionState",
					audioDeviceAttributesClass, int.class, int.class);
			setDeviceConnectionState.setAccessible(true);
			setDeviceConnectionState.invoke(audioSystem, audioDeviceAttributes, 1, 0);
		}else {
			Method setDeviceConnectionState = audioSystem.getDeclaredMethod("setDeviceConnectionState",
					int.class, int.class, String.class, String.class, int.class);
			setDeviceConnectionState.setAccessible(true);
			setDeviceConnectionState.invoke(audioSystem, 0x8000, 1, "", "", 0);
		}
	} catch (ClassNotFoundException
			| NoSuchMethodException
			| InvocationTargetException
			| InstantiationException
			| IllegalAccessException e) {
		LogUtils.e(TAG, "SET CONNECTION STATE Error1: " + e.getCause());
	}
}

public static void setDeviceConnectionStatePhone() {
	LogUtils.i(TAG, "set Connection State to Phone" );
	try {
		Class<?> audioSystem = Class.forName("android.media.AudioSystem");
		if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) {
			Class<?> audioDeviceAttributesClass = Class.forName("android.media.AudioDeviceAttributes");
			Constructor<?> constructor = audioDeviceAttributesClass.getDeclaredConstructor(int.class, String.class);
			constructor.setAccessible(true);
			Object audioDeviceAttributes = constructor.newInstance(0x8000,"");

			Method setDeviceConnectionState = audioSystem.getDeclaredMethod("setDeviceConnectionState",
					audioDeviceAttributesClass, int.class, int.class);
			setDeviceConnectionState.setAccessible(true);
			setDeviceConnectionState.invoke(audioSystem, audioDeviceAttributes, 0, 0);
		}else {
			Method setDeviceConnectionState = audioSystem.getDeclaredMethod("setDeviceConnectionState",
					int.class, int.class, String.class, String.class, int.class);
			setDeviceConnectionState.setAccessible(true);
			setDeviceConnectionState.invoke(audioSystem, 0x8000, 0, "", "", 0);
		}
	} catch (ClassNotFoundException
			| NoSuchMethodException
			| InvocationTargetException
			| InstantiationException
			| IllegalAccessException e) {
		LogUtils.e(TAG, "SET CONNECTION STATE Error1: " + e.getCause());
	}
}
posted @ 2023-03-30 17:09  咸鱼Jay  阅读(248)  评论(0编辑  收藏  举报
页脚HTML代码