aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid/lena/scrcpy/Wire.java
diff options
context:
space:
mode:
authorLena <lena@omega>2026-01-01 00:00:00 +0000
committerLena <lena@omega>2026-06-24 22:14:50 +0300
commiteb0c8951196c637e44daf3c0617b131b997d5d2c (patch)
tree2f83b6a41cee745467e53fc401942b82cea286ea /app/src/main/java/invalid/lena/scrcpy/Wire.java
downloadscrcpy-android-eb0c8951196c637e44daf3c0617b131b997d5d2c.tar.gz
scrcpy-android: mirror an Android device over wireless ADB0.1
Native Java app for Android 12+ that mirrors another Android device over wireless ADB, forwarding video, audio, touch input, and clipboard. Bundles a pinned scrcpy-server.jar and the vendored libadb-android stack. Supports h264/h265/av1 video and raw/opus audio with in-app codec selection. No NDK, no Kotlin. Includes JVM unit tests and a Docker-based emulator e2e rig.
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Wire.java')
-rw-r--r--app/src/main/java/invalid/lena/scrcpy/Wire.java99
1 files changed, 99 insertions, 0 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Wire.java b/app/src/main/java/invalid/lena/scrcpy/Wire.java
new file mode 100644
index 0000000..7ed6c6a
--- /dev/null
+++ b/app/src/main/java/invalid/lena/scrcpy/Wire.java
@@ -0,0 +1,99 @@
+package invalid.lena.scrcpy;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+// Byte-order helpers used everywhere we touch raw streams.
+//
+// scrcpy server framing is big-endian (java standard).
+// adb sync framing is little-endian (legacy).
+//
+// FOURCCs are read as a big-endian uint32, which matches the literal
+// 4-char ASCII tag, so '"h264"' becomes 0x68323634.
+public final class Wire {
+
+ private Wire() {}
+
+ // ---- big-endian (scrcpy) ----
+
+ public static int readBe32(byte[] b, int off) {
+ return ((b[off] & 0xff) << 24)
+ | ((b[off + 1] & 0xff) << 16)
+ | ((b[off + 2] & 0xff) << 8)
+ | (b[off + 3] & 0xff);
+ }
+
+ public static long readBe64(byte[] b, int off) {
+ return ((long)(readBe32(b, off)) << 32) | (readBe32(b, off + 4) & 0xffffffffL);
+ }
+
+ public static void writeBe32(byte[] b, int off, int v) {
+ b[off] = (byte)(v >>> 24);
+ b[off + 1] = (byte)(v >>> 16);
+ b[off + 2] = (byte)(v >>> 8);
+ b[off + 3] = (byte) v;
+ }
+
+ public static void writeBe64(byte[] b, int off, long v) {
+ writeBe32(b, off, (int)(v >>> 32));
+ writeBe32(b, off + 4, (int) v);
+ }
+
+ // ---- little-endian (adb sync) ----
+
+ public static int readLe32(byte[] b, int off) {
+ return (b[off] & 0xff)
+ | ((b[off + 1] & 0xff) << 8)
+ | ((b[off + 2] & 0xff) << 16)
+ | ((b[off + 3] & 0xff) << 24);
+ }
+
+ public static void writeLe32(byte[] b, int off, int v) {
+ b[off] = (byte) v;
+ b[off + 1] = (byte)(v >>> 8);
+ b[off + 2] = (byte)(v >>> 16);
+ b[off + 3] = (byte)(v >>> 24);
+ }
+
+ // ---- I/O helpers ----
+
+ public static void readFully(InputStream in, byte[] buf, int off, int len) throws IOException {
+ int got = 0;
+ while (got < len) {
+ int n = in.read(buf, off + got, len - got);
+ if (n < 0) throw new EOFException(
+ "short read: wanted " + len + " got " + got);
+ got += n;
+ }
+ }
+
+ public static void readFully(InputStream in, byte[] buf) throws IOException {
+ readFully(in, buf, 0, buf.length);
+ }
+
+ // scrcpy codec identifiers, mirrored from com.genymobile.scrcpy.{video,audio}.*Codec.
+ // Short names (raw, aac) are NUL-padded on the left, not space-padded on the right.
+ // Declared as int-literal constants so they can drive switch-case labels.
+ public static final int CODEC_H264 = 0x68_32_36_34; // 'h264'
+ public static final int CODEC_H265 = 0x68_32_36_35; // 'h265'
+ public static final int CODEC_AV1 = 0x61_76_30_31; // 'av01'
+ public static final int CODEC_OPUS = 0x6f_70_75_73; // 'opus'
+ public static final int CODEC_FLAC = 0x66_6c_61_63; // 'flac'
+ public static final int CODEC_AAC = 0x00_61_61_63; // '\0aac'
+ public static final int CODEC_RAW = 0x00_72_61_77; // '\0raw'
+
+ public static String fourccName(int v) {
+ // Skip any leading NUL bytes - scrcpy left-pads short names like
+ // 'raw' and 'aac' with \0, which would otherwise render as
+ // non-printable characters in logs.
+ char[] cs = new char[]{
+ (char)((v >>> 24) & 0xff),
+ (char)((v >>> 16) & 0xff),
+ (char)((v >>> 8) & 0xff),
+ (char)( v & 0xff)};
+ int from = 0;
+ while (from < cs.length && cs[from] == 0) from++;
+ return new String(cs, from, cs.length - from);
+ }
+}