【问题标题】:How to stream audio from Android phone to another Android phone using bluetooth?如何使用蓝牙将音频从 Android 手机流式传输到另一部 Android 手机?
【发布时间】:2014-02-27 23:06:12
【问题描述】:

我正在尝试为我的一门课程构建蓝牙对讲机应用程序,但我似乎无法使其正常工作。我修改了 BluetoothChat 示例 (http://developer.android.com/guide/topics/connectivity/bluetooth.html) 以尝试满足我的需求,并且我可以成功地将录音从一部手机传输到另一部手机,但结果非常不稳定。我正在使用 AudioRecord 和 AudioTrack 进行录制和播放。

我有这些全局变量

private static final int RECORDER_SAMPLERATE = 8000;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

private AudioRecord recorder = null;
private AudioTrack audioTrack = null;
private int bufferSize = 0;
private Thread recordingThread = null;
private boolean isRecording = false;

在我的 onCreate 方法中,我设置了 bufferSize 并初始化了 AudioTrack。

bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING);

audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 
            RECORDER_SAMPLERATE, AudioFormat.CHANNEL_OUT_STEREO, 
            RECORDER_AUDIO_ENCODING, bufferSize, AudioTrack.MODE_STREAM);

这是我按下记录/发送按钮时的代码(我目前只能记录 3 秒的数据。)

private void startRecording() {
    recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
            RECORDER_SAMPLERATE, RECORDER_CHANNELS,
            RECORDER_AUDIO_ENCODING, bufferSize);

        int i = recorder.getState();
        if (i == 1)
            recorder.startRecording();

        isRecording = true;

        new Timer().schedule(new TimerTask() {

            @Override
            public void run() {
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() 
                    {
                        enableButtons(false);
                        if(null != recorder){
                                isRecording = false;

                                recorder.stop();
                                recorder.release();

                                recorder = null;
                                recordingThread = null;
                        }
                    }
                });

            }
        }, 3000);

    recordingThread = new Thread(new Runnable() {

        @Override
        public void run() {
            writeOutAudioData();
        }
    }, "AudioRecorder Thread");

    recordingThread.start();
}

private void writeOutAudioData() {

    byte data[] = new byte[bufferSize];

    int read = 0;

        while (isRecording) 
        {
            read = recorder.read(data, 0, bufferSize);

            if (AudioRecord.ERROR_INVALID_OPERATION != read) 
            {
                    mChatService.write(data);
                    //TODO

            }
        }
}

这是处理所有传入和传出传输的代码

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket, String socketType) {
        Log.d(TAG, "create ConnectedThread: " + socketType);
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the BluetoothSocket input and output streams
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            Log.e(TAG, "temp sockets not created", e);
        }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;

        // Keep listening to the InputStream while connected
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);

                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "disconnected", e);
                connectionLost();
                // Start the service over to restart listening mode
                BluetoothChatService.this.start();
                break;
            }
        }
    }

    /**
     * Write to the connected OutStream.
     * @param buffer  The bytes to write
     */
    public void write(byte[] buffer) {
        try {
            mmOutStream.write(buffer);

            // Share the sent message back to the UI Activity
            mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
        } catch (IOException e) {
            Log.e(TAG, "Exception during write", e);
        }
    }

这是从 BluetoothChatService 获取信息的处理程序的代码

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case MESSAGE_STATE_CHANGE:
            if (D)
                Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
            switch (msg.arg1) {
            case BluetoothChatService.STATE_CONNECTED:
                setStatus(getString(R.string.title_connected_to,
                        mConnectedDeviceName));
                mConversationArrayAdapter.clear();
                break;
            case BluetoothChatService.STATE_CONNECTING:
                setStatus(R.string.title_connecting);
                break;
            case BluetoothChatService.STATE_LISTEN:
            case BluetoothChatService.STATE_NONE:
                setStatus(R.string.title_not_connected);
                break;
            }
            break;
        case MESSAGE_WRITE:
            //byte[] writeBuf = (byte[]) msg.obj;
            break;
        case MESSAGE_READ:
            final byte[] readBuf = (byte[]) msg.obj;
            final byte bytes = (byte) msg.arg1;

            Thread audioPlay = new Thread()
            {
                public void run(){
                    audioTrack.play();
                    audioTrack.write(readBuf, 0, readBuf.length);
                    audioTrack.stop();
                }

            };
            audioPlay.start();
            break;
        }
    }
};

我只是不明白 ConnectedThread 类中 byte[] 缓冲区的大小。我应该将其保留在 1024 还是将其更改为其他内容?而且我无法弄清楚读取这些传入字节并将它们写入 AudioTrack 进行播放(没有静态)的正确方法。 我在一部手机上使用 Android 4.3,在另一部手机上使用 4.2.2。 任何帮助将不胜感激!谢谢!

【问题讨论】:

  • 确实找到了解决办法

标签: android bluetooth audio-streaming audiorecord audiotrack


【解决方案1】:

我知道它已经大约一年了,但我也遇到了这个问题。

您必须确保您发送的内容正是您想要发送的内容,并且您写入 AudioTrack 的内容正是您收到的内容。一个简单的测试是计算发送的字节数并与接收的(感知接收的)字节进行比较。

我相信关键是使用您的音轨参数计算的最小缓冲区大小。

int minsize = AudioTrack.getMinBufferSize(RECORDER_SAMPLERATE, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

您可以以任何您想要的块大小发送数据,但您会看到蓝牙套接字通常为 1007 字节(这是我在测试中看到的)。我怀疑最好的性能是如果你允许套接字为你分块,因为它无论如何都会执行检查,因此你可能想要发送相同的minsize

我还建议您将音频格式更改为单声道,除非您的设备有双耳麦克风?

我承认我并没有尝试消化您的所有代码,但我认为您将数据封装在消息中会产生一些不必要的开销。它是一个数据流,我认为它不需要封装。我建议使用BluetoothSocketServerBluetoothSocket

最后一点,因为我正在为遇到此问题的任何其他人写这篇文章。请注意,流模式下的AudioTrack.write() 将阻塞,除非您将minsize 数据写入其中。虽然在 android 参考网站上对此进行了解释,但我想强调它的重要性,以避免浪费时间调试死锁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-08
    • 1970-01-01
    • 1970-01-01
    • 2017-06-01
    • 2020-02-06
    • 1970-01-01
    • 2012-04-27
    • 2016-11-18
    相关资源
    最近更新 更多