Skip to content

Commit

Permalink
Add raw audio recorder
Browse files Browse the repository at this point in the history
Add an alternative AudioRecorder to stream raw packets without encoding.

PR #3757 <#3757>
  • Loading branch information
rom1v committed Mar 3, 2023
1 parent ed070a0 commit 73cd59a
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 3 deletions.
1 change: 1 addition & 0 deletions server/src/main/java/com/genymobile/scrcpy/AudioCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.media.MediaFormat;

public enum AudioCodec implements Codec {
RAW(0x00_72_61_77, "raw", MediaFormat.MIMETYPE_AUDIO_RAW),
OPUS(0x6f_70_75_73, "opus", MediaFormat.MIMETYPE_AUDIO_OPUS),
AAC(0x00_61_61_63, "aac", MediaFormat.MIMETYPE_AUDIO_AAC);

Expand Down
75 changes: 75 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/AudioRawRecorder.java
Original file line number Diff line number Diff line change
@@ -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 AudioRecorder {

private final Streamer streamer;

private Thread thread;

private static final int BUFFER_MS = 5; // milliseconds
private static final int BUFFER_SIZE = AudioCapture.millisToBytes(BUFFER_MS);

public AudioRawRecorder(Streamer streamer) {
this.streamer = streamer;
}

private void record() throws IOException, AudioCaptureForegroundException {
final ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_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, BUFFER_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();
}
}
}
11 changes: 8 additions & 3 deletions server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,14 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc
}

if (audio) {
Streamer audioStreamer = new Streamer(connection.getAudioFd(), options.getAudioCodec(), options.getSendCodecId(),
options.getSendFrameMeta());
audioRecorder = new AudioEncoder(audioStreamer, options.getAudioBitRate(), options.getAudioCodecOptions(), options.getAudioEncoder());
AudioCodec audioCodec = options.getAudioCodec();
Streamer audioStreamer = new Streamer(connection.getAudioFd(), audioCodec, options.getSendCodecId(), options.getSendFrameMeta());
if (audioCodec == AudioCodec.RAW) {
audioRecorder = new AudioRawRecorder(audioStreamer);
} else {
audioRecorder = new AudioEncoder(audioStreamer, options.getAudioBitRate(), options.getAudioCodecOptions(),
options.getAudioEncoder());
}
audioRecorder.start();
}

Expand Down

0 comments on commit 73cd59a

Please sign in to comment.