Skip to content

Commit 4bfb27b

Browse files
committed
Add raw audio recorder
Add an alternative AudioRecorder to stream raw packets without encoding. PR #3757 <Genymobile/scrcpy#3757>
1 parent b086847 commit 4bfb27b

File tree

3 files changed

+84
-3
lines changed

3 files changed

+84
-3
lines changed

server/src/main/java/com/genymobile/scrcpy/AudioCodec.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.media.MediaFormat;
44

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

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.genymobile.scrcpy;
2+
3+
import android.media.MediaCodec;
4+
5+
import java.io.IOException;
6+
import java.nio.ByteBuffer;
7+
8+
public final class AudioRawRecorder implements AudioRecorder {
9+
10+
private final Streamer streamer;
11+
12+
private Thread thread;
13+
14+
private static final int READ_MS = 5; // milliseconds
15+
private static final int READ_SIZE = AudioCapture.millisToBytes(READ_MS);
16+
17+
public AudioRawRecorder(Streamer streamer) {
18+
this.streamer = streamer;
19+
}
20+
21+
private void record() throws IOException, AudioCaptureForegroundException {
22+
final ByteBuffer buffer = ByteBuffer.allocateDirect(READ_SIZE);
23+
final MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
24+
25+
AudioCapture capture = new AudioCapture();
26+
try {
27+
capture.start();
28+
29+
streamer.writeHeader();
30+
while (!Thread.currentThread().isInterrupted()) {
31+
buffer.position(0);
32+
int r = capture.read(buffer, READ_SIZE, bufferInfo);
33+
if (r < 0) {
34+
throw new IOException("Could not read audio: " + r);
35+
}
36+
buffer.limit(r);
37+
38+
streamer.writePacket(buffer, bufferInfo);
39+
}
40+
} catch (Throwable e) {
41+
// Notify the client that the audio could not be captured
42+
streamer.writeDisableStream(false);
43+
throw e;
44+
} finally {
45+
capture.stop();
46+
}
47+
}
48+
49+
public void start() {
50+
thread = new Thread(() -> {
51+
try {
52+
record();
53+
} catch (AudioCaptureForegroundException e) {
54+
// Do not print stack trace, a user-friendly error-message has already been logged
55+
} catch (IOException e) {
56+
Ln.e("Audio recording error", e);
57+
} finally {
58+
Ln.d("Audio recorder stopped");
59+
}
60+
});
61+
thread.start();
62+
}
63+
64+
public void stop() {
65+
if (thread != null) {
66+
thread.interrupt();
67+
}
68+
}
69+
70+
public void join() throws InterruptedException {
71+
if (thread != null) {
72+
thread.join();
73+
}
74+
}
75+
}

server/src/main/java/com/genymobile/scrcpy/Server.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,14 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc
109109
}
110110

111111
if (audio) {
112-
Streamer audioStreamer = new Streamer(connection.getAudioFd(), options.getAudioCodec(), options.getSendCodecId(),
113-
options.getSendFrameMeta());
114-
audioRecorder = new AudioEncoder(audioStreamer, options.getAudioBitRate(), options.getAudioCodecOptions(), options.getAudioEncoder());
112+
AudioCodec audioCodec = options.getAudioCodec();
113+
Streamer audioStreamer = new Streamer(connection.getAudioFd(), audioCodec, options.getSendCodecId(), options.getSendFrameMeta());
114+
if (audioCodec == AudioCodec.RAW) {
115+
audioRecorder = new AudioRawRecorder(audioStreamer);
116+
} else {
117+
audioRecorder = new AudioEncoder(audioStreamer, options.getAudioBitRate(), options.getAudioCodecOptions(),
118+
options.getAudioEncoder());
119+
}
115120
audioRecorder.start();
116121
}
117122

0 commit comments

Comments
 (0)