From bd2726370fd8c11d172dd4e2ed90cf5eac73749a Mon Sep 17 00:00:00 2001 From: Branden Archer Date: Fri, 1 Jan 2016 01:09:31 -0500 Subject: [PATCH 1/5] simplify playback of audio without AudioPlayer The additional thread to feed the AudioTrack was unnecessary. Because the AudioTrack API is blocking when data is written, and internally it buffers data, writing the data as soon as it is received from the network leads to better playback performance. It is also much simpler. --- src/protect/babymonitor/AudioPlayer.java | 67 --------------------- src/protect/babymonitor/ListenActivity.java | 37 ++++-------- 2 files changed, 12 insertions(+), 92 deletions(-) delete mode 100644 src/protect/babymonitor/AudioPlayer.java diff --git a/src/protect/babymonitor/AudioPlayer.java b/src/protect/babymonitor/AudioPlayer.java deleted file mode 100644 index c272f22..0000000 --- a/src/protect/babymonitor/AudioPlayer.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * This file is part of the Protect Baby Monitor. - * - * Protect Baby Monitor is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Protect Baby Monitor is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Protect Baby Monitor. If not, see . - */ -package protect.babymonitor; - -import java.util.concurrent.BlockingQueue; - -import android.media.AudioTrack; -import android.util.Log; - -public class AudioPlayer implements Runnable -{ - final String TAG = "BabyMonitor"; - - private final AudioTrack _audioTrack; - private final BlockingQueue _queue; - - public AudioPlayer(AudioTrack audioTrack, BlockingQueue queue) - { - _audioTrack = audioTrack; - _queue = queue; - } - - @Override - public void run() - { - Log.i(TAG, "Audio player thread started"); - - android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); - - _audioTrack.play(); - - try - { - while(Thread.currentThread().isInterrupted() == false) - { - byte [] data = _queue.take(); - int written = _audioTrack.write(data, 0, data.length); - - if(written != data.length) - { - Log.i(TAG, "Did not write bytes: " + (data.length - written)); - } - } - } - catch (InterruptedException e) - { - - } - - Log.i(TAG, "Audio player thread stopping"); - _audioTrack.stop(); - } -} diff --git a/src/protect/babymonitor/ListenActivity.java b/src/protect/babymonitor/ListenActivity.java index d0f1795..7e45868 100644 --- a/src/protect/babymonitor/ListenActivity.java +++ b/src/protect/babymonitor/ListenActivity.java @@ -20,9 +20,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import android.app.Activity; import android.media.AudioFormat; @@ -58,42 +55,32 @@ public class ListenActivity extends Activity bufferSize, AudioTrack.MODE_STREAM); - final BlockingQueue queue = new LinkedBlockingQueue(1 /*max samples queued*/); - - AudioPlayer audioPlayer = new AudioPlayer(audioTrack, queue); - Thread playThread = new Thread(audioPlayer); - playThread.start(); - setVolumeControlStream(AudioManager.STREAM_MUSIC); InputStream is = socket.getInputStream(); int read = 0; - while(socket.isConnected() && read != -1 && Thread.currentThread().isInterrupted() == false) + audioTrack.play(); + + try { byte [] buffer = new byte[bufferSize*2]; - read = is.read(buffer); - if(read > 0) + while(socket.isConnected() && read != -1 && Thread.currentThread().isInterrupted() == false) { - if(read < buffer.length) - { - buffer = Arrays.copyOf(buffer, read); - } + read = is.read(buffer); - try + if(read > 0) { - queue.add(buffer); - } - catch(IllegalStateException e) - { - Log.i(TAG, "Buffer full, dropping data"); + audioTrack.write(buffer, 0, read); } } } - - playThread.interrupt(); - socket.close(); + finally + { + audioTrack.stop(); + socket.close(); + } } @Override From f49e6be777cfc373c569d47cb7f495c39810e7f0 Mon Sep 17 00:00:00 2001 From: Branden Archer Date: Fri, 1 Jan 2016 01:17:35 -0500 Subject: [PATCH 2/5] Increase TCP send buffer size to the minimum audio buffer size The original buffer sized used was 1/2 what was intended. --- src/protect/babymonitor/MonitorActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protect/babymonitor/MonitorActivity.java b/src/protect/babymonitor/MonitorActivity.java index 52895bb..eee9adf 100644 --- a/src/protect/babymonitor/MonitorActivity.java +++ b/src/protect/babymonitor/MonitorActivity.java @@ -65,6 +65,7 @@ public class MonitorActivity extends Activity frequency, channelConfiguration, audioEncoding, bufferSize); + final int byteBufferSize = bufferSize*2; byte[] buffer = new byte[bufferSize*2]; try @@ -73,7 +74,7 @@ public class MonitorActivity extends Activity OutputStream out = socket.getOutputStream(); - socket.setSendBufferSize(bufferSize); + socket.setSendBufferSize(byteBufferSize); Log.d(TAG, "Socket send buffer size: " + socket.getSendBufferSize()); while (socket.isConnected() && Thread.currentThread().isInterrupted() == false) From f2007e16933dd88d4b7004c5b6c8946c45cd236d Mon Sep 17 00:00:00 2001 From: Branden Archer Date: Fri, 1 Jan 2016 01:20:26 -0500 Subject: [PATCH 3/5] Change variables which never should change to 'final' --- src/protect/babymonitor/DiscoverActivity.java | 6 ++--- src/protect/babymonitor/ListenActivity.java | 18 +++++++-------- src/protect/babymonitor/MonitorActivity.java | 22 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/protect/babymonitor/DiscoverActivity.java b/src/protect/babymonitor/DiscoverActivity.java index 3abfff3..3bef35b 100644 --- a/src/protect/babymonitor/DiscoverActivity.java +++ b/src/protect/babymonitor/DiscoverActivity.java @@ -82,9 +82,9 @@ public class DiscoverActivity extends Activity public void onItemClick(AdapterView parent, View view, int position, long id) { - ServiceInfoWrapper info = (ServiceInfoWrapper) parent.getItemAtPosition(position); - Intent i = new Intent(getApplicationContext(), ListenActivity.class); - Bundle b = new Bundle(); + final ServiceInfoWrapper info = (ServiceInfoWrapper) parent.getItemAtPosition(position); + final Intent i = new Intent(getApplicationContext(), ListenActivity.class); + final Bundle b = new Bundle(); b.putString("address", info.getAddress()); b.putInt("port", info.getPort()); b.putString("name", info.getName()); diff --git a/src/protect/babymonitor/ListenActivity.java b/src/protect/babymonitor/ListenActivity.java index 7e45868..95d9b70 100644 --- a/src/protect/babymonitor/ListenActivity.java +++ b/src/protect/babymonitor/ListenActivity.java @@ -39,14 +39,14 @@ public class ListenActivity extends Activity String _name; Thread _listenThread; - private void streamAudio(Socket socket) throws IllegalArgumentException, IllegalStateException, IOException + private void streamAudio(final Socket socket) throws IllegalArgumentException, IllegalStateException, IOException { Log.i(TAG, "Setting up stream"); - int frequency = 11025; - int channelConfiguration = AudioFormat.CHANNEL_OUT_MONO; - int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; - int bufferSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding); + final int frequency = 11025; + final int channelConfiguration = AudioFormat.CHANNEL_OUT_MONO; + final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; + final int bufferSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding); final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency, @@ -57,14 +57,14 @@ public class ListenActivity extends Activity setVolumeControlStream(AudioManager.STREAM_MUSIC); - InputStream is = socket.getInputStream(); + final InputStream is = socket.getInputStream(); int read = 0; audioTrack.play(); try { - byte [] buffer = new byte[bufferSize*2]; + final byte [] buffer = new byte[bufferSize*2]; while(socket.isConnected() && read != -1 && Thread.currentThread().isInterrupted() == false) { @@ -88,7 +88,7 @@ public class ListenActivity extends Activity { super.onCreate(savedInstanceState); - Bundle b = getIntent().getExtras(); + final Bundle b = getIntent().getExtras(); _address = b.getString("address"); _port = b.getInt("port"); _name = b.getString("name"); @@ -115,7 +115,7 @@ public class ListenActivity extends Activity { try { - Socket socket = new Socket(_address, _port); + final Socket socket = new Socket(_address, _port); streamAudio(socket); } catch (UnknownHostException e) diff --git a/src/protect/babymonitor/MonitorActivity.java b/src/protect/babymonitor/MonitorActivity.java index eee9adf..45a1875 100644 --- a/src/protect/babymonitor/MonitorActivity.java +++ b/src/protect/babymonitor/MonitorActivity.java @@ -56,30 +56,30 @@ public class MonitorActivity extends Activity } }); - int frequency = 11025; - int channelConfiguration = AudioFormat.CHANNEL_IN_MONO; - int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; + final int frequency = 11025; + final int channelConfiguration = AudioFormat.CHANNEL_IN_MONO; + final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; - int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding); - AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, + final int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding); + final AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, bufferSize); final int byteBufferSize = bufferSize*2; - byte[] buffer = new byte[bufferSize*2]; + final byte[] buffer = new byte[bufferSize*2]; try { audioRecord.startRecording(); - OutputStream out = socket.getOutputStream(); + final OutputStream out = socket.getOutputStream(); socket.setSendBufferSize(byteBufferSize); Log.d(TAG, "Socket send buffer size: " + socket.getSendBufferSize()); while (socket.isConnected() && Thread.currentThread().isInterrupted() == false) { - int read = audioRecord.read(buffer, 0, bufferSize); + final int read = audioRecord.read(buffer, 0, bufferSize); out.write(buffer, 0, read); } } @@ -114,7 +114,7 @@ public class MonitorActivity extends Activity serverSocket = new ServerSocket(0); // Store the chosen port. - int localPort = serverSocket.getLocalPort(); + final int localPort = serverSocket.getLocalPort(); // Register the service so that parent devices can // locate the child device @@ -202,9 +202,9 @@ public class MonitorActivity extends Activity return super.onOptionsItemSelected(item); } - private void registerService(int port) + private void registerService(final int port) { - NsdServiceInfo serviceInfo = new NsdServiceInfo(); + final NsdServiceInfo serviceInfo = new NsdServiceInfo(); serviceInfo.setServiceName("ProtectBabyMonitor"); serviceInfo.setServiceType("_babymonitor._tcp."); serviceInfo.setPort(port); From b361d56539571fbde87395bbbe97fbe3c5bac9c4 Mon Sep 17 00:00:00 2001 From: Branden Archer Date: Fri, 1 Jan 2016 01:21:28 -0500 Subject: [PATCH 4/5] Use byteBufferSize instead of bufferSize*2 byteBufferSize is already defined to be bufferSize*2 --- src/protect/babymonitor/ListenActivity.java | 3 ++- src/protect/babymonitor/MonitorActivity.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/protect/babymonitor/ListenActivity.java b/src/protect/babymonitor/ListenActivity.java index 95d9b70..bc2539e 100644 --- a/src/protect/babymonitor/ListenActivity.java +++ b/src/protect/babymonitor/ListenActivity.java @@ -47,6 +47,7 @@ public class ListenActivity extends Activity final int channelConfiguration = AudioFormat.CHANNEL_OUT_MONO; final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; final int bufferSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding); + final int byteBufferSize = bufferSize*2; final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency, @@ -64,7 +65,7 @@ public class ListenActivity extends Activity try { - final byte [] buffer = new byte[bufferSize*2]; + final byte [] buffer = new byte[byteBufferSize]; while(socket.isConnected() && read != -1 && Thread.currentThread().isInterrupted() == false) { diff --git a/src/protect/babymonitor/MonitorActivity.java b/src/protect/babymonitor/MonitorActivity.java index 45a1875..7d3774c 100644 --- a/src/protect/babymonitor/MonitorActivity.java +++ b/src/protect/babymonitor/MonitorActivity.java @@ -66,7 +66,7 @@ public class MonitorActivity extends Activity audioEncoding, bufferSize); final int byteBufferSize = bufferSize*2; - final byte[] buffer = new byte[bufferSize*2]; + final byte[] buffer = new byte[byteBufferSize]; try { From 1603ff11bbb0b9a55ac2fbda91b1692c12ef85c4 Mon Sep 17 00:00:00 2001 From: Branden Archer Date: Fri, 1 Jan 2016 01:22:20 -0500 Subject: [PATCH 5/5] Replace \032 in a service name with " " Some implementations of mDNS on Android display a space as \\032 and some display it as \032 Attempt to convert both types to a " " before displaying it --- src/protect/babymonitor/DiscoverActivity.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/protect/babymonitor/DiscoverActivity.java b/src/protect/babymonitor/DiscoverActivity.java index 3bef35b..f75565c 100644 --- a/src/protect/babymonitor/DiscoverActivity.java +++ b/src/protect/babymonitor/DiscoverActivity.java @@ -206,8 +206,13 @@ class ServiceInfoWrapper // If there is more than one service on the network, it will // have a number at the end, but will appear as the following: // "ProtectBabyMonitor\\032(number) - // Replace \\032 with a "" - return _info.getServiceName().replace("\\\\032", " "); + // or + // "ProtectBabyMonitor\032(number) + // Replace \\032 and \032 with a " " + String serviceName = _info.getServiceName(); + serviceName = serviceName.replace("\\\\032", " "); + serviceName = serviceName.replace("\\032", " "); + return serviceName; } @Override