Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AudioTrack buffer underrun when streaming from bluetooth socket

I am testing the concept of an Android application that allows you to stream Bluetooth over RFCOMM (from PC to phone). I am able to transfer audio from my computer to the phone no problem and start streaming the audio.

The problem is that the audio begins to stutter and I get a buffer underrun error message from the AudioTrack. Reading from the socket is what is taking the most time. When I timed it, underruns occurred when it takes >= 1000 milliseconds for the read to return, when on average it takes a few hundred to return. Here is my code below:

    public ConnectedThread(BluetoothSocket socket) {
        this.setPriority(MAX_PRIORITY);
        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);
        }
        minBuffSize = AudioTrack.getMinBufferSize(48000,  CHANNEL_OUT_MONO,  AudioFormat.ENCODING_PCM_16BIT);
        m_audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 48000, CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_PCM_16BIT, minBuffSize /* 1 second buffer */,
                AudioTrack.MODE_STREAM);

        mmBuffer = new byte[minBuffSize * 2];
        mmInStream = tmpIn;
        mmOutStream = tmpOut;
//        distream = new DataInputStream(new BufferedInputStream(mmInStream, 1000000));
        distream = new DataInputStream(mmInStream);
    }

    public void run() {

        int numBytes = 0; // bytes returned from read()

        // Keep listening to the InputStream until an exception occurs.

        boolean firstRun = true;

        while (true) {

            try {
                final long startTime = System.currentTimeMillis();
                distream.readFully(mmBuffer);
                final long endTime = System.currentTimeMillis();
                int temp = m_audioTrack.write(mmBuffer, 0, mmBuffer.length);
                System.out.println("Total execution time: " + (endTime - startTime) );
                if (firstRun) {
                    m_audioTrack.play();
                    firstRun = false;
                }
            } catch (IOException e) {
                Log.d(TAG, "Input stream was disconnected", e);
                break;
            }

        }

Any suggestions on how I can speed up the reads from the socket or prevent the underruns in general?

like image 427
KShak Avatar asked Sep 10 '17 22:09

KShak


1 Answers

Root cause analysis

Let's consider this piece of code:

minBuffSize = AudioTrack.getMinBufferSize(48000,  CHANNEL_OUT_MONO,  AudioFormat.ENCODING_PCM_16BIT);
m_audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 48000, CHANNEL_OUT_STEREO,
        AudioFormat.ENCODING_PCM_16BIT, minBuffSize /* 1 second buffer */,
        AudioTrack.MODE_STREAM);

Probable root cause

Please, note the inconsistency:

  • The AudioTrack.getMinBufferSize() method is called with the CHANNEL_OUT_MONO value.
  • The AudioTrack constructor is called with the CHANNEL_OUT_STEREO value.

Solution

Please, consider using the same arguments for the AudioTrack.getMinBufferSize() method call and, in particular, the CHANNEL_OUT_STEREO value. Even better, please, consider extracting the appropriate constants or local variables to reuse them for both method calls: sampleRate, channelConfig, audioFormat.

Additional references (Probably, not needed.)

  1. On a different problem, but it might be useful. The question and the answers to it: android - AudioRecord: buffer overflow? - Stack Overflow.
like image 155
Sergey Vyacheslavovich Brunov Avatar answered Nov 07 '22 13:11

Sergey Vyacheslavovich Brunov