My problem changed a bit, please take a look at the EDIT 2 below
I am learning how to work with Services and audio recording on Android.
I want to create an app, that will do nothing except starting a service: after requesting permissions (RECORD_AUDIO
, INTERNET
) the app only calls startService()
from the MainActivity
.
The service will then record audio and stream it to given IP address. I took my inspiration for the audio streaming server and client from the answers to this question. I am testing the app on Android 6.0 in the Android Studio Emulator so far.
This is the onStartCommand()
method of my service. I create a new thread where I wait until the user has clicked "Allow" in the permissions request dialog. When I receive the permissions I initialize the AudioRecord
and then I read from it in a while
-loop and send the data away.
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Thread streamThread = new Thread(new Runnable() {
@Override
public void run() {
waitForPermissions();
try {
DatagramSocket socket = new DatagramSocket();
byte[] buffer = new byte[minBufSize];
DatagramPacket packet;
final InetAddress destination = InetAddress.getByName(address);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, minBufSize * 10);
recorder.startRecording();
while (status == true) {
minBufSize = recorder.read(buffer, 0, buffer.length);
packet = new DatagramPacket(buffer, buffer.length, destination, port);
socket.send(packet);
}
} catch (...) {...}
}
});
streamThread.start();
return START_STICKY;
}
In onDestroy()
I set the flag status
to false
so that the loop in the thread created in onStartCommand()
terminates. Then I release the recorder
so that it can be initialized and used again.
@Override
public void onDestroy(){
status = false;
recorder.stop();
recorder.release();
super.onDestroy();
}
This app works works only partially:
- When I launch the listening server on the computer and the app in the emulator, I can hear the audio normally.
- When I minimize the app (tap Home button), the audio stream is still OK.
- My problem is that when I kill the app (swipe it right in the running apps screen), the audio stops. In the Logcat I can see that the service has been restarted and runs as usual (initializes the
AudioRecord
and everything, no exceptions thrown), only I can't hear anything. When I run the app again (and therefore call thestartService()
again), I see an exception in the Logcat telling me that theAudioRecord
start failed with an error code -38 (meaning that the microphone is in use by the previous instance of the service).
What am I doing wrong here? Why is the service running, but audio not streaming when I kill the app?
Many thanks for your answers.
EDIT:
I know this approach won't work on newer Android versions. This app is just for private purposes and will be run on one of my old phones, either with Android 5.1.1 or 6.0.
After I read the article in this answer I moved what was previously in onDestroy
to onTaskRemoved
. However, nothing changed.
The Service is apparently still sending some packets after it is restarted. I looked at them in Wireshark and the packets are still leaving the device and the Server is still receiving them. The payload of the packets is nonzero, unique for every packet.
EDIT 2:
I changed the buffer size to AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) * 10
in the Android app and AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) * 20
in the Server application. Now when I swipe the app, the Service
restarts and then I can hear some audio. But the sound is very chunky after the restart and I don't know why. It also gets chunky when I uninstall the app, keep the server running and then install and run the app again.
So the problem is probably on the Server side. What could be the problem with the server? Am I working correctly with the SourceDataLine
? The Server code looks like this (taken from here):
...
static int sampleRate = 8000;
static int bufferLen = minBufSize * 20;
public static void main(String args[]) throws Exception {
DatagramSocket serverSocket = new DatagramSocket(port);
byte[] receiveData = new byte[bufferLen];
format = new AudioFormat(sampleRate, 16, 1, true, false);
dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(format);
sourceDataLine.start();
FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(volumeControl.getMaximum());
while (status == true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
toSpeaker(receivePacket.getData(), receivePacket.getLength());
}
sourceDataLine.drain();
sourceDataLine.close();
}
public static void toSpeaker(byte soundbytes[], int length) {
try {
sourceDataLine.write(soundbytes, 0, length);
} catch (...) {...}
}
...
The sample rate of the server is identical to the Android app. Format is AudioFormat.ENCODING_PCM_16BIT
.