mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-05 19:42:15 +02:00
wip: add scrcpy module
This commit is contained in:
parent
20b914fe6f
commit
c02dea23e8
31 changed files with 2678 additions and 10 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -60,6 +60,7 @@
|
|||
"typeof",
|
||||
"uifabric",
|
||||
"webadb",
|
||||
"webcodecs",
|
||||
"webpackbar",
|
||||
"websockify",
|
||||
"webusb",
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"@yume-chan/adb-backend-ws": "^0.0.9",
|
||||
"@yume-chan/async": "^2.1.4",
|
||||
"@yume-chan/event": "^0.0.9",
|
||||
"@yume-chan/scrcpy": "^0.0.9",
|
||||
"@yume-chan/struct": "^0.0.9",
|
||||
"mobx": "^6.3.3",
|
||||
"mobx-react-lite": "^3.2.1",
|
||||
|
@ -39,6 +40,7 @@
|
|||
"@types/react": "17.0.27",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"eslint": "7.32.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"eslint-config-next": "^11.1.3-canary.52",
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ const ROUTES = [
|
|||
url: '/shell',
|
||||
name: 'Interactive Shell',
|
||||
},
|
||||
{
|
||||
url: '/scrcpy',
|
||||
name: 'Scrcpy',
|
||||
},
|
||||
{
|
||||
url: '/tcpip',
|
||||
name: 'ADB over WiFi',
|
||||
|
|
|
@ -43,7 +43,7 @@ One USB device can only be accessed by one application at a time. Please make su
|
|||
Extra software is required to bridge the connection. See <ExternalLink href="https://github.com/yume-chan/ya-webadb/discussions/245#discussioncomment-384030">this discussion</ExternalLink>.
|
||||
|
||||
export default ({children}) => (
|
||||
<div style={{ padding: '0 16px' }}>
|
||||
<div style={{ height: '100%', padding: '0 16px', overflow: 'auto' }}>
|
||||
<Head>
|
||||
<title>WebADB</title>
|
||||
</Head>
|
||||
|
|
94
apps/demo/pages/scrcpy.tsx
Normal file
94
apps/demo/pages/scrcpy.tsx
Normal file
|
@ -0,0 +1,94 @@
|
|||
import { Stack } from "@fluentui/react";
|
||||
import { EventEmitter } from "@yume-chan/event";
|
||||
import serverUrl from 'file-loader!@yume-chan/scrcpy/bin/scrcpy-server';
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import React from "react";
|
||||
import { RouteStackProps } from "../utils";
|
||||
|
||||
export const ScrcpyServerVersion = '1.17';
|
||||
|
||||
class FetchWithProgress {
|
||||
public readonly promise: Promise<ArrayBuffer>;
|
||||
|
||||
private _downloaded = 0;
|
||||
public get downloaded() { return this._downloaded; }
|
||||
|
||||
private _total = 0;
|
||||
public get total() { return this._total; }
|
||||
|
||||
private progressEvent = new EventEmitter<[download: number, total: number]>();
|
||||
public get onProgress() { return this.progressEvent.event; }
|
||||
|
||||
public constructor(url: string) {
|
||||
this.promise = this.fetch(url);
|
||||
}
|
||||
|
||||
private async fetch(url: string) {
|
||||
const response = await window.fetch(url);
|
||||
this._total = Number.parseInt(response.headers.get('Content-Length') ?? '0', 10);
|
||||
this.progressEvent.fire([this._downloaded, this._total]);
|
||||
|
||||
const reader = response.body!.getReader();
|
||||
const chunks: Uint8Array[] = [];
|
||||
while (true) {
|
||||
const result = await reader.read();
|
||||
if (result.done) {
|
||||
break;
|
||||
}
|
||||
chunks.push(result.value);
|
||||
this._downloaded += result.value.byteLength;
|
||||
this.progressEvent.fire([this._downloaded, this._total]);
|
||||
}
|
||||
|
||||
this._total = chunks.reduce((result, item) => result + item.byteLength, 0);
|
||||
const result = new Uint8Array(this._total);
|
||||
let position = 0;
|
||||
for (const chunk of chunks) {
|
||||
result.set(chunk, position);
|
||||
position += chunk.byteLength;
|
||||
}
|
||||
return result.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
let cachedValue: FetchWithProgress | undefined;
|
||||
function fetchServer(onProgress?: (e: [downloaded: number, total: number]) => void) {
|
||||
if (!cachedValue) {
|
||||
cachedValue = new FetchWithProgress(serverUrl);
|
||||
}
|
||||
|
||||
if (onProgress) {
|
||||
cachedValue.onProgress(onProgress);
|
||||
onProgress([cachedValue.downloaded, cachedValue.total]);
|
||||
}
|
||||
|
||||
return cachedValue.promise;
|
||||
}
|
||||
|
||||
const DECODERS: { name: string; factory: DecoderConstructor; }[] = [{
|
||||
name: 'TinyH264 (Software)',
|
||||
factory: TinyH264DecoderWrapper,
|
||||
}];
|
||||
|
||||
if (typeof window.VideoDecoder === 'function') {
|
||||
DECODERS.push({
|
||||
name: 'WebCodecs',
|
||||
factory: WebCodecsDecoder,
|
||||
});
|
||||
}
|
||||
|
||||
const Scrcpy: NextPage = () => {
|
||||
return (
|
||||
<Stack {...RouteStackProps}>
|
||||
<Head>
|
||||
<title>Scrcpy - WebADB</title>
|
||||
</Head>
|
||||
|
||||
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default observer(Scrcpy);
|
4
apps/demo/types/file-loader.d.ts
vendored
Normal file
4
apps/demo/types/file-loader.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
declare module "file-loader!*" {
|
||||
const url: string;
|
||||
export default url;
|
||||
}
|
429
apps/demo/types/webcodecs.d.ts
vendored
Normal file
429
apps/demo/types/webcodecs.d.ts
vendored
Normal file
|
@ -0,0 +1,429 @@
|
|||
/////////////////////////////
|
||||
/// Window APIs
|
||||
/////////////////////////////
|
||||
|
||||
interface AudioDataCopyToOptions {
|
||||
format?: AudioSampleFormat;
|
||||
frameCount?: number;
|
||||
frameOffset?: number;
|
||||
planeIndex: number;
|
||||
}
|
||||
|
||||
interface AudioDataInit {
|
||||
data: BufferSource;
|
||||
format: AudioSampleFormat;
|
||||
numberOfChannels: number;
|
||||
numberOfFrames: number;
|
||||
sampleRate: number;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
interface AudioDecoderConfig {
|
||||
codec: string;
|
||||
description?: BufferSource;
|
||||
numberOfChannels: number;
|
||||
sampleRate: number;
|
||||
}
|
||||
|
||||
interface AudioDecoderInit {
|
||||
error: WebCodecsErrorCallback;
|
||||
output: AudioDataOutputCallback;
|
||||
}
|
||||
|
||||
interface AudioDecoderSupport {
|
||||
config?: AudioDecoderConfig;
|
||||
supported?: boolean;
|
||||
}
|
||||
|
||||
interface AudioEncoderConfig {
|
||||
bitrate?: number;
|
||||
codec: string;
|
||||
numberOfChannels?: number;
|
||||
sampleRate?: number;
|
||||
}
|
||||
|
||||
interface AudioEncoderInit {
|
||||
error: WebCodecsErrorCallback;
|
||||
output: EncodedAudioChunkOutputCallback;
|
||||
}
|
||||
|
||||
interface AudioEncoderSupport {
|
||||
config?: AudioEncoderConfig;
|
||||
supported?: boolean;
|
||||
}
|
||||
|
||||
interface EncodedAudioChunkInit {
|
||||
data: BufferSource;
|
||||
duration?: number;
|
||||
timestamp: number;
|
||||
type: EncodedAudioChunkType;
|
||||
}
|
||||
|
||||
interface EncodedAudioChunkMetadata {
|
||||
decoderConfig?: AudioDecoderConfig;
|
||||
}
|
||||
|
||||
interface EncodedVideoChunkInit {
|
||||
data: BufferSource;
|
||||
duration?: number;
|
||||
timestamp: number;
|
||||
type: EncodedVideoChunkType;
|
||||
}
|
||||
|
||||
interface EncodedVideoChunkMetadata {
|
||||
alphaSideData?: BufferSource;
|
||||
decoderConfig?: VideoDecoderConfig;
|
||||
svc?: SvcOutputMetadata;
|
||||
}
|
||||
|
||||
interface ImageDecodeOptions {
|
||||
completeFramesOnly?: boolean;
|
||||
frameIndex?: number;
|
||||
}
|
||||
|
||||
interface ImageDecodeResult {
|
||||
complete: boolean;
|
||||
image: VideoFrame;
|
||||
}
|
||||
|
||||
interface ImageDecoderInit {
|
||||
colorSpaceConversion?: ColorSpaceConversion;
|
||||
data: ImageBufferSource;
|
||||
desiredHeight?: number;
|
||||
desiredWidth?: number;
|
||||
preferAnimation?: boolean;
|
||||
premultiplyAlpha?: PremultiplyAlpha;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface PlaneLayout {
|
||||
offset: number;
|
||||
stride: number;
|
||||
}
|
||||
|
||||
interface SvcOutputMetadata {
|
||||
temporalLayerId?: number;
|
||||
}
|
||||
|
||||
interface VideoColorSpaceInit {
|
||||
fullRange?: boolean;
|
||||
matrix?: VideoMatrixCoefficients;
|
||||
primaries?: VideoColorPrimaries;
|
||||
transfer?: VideoTransferCharacteristics;
|
||||
}
|
||||
|
||||
interface VideoDecoderConfig {
|
||||
codec: string;
|
||||
codedHeight?: number;
|
||||
codedWidth?: number;
|
||||
colorSpace?: VideoColorSpaceInit;
|
||||
description?: BufferSource;
|
||||
displayAspectHeight?: number;
|
||||
displayAspectWidth?: number;
|
||||
hardwareAcceleration?: HardwareAcceleration;
|
||||
optimizeForLatency?: boolean;
|
||||
}
|
||||
|
||||
interface VideoDecoderInit {
|
||||
error: WebCodecsErrorCallback;
|
||||
output: VideoFrameOutputCallback;
|
||||
}
|
||||
|
||||
interface VideoDecoderSupport {
|
||||
config?: VideoDecoderConfig;
|
||||
supported?: boolean;
|
||||
}
|
||||
|
||||
interface VideoEncoderConfig {
|
||||
alpha?: AlphaOption;
|
||||
bitrate?: number;
|
||||
bitrateMode?: BitrateMode;
|
||||
codec: string;
|
||||
displayHeight?: number;
|
||||
displayWidth?: number;
|
||||
framerate?: number;
|
||||
hardwareAcceleration?: HardwareAcceleration;
|
||||
height: number;
|
||||
latencyMode?: LatencyMode;
|
||||
scalabilityMode?: string;
|
||||
width: number;
|
||||
}
|
||||
|
||||
interface VideoEncoderEncodeOptions {
|
||||
keyFrame?: boolean;
|
||||
}
|
||||
|
||||
interface VideoEncoderInit {
|
||||
error: WebCodecsErrorCallback;
|
||||
output: EncodedVideoChunkOutputCallback;
|
||||
}
|
||||
|
||||
interface VideoFrameBufferInit {
|
||||
codedHeight: number;
|
||||
codedWidth: number;
|
||||
colorSpace?: VideoColorSpaceInit;
|
||||
displayHeight?: number;
|
||||
displayWidth?: number;
|
||||
duration?: number;
|
||||
format: VideoPixelFormat;
|
||||
layout?: PlaneLayout[];
|
||||
timestamp: number;
|
||||
visibleRect?: DOMRectInit;
|
||||
}
|
||||
|
||||
interface VideoFrameCopyToOptions {
|
||||
layout?: PlaneLayout[];
|
||||
rect?: DOMRectInit;
|
||||
}
|
||||
|
||||
interface VideoFrameInit {
|
||||
alpha?: AlphaOption;
|
||||
displayHeight?: number;
|
||||
displayWidth?: number;
|
||||
duration?: number;
|
||||
timestamp?: number;
|
||||
visibleRect?: DOMRectInit;
|
||||
}
|
||||
|
||||
interface AudioData {
|
||||
readonly duration: number;
|
||||
readonly format: AudioSampleFormat | null;
|
||||
readonly numberOfChannels: number;
|
||||
readonly numberOfFrames: number;
|
||||
readonly sampleRate: number;
|
||||
readonly timestamp: number;
|
||||
allocationSize(options: AudioDataCopyToOptions): number;
|
||||
clone(): AudioData;
|
||||
close(): void;
|
||||
copyTo(destination: BufferSource, options: AudioDataCopyToOptions): void;
|
||||
}
|
||||
|
||||
declare var AudioData: {
|
||||
prototype: AudioData;
|
||||
new(init: AudioDataInit): AudioData;
|
||||
};
|
||||
|
||||
/** Available only in secure contexts. */
|
||||
interface AudioDecoder {
|
||||
readonly decodeQueueSize: number;
|
||||
readonly state: CodecState;
|
||||
close(): void;
|
||||
configure(config: AudioDecoderConfig): void;
|
||||
decode(chunk: EncodedAudioChunk): void;
|
||||
flush(): Promise<void>;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
declare var AudioDecoder: {
|
||||
prototype: AudioDecoder;
|
||||
new(init: AudioDecoderInit): AudioDecoder;
|
||||
isConfigSupported(config: AudioDecoderConfig): Promise<AudioDecoderSupport>;
|
||||
};
|
||||
|
||||
/** Available only in secure contexts. */
|
||||
interface AudioEncoder {
|
||||
readonly encodeQueueSize: number;
|
||||
readonly state: CodecState;
|
||||
close(): void;
|
||||
configure(config: AudioEncoderConfig): void;
|
||||
encode(data: AudioData): void;
|
||||
flush(): Promise<void>;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
declare var AudioEncoder: {
|
||||
prototype: AudioEncoder;
|
||||
new(init: AudioEncoderInit): AudioEncoder;
|
||||
isConfigSupported(config: AudioEncoderConfig): Promise<AudioEncoderSupport>;
|
||||
};
|
||||
|
||||
interface EncodedAudioChunk {
|
||||
readonly byteLength: number;
|
||||
readonly duration: number | null;
|
||||
readonly timestamp: number;
|
||||
readonly type: EncodedAudioChunkType;
|
||||
copyTo(destination: BufferSource): void;
|
||||
}
|
||||
|
||||
declare var EncodedAudioChunk: {
|
||||
prototype: EncodedAudioChunk;
|
||||
new(init: EncodedAudioChunkInit): EncodedAudioChunk;
|
||||
};
|
||||
|
||||
interface EncodedVideoChunk {
|
||||
readonly byteLength: number;
|
||||
readonly duration: number | null;
|
||||
readonly timestamp: number;
|
||||
readonly type: EncodedVideoChunkType;
|
||||
copyTo(destination: BufferSource): void;
|
||||
}
|
||||
|
||||
declare var EncodedVideoChunk: {
|
||||
prototype: EncodedVideoChunk;
|
||||
new(init: EncodedVideoChunkInit): EncodedVideoChunk;
|
||||
};
|
||||
|
||||
/** Available only in secure contexts. */
|
||||
interface ImageDecoder {
|
||||
readonly complete: boolean;
|
||||
readonly completed: Promise<undefined>;
|
||||
readonly tracks: ImageTrackList;
|
||||
close(): void;
|
||||
decode(options?: ImageDecodeOptions): Promise<ImageDecodeResult>;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
declare var ImageDecoder: {
|
||||
prototype: ImageDecoder;
|
||||
new(init: ImageDecoderInit): ImageDecoder;
|
||||
isTypeSupported(type: string): Promise<boolean>;
|
||||
};
|
||||
|
||||
interface ImageTrackEventMap {
|
||||
"change": Event;
|
||||
}
|
||||
|
||||
interface ImageTrack extends EventTarget {
|
||||
readonly animated: boolean;
|
||||
readonly frameCount: number;
|
||||
onchange: ((this: ImageTrack, ev: Event) => any) | null;
|
||||
readonly repetitionCount: number;
|
||||
selected: boolean;
|
||||
addEventListener<K extends keyof ImageTrackEventMap>(type: K, listener: (this: ImageTrack, ev: ImageTrackEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
||||
removeEventListener<K extends keyof ImageTrackEventMap>(type: K, listener: (this: ImageTrack, ev: ImageTrackEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
||||
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
||||
}
|
||||
|
||||
declare var ImageTrack: {
|
||||
prototype: ImageTrack;
|
||||
new(): ImageTrack;
|
||||
};
|
||||
|
||||
interface ImageTrackList {
|
||||
readonly length: number;
|
||||
readonly ready: Promise<undefined>;
|
||||
readonly selectedIndex: number;
|
||||
readonly selectedTrack: ImageTrack | null;
|
||||
[index: number]: ImageTrack;
|
||||
}
|
||||
|
||||
declare var ImageTrackList: {
|
||||
prototype: ImageTrackList;
|
||||
new(): ImageTrackList;
|
||||
};
|
||||
|
||||
interface VideoColorSpace {
|
||||
readonly fullRange: boolean | null;
|
||||
readonly matrix: VideoMatrixCoefficients | null;
|
||||
readonly primaries: VideoColorPrimaries | null;
|
||||
readonly transfer: VideoTransferCharacteristics | null;
|
||||
toJSON(): VideoColorSpaceInit;
|
||||
}
|
||||
|
||||
declare var VideoColorSpace: {
|
||||
prototype: VideoColorSpace;
|
||||
new(init?: VideoColorSpaceInit): VideoColorSpace;
|
||||
};
|
||||
|
||||
/** Available only in secure contexts. */
|
||||
interface VideoDecoder {
|
||||
readonly decodeQueueSize: number;
|
||||
readonly state: CodecState;
|
||||
close(): void;
|
||||
configure(config: VideoDecoderConfig): void;
|
||||
decode(chunk: EncodedVideoChunk): void;
|
||||
flush(): Promise<void>;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
declare var VideoDecoder: {
|
||||
prototype: VideoDecoder;
|
||||
new(init: VideoDecoderInit): VideoDecoder;
|
||||
isConfigSupported(config: VideoDecoderConfig): Promise<VideoDecoderSupport>;
|
||||
};
|
||||
|
||||
/** Available only in secure contexts. */
|
||||
interface VideoEncoder {
|
||||
readonly encodeQueueSize: number;
|
||||
readonly state: CodecState;
|
||||
close(): void;
|
||||
configure(config: VideoEncoderConfig): void;
|
||||
encode(frame: VideoFrame, options?: VideoEncoderEncodeOptions): void;
|
||||
flush(): Promise<void>;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
declare var VideoEncoder: {
|
||||
prototype: VideoEncoder;
|
||||
new(init: VideoEncoderInit): VideoEncoder;
|
||||
isConfigSupported(config: VideoEncoderConfig): Promise<boolean>;
|
||||
};
|
||||
|
||||
interface VideoFrame {
|
||||
readonly codedHeight: number;
|
||||
readonly codedRect: DOMRectReadOnly | null;
|
||||
readonly codedWidth: number;
|
||||
readonly colorSpace: VideoColorSpace;
|
||||
readonly displayHeight: number;
|
||||
readonly displayWidth: number;
|
||||
readonly duration: number | null;
|
||||
readonly format: VideoPixelFormat | null;
|
||||
readonly timestamp: number | null;
|
||||
readonly visibleRect: DOMRectReadOnly | null;
|
||||
allocationSize(options?: VideoFrameCopyToOptions): number;
|
||||
clone(): VideoFrame;
|
||||
close(): void;
|
||||
copyTo(destination: BufferSource, options?: VideoFrameCopyToOptions): Promise<PlaneLayout[]>;
|
||||
}
|
||||
|
||||
declare var VideoFrame: {
|
||||
prototype: VideoFrame;
|
||||
new(image: CanvasImageSource, init?: VideoFrameInit): VideoFrame;
|
||||
new(data: BufferSource, init: VideoFrameBufferInit): VideoFrame;
|
||||
};
|
||||
|
||||
interface AudioDataOutputCallback {
|
||||
(output: AudioData): void;
|
||||
}
|
||||
|
||||
interface EncodedAudioChunkOutputCallback {
|
||||
(output: EncodedAudioChunk, metadata?: EncodedAudioChunkMetadata): void;
|
||||
}
|
||||
|
||||
interface EncodedVideoChunkOutputCallback {
|
||||
(chunk: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata): void;
|
||||
}
|
||||
|
||||
interface VideoFrameOutputCallback {
|
||||
(output: VideoFrame): void;
|
||||
}
|
||||
|
||||
interface WebCodecsErrorCallback {
|
||||
(error: DOMException): void;
|
||||
}
|
||||
|
||||
interface HTMLElementTagNameMap {
|
||||
}
|
||||
|
||||
interface HTMLElementDeprecatedTagNameMap {
|
||||
}
|
||||
|
||||
interface SVGElementTagNameMap {
|
||||
}
|
||||
|
||||
/** @deprecated Directly use HTMLElementTagNameMap or SVGElementTagNameMap as appropriate, instead. */
|
||||
type ElementTagNameMap = HTMLElementTagNameMap & Pick<SVGElementTagNameMap, Exclude<keyof SVGElementTagNameMap, keyof HTMLElementTagNameMap>>;
|
||||
|
||||
type ImageBufferSource = BufferSource | ReadableStream;
|
||||
type AlphaOption = "discard" | "keep";
|
||||
type AudioSampleFormat = "f32" | "f32-planar" | "s16" | "s16-planar" | "s32" | "s32-planar" | "u8" | "u8-planar";
|
||||
type CodecState = "closed" | "configured" | "unconfigured";
|
||||
type EncodedAudioChunkType = "delta" | "key";
|
||||
type EncodedVideoChunkType = "delta" | "key";
|
||||
type HardwareAcceleration = "no-preference" | "prefer-hardware" | "prefer-software";
|
||||
type LatencyMode = "quality" | "realtime";
|
||||
type VideoColorPrimaries = "bt470bg" | "bt709" | "smpte170m";
|
||||
type VideoMatrixCoefficients = "bt470bg" | "bt709" | "rgb" | "smpte170m";
|
||||
type VideoPixelFormat = "BGRA" | "BGRX" | "I420" | "I420A" | "I422" | "I444" | "NV12" | "RGBA" | "RGBX";
|
||||
type VideoTransferCharacteristics = "bt709" | "iec61966-2-1" | "smpte170m";
|
13
apps/demo/utils/decoder/types.ts
Normal file
13
apps/demo/utils/decoder/types.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { Disposable } from "@yume-chan/event";
|
||||
import { ValueOrPromise } from "@yume-chan/struct";
|
||||
import { FrameSize } from "@yume-chan/scrcpy";
|
||||
|
||||
export interface Decoder extends Disposable {
|
||||
configure(config: FrameSize): ValueOrPromise<void>;
|
||||
|
||||
decode(data: BufferSource): ValueOrPromise<void>;
|
||||
}
|
||||
|
||||
export interface DecoderConstructor {
|
||||
new(canvas: HTMLCanvasElement): Decoder;
|
||||
}
|
48
apps/demo/utils/decoder/webcodes-decoder.ts
Normal file
48
apps/demo/utils/decoder/webcodes-decoder.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
import { FrameSize } from "@yume-chan/scrcpy";
|
||||
import { ValueOrPromise } from "@yume-chan/struct";
|
||||
import { Decoder } from './types';
|
||||
|
||||
function toHex(value: number) {
|
||||
return value.toString(16).padStart(2, '0').toUpperCase();
|
||||
}
|
||||
|
||||
export class WebCodecsDecoder implements Decoder {
|
||||
private decoder: VideoDecoder;
|
||||
private context: CanvasRenderingContext2D;
|
||||
|
||||
public constructor(canvas: HTMLCanvasElement) {
|
||||
this.context = canvas.getContext('2d')!;
|
||||
this.decoder = new VideoDecoder({
|
||||
output: (frame) => {
|
||||
this.context.drawImage(frame, 0, 0);
|
||||
frame.close();
|
||||
},
|
||||
error() { },
|
||||
});
|
||||
}
|
||||
|
||||
public configure(config: FrameSize): ValueOrPromise<void> {
|
||||
const { sequenceParameterSet: { profile_idc, constraint_set, level_idc } } = config;
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc6381#section-3.3
|
||||
// ISO Base Media File Format Name Space
|
||||
const codec = `avc1.${[profile_idc, constraint_set, level_idc].map(toHex).join('')}`;
|
||||
this.decoder.configure({
|
||||
codec: codec,
|
||||
optimizeForLatency: true,
|
||||
});
|
||||
}
|
||||
|
||||
decode(data: BufferSource): ValueOrPromise<void> {
|
||||
this.decoder.decode(new EncodedVideoChunk({
|
||||
type: 'key',
|
||||
timestamp: 0,
|
||||
data,
|
||||
}));
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.decoder.close();
|
||||
}
|
||||
}
|
|
@ -182,6 +182,15 @@
|
|||
"enableParallelism": true,
|
||||
"incremental": true,
|
||||
"watchForChanges": true
|
||||
},
|
||||
{
|
||||
"commandKind": "bulk",
|
||||
"name": "postinstall",
|
||||
"summary": "Run all postinstall scripts",
|
||||
"ignoreMissingScript": true,
|
||||
"enableParallelism": true,
|
||||
"ignoreDependencyOrder": true,
|
||||
"safeForSimultaneousRushProcesses": true
|
||||
}
|
||||
],
|
||||
/**
|
||||
|
|
567
common/config/rush/pnpm-lock.yaml
generated
567
common/config/rush/pnpm-lock.yaml
generated
|
@ -13,20 +13,23 @@ dependencies:
|
|||
'@rush-temp/adb-backend-ws': file:projects/adb-backend-ws.tgz
|
||||
'@rush-temp/demo': file:projects/demo.tgz_@mdx-js+react@1.6.22
|
||||
'@rush-temp/event': file:projects/event.tgz
|
||||
'@rush-temp/scrcpy': file:projects/scrcpy.tgz
|
||||
'@rush-temp/struct': file:projects/struct.tgz
|
||||
'@rush-temp/ts-package-builder': file:projects/ts-package-builder.tgz
|
||||
'@rush-temp/unofficial-adb-book': file:projects/unofficial-adb-book.tgz_@types+react@17.0.27
|
||||
'@svgr/webpack': 5.5.0
|
||||
'@types/dom-webcodecs': 0.1.2
|
||||
'@types/jest': 26.0.24
|
||||
'@types/node': 16.9.1
|
||||
'@types/react': 17.0.27
|
||||
'@types/w3c-web-usb': 1.0.5
|
||||
'@types/wicg-file-system-access': 2020.9.4
|
||||
'@yume-chan/async': 2.1.4
|
||||
clsx: 1.1.1
|
||||
copy-webpack-plugin: 9.0.1
|
||||
eslint: 7.32.0
|
||||
eslint-config-next: 11.1.3-canary.52_eslint@7.32.0+next@11.1.2
|
||||
file-loader: 6.2.0
|
||||
gh-release-fetch: 2.0.4
|
||||
jest: 26.6.3
|
||||
json5: 2.2.0
|
||||
mini-svg-data-uri: 1.3.3
|
||||
|
@ -37,6 +40,7 @@ dependencies:
|
|||
plantuml-encoder: 1.4.0
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2_react@17.0.2
|
||||
streamsaver: 2.0.5
|
||||
tinyh264: 0.0.7
|
||||
tslib: 2.3.1
|
||||
unist-util-visit: 2.0.3
|
||||
|
@ -2716,6 +2720,12 @@ packages:
|
|||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
|
||||
/@sindresorhus/is/0.7.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
|
||||
/@sinonjs/commons/1.8.3:
|
||||
dependencies:
|
||||
type-detect: 4.0.8
|
||||
|
@ -2904,6 +2914,24 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==
|
||||
/@types/decompress/4.2.4:
|
||||
dependencies:
|
||||
'@types/node': 16.9.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-/C8kTMRTNiNuWGl5nEyKbPiMv6HA+0RbEXzFhFBEzASM6+oa4tJro9b8nj7eRlOFfuLdzUU+DS/GPDlvvzMOhA==
|
||||
/@types/dom-webcodecs/0.1.2:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-e5DuY7mZedrV8rrgEnm1gPmy4h2x11ryUTnkjmbdR6SWGCS3Qvz1ZJudtBIrbPNEhnpGM634SL7ZHQDz7dsBCg==
|
||||
/@types/download/8.0.1:
|
||||
dependencies:
|
||||
'@types/decompress': 4.2.4
|
||||
'@types/got': 8.3.6
|
||||
'@types/node': 16.9.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-t5DjMD6Y1DxjXtEHl7Kt+nQn9rOmVLYD8p4Swrcc5QpgyqyqR2gXTIK6RwwMnNeFJ+ZIiIW789fQKzCrK7AOFA==
|
||||
/@types/eslint-scope/3.7.1:
|
||||
dependencies:
|
||||
'@types/eslint': 7.28.0
|
||||
|
@ -2943,6 +2971,12 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA==
|
||||
/@types/got/8.3.6:
|
||||
dependencies:
|
||||
'@types/node': 16.9.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-nvLlj+831dhdm4LR2Ly+HTpdLyBaMynoOr6wpIxS19d/bPeHQxFU5XQ6Gp6ohBpxvCWZM1uHQIC2+ySRH1rGrQ==
|
||||
/@types/graceful-fs/4.1.5:
|
||||
dependencies:
|
||||
'@types/node': 15.14.2
|
||||
|
@ -3000,6 +3034,13 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
|
||||
/@types/node-fetch/2.5.12:
|
||||
dependencies:
|
||||
'@types/node': 16.9.1
|
||||
form-data: 3.0.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==
|
||||
/@types/node/15.14.2:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -3050,6 +3091,10 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
|
||||
/@types/semver/7.3.8:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-D/2EJvAlCEtYFEYmmlGwbGXuK886HzyCc3nZX/tkFTQdEU8jZDAgiv08P162yB17y4ZXZoq7yFAnW4GDBb9Now==
|
||||
/@types/stack-utils/2.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -3532,6 +3577,14 @@ packages:
|
|||
node: '>= 8'
|
||||
resolution:
|
||||
integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
|
||||
/archive-type/4.0.0:
|
||||
dependencies:
|
||||
file-type: 4.4.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA=
|
||||
/arg/5.0.0:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -3958,6 +4011,13 @@ packages:
|
|||
optional: true
|
||||
resolution:
|
||||
integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
|
||||
/bl/1.2.3:
|
||||
dependencies:
|
||||
readable-stream: 2.3.7
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==
|
||||
/bluebird/3.7.2:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -4143,6 +4203,25 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
|
||||
/buffer-alloc-unsafe/1.1.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
|
||||
/buffer-alloc/1.2.0:
|
||||
dependencies:
|
||||
buffer-alloc-unsafe: 1.1.0
|
||||
buffer-fill: 1.0.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
|
||||
/buffer-crc32/0.2.13:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||
/buffer-fill/1.0.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-+PeLdniYiO858gXNY39o5wISKyw=
|
||||
/buffer-from/1.1.1:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -4202,6 +4281,18 @@ packages:
|
|||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
|
||||
/cacheable-request/2.1.4:
|
||||
dependencies:
|
||||
clone-response: 1.0.2
|
||||
get-stream: 3.0.0
|
||||
http-cache-semantics: 3.8.1
|
||||
keyv: 3.0.0
|
||||
lowercase-keys: 1.0.0
|
||||
normalize-url: 2.0.1
|
||||
responselike: 1.0.2
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=
|
||||
/cacheable-request/6.1.0:
|
||||
dependencies:
|
||||
clone-response: 1.0.2
|
||||
|
@ -5229,6 +5320,64 @@ packages:
|
|||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
|
||||
/decompress-tar/4.1.1:
|
||||
dependencies:
|
||||
file-type: 5.2.0
|
||||
is-stream: 1.1.0
|
||||
tar-stream: 1.6.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
|
||||
/decompress-tarbz2/4.1.1:
|
||||
dependencies:
|
||||
decompress-tar: 4.1.1
|
||||
file-type: 6.2.0
|
||||
is-stream: 1.1.0
|
||||
seek-bzip: 1.0.6
|
||||
unbzip2-stream: 1.4.3
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
|
||||
/decompress-targz/4.1.1:
|
||||
dependencies:
|
||||
decompress-tar: 4.1.1
|
||||
file-type: 5.2.0
|
||||
is-stream: 1.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
|
||||
/decompress-unzip/4.0.1:
|
||||
dependencies:
|
||||
file-type: 3.9.0
|
||||
get-stream: 2.3.1
|
||||
pify: 2.3.0
|
||||
yauzl: 2.10.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-3qrM39FK6vhVePczroIQ+bSEj2k=
|
||||
/decompress/4.2.1:
|
||||
dependencies:
|
||||
decompress-tar: 4.1.1
|
||||
decompress-tarbz2: 4.1.1
|
||||
decompress-targz: 4.1.1
|
||||
decompress-unzip: 4.0.1
|
||||
graceful-fs: 4.2.6
|
||||
make-dir: 1.3.0
|
||||
pify: 2.3.0
|
||||
strip-dirs: 2.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==
|
||||
/deep-equal/1.1.1:
|
||||
dependencies:
|
||||
is-arguments: 1.1.0
|
||||
|
@ -5553,6 +5702,24 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==
|
||||
/download/8.0.0:
|
||||
dependencies:
|
||||
archive-type: 4.0.0
|
||||
content-disposition: 0.5.3
|
||||
decompress: 4.2.1
|
||||
ext-name: 5.0.0
|
||||
file-type: 11.1.0
|
||||
filenamify: 3.0.0
|
||||
get-stream: 4.1.0
|
||||
got: 8.3.2
|
||||
make-dir: 2.1.0
|
||||
p-event: 2.3.1
|
||||
pify: 4.0.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=10'
|
||||
resolution:
|
||||
integrity: sha512-ASRY5QhDk7FK+XrQtQyvhpDKanLluEEQtWl/J7Lxuf/b+i8RYh997QeXvL85xitrmRKVlx9c7eTrcRdq2GS4eA==
|
||||
/duplexer/0.1.2:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -6250,6 +6417,23 @@ packages:
|
|||
node: '>= 0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
|
||||
/ext-list/2.2.2:
|
||||
dependencies:
|
||||
mime-db: 1.48.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==
|
||||
/ext-name/5.0.0:
|
||||
dependencies:
|
||||
ext-list: 2.2.2
|
||||
sort-keys-length: 1.0.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==
|
||||
/extend-shallow/2.0.1:
|
||||
dependencies:
|
||||
is-extendable: 0.1.1
|
||||
|
@ -6358,6 +6542,12 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==
|
||||
/fd-slicer/1.1.0:
|
||||
dependencies:
|
||||
pend: 1.2.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
|
||||
/feed/4.2.2:
|
||||
dependencies:
|
||||
xml-js: 1.6.11
|
||||
|
@ -6405,11 +6595,57 @@ packages:
|
|||
webpack: ^4.0.0 || ^5.0.0
|
||||
resolution:
|
||||
integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
|
||||
/file-type/11.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-rM0UO7Qm9K7TWTtA6AShI/t7H5BPjDeGVDaNyg9BjHAj3PysKy7+8C8D137R88jnR3rFJZQB/tFgydl5sN5m7g==
|
||||
/file-type/3.9.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-JXoHg4TR24CHvESdEH1SpSZyuek=
|
||||
/file-type/4.4.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-G2AOX8ofvcboDApwxxyNul95BsU=
|
||||
/file-type/5.2.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-LdvqfHP/42No365J3DOMBYwritY=
|
||||
/file-type/6.2.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
|
||||
/file-uri-to-path/1.0.0:
|
||||
dev: false
|
||||
optional: true
|
||||
resolution:
|
||||
integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
/filename-reserved-regex/2.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-q/c9+rc10EVECr/qLZHzieu/oik=
|
||||
/filenamify/3.0.0:
|
||||
dependencies:
|
||||
filename-reserved-regex: 2.0.0
|
||||
strip-outer: 1.0.1
|
||||
trim-repeated: 1.0.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-5EFZ//MsvJgXjBAFJ+Bh2YaCTRF/VP1YOmGrgt+KJ4SFRLjI87EIdwLLuT6wQX0I4F9W41xutobzczjsOKlI/g==
|
||||
/filesize/6.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -6588,6 +6824,17 @@ packages:
|
|||
node: '>= 0.6'
|
||||
resolution:
|
||||
integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||
/from2/2.3.0:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
readable-stream: 2.3.7
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
|
||||
/fs-constants/1.0.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||
/fs-extra/10.0.0:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.6
|
||||
|
@ -6669,6 +6916,21 @@ packages:
|
|||
node: '>=8.0.0'
|
||||
resolution:
|
||||
integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
|
||||
/get-stream/2.3.1:
|
||||
dependencies:
|
||||
object-assign: 4.1.1
|
||||
pinkie-promise: 2.0.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=
|
||||
/get-stream/3.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
|
||||
/get-stream/4.1.0:
|
||||
dependencies:
|
||||
pump: 3.0.0
|
||||
|
@ -6706,6 +6968,20 @@ packages:
|
|||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
|
||||
/gh-release-fetch/2.0.4:
|
||||
dependencies:
|
||||
'@types/download': 8.0.1
|
||||
'@types/node-fetch': 2.5.12
|
||||
'@types/semver': 7.3.8
|
||||
download: 8.0.0
|
||||
make-dir: 3.1.0
|
||||
node-fetch: 2.6.1
|
||||
semver: 7.3.5
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=10'
|
||||
resolution:
|
||||
integrity: sha512-PALrCv6MuxEKsj5Oz9G81iU6pxvoxgpSnwbtIqAkQ6m6fioFicNznZUl/aOW92rK2k8cuaM48Rd59G7eV2QsTA==
|
||||
/github-slugger/1.3.0:
|
||||
dependencies:
|
||||
emoji-regex: 6.1.1
|
||||
|
@ -6828,6 +7104,30 @@ packages:
|
|||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
|
||||
/got/8.3.2:
|
||||
dependencies:
|
||||
'@sindresorhus/is': 0.7.0
|
||||
cacheable-request: 2.1.4
|
||||
decompress-response: 3.3.0
|
||||
duplexer3: 0.1.4
|
||||
get-stream: 3.0.0
|
||||
into-stream: 3.1.0
|
||||
is-retry-allowed: 1.2.0
|
||||
isurl: 1.0.0
|
||||
lowercase-keys: 1.0.1
|
||||
mimic-response: 1.0.1
|
||||
p-cancelable: 0.4.1
|
||||
p-timeout: 2.0.1
|
||||
pify: 3.0.0
|
||||
safe-buffer: 5.2.1
|
||||
timed-out: 4.0.1
|
||||
url-parse-lax: 3.0.0
|
||||
url-to-options: 1.0.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==
|
||||
/got/9.6.0:
|
||||
dependencies:
|
||||
'@sindresorhus/is': 0.14.0
|
||||
|
@ -6903,12 +7203,22 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
/has-symbol-support-x/1.4.2:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==
|
||||
/has-symbols/1.0.2:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 0.4'
|
||||
resolution:
|
||||
integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
|
||||
/has-to-string-tag-x/1.4.1:
|
||||
dependencies:
|
||||
has-symbol-support-x: 1.4.2
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==
|
||||
/has-tostringtag/1.0.0:
|
||||
dependencies:
|
||||
has-symbols: 1.0.2
|
||||
|
@ -7195,6 +7505,10 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
|
||||
/http-cache-semantics/3.8.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==
|
||||
/http-cache-semantics/4.1.0:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -7468,6 +7782,15 @@ packages:
|
|||
node: '>= 0.10'
|
||||
resolution:
|
||||
integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
|
||||
/into-stream/3.1.0:
|
||||
dependencies:
|
||||
from2: 2.3.0
|
||||
p-is-promise: 1.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=
|
||||
/ip-regex/2.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -7740,6 +8063,10 @@ packages:
|
|||
node: '>= 0.4'
|
||||
resolution:
|
||||
integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
|
||||
/is-natural-number/4.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=
|
||||
/is-negative-zero/2.0.1:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -7784,6 +8111,10 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
|
||||
/is-object/1.0.2:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==
|
||||
/is-path-cwd/2.2.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -7812,6 +8143,12 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||
/is-plain-obj/1.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
|
||||
/is-plain-obj/2.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -7858,6 +8195,12 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
|
||||
/is-retry-allowed/1.2.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
|
||||
/is-root/2.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -8028,6 +8371,15 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==
|
||||
/isurl/1.0.0:
|
||||
dependencies:
|
||||
has-to-string-tag-x: 1.4.1
|
||||
is-object: 1.0.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 4'
|
||||
resolution:
|
||||
integrity: sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==
|
||||
/jest-changed-files/26.6.2:
|
||||
dependencies:
|
||||
'@jest/types': 26.6.2
|
||||
|
@ -8605,6 +8957,12 @@ packages:
|
|||
node: '>=4.0'
|
||||
resolution:
|
||||
integrity: sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==
|
||||
/keyv/3.0.0:
|
||||
dependencies:
|
||||
json-buffer: 3.0.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==
|
||||
/keyv/3.1.0:
|
||||
dependencies:
|
||||
json-buffer: 3.0.0
|
||||
|
@ -8889,6 +9247,12 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
|
||||
/lowercase-keys/1.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=
|
||||
/lowercase-keys/1.0.1:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -8909,6 +9273,23 @@ packages:
|
|||
node: '>=10'
|
||||
resolution:
|
||||
integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
|
||||
/make-dir/1.3.0:
|
||||
dependencies:
|
||||
pify: 3.0.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
|
||||
/make-dir/2.1.0:
|
||||
dependencies:
|
||||
pify: 4.0.1
|
||||
semver: 5.7.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
|
||||
/make-dir/3.1.0:
|
||||
dependencies:
|
||||
semver: 6.3.0
|
||||
|
@ -9568,6 +9949,16 @@ packages:
|
|||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
|
||||
/normalize-url/2.0.1:
|
||||
dependencies:
|
||||
prepend-http: 2.0.0
|
||||
query-string: 5.1.1
|
||||
sort-keys: 2.0.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
|
||||
/normalize-url/4.5.1:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -9815,6 +10206,12 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
|
||||
/p-cancelable/0.4.1:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==
|
||||
/p-cancelable/1.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -9827,12 +10224,26 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==
|
||||
/p-event/2.3.1:
|
||||
dependencies:
|
||||
p-timeout: 2.0.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==
|
||||
/p-finally/1.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
|
||||
/p-is-promise/1.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=
|
||||
/p-limit/1.3.0:
|
||||
dependencies:
|
||||
p-try: 1.0.0
|
||||
|
@ -9911,6 +10322,14 @@ packages:
|
|||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==
|
||||
/p-timeout/2.0.1:
|
||||
dependencies:
|
||||
p-finally: 1.0.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==
|
||||
/p-try/1.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -10115,6 +10534,10 @@ packages:
|
|||
node: '>=0.12'
|
||||
resolution:
|
||||
integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
|
||||
/pend/1.2.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-elfrVQpng/kRUzH89GY9XI4AelA=
|
||||
/picomatch/2.3.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -10892,6 +11315,16 @@ packages:
|
|||
node: '>=0.6'
|
||||
resolution:
|
||||
integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
/query-string/5.1.1:
|
||||
dependencies:
|
||||
decode-uri-component: 0.2.0
|
||||
object-assign: 4.1.1
|
||||
strict-uri-encode: 1.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
|
||||
/querystring-es3/0.2.1:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -11734,6 +12167,13 @@ packages:
|
|||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==
|
||||
/seek-bzip/1.0.6:
|
||||
dependencies:
|
||||
commander: 2.20.3
|
||||
dev: false
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==
|
||||
/select-hose/2.0.0:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -12044,6 +12484,30 @@ packages:
|
|||
node: '>= 6.3.0'
|
||||
resolution:
|
||||
integrity: sha512-YP5W/h4Sid/YP7Lp87ejJ5jP13/Mtqt2vx33XyhO+IAugKlufRPbOrPlIiEUuxmpNBSBd3EeeQpFhdu3RfI2Ag==
|
||||
/sort-keys-length/1.0.1:
|
||||
dependencies:
|
||||
sort-keys: 1.1.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=
|
||||
/sort-keys/1.1.2:
|
||||
dependencies:
|
||||
is-plain-obj: 1.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-RBttTTRnmPG05J6JIK37oOVD+a0=
|
||||
/sort-keys/2.0.0:
|
||||
dependencies:
|
||||
is-plain-obj: 1.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
|
||||
/source-list-map/2.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -12254,6 +12718,12 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-KIWtBvi8A6FiFZGNSyuIZRZM6C8AvnWTiCx/TYa7so420vC5sQwcBKkdqInuGWoWMfeWy/P+/cRqMtWVf4RW9w==
|
||||
/strict-uri-encode/1.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
|
||||
/string-hash/1.1.3:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -12396,6 +12866,12 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
|
||||
/strip-dirs/2.1.0:
|
||||
dependencies:
|
||||
is-natural-number: 4.0.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
|
||||
/strip-eof/1.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -12420,6 +12896,14 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
/strip-outer/1.0.1:
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==
|
||||
/style-to-object/0.3.0:
|
||||
dependencies:
|
||||
inline-style-parser: 0.1.1
|
||||
|
@ -12582,6 +13066,20 @@ packages:
|
|||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
|
||||
/tar-stream/1.6.2:
|
||||
dependencies:
|
||||
bl: 1.2.3
|
||||
buffer-alloc: 1.2.0
|
||||
end-of-stream: 1.4.4
|
||||
fs-constants: 1.0.0
|
||||
readable-stream: 2.3.7
|
||||
to-buffer: 1.1.1
|
||||
xtend: 4.0.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 0.8.0'
|
||||
resolution:
|
||||
integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
|
||||
/terminal-link/2.1.1:
|
||||
dependencies:
|
||||
ansi-escapes: 4.3.2
|
||||
|
@ -12647,10 +13145,20 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==
|
||||
/through/2.3.8:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||
/thunky/1.1.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
|
||||
/timed-out/4.0.1:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=
|
||||
/timers-browserify/2.0.12:
|
||||
dependencies:
|
||||
setimmediate: 1.0.5
|
||||
|
@ -12683,6 +13191,10 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
|
||||
/to-buffer/1.1.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
|
||||
/to-fast-properties/2.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -12767,6 +13279,14 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==
|
||||
/trim-repeated/1.0.0:
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-42RqLqTokTEr9+rObPsFOAvAHCE=
|
||||
/trim-trailing-lines/1.1.4:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -12948,6 +13468,13 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==
|
||||
/unbzip2-stream/1.4.3:
|
||||
dependencies:
|
||||
buffer: 5.6.0
|
||||
through: 2.3.8
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
|
||||
/unherit/1.1.3:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
|
@ -13194,6 +13721,12 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==
|
||||
/url-to-options/1.0.1:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 4'
|
||||
resolution:
|
||||
integrity: sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=
|
||||
/url/0.11.0:
|
||||
dependencies:
|
||||
punycode: 1.3.2
|
||||
|
@ -13912,6 +14445,13 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
|
||||
/yauzl/2.10.0:
|
||||
dependencies:
|
||||
buffer-crc32: 0.2.13
|
||||
fd-slicer: 1.1.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||
/yocto-queue/0.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -13973,6 +14513,7 @@ packages:
|
|||
'@fluentui/react-hooks': 8.3.4_b094b78811fc8d2f00a90f13d0251fb6
|
||||
'@mdx-js/loader': 1.6.22_react@17.0.2
|
||||
'@next/mdx': 11.1.2_f56c41adb6190c4680be4a1c0222355d
|
||||
'@types/dom-webcodecs': 0.1.2
|
||||
'@types/filesystem': 0.0.32
|
||||
'@types/react': 17.0.27
|
||||
'@types/wicg-file-system-access': 2020.9.4
|
||||
|
@ -13980,6 +14521,7 @@ packages:
|
|||
copy-webpack-plugin: 9.0.1
|
||||
eslint: 7.32.0
|
||||
eslint-config-next: 11.1.3-canary.52_82ad98da5e129ae9d310451a179848d9
|
||||
file-loader: 6.2.0
|
||||
mobx: 6.3.3
|
||||
mobx-react-lite: 3.2.1_8aec6830c9f393111667240500a3b25c
|
||||
next: 11.1.2_bd7797d63db473de6e2318609a63c933
|
||||
|
@ -14000,7 +14542,7 @@ packages:
|
|||
peerDependencies:
|
||||
'@mdx-js/react': '*'
|
||||
resolution:
|
||||
integrity: sha512-adqQuE89q3q1oZWmQOhxsd1fcXBx1/1W+uaXNPvI2Rz+vpz1CkDacSaZbgm84Ohp2L7yncyD+cJbcB8enibhpQ==
|
||||
integrity: sha512-yQ64kuvfu4zYm4ZajZmSBH/mu4xK54yOuKLfQXbX1UDDxM/Kn3hj2ZWuqXMqrCUf64R1t9PP8/XFKYgsvJkvew==
|
||||
tarball: file:projects/demo.tgz
|
||||
version: 0.0.0
|
||||
file:projects/event.tgz:
|
||||
|
@ -14015,6 +14557,19 @@ packages:
|
|||
integrity: sha512-xKXpC79xXSyXj/rzUHlyCAFT5X9mUejkCcF2DarGiRw+gT7tug6na+EvUz2dZUVCtIzyUJFtuhbS39n0qy0A+g==
|
||||
tarball: file:projects/event.tgz
|
||||
version: 0.0.0
|
||||
file:projects/scrcpy.tgz:
|
||||
dependencies:
|
||||
'@yume-chan/async': 2.1.4
|
||||
gh-release-fetch: 2.0.4
|
||||
jest: 26.6.3
|
||||
tslib: 2.3.1
|
||||
typescript: 4.4.3
|
||||
dev: false
|
||||
name: '@rush-temp/scrcpy'
|
||||
resolution:
|
||||
integrity: sha512-46RuBeh15le2Jo335bNRl46u4ZKAVqX7chrduwiY2i1wqP2Uiw7w3pa0Ygkdb3zIbOoZG6FpTX3xPI+MzJAjhQ==
|
||||
tarball: file:projects/scrcpy.tgz
|
||||
version: 0.0.0
|
||||
file:projects/struct.tgz:
|
||||
dependencies:
|
||||
'@types/node': 16.9.1
|
||||
|
@ -14078,20 +14633,23 @@ specifiers:
|
|||
'@rush-temp/adb-backend-ws': file:./projects/adb-backend-ws.tgz
|
||||
'@rush-temp/demo': file:./projects/demo.tgz
|
||||
'@rush-temp/event': file:./projects/event.tgz
|
||||
'@rush-temp/scrcpy': file:./projects/scrcpy.tgz
|
||||
'@rush-temp/struct': file:./projects/struct.tgz
|
||||
'@rush-temp/ts-package-builder': file:./projects/ts-package-builder.tgz
|
||||
'@rush-temp/unofficial-adb-book': file:./projects/unofficial-adb-book.tgz
|
||||
'@svgr/webpack': ^5.5.0
|
||||
'@types/dom-webcodecs': ^0.1.2
|
||||
'@types/jest': ^26.0.23
|
||||
'@types/node': ^16.9.1
|
||||
'@types/react': ^17.0.27
|
||||
'@types/react': 17.0.27
|
||||
'@types/w3c-web-usb': ^1.0.4
|
||||
'@types/wicg-file-system-access': ^2020.9.4
|
||||
'@yume-chan/async': ^2.1.4
|
||||
clsx: ^1.1.1
|
||||
copy-webpack-plugin: ^9.0.1
|
||||
eslint: 7.32.0
|
||||
eslint-config-next: ^11.1.3-canary.52
|
||||
file-loader: ^6.2.0
|
||||
gh-release-fetch: ^2.0.4
|
||||
jest: ^26.6.3
|
||||
json5: ^2.2.0
|
||||
mini-svg-data-uri: ~1.3.3
|
||||
|
@ -14102,6 +14660,7 @@ specifiers:
|
|||
plantuml-encoder: ~1.4.0
|
||||
react: ^17.0.2
|
||||
react-dom: ^17.0.2
|
||||
streamsaver: ^2.0.5
|
||||
tinyh264: ^0.0.7
|
||||
tslib: ^2.3.1
|
||||
unist-util-visit: ^2.0.0
|
||||
|
|
|
@ -42,7 +42,7 @@ function _getRushVersion() {
|
|||
console.log(`Using Rush version from environment variable ${RUSH_PREVIEW_VERSION}=${rushPreviewVersion}`);
|
||||
return rushPreviewVersion;
|
||||
}
|
||||
const rushJsonFolder = install_run_1.findRushJsonFolder();
|
||||
const rushJsonFolder = (0, install_run_1.findRushJsonFolder)();
|
||||
const rushJsonPath = path.join(rushJsonFolder, install_run_1.RUSH_JSON_FILENAME);
|
||||
try {
|
||||
const rushJsonContents = fs.readFileSync(rushJsonPath, 'utf-8');
|
||||
|
@ -76,10 +76,10 @@ function _run() {
|
|||
}
|
||||
process.exit(1);
|
||||
}
|
||||
install_run_1.runWithErrorAndStatusCode(() => {
|
||||
(0, install_run_1.runWithErrorAndStatusCode)(() => {
|
||||
const version = _getRushVersion();
|
||||
console.log(`The rush.json configuration requests Rush version ${version}`);
|
||||
return install_run_1.installAndRun(PACKAGE_NAME, version, bin, packageBinArgs);
|
||||
return (0, install_run_1.installAndRun)(PACKAGE_NAME, version, bin, packageBinArgs);
|
||||
});
|
||||
}
|
||||
_run();
|
||||
|
|
1
libraries/scrcpy/.gitignore
vendored
Normal file
1
libraries/scrcpy/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
bin
|
14
libraries/scrcpy/.npmignore
Normal file
14
libraries/scrcpy/.npmignore
Normal file
|
@ -0,0 +1,14 @@
|
|||
.rush
|
||||
|
||||
# Test
|
||||
coverage
|
||||
**/*.spec.ts
|
||||
**/*.spec.js
|
||||
**/*.spec.js.map
|
||||
**/__helpers__
|
||||
jest.config.js
|
||||
|
||||
tsconfig.json
|
||||
|
||||
# Logs
|
||||
*.log
|
21
libraries/scrcpy/LICENSE
Normal file
21
libraries/scrcpy/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Simon Chan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
5
libraries/scrcpy/README.md
Normal file
5
libraries/scrcpy/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# @yume-chan/scrcpy
|
||||
|
||||
TypeScript implementation of [Scrcpy](https://github.com/Genymobile/scrcpy) client.
|
||||
|
||||
It uses the official Scrcpy server releases.
|
4
libraries/scrcpy/jest.config.js
Normal file
4
libraries/scrcpy/jest.config.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
testMatch: ['<rootDir>/cjs/**/*.spec.js'],
|
||||
testEnvironment: 'node',
|
||||
};
|
48
libraries/scrcpy/package.json
Normal file
48
libraries/scrcpy/package.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "@yume-chan/scrcpy",
|
||||
"version": "0.0.9",
|
||||
"description": "TypeScript implementation of Scrcpy.",
|
||||
"keywords": [
|
||||
"adb",
|
||||
"android-phone",
|
||||
"scrcpy"
|
||||
],
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "Simon Chan",
|
||||
"email": "cnsimonchan@live.com",
|
||||
"url": "https://chensi.moe/blog"
|
||||
},
|
||||
"homepage": "https://github.com/yume-chan/ya-webadb/tree/master/packages/scrcpy#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/yume-chan/ya-webadb.git",
|
||||
"directory": "packages/scrcpy"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/yume-chan/ya-webadb/issues"
|
||||
},
|
||||
"main": "cjs/index.js",
|
||||
"module": "esm/index.js",
|
||||
"types": "dts/index.d.ts",
|
||||
"scripts": {
|
||||
"postinstall": "node scripts/fetch-server",
|
||||
"build": "build-ts-package",
|
||||
"build:watch": "build-ts-package --incremental",
|
||||
"test": "jest --coverage",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@yume-chan/adb": "^0.0.9",
|
||||
"@yume-chan/async": "^2.1.4",
|
||||
"@yume-chan/event": "^0.0.9",
|
||||
"@yume-chan/struct": "^0.0.9",
|
||||
"tslib": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@yume-chan/ts-package-builder": "^1.0.0",
|
||||
"gh-release-fetch": "^2.0.4",
|
||||
"jest": "^26.6.3",
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
}
|
203
libraries/scrcpy/scrcpy.LICENSE
Normal file
203
libraries/scrcpy/scrcpy.LICENSE
Normal file
|
@ -0,0 +1,203 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (C) 2018 Genymobile
|
||||
Copyright (C) 2018-2021 Romain Vimont
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
26
libraries/scrcpy/scripts/fetch-server.js
Normal file
26
libraries/scrcpy/scripts/fetch-server.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
const { fetchVersion } = require('gh-release-fetch');
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
|
||||
const SERVER_VERSION = '1.19';
|
||||
|
||||
(async () => {
|
||||
console.log('Downloading scrcpy server binary...');
|
||||
|
||||
const binFolder = path.resolve(__dirname, '..', 'bin');
|
||||
|
||||
await fetchVersion({
|
||||
repository: 'Genymobile/scrcpy',
|
||||
version: `v${SERVER_VERSION}`,
|
||||
package: `scrcpy-server-v${SERVER_VERSION}`,
|
||||
destination: binFolder,
|
||||
extract: false,
|
||||
});
|
||||
|
||||
await fs.rename(
|
||||
path.resolve(binFolder, `scrcpy-server-v${SERVER_VERSION}`),
|
||||
path.resolve(binFolder, 'scrcpy-server')
|
||||
);
|
||||
|
||||
fs.writeFile(path.resolve(__dirname, '..', 'src', 'version.ts'), `export const BUNDLED_SERVER_VERSION = "${SERVER_VERSION}";`);
|
||||
})();
|
599
libraries/scrcpy/src/client.ts
Normal file
599
libraries/scrcpy/src/client.ts
Normal file
|
@ -0,0 +1,599 @@
|
|||
import { Adb, AdbBufferedStream, AdbLegacyShell, AdbShell, DataEventEmitter } from '@yume-chan/adb';
|
||||
import { PromiseResolver } from '@yume-chan/async';
|
||||
import { EventEmitter } from '@yume-chan/event';
|
||||
import Struct from '@yume-chan/struct';
|
||||
import { AndroidCodecLevel, AndroidCodecProfile } from './codec';
|
||||
import { ScrcpyClientConnection, ScrcpyClientForwardConnection, ScrcpyClientReverseConnection } from "./connection";
|
||||
import { AndroidKeyEventAction, AndroidMotionEventAction, ScrcpyControlMessageType, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, ScrcpySimpleControlMessage } from './message';
|
||||
import { DEFAULT_SERVER_PATH, pushServer, PushServerOptions } from "./push-server";
|
||||
import { parse_sequence_parameter_set, SequenceParameterSet } from './sps';
|
||||
|
||||
export enum ScrcpyLogLevel {
|
||||
Debug = 'debug',
|
||||
Info = 'info',
|
||||
Warn = 'warn',
|
||||
Error = 'error',
|
||||
}
|
||||
|
||||
interface ScrcpyError {
|
||||
type: string;
|
||||
|
||||
message: string;
|
||||
|
||||
stackTrace: string[];
|
||||
}
|
||||
|
||||
interface ScrcpyOutput {
|
||||
level: ScrcpyLogLevel;
|
||||
|
||||
message: string;
|
||||
|
||||
error?: ScrcpyError;
|
||||
}
|
||||
|
||||
class LineReader {
|
||||
private readonly text: string;
|
||||
|
||||
private start = 0;
|
||||
|
||||
private peekLine: string | undefined;
|
||||
|
||||
private peekEnd = 0;
|
||||
|
||||
constructor(text: string) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public next(): string | undefined {
|
||||
let result = this.peek();
|
||||
this.start = this.peekEnd;
|
||||
this.peekEnd = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
public peek(): string | undefined {
|
||||
if (this.peekEnd) {
|
||||
return this.peekLine;
|
||||
}
|
||||
|
||||
const index = this.text.indexOf('\n', this.start);
|
||||
if (index === -1) {
|
||||
this.peekLine = undefined;
|
||||
this.peekEnd = this.text.length;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const line = this.text.substring(this.start, index);
|
||||
this.peekLine = line;
|
||||
this.peekEnd = index + 1;
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
function* parseScrcpyOutput(text: string): Generator<ScrcpyOutput> {
|
||||
const lines = new LineReader(text);
|
||||
let line: string | undefined;
|
||||
while (line = lines.next()) {
|
||||
if (line === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith('[server] ')) {
|
||||
line = line.substring('[server] '.length);
|
||||
|
||||
if (line.startsWith('DEBUG: ')) {
|
||||
yield {
|
||||
level: ScrcpyLogLevel.Debug,
|
||||
message: line.substring('DEBUG: '.length),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith('INFO: ')) {
|
||||
yield {
|
||||
level: ScrcpyLogLevel.Info,
|
||||
message: line.substring('INFO: '.length),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith('ERROR: ')) {
|
||||
line = line.substring('ERROR: '.length);
|
||||
const message = line;
|
||||
|
||||
let error: ScrcpyError | undefined;
|
||||
if (line.startsWith('Exception on thread')) {
|
||||
if (line = lines.next()) {
|
||||
const [errorType, errorMessage] = line.split(': ', 2);
|
||||
const stackTrace: string[] = [];
|
||||
while (line = lines.peek()) {
|
||||
if (line.startsWith('\t')) {
|
||||
stackTrace.push(line.trim());
|
||||
lines.next();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
error = {
|
||||
type: errorType,
|
||||
message: errorMessage,
|
||||
stackTrace,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
yield {
|
||||
level: ScrcpyLogLevel.Error,
|
||||
message,
|
||||
error,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
yield {
|
||||
level: ScrcpyLogLevel.Info,
|
||||
message: line,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export enum ScrcpyScreenOrientation {
|
||||
Unlocked = -1,
|
||||
Portrait = 0,
|
||||
Landscape = 1,
|
||||
PortraitFlipped = 2,
|
||||
LandscapeFlipped = 3,
|
||||
}
|
||||
|
||||
const Size =
|
||||
new Struct()
|
||||
.uint16('width')
|
||||
.uint16('height');
|
||||
|
||||
const VideoPacket =
|
||||
new Struct()
|
||||
.int64('pts')
|
||||
.uint32('size')
|
||||
.arrayBuffer('data', { lengthField: 'size' });
|
||||
|
||||
export const NoPts = BigInt(-1);
|
||||
|
||||
export type VideoPacket = typeof VideoPacket['TDeserializeResult'];
|
||||
|
||||
const ClipboardMessage =
|
||||
new Struct()
|
||||
.uint32('length')
|
||||
.string('content', { lengthField: 'length' });
|
||||
|
||||
export interface ScrcpyClientOptions {
|
||||
device: Adb;
|
||||
|
||||
path?: string;
|
||||
|
||||
version: string;
|
||||
|
||||
logLevel?: ScrcpyLogLevel;
|
||||
|
||||
/**
|
||||
* The maximum value of both width and height.
|
||||
*/
|
||||
maxSize?: number | undefined;
|
||||
|
||||
bitRate: number;
|
||||
|
||||
maxFps?: number;
|
||||
|
||||
/**
|
||||
* The orientation of the video stream.
|
||||
*
|
||||
* It will not keep the device screen in specific orientation,
|
||||
* only the captured video will in this orientation.
|
||||
*/
|
||||
orientation?: ScrcpyScreenOrientation;
|
||||
|
||||
tunnelForward?: boolean;
|
||||
|
||||
profile?: AndroidCodecProfile;
|
||||
|
||||
level?: AndroidCodecLevel;
|
||||
|
||||
encoder?: string;
|
||||
}
|
||||
|
||||
export interface FrameSize {
|
||||
sequenceParameterSet: SequenceParameterSet;
|
||||
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
cropLeft: number;
|
||||
cropRight: number;
|
||||
|
||||
cropTop: number;
|
||||
cropBottom: number;
|
||||
|
||||
croppedWidth: number;
|
||||
croppedHeight: number;
|
||||
}
|
||||
|
||||
const ENCODER_REGEX = /^\s+scrcpy --encoder-name '(.*?)'/;
|
||||
|
||||
export class ScrcpyClient {
|
||||
public static pushServer(
|
||||
device: Adb,
|
||||
file: ArrayBuffer,
|
||||
options?: PushServerOptions
|
||||
) {
|
||||
pushServer(device, file, options);
|
||||
}
|
||||
|
||||
public static async getEncoders(options: ScrcpyClientOptions): Promise<string[]> {
|
||||
const client = new ScrcpyClient({
|
||||
...options,
|
||||
// Provide an invalid encoder name
|
||||
// So the server will return all available encoders
|
||||
encoder: '_',
|
||||
});
|
||||
|
||||
const resolver = new PromiseResolver<string[]>();
|
||||
const encoders: string[] = [];
|
||||
client.onError(({ message, error }) => {
|
||||
if (error && error.type !== 'com.genymobile.scrcpy.InvalidEncoderException') {
|
||||
resolver.reject(new Error(`${error.type}: ${error.message}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const match = message.match(ENCODER_REGEX);
|
||||
if (match) {
|
||||
encoders.push(match[1]);
|
||||
}
|
||||
});
|
||||
|
||||
client.onClose(() => {
|
||||
resolver.resolve(encoders);
|
||||
});
|
||||
|
||||
// Scrcpy server will open connections, before initializing encoder
|
||||
// Thus although an invalid encoder name is given, the start process will success
|
||||
await client.start();
|
||||
|
||||
return resolver.promise;
|
||||
}
|
||||
|
||||
private readonly options: ScrcpyClientOptions;
|
||||
|
||||
public get backend() { return this.options.device.backend; }
|
||||
|
||||
private process: AdbShell | undefined;
|
||||
|
||||
private videoStream: AdbBufferedStream | undefined;
|
||||
|
||||
private controlStream: AdbBufferedStream | undefined;
|
||||
|
||||
private readonly debugEvent = new EventEmitter<string>();
|
||||
public get onDebug() { return this.debugEvent.event; }
|
||||
|
||||
private readonly infoEvent = new EventEmitter<string>();
|
||||
public get onInfo() { return this.infoEvent.event; }
|
||||
|
||||
private readonly errorEvent = new EventEmitter<ScrcpyOutput>();
|
||||
public get onError() { return this.errorEvent.event; }
|
||||
|
||||
private readonly closeEvent = new EventEmitter<void>();
|
||||
public get onClose() { return this.closeEvent.event; }
|
||||
|
||||
private _running = false;
|
||||
public get running() { return this._running; }
|
||||
|
||||
private _screenWidth: number | undefined;
|
||||
public get screenWidth() { return this._screenWidth; }
|
||||
|
||||
private _screenHeight: number | undefined;
|
||||
public get screenHeight() { return this._screenHeight; }
|
||||
|
||||
private readonly sizeChangedEvent = new EventEmitter<FrameSize>();
|
||||
public get onSizeChanged() { return this.sizeChangedEvent.event; }
|
||||
|
||||
private readonly videoDataEvent = new DataEventEmitter<VideoPacket>();
|
||||
public get onVideoData() { return this.videoDataEvent.event; }
|
||||
|
||||
private readonly clipboardChangeEvent = new EventEmitter<string>();
|
||||
public get onClipboardChange() { return this.clipboardChangeEvent.event; }
|
||||
|
||||
private sendingTouchMessage = false;
|
||||
|
||||
public constructor(options: ScrcpyClientOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public async start(): Promise<void> {
|
||||
const {
|
||||
device,
|
||||
path = DEFAULT_SERVER_PATH,
|
||||
version,
|
||||
logLevel = ScrcpyLogLevel.Error,
|
||||
maxSize = 0,
|
||||
bitRate,
|
||||
maxFps = 0,
|
||||
orientation = ScrcpyScreenOrientation.Unlocked,
|
||||
tunnelForward = false,
|
||||
profile = AndroidCodecProfile.Baseline,
|
||||
level = AndroidCodecLevel.Level4,
|
||||
encoder = '-',
|
||||
} = this.options;
|
||||
|
||||
let connection: ScrcpyClientConnection | undefined;
|
||||
let process: AdbShell | undefined;
|
||||
|
||||
try {
|
||||
if (tunnelForward) {
|
||||
connection = new ScrcpyClientForwardConnection(device);
|
||||
} else {
|
||||
connection = new ScrcpyClientReverseConnection(device);
|
||||
}
|
||||
await connection.initialize();
|
||||
|
||||
process = await device.childProcess.spawn([
|
||||
`CLASSPATH=${path}`,
|
||||
'app_process',
|
||||
/* unused */ '/',
|
||||
'com.genymobile.scrcpy.Server',
|
||||
version,
|
||||
logLevel,
|
||||
maxSize.toString(), // (0: unlimited)
|
||||
bitRate.toString(),
|
||||
maxFps.toString(),
|
||||
orientation.toString(),
|
||||
tunnelForward.toString(),
|
||||
/* crop */ '-',
|
||||
/* send_frame_meta */ 'true', // always send frame meta (packet boundaries + timestamp)
|
||||
/* control */ 'true',
|
||||
/* display_id */ '0',
|
||||
/* show_touches */ 'false',
|
||||
/* stay_awake */ 'true',
|
||||
/* codec_options */ `profile=${profile},level=${level}`,
|
||||
encoder,
|
||||
], {
|
||||
// Disable Shell Protocol to simplify processing
|
||||
shells: [AdbLegacyShell],
|
||||
});
|
||||
|
||||
process.onStdout(this.handleProcessOutput, this);
|
||||
|
||||
const resolver = new PromiseResolver<never>();
|
||||
const removeExitListener = process.onExit(() => {
|
||||
resolver.reject(new Error('scrcpy server exited prematurely'));
|
||||
});
|
||||
|
||||
const [videoStream, controlStream] = await Promise.race([
|
||||
resolver.promise,
|
||||
connection.getStreams(),
|
||||
]);
|
||||
|
||||
removeExitListener();
|
||||
this.process = process;
|
||||
this.process.onExit(this.handleProcessClosed, this);
|
||||
this.videoStream = videoStream;
|
||||
this.controlStream = controlStream;
|
||||
|
||||
this._running = true;
|
||||
this.receiveVideo();
|
||||
this.receiveControl();
|
||||
} catch (e) {
|
||||
await process?.kill();
|
||||
throw e;
|
||||
} finally {
|
||||
connection?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private handleProcessOutput(data: ArrayBuffer) {
|
||||
const string = this.options.device.backend.decodeUtf8(data);
|
||||
for (const output of parseScrcpyOutput(string)) {
|
||||
switch (output.level) {
|
||||
case ScrcpyLogLevel.Debug:
|
||||
this.debugEvent.fire(output.message);
|
||||
break;
|
||||
case ScrcpyLogLevel.Info:
|
||||
this.infoEvent.fire(output.message);
|
||||
break;
|
||||
case ScrcpyLogLevel.Error:
|
||||
this.errorEvent.fire(output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleProcessClosed() {
|
||||
this._running = false;
|
||||
this.closeEvent.fire();
|
||||
}
|
||||
|
||||
private async receiveVideo() {
|
||||
if (!this.videoStream) {
|
||||
throw new Error('receiveVideo started before initialization');
|
||||
}
|
||||
|
||||
try {
|
||||
// Device name, we don't need it
|
||||
await this.videoStream.read(64);
|
||||
|
||||
// Initial video size
|
||||
const { width, height } = await Size.deserialize(this.videoStream);
|
||||
this._screenWidth = width;
|
||||
this._screenHeight = height;
|
||||
|
||||
let buffer: ArrayBuffer | undefined;
|
||||
while (this._running) {
|
||||
const { pts, data } = await VideoPacket.deserialize(this.videoStream);
|
||||
if (!data || data.byteLength === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pts === NoPts) {
|
||||
const sequenceParameterSet = parse_sequence_parameter_set(data.slice(0));
|
||||
|
||||
const {
|
||||
pic_width_in_mbs_minus1,
|
||||
pic_height_in_map_units_minus1,
|
||||
frame_mbs_only_flag,
|
||||
frame_crop_left_offset,
|
||||
frame_crop_right_offset,
|
||||
frame_crop_top_offset,
|
||||
frame_crop_bottom_offset,
|
||||
} = sequenceParameterSet;
|
||||
const width = (pic_width_in_mbs_minus1 + 1) * 16;
|
||||
const height = (pic_height_in_map_units_minus1 + 1) * (2 - frame_mbs_only_flag) * 16;
|
||||
const cropLeft = frame_crop_left_offset * 2;
|
||||
const cropRight = frame_crop_right_offset * 2;
|
||||
const cropTop = frame_crop_top_offset * 2;
|
||||
const cropBottom = frame_crop_bottom_offset * 2;
|
||||
|
||||
const screenWidth = width - cropLeft - cropRight;
|
||||
const screenHeight = height - cropTop - cropBottom;
|
||||
this._screenWidth = screenWidth;
|
||||
this._screenHeight = screenHeight;
|
||||
|
||||
this.sizeChangedEvent.fire({
|
||||
sequenceParameterSet,
|
||||
width,
|
||||
height,
|
||||
cropLeft: cropLeft,
|
||||
cropRight: cropRight,
|
||||
cropTop: cropTop,
|
||||
cropBottom: cropBottom,
|
||||
croppedWidth: screenWidth,
|
||||
croppedHeight: screenHeight,
|
||||
});
|
||||
|
||||
buffer = data;
|
||||
continue;
|
||||
}
|
||||
|
||||
let array: Uint8Array;
|
||||
if (buffer) {
|
||||
array = new Uint8Array(buffer.byteLength + data!.byteLength);
|
||||
array.set(new Uint8Array(buffer));
|
||||
array.set(new Uint8Array(data!), buffer.byteLength);
|
||||
buffer = undefined;
|
||||
} else {
|
||||
array = new Uint8Array(data!);
|
||||
}
|
||||
|
||||
await this.videoDataEvent.fire({
|
||||
pts,
|
||||
size: array.byteLength,
|
||||
data: array.buffer,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (!this._running) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async receiveControl() {
|
||||
if (!this.controlStream) {
|
||||
throw new Error('receiveControl started before initialization');
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const type = await this.controlStream.read(1);
|
||||
switch (new Uint8Array(type)[0]) {
|
||||
case 0:
|
||||
const { content } = await ClipboardMessage.deserialize(this.controlStream);
|
||||
this.clipboardChangeEvent.fire(content!);
|
||||
break;
|
||||
default:
|
||||
throw new Error('unknown control message type');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (!this._running) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async injectKeyCode(message: Omit<ScrcpyInjectKeyCodeControlMessage, 'type' | 'action'>) {
|
||||
if (!this.controlStream) {
|
||||
throw new Error('injectKeyCode called before initialization');
|
||||
}
|
||||
|
||||
await this.controlStream.write(ScrcpyInjectKeyCodeControlMessage.serialize({
|
||||
...message,
|
||||
type: ScrcpyControlMessageType.InjectKeycode,
|
||||
action: AndroidKeyEventAction.Down,
|
||||
}, this.backend));
|
||||
|
||||
await this.controlStream.write(ScrcpyInjectKeyCodeControlMessage.serialize({
|
||||
...message,
|
||||
type: ScrcpyControlMessageType.InjectKeycode,
|
||||
action: AndroidKeyEventAction.Up,
|
||||
}, this.backend));
|
||||
}
|
||||
|
||||
public async injectText(text: string) {
|
||||
if (!this.controlStream) {
|
||||
throw new Error('injectText called before initialization');
|
||||
}
|
||||
|
||||
await this.controlStream.write(ScrcpyInjectTextControlMessage.serialize({
|
||||
type: ScrcpyControlMessageType.InjectText,
|
||||
text,
|
||||
}, this.backend));
|
||||
}
|
||||
|
||||
public async injectTouch(message: Omit<ScrcpyInjectTouchControlMessage, 'type' | 'screenWidth' | 'screenHeight'>) {
|
||||
if (!this.controlStream) {
|
||||
throw new Error('injectTouch called before initialization');
|
||||
}
|
||||
|
||||
if (!this.screenWidth || !this.screenHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ADB streams are actually pretty low-bandwidth and laggy
|
||||
// Re-sample move events to avoid flooding the connection
|
||||
if (this.sendingTouchMessage &&
|
||||
message.action === AndroidMotionEventAction.Move) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendingTouchMessage = true;
|
||||
const buffer = ScrcpyInjectTouchControlMessage.serialize({
|
||||
...message,
|
||||
type: ScrcpyControlMessageType.InjectTouch,
|
||||
screenWidth: this.screenWidth,
|
||||
screenHeight: this.screenHeight,
|
||||
}, this.backend);
|
||||
await this.controlStream.write(buffer);
|
||||
this.sendingTouchMessage = false;
|
||||
}
|
||||
|
||||
public async pressBackOrTurnOnScreen() {
|
||||
if (!this.controlStream) {
|
||||
throw new Error('pressBackOrTurnOnScreen called before initialization');
|
||||
}
|
||||
|
||||
const buffer = ScrcpySimpleControlMessage.serialize(
|
||||
{ type: ScrcpyControlMessageType.BackOrScreenOn },
|
||||
this.backend
|
||||
);
|
||||
await this.controlStream.write(buffer);
|
||||
}
|
||||
|
||||
public async close() {
|
||||
if (!this._running) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._running = false;
|
||||
this.videoStream?.close();
|
||||
this.controlStream?.close();
|
||||
await this.process?.kill();
|
||||
}
|
||||
}
|
36
libraries/scrcpy/src/codec.ts
Normal file
36
libraries/scrcpy/src/codec.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
// See https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel
|
||||
export enum AndroidCodecProfile {
|
||||
Baseline = 0x01,
|
||||
Main = 0x02,
|
||||
Extended = 0x04,
|
||||
High = 0x08,
|
||||
High10 = 0x10,
|
||||
High422 = 0x20,
|
||||
High444 = 0x40,
|
||||
ConstrainedBaseline = 0x10000,
|
||||
ConstrainedHigh = 0x80000,
|
||||
}
|
||||
|
||||
export enum AndroidCodecLevel {
|
||||
Level1 = 0x01,
|
||||
Level1b = 0x02,
|
||||
Level11 = 0x04,
|
||||
Level12 = 0x08,
|
||||
Level13 = 0x10,
|
||||
Level2 = 0x20,
|
||||
Level21 = 0x40,
|
||||
Level22 = 0x80,
|
||||
Level3 = 0x100,
|
||||
Level31 = 0x200,
|
||||
Level32 = 0x400,
|
||||
Level4 = 0x800,
|
||||
Level41 = 0x1000,
|
||||
Level42 = 0x2000,
|
||||
Level5 = 0x4000,
|
||||
Level51 = 0x8000,
|
||||
Level52 = 0x10000,
|
||||
Level6 = 0x20000,
|
||||
Level61 = 0x40000,
|
||||
Level62 = 0x80000,
|
||||
}
|
89
libraries/scrcpy/src/connection.ts
Normal file
89
libraries/scrcpy/src/connection.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
import { Adb, AdbBufferedStream, AdbSocket, EventQueue } from "@yume-chan/adb";
|
||||
import { Disposable } from "@yume-chan/event";
|
||||
import { ValueOrPromise } from "@yume-chan/struct";
|
||||
import { delay } from "./utils";
|
||||
|
||||
export abstract class ScrcpyClientConnection implements Disposable {
|
||||
protected device: Adb;
|
||||
|
||||
public constructor(device: Adb) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public initialize(): ValueOrPromise<void> { }
|
||||
|
||||
public abstract getStreams(): ValueOrPromise<[videoSteam: AdbBufferedStream, controlStream: AdbBufferedStream]>;
|
||||
|
||||
public dispose(): void { }
|
||||
}
|
||||
|
||||
export class ScrcpyClientForwardConnection extends ScrcpyClientConnection {
|
||||
private async connect(): Promise<AdbBufferedStream> {
|
||||
return new AdbBufferedStream(await this.device.createSocket('localabstract:scrcpy'));
|
||||
}
|
||||
|
||||
private async connectAndRetry(): Promise<AdbBufferedStream> {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
try {
|
||||
return await this.connect();
|
||||
} catch (e) {
|
||||
await delay(100);
|
||||
}
|
||||
}
|
||||
throw new Error(`Can't connect to server after 100 retries`);
|
||||
}
|
||||
|
||||
private async connectAndReadByte(): Promise<AdbBufferedStream> {
|
||||
const stream = await this.connectAndRetry();
|
||||
// server will write a `0` to signal connection success
|
||||
await stream.read(1);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public async getStreams(): Promise<[videoSteam: AdbBufferedStream, controlStream: AdbBufferedStream]> {
|
||||
return [
|
||||
await this.connectAndReadByte(),
|
||||
await this.connectAndRetry()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class ScrcpyClientReverseConnection extends ScrcpyClientConnection {
|
||||
private streams!: EventQueue<AdbSocket>;
|
||||
|
||||
private address!: string;
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
// try to unbind first
|
||||
try {
|
||||
await this.device.reverse.remove('localabstract:scrcpy');
|
||||
} catch {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
this.streams = new EventQueue<AdbSocket>();
|
||||
this.address = await this.device.reverse.add('localabstract:scrcpy', 27183, {
|
||||
onSocket: (packet, stream) => {
|
||||
this.streams.enqueue(stream);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async accept(): Promise<AdbBufferedStream> {
|
||||
return new AdbBufferedStream(await this.streams.dequeue());
|
||||
}
|
||||
|
||||
public async getStreams(): Promise<[videoSteam: AdbBufferedStream, controlStream: AdbBufferedStream]> {
|
||||
return [
|
||||
await this.accept(),
|
||||
await this.accept(),
|
||||
];
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
// Don't await this!
|
||||
// `reverse.remove`'s response will never arrive
|
||||
// before we read all pending data from `videoStream`
|
||||
this.device.reverse.remove(this.address);
|
||||
}
|
||||
}
|
7
libraries/scrcpy/src/index.ts
Normal file
7
libraries/scrcpy/src/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export * from './client';
|
||||
export * from './codec';
|
||||
export * from './connection';
|
||||
export * from './message';
|
||||
export * from './push-server';
|
||||
export * from './utils';
|
||||
export * from './version';
|
114
libraries/scrcpy/src/message.ts
Normal file
114
libraries/scrcpy/src/message.ts
Normal file
|
@ -0,0 +1,114 @@
|
|||
import Struct, { placeholder } from '@yume-chan/struct';
|
||||
|
||||
export enum ScrcpyControlMessageType {
|
||||
InjectKeycode,
|
||||
InjectText,
|
||||
InjectTouch,
|
||||
InjectScroll,
|
||||
BackOrScreenOn,
|
||||
ExpandNotificationPanel,
|
||||
CollapseNotificationPanel,
|
||||
GetClipboard,
|
||||
SetClipboard,
|
||||
SetScreenPowerMode,
|
||||
RotateDevice,
|
||||
}
|
||||
|
||||
export const ScrcpySimpleControlMessage =
|
||||
new Struct()
|
||||
.uint8('type', placeholder<ScrcpyControlMessageType.BackOrScreenOn>());
|
||||
|
||||
export type ScrcpySimpleControlMessage = typeof ScrcpySimpleControlMessage['TInit'];
|
||||
|
||||
export enum AndroidMotionEventAction {
|
||||
Down,
|
||||
Up,
|
||||
Move,
|
||||
Cancel,
|
||||
Outside,
|
||||
PointerDown,
|
||||
PointerUp,
|
||||
HoverMove,
|
||||
Scroll,
|
||||
HoverEnter,
|
||||
HoverExit,
|
||||
ButtonPress,
|
||||
ButtonRelease,
|
||||
}
|
||||
|
||||
export const ScrcpyInjectTouchControlMessage =
|
||||
new Struct()
|
||||
.uint8('type', ScrcpyControlMessageType.InjectTouch as const)
|
||||
.uint8('action', placeholder<AndroidMotionEventAction>())
|
||||
.uint64('pointerId')
|
||||
.uint32('pointerX')
|
||||
.uint32('pointerY')
|
||||
.uint16('screenWidth')
|
||||
.uint16('screenHeight')
|
||||
.uint16('pressure')
|
||||
.uint32('buttons');
|
||||
|
||||
export type ScrcpyInjectTouchControlMessage = typeof ScrcpyInjectTouchControlMessage['TInit'];
|
||||
|
||||
export const ScrcpyInjectTextControlMessage =
|
||||
new Struct()
|
||||
.uint8('type', ScrcpyControlMessageType.InjectText as const)
|
||||
.uint32('length')
|
||||
.string('text', { lengthField: 'length' });
|
||||
|
||||
export type ScrcpyInjectTextControlMessage =
|
||||
typeof ScrcpyInjectTextControlMessage['TInit'];
|
||||
|
||||
export enum AndroidKeyEventAction {
|
||||
Down = 0,
|
||||
Up = 1,
|
||||
}
|
||||
|
||||
export enum AndroidKeyCode {
|
||||
Home = 3,
|
||||
Back = 4,
|
||||
A = 29,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
Delete = 67,
|
||||
AppSwitch = 187,
|
||||
}
|
||||
|
||||
export const ScrcpyInjectKeyCodeControlMessage =
|
||||
new Struct()
|
||||
.uint8('type', ScrcpyControlMessageType.InjectKeycode as const)
|
||||
.uint8('action', placeholder<AndroidKeyEventAction>())
|
||||
.uint32('keyCode')
|
||||
.uint32('repeat')
|
||||
.uint32('metaState');
|
||||
|
||||
export type ScrcpyInjectKeyCodeControlMessage =
|
||||
typeof ScrcpyInjectKeyCodeControlMessage['TInit'];
|
||||
|
||||
export type ScrcpyControlMessage =
|
||||
ScrcpySimpleControlMessage |
|
||||
ScrcpyInjectTouchControlMessage |
|
||||
ScrcpyInjectKeyCodeControlMessage;
|
28
libraries/scrcpy/src/push-server.ts
Normal file
28
libraries/scrcpy/src/push-server.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { Adb } from "@yume-chan/adb";
|
||||
|
||||
export const DEFAULT_SERVER_PATH = '/data/local/tmp/scrcpy-server.jar';
|
||||
|
||||
export interface PushServerOptions {
|
||||
path?: string;
|
||||
onProgress?: (progress: number) => void;
|
||||
}
|
||||
|
||||
export async function pushServer(
|
||||
device: Adb,
|
||||
file: ArrayBuffer,
|
||||
options: PushServerOptions = {}
|
||||
) {
|
||||
const {
|
||||
path = DEFAULT_SERVER_PATH,
|
||||
onProgress,
|
||||
} = options;
|
||||
|
||||
const sync = await device.sync();
|
||||
return sync.write(
|
||||
path,
|
||||
file,
|
||||
undefined,
|
||||
undefined,
|
||||
onProgress
|
||||
);
|
||||
}
|
284
libraries/scrcpy/src/sps.ts
Normal file
284
libraries/scrcpy/src/sps.ts
Normal file
|
@ -0,0 +1,284 @@
|
|||
class BitReader {
|
||||
private buffer: Uint8Array;
|
||||
|
||||
private bytePosition = 0;
|
||||
|
||||
private bitPosition = 0;
|
||||
|
||||
public constructor(buffer: Uint8Array) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public read(length: number): number {
|
||||
let result = 0;
|
||||
for (let i = 0; i < length; i += 1) {
|
||||
result = (result << 1) | this.next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public next(): number {
|
||||
const value = (this.buffer[this.bytePosition] >> (7 - this.bitPosition)) & 1;
|
||||
this.bitPosition += 1;
|
||||
if (this.bitPosition === 8) {
|
||||
this.bytePosition += 1;
|
||||
this.bitPosition = 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public decodeExponentialGolombNumber(): number {
|
||||
let length = 0;
|
||||
while (this.next() === 0) {
|
||||
length += 1;
|
||||
}
|
||||
if (length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return (1 << length | this.read(length)) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> {
|
||||
// -1 means we haven't found the first start code
|
||||
let start = -1;
|
||||
let writeIndex = 0;
|
||||
|
||||
// How many `0x00`s in a row we have counted
|
||||
let zeroCount = 0;
|
||||
|
||||
let inEmulation = false;
|
||||
|
||||
for (const byte of buffer) {
|
||||
buffer[writeIndex] = byte;
|
||||
writeIndex += 1;
|
||||
|
||||
if (inEmulation) {
|
||||
if (byte > 0x03) {
|
||||
// `0x00000304` or larger are invalid
|
||||
throw new Error('Invalid data');
|
||||
}
|
||||
|
||||
inEmulation = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (byte == 0x00) {
|
||||
zeroCount += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const lastZeroCount = zeroCount;
|
||||
zeroCount = 0;
|
||||
|
||||
if (start === -1) {
|
||||
// 0x000001 is the start code
|
||||
// But it can be preceded by any number of zeros
|
||||
// So 2 is the minimal
|
||||
if (lastZeroCount >= 2 && byte === 0x01) {
|
||||
// Found start of first NAL unit
|
||||
writeIndex = 0;
|
||||
start = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not begin with start code
|
||||
throw new Error('Invalid data');
|
||||
}
|
||||
|
||||
if (lastZeroCount < 2) {
|
||||
// zero or one `0x00`s are acceptable
|
||||
continue;
|
||||
}
|
||||
|
||||
if (byte === 0x01) {
|
||||
// Remove all leading `0x00`s and this `0x01`
|
||||
writeIndex -= lastZeroCount + 1;
|
||||
|
||||
// Found another NAL unit
|
||||
yield buffer.subarray(start, writeIndex);
|
||||
|
||||
start = writeIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lastZeroCount > 2) {
|
||||
// Too much `0x00`s
|
||||
throw new Error('Invalid data');
|
||||
}
|
||||
|
||||
switch (byte) {
|
||||
case 0x02:
|
||||
// Didn't find why, but 7.4.1 NAL unit semantics forbids `0x000002` appearing in NAL units
|
||||
throw new Error('Invalid data');
|
||||
case 0x03:
|
||||
// `0x000003` is the "emulation_prevention_three_byte"
|
||||
// `0x00000300`, `0x00000301`, `0x00000302` and `0x00000303` represent
|
||||
// `0x000000`, `0x000001`, `0x000002` and `0x000003` respectively
|
||||
|
||||
// Remove current byte
|
||||
writeIndex -= 1;
|
||||
|
||||
inEmulation = true;
|
||||
break;
|
||||
default:
|
||||
// `0x000004` or larger are ok
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inEmulation || zeroCount !== 0) {
|
||||
throw new Error('Invalid data');
|
||||
}
|
||||
|
||||
yield buffer.subarray(start, writeIndex);
|
||||
}
|
||||
|
||||
// 7.3.2.1.1 Sequence parameter set data syntax
|
||||
export function parse_sequence_parameter_set(buffer: ArrayBuffer) {
|
||||
for (const nalu of iterateNalu(new Uint8Array(buffer))) {
|
||||
const reader = new BitReader(nalu);
|
||||
if (reader.next() !== 0) {
|
||||
throw new Error('Invalid data');
|
||||
}
|
||||
|
||||
const nal_ref_idc = reader.read(2);
|
||||
const nal_unit_type = reader.read(5);
|
||||
|
||||
if (nal_unit_type !== 7) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nal_ref_idc === 0) {
|
||||
throw new Error('Invalid data');
|
||||
}
|
||||
|
||||
const profile_idc = reader.read(8);
|
||||
const constraint_set = reader.read(8);
|
||||
|
||||
const constraint_set_reader = new BitReader(new Uint8Array([constraint_set]));
|
||||
const constraint_set0_flag = !!constraint_set_reader.next();
|
||||
const constraint_set1_flag = !!constraint_set_reader.next();
|
||||
const constraint_set2_flag = !!constraint_set_reader.next();
|
||||
const constraint_set3_flag = !!constraint_set_reader.next();
|
||||
const constraint_set4_flag = !!constraint_set_reader.next();
|
||||
const constraint_set5_flag = !!constraint_set_reader.next();
|
||||
|
||||
// reserved_zero_2bits
|
||||
if (constraint_set_reader.read(2) !== 0) {
|
||||
throw new Error('Invalid data');
|
||||
}
|
||||
|
||||
const level_idc = reader.read(8);
|
||||
const seq_parameter_set_id = reader.decodeExponentialGolombNumber();
|
||||
|
||||
if (profile_idc === 100 || profile_idc === 110 ||
|
||||
profile_idc === 122 || profile_idc === 244 || profile_idc === 44 ||
|
||||
profile_idc === 83 || profile_idc === 86 || profile_idc === 118 ||
|
||||
profile_idc === 128 || profile_idc === 138 || profile_idc === 139 ||
|
||||
profile_idc === 134) {
|
||||
const chroma_format_idc = reader.decodeExponentialGolombNumber();
|
||||
if (chroma_format_idc === 3) {
|
||||
const separate_colour_plane_flag = !!reader.next();
|
||||
}
|
||||
|
||||
const bit_depth_luma_minus8 = reader.decodeExponentialGolombNumber();
|
||||
const bit_depth_chroma_minus8 = reader.decodeExponentialGolombNumber();
|
||||
|
||||
const qpprime_y_zero_transform_bypass_flag = !!reader.next();
|
||||
|
||||
const seq_scaling_matrix_present_flag = !!reader.next();
|
||||
if (seq_scaling_matrix_present_flag) {
|
||||
const seq_scaling_list_present_flag: boolean[] = [];
|
||||
for (let i = 0; i < ((chroma_format_idc !== 3) ? 8 : 12); i++) {
|
||||
seq_scaling_list_present_flag[i] = !!reader.next();
|
||||
if (seq_scaling_list_present_flag[i])
|
||||
if (i < 6) {
|
||||
// TODO
|
||||
// scaling_list( ScalingList4x4[ i ], 16,
|
||||
// UseDefaultScalingMatrix4x4Flag[ i ])
|
||||
} else {
|
||||
// TODO
|
||||
// scaling_list( ScalingList8x8[ i − 6 ], 64,
|
||||
// UseDefaultScalingMatrix8x8Flag[ i − 6 ] )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const log2_max_frame_num_minus4 = reader.decodeExponentialGolombNumber();
|
||||
const pic_order_cnt_type = reader.decodeExponentialGolombNumber();
|
||||
if (pic_order_cnt_type === 0) {
|
||||
const log2_max_pic_order_cnt_lsb_minus4 = reader.decodeExponentialGolombNumber();
|
||||
} else if (pic_order_cnt_type === 1) {
|
||||
const delta_pic_order_always_zero_flag = reader.next();
|
||||
const offset_for_non_ref_pic = reader.decodeExponentialGolombNumber();
|
||||
const offset_for_top_to_bottom_field = reader.decodeExponentialGolombNumber();
|
||||
const num_ref_frames_in_pic_order_cnt_cycle = reader.decodeExponentialGolombNumber();
|
||||
const offset_for_ref_frame: number[] = [];
|
||||
for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
|
||||
offset_for_ref_frame[i] = reader.decodeExponentialGolombNumber();
|
||||
}
|
||||
}
|
||||
|
||||
const max_num_ref_frames = reader.decodeExponentialGolombNumber();
|
||||
const gaps_in_frame_num_value_allowed_flag = reader.next();
|
||||
const pic_width_in_mbs_minus1 = reader.decodeExponentialGolombNumber();
|
||||
const pic_height_in_map_units_minus1 = reader.decodeExponentialGolombNumber();
|
||||
|
||||
const frame_mbs_only_flag = reader.next();
|
||||
if (!frame_mbs_only_flag) {
|
||||
const mb_adaptive_frame_field_flag = !!reader.next();
|
||||
}
|
||||
|
||||
const direct_8x8_inference_flag = reader.next();
|
||||
|
||||
const frame_cropping_flag = !!reader.next();
|
||||
let frame_crop_left_offset: number;
|
||||
let frame_crop_right_offset: number;
|
||||
let frame_crop_top_offset: number;
|
||||
let frame_crop_bottom_offset: number;
|
||||
if (frame_cropping_flag) {
|
||||
frame_crop_left_offset = reader.decodeExponentialGolombNumber();
|
||||
frame_crop_right_offset = reader.decodeExponentialGolombNumber();
|
||||
frame_crop_top_offset = reader.decodeExponentialGolombNumber();
|
||||
frame_crop_bottom_offset = reader.decodeExponentialGolombNumber();
|
||||
} else {
|
||||
frame_crop_left_offset = 0;
|
||||
frame_crop_right_offset = 0;
|
||||
frame_crop_top_offset = 0;
|
||||
frame_crop_bottom_offset = 0;
|
||||
}
|
||||
|
||||
const vui_parameters_present_flag = !!reader.next();
|
||||
if (vui_parameters_present_flag) {
|
||||
// TODO
|
||||
// vui_parameters( )
|
||||
}
|
||||
|
||||
return {
|
||||
profile_idc,
|
||||
constraint_set,
|
||||
constraint_set0_flag,
|
||||
constraint_set1_flag,
|
||||
constraint_set2_flag,
|
||||
constraint_set3_flag,
|
||||
constraint_set4_flag,
|
||||
constraint_set5_flag,
|
||||
level_idc,
|
||||
seq_parameter_set_id,
|
||||
pic_width_in_mbs_minus1,
|
||||
pic_height_in_map_units_minus1,
|
||||
frame_mbs_only_flag,
|
||||
frame_cropping_flag,
|
||||
frame_crop_left_offset,
|
||||
frame_crop_right_offset,
|
||||
frame_crop_top_offset,
|
||||
frame_crop_bottom_offset,
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error('Invalid data');
|
||||
}
|
||||
|
||||
export type SequenceParameterSet = ReturnType<typeof parse_sequence_parameter_set>;
|
5
libraries/scrcpy/src/utils.ts
Normal file
5
libraries/scrcpy/src/utils.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export function delay(time: number): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
(globalThis as any).setTimeout(resolve, time);
|
||||
});
|
||||
}
|
1
libraries/scrcpy/src/version.ts
Normal file
1
libraries/scrcpy/src/version.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const BUNDLED_SERVER_VERSION = "1.19";
|
14
libraries/scrcpy/tsconfig.json
Normal file
14
libraries/scrcpy/tsconfig.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "./node_modules/@yume-chan/ts-package-builder/tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2016",
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../event/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../struct/tsconfig.json"
|
||||
}
|
||||
]
|
||||
}
|
10
rush.json
10
rush.json
|
@ -16,7 +16,7 @@
|
|||
* path segment in the "$schema" field for all your Rush config files. This will ensure
|
||||
* correct error-underlining and tab-completion for editors such as VS Code.
|
||||
*/
|
||||
"rushVersion": "5.46.1",
|
||||
"rushVersion": "5.55.1",
|
||||
|
||||
/**
|
||||
* The next field selects which package manager should be installed and determines its version.
|
||||
|
@ -319,7 +319,9 @@
|
|||
/**
|
||||
* The list of shell commands to run after the Rush installation finishes
|
||||
*/
|
||||
"postRushInstall": [],
|
||||
"postRushInstall": [
|
||||
"rush postinstall"
|
||||
],
|
||||
|
||||
/**
|
||||
* The list of shell commands to run before the Rush build command starts
|
||||
|
@ -481,6 +483,10 @@
|
|||
"packageName": "@yume-chan/adb-backend-ws",
|
||||
"projectFolder": "libraries/adb-backend-ws"
|
||||
},
|
||||
{
|
||||
"packageName": "@yume-chan/scrcpy",
|
||||
"projectFolder": "libraries/scrcpy"
|
||||
},
|
||||
{
|
||||
"packageName": "demo",
|
||||
"projectFolder": "apps/demo"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue