Android应用如何通过Bluetooth SCO同时实现耳机麦克风输入与音频输出?
实现蓝牙SCO双向音频(采集+播放)的可行方案
你已经开启了Bluetooth SCO,但要实现同时采集和播放,还需要完善音频模式配置、设备路由以及监听连接状态,以下是具体步骤:
1. 完善AudioManager配置
仅调用audiomanager.setBluetoothScoOn(true)不足以建立稳定的SCO连接,还需要启动SCO并切换到通信音频模式:
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); // 设置音频模式为通信模式,强制系统使用蓝牙SCO的双向通道 audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); // 请求启动SCO连接(系统会异步建立连接) audioManager.startBluetoothSco(); // 开启SCO音频 audioManager.setBluetoothScoOn(true);
2. 监听SCO连接状态
SCO连接是异步建立的,必须等待连接成功后再初始化录音和播放组件,通过广播监听状态:
private BroadcastReceiver scoReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1); if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) { // SCO连接成功,初始化录音和播放 initAudioRecordAndTrack(); } } }; // 在onCreate中注册广播 registerReceiver(scoReceiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
3. 初始化AudioRecord(采集)和AudioTrack(播放)
确保两者都路由到蓝牙SCO通道,参数要匹配:
private AudioRecord audioRecord; private AudioTrack audioTrack; private boolean isRunning = false; private void initAudioRecordAndTrack() { int sampleRate = 8000; // 多数蓝牙SCO设备支持的采样率 int channelConfig = AudioFormat.CHANNEL_IN_MONO; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 计算录音最小缓冲区 int recordBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); // 使用VOICE_COMMUNICATION音频源,强制调用SCO麦克风 audioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, sampleRate, channelConfig, audioFormat, recordBufferSize); // 初始化播放组件,使用VOICE_CALL流(对应SCO输出) int playBufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, audioFormat); audioTrack = new AudioTrack.Builder() .setAudioAttributes(new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build()) .setAudioFormat(new AudioFormat.Builder() .setSampleRate(sampleRate) .setEncoding(audioFormat) .setChannelMask(AudioFormat.CHANNEL_OUT_MONO) .build()) .setBufferSizeInBytes(playBufferSize) .build(); // 启动录音和播放线程 startAudioLoop(); }
4. 同时运行录音与播放
如果需要采集环境音的同时播放提示音,可在录音线程中混入预加载的提示音PCM数据,或单独启动提示音播放线程(确保使用相同的音频属性),以下是实时采集+播放的基础示例:
private void startAudioLoop() { isRunning = true; new Thread(() -> { audioRecord.startRecording(); audioTrack.play(); byte[] buffer = new byte[1024]; while (isRunning) { int readSize = audioRecord.read(buffer, 0, buffer.length); if (readSize > 0) { // 此处可混入提示音PCM数据后再写入播放组件 audioTrack.write(buffer, 0, readSize); } } audioRecord.stop(); audioTrack.stop(); audioRecord.release(); audioTrack.release(); }).start(); }
5. 权限配置
在AndroidManifest.xml中添加必要权限:
<!-- 录音权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- Android 12+蓝牙连接权限 --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- Android 11及以下蓝牙权限 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
注意事项
- 部分设备的蓝牙SCO仅支持8000Hz采样率,使用更高采样率可能导致功能失效;
- 当系统来电或其他应用抢占SCO通道时,需监听广播重新建立连接;
- 应用退出时记得停止SCO连接:
audioManager.stopBluetoothSco(); audioManager.setBluetoothScoOn(false);,并注销广播。
内容的提问来源于stack exchange,提问作者Mayank Kansal
相关产品推荐
相关产品推荐

