mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-02 17:29:17 +02:00
feat(adb-scrcpy): infer type of videoStream
from video
option
This commit is contained in:
parent
02f5bd5929
commit
24b65fd2c1
61 changed files with 653 additions and 336 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -115,7 +115,7 @@
|
|||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"explorer.sortOrder": "mixed",
|
||||
"prettier.prettierPath": "./node_modules/prettier/index.cjs",
|
||||
"prettier.prettierPath": "./toolchain/eslint-config/node_modules/prettier/index.cjs",
|
||||
"cSpell.numSuggestions": 4,
|
||||
"cSpell.ignoreRegExpList": [
|
||||
"0x[0-9a-f_]+"
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
"scripts": {
|
||||
"build": "tsc -b tsconfig.build.json",
|
||||
"lint": "run-eslint && prettier src/**/*.ts --write --tab-width 4",
|
||||
"prepublishOnly": "npm run build"
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "run-test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@yume-chan/adb": "workspace:^",
|
||||
|
@ -40,7 +41,9 @@
|
|||
"@yume-chan/struct": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.10",
|
||||
"@yume-chan/eslint-config": "workspace:^",
|
||||
"@yume-chan/test-runner": "workspace:^",
|
||||
"@yume-chan/tsconfig": "workspace:^",
|
||||
"prettier": "^3.4.2",
|
||||
"typescript": "^5.7.3"
|
||||
|
|
|
@ -15,7 +15,7 @@ export function createConnection(
|
|||
adb: Adb,
|
||||
options: Required<
|
||||
Pick<
|
||||
ScrcpyOptions2_1.Init,
|
||||
ScrcpyOptions2_1.Init<boolean>,
|
||||
| "tunnelForward"
|
||||
| "control"
|
||||
| "sendDummyByte"
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "../connection.js";
|
||||
import { AdbScrcpyOptions } from "../types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_1 extends AdbScrcpyOptions<ScrcpyOptions2_1.Init> {
|
||||
constructor(init: ScrcpyOptions2_1.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_1<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_1.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_1.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_1(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_1 extends AdbScrcpyOptions<ScrcpyOptions2_1.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_1 {
|
||||
export type Init = ScrcpyOptions2_1.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_1.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_1_1 extends AdbScrcpyOptions<ScrcpyOptions2_1_1.Init> {
|
||||
constructor(init: ScrcpyOptions2_1_1.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_1_1<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_1_1.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_1_1.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_1_1(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_1_1 extends AdbScrcpyOptions<ScrcpyOptions2_1_1.I
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_1_1 {
|
||||
export type Init = ScrcpyOptions2_1_1.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_1_1.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_2 extends AdbScrcpyOptions<ScrcpyOptions2_2.Init> {
|
||||
constructor(init: ScrcpyOptions2_2.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_2<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_2.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_2.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_2(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_2 extends AdbScrcpyOptions<ScrcpyOptions2_2.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_2 {
|
||||
export type Init = ScrcpyOptions2_2.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_2.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_3 extends AdbScrcpyOptions<ScrcpyOptions2_3.Init> {
|
||||
constructor(init: ScrcpyOptions2_3.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_3<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_3.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_3.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_3(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_3 extends AdbScrcpyOptions<ScrcpyOptions2_3.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_3 {
|
||||
export type Init = ScrcpyOptions2_3.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_3.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_3_1 extends AdbScrcpyOptions<ScrcpyOptions2_3_1.Init> {
|
||||
constructor(init: ScrcpyOptions2_3_1.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_3_1<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_3_1.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_3_1.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_3_1(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_3_1 extends AdbScrcpyOptions<ScrcpyOptions2_3_1.I
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_3_1 {
|
||||
export type Init = ScrcpyOptions2_3_1.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_3_1.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_4 extends AdbScrcpyOptions<ScrcpyOptions2_4.Init> {
|
||||
constructor(init: ScrcpyOptions2_4.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_4<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_4.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_4.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_4(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_4 extends AdbScrcpyOptions<ScrcpyOptions2_4.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_4 {
|
||||
export type Init = ScrcpyOptions2_4.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_4.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_5 extends AdbScrcpyOptions<ScrcpyOptions2_5.Init> {
|
||||
constructor(init: ScrcpyOptions2_5.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_5<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_5.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_5.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_5(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_5 extends AdbScrcpyOptions<ScrcpyOptions2_5.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_5 {
|
||||
export type Init = ScrcpyOptions2_5.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_5.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_6 extends AdbScrcpyOptions<ScrcpyOptions2_6.Init> {
|
||||
constructor(init: ScrcpyOptions2_6.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_6<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_6.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_6.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_6(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_6 extends AdbScrcpyOptions<ScrcpyOptions2_6.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_6 {
|
||||
export type Init = ScrcpyOptions2_6.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_6.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions2_7 extends AdbScrcpyOptions<ScrcpyOptions2_7.Init> {
|
||||
constructor(init: ScrcpyOptions2_7.Init, version?: string) {
|
||||
export class AdbScrcpyOptions2_7<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions2_7.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions2_7.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions2_7(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions2_7 extends AdbScrcpyOptions<ScrcpyOptions2_7.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions2_7 {
|
||||
export type Init = ScrcpyOptions2_7.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_7.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions3_0 extends AdbScrcpyOptions<ScrcpyOptions3_0.Init> {
|
||||
constructor(init: ScrcpyOptions3_0.Init, version?: string) {
|
||||
export class AdbScrcpyOptions3_0<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions3_0.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions3_0.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions3_0(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions3_0 extends AdbScrcpyOptions<ScrcpyOptions3_0.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions3_0 {
|
||||
export type Init = ScrcpyOptions3_0.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions3_0.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions3_0_1 extends AdbScrcpyOptions<ScrcpyOptions3_0_1.Init> {
|
||||
constructor(init: ScrcpyOptions3_0_1.Init, version?: string) {
|
||||
export class AdbScrcpyOptions3_0_1<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions3_0_1.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions3_0_1.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions3_0_1(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions3_0_1 extends AdbScrcpyOptions<ScrcpyOptions3_0_1.I
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions3_0_1 {
|
||||
export type Init = ScrcpyOptions3_0_1.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions3_0_1.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions3_0_2 extends AdbScrcpyOptions<ScrcpyOptions3_0_2.Init> {
|
||||
constructor(init: ScrcpyOptions3_0_2.Init, version?: string) {
|
||||
export class AdbScrcpyOptions3_0_2<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions3_0_2.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions3_0_2.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions3_0_2(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions3_0_2 extends AdbScrcpyOptions<ScrcpyOptions3_0_2.I
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions3_0_2 {
|
||||
export type Init = ScrcpyOptions3_0_2.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions3_0_2.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import {
|
|||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyOptions3_1 extends AdbScrcpyOptions<ScrcpyOptions3_1.Init> {
|
||||
constructor(init: ScrcpyOptions3_1.Init, version?: string) {
|
||||
export class AdbScrcpyOptions3_1<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions<ScrcpyOptions3_1.Init<TVideo>> {
|
||||
constructor(init: ScrcpyOptions3_1.Init<TVideo>, version?: string) {
|
||||
super(new ScrcpyOptions3_1(init, version));
|
||||
}
|
||||
|
||||
|
@ -29,5 +31,6 @@ export class AdbScrcpyOptions3_1 extends AdbScrcpyOptions<ScrcpyOptions3_1.Init>
|
|||
}
|
||||
|
||||
export namespace AdbScrcpyOptions3_1 {
|
||||
export type Init = ScrcpyOptions3_1.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions3_1.Init<TVideo>;
|
||||
}
|
||||
|
|
154
libraries/adb-scrcpy/src/client.spec.ts
Normal file
154
libraries/adb-scrcpy/src/client.spec.ts
Normal file
|
@ -0,0 +1,154 @@
|
|||
import { describe, it } from "node:test";
|
||||
|
||||
import type { Adb } from "@yume-chan/adb";
|
||||
import { DefaultServerPath } from "@yume-chan/scrcpy";
|
||||
|
||||
import { AdbScrcpyOptions1_15 } from "./1_15/options.js";
|
||||
import { AdbScrcpyOptions2_0 } from "./2_0/options.js";
|
||||
import { AdbScrcpyOptions2_1 } from "./2_1/options.js";
|
||||
import { AdbScrcpyOptions3_1 } from "./3_1.js";
|
||||
import { AdbScrcpyClient } from "./client.js";
|
||||
import type { AdbScrcpyVideoStream } from "./video.js";
|
||||
|
||||
const TypeOnlyTest = false;
|
||||
declare const adb: Adb;
|
||||
|
||||
function expect(value: true): void {
|
||||
void value;
|
||||
}
|
||||
|
||||
function equal<X>(): <Y>(
|
||||
value: Y,
|
||||
) => (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
|
||||
? true
|
||||
: false {
|
||||
return (() => {}) as never;
|
||||
}
|
||||
|
||||
describe("AdbScrcpyClient", () => {
|
||||
describe("videoStream", () => {
|
||||
it("should have value in lower versions", async () => {
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions1_15({}),
|
||||
);
|
||||
expect(
|
||||
equal<Promise<AdbScrcpyVideoStream>>()(client.videoStream),
|
||||
);
|
||||
}
|
||||
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions2_0({}),
|
||||
);
|
||||
expect(
|
||||
equal<Promise<AdbScrcpyVideoStream>>()(client.videoStream),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("should have value when video: true", async () => {
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions2_1({ video: true }),
|
||||
);
|
||||
expect(
|
||||
equal<Promise<AdbScrcpyVideoStream>>()(client.videoStream),
|
||||
);
|
||||
}
|
||||
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions3_1({ video: true }),
|
||||
);
|
||||
expect(
|
||||
equal<Promise<AdbScrcpyVideoStream>>()(client.videoStream),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("should be undefined when video: false", async () => {
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions2_1({ video: false }),
|
||||
);
|
||||
expect(equal<undefined>()(client.videoStream));
|
||||
}
|
||||
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions3_1({ video: false }),
|
||||
);
|
||||
expect(equal<undefined>()(client.videoStream));
|
||||
}
|
||||
});
|
||||
|
||||
it("should be a union when video: undefined", async () => {
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions2_1({}),
|
||||
);
|
||||
expect(
|
||||
equal<Promise<AdbScrcpyVideoStream> | undefined>()(
|
||||
client.videoStream,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions3_1({}),
|
||||
);
|
||||
expect(
|
||||
equal<Promise<AdbScrcpyVideoStream> | undefined>()(
|
||||
client.videoStream,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("should be a union when video: boolean", async () => {
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions2_1({ video: true as boolean }),
|
||||
);
|
||||
expect(
|
||||
equal<Promise<AdbScrcpyVideoStream> | undefined>()(
|
||||
client.videoStream,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (TypeOnlyTest) {
|
||||
const client = await AdbScrcpyClient.start(
|
||||
adb,
|
||||
DefaultServerPath,
|
||||
new AdbScrcpyOptions3_1({ video: true as boolean }),
|
||||
);
|
||||
expect(
|
||||
equal<Promise<AdbScrcpyVideoStream> | undefined>()(
|
||||
client.videoStream,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -11,15 +11,10 @@ import type {
|
|||
ScrcpyEncoder,
|
||||
ScrcpyMediaStreamPacket,
|
||||
ScrcpyOptions1_15,
|
||||
ScrcpyVideoStreamMetadata,
|
||||
} from "@yume-chan/scrcpy";
|
||||
import {
|
||||
Av1,
|
||||
DefaultServerPath,
|
||||
ScrcpyControlMessageWriter,
|
||||
ScrcpyVideoCodecId,
|
||||
h264ParseConfiguration,
|
||||
h265ParseConfiguration,
|
||||
} from "@yume-chan/scrcpy";
|
||||
import type {
|
||||
Consumable,
|
||||
|
@ -30,7 +25,6 @@ import type {
|
|||
import {
|
||||
AbortController,
|
||||
BufferedReadableStream,
|
||||
InspectStream,
|
||||
PushReadableStream,
|
||||
SplitStringStream,
|
||||
TextDecoderStream,
|
||||
|
@ -40,6 +34,7 @@ import { ExactReadableEndedError } from "@yume-chan/struct";
|
|||
|
||||
import type { AdbScrcpyConnection } from "./connection.js";
|
||||
import type { AdbScrcpyOptions } from "./types.js";
|
||||
import { AdbScrcpyVideoStream } from "./video.js";
|
||||
|
||||
function arrayToStream<T>(array: T[]): ReadableStream<T> {
|
||||
return new PushReadableStream(async (controller) => {
|
||||
|
@ -73,8 +68,8 @@ export class AdbScrcpyExitedError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
interface AdbScrcpyClientInit {
|
||||
options: AdbScrcpyOptions<object>;
|
||||
interface AdbScrcpyClientInit<TOptions extends AdbScrcpyOptions<object>> {
|
||||
options: TOptions;
|
||||
process: AdbSubprocessProtocol;
|
||||
stdout: ReadableStream<string>;
|
||||
|
||||
|
@ -85,11 +80,6 @@ interface AdbScrcpyClientInit {
|
|||
| undefined;
|
||||
}
|
||||
|
||||
export interface AdbScrcpyVideoStream {
|
||||
stream: ReadableStream<ScrcpyMediaStreamPacket>;
|
||||
metadata: ScrcpyVideoStreamMetadata;
|
||||
}
|
||||
|
||||
export interface AdbScrcpyAudioStreamSuccessMetadata
|
||||
extends Omit<ScrcpyAudioStreamSuccessMetadata, "stream"> {
|
||||
readonly stream: ReadableStream<ScrcpyMediaStreamPacket>;
|
||||
|
@ -100,7 +90,7 @@ export type AdbScrcpyAudioStreamMetadata =
|
|||
| ScrcpyAudioStreamErroredMetadata
|
||||
| AdbScrcpyAudioStreamSuccessMetadata;
|
||||
|
||||
export class AdbScrcpyClient {
|
||||
export class AdbScrcpyClient<TOptions extends AdbScrcpyOptions<object>> {
|
||||
static async pushServer(
|
||||
adb: Adb,
|
||||
file: ReadableStream<MaybeConsumable<Uint8Array>>,
|
||||
|
@ -117,13 +107,15 @@ export class AdbScrcpyClient {
|
|||
}
|
||||
}
|
||||
|
||||
static async start(
|
||||
adb: Adb,
|
||||
path: string,
|
||||
options: AdbScrcpyOptions<
|
||||
static async start<
|
||||
TOptions extends AdbScrcpyOptions<
|
||||
Pick<ScrcpyOptions1_15.Init, "tunnelForward">
|
||||
>,
|
||||
) {
|
||||
>(
|
||||
adb: Adb,
|
||||
path: string,
|
||||
options: TOptions,
|
||||
): Promise<AdbScrcpyClient<TOptions>> {
|
||||
let connection: AdbScrcpyConnection | undefined;
|
||||
let process: AdbSubprocessProtocol | undefined;
|
||||
|
||||
|
@ -239,7 +231,7 @@ export class AdbScrcpyClient {
|
|||
return options.getDisplays(adb, path);
|
||||
}
|
||||
|
||||
#options: AdbScrcpyOptions<object>;
|
||||
#options: TOptions;
|
||||
#process: AdbSubprocessProtocol;
|
||||
|
||||
#stdout: ReadableStream<string>;
|
||||
|
@ -251,16 +243,6 @@ export class AdbScrcpyClient {
|
|||
return this.#process.exit;
|
||||
}
|
||||
|
||||
#screenWidth: number | undefined;
|
||||
get screenWidth() {
|
||||
return this.#screenWidth;
|
||||
}
|
||||
|
||||
#screenHeight: number | undefined;
|
||||
get screenHeight() {
|
||||
return this.#screenHeight;
|
||||
}
|
||||
|
||||
#videoStream: Promise<AdbScrcpyVideoStream> | undefined;
|
||||
/**
|
||||
* Gets a `Promise` that resolves to the parsed video stream.
|
||||
|
@ -271,8 +253,12 @@ export class AdbScrcpyClient {
|
|||
* Note: if it's not `undefined`, it must be consumed to prevent
|
||||
* the connection from being blocked.
|
||||
*/
|
||||
get videoStream() {
|
||||
return this.#videoStream;
|
||||
get videoStream(): TOptions["value"] extends { video: infer T }
|
||||
? T extends false
|
||||
? undefined
|
||||
: Promise<AdbScrcpyVideoStream>
|
||||
: Promise<AdbScrcpyVideoStream> {
|
||||
return this.#videoStream as never;
|
||||
}
|
||||
|
||||
#audioStream: Promise<AdbScrcpyAudioStreamMetadata> | undefined;
|
||||
|
@ -312,7 +298,7 @@ export class AdbScrcpyClient {
|
|||
videoStream,
|
||||
audioStream,
|
||||
controlStream,
|
||||
}: AdbScrcpyClientInit) {
|
||||
}: AdbScrcpyClientInit<TOptions>) {
|
||||
this.#options = options;
|
||||
this.#process = process;
|
||||
this.#stdout = stdout;
|
||||
|
@ -358,65 +344,10 @@ export class AdbScrcpyClient {
|
|||
}
|
||||
}
|
||||
|
||||
#configureH264(data: Uint8Array) {
|
||||
const { croppedWidth, croppedHeight } = h264ParseConfiguration(data);
|
||||
|
||||
this.#screenWidth = croppedWidth;
|
||||
this.#screenHeight = croppedHeight;
|
||||
}
|
||||
|
||||
#configureH265(data: Uint8Array) {
|
||||
const { croppedWidth, croppedHeight } = h265ParseConfiguration(data);
|
||||
|
||||
this.#screenWidth = croppedWidth;
|
||||
this.#screenHeight = croppedHeight;
|
||||
}
|
||||
|
||||
#configureAv1(data: Uint8Array) {
|
||||
const parser = new Av1(data);
|
||||
const sequenceHeader = parser.searchSequenceHeaderObu();
|
||||
if (!sequenceHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { max_frame_width_minus_1, max_frame_height_minus_1 } =
|
||||
sequenceHeader;
|
||||
|
||||
const width = max_frame_width_minus_1 + 1;
|
||||
const height = max_frame_height_minus_1 + 1;
|
||||
|
||||
this.#screenWidth = width;
|
||||
this.#screenHeight = height;
|
||||
}
|
||||
|
||||
async #createVideoStream(initialStream: ReadableStream<Uint8Array>) {
|
||||
const { stream, metadata } =
|
||||
const { metadata, stream } =
|
||||
await this.#options.parseVideoStreamMetadata(initialStream);
|
||||
|
||||
return {
|
||||
stream: stream
|
||||
.pipeThrough(this.#options.createMediaStreamTransformer())
|
||||
.pipeThrough(
|
||||
new InspectStream((packet) => {
|
||||
if (packet.type === "configuration") {
|
||||
switch (metadata.codec) {
|
||||
case ScrcpyVideoCodecId.H264:
|
||||
this.#configureH264(packet.data);
|
||||
break;
|
||||
case ScrcpyVideoCodecId.H265:
|
||||
this.#configureH265(packet.data);
|
||||
break;
|
||||
case ScrcpyVideoCodecId.AV1:
|
||||
// AV1 configuration is in normal stream
|
||||
break;
|
||||
}
|
||||
} else if (metadata.codec === ScrcpyVideoCodecId.AV1) {
|
||||
this.#configureAv1(packet.data);
|
||||
}
|
||||
}),
|
||||
),
|
||||
metadata,
|
||||
};
|
||||
return new AdbScrcpyVideoStream(this.#options, metadata, stream);
|
||||
}
|
||||
|
||||
async #createAudioStream(
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { AdbScrcpyOptions3_1 } from "./3_1.js";
|
||||
|
||||
export class AdbScrcpyOptionsLatest extends AdbScrcpyOptions3_1 {
|
||||
constructor(init: AdbScrcpyOptions3_1.Init, version: string) {
|
||||
export class AdbScrcpyOptionsLatest<
|
||||
TVideo extends boolean,
|
||||
> extends AdbScrcpyOptions3_1<TVideo> {
|
||||
constructor(init: AdbScrcpyOptions3_1.Init<TVideo>, version: string) {
|
||||
super(init, version);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace AdbScrcpyOptionsLatest {
|
||||
export type Init = AdbScrcpyOptions3_1.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
AdbScrcpyOptions3_1.Init<TVideo>;
|
||||
}
|
||||
|
|
108
libraries/adb-scrcpy/src/video.ts
Normal file
108
libraries/adb-scrcpy/src/video.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
import { EventEmitter } from "@yume-chan/event";
|
||||
import type {
|
||||
ScrcpyMediaStreamPacket,
|
||||
ScrcpyVideoStreamMetadata,
|
||||
} from "@yume-chan/scrcpy";
|
||||
import {
|
||||
Av1,
|
||||
h264ParseConfiguration,
|
||||
h265ParseConfiguration,
|
||||
ScrcpyVideoCodecId,
|
||||
} from "@yume-chan/scrcpy";
|
||||
import type { ReadableStream } from "@yume-chan/stream-extra";
|
||||
import { InspectStream } from "@yume-chan/stream-extra";
|
||||
|
||||
import type { AdbScrcpyOptions } from "./types.js";
|
||||
|
||||
export class AdbScrcpyVideoStream {
|
||||
#options: AdbScrcpyOptions<object>;
|
||||
|
||||
#metadata: ScrcpyVideoStreamMetadata;
|
||||
get metadata(): ScrcpyVideoStreamMetadata {
|
||||
return this.#metadata;
|
||||
}
|
||||
|
||||
#stream: ReadableStream<ScrcpyMediaStreamPacket>;
|
||||
get stream(): ReadableStream<ScrcpyMediaStreamPacket> {
|
||||
return this.#stream;
|
||||
}
|
||||
|
||||
#sizeChanged = new EventEmitter<{ width: number; height: number }>();
|
||||
get sizeChanged() {
|
||||
return this.#sizeChanged.event;
|
||||
}
|
||||
|
||||
#width: number = 0;
|
||||
get width() {
|
||||
return this.#width;
|
||||
}
|
||||
|
||||
#height: number = 0;
|
||||
get height() {
|
||||
return this.#height;
|
||||
}
|
||||
|
||||
constructor(
|
||||
options: AdbScrcpyOptions<object>,
|
||||
metadata: ScrcpyVideoStreamMetadata,
|
||||
stream: ReadableStream<Uint8Array>,
|
||||
) {
|
||||
this.#options = options;
|
||||
this.#metadata = metadata;
|
||||
this.#stream = stream
|
||||
.pipeThrough(this.#options.createMediaStreamTransformer())
|
||||
.pipeThrough(
|
||||
new InspectStream((packet) => {
|
||||
if (packet.type === "configuration") {
|
||||
switch (metadata.codec) {
|
||||
case ScrcpyVideoCodecId.H264:
|
||||
this.#configureH264(packet.data);
|
||||
break;
|
||||
case ScrcpyVideoCodecId.H265:
|
||||
this.#configureH265(packet.data);
|
||||
break;
|
||||
case ScrcpyVideoCodecId.AV1:
|
||||
// AV1 configuration is in data packet
|
||||
break;
|
||||
}
|
||||
} else if (metadata.codec === ScrcpyVideoCodecId.AV1) {
|
||||
this.#configureAv1(packet.data);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#configureH264(data: Uint8Array) {
|
||||
const { croppedWidth, croppedHeight } = h264ParseConfiguration(data);
|
||||
|
||||
this.#width = croppedWidth;
|
||||
this.#height = croppedHeight;
|
||||
this.#sizeChanged.fire({ width: croppedWidth, height: croppedHeight });
|
||||
}
|
||||
|
||||
#configureH265(data: Uint8Array) {
|
||||
const { croppedWidth, croppedHeight } = h265ParseConfiguration(data);
|
||||
|
||||
this.#width = croppedWidth;
|
||||
this.#height = croppedHeight;
|
||||
this.#sizeChanged.fire({ width: croppedWidth, height: croppedHeight });
|
||||
}
|
||||
|
||||
#configureAv1(data: Uint8Array) {
|
||||
const parser = new Av1(data);
|
||||
const sequenceHeader = parser.searchSequenceHeaderObu();
|
||||
if (!sequenceHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { max_frame_width_minus_1, max_frame_height_minus_1 } =
|
||||
sequenceHeader;
|
||||
|
||||
const width = max_frame_width_minus_1 + 1;
|
||||
const height = max_frame_height_minus_1 + 1;
|
||||
|
||||
this.#width = width;
|
||||
this.#height = height;
|
||||
this.#sizeChanged.fire({ width, height });
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
"extends": "./tsconfig.build.json",
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
},
|
||||
"exclude": []
|
||||
|
|
|
@ -50,6 +50,16 @@ export class TinyH264Decoder implements ScrcpyVideoDecoder {
|
|||
return this.#sizeChanged.event;
|
||||
}
|
||||
|
||||
#width: number = 0;
|
||||
get width() {
|
||||
return this.#width;
|
||||
}
|
||||
|
||||
#height: number = 0;
|
||||
get height() {
|
||||
return this.#height;
|
||||
}
|
||||
|
||||
#frameRendered = 0;
|
||||
get framesRendered() {
|
||||
return this.#frameRendered;
|
||||
|
@ -124,12 +134,16 @@ export class TinyH264Decoder implements ScrcpyVideoDecoder {
|
|||
cropLeft,
|
||||
cropTop,
|
||||
} = h264ParseConfiguration(data);
|
||||
|
||||
this.#width = croppedWidth;
|
||||
this.#height = croppedHeight;
|
||||
this.#sizeChanged.fire({
|
||||
width: croppedWidth,
|
||||
height: croppedHeight,
|
||||
});
|
||||
|
||||
// H.264 Baseline profile only supports YUV 420 pixel format
|
||||
// So chroma width/height is each half of video width/height
|
||||
const chromaWidth = encodedWidth / 2;
|
||||
const chromaHeight = encodedHeight / 2;
|
||||
|
||||
|
|
|
@ -12,8 +12,12 @@ export interface ScrcpyVideoDecoderCapability {
|
|||
|
||||
export interface ScrcpyVideoDecoder extends Disposable {
|
||||
readonly sizeChanged: Event<{ width: number; height: number }>;
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
|
||||
readonly framesRendered: number;
|
||||
readonly framesSkipped: number;
|
||||
|
||||
readonly writable: WritableStream<ScrcpyMediaStreamPacket>;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,87 +10,9 @@ import { WritableStream } from "@yume-chan/stream-extra";
|
|||
|
||||
import { Av1Codec, H264Decoder, H265Decoder } from "./codec/index.js";
|
||||
import type { CodecDecoder } from "./codec/type.js";
|
||||
import { Pool } from "./pool.js";
|
||||
import type { VideoFrameRenderer } from "./render/index.js";
|
||||
|
||||
class Pool<T> {
|
||||
#controller!: ReadableStreamDefaultController<T>;
|
||||
#readable = new ReadableStream<T>(
|
||||
{
|
||||
start: (controller) => {
|
||||
this.#controller = controller;
|
||||
},
|
||||
pull: (controller) => {
|
||||
controller.enqueue(this.#initializer());
|
||||
},
|
||||
},
|
||||
{ highWaterMark: 0 },
|
||||
);
|
||||
#reader = this.#readable.getReader();
|
||||
|
||||
#initializer: () => T;
|
||||
|
||||
#size = 0;
|
||||
#capacity: number;
|
||||
|
||||
constructor(initializer: () => T, capacity: number) {
|
||||
this.#initializer = initializer;
|
||||
this.#capacity = capacity;
|
||||
}
|
||||
|
||||
async borrow() {
|
||||
const result = await this.#reader.read();
|
||||
return result.value!;
|
||||
}
|
||||
|
||||
return(value: T) {
|
||||
if (this.#size < this.#capacity) {
|
||||
this.#controller.enqueue(value);
|
||||
this.#size += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VideoFrameCapturer {
|
||||
#canvas: OffscreenCanvas | HTMLCanvasElement;
|
||||
#context: ImageBitmapRenderingContext;
|
||||
|
||||
constructor() {
|
||||
if (typeof OffscreenCanvas !== "undefined") {
|
||||
this.#canvas = new OffscreenCanvas(1, 1);
|
||||
} else {
|
||||
this.#canvas = document.createElement("canvas");
|
||||
this.#canvas.width = 1;
|
||||
this.#canvas.height = 1;
|
||||
}
|
||||
this.#context = this.#canvas.getContext("bitmaprenderer", {
|
||||
alpha: false,
|
||||
})!;
|
||||
}
|
||||
|
||||
async capture(frame: VideoFrame): Promise<Blob> {
|
||||
this.#canvas.width = frame.displayWidth;
|
||||
this.#canvas.height = frame.displayHeight;
|
||||
|
||||
const bitmap = await createImageBitmap(frame);
|
||||
this.#context.transferFromImageBitmap(bitmap);
|
||||
|
||||
if (this.#canvas instanceof OffscreenCanvas) {
|
||||
return await this.#canvas.convertToBlob({
|
||||
type: "image/png",
|
||||
});
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
(this.#canvas as HTMLCanvasElement).toBlob((blob) => {
|
||||
if (!blob) {
|
||||
reject(new Error("Failed to convert canvas to blob"));
|
||||
} else {
|
||||
resolve(blob);
|
||||
}
|
||||
}, "image/png");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
import { VideoFrameCapturer } from "./snapshot.js";
|
||||
|
||||
const VideoFrameCapturerPool =
|
||||
/* #__PURE__ */
|
||||
|
@ -144,6 +66,16 @@ export class WebCodecsVideoDecoder implements ScrcpyVideoDecoder {
|
|||
return this.#sizeChanged.event;
|
||||
}
|
||||
|
||||
#width: number = 0;
|
||||
get width() {
|
||||
return this.#width;
|
||||
}
|
||||
|
||||
#height: number = 0;
|
||||
get height() {
|
||||
return this.#height;
|
||||
}
|
||||
|
||||
#decoder: VideoDecoder;
|
||||
|
||||
#drawing = false;
|
||||
|
@ -218,7 +150,7 @@ export class WebCodecsVideoDecoder implements ScrcpyVideoDecoder {
|
|||
},
|
||||
});
|
||||
|
||||
this.#onVerticalSync();
|
||||
this.#handleAnimationFrame();
|
||||
}
|
||||
|
||||
#setError(error: Error) {
|
||||
|
@ -261,16 +193,20 @@ export class WebCodecsVideoDecoder implements ScrcpyVideoDecoder {
|
|||
|
||||
#updateSize = (width: number, height: number) => {
|
||||
this.#renderer.setSize(width, height);
|
||||
this.#width = width;
|
||||
this.#height = height;
|
||||
this.#sizeChanged.fire({ width, height });
|
||||
};
|
||||
|
||||
#onVerticalSync = () => {
|
||||
#handleAnimationFrame = () => {
|
||||
if (this.#framesDraw > 0) {
|
||||
this.#framesPresented += 1;
|
||||
this.#framesSkipped += this.#framesDraw - 1;
|
||||
this.#framesDraw = 0;
|
||||
}
|
||||
this.#animationFrameId = requestAnimationFrame(this.#onVerticalSync);
|
||||
this.#animationFrameId = requestAnimationFrame(
|
||||
this.#handleAnimationFrame,
|
||||
);
|
||||
};
|
||||
|
||||
async snapshot() {
|
||||
|
|
37
libraries/scrcpy-decoder-webcodecs/src/video/pool.ts
Normal file
37
libraries/scrcpy-decoder-webcodecs/src/video/pool.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
export class Pool<T> {
|
||||
#controller!: ReadableStreamDefaultController<T>;
|
||||
#readable = new ReadableStream<T>(
|
||||
{
|
||||
start: (controller) => {
|
||||
this.#controller = controller;
|
||||
},
|
||||
pull: (controller) => {
|
||||
controller.enqueue(this.#initializer());
|
||||
},
|
||||
},
|
||||
{ highWaterMark: 0 },
|
||||
);
|
||||
#reader = this.#readable.getReader();
|
||||
|
||||
#initializer: () => T;
|
||||
|
||||
#size = 0;
|
||||
#capacity: number;
|
||||
|
||||
constructor(initializer: () => T, capacity: number) {
|
||||
this.#initializer = initializer;
|
||||
this.#capacity = capacity;
|
||||
}
|
||||
|
||||
async borrow() {
|
||||
const result = await this.#reader.read();
|
||||
return result.value!;
|
||||
}
|
||||
|
||||
return(value: T) {
|
||||
if (this.#size < this.#capacity) {
|
||||
this.#controller.enqueue(value);
|
||||
this.#size += 1;
|
||||
}
|
||||
}
|
||||
}
|
41
libraries/scrcpy-decoder-webcodecs/src/video/snapshot.ts
Normal file
41
libraries/scrcpy-decoder-webcodecs/src/video/snapshot.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
export class VideoFrameCapturer {
|
||||
#canvas: OffscreenCanvas | HTMLCanvasElement;
|
||||
#context: ImageBitmapRenderingContext;
|
||||
|
||||
constructor() {
|
||||
if (typeof OffscreenCanvas !== "undefined") {
|
||||
this.#canvas = new OffscreenCanvas(1, 1);
|
||||
} else {
|
||||
this.#canvas = document.createElement("canvas");
|
||||
this.#canvas.width = 1;
|
||||
this.#canvas.height = 1;
|
||||
}
|
||||
this.#context = this.#canvas.getContext("bitmaprenderer", {
|
||||
alpha: false,
|
||||
})!;
|
||||
}
|
||||
|
||||
async capture(frame: VideoFrame): Promise<Blob> {
|
||||
this.#canvas.width = frame.displayWidth;
|
||||
this.#canvas.height = frame.displayHeight;
|
||||
|
||||
const bitmap = await createImageBitmap(frame);
|
||||
this.#context.transferFromImageBitmap(bitmap);
|
||||
|
||||
if (this.#canvas instanceof OffscreenCanvas) {
|
||||
return await this.#canvas.convertToBlob({
|
||||
type: "image/png",
|
||||
});
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
(this.#canvas as HTMLCanvasElement).toBlob((blob) => {
|
||||
if (!blob) {
|
||||
reject(new Error("Failed to convert canvas to blob"));
|
||||
} else {
|
||||
resolve(blob);
|
||||
}
|
||||
}, "image/png");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,8 +36,8 @@ export const InjectTouchControlMessage = struct(
|
|||
pointerId: u64,
|
||||
pointerX: u32,
|
||||
pointerY: u32,
|
||||
screenWidth: u16,
|
||||
screenHeight: u16,
|
||||
videoWidth: u16,
|
||||
videoHeight: u16,
|
||||
pressure: UnsignedFloat,
|
||||
buttons: u32,
|
||||
},
|
||||
|
|
|
@ -12,8 +12,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: 0.5,
|
||||
scrollY: 0.5,
|
||||
buttons: 0,
|
||||
|
@ -27,8 +27,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: 1.5,
|
||||
scrollY: 1.5,
|
||||
buttons: 0,
|
||||
|
@ -43,8 +43,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: 0.5,
|
||||
scrollY: 0.5,
|
||||
buttons: 0,
|
||||
|
@ -53,8 +53,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: 0.5,
|
||||
scrollY: 0.5,
|
||||
buttons: 0,
|
||||
|
@ -69,8 +69,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: -0.5,
|
||||
scrollY: -0.5,
|
||||
buttons: 0,
|
||||
|
@ -79,8 +79,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: -0.5,
|
||||
scrollY: -0.5,
|
||||
buttons: 0,
|
||||
|
|
|
@ -9,8 +9,8 @@ export const InjectScrollControlMessage = struct(
|
|||
type: u8,
|
||||
pointerX: u32,
|
||||
pointerY: u32,
|
||||
screenWidth: u16,
|
||||
screenHeight: u16,
|
||||
videoWidth: u16,
|
||||
videoHeight: u16,
|
||||
scrollX: s32,
|
||||
scrollY: s32,
|
||||
},
|
||||
|
|
|
@ -12,8 +12,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: 1.5,
|
||||
scrollY: 1.5,
|
||||
buttons: 0,
|
||||
|
|
|
@ -102,8 +102,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: 0.5,
|
||||
scrollY: 0.5,
|
||||
buttons: 0,
|
||||
|
@ -115,8 +115,8 @@ describe("ScrollController", () => {
|
|||
type: ScrcpyControlMessageType.InjectScroll,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
videoWidth: 0,
|
||||
videoHeight: 0,
|
||||
scrollX: 1.5,
|
||||
scrollY: 1.5,
|
||||
buttons: 0,
|
||||
|
|
|
@ -29,8 +29,8 @@ export const InjectScrollControlMessage = /* #__PURE__ */ (() =>
|
|||
type: u8(ScrcpyControlMessageType.InjectScroll),
|
||||
pointerX: u32,
|
||||
pointerY: u32,
|
||||
screenWidth: u16,
|
||||
screenHeight: u16,
|
||||
videoWidth: u16,
|
||||
videoHeight: u16,
|
||||
scrollX: SignedFloat,
|
||||
scrollY: SignedFloat,
|
||||
buttons: u32,
|
||||
|
|
|
@ -15,8 +15,8 @@ export const InjectTouchControlMessage = /* #__PURE__ */ (() =>
|
|||
pointerId: u64,
|
||||
pointerX: u32,
|
||||
pointerY: u32,
|
||||
screenWidth: u16,
|
||||
screenHeight: u16,
|
||||
videoWidth: u16,
|
||||
videoHeight: u16,
|
||||
pressure: PrevImpl.UnsignedFloat,
|
||||
actionButton: u32,
|
||||
buttons: u32,
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ScrcpyAudioCodec } from "../../base/index.js";
|
|||
|
||||
export async function parseAudioStreamMetadata(
|
||||
stream: ReadableStream<Uint8Array>,
|
||||
options: Pick<Required<Init>, "sendCodecMeta" | "audioCodec">,
|
||||
options: Pick<Required<Init<boolean>>, "sendCodecMeta" | "audioCodec">,
|
||||
): Promise<ScrcpyAudioStreamMetadata> {
|
||||
const buffered = new BufferedReadableStream(stream);
|
||||
|
||||
|
|
|
@ -6,4 +6,4 @@ export const Defaults = /* #__PURE__ */ (() =>
|
|||
...PrevImpl.Defaults,
|
||||
video: true,
|
||||
audioSource: "output",
|
||||
}) as const satisfies Required<Init>)();
|
||||
}) as const satisfies Required<Init<true>>)();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { PrevImpl } from "./prev.js";
|
||||
|
||||
export interface Init extends PrevImpl.Init {
|
||||
video?: boolean;
|
||||
export interface Init<TVideo extends boolean> extends PrevImpl.Init {
|
||||
video?: TVideo;
|
||||
audioSource?: "output" | "mic";
|
||||
}
|
||||
|
|
|
@ -37,12 +37,14 @@ import {
|
|||
setListEncoders,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions2_1 implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions2_1<TVideo extends boolean>
|
||||
implements ScrcpyOptions<Init<TVideo>>
|
||||
{
|
||||
static readonly Defaults = Defaults;
|
||||
|
||||
readonly version: string;
|
||||
|
||||
readonly value: Required<Init>;
|
||||
readonly value: Required<Init<TVideo>>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
return ControlMessageTypes;
|
||||
|
@ -55,8 +57,8 @@ export class ScrcpyOptions2_1 implements ScrcpyOptions<Init> {
|
|||
|
||||
#ackClipboardHandler: AckClipboardHandler | undefined;
|
||||
|
||||
constructor(init: Init, version = "2.1") {
|
||||
this.value = { ...Defaults, ...init };
|
||||
constructor(init: Init<TVideo>, version = "2.1") {
|
||||
this.value = { ...Defaults, ...init } as never;
|
||||
this.version = version;
|
||||
|
||||
if (this.value.control && this.value.clipboardAutosync) {
|
||||
|
@ -66,7 +68,7 @@ export class ScrcpyOptions2_1 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
|
||||
serialize(): string[] {
|
||||
return serialize(this.value, Defaults);
|
||||
return serialize<Init<boolean>>(this.value, Defaults);
|
||||
}
|
||||
|
||||
setListDisplays(): void {
|
||||
|
@ -154,8 +156,8 @@ export class ScrcpyOptions2_1 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
}
|
||||
|
||||
type Init_ = Init;
|
||||
type Init_<TVideo extends boolean> = Init<TVideo>;
|
||||
|
||||
export namespace ScrcpyOptions2_1 {
|
||||
export type Init = Init_;
|
||||
export type Init<TVideo extends boolean = boolean> = Init_<TVideo>;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { ScrcpyOptions2_1 } from "./2_1/index.js";
|
||||
|
||||
export class ScrcpyOptions2_1_1 extends ScrcpyOptions2_1 {
|
||||
constructor(init: ScrcpyOptions2_1.Init, version = "2.1.1") {
|
||||
export class ScrcpyOptions2_1_1<
|
||||
TVideo extends boolean,
|
||||
> extends ScrcpyOptions2_1<TVideo> {
|
||||
constructor(init: ScrcpyOptions2_1.Init<TVideo>, version = "2.1.1") {
|
||||
super(init, version);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ScrcpyOptions2_1_1 {
|
||||
export type Init = ScrcpyOptions2_1.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_1.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -14,4 +14,4 @@ export const Defaults = /* #__PURE__ */ (() =>
|
|||
cameraHighSpeed: false,
|
||||
listCameras: false,
|
||||
listCameraSizes: false,
|
||||
}) as const satisfies Required<Init>)();
|
||||
}) as const satisfies Required<Init<true>>)();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { PrevImpl } from "./prev.js";
|
||||
|
||||
export interface Init extends PrevImpl.Init {
|
||||
export interface Init<TVideo extends boolean> extends PrevImpl.Init<TVideo> {
|
||||
videoSource?: "display" | "camera";
|
||||
cameraId?: string | undefined;
|
||||
cameraSize?: string | undefined;
|
||||
|
|
|
@ -37,12 +37,14 @@ import {
|
|||
setListEncoders,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions2_2 implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions2_2<TVideo extends boolean>
|
||||
implements ScrcpyOptions<Init<TVideo>>
|
||||
{
|
||||
static readonly Defaults = Defaults;
|
||||
|
||||
readonly version: string;
|
||||
|
||||
readonly value: Required<Init>;
|
||||
readonly value: Required<Init<TVideo>>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
return ControlMessageTypes;
|
||||
|
@ -55,8 +57,8 @@ export class ScrcpyOptions2_2 implements ScrcpyOptions<Init> {
|
|||
|
||||
#ackClipboardHandler: AckClipboardHandler | undefined;
|
||||
|
||||
constructor(init: Init, version = "v2.2") {
|
||||
this.value = { ...Defaults, ...init };
|
||||
constructor(init: Init<TVideo>, version = "v2.2") {
|
||||
this.value = { ...Defaults, ...init } as never;
|
||||
this.version = version;
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
|
@ -70,7 +72,7 @@ export class ScrcpyOptions2_2 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
|
||||
serialize(): string[] {
|
||||
return serialize(this.value, Defaults);
|
||||
return serialize<Init<boolean>>(this.value, Defaults);
|
||||
}
|
||||
|
||||
setListDisplays(): void {
|
||||
|
@ -158,8 +160,8 @@ export class ScrcpyOptions2_2 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
}
|
||||
|
||||
type Init_ = Init;
|
||||
type Init_<TVideo extends boolean> = Init<TVideo>;
|
||||
|
||||
export namespace ScrcpyOptions2_2 {
|
||||
export type Init = Init_;
|
||||
export type Init<TVideo extends boolean = boolean> = Init_<TVideo>;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { PrevImpl } from "./prev.js";
|
||||
|
||||
export interface Init extends Omit<PrevImpl.Init, "audioCodec"> {
|
||||
audioCodec?: PrevImpl.Init["audioCodec"] | "flac";
|
||||
export interface Init<TVideo extends boolean>
|
||||
extends Omit<PrevImpl.Init<TVideo>, "audioCodec"> {
|
||||
audioCodec?: PrevImpl.Init<TVideo>["audioCodec"] | "flac";
|
||||
}
|
||||
|
|
|
@ -37,12 +37,14 @@ import {
|
|||
setListEncoders,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions2_3 implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions2_3<TVideo extends boolean>
|
||||
implements ScrcpyOptions<Init<TVideo>>
|
||||
{
|
||||
static readonly Defaults = Defaults;
|
||||
|
||||
readonly version: string;
|
||||
|
||||
readonly value: Required<Init>;
|
||||
readonly value: Required<Init<TVideo>>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
return ControlMessageTypes;
|
||||
|
@ -55,8 +57,8 @@ export class ScrcpyOptions2_3 implements ScrcpyOptions<Init> {
|
|||
|
||||
#ackClipboardHandler: AckClipboardHandler | undefined;
|
||||
|
||||
constructor(init: Init, version = "2.3") {
|
||||
this.value = { ...Defaults, ...init };
|
||||
constructor(init: Init<TVideo>, version = "2.3") {
|
||||
this.value = { ...Defaults, ...init } as never;
|
||||
this.version = version;
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
|
@ -70,7 +72,7 @@ export class ScrcpyOptions2_3 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
|
||||
serialize(): string[] {
|
||||
return serialize(this.value, Defaults);
|
||||
return serialize<Init<boolean>>(this.value, Defaults);
|
||||
}
|
||||
|
||||
setListDisplays(): void {
|
||||
|
@ -158,8 +160,8 @@ export class ScrcpyOptions2_3 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
}
|
||||
|
||||
type Init_ = Init;
|
||||
type Init_<TVideo extends boolean> = Init<TVideo>;
|
||||
|
||||
export namespace ScrcpyOptions2_3 {
|
||||
export type Init = Init_;
|
||||
export type Init<TVideo extends boolean = boolean> = Init_<TVideo>;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { ScrcpyOptions2_3 } from "./2_3/index.js";
|
||||
|
||||
export class ScrcpyOptions2_3_1 extends ScrcpyOptions2_3 {
|
||||
constructor(init: ScrcpyOptions2_3.Init, version = "2.3.1") {
|
||||
export class ScrcpyOptions2_3_1<
|
||||
TVideo extends boolean,
|
||||
> extends ScrcpyOptions2_3<TVideo> {
|
||||
constructor(init: ScrcpyOptions2_3.Init<TVideo>, version = "2.3.1") {
|
||||
super(init, version);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ScrcpyOptions2_3_1 {
|
||||
export type Init = ScrcpyOptions2_3.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_3.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -41,12 +41,14 @@ import {
|
|||
UHidOutputStream,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions2_4 implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions2_4<TVideo extends boolean>
|
||||
implements ScrcpyOptions<Init<TVideo>>
|
||||
{
|
||||
static readonly Defaults = Defaults;
|
||||
|
||||
readonly version: string;
|
||||
|
||||
readonly value: Required<Init>;
|
||||
readonly value: Required<Init<TVideo>>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
return ControlMessageTypes;
|
||||
|
@ -66,8 +68,8 @@ export class ScrcpyOptions2_4 implements ScrcpyOptions<Init> {
|
|||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init, version = "2.4") {
|
||||
this.value = { ...Defaults, ...init };
|
||||
constructor(init: Init<TVideo>, version = "2.4") {
|
||||
this.value = { ...Defaults, ...init } as never;
|
||||
this.version = version;
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
|
@ -85,7 +87,7 @@ export class ScrcpyOptions2_4 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
|
||||
serialize(): string[] {
|
||||
return serialize(this.value, Defaults);
|
||||
return serialize<Init<boolean>>(this.value, Defaults);
|
||||
}
|
||||
|
||||
setListDisplays(): void {
|
||||
|
@ -185,8 +187,8 @@ export class ScrcpyOptions2_4 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
}
|
||||
|
||||
type Init_ = Init;
|
||||
type Init_<TVideo extends boolean> = Init<TVideo>;
|
||||
|
||||
export namespace ScrcpyOptions2_4 {
|
||||
export type Init = Init_;
|
||||
export type Init<TVideo extends boolean = boolean> = Init_<TVideo>;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { ScrcpyOptions2_4 } from "./2_4/index.js";
|
||||
|
||||
export class ScrcpyOptions2_5 extends ScrcpyOptions2_4 {
|
||||
constructor(init: ScrcpyOptions2_4.Init, version = "2.5") {
|
||||
export class ScrcpyOptions2_5<
|
||||
TVideo extends boolean,
|
||||
> extends ScrcpyOptions2_4<TVideo> {
|
||||
constructor(init: ScrcpyOptions2_4.Init<TVideo>, version = "2.5") {
|
||||
super(init, version);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ScrcpyOptions2_5 {
|
||||
export type Init = ScrcpyOptions2_4.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_4.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ export const Defaults = /* #__PURE__ */ (() =>
|
|||
({
|
||||
...PrevImpl.Defaults,
|
||||
audioDup: false,
|
||||
}) as const satisfies Required<Init>)();
|
||||
}) as const satisfies Required<Init<true>>)();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { PrevImpl } from "./prev.js";
|
||||
|
||||
export interface Init extends Omit<PrevImpl.Init, "audioSource"> {
|
||||
audioSource?: PrevImpl.Init["audioSource"] | "playback";
|
||||
export interface Init<TVideo extends boolean>
|
||||
extends Omit<PrevImpl.Init<TVideo>, "audioSource"> {
|
||||
audioSource?: PrevImpl.Init<TVideo>["audioSource"] | "playback";
|
||||
audioDup?: boolean;
|
||||
}
|
||||
|
|
|
@ -41,12 +41,14 @@ import {
|
|||
UHidOutputStream,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions2_6 implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions2_6<TVideo extends boolean>
|
||||
implements ScrcpyOptions<Init<TVideo>>
|
||||
{
|
||||
static readonly Defaults = Defaults;
|
||||
|
||||
readonly version: string;
|
||||
|
||||
readonly value: Required<Init>;
|
||||
readonly value: Required<Init<TVideo>>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
return ControlMessageTypes;
|
||||
|
@ -66,8 +68,8 @@ export class ScrcpyOptions2_6 implements ScrcpyOptions<Init> {
|
|||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init, version = "2.6") {
|
||||
this.value = { ...Defaults, ...init };
|
||||
constructor(init: Init<TVideo>, version = "2.6") {
|
||||
this.value = { ...Defaults, ...init } as never;
|
||||
this.version = version;
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
|
@ -89,7 +91,7 @@ export class ScrcpyOptions2_6 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
|
||||
serialize(): string[] {
|
||||
return serialize(this.value, Defaults);
|
||||
return serialize<Init<boolean>>(this.value, Defaults);
|
||||
}
|
||||
|
||||
setListDisplays(): void {
|
||||
|
@ -183,8 +185,8 @@ export class ScrcpyOptions2_6 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
}
|
||||
|
||||
type Init_ = Init;
|
||||
type Init_<TVideo extends boolean> = Init<TVideo>;
|
||||
|
||||
export namespace ScrcpyOptions2_6 {
|
||||
export type Init = Init_;
|
||||
export type Init<TVideo extends boolean = boolean> = Init_<TVideo>;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { ScrcpyOptions2_6 } from "./2_6/index.js";
|
||||
|
||||
export class ScrcpyOptions2_6_1 extends ScrcpyOptions2_6 {
|
||||
constructor(init: ScrcpyOptions2_6.Init, version = "2.6.1") {
|
||||
export class ScrcpyOptions2_6_1<
|
||||
TVideo extends boolean,
|
||||
> extends ScrcpyOptions2_6<TVideo> {
|
||||
constructor(init: ScrcpyOptions2_6.Init<TVideo>, version = "2.6.1") {
|
||||
super(init, version);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ScrcpyOptions2_6_1 {
|
||||
export type Init = ScrcpyOptions2_6.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions2_6.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -41,12 +41,14 @@ import {
|
|||
UHidOutputStream,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions2_7 implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions2_7<TVideo extends boolean>
|
||||
implements ScrcpyOptions<Init<TVideo>>
|
||||
{
|
||||
static readonly Defaults = Defaults;
|
||||
|
||||
readonly version: string;
|
||||
|
||||
readonly value: Required<Init>;
|
||||
readonly value: Required<Init<TVideo>>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
return ControlMessageTypes;
|
||||
|
@ -66,8 +68,8 @@ export class ScrcpyOptions2_7 implements ScrcpyOptions<Init> {
|
|||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init, version = "2.7") {
|
||||
this.value = { ...Defaults, ...init };
|
||||
constructor(init: Init<TVideo>, version = "2.7") {
|
||||
this.value = { ...Defaults, ...init } as never;
|
||||
this.version = version;
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
|
@ -89,7 +91,7 @@ export class ScrcpyOptions2_7 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
|
||||
serialize(): string[] {
|
||||
return serialize(this.value, Defaults);
|
||||
return serialize<Init<boolean>>(this.value, Defaults);
|
||||
}
|
||||
|
||||
setListDisplays(): void {
|
||||
|
@ -183,8 +185,8 @@ export class ScrcpyOptions2_7 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
}
|
||||
|
||||
type Init_ = Init;
|
||||
type Init_<TVideo extends boolean> = Init<TVideo>;
|
||||
|
||||
export namespace ScrcpyOptions2_7 {
|
||||
export type Init = Init_;
|
||||
export type Init<TVideo extends boolean = boolean> = Init_<TVideo>;
|
||||
}
|
||||
|
|
|
@ -12,4 +12,4 @@ export const Defaults = /* #__PURE__ */ (() =>
|
|||
listApps: false,
|
||||
newDisplay: undefined,
|
||||
vdSystemDecorations: true,
|
||||
}) as const satisfies Required<Init>)();
|
||||
}) as const satisfies Required<Init<true>>)();
|
||||
|
|
|
@ -106,7 +106,8 @@ export class NewDisplay implements ScrcpyOptionValue {
|
|||
}
|
||||
}
|
||||
|
||||
export interface Init extends Omit<PrevImpl.Init, "lockVideoOrientation"> {
|
||||
export interface Init<TVideo extends boolean>
|
||||
extends Omit<PrevImpl.Init<TVideo>, "lockVideoOrientation"> {
|
||||
captureOrientation?: CaptureOrientation | string | undefined;
|
||||
angle?: number;
|
||||
screenOffTimeout?: number | undefined;
|
||||
|
|
|
@ -41,12 +41,14 @@ import {
|
|||
UHidOutputStream,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions3_0 implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions3_0<TVideo extends boolean>
|
||||
implements ScrcpyOptions<Init<TVideo>>
|
||||
{
|
||||
static readonly Defaults = Defaults;
|
||||
|
||||
readonly version: string;
|
||||
|
||||
readonly value: Required<Init>;
|
||||
readonly value: Required<Init<TVideo>>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
return ControlMessageTypes;
|
||||
|
@ -66,8 +68,8 @@ export class ScrcpyOptions3_0 implements ScrcpyOptions<Init> {
|
|||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init, version = "3.0") {
|
||||
this.value = { ...Defaults, ...init };
|
||||
constructor(init: Init<TVideo>, version = "3.0") {
|
||||
this.value = { ...Defaults, ...init } as never;
|
||||
this.version = version;
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
|
@ -89,7 +91,7 @@ export class ScrcpyOptions3_0 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
|
||||
serialize(): string[] {
|
||||
return serialize(this.value, Defaults);
|
||||
return serialize<Init<boolean>>(this.value, Defaults);
|
||||
}
|
||||
|
||||
setListDisplays(): void {
|
||||
|
@ -183,8 +185,8 @@ export class ScrcpyOptions3_0 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
}
|
||||
|
||||
type Init_ = Init;
|
||||
type Init_<TVideo extends boolean> = Init<TVideo>;
|
||||
|
||||
export namespace ScrcpyOptions3_0 {
|
||||
export type Init = Init_;
|
||||
export type Init<TVideo extends boolean = boolean> = Init_<TVideo>;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { ScrcpyOptions3_0 } from "./3_0/index.js";
|
||||
|
||||
export class ScrcpyOptions3_0_1 extends ScrcpyOptions3_0 {
|
||||
constructor(init: ScrcpyOptions3_0.Init, version = "3.0.1") {
|
||||
export class ScrcpyOptions3_0_1<
|
||||
TVideo extends boolean,
|
||||
> extends ScrcpyOptions3_0<TVideo> {
|
||||
constructor(init: ScrcpyOptions3_0.Init<TVideo>, version = "3.0.1") {
|
||||
super(init, version);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ScrcpyOptions3_0_1 {
|
||||
export type Init = ScrcpyOptions3_0.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions3_0.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { ScrcpyOptions3_0 } from "./3_0/index.js";
|
||||
|
||||
export class ScrcpyOptions3_0_2 extends ScrcpyOptions3_0 {
|
||||
constructor(init: ScrcpyOptions3_0.Init, version = "3.0.2") {
|
||||
export class ScrcpyOptions3_0_2<
|
||||
TVideo extends boolean,
|
||||
> extends ScrcpyOptions3_0<TVideo> {
|
||||
constructor(init: ScrcpyOptions3_0.Init<TVideo>, version = "3.0.2") {
|
||||
super(init, version);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ScrcpyOptions3_0_2 {
|
||||
export type Init = ScrcpyOptions3_0.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions3_0.Init<TVideo>;
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ export const Defaults = /* #__PURE__ */ (() =>
|
|||
({
|
||||
...PrevImpl.Defaults,
|
||||
vdDestroyContent: false,
|
||||
}) as const satisfies Required<Init>)();
|
||||
}) as const satisfies Required<Init<true>>)();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { PrevImpl } from "./prev.js";
|
||||
|
||||
export interface Init extends PrevImpl.Init {
|
||||
export interface Init<TVideo extends boolean> extends PrevImpl.Init<TVideo> {
|
||||
vdDestroyContent?: boolean;
|
||||
}
|
||||
|
|
|
@ -41,12 +41,14 @@ import {
|
|||
UHidOutputStream,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions3_1 implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions3_1<TVideo extends boolean>
|
||||
implements ScrcpyOptions<Init<TVideo>>
|
||||
{
|
||||
static readonly Defaults = Defaults;
|
||||
|
||||
readonly version: string;
|
||||
|
||||
readonly value: Required<Init>;
|
||||
readonly value: Required<Init<TVideo>>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
return ControlMessageTypes;
|
||||
|
@ -66,8 +68,8 @@ export class ScrcpyOptions3_1 implements ScrcpyOptions<Init> {
|
|||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init, version = "3.1") {
|
||||
this.value = { ...Defaults, ...init };
|
||||
constructor(init: Init<TVideo>, version = "3.1") {
|
||||
this.value = { ...Defaults, ...init } as never;
|
||||
this.version = version;
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
|
@ -89,7 +91,7 @@ export class ScrcpyOptions3_1 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
|
||||
serialize(): string[] {
|
||||
return serialize(this.value, Defaults);
|
||||
return serialize<Init<boolean>>(this.value, Defaults);
|
||||
}
|
||||
|
||||
setListDisplays(): void {
|
||||
|
@ -183,8 +185,8 @@ export class ScrcpyOptions3_1 implements ScrcpyOptions<Init> {
|
|||
}
|
||||
}
|
||||
|
||||
type Init_ = Init;
|
||||
type Init_<TVideo extends boolean> = Init<TVideo>;
|
||||
|
||||
export namespace ScrcpyOptions3_1 {
|
||||
export type Init = Init_;
|
||||
export type Init<TVideo extends boolean = boolean> = Init_<TVideo>;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import { ScrcpyOptions3_1 } from "./3_1/options.js";
|
||||
|
||||
export class ScrcpyOptionsLatest extends ScrcpyOptions3_1 {
|
||||
constructor(init: ScrcpyOptions3_1.Init, version: string) {
|
||||
export class ScrcpyOptionsLatest<
|
||||
TVideo extends boolean,
|
||||
> extends ScrcpyOptions3_1<TVideo> {
|
||||
constructor(init: ScrcpyOptions3_1.Init<TVideo>, version: string) {
|
||||
super(init, version);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ScrcpyOptionsLatest {
|
||||
export type Init = ScrcpyOptions3_1.Init;
|
||||
export type Init<TVideo extends boolean = boolean> =
|
||||
ScrcpyOptions3_1.Init<TVideo>;
|
||||
}
|
||||
|
||||
export {
|
||||
|
|
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
|
@ -163,9 +163,15 @@ importers:
|
|||
specifier: workspace:^
|
||||
version: link:../struct
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.10.10
|
||||
version: 22.10.10
|
||||
'@yume-chan/eslint-config':
|
||||
specifier: workspace:^
|
||||
version: link:../../toolchain/eslint-config
|
||||
'@yume-chan/test-runner':
|
||||
specifier: workspace:^
|
||||
version: link:../../toolchain/test-runner
|
||||
'@yume-chan/tsconfig':
|
||||
specifier: workspace:^
|
||||
version: link:../../toolchain/tsconfig
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue