蓝牙套接字会影响播放和录制音频的线程

时间:2019-09-04 22:15:24

标签: java android multithreading audio bluetooth-socket

我有一个同时运行3个线程的应用程序。一个线程用于建立电话与另一个蓝牙设备(Arduino)之间的蓝牙连接。线程2播放通过蓝牙从另一部电话传入的音频。线程3记录并通过蓝牙将音频发送到另一部手机。

如果电话试图建立与Arduino的连接(当线程1正在运行bluetoothsocket.connect();时),则音频通信会出现很多故障。但是,当电话不尝试建立与Arduino的连接或连接已经建立且线程1已完成时,则通信良好。

这是线程1的代码-arduino(此代码与类有关)

public class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;

        public ConnectThread(BluetoothDevice device) {
            // As mmSocket is final, we use a temporary socket variable
            BluetoothSocket tmp = null;
            mmDevice = device;

            // Get a BluetoothSocket to connect with the given BluetoothDevice
            try {
                // MY_UUID is the app's UUID string, also used by the server code
                tmp = device.createRfcommSocketToServiceRecord(UUID);
            } catch (IOException e) { }
            mmSocket = tmp;
        }

        public void run() {
            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            // Cancel discovery because it will slow down the connection
            mBluetoothAdapter.cancelDiscovery();

            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 {
                    mmSocket.close();
                } catch (IOException closeException) { }
                // Send the name of the disconnected device back to the UI Activity
                sendDeviceConnectionToActivity(deviceMAC, false);
                Log.d("Bluetoot connected -->", "NNNNNNNN" + connectException);
                return;
            }

            // Do work to manage the connection (in a separate thread)
            manageConnectedSocket(mmSocket, mmDevice);
//            mConnectedThread = new ConnectedThread(mmSocket);
//            mConnectedThread.start();
            Log.d("Bluetoot connected -->", mmDevice.getName());
        }

        /** Will cancel an in-progress connection, and close the socket */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }

线程2和3中的音频代码(此代码与另一个类一起使用)

public void audioCreate() {
        // Audio track object
        track = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
                16000, AudioFormat.CHANNEL_OUT_MONO,
                encoding, minSize, AudioTrack.MODE_STREAM);
        // Audio record object
        recorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 16000,
                AudioFormat.CHANNEL_IN_MONO, encoding,
                bufferSize);
    }

public void initiateBluetoothConexion(BluetoothDevice deviceSelected) {
//        Toast.makeText(getApplicationContext(), "Service On", Toast.LENGTH_SHORT).show();
        deviceMAC = deviceSelected.getAddress();

        mBluetoothAdapter.cancelDiscovery();
        // Cancel any thread attempting to make a connection
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }
        mConnectThread = new ConnectThread(deviceSelected);
        mConnectThread.setPriority(10);
        mConnectThread.start();
    }

    public class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;

        public ConnectThread(BluetoothDevice device) {
            // As mmSocket is final, we use a temporary socket variable
            BluetoothSocket tmp = null;
            mmDevice = device;

            // Get a BluetoothSocket to connect with the given BluetoothDevice
            try {
                // MY_UUID is the app's UUID string, also used by the server code
                tmp = device.createRfcommSocketToServiceRecord(UUID);
            } catch (IOException e) { }
            mmSocket = tmp;
        }

        public void run() {
            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
            // Cancel discovery because it will slow down the connection
            mBluetoothAdapter.cancelDiscovery();

            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 {
                    mmSocket.close();
                } catch (IOException closeException) { }
                // Send the name of the disconnected device back to the UI Activity
                sendDeviceConnectionToActivity(deviceMAC, false);
                return;
            }

            // Do work to manage the connection (in a separate thread)
            manageConnectedSocket(mmSocket, mmDevice);
//            mConnectedThread = new ConnectedThread(mmSocket);
//            mConnectedThread.start();
            Log.d("Bluetoot connected -->", mmDevice.getName());
        }

        /** Will cancel an in-progress connection, and close the socket */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }

    private void manageConnectedSocket(BluetoothSocket mmSocket, BluetoothDevice mmDevice) {
        // Cancel the thread that completed the connection
//        if (mConnectThread != null) {
//            mConnectThread.cancel();
//            mConnectThread = null;
//        }
        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }
        // Start the thread to manage the connection and perform transmissions
        mConnectedThread = new ConnectedThread(mmSocket);
        mConnectedThread.setPriority(10);
        mConnectedThread.start();
        // Send the name of the connected device back to the UI Activity
        Log.d(TAG, "Connected to " + mmDevice.getName());
        sendDeviceConnectionToActivity(mmDevice.getAddress(), true);
//        setState(STATE_CONNECTED);
    }

    public class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        private byte buffer[] = null;
        private byte playBuffer[] = null;
        private boolean intercomm = false;

        public ConnectedThread(BluetoothSocket socket) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get the input and output streams; using temp objects because
            // member streams are final.
            try {
                tmpIn = socket.getInputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when creating input stream", e);
            }
            try {
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when creating output stream", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
            intercomm = true;
        }

        public void run() {
            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
            playBuffer = new byte[minSize];
            // Playback received audio
            track.play();

            startRecording();

            // receive recording until an exception occurs.
            while (intercomm) {
                try {
                    if (mmInStream.available() == 0) {
                        //Do nothing
                    } else {
                        mmInStream.read(playBuffer);
                        track.write(playBuffer, 0, playBuffer.length);
                    }
                } catch (IOException e) {
                    Log.d("AUDIO", "Error when receiving recording");
                    sendDeviceConnectionToActivity(deviceMAC, false);
                    break;
                }
            }
        }

        // Record Audio
        public void startRecording() {
            Log.d("AUDIO", "Assigning recorder");
            buffer = new byte[bufferSize];

            // Start Recording
            recorder.startRecording();
            Log.d("startRecording", "passed");
            // Start a thread
            recordingThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
                    Log.d("startRecording", "sendRecording");
                    sendRecording();
                }
            }, "AudioRecorder Thread");
            recordingThread.setPriority(10);
            recordingThread.start();
        }
        // Method for sending Audio
        public void sendRecording() {
            // Infinite loop until microphone button is released
            while (intercomm) {
                try {
                    recorder.read(buffer, 0, bufferSize);
                    mmOutStream.write(buffer);
                } catch (IOException e) {
                    Log.d("AUDIO", "Error when sending recording");
                    sendErrorsToActivity("Error sending audio");
                }
            }
        }

        // Call this method from the main activity to shut down the connection.
        public void cancel() {
            intercomm = false;
            stopPlaying();
            stopRecording();
            destroyProcesses();
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "Could not close the connect socket", e);
            }
        }

        // Stop playing and free up resources
        public void stopPlaying() {
            if (track != null) {
                track.stop();
                track.flush();
            }
        }

        // Stop Recording and free up resources
        public void stopRecording() {
            if (recorder != null) {
                recorder.stop();
            }
        }

        public void destroyProcesses() {
            //Release resources for audio objects
            track.release();
            recorder.release();
        }
    }


我在octacore android oreo中测试了代码。但是,当我在sdk 23的手机上进行操作时,情况最糟。

1 个答案:

答案 0 :(得分:0)

您的AudioTrack饿死了,因为它没有足够快地从arduino接收数据。这很可能是由于BT连接过程中网络争用的增加。

您似乎正在为AudioTrack配置尽可能小的播放缓冲区。在大多数设备上,这只是几毫秒的音频,因此,如果AudioTrack每隔几毫秒没有收到更多数据,它将饿死,并且您会听到毛刺。

一种解决方案是增加AudioTrack's缓冲区的大小(可能增加到大约8000个样本或更多)。

此外,您尚未检查mmInStream.read()的返回值,这意味着您可能正在尝试“播放”仅部分填充的playBuffer

像现在一样,更改线程优先级不太可能产生本质上的差异。