如何使用蓝牙将音频从Android手机传输到另一部Android手机?

时间:2014-02-27 23:06:13

标签: android bluetooth audio-streaming audiorecord audiotrack

我正在尝试为我的一个课程构建一个蓝牙对讲机应用程序,但我似乎无法让它正常工作。我修改了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。 任何帮助将不胜感激!谢谢!

1 个答案:

答案 0 :(得分:0)

我知道它已经有一年了,但我也有这个问题。

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

我认为关键是使用您的audiotrack参数计算的最小缓冲区大小。

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

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

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

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

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

相关问题