Skip to content

Commit

Permalink
feat(scrcpy): add support for Scrcpy 3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
yume-chan committed Dec 23, 2024
1 parent cb44b63 commit 8c5b3c2
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/quiet-ghosts-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@yume-chan/scrcpy": minor
---

Add support for Scrcpy 3.1
8 changes: 8 additions & 0 deletions libraries/scrcpy/src/3_1/impl/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";

export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
vdDestroyContent: false,
}) as const satisfies Required<Init>)();
7 changes: 7 additions & 0 deletions libraries/scrcpy/src/3_1/impl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from "../../3_0/impl/index.js";
export { Defaults } from "./defaults.js";
export type { Init } from "./init.js";
export {
serializeUHidCreateControlMessage,
UHidCreateControlMessage,
} from "./serialize-uhid-create.js";
5 changes: 5 additions & 0 deletions libraries/scrcpy/src/3_1/impl/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { PrevImpl } from "./prev.js";

export interface Init extends PrevImpl.Init {
vdDestroyContent?: boolean;
}
1 change: 1 addition & 0 deletions libraries/scrcpy/src/3_1/impl/prev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as PrevImpl from "../../3_0/impl/index.js";
28 changes: 28 additions & 0 deletions libraries/scrcpy/src/3_1/impl/serialize-uhid-create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { StructInit } from "@yume-chan/struct";
import { buffer, string, struct, u16, u8 } from "@yume-chan/struct";

import { ScrcpyControlMessageType } from "../../base/control-message-type.js";
import type { ScrcpyUHidCreateControlMessage } from "../../latest.js";

export const UHidCreateControlMessage = /* #__PURE__ */ (() =>
struct(
{
type: u8(ScrcpyControlMessageType.UHidCreate),
id: u16,
vendorId: u16,
productId: u16,
name: string(u8),
data: buffer(u16),
},
{ littleEndian: false },
))();

export type UHidCreateControlMessage = StructInit<
typeof UHidCreateControlMessage
>;

export function serializeUHidCreateControlMessage(
message: ScrcpyUHidCreateControlMessage,
) {
return UHidCreateControlMessage.serialize(message);
}
2 changes: 2 additions & 0 deletions libraries/scrcpy/src/3_1/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as ScrcpyOptions3_1Impl from "./impl/index.js";
export * from "./options.js";
190 changes: 190 additions & 0 deletions libraries/scrcpy/src/3_1/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type { AsyncExactReadable } from "@yume-chan/struct";

import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
ScrcpyOptions,
ScrcpyScrollController,
ScrcpyVideoStream,
} from "../base/index.js";
import type {
ScrcpyBackOrScreenOnControlMessage,
ScrcpyInjectTouchControlMessage,
ScrcpySetClipboardControlMessage,
ScrcpyUHidCreateControlMessage,
ScrcpyUHidOutputDeviceMessage,
} from "../latest.js";

import type { Init } from "./impl/index.js";
import {
AckClipboardHandler,
ClipboardStream,
ControlMessageTypes,
createMediaStreamTransformer,
createScrollController,
Defaults,
parseAudioStreamMetadata,
parseDisplay,
parseEncoder,
parseVideoStreamMetadata,
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeUHidCreateControlMessage,
setListDisplays,
setListEncoders,
UHidOutputStream,
} from "./impl/index.js";

export class ScrcpyOptions3_1 implements ScrcpyOptions<Init> {
static readonly Defaults = Defaults;

readonly version: string;

readonly value: Required<Init>;

get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
return ControlMessageTypes;
}

#clipboard: ClipboardStream | undefined;
get clipboard(): ReadableStream<string> | undefined {
return this.#clipboard;
}

#ackClipboardHandler: AckClipboardHandler | undefined;

#uHidOutput: UHidOutputStream | undefined;
get uHidOutput():
| ReadableStream<ScrcpyUHidOutputDeviceMessage>
| undefined {
return this.#uHidOutput;
}

constructor(init: Init, version = "3.1") {
this.value = { ...Defaults, ...init };
this.version = version;

if (this.value.videoSource === "camera") {
this.value.control = false;
}

if (this.value.audioDup) {
this.value.audioSource = "playback";
}

if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = new ClipboardStream();
this.#ackClipboardHandler = new AckClipboardHandler();
}

this.#uHidOutput = new UHidOutputStream();
}
}

serialize(): string[] {
return serialize(this.value, Defaults);
}

setListDisplays(): void {
setListDisplays(this.value);
}

parseDisplay(line: string): ScrcpyDisplay | undefined {
return parseDisplay(line);
}

setListEncoders() {
setListEncoders(this.value);
}

parseEncoder(line: string): ScrcpyEncoder | undefined {
return parseEncoder(line);
}

parseVideoStreamMetadata(
stream: ReadableStream<Uint8Array>,
): MaybePromiseLike<ScrcpyVideoStream> {
return parseVideoStreamMetadata(this.value, stream);
}

parseAudioStreamMetadata(
stream: ReadableStream<Uint8Array>,
): MaybePromiseLike<ScrcpyAudioStreamMetadata> {
return parseAudioStreamMetadata(stream, this.value);
}

async parseDeviceMessage(
id: number,
stream: AsyncExactReadable,
): Promise<void> {
if (await this.#clipboard?.parse(id, stream)) {
return;
}

if (await this.#ackClipboardHandler?.parse(id, stream)) {
return;
}

throw new Error("Unknown device message");
}

endDeviceMessageStream(e?: unknown): void {
if (e) {
this.#clipboard?.error(e);
this.#ackClipboardHandler?.error(e);
} else {
this.#clipboard?.close();
this.#ackClipboardHandler?.close();
}
}

createMediaStreamTransformer(): TransformStream<
Uint8Array,
ScrcpyMediaStreamPacket
> {
return createMediaStreamTransformer(this.value);
}

serializeInjectTouchControlMessage(
message: ScrcpyInjectTouchControlMessage,
): Uint8Array {
return serializeInjectTouchControlMessage(message);
}

serializeBackOrScreenOnControlMessage(
message: ScrcpyBackOrScreenOnControlMessage,
): Uint8Array | undefined {
return serializeBackOrScreenOnControlMessage(message);
}

serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
message,
);
}

createScrollController(): ScrcpyScrollController {
return createScrollController();
}

serializeUHidCreateControlMessage(
message: ScrcpyUHidCreateControlMessage,
): Uint8Array {
return serializeUHidCreateControlMessage(message);
}
}

type Init_ = Init;

export namespace ScrcpyOptions3_1 {
export type Init = Init_;
}
10 changes: 5 additions & 5 deletions libraries/scrcpy/src/latest.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ScrcpyOptions3_0 } from "./3_0/options.js";
import { ScrcpyOptions3_1 } from "./3_1/options.js";

export class ScrcpyOptionsLatest extends ScrcpyOptions3_0 {
constructor(init: ScrcpyOptions3_0.Init, version: string) {
export class ScrcpyOptionsLatest extends ScrcpyOptions3_1 {
constructor(init: ScrcpyOptions3_1.Init, version: string) {
super(init, version);
}
}

export { ScrcpyOptions3_0Impl as ScrcpyOptionsLatestImpl } from "./3_0/index.js";
export { ScrcpyOptions3_1Impl as ScrcpyOptionsLatestImpl } from "./3_1/index.js";

export {
BackOrScreenOnControlMessage as ScrcpyBackOrScreenOnControlMessage,
Expand All @@ -23,4 +23,4 @@ export {
SetClipboardControlMessage as ScrcpySetClipboardControlMessage,
UHidCreateControlMessage as ScrcpyUHidCreateControlMessage,
UHidOutputDeviceMessage as ScrcpyUHidOutputDeviceMessage,
} from "./3_0/impl/index.js";
} from "./3_1/impl/index.js";

0 comments on commit 8c5b3c2

Please sign in to comment.