【问题标题】:Programmatically connect to paired Bluetooth speaker and play audio以编程方式连接到配对的蓝牙扬声器并播放音频
【发布时间】:2014-04-09 05:03:43
【问题描述】:

在我们的应用中,我想使用 Android v4.2 或更高版本连接到之前配对的 A2DP 蓝牙扬声器并直接播放音频。

我可以使用此代码成功创建 A2DP 配置文件对象以启动该过程:


/* Manifest permissions */
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(this, mProfileListener, BluetoothProfile.A2DP)

和下面的监听器响应连接:

private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {

    if (profile == BluetoothProfile.A2DP) {

        mBluetoothSpeaker = (BluetoothA2dp) proxy;

        // no devices are connected         
        List<BluetoothDevice> connectedDevices = mBluetoothSpeaker.getConnectedDevices();

        //the one paired (and disconnected) speaker is returned here
        int[] statesToCheck = {BluetoothA2dp.STATE_DISCONNECTED};           
        List<BluetoothDevice> disconnectedDevices = mBluetoothSpeaker.getDevicesMatchingConnectionStates(statesToCheck);



        BluetoothDevice btSpeaker = disconnectedDevices.get(0); 

        //WHAT NOW?

    }
}
public void onServiceDisconnected(int profile) {
    if (profile == BluetoothProfile.A2DP) {
        mBluetoothSpeaker = null;
    }
}
};

我只是有点不知道现在该做什么,连接设备,并将音频输出定向到它。我尝试使用以下代码连接到设备as detailed in the Android docs,但最终的BluetoothSpeaker.getConnectedDevices() 调用未返回任何连接的设备。

    BluetoothSocket tmp = null;
    UUID MY_UUID = UUID.fromString("00001108-0000-1000-8000-00805f9b34fb");
    try {           
        tmp = btSpeaker.createInsecureRfcommSocketToServiceRecord(MY_UUID);

    } catch (IOException e1) {
        // TODO Auto-generated catch block
        Log.d("createRfcommSocketToServiceRecord ERROR", e1.getMessage());
    }
    mmSocket = tmp;

    try {
        // Connect the device through the socket. This will block
        // until it succeeds or throws an exception
        mmSocket.connect();
    } catch (IOException connectException) {
        // Unable to connect; close the socket and get out
        try {
            Log.d("connectException", connectException.getMessage());
            mmSocket.close();
        } catch (IOException closeException) { }
        return;
    }

    connectedDevices = mBluetoothSpeaker.getConnectedDevices();

代码似乎确实以某种方式连接到设备,因为当我停止执行时,蓝牙扬声器会宣布它已准备好配对(就像它与音频源断开连接时一样)。

BluetoothA2dp 的旧版本似乎有一个 connect(BluetoothDevice device) 方法,但现在已被删除(从 4.2 开始),我正在努力寻找如何以编程方式连接到 A2DP 设备的任何明确示例,并将音频输出定向到它。我们将不胜感激地收到任何有关如何接近的帮助。

任何有关如何解决此问题的建议将不胜感激。

【问题讨论】:

  • 我假设您正在创建您的mProfileListener 实例之前调用getProfileProxy(...)?
  • 是的,onServiceConnected 块很高兴地触发。
  • 这个问题的答案stackoverflow.com/questions/12542523/… 和上面的 cmets 似乎并不乐观。
  • 感谢 Squonk,我可以通过调用 createInsecureRfcommSocketToServiceRecord() 来连接设备(或至少触发 BluetoothDevice.ACTION_ACL_CONNECTED 事件),但如果该帖子是正确的,那么 RFcomm 无论如何对于 A2DP 广播都是错误的.连接到 A2DP 设备的正确方法是什么? Android 蓝牙文档中似乎有一个巨大的漏洞(或者至少我对它的理解)。
  • 老实说,我不知道,因为我从未尝试过 A2DP,但我同意文档中存在差距。 A2DP 有一些选项,但没有使用它的有用方法(或者至少没有解释如何使用它的有用文档),这似乎很奇怪。如果我遇到任何事情,我会回复你。

标签: java android bluetooth android-bluetooth a2dp


【解决方案1】:

这个对我有用。 收到BluetoothA2dp.STATE_CONNECTED后,就可以正常播放音乐了。

public class A2DPActivity extends Activity {

protected static final String TAG = "ZS-A2dp";

Button mBtPlay;

BluetoothAdapter mBtAdapter;
BluetoothA2dp mA2dpService;

AudioManager mAudioManager;
MediaPlayer mPlayer;

BroadcastReceiver mReceiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context ctx, Intent intent) {
        String action = intent.getAction();
        Log.d(TAG, "receive intent for action : " + action);
        if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
            int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_DISCONNECTED);
            if (state == BluetoothA2dp.STATE_CONNECTED) {
                setIsA2dpReady(true);
                playMusic();
            } else if (state == BluetoothA2dp.STATE_DISCONNECTED) {
                setIsA2dpReady(false);
            }
        } else if (action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)) {
            int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_NOT_PLAYING);
            if (state == BluetoothA2dp.STATE_PLAYING) {
                Log.d(TAG, "A2DP start playing");
                Toast.makeText(A2DPActivity.this, "A2dp is playing", Toast.LENGTH_SHORT).show();
            } else {
                Log.d(TAG, "A2DP stop playing");
                Toast.makeText(A2DPActivity.this, "A2dp is stopped", Toast.LENGTH_SHORT).show();
            }
        }
    }

};

boolean mIsA2dpReady = false;
void setIsA2dpReady(boolean ready) {
    mIsA2dpReady = ready;
    Toast.makeText(this, "A2DP ready ? " + (ready ? "true" : "false"), Toast.LENGTH_SHORT).show();
}

private ServiceListener mA2dpListener = new ServiceListener() {

    @Override
    public void onServiceConnected(int profile, BluetoothProfile a2dp) {
        Log.d(TAG, "a2dp service connected. profile = " + profile);
        if (profile == BluetoothProfile.A2DP) {
            mA2dpService = (BluetoothA2dp) a2dp;
            if (mAudioManager.isBluetoothA2dpOn()) {
                setIsA2dpReady(true);
                playMusic();
            } else {
                Log.d(TAG, "bluetooth a2dp is not on while service connected");
            }
        }
    }

    @Override
    public void onServiceDisconnected(int profile) {
        setIsA2dpReady(false);
    }

};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    LinearLayout ll = new LinearLayout(this);
    setContentView(ll);

    mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    registerReceiver(mReceiver, new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED));
    registerReceiver(mReceiver, new IntentFilter(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED));

    mBtAdapter = BluetoothAdapter.getDefaultAdapter();
    mBtAdapter.getProfileProxy(this, mA2dpListener , BluetoothProfile.A2DP);

}

@Override
protected void onDestroy() {
    mBtAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dpService);
    releaseMediaPlayer();
    unregisterReceiver(mReceiver);
    super.onDestroy();
}

@Override
protected void onPause() {
    releaseMediaPlayer();
    super.onPause();
}

private void releaseMediaPlayer() {
    if (mPlayer != null) {
        mPlayer.release();
        mPlayer = null;
    }
}

private void playMusic() {
    mPlayer = new MediaPlayer();
    AssetManager assetManager = this.getAssets();
    AssetFileDescriptor fd;
    try {
        fd = assetManager.openFd("Radioactive.mp3");
        Log.d(TAG, "fd = " + fd);
        mPlayer.setDataSource(fd.getFileDescriptor());
        mPlayer.prepare();
        Log.d(TAG, "start play music");
        mPlayer.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

【讨论】:

  • Zxshi,非常感谢您的回答。我很高兴听到你设法让这个工作。我目前正在躲避 A2DP,并在我们项目的其他一些方面工作,但我保证我会尽快尝试这些,看看我是否也能让它们工作。
  • 谢谢,非常有帮助的答案。我有一个稍微不同的要求。我正在尝试使用来自麦克风的实时音频并发送到 A2DP 蓝牙扬声器。谁能帮我解决一下?
  • A2DP 具有低延迟。有什么压缩方法吗?
【解决方案2】:

泰德,

当我尝试使用蓝牙耳机时,我遇到了和你一样的问题。 我想我的工作可能适用于 A2DP。 因为我的耳机只支持免提模式。我只测试蓝牙耳机。

无需建立RFComm频道。

对我来说。 连接到 BluetoothHeadsetService 后, 1.检查音频是否已经连接

mBluetoothSpeaker.isAudioConnected(btSpeaker);

2。如果没有,建立音频连接

mBluetoothSpeaker.startVoiceRecognition(btSpeaker);

3。为 BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED 和 BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED 注册 BroadcastReceiver

registerReceiver(mReceiver, new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED));
registerReceiver(mReceiver, new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED));

4。广播接收器

protected BroadcastReceiver mReceiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        int state = BluetoothHeadset.STATE_DISCONNECTED;
        int previousState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, BluetoothHeadset.STATE_DISCONNECTED);
        if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
            state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
            if (state == BluetoothHeadset.STATE_CONNECTED) {
                mConnectedHeadset = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                mInfoTextview.append("\n\nDevice name = " + mConnectedHeadset.getName());

                // Audio should not be connected yet but just to make sure.
                if (mBluetoothHeadset.isAudioConnected(mConnectedHeadset)) {
                    Log.d(TAG, "Headset audio connected already");
                } else {
                    if (!mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset)) {
                        Log.e(TAG, "maybe you do not call stopVoiceRecognition previously");
                        mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);
                        mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset);
                    }
                }
            }
            else if (state == BluetoothHeadset.STATE_DISCONNECTED) {
                mConnectedHeadset = null;
            }
        }
        else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED))// audio
        {
            state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
            if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
                // Bluetooth audio connected. you send audio stream to headset now!!!
                mAudioManager.setMode(AudioManager.STREAM_VOICE_CALL);
                mMediaPlayer = new MediaPlayer();
                AssetManager assetManager = getAssets();
                try {
        AssetFileDescriptor fd = assetManager.openFd("Radioactive.mp3");
        mMediaPlayer.setDataSource(fd.getFileDescriptor());
                    // set audio stream type to STREAM_VOICE_CALL will send audio to headset
                    // @see <a href="http://developer.android.com/reference/android/media/AudioManager.html#startBluetoothSco()">SCO</a>
                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
        mMediaPlayer.prepare();
        Log.d(TAG, "start play music");
        mMediaPlayer.start();
        } catch (IOException e) {
        e.printStackTrace();
        }
            }
            else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
                if (mMediaPlayer != null) {
                mMediaPlayer.stop();
                mMediaPlayer = null;
                }
                mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);
            }
        }   
    }
};

【讨论】:

    【解决方案3】:

    遇到同样的问题,但发现旧帖子:

    Programmatically connect to paired Bluetooth device

    简而言之,为了连接到配对的 a2dp 设备,您只需调用 BluetoothA2dp.connect(myPairedA2dpDevice),但现在该方法对公共 API 是隐藏的,这没有帮助。因此,您可以通过 Java 反射访问它。这是一种 hack,但按照 Google 的说法,目前似乎还没有一个干净的解决方案。

    【讨论】:

      【解决方案4】:
      mMediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
                          .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
                          .setLegacyStreamType(AudioManager.STREAM_MUSIC)
                          //.setUsage(AudioAttributes.USAGE_ALARM)
                          .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                          .build());
      

      通过评论 .setUsage(AudioAttributes.USAGE_ALARM) 行,它对我有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-07
        • 1970-01-01
        相关资源
        最近更新 更多