Add raw audio recorder

Add an alternative AudioRecorder to stream raw packets without encoding.

PR #3757 <https://github.com/Genymobile/scrcpy/pull/3757>
This commit is contained in:
Romain Vimont 2023-03-03 21:14:28 +01:00
parent dc228eaad0
commit 66b6c06443
3 changed files with 86 additions and 3 deletions

View file

@ -4,7 +4,8 @@ import android.media.MediaFormat;
public enum AudioCodec implements Codec { public enum AudioCodec implements Codec {
OPUS(0x6f_70_75_73, "opus", MediaFormat.MIMETYPE_AUDIO_OPUS), OPUS(0x6f_70_75_73, "opus", MediaFormat.MIMETYPE_AUDIO_OPUS),
AAC(0x00_61_61_63, "aac", MediaFormat.MIMETYPE_AUDIO_AAC); AAC(0x00_61_61_63, "aac", MediaFormat.MIMETYPE_AUDIO_AAC),
RAW(0x00_72_61_77, "raw", MediaFormat.MIMETYPE_AUDIO_RAW);
private final int id; // 4-byte ASCII representation of the name private final int id; // 4-byte ASCII representation of the name
private final String name; private final String name;

View file

@ -0,0 +1,75 @@
package com.genymobile.scrcpy;
import android.media.MediaCodec;
import java.io.IOException;
import java.nio.ByteBuffer;
public final class AudioRawRecorder implements AsyncProcessor {
private final Streamer streamer;
private Thread thread;
private static final int READ_MS = 5; // milliseconds
private static final int READ_SIZE = AudioCapture.millisToBytes(READ_MS);
public AudioRawRecorder(Streamer streamer) {
this.streamer = streamer;
}
private void record() throws IOException, AudioCaptureForegroundException {
final ByteBuffer buffer = ByteBuffer.allocateDirect(READ_SIZE);
final MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
AudioCapture capture = new AudioCapture();
try {
capture.start();
streamer.writeHeader();
while (!Thread.currentThread().isInterrupted()) {
buffer.position(0);
int r = capture.read(buffer, READ_SIZE, bufferInfo);
if (r < 0) {
throw new IOException("Could not read audio: " + r);
}
buffer.limit(r);
streamer.writePacket(buffer, bufferInfo);
}
} catch (Throwable e) {
// Notify the client that the audio could not be captured
streamer.writeDisableStream(false);
throw e;
} finally {
capture.stop();
}
}
public void start() {
thread = new Thread(() -> {
try {
record();
} catch (AudioCaptureForegroundException e) {
// Do not print stack trace, a user-friendly error-message has already been logged
} catch (IOException e) {
Ln.e("Audio recording error", e);
} finally {
Ln.d("Audio recorder stopped");
}
});
thread.start();
}
public void stop() {
if (thread != null) {
thread.interrupt();
}
}
public void join() throws InterruptedException {
if (thread != null) {
thread.join();
}
}
}

View file

@ -107,9 +107,16 @@ public final class Server {
} }
if (audio) { if (audio) {
Streamer audioStreamer = new Streamer(connection.getAudioFd(), options.getAudioCodec(), options.getSendCodecId(), AudioCodec audioCodec = options.getAudioCodec();
Streamer audioStreamer = new Streamer(connection.getAudioFd(), audioCodec, options.getSendCodecId(),
options.getSendFrameMeta()); options.getSendFrameMeta());
AudioEncoder audioRecorder = new AudioEncoder(audioStreamer, options.getAudioBitRate(), options.getAudioCodecOptions(), options.getAudioEncoder()); AsyncProcessor audioRecorder;
if (audioCodec == AudioCodec.RAW) {
audioRecorder = new AudioRawRecorder(audioStreamer);
} else {
audioRecorder = new AudioEncoder(audioStreamer, options.getAudioBitRate(), options.getAudioCodecOptions(),
options.getAudioEncoder());
}
asyncProcessors.add(audioRecorder); asyncProcessors.add(audioRecorder);
} }