Skip to main content

Start server

The server is a jar file, so it's not directly executable. On Android, app_process is the program to start the system server, all the apps, and standalone Java applications:

Equivalent ADB command
adb shell CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 2.1

With @yume-chan/scrcpy

@yume-chan/scrcpy only provides APIs to serialize and deserialize Scrcpy protocol messages. It can't start the server directly.

The ScrcpyOptions#serialize method generates the command line arguments for the specified options:

import { ScrcpyOptions2_1 } from "@yume-chan/scrcpy";

const options = ScrcpyOptions2_1({
// options
});

const commandLineArguments: string[] = options.serialize();

With @yume-chan/adb-scrcpy

Use AdbScrcpyClient.start() method with an option class to start the server. It automatically sets up port forwarding, launches the server, and connects to it.

import {
AdbScrcpyClient,
AdbScrcpyOptions2_1,
DEFAULT_SERVER_PATH,
ScrcpyOptions2_1,
} from "@yume-chan/scrcpy";
import SCRCPY_SERVER_VERSION from "@yume-chan/scrcpy/bin/version.js";

const options = new AdbScrcpyOptions2_1(
ScrcpyOptions2_1({
// options
}),
);

const client: AdbScrcpyClient = await AdbScrcpyClient.start(
adb,
DEFAULT_SERVER_PATH,
// If server binary was downloaded manually, must provide the correct version
SCRCPY_SERVER_VERSION,
options,
);

const stdout: ReadableStream<string> = client.stdout;

// `undefined` if `video: false` option was given
if (client.videoSteam) {
const { metadata: videoMetadata, stream: videoPacketStream } = await client.videoStream;
}

// `undefined` if `audio: false` option was given
if (client.audioStream) {
const metadata = await client.audioStream;
switch (metadata.type) {
case "disabled":
// Audio not supported by device
break;
case "errored":
// Other error when initializing audio
break;
case "success":
// Audio packets in the codec specified in options
const audioPacketStream: ReadableStream<ScrcpyMediaStreamPacket> = metadata.stream;
break;
}
}

// `undefined` if `control: false` option was given
const controller: ScrcpyControlMessageWriter | undefined = client.controller;
const clipboardStream: ReadableStream<string> = options.clipboard;

clipboardStream
.pipeTo(
new WritableStream<string>({
write(chunk) {
// Handle device clipboard change
console.log(chunk);
},
}),
)
.catch((error) => {
console.error(error);
});

// to stop the server
client.close();

With Tango

Subprocess#spawn can start a process on device:

import type { Adb } from "@yume-chan/adb";

declare const adb: Adb;

const process = await adb.subprocess.spawn(
"CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 2.1",
);
await process.stdout.pipeThrough(new DecodeUtf8Stream()).pipeTo(
new WritableStream<string>({
write(chunk) {
console.log(chunk);
},
}),
);