Skip to content

Commit

Permalink
Improve error handling for audio recording failures
Browse files Browse the repository at this point in the history
This commit enhances the stability of the audio recording feature by improving error handling mechanisms. It ensures that resources are properly cleaned up in case of failures, preventing potential resource leaks and application crashes.

Resolves loonix#6
  • Loading branch information
SathushChandranAL committed Apr 24, 2024
1 parent 3d3c3a1 commit fa1f21b
Showing 1 changed file with 87 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
import android.os.Build;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
Expand Down Expand Up @@ -42,7 +40,7 @@ public class AACRecordThread extends RecordThread {

AACRecordThread(int sampleRate, String filePath, String extension) {
super(sampleRate, filePath, extension);
// TODO: find a way to change sample rate

this.sampleRate = SAMPLE_RATE;
}

Expand Down Expand Up @@ -110,15 +108,15 @@ public HashMap<String, Object> stop() {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Error closing file output stream", e);
}

return currentResult;
}

@Override
public int getDuration() {
long duration = dataSize / (sampleRate * 2 * 1);
long duration = dataSize / ((long) sampleRate * 2);
return (int) duration;
}

Expand All @@ -132,29 +130,30 @@ public double getAveragePower() {
return averagePower;
}

@Override
public String getFilePath() {
return filePath;
}



@Override

public void run() {
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] codecOutputBuffers = mediaCodec.getOutputBuffers();

while (status == "recording") {
while ("recording".equals(status)) {
boolean success = handleCodecInput(audioRecord, mediaCodec, codecInputBuffers, Thread.currentThread().isAlive());
if (success) {
try {
handleCodecOutput(mediaCodec, codecOutputBuffers, bufferInfo, fileOutputStream);
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Error handling codec output", e);
break; // Exit the loop on exception to avoid continuous failure
}
}
}
}


private void startThread() {
recordingThread = new Thread(this, "Audio Processing Thread");
recordingThread.start();
Expand All @@ -166,69 +165,91 @@ private void resetRecorder() {
dataSize = 0;
}

private boolean handleCodecInput(AudioRecord audioRecord,
MediaCodec mediaCodec, ByteBuffer[] codecInputBuffers,
boolean running) {
private synchronized boolean handleCodecInput(AudioRecord audioRecord, MediaCodec mediaCodec, ByteBuffer[] codecInputBuffers, boolean running) {
if ((mediaCodec == null) || !status.equals("recording") ) {
return false; // Early exit if codec is not in the correct state
}

byte[] audioRecordData = new byte[bufferSize];
int length = audioRecord.read(audioRecordData, 0, audioRecordData.length);
dataSize += audioRecordData.length;
updatePowers(audioRecordData);

if (length == AudioRecord.ERROR_BAD_VALUE ||
length == AudioRecord.ERROR_INVALID_OPERATION ||
length != bufferSize) {

if (length != bufferSize) {
Log.d(TAG, "length != bufferSize");
if (length > 0) {
try {
int codecInputBufferIndex = mediaCodec.dequeueInputBuffer(10000);
if (codecInputBufferIndex >= 0) {
ByteBuffer codecBuffer = codecInputBuffers[codecInputBufferIndex];
codecBuffer.clear();
codecBuffer.put(audioRecordData);
mediaCodec.queueInputBuffer(codecInputBufferIndex, 0, length, 0, running ? 0 : MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
return true;
} catch (IllegalStateException e) {
Log.e(TAG, "IllegalStateException handling codec input", e);
return false;
} catch (Exception e) {
Log.e(TAG, "Exception handling codec input", e);
return false;
}
}
return false;
}

int codecInputBufferIndex = mediaCodec.dequeueInputBuffer(10 * 1000);

if (codecInputBufferIndex >= 0) {
ByteBuffer codecBuffer = codecInputBuffers[codecInputBufferIndex];
codecBuffer.clear();
codecBuffer.put(audioRecordData);
mediaCodec.queueInputBuffer(codecInputBufferIndex, 0, length, 0, running ? 0 : MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}

return true;
}
private void handleCodecOutput(MediaCodec mediaCodec, ByteBuffer[] codecOutputBuffers, MediaCodec.BufferInfo bufferInfo, OutputStream outputStream) throws IOException {
int codecOutputBufferIndex;
try {
codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
while (codecOutputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
if (codecOutputBufferIndex >= 0) {
ByteBuffer encoderOutputBuffer = codecOutputBuffers[codecOutputBufferIndex];

private void handleCodecOutput(MediaCodec mediaCodec,
ByteBuffer[] codecOutputBuffers,
MediaCodec.BufferInfo bufferInfo,
OutputStream outputStream)
throws IOException {
int codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
encoderOutputBuffer.position(bufferInfo.offset);
encoderOutputBuffer.limit(bufferInfo.offset + bufferInfo.size);

while (codecOutputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
if (codecOutputBufferIndex >= 0) {
ByteBuffer encoderOutputBuffer = codecOutputBuffers[codecOutputBufferIndex];
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
byte[] header = createAdtsHeader(bufferInfo.size - bufferInfo.offset);
outputStream.write(header);

encoderOutputBuffer.position(bufferInfo.offset);
encoderOutputBuffer.limit(bufferInfo.offset + bufferInfo.size);
byte[] data = new byte[encoderOutputBuffer.remaining()];
if (encoderOutputBuffer.remaining() >= data.length) {

if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
byte[] header = createAdtsHeader(bufferInfo.size - bufferInfo.offset);

encoderOutputBuffer.get(data);
outputStream.write(data);
} else {
// Buffer does not have enough data
Log.e(TAG, "Buffer underflow error!");
}

outputStream.write(header);
}

byte[] data = new byte[encoderOutputBuffer.remaining()];
encoderOutputBuffer.get(data);
outputStream.write(data);
encoderOutputBuffer.clear();
mediaCodec.releaseOutputBuffer(codecOutputBufferIndex, false);
} else if (codecOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
codecOutputBuffers = mediaCodec.getOutputBuffers();
}
codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
} catch (IllegalStateException | IOException e) {
Log.e(TAG, "Failed handling codec output due to an exception", e);

try {
// Attempt to stop and release the codec
if (mediaCodec != null) {
mediaCodec.stop();
mediaCodec.release();
mediaCodec = null; // recreate before using
}
// Close the output stream if it's not null
if (outputStream != null) {
outputStream.close();
outputStream = null; // Reset outputStream to ensure no further usage without proper initialization
}

encoderOutputBuffer.clear();
status = "error"; // handle in thread management

mediaCodec.releaseOutputBuffer(codecOutputBufferIndex, false);
} else if (codecOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
codecOutputBuffers = mediaCodec.getOutputBuffers();
} catch (Exception cleanupException) {
Log.e(TAG, "Error during cleanup after an initial exception", cleanupException);
}

codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
}

Expand Down Expand Up @@ -283,24 +304,20 @@ private AudioRecord createAudioRecord(int bufferSize) {
throw new RuntimeException("Unable to initialize AudioRecord");
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (android.media.audiofx.NoiseSuppressor.isAvailable()) {
android.media.audiofx.NoiseSuppressor noiseSuppressor = android.media.audiofx.NoiseSuppressor
.create(audioRecord.getAudioSessionId());
if (noiseSuppressor != null) {
noiseSuppressor.setEnabled(true);
}
if (android.media.audiofx.NoiseSuppressor.isAvailable()) {
android.media.audiofx.NoiseSuppressor noiseSuppressor = android.media.audiofx.NoiseSuppressor
.create(audioRecord.getAudioSessionId());
if (noiseSuppressor != null) {
noiseSuppressor.setEnabled(true);
}
}


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (android.media.audiofx.AutomaticGainControl.isAvailable()) {
android.media.audiofx.AutomaticGainControl automaticGainControl = android.media.audiofx.AutomaticGainControl
.create(audioRecord.getAudioSessionId());
if (automaticGainControl != null) {
automaticGainControl.setEnabled(true);
}
if (android.media.audiofx.AutomaticGainControl.isAvailable()) {
android.media.audiofx.AutomaticGainControl automaticGainControl = android.media.audiofx.AutomaticGainControl
.create(audioRecord.getAudioSessionId());
if (automaticGainControl != null) {
automaticGainControl.setEnabled(true);
}
}

Expand Down

0 comments on commit fa1f21b

Please sign in to comment.