一、问题背景:
音乐播放器播放music,发现能够播放但是没有声音,但是用tinyplay播放又有声音。
二、问题分析:
无声是必现的,抓取log分析,发现播放音频时并没有走primary module,而是走的r_submix module,这个无声问题就能解释通了,音频流并没有走到adsp。
07-24 11:59:21.285 1015 1859 I AudioFlinger: openOutput() this 0xb4000077f35ce9c0, module 26 Device AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @:0, SamplingRate 48000, Format 0x000001, Channels 0x3, flags 0
07-24 11:59:21.286 907 930 D r_submix: adev_open_output_stream(address=0)
07-24 11:59:21.287 907 930 I r_submix: adev_open_output_stream(): about to create pipe at index 9, rate 48000, pipe size 4096
07-24 11:59:21.287 907 930 D r_submix: submix_audio_device_create_pipe_l(addr=0, idx=9)
07-24 11:59:21.287 907 930 D r_submix: now using address 0 for route 9
为什么会走到AUDIO_DEVICE_OUT_REMOTE_SUBMIX这个设备出去呢? 从官网上查询到REMOTE_SUBMIX这个音频类型,描述上看就是捕获设备音频流投到远端设备上。
参考官网:MediaRecorder.AudioSource | Android Developers (google.cn)
从log上看,确实有录音动作,AUDIO_SOURCE_REMOTE_SUBMIX这个就是REMOTE_SUBMIX这个音源类型
7-24 11:59:21.235 1015 1015 V APM_AudioPolicyManager: getInputForAttr() source 8, sampling rate 48000, format 0x1, channel mask 0xc, session 225, flags 0 attributes={ Content type: AUDIO_CONTENT_TYPE_UNKNOWN Usage: AUDIO_USAGE_UNKNOWN Source: AUDIO_SOURCE_REMOTE_SUBMIX Flags: 0x800 Tags: } requested device ID 0
07-24 11:59:21.236 1015 1015 V APM_AudioPolicyManager: getInputForAttr found device type is 0x80000100
07-24 11:59:21.238 907 930 D r_submix: adev_open_input_stream(addr=0)
07-24 11:59:21.239 907 930 I r_submix: adev_open_input_stream(): about to create pipe at index 9, rate 48000, pipe size 4096
07-24 11:59:21.239 907 930 D r_submix: submix_audio_device_create_pipe_l(addr=0, idx=9)
07-24 11:59:21.240 907 930 D r_submix: now using address 0 for route 9
同测试沟通,了解到使用了scrcpy投屏工具,这就对上了
三、scrcpy音频录制分析:
下载scrcpy源码:github.com/Genymobile/…
源码中确实有录音动作,AudioCapture.java中会创建AudioRecord scrcpy/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
private static AudioRecord createAudioRecord(int audioSource) { AudioRecord.Builder builder = new AudioRecord.Builder(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // On older APIs, Workarounds.fillAppInfo() must be called beforehand builder.setContext(FakeContext.get()); } builder.setAudioSource(audioSource); builder.setAudioFormat(createAudioFormat()); int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, ENCODING); // This buffer size does not impact latency builder.setBufferSizeInBytes(8 * minBufferSize); return builder.build(); }
跟踪audioSource来源,发现是MediaRecorder.AudioSource.REMOTE_SUBMIX这个音频源,这个又对上了
Options.java
private AudioSource audioSource = AudioSource.OUTPUT;
AudioSource.java
OUTPUT("output", MediaRecorder.AudioSource.REMOTE_SUBMIX),
scrcpy录制Android设备音频原理:源码中的java代码编译打包到scrcpy-server,然后push到Android设备,执行app_process运行scrcpy-server
四、总结:
scrcpy投屏工具将Android设备音频流录制后投到PC端,PC端插上耳机后能听到Android设备播放的music