我创建了一个带有示例项目的GitHub存储库,该示例项目显示了我在此处询问的以下问题:
https://github.com/paulpv/audio-loopback/tree/simplified/src/com/twistpair/wave/experimental/loopback
(请坚持使用“简化”分支,而忽略“主”分支)

两个主要文件是:

  • https://github.com/paulpv/audio-loopback/blob/simplified/src/com/twistpair/wave/experimental/loopback/MainActivity.java
  • https://github.com/paulpv/audio-loopback/blob/simplified/src/com/twistpair/wave/experimental/loopback/AudioStateManager.java

  • 免责声明:我目前仅使用运行CyanogenMod 10 Jelly Bean的单个Samsung Epic SPH-D700进行编码和测试。我没有在其他设备上尝试过此方法,但是也许这可能有助于阻止我将头发拔出并发疯。

    我一直在努力将Android蓝牙SCO可靠地传递到 的启动和停止以及捕获/播放音频 FOR MONTHS !
    一旦我可以将手机设置为SCO模式,分别通过AudioRecord和AudioTrack进行捕获和回放就可以正常工作了。
    我遇到的问题是我无法可靠地使手机进入SCO模式!

    在“Internet”上使用startBluetoothSco()和setBluetoothScoOn(true)的示例似乎都很简单明了,但是当我在设备上使用它们时,它们几乎永远无法可靠地工作。
    我创建了自己的测试应用程序,除了启动和停止SCO外什么也不做,我什至无法使它可靠地工作!

    我的代码监听BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED EXTRA_STATE==CONNECTED.我可以可靠地检测到何时连接或断开了任何耳机。

    当检测到连接时,我的处理程序立即调用startBluetoothSco()。
    A可能会发誓,至少将SCO_AUDIO_STATE踢到NNECTED的C O了,但是99%的时间它只会导致从DISCONNECTED->CONNECTING->DISCONNECTED过渡。

    这是我的GitHub示例应用程序中带注释的日志输出:
    10-03 17:00:13.970: I/dalvikvm(29487): Debugger is active
    10-03 17:00:14.158: I/System.out(29487): Debugger has connected
    10-03 17:00:15.779: I/System.out(29487): waiting for debugger to settle...
    10-03 17:00:15.978: I/System.out(29487): debugger has settled (1325)
    

    我的应用程序在关闭Jawbone耳机的情况下启动,并更新了UI ...
    10-03 17:00:16.568: D/MainActivity(29487): updateScreen()...
    10-03 17:00:16.572: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false
    

    ... UI更新完成
    即时广播,可告诉我当前的SCO_AUDIO_STATE ...
    10-03 17:00:16.689: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
    10-03 17:00:16.689: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=2, "android.media.extra.SCO_AUDIO_STATE"=0}
    10-03 17:00:16.689: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
    10-03 17:00:16.693: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_CONNECTING(2)
    10-03 17:00:16.693: D/AudioStateManager(29487): ==> scoAudioState=SCO_AUDIO_STATE_DISCONNECTED(0)
    10-03 17:00:16.693: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED: SCO_AUDIO_STATE_DISCONNECTED
    

    ...当前的SCO_AUDIO_STATE ==已断开连接;没问题,因为我的耳机已关闭。
    调用我的SCO Disconnected事件监听器,并使用两个sendMessages更新UI ...
    10-03 17:00:16.693: I/MainActivity(29487): onAudioManagerScoAudioDisconnected()
    10-03 17:00:16.755: D/libEGL(29487): loaded /vendor/lib/egl/libEGL_POWERVR_SGX540_120.so
    10-03 17:00:16.787: D/libEGL(29487): loaded /vendor/lib/egl/libGLESv1_CM_POWERVR_SGX540_120.so
    10-03 17:00:16.791: D/libEGL(29487): loaded /vendor/lib/egl/libGLESv2_POWERVR_SGX540_120.so
    10-03 17:00:16.888: D/OpenGLRenderer(29487): Enabling debug mode 0
    10-03 17:00:16.912: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
    10-03 17:00:16.912: D/MainActivity(29487): updateScreen()...
    10-03 17:00:16.912: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false
    10-03 17:00:16.927: D/MainActivity(29487): MSG_UPDATE_AUDIO_OUTPUT_STREAM_TYPE
    10-03 17:00:16.927: D/MainActivity(29487): updateScreen()...
    10-03 17:00:16.931: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false
    

    ... UI更新完成

    约20秒后,我打开Jawbone耳机...
    10-03 17:00:37.572: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED flg=0x10 (has extras) }
    10-03 17:00:37.583: D/AudioStateManager(29487): extras={"android.bluetooth.device.extra.DEVICE"=00:21:3C:00:3E:02, "android.bluetooth.profile.extra.PREVIOUS_STATE"=0, "android.bluetooth.profile.extra.STATE"=1}
    10-03 17:00:37.587: D/AudioStateManager(29487): mReceiver: BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED
    10-03 17:00:37.587: D/AudioStateManager(29487): ==> bluetoothDevice=00:21:3C:00:3E:02
    10-03 17:00:37.587: D/AudioStateManager(29487): ==> bluetoothHeadsetStatePrevious=STATE_DISCONNECTED(0)
    10-03 17:00:37.587: D/AudioStateManager(29487): ==> bluetoothHeadsetState=STATE_CONNECTING(1)
    10-03 17:00:37.619: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED flg=0x10 (has extras) }
    10-03 17:00:37.623: D/AudioStateManager(29487): extras={"android.bluetooth.device.extra.DEVICE"=00:21:3C:00:3E:02, "android.bluetooth.profile.extra.PREVIOUS_STATE"=1, "android.bluetooth.profile.extra.STATE"=2}
    10-03 17:00:37.623: D/AudioStateManager(29487): mReceiver: BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED
    10-03 17:00:37.623: D/AudioStateManager(29487): ==> bluetoothDevice=00:21:3C:00:3E:02
    10-03 17:00:37.626: D/AudioStateManager(29487): ==> bluetoothHeadsetStatePrevious=STATE_CONNECTING(1)
    10-03 17:00:37.626: D/AudioStateManager(29487): ==> bluetoothHeadsetState=STATE_CONNECTED(2)
    

    颚骨已连接;我的事件监听器称为...
    10-03 17:00:37.626: I/MainActivity(29487): onBluetoothHeadsetConnected()
    

    ...看到我们可以上合组织...
    10-03 17:00:37.626: D/AudioStateManager(29487): mAudioManager.isBluetoothScoAvailableOffCall()=true
    

    ...并自动调用startBluetoothSco()
    这里是问题所在!为什么调用startBluetoothSco不会导致SCO_AUDIO_STATE == CONNECTED?!?!
    10-03 17:00:37.626: D/AudioStateManager(29487): startBluetoothSco()
    10-03 17:00:37.626: I/AudioStateManager(29487): mAudioManager.startBluetoothSco();
    

    我的事件监听器完成了一个sendMessage来更新带有当前BT状态的UI ...
    10-03 17:00:37.646: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
    10-03 17:00:37.650: D/MainActivity(29487): updateScreen()...
    10-03 17:00:37.650: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false
    

    ... UI更新完成
    startBluetoothSco的第一个结果是...
    10-03 17:00:37.681: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
    10-03 17:00:37.681: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_STATE"=2, "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=0}
    10-03 17:00:37.681: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
    10-03 17:00:37.685: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_DISCONNECTED(0)
    10-03 17:00:37.685: D/AudioStateManager(29487): ==> scoAudioState=SCO_AUDIO_STATE_CONNECTING(2)
    

    ...已从“断开连接”移至“正在连接”
    startBluetoothSco的第二个结果出现在...
    10-03 17:00:37.759: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
    10-03 17:00:37.763: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_STATE"=0, "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=2}
    10-03 17:00:37.763: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
    10-03 17:00:37.763: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_CONNECTING(2)
    10-03 17:00:37.763: D/AudioStateManager(29487): ==> scoAudioState=SCO_AUDIO_STATE_DISCONNECTED(0)
    10-03 17:00:37.763: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED: SCO_AUDIO_STATE_DISCONNECTED
    

    ...从CONNECTING移至 DISCONNECTED
    我本来希望SCO从CONNECTING变为 CONNECTED !
    我的事件监听器被调用并使用两个sendMessages更新UI ...
    10-03 17:00:37.763: I/MainActivity(29487): onAudioManagerScoAudioDisconnected()
    10-03 17:00:37.767: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
    10-03 17:00:37.767: D/MainActivity(29487): updateScreen()...
    10-03 17:00:37.767: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false
    10-03 17:00:37.783: D/MainActivity(29487): MSG_UPDATE_AUDIO_OUTPUT_STREAM_TYPE
    10-03 17:00:37.783: D/MainActivity(29487): updateScreen()...
    10-03 17:00:37.783: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false
    

    ... UI更新完成

    我等待〜20秒以连接SCO,但是它永远不会实现。
    我按我的应用程序的“startBluetoothSco”按钮。
    请注意,此结果完全相同,因此会在17:00:37.626启动bluetoothSco()
    10-03 17:01:01.689: D/AudioStateManager(29487): startBluetoothSco()
    10-03 17:01:01.689: I/AudioStateManager(29487): mAudioManager.startBluetoothSco();
    

    startBluetoothSco的第一个结果是...
    10-03 17:01:01.708: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
    10-03 17:01:01.712: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_STATE"=2, "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=0}
    10-03 17:01:01.712: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
    10-03 17:01:01.712: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_DISCONNECTED(0)
    10-03 17:01:01.712: D/AudioStateManager(29487): ==> scoAudioState=SCO_AUDIO_STATE_CONNECTING(2)
    

    ...已从“断开连接”移至“正在连接”
    这是不同于17:00:37.626的startBluetoothSco()自动调用的地方
    我们收到了 BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED 事件...
    10-03 17:01:01.716: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED flg=0x10 (has extras) }
    10-03 17:01:01.720: D/AudioStateManager(29487): extras={"android.bluetooth.device.extra.DEVICE"=00:21:3C:00:3E:02, "android.bluetooth.profile.extra.PREVIOUS_STATE"=10, "android.bluetooth.profile.extra.STATE"=11}
    10-03 17:01:01.720: D/AudioStateManager(29487): mReceiver: BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED
    10-03 17:01:01.720: D/AudioStateManager(29487): ==> bluetoothDevice=00:21:3C:00:3E:02
    10-03 17:01:01.720: D/AudioStateManager(29487): ==> bluetoothHeadsetAudioStatePrevious=STATE_AUDIO_DISCONNECTED(10)
    10-03 17:01:01.720: D/AudioStateManager(29487): ==> bluetoothHeadsetAudioState=STATE_AUDIO_CONNECTING(11)
    

    ...已从“断开连接”移至“正在连接”
    我们收到另一个BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED事件...
    10-03 17:01:02.572: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED flg=0x10 (has extras) }
    10-03 17:01:02.576: D/AudioStateManager(29487): extras={"android.bluetooth.device.extra.DEVICE"=00:21:3C:00:3E:02, "android.bluetooth.profile.extra.PREVIOUS_STATE"=11, "android.bluetooth.profile.extra.STATE"=12}
    10-03 17:01:02.576: D/AudioStateManager(29487): mReceiver: BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED
    10-03 17:01:02.576: D/AudioStateManager(29487): ==> bluetoothDevice=00:21:3C:00:3E:02
    10-03 17:01:02.576: D/AudioStateManager(29487): ==> bluetoothHeadsetAudioStatePrevious=STATE_AUDIO_CONNECTING(11)
    10-03 17:01:02.580: D/AudioStateManager(29487): ==> bluetoothHeadsetAudioState=STATE_AUDIO_CONNECTED(12)
    

    ...从CONNECTING移至CONNECTED
    事件使用一个sendMessage更新UI
    10-03 17:01:02.580: I/MainActivity(29487): onBluetoothHeadsetAudioConnected()
    10-03 17:01:02.580: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
    10-03 17:01:02.580: D/MainActivity(29487): updateScreen()...
    10-03 17:01:02.583: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true
    

    ... UI更新完成(说实话,我不确定第二次调用isBluetoothSco是什么)
    10-03 17:01:02.603: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true
    

    startBluetoothSco的第二个结果出现在...
    10-03 17:01:02.603: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
    10-03 17:01:02.607: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_STATE"=1, "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=2}
    10-03 17:01:02.607: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
    10-03 17:01:02.607: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_CONNECTING(2)
    10-03 17:01:02.607: D/AudioStateManager(29487): ==> scoAudioState=.SCO_AUDIO_STATE_CONNECTED(1)
    10-03 17:01:02.607: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED: SCO_AUDIO_STATE_CONNECTED
    

    ...从CONNECTING移至CONNECTED

    最后!
    我的事件监听器被调用并使用两个sendMessages更新UI ...
    10-03 17:01:02.611: I/MainActivity(29487): onAudioManagerScoAudioConnected()
    10-03 17:01:02.630: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
    10-03 17:01:02.630: D/MainActivity(29487): updateScreen()...
    10-03 17:01:02.634: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true
    10-03 17:01:02.650: D/MainActivity(29487): MSG_UPDATE_AUDIO_OUTPUT_STREAM_TYPE
    10-03 17:01:02.650: D/MainActivity(29487): updateScreen()...
    10-03 17:01:02.650: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true
    

    ... UI更新完成

    一段时间后,当我“手动”启动SCO时,一切(这次)都可以工作,但是如果我在连接耳机后立即自动启动SCO,则一切正常。

    更糟的是,当事情没有按预期进行时,我在SCO状态中看到了奇怪的行为:
  • 将startBluetoothSco()延迟3-5秒钟似乎没有什么区别。我没有尝试将其延迟超过5秒。等待5秒钟以上的音频开始流向BT耳机是荒谬的。
  • 有时,即使自从上次DISCONNECTED状态说该状态已更改为CONNECTED以来从未收到广播事件,有时调用isBluetoothScoOn()也会返回true。
  • 有时从UI中“手动”调用startBluetoothSco()不会执行任何操作,就像SCO已经打开一样,但是自从上次DISCONNECTED状态说状态变为CONNECTED以来,我从未收到任何广播事件。
  • 尝试打开AudioTrack或AudioRecord时没有声音(当SCO正常运行时,相同的代码也可以正常工作,即:问题是SCO状态,而不是AudioTrack/AudioRecord调用)。
  • 调用stopBluetoothSco()不会导致任何事件报告状态为DISCONNECTED。
  • setBluetoothScoOn(false/true)没有区别。老实说,我不理解看似多余的“startBluetoothSco()/stopBluetoothSco()”和“setBluetoothScoOn(boolean)”之间的区别。当一切正常时,我调用startBluetoothSco()会导致isBluetoothScoOn()返回true,这使我认为我不需要调用setBluetoothScoOn(true)。
  • 重新启动电话没有什么影响。
  • 重新启动耳机没有什么影响。
  • 更改为其他耳机没有区别。
  • 有时耳机会丢失其配对,必须重新配对。

  • 鉴于Google/Android在蓝牙支持方面的往绩,这几乎没有让我感到惊讶。

    有人可以让我摆脱困境,并明确解释如何在Android中可靠地启动和停止蓝牙SCO吗?

    PS:是否有官方 channel 升级此类问题[w/Google?三星?]?或者,StackOverflow是我找到实际答案的最佳机会吗?

    最佳答案

    Android文档中缺少许多内容,但是,如果在音频路由期间每次都调用startBluetoothSco()和stopBluetoothSco(),则正确路由音频应该没有问题。
    即使我已经看到连接长时间处于空闲状态,并启动StartBluetoothSco(),我们直接断开连接。
    为了解决这个问题,我在这里写了一个解决方法:https://github.com/kodered/Bluetooth-Refresh-Logic

    希望这可以帮助。

    关于android - Android startBluetoothSco未启动sco但isBluetoothScoOn返回true,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12737446/

    10-10 20:02