mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-03 09:49:24 +02:00
feat(scrcpy): support server version 2.1
This commit is contained in:
parent
ef583779fa
commit
419a7559fe
16 changed files with 243 additions and 94 deletions
|
@ -3,7 +3,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "fetch-scrcpy-server 2.0 && node scripts/manifest.mjs",
|
"postinstall": "fetch-scrcpy-server 2.1 && node scripts/manifest.mjs",
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
|
|
|
@ -320,7 +320,7 @@ export class ScrcpyPageState {
|
||||||
|
|
||||||
RECORD_STATE.recorder = new MatroskaMuxingRecorder();
|
RECORD_STATE.recorder = new MatroskaMuxingRecorder();
|
||||||
|
|
||||||
client.videoStream.then(({ stream, metadata }) => {
|
client.videoStream!.then(({ stream, metadata }) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
RECORD_STATE.recorder.videoMetadata = metadata;
|
RECORD_STATE.recorder.videoMetadata = metadata;
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,6 +23,7 @@ Similar to `@yume-chan/scrcpy`, this package supports multiple Scrcpy versions,
|
||||||
| 1.16~1.21 | `AdbScrcpyOptions1_16` |
|
| 1.16~1.21 | `AdbScrcpyOptions1_16` |
|
||||||
| 1.22~1.25 | `AdbScrcpyOptions1_22` |
|
| 1.22~1.25 | `AdbScrcpyOptions1_22` |
|
||||||
| 2.0 | `AdbScrcpyOptions2_0` |
|
| 2.0 | `AdbScrcpyOptions2_0` |
|
||||||
|
| 2.1 | `AdbScrcpyOptions2_1` |
|
||||||
|
|
||||||
### Push server binary
|
### Push server binary
|
||||||
|
|
||||||
|
@ -73,9 +74,9 @@ To start the server, use the `AdbScrcpyClient.start()` method. It automatically
|
||||||
```js
|
```js
|
||||||
import {
|
import {
|
||||||
AdbScrcpyClient,
|
AdbScrcpyClient,
|
||||||
AdbScrcpyOptions2_0,
|
AdbScrcpyOptions2_1,
|
||||||
DEFAULT_SERVER_PATH,
|
DEFAULT_SERVER_PATH,
|
||||||
ScrcpyOptions2_0,
|
ScrcpyOptions2_1,
|
||||||
} from "@yume-chan/scrcpy";
|
} from "@yume-chan/scrcpy";
|
||||||
import SCRCPY_SERVER_VERSION from "@yume-chan/scrcpy/bin/version.js";
|
import SCRCPY_SERVER_VERSION from "@yume-chan/scrcpy/bin/version.js";
|
||||||
|
|
||||||
|
@ -84,18 +85,40 @@ const client: AdbScrcpyClient = await AdbScrcpyClient.start(
|
||||||
DEFAULT_SERVER_PATH,
|
DEFAULT_SERVER_PATH,
|
||||||
// If server binary was downloaded manually, must provide the correct version
|
// If server binary was downloaded manually, must provide the correct version
|
||||||
SCRCPY_SERVER_VERSION,
|
SCRCPY_SERVER_VERSION,
|
||||||
new AdbScrcpyOptions2_0(
|
new AdbScrcpyOptions2_1(
|
||||||
ScrcpyOptions2_0({
|
ScrcpyOptions2_1({
|
||||||
// options
|
// options
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const stdout: ReadableStream<string> = client.stdout;
|
const stdout: ReadableStream<string> = client.stdout;
|
||||||
const { metadata: videoMetadata, stream: videoPacketStream } =
|
|
||||||
|
// `undefined` if `video: false` option was given
|
||||||
|
if (client.videoSteam) {
|
||||||
|
const { metadata: videoMetadata, stream: videoPacketStream } =
|
||||||
await client.videoStream;
|
await client.videoStream;
|
||||||
const { metadata: audioMetadata, stream: audioPacketStream } =
|
}
|
||||||
await client.audioStream;
|
|
||||||
|
// `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 controlMessageWriter: ScrcpyControlMessageWriter | undefined =
|
const controlMessageWriter: ScrcpyControlMessageWriter | undefined =
|
||||||
client.controlMessageWriter;
|
client.controlMessageWriter;
|
||||||
const deviceMessageStream: ReadableStream<ScrcpyDeviceMessage> | undefined =
|
const deviceMessageStream: ReadableStream<ScrcpyDeviceMessage> | undefined =
|
||||||
|
@ -110,7 +133,6 @@ client.close();
|
||||||
In Web Streams API, pipes will block its upstream when downstream's queue is full (back-pressure mechanism). If multiple streams are separated from the same source (for example, all Scrcpy streams are from the same USB or TCP connection), blocking one stream means blocking all of them, so it's important to always read from all streams, even if you don't care about their data.
|
In Web Streams API, pipes will block its upstream when downstream's queue is full (back-pressure mechanism). If multiple streams are separated from the same source (for example, all Scrcpy streams are from the same USB or TCP connection), blocking one stream means blocking all of them, so it's important to always read from all streams, even if you don't care about their data.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// when using `AdbScrcpyClient`
|
|
||||||
stdout
|
stdout
|
||||||
.pipeTo(
|
.pipeTo(
|
||||||
new WritableStream<string>({
|
new WritableStream<string>({
|
||||||
|
@ -126,7 +148,7 @@ stdout
|
||||||
|
|
||||||
videoPacketStream
|
videoPacketStream
|
||||||
.pipeTo(
|
.pipeTo(
|
||||||
new WritableStream<ScrcpyVideoStreamPacket>({
|
new WritableStream<ScrcpyMediaStreamPacket>({
|
||||||
write: (packet) => {
|
write: (packet) => {
|
||||||
// Handle or ignore the video packet
|
// Handle or ignore the video packet
|
||||||
},
|
},
|
||||||
|
@ -134,6 +156,16 @@ videoPacketStream
|
||||||
)
|
)
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
|
||||||
|
audioPacketStream
|
||||||
|
.pipeTo(
|
||||||
|
new WritableStream<ScrcpyMediaStreamPacket>({
|
||||||
|
write: (packet) => {
|
||||||
|
// Handle or ignore the audio packet
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch(() => {});
|
||||||
|
|
||||||
deviceMessageStream
|
deviceMessageStream
|
||||||
.pipeTo(
|
.pipeTo(
|
||||||
new WritableStream<ScrcpyDeviceMessage>({
|
new WritableStream<ScrcpyDeviceMessage>({
|
||||||
|
|
|
@ -75,7 +75,7 @@ interface AdbScrcpyClientInit {
|
||||||
process: AdbSubprocessProtocol;
|
process: AdbSubprocessProtocol;
|
||||||
stdout: ReadableStream<string>;
|
stdout: ReadableStream<string>;
|
||||||
|
|
||||||
videoStream: ReadableStream<Uint8Array>;
|
videoStream: ReadableStream<Uint8Array> | undefined;
|
||||||
audioStream: ReadableStream<Uint8Array> | undefined;
|
audioStream: ReadableStream<Uint8Array> | undefined;
|
||||||
controlStream:
|
controlStream:
|
||||||
| ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>
|
| ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>
|
||||||
|
@ -259,7 +259,7 @@ export class AdbScrcpyClient {
|
||||||
return this._screenHeight;
|
return this._screenHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _videoStream: Promise<AdbScrcpyVideoStream>;
|
private _videoStream: Promise<AdbScrcpyVideoStream> | undefined;
|
||||||
public get videoStream() {
|
public get videoStream() {
|
||||||
return this._videoStream;
|
return this._videoStream;
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,9 @@ export class AdbScrcpyClient {
|
||||||
this._process = process;
|
this._process = process;
|
||||||
this._stdout = stdout;
|
this._stdout = stdout;
|
||||||
|
|
||||||
this._videoStream = this.createVideoStream(videoStream);
|
this._videoStream = videoStream
|
||||||
|
? this.createVideoStream(videoStream)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
this._audioStream = audioStream
|
this._audioStream = audioStream
|
||||||
? this.createAudioStream(audioStream)
|
? this.createAudioStream(audioStream)
|
||||||
|
|
|
@ -17,6 +17,10 @@ import type { ValueOrPromise } from "@yume-chan/struct";
|
||||||
export interface AdbScrcpyConnectionOptions {
|
export interface AdbScrcpyConnectionOptions {
|
||||||
scid: number;
|
scid: number;
|
||||||
|
|
||||||
|
video: boolean;
|
||||||
|
|
||||||
|
audio: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to create a control stream
|
* Whether to create a control stream
|
||||||
*/
|
*/
|
||||||
|
@ -26,18 +30,14 @@ export interface AdbScrcpyConnectionOptions {
|
||||||
* In forward tunnel mode, read a byte from video socket on start to detect connection issues
|
* In forward tunnel mode, read a byte from video socket on start to detect connection issues
|
||||||
*/
|
*/
|
||||||
sendDummyByte: boolean;
|
sendDummyByte: boolean;
|
||||||
|
|
||||||
audio: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SCRCPY_SOCKET_NAME_PREFIX = "scrcpy";
|
export const SCRCPY_SOCKET_NAME_PREFIX = "scrcpy";
|
||||||
|
|
||||||
export interface AdbScrcpyConnectionStreams {
|
export interface AdbScrcpyConnectionStreams {
|
||||||
video: ReadableStream<Uint8Array>;
|
video?: ReadableStream<Uint8Array>;
|
||||||
audio: ReadableStream<Uint8Array> | undefined;
|
audio?: ReadableStream<Uint8Array>;
|
||||||
control:
|
control?: ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>;
|
||||||
| ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>
|
|
||||||
| undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AdbScrcpyConnection implements Disposable {
|
export abstract class AdbScrcpyConnection implements Disposable {
|
||||||
|
@ -81,12 +81,25 @@ export class AdbScrcpyForwardConnection extends AdbScrcpyConnection {
|
||||||
return this.adb.createSocket(this.socketName);
|
return this.adb.createSocket(this.socketName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async connectAndRetry(): Promise<
|
private async connectAndRetry(
|
||||||
ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>
|
sendDummyByte: boolean
|
||||||
> {
|
): Promise<ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>> {
|
||||||
for (let i = 0; !this._disposed && i < 100; i += 1) {
|
for (let i = 0; !this._disposed && i < 100; i += 1) {
|
||||||
try {
|
try {
|
||||||
return await this.connect();
|
const stream = await this.connect();
|
||||||
|
if (sendDummyByte) {
|
||||||
|
// Can't guarantee the stream will preserve message boundaries,
|
||||||
|
// so buffer the stream
|
||||||
|
const buffered = new BufferedReadableStream(
|
||||||
|
stream.readable
|
||||||
|
);
|
||||||
|
await buffered.readExactly(1);
|
||||||
|
return {
|
||||||
|
readable: buffered.release(),
|
||||||
|
writable: stream.writable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Maybe the server is still starting
|
// Maybe the server is still starting
|
||||||
await delay(100);
|
await delay(100);
|
||||||
|
@ -95,30 +108,30 @@ export class AdbScrcpyForwardConnection extends AdbScrcpyConnection {
|
||||||
throw new Error(`Can't connect to server after 100 retries`);
|
throw new Error(`Can't connect to server after 100 retries`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async connectVideoStream(): Promise<ReadableStream<Uint8Array>> {
|
|
||||||
const { readable: stream } = await this.connectAndRetry();
|
|
||||||
if (this.options.sendDummyByte) {
|
|
||||||
// Can't guarantee the stream will preserve message boundaries,
|
|
||||||
// so buffer the stream
|
|
||||||
const buffered = new BufferedReadableStream(stream);
|
|
||||||
await buffered.readExactly(1);
|
|
||||||
return buffered.release();
|
|
||||||
}
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async getStreams(): Promise<AdbScrcpyConnectionStreams> {
|
public override async getStreams(): Promise<AdbScrcpyConnectionStreams> {
|
||||||
const video = await this.connectVideoStream();
|
let { sendDummyByte } = this.options;
|
||||||
|
|
||||||
const audio = this.options.audio
|
const streams: AdbScrcpyConnectionStreams = {};
|
||||||
? (await this.connectAndRetry()).readable
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const control = this.options.control
|
if (this.options.video) {
|
||||||
? await this.connectAndRetry()
|
const video = await this.connectAndRetry(sendDummyByte);
|
||||||
: undefined;
|
streams.video = video.readable;
|
||||||
|
sendDummyByte = false;
|
||||||
|
}
|
||||||
|
|
||||||
return { video, audio, control };
|
if (this.options.audio) {
|
||||||
|
const audio = await this.connectAndRetry(sendDummyByte);
|
||||||
|
streams.audio = audio.readable;
|
||||||
|
sendDummyByte = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.control) {
|
||||||
|
const control = await this.connectAndRetry(sendDummyByte);
|
||||||
|
sendDummyByte = false;
|
||||||
|
streams.control = control;
|
||||||
|
}
|
||||||
|
|
||||||
|
return streams;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override dispose(): void {
|
public override dispose(): void {
|
||||||
|
@ -161,15 +174,24 @@ export class AdbScrcpyReverseConnection extends AdbScrcpyConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getStreams(): Promise<AdbScrcpyConnectionStreams> {
|
public async getStreams(): Promise<AdbScrcpyConnectionStreams> {
|
||||||
const { readable: video } = await this.accept();
|
const streams: AdbScrcpyConnectionStreams = {};
|
||||||
|
|
||||||
const audio = this.options.audio
|
if (this.options.video) {
|
||||||
? (await this.accept()).readable
|
const video = await this.accept();
|
||||||
: undefined;
|
streams.video = video.readable;
|
||||||
|
}
|
||||||
|
|
||||||
const control = this.options.control ? await this.accept() : undefined;
|
if (this.options.audio) {
|
||||||
|
const audio = await this.accept();
|
||||||
|
streams.audio = audio.readable;
|
||||||
|
}
|
||||||
|
|
||||||
return { video, audio, control };
|
if (this.options.control) {
|
||||||
|
const control = await this.accept();
|
||||||
|
streams.control = control;
|
||||||
|
}
|
||||||
|
|
||||||
|
return streams;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override dispose() {
|
public override dispose() {
|
||||||
|
|
|
@ -107,11 +107,12 @@ export class AdbScrcpyOptions1_16 extends AdbScrcpyOptionsBase<ScrcpyOptionsInit
|
||||||
adb,
|
adb,
|
||||||
{
|
{
|
||||||
scid: -1,
|
scid: -1,
|
||||||
|
video: true,
|
||||||
|
audio: false,
|
||||||
// Old versions always have control stream no matter what the option is
|
// Old versions always have control stream no matter what the option is
|
||||||
// Pass `control: false` to `Connection` will disable the control stream
|
// Pass `control: false` to `Connection` will disable the control stream
|
||||||
control: true,
|
control: true,
|
||||||
sendDummyByte: true,
|
sendDummyByte: true,
|
||||||
audio: false,
|
|
||||||
},
|
},
|
||||||
this.tunnelForwardOverride || this.value.tunnelForward
|
this.tunnelForwardOverride || this.value.tunnelForward
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,9 +32,10 @@ export class AdbScrcpyOptions1_22 extends AdbScrcpyOptionsBase<ScrcpyOptionsInit
|
||||||
adb,
|
adb,
|
||||||
{
|
{
|
||||||
scid: -1,
|
scid: -1,
|
||||||
|
video: true,
|
||||||
|
audio: false,
|
||||||
control: this.value.control,
|
control: this.value.control,
|
||||||
sendDummyByte: this.value.sendDummyByte,
|
sendDummyByte: this.value.sendDummyByte,
|
||||||
audio: false,
|
|
||||||
},
|
},
|
||||||
this.tunnelForwardOverride || this.value.tunnelForward
|
this.tunnelForwardOverride || this.value.tunnelForward
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,27 +9,29 @@ import { AdbScrcpyClient, AdbScrcpyExitedError } from "../client.js";
|
||||||
import type { AdbScrcpyConnection } from "../connection.js";
|
import type { AdbScrcpyConnection } from "../connection.js";
|
||||||
|
|
||||||
import { AdbScrcpyOptions1_16 } from "./1_16.js";
|
import { AdbScrcpyOptions1_16 } from "./1_16.js";
|
||||||
|
import type { AdbScrcpyOptions } from "./types.js";
|
||||||
import { AdbScrcpyOptionsBase } from "./types.js";
|
import { AdbScrcpyOptionsBase } from "./types.js";
|
||||||
|
|
||||||
export class AdbScrcpyOptions2_0 extends AdbScrcpyOptionsBase<ScrcpyOptionsInit2_0> {
|
export class AdbScrcpyOptions2_0 extends AdbScrcpyOptionsBase<ScrcpyOptionsInit2_0> {
|
||||||
public override async getEncoders(
|
public static async getEncoders(
|
||||||
adb: Adb,
|
adb: Adb,
|
||||||
path: string,
|
path: string,
|
||||||
version: string
|
version: string,
|
||||||
): Promise<ScrcpyEncoder[]> {
|
options: AdbScrcpyOptions<object>
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const client = await AdbScrcpyClient.start(
|
const client = await AdbScrcpyClient.start(
|
||||||
adb,
|
adb,
|
||||||
path,
|
path,
|
||||||
version,
|
version,
|
||||||
this
|
options
|
||||||
);
|
);
|
||||||
await client.close();
|
await client.close();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof AdbScrcpyExitedError) {
|
if (e instanceof AdbScrcpyExitedError) {
|
||||||
const encoders: ScrcpyEncoder[] = [];
|
const encoders: ScrcpyEncoder[] = [];
|
||||||
for (const line of e.output) {
|
for (const line of e.output) {
|
||||||
const encoder = this.parseEncoder(line);
|
const encoder = options.parseEncoder(line);
|
||||||
if (encoder) {
|
if (encoder) {
|
||||||
encoders.push(encoder);
|
encoders.push(encoder);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +42,14 @@ export class AdbScrcpyOptions2_0 extends AdbScrcpyOptionsBase<ScrcpyOptionsInit2
|
||||||
throw new Error("Unexpected error");
|
throw new Error("Unexpected error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async getEncoders(
|
||||||
|
adb: Adb,
|
||||||
|
path: string,
|
||||||
|
version: string
|
||||||
|
): Promise<ScrcpyEncoder[]> {
|
||||||
|
return AdbScrcpyOptions2_0.getEncoders(adb, path, version, this);
|
||||||
|
}
|
||||||
|
|
||||||
public override getDisplays(
|
public override getDisplays(
|
||||||
adb: Adb,
|
adb: Adb,
|
||||||
path: string,
|
path: string,
|
||||||
|
@ -53,9 +63,10 @@ export class AdbScrcpyOptions2_0 extends AdbScrcpyOptionsBase<ScrcpyOptionsInit2
|
||||||
adb,
|
adb,
|
||||||
{
|
{
|
||||||
scid: this.value.scid.value,
|
scid: this.value.scid.value,
|
||||||
|
video: true,
|
||||||
|
audio: this.value.audio,
|
||||||
control: this.value.control,
|
control: this.value.control,
|
||||||
sendDummyByte: this.value.sendDummyByte,
|
sendDummyByte: this.value.sendDummyByte,
|
||||||
audio: this.value.audio,
|
|
||||||
},
|
},
|
||||||
this.tunnelForwardOverride || this.value.tunnelForward
|
this.tunnelForwardOverride || this.value.tunnelForward
|
||||||
);
|
);
|
||||||
|
|
44
libraries/adb-scrcpy/src/options/2_1.ts
Normal file
44
libraries/adb-scrcpy/src/options/2_1.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import type { Adb } from "@yume-chan/adb";
|
||||||
|
import type {
|
||||||
|
ScrcpyDisplay,
|
||||||
|
ScrcpyEncoder,
|
||||||
|
ScrcpyOptionsInit2_1,
|
||||||
|
} from "@yume-chan/scrcpy";
|
||||||
|
|
||||||
|
import type { AdbScrcpyConnection } from "../connection.js";
|
||||||
|
|
||||||
|
import { AdbScrcpyOptions1_16 } from "./1_16.js";
|
||||||
|
import { AdbScrcpyOptions2_0 } from "./2_0.js";
|
||||||
|
import { AdbScrcpyOptionsBase } from "./types.js";
|
||||||
|
|
||||||
|
export class AdbScrcpyOptions2_1 extends AdbScrcpyOptionsBase<ScrcpyOptionsInit2_1> {
|
||||||
|
public override async getEncoders(
|
||||||
|
adb: Adb,
|
||||||
|
path: string,
|
||||||
|
version: string
|
||||||
|
): Promise<ScrcpyEncoder[]> {
|
||||||
|
return AdbScrcpyOptions2_0.getEncoders(adb, path, version, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override getDisplays(
|
||||||
|
adb: Adb,
|
||||||
|
path: string,
|
||||||
|
version: string
|
||||||
|
): Promise<ScrcpyDisplay[]> {
|
||||||
|
return AdbScrcpyOptions1_16.getDisplays(adb, path, version, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override createConnection(adb: Adb): AdbScrcpyConnection {
|
||||||
|
return AdbScrcpyOptions1_16.createConnection(
|
||||||
|
adb,
|
||||||
|
{
|
||||||
|
scid: this.value.scid.value,
|
||||||
|
video: this.value.video,
|
||||||
|
audio: this.value.audio,
|
||||||
|
control: this.value.control,
|
||||||
|
sendDummyByte: this.value.sendDummyByte,
|
||||||
|
},
|
||||||
|
this.tunnelForwardOverride || this.value.tunnelForward
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,20 @@
|
||||||
{
|
{
|
||||||
"extends": "./node_modules/@yume-chan/tsconfig/tsconfig.base.json"
|
"extends": "./node_modules/@yume-chan/tsconfig/tsconfig.base.json",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "../adb/tsconfig.build.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../event/tsconfig.build.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../scrcpy/tsconfig.build.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../stream-extra/tsconfig.build.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../struct/tsconfig.build.json"
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,5 @@
|
||||||
{
|
{
|
||||||
"references": [
|
"references": [
|
||||||
{
|
|
||||||
"path": "../adb/tsconfig.build.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../event/tsconfig.build.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../scrcpy/tsconfig.build.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../stream-extra/tsconfig.build.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../struct/tsconfig.build.json"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.test.json"
|
"path": "./tsconfig.test.json"
|
||||||
},
|
},
|
||||||
|
|
|
@ -67,7 +67,7 @@ npx fetch-scrcpy-server <version>
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
npx fetch-scrcpy-server 1.25
|
npx fetch-scrcpy-server 2.1
|
||||||
```
|
```
|
||||||
|
|
||||||
Add it to `scripts.postinstall` in your `package.json`, so running `npm install` automatically invokes the script:
|
Add it to `scripts.postinstall` in your `package.json`, so running `npm install` automatically invokes the script:
|
||||||
|
@ -75,7 +75,7 @@ Add it to `scripts.postinstall` in your `package.json`, so running `npm install`
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "fetch-scrcpy-server 1.25"
|
"postinstall": "fetch-scrcpy-server 2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -87,7 +87,7 @@ It will also save the version number to `bin/version.js`.
|
||||||
```js
|
```js
|
||||||
import SCRCPY_SERVER_VERSION from "@yume-chan/scrcpy/bin/version.js";
|
import SCRCPY_SERVER_VERSION from "@yume-chan/scrcpy/bin/version.js";
|
||||||
|
|
||||||
console.log(SCRCPY_SERVER_VERSION); // "1.25"
|
console.log(SCRCPY_SERVER_VERSION); // "2.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Use the server binary
|
### Use the server binary
|
||||||
|
@ -171,6 +171,7 @@ The latest one may continue to work for future server versions, but there is no
|
||||||
| 1.24 | `ScrcpyOptions1_24` |
|
| 1.24 | `ScrcpyOptions1_24` |
|
||||||
| 1.25 | `ScrcpyOptions1_25` |
|
| 1.25 | `ScrcpyOptions1_25` |
|
||||||
| 2.0 | `ScrcpyOptions2_0` |
|
| 2.0 | `ScrcpyOptions2_0` |
|
||||||
|
| 2.1 | `ScrcpyOptions2_1` |
|
||||||
|
|
||||||
## Reading and writing packets
|
## Reading and writing packets
|
||||||
|
|
||||||
|
@ -183,9 +184,9 @@ This packets operates on Web Streams API streams.
|
||||||
Requires a `ReadableStream<Uint8Array>` that reads from the video socket.
|
Requires a `ReadableStream<Uint8Array>` that reads from the video socket.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { ScrcpyOptions1_25, ScrcpyVideoStreamPacket } from "@yume-chan/scrcpy";
|
import { ScrcpyOptions2_1, ScrcpyVideoStreamPacket } from "@yume-chan/scrcpy";
|
||||||
|
|
||||||
const options = new ScrcpyOptions1_25({
|
const options = new ScrcpyOptions2_1({
|
||||||
// use the same version and options when starting the server
|
// use the same version and options when starting the server
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -212,10 +213,10 @@ Control socket is optional if control is not enabled. Video socket and control s
|
||||||
```ts
|
```ts
|
||||||
import {
|
import {
|
||||||
ScrcpyControlMessageWriter,
|
ScrcpyControlMessageWriter,
|
||||||
ScrcpyOptions1_25,
|
ScrcpyOptions2_1,
|
||||||
} from "@yume-chan/scrcpy";
|
} from "@yume-chan/scrcpy";
|
||||||
|
|
||||||
const options = new ScrcpyOptions1_25({
|
const options = new ScrcpyOptions2_1({
|
||||||
// use the same version and options when starting the server
|
// use the same version and options when starting the server
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -235,10 +236,7 @@ controlMessageWriter.injectText("Hello World!");
|
||||||
Requires a `ReadableStream<Uint8Array>` that reads from the control socket.
|
Requires a `ReadableStream<Uint8Array>` that reads from the control socket.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import {
|
import { ScrcpyDeviceMessageDeserializeStream } from "@yume-chan/scrcpy";
|
||||||
ScrcpyDeviceMessageDeserializeStream,
|
|
||||||
ScrcpyOptions1_24,
|
|
||||||
} from "@yume-chan/scrcpy";
|
|
||||||
|
|
||||||
const controlStream: ReadableWritablePair<Uint8Array, Uint8Array>; // get the stream yourself
|
const controlStream: ReadableWritablePair<Uint8Array, Uint8Array>; // get the stream yourself
|
||||||
|
|
||||||
|
@ -253,7 +251,7 @@ const deviceMessageStream: ReadableStream<ScrcpyDeviceMessage> =
|
||||||
In Web Streams API, pipes will block its upstream when downstream's queue is full (back-pressure mechanism). If multiple streams are separated from the same source (for example, all Scrcpy streams are from the same USB or TCP connection), blocking one stream means blocking all of them, so it's important to always read from all streams, even if you don't care about their data.
|
In Web Streams API, pipes will block its upstream when downstream's queue is full (back-pressure mechanism). If multiple streams are separated from the same source (for example, all Scrcpy streams are from the same USB or TCP connection), blocking one stream means blocking all of them, so it's important to always read from all streams, even if you don't care about their data.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// when using `AdbScrcpyClient`
|
// if using `AdbScrcpyClient`
|
||||||
stdout
|
stdout
|
||||||
.pipeTo(
|
.pipeTo(
|
||||||
new WritableStream<string>({
|
new WritableStream<string>({
|
||||||
|
|
|
@ -115,7 +115,7 @@ export class ScrcpyOptions1_18 extends ScrcpyOptionsBase<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public serialize(): string[] {
|
public override serialize(): string[] {
|
||||||
return ScrcpyOptions1_16.serialize(
|
return ScrcpyOptions1_16.serialize(
|
||||||
this.value,
|
this.value,
|
||||||
ScrcpyOptions1_18.SERIALIZE_ORDER
|
ScrcpyOptions1_18.SERIALIZE_ORDER
|
||||||
|
|
35
libraries/scrcpy/src/options/2_1.ts
Normal file
35
libraries/scrcpy/src/options/2_1.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { ScrcpyOptions1_21 } from "./1_21.js";
|
||||||
|
import type { ScrcpyOptionsInit2_0 } from "./2_0.js";
|
||||||
|
import { ScrcpyOptions2_0 } from "./2_0.js";
|
||||||
|
import { ScrcpyOptionsBase } from "./types.js";
|
||||||
|
|
||||||
|
export interface ScrcpyOptionsInit2_1 extends ScrcpyOptionsInit2_0 {
|
||||||
|
video?: boolean;
|
||||||
|
audioSource?: "output" | "mic";
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ScrcpyOptions2_1 extends ScrcpyOptionsBase<
|
||||||
|
ScrcpyOptionsInit2_1,
|
||||||
|
ScrcpyOptions2_0
|
||||||
|
> {
|
||||||
|
public static readonly DEFAULTS = {
|
||||||
|
...ScrcpyOptions2_0.DEFAULTS,
|
||||||
|
video: true,
|
||||||
|
audioSource: "output",
|
||||||
|
} as const satisfies Required<ScrcpyOptionsInit2_1>;
|
||||||
|
|
||||||
|
public override get defaults(): Required<ScrcpyOptionsInit2_1> {
|
||||||
|
return ScrcpyOptions2_1.DEFAULTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(init: ScrcpyOptionsInit2_1) {
|
||||||
|
super(new ScrcpyOptions2_0(init), {
|
||||||
|
...ScrcpyOptions2_1.DEFAULTS,
|
||||||
|
...init,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override serialize(): string[] {
|
||||||
|
return ScrcpyOptions1_21.serialize(this.value, this.defaults);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ export * from "./1_23.js";
|
||||||
export * from "./1_24.js";
|
export * from "./1_24.js";
|
||||||
export * from "./1_25/index.js";
|
export * from "./1_25/index.js";
|
||||||
export * from "./2_0.js";
|
export * from "./2_0.js";
|
||||||
|
export * from "./2_1.js";
|
||||||
export * from "./codec.js";
|
export * from "./codec.js";
|
||||||
export * from "./latest.js";
|
export * from "./latest.js";
|
||||||
export * from "./types.js";
|
export * from "./types.js";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ScrcpyLogLevel1_18, ScrcpyVideoOrientation1_18 } from "./1_18.js";
|
import { ScrcpyLogLevel1_18, ScrcpyVideoOrientation1_18 } from "./1_18.js";
|
||||||
import type { ScrcpyOptionsInit2_0 } from "./2_0.js";
|
import type { ScrcpyOptionsInit2_1 } from "./2_1.js";
|
||||||
import { ScrcpyOptions2_0 } from "./2_0.js";
|
import { ScrcpyOptions2_1 } from "./2_1.js";
|
||||||
|
|
||||||
export const ScrcpyLogLevel = ScrcpyLogLevel1_18;
|
export const ScrcpyLogLevel = ScrcpyLogLevel1_18;
|
||||||
export type ScrcpyLogLevel = ScrcpyLogLevel1_18;
|
export type ScrcpyLogLevel = ScrcpyLogLevel1_18;
|
||||||
|
@ -8,7 +8,7 @@ export type ScrcpyLogLevel = ScrcpyLogLevel1_18;
|
||||||
export const ScrcpyVideoOrientation = ScrcpyVideoOrientation1_18;
|
export const ScrcpyVideoOrientation = ScrcpyVideoOrientation1_18;
|
||||||
export type ScrcpyVideoOrientation = ScrcpyVideoOrientation1_18;
|
export type ScrcpyVideoOrientation = ScrcpyVideoOrientation1_18;
|
||||||
|
|
||||||
export type ScrcpyOptionsInitLatest = ScrcpyOptionsInit2_0;
|
export type ScrcpyOptionsInitLatest = ScrcpyOptionsInit2_1;
|
||||||
export class ScrcpyOptionsLatest extends ScrcpyOptions2_0 {}
|
export class ScrcpyOptionsLatest extends ScrcpyOptions2_1 {}
|
||||||
|
|
||||||
export const ScrcpyLatestVersion = "2.0";
|
export const ScrcpyLatestVersion = "2.1";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue