Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5ab96832 authored by luca020400's avatar luca020400
Browse files

Fix high quality recorder

The current threading model was completely wrong.

A semaphore was used to lock the thread externally,
this should never be attempted, the correct way to block a thread is
to make it do nothing, in our case if we read 0 bytes from the
audio record

Also fix a potential deadlock in stopRecording by
waiting at most 1 second after stopping the thread

Change-Id: I61448185a8fee4287caf69f0c2a1d34b1517c367
parent 540b4719
Loading
Loading
Loading
Loading
+17 −33
Original line number Diff line number Diff line
@@ -24,13 +24,11 @@ import android.util.Log;
import androidx.annotation.RequiresPermission;

import org.lineageos.recorder.utils.PcmConverter;
import org.lineageos.recorder.utils.Utils;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;

public class HighQualityRecorder implements SoundRecording {
@@ -46,10 +44,9 @@ public class HighQualityRecorder implements SoundRecording {
    private AudioRecord mRecord;
    private Path mPath;

    private volatile byte[] mData;
    private byte[] mData;
    private Thread mThread;
    private final AtomicBoolean mIsRecording = new AtomicBoolean(false);
    private final Semaphore mPauseSemaphore = new Semaphore(1);

    @Override
    @RequiresPermission(Manifest.permission.RECORD_AUDIO)
@@ -73,21 +70,15 @@ public class HighQualityRecorder implements SoundRecording {
        }

        mIsRecording.set(false);
        try {
            mThread.join();
        } catch (InterruptedException e) {
            Log.e(TAG, "Interrupted thread", e);
        }

        // needed to prevent app crash when starting and stopping too fast
        try {
            mRecord.stop();
            PcmConverter.convertToWave(mPath, BUFFER_SIZE);
        } catch (RuntimeException rte) {
            return false;
            mThread.join(1000);
        } catch (InterruptedException e) {
            // Wait at most 1 second, if we fail save the current data
        } finally {
            mRecord.release();
            PcmConverter.convertToWave(mPath, BUFFER_SIZE);
        }

        return true;
    }

@@ -97,23 +88,17 @@ public class HighQualityRecorder implements SoundRecording {
            return false;
        }

        try {
            mPauseSemaphore.acquire();
        } catch (InterruptedException e) {
            Log.e(TAG, "Failed to acquire pause semaphore", e);
        }
        mRecord.stop();
        return true;
    }

    @Override
    public boolean resumeRecording() {
        if (!mIsRecording.get() || mPauseSemaphore.availablePermits() == 1) {
        if (!mIsRecording.get()) {
            return false;
        }

        mRecord.startRecording();
        mPauseSemaphore.release();
        return true;
    }

@@ -141,12 +126,8 @@ public class HighQualityRecorder implements SoundRecording {
    }

    private void recordingThreadImpl() {
        BufferedOutputStream out = null;
        try {
            out = new BufferedOutputStream(Files.newOutputStream(mPath));

        try (BufferedOutputStream out = new BufferedOutputStream(Files.newOutputStream(mPath))) {
            while (mIsRecording.get()) {
                mPauseSemaphore.acquireUninterruptibly();
                try {
                    int status = mRecord.read(mData, 0, BUFFER_SIZE);
                    switch (status) {
@@ -155,22 +136,25 @@ public class HighQualityRecorder implements SoundRecording {
                            Log.e(TAG, "Error reading audio record data");
                            mIsRecording.set(false);
                            break;
                        case AudioRecord.ERROR_DEAD_OBJECT:
                        case AudioRecord.ERROR:
                            continue;
                        default:
                            // Status indicates the number of bytes
                            if (status != 0) {
                                out.write(mData, 0, BUFFER_SIZE);
                            break;
                            }
                    }
                } catch (IOException e) {
                    Log.e(TAG, "Failed to write audio stream", e);
                    // Stop recording
                    mIsRecording.set(false);
                } finally {
                    mPauseSemaphore.release();
                }
            }
            mRecord.stop();
            mRecord.release();
        } catch (IOException e) {
            Log.e(TAG, "Can't find output file", e);
        } finally {
            Utils.closeQuietly(out);
        }
    }
}