@midscene/android 1.12.2-beta-20260828072235.0 → 1.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/cli.mjs +79 -56
- package/dist/es/index.mjs +78 -55
- package/dist/lib/cli.js +80 -57
- package/dist/lib/index.js +79 -56
- package/dist/types/index.d.ts +16 -10
- package/package.json +4 -4
package/dist/es/cli.mjs
CHANGED
|
@@ -32,11 +32,10 @@ var __webpack_modules__ = {
|
|
|
32
32
|
ScrcpyScreenshotManager: ()=>ScrcpyScreenshotManager,
|
|
33
33
|
ov: ()=>DEFAULT_SCRCPY_CONFIG
|
|
34
34
|
});
|
|
35
|
-
var
|
|
36
|
-
var
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
var _resource_path__rspack_import_4 = __webpack_require__("./src/resource-path.ts");
|
|
35
|
+
var node_module__rspack_import_0 = __webpack_require__("node:module");
|
|
36
|
+
var node_path__rspack_import_1 = __webpack_require__("node:path");
|
|
37
|
+
var _midscene_shared_logger__rspack_import_2 = __webpack_require__("@midscene/shared/logger");
|
|
38
|
+
var _resource_path__rspack_import_3 = __webpack_require__("./src/resource-path.ts");
|
|
40
39
|
function _define_property(obj, key, value) {
|
|
41
40
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
42
41
|
value: value,
|
|
@@ -47,8 +46,8 @@ var __webpack_modules__ = {
|
|
|
47
46
|
else obj[key] = value;
|
|
48
47
|
return obj;
|
|
49
48
|
}
|
|
50
|
-
const debugScrcpy = (0,
|
|
51
|
-
const warnScrcpy = (0,
|
|
49
|
+
const debugScrcpy = (0, _midscene_shared_logger__rspack_import_2.getDebug)('android:scrcpy');
|
|
50
|
+
const warnScrcpy = (0, _midscene_shared_logger__rspack_import_2.getDebug)('android:scrcpy', {
|
|
52
51
|
console: true
|
|
53
52
|
});
|
|
54
53
|
const NAL_TYPE_IDR = 5;
|
|
@@ -147,10 +146,9 @@ var __webpack_modules__ = {
|
|
|
147
146
|
try {
|
|
148
147
|
debugScrcpy('Starting scrcpy connection...');
|
|
149
148
|
const { AdbScrcpyClient } = await import("@yume-chan/adb-scrcpy");
|
|
150
|
-
const { ReadableStream } = await import("@yume-chan/stream-extra");
|
|
151
149
|
const { DefaultServerPath } = await import("@yume-chan/scrcpy");
|
|
152
150
|
const serverBinPath = this.resolveServerBinPath();
|
|
153
|
-
await
|
|
151
|
+
await this.pushServer(serverBinPath, DefaultServerPath);
|
|
154
152
|
const scrcpyOptions = await this.createScrcpyOptions();
|
|
155
153
|
await this.ensureFrameClockCalibration();
|
|
156
154
|
this.scrcpyClient = await AdbScrcpyClient.start(this.adb, DefaultServerPath, scrcpyOptions);
|
|
@@ -234,12 +232,12 @@ var __webpack_modules__ = {
|
|
|
234
232
|
return output.filter((line)=>'string' == typeof line);
|
|
235
233
|
}
|
|
236
234
|
resolveServerBinPath() {
|
|
237
|
-
const androidPkgJson = (0,
|
|
238
|
-
return (0,
|
|
235
|
+
const androidPkgJson = (0, node_module__rspack_import_0.createRequire)(import.meta.url).resolve('@midscene/android/package.json');
|
|
236
|
+
return (0, _resource_path__rspack_import_3.J)(node_path__rspack_import_1["default"].join(node_path__rspack_import_1["default"].dirname(androidPkgJson), 'bin', 'scrcpy-server'));
|
|
239
237
|
}
|
|
240
238
|
getFfmpegPath() {
|
|
241
239
|
try {
|
|
242
|
-
const dynamicRequire = (0,
|
|
240
|
+
const dynamicRequire = (0, node_module__rspack_import_0.createRequire)(import.meta.url);
|
|
243
241
|
const ffmpegInstaller = dynamicRequire('@ffmpeg-installer/ffmpeg');
|
|
244
242
|
debugScrcpy(`Using ffmpeg from npm package: ${ffmpegInstaller.path}`);
|
|
245
243
|
return ffmpegInstaller.path;
|
|
@@ -778,36 +776,63 @@ var __webpack_modules__ = {
|
|
|
778
776
|
isConnected() {
|
|
779
777
|
return this.isInitialized && null !== this.scrcpyClient;
|
|
780
778
|
}
|
|
781
|
-
constructor(adb, options = {}){
|
|
779
|
+
constructor(adb, pushServer, options = {}){
|
|
780
|
+
_define_property(this, "pushServer", void 0);
|
|
782
781
|
_define_property(this, "adb", void 0);
|
|
783
|
-
_define_property(this, "scrcpyClient",
|
|
784
|
-
_define_property(this, "videoStream",
|
|
785
|
-
_define_property(this, "spsHeader",
|
|
786
|
-
_define_property(this, "idleTimer",
|
|
787
|
-
_define_property(this, "connectionPromise",
|
|
788
|
-
_define_property(this, "disposed",
|
|
789
|
-
_define_property(this, "isInitialized",
|
|
782
|
+
_define_property(this, "scrcpyClient", void 0);
|
|
783
|
+
_define_property(this, "videoStream", void 0);
|
|
784
|
+
_define_property(this, "spsHeader", void 0);
|
|
785
|
+
_define_property(this, "idleTimer", void 0);
|
|
786
|
+
_define_property(this, "connectionPromise", void 0);
|
|
787
|
+
_define_property(this, "disposed", void 0);
|
|
788
|
+
_define_property(this, "isInitialized", void 0);
|
|
790
789
|
_define_property(this, "options", void 0);
|
|
791
|
-
_define_property(this, "ffmpegAvailable",
|
|
792
|
-
_define_property(this, "keyframeResolvers",
|
|
793
|
-
_define_property(this, "keyframeListeners",
|
|
794
|
-
_define_property(this, "lastRawKeyframe",
|
|
795
|
-
_define_property(this, "lastRawKeyframeAt", 0);
|
|
790
|
+
_define_property(this, "ffmpegAvailable", void 0);
|
|
791
|
+
_define_property(this, "keyframeResolvers", void 0);
|
|
792
|
+
_define_property(this, "keyframeListeners", void 0);
|
|
793
|
+
_define_property(this, "lastRawKeyframe", void 0);
|
|
794
|
+
_define_property(this, "lastRawKeyframeAt", void 0);
|
|
796
795
|
_define_property(this, "lastRawKeyframePtsUs", void 0);
|
|
797
796
|
_define_property(this, "lastRawKeyframeEstimatedAgeMs", void 0);
|
|
798
|
-
_define_property(this, "videoResolution",
|
|
799
|
-
_define_property(this, "streamReader",
|
|
800
|
-
_define_property(this, "frameFreshnessBarrierPtsUs",
|
|
801
|
-
_define_property(this, "frameFreshnessBarrierReason",
|
|
802
|
-
_define_property(this, "streamStartupWindow",
|
|
803
|
-
_define_property(this, "frameFreshnessBarrierPending",
|
|
804
|
-
_define_property(this, "frameFreshnessBarrierGeneration", 0);
|
|
805
|
-
_define_property(this, "deviceClockCalibration",
|
|
806
|
-
_define_property(this, "deviceClockCalibrationPromise",
|
|
807
|
-
_define_property(this, "lastFramePtsUs",
|
|
808
|
-
_define_property(this, "frameFreshnessError",
|
|
809
|
-
_define_property(this, "lastFrameFreshnessWarningAt", 0);
|
|
810
|
-
_define_property(this, "disposePromise",
|
|
797
|
+
_define_property(this, "videoResolution", void 0);
|
|
798
|
+
_define_property(this, "streamReader", void 0);
|
|
799
|
+
_define_property(this, "frameFreshnessBarrierPtsUs", void 0);
|
|
800
|
+
_define_property(this, "frameFreshnessBarrierReason", void 0);
|
|
801
|
+
_define_property(this, "streamStartupWindow", void 0);
|
|
802
|
+
_define_property(this, "frameFreshnessBarrierPending", void 0);
|
|
803
|
+
_define_property(this, "frameFreshnessBarrierGeneration", void 0);
|
|
804
|
+
_define_property(this, "deviceClockCalibration", void 0);
|
|
805
|
+
_define_property(this, "deviceClockCalibrationPromise", void 0);
|
|
806
|
+
_define_property(this, "lastFramePtsUs", void 0);
|
|
807
|
+
_define_property(this, "frameFreshnessError", void 0);
|
|
808
|
+
_define_property(this, "lastFrameFreshnessWarningAt", void 0);
|
|
809
|
+
_define_property(this, "disposePromise", void 0);
|
|
810
|
+
this.pushServer = pushServer;
|
|
811
|
+
this.scrcpyClient = null;
|
|
812
|
+
this.videoStream = null;
|
|
813
|
+
this.spsHeader = null;
|
|
814
|
+
this.idleTimer = null;
|
|
815
|
+
this.connectionPromise = null;
|
|
816
|
+
this.disposed = false;
|
|
817
|
+
this.isInitialized = false;
|
|
818
|
+
this.ffmpegAvailable = null;
|
|
819
|
+
this.keyframeResolvers = [];
|
|
820
|
+
this.keyframeListeners = new Set();
|
|
821
|
+
this.lastRawKeyframe = null;
|
|
822
|
+
this.lastRawKeyframeAt = 0;
|
|
823
|
+
this.videoResolution = null;
|
|
824
|
+
this.streamReader = null;
|
|
825
|
+
this.frameFreshnessBarrierPtsUs = null;
|
|
826
|
+
this.frameFreshnessBarrierReason = null;
|
|
827
|
+
this.streamStartupWindow = null;
|
|
828
|
+
this.frameFreshnessBarrierPending = false;
|
|
829
|
+
this.frameFreshnessBarrierGeneration = 0;
|
|
830
|
+
this.deviceClockCalibration = null;
|
|
831
|
+
this.deviceClockCalibrationPromise = null;
|
|
832
|
+
this.lastFramePtsUs = null;
|
|
833
|
+
this.frameFreshnessError = null;
|
|
834
|
+
this.lastFrameFreshnessWarningAt = 0;
|
|
835
|
+
this.disposePromise = null;
|
|
811
836
|
this.adb = adb;
|
|
812
837
|
const requestedBitRate = options.videoBitRate ?? DEFAULT_VIDEO_BIT_RATE;
|
|
813
838
|
const clampedBitRate = Math.min(requestedBitRate, MAX_VIDEO_BIT_RATE);
|
|
@@ -1043,10 +1068,6 @@ function formatScrcpyFreshFrameFailure(error) {
|
|
|
1043
1068
|
const bitRateDescription = Number.isFinite(videoBitRate) ? ` Current videoBitRate: ${videoBitRate} bps (${videoBitRate / 1000000} Mbps).` : '';
|
|
1044
1069
|
return `No usable scrcpy frame crossed the active freshness target within ${timeoutMs}ms. This can happen when a static screen emits no new encoded frame, or when the video stream or transport is interrupted. This timeout does not by itself identify bandwidth or the configured video bitrate as the cause.${bitRateDescription}`;
|
|
1045
1070
|
}
|
|
1046
|
-
const DEFAULT_ADB_SERVER_ENDPOINT = {
|
|
1047
|
-
host: '127.0.0.1',
|
|
1048
|
-
port: 5037
|
|
1049
|
-
};
|
|
1050
1071
|
class ScrcpyDeviceAdapter {
|
|
1051
1072
|
isEnabled() {
|
|
1052
1073
|
if (!this.isConfigured()) return false;
|
|
@@ -1111,13 +1132,18 @@ class ScrcpyDeviceAdapter {
|
|
|
1111
1132
|
const { Adb, AdbServerClient } = await import("@yume-chan/adb");
|
|
1112
1133
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
1113
1134
|
const { ScrcpyScreenshotManager: ScrcpyManager } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/scrcpy-manager.ts"));
|
|
1114
|
-
const
|
|
1115
|
-
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector(
|
|
1135
|
+
const adbBackend = await this.resolveAdbBackend();
|
|
1136
|
+
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector({
|
|
1137
|
+
host: adbBackend.adbHost ?? '127.0.0.1',
|
|
1138
|
+
port: adbBackend.adbPort ?? 5037
|
|
1139
|
+
}));
|
|
1116
1140
|
adb = new Adb(await adbClient.createTransport({
|
|
1117
1141
|
serial: this.deviceId
|
|
1118
1142
|
}));
|
|
1119
1143
|
const config = this.resolveConfig();
|
|
1120
|
-
manager = new ScrcpyManager(adb, {
|
|
1144
|
+
manager = new ScrcpyManager(adb, async (localPath, remotePath)=>{
|
|
1145
|
+
await adbBackend.push(localPath, remotePath);
|
|
1146
|
+
}, {
|
|
1121
1147
|
maxSize: config.maxSize,
|
|
1122
1148
|
videoBitRate: config.videoBitRate,
|
|
1123
1149
|
idleTimeoutMs: config.idleTimeoutMs
|
|
@@ -1311,10 +1337,13 @@ class ScrcpyDeviceAdapter {
|
|
|
1311
1337
|
this.resolvedConfig = null;
|
|
1312
1338
|
this.clearFailure();
|
|
1313
1339
|
}
|
|
1314
|
-
constructor(deviceId, scrcpyConfig,
|
|
1340
|
+
constructor(deviceId, scrcpyConfig, resolveAdbBackend = ()=>adb_createAndroidAdb({
|
|
1341
|
+
adbExecTimeout: 60000,
|
|
1342
|
+
deviceId
|
|
1343
|
+
})){
|
|
1315
1344
|
_define_property(this, "deviceId", void 0);
|
|
1316
1345
|
_define_property(this, "scrcpyConfig", void 0);
|
|
1317
|
-
_define_property(this, "
|
|
1346
|
+
_define_property(this, "resolveAdbBackend", void 0);
|
|
1318
1347
|
_define_property(this, "manager", void 0);
|
|
1319
1348
|
_define_property(this, "managerPromise", void 0);
|
|
1320
1349
|
_define_property(this, "resolvedConfig", void 0);
|
|
@@ -1327,7 +1356,7 @@ class ScrcpyDeviceAdapter {
|
|
|
1327
1356
|
_define_property(this, "keyframeUnsubscribers", void 0);
|
|
1328
1357
|
this.deviceId = deviceId;
|
|
1329
1358
|
this.scrcpyConfig = scrcpyConfig;
|
|
1330
|
-
this.
|
|
1359
|
+
this.resolveAdbBackend = resolveAdbBackend;
|
|
1331
1360
|
this.manager = null;
|
|
1332
1361
|
this.managerPromise = null;
|
|
1333
1362
|
this.resolvedConfig = null;
|
|
@@ -1827,13 +1856,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1827
1856
|
return adapter.getStatus();
|
|
1828
1857
|
}
|
|
1829
1858
|
getScrcpyAdapter() {
|
|
1830
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig,
|
|
1831
|
-
const adb = await this.getAdb();
|
|
1832
|
-
return {
|
|
1833
|
-
host: adb.adbHost ?? '127.0.0.1',
|
|
1834
|
-
port: adb.adbPort ?? 5037
|
|
1835
|
-
};
|
|
1836
|
-
});
|
|
1859
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig, ()=>this.getAdb());
|
|
1837
1860
|
return this.scrcpyAdapter;
|
|
1838
1861
|
}
|
|
1839
1862
|
async openScrcpyFrameSource() {
|
|
@@ -3205,7 +3228,7 @@ class AndroidMidsceneTools extends BaseMidsceneTools {
|
|
|
3205
3228
|
const tools = new AndroidMidsceneTools();
|
|
3206
3229
|
runToolsCLI(tools, 'midscene-android', {
|
|
3207
3230
|
stripPrefix: 'android_',
|
|
3208
|
-
version: "1.12.2
|
|
3231
|
+
version: "1.12.2",
|
|
3209
3232
|
extraCommands: createReportCliCommands()
|
|
3210
3233
|
}).catch((e)=>{
|
|
3211
3234
|
process.exit(reportCLIError(e));
|
package/dist/es/index.mjs
CHANGED
|
@@ -31,11 +31,10 @@ var __webpack_modules__ = {
|
|
|
31
31
|
ScrcpyScreenshotManager: ()=>ScrcpyScreenshotManager,
|
|
32
32
|
ov: ()=>DEFAULT_SCRCPY_CONFIG
|
|
33
33
|
});
|
|
34
|
-
var
|
|
35
|
-
var
|
|
36
|
-
var
|
|
37
|
-
var
|
|
38
|
-
var _resource_path__rspack_import_4 = __webpack_require__("./src/resource-path.ts");
|
|
34
|
+
var node_module__rspack_import_0 = __webpack_require__("node:module");
|
|
35
|
+
var node_path__rspack_import_1 = __webpack_require__("node:path");
|
|
36
|
+
var _midscene_shared_logger__rspack_import_2 = __webpack_require__("@midscene/shared/logger");
|
|
37
|
+
var _resource_path__rspack_import_3 = __webpack_require__("./src/resource-path.ts");
|
|
39
38
|
function _define_property(obj, key, value) {
|
|
40
39
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
41
40
|
value: value,
|
|
@@ -46,8 +45,8 @@ var __webpack_modules__ = {
|
|
|
46
45
|
else obj[key] = value;
|
|
47
46
|
return obj;
|
|
48
47
|
}
|
|
49
|
-
const debugScrcpy = (0,
|
|
50
|
-
const warnScrcpy = (0,
|
|
48
|
+
const debugScrcpy = (0, _midscene_shared_logger__rspack_import_2.getDebug)('android:scrcpy');
|
|
49
|
+
const warnScrcpy = (0, _midscene_shared_logger__rspack_import_2.getDebug)('android:scrcpy', {
|
|
51
50
|
console: true
|
|
52
51
|
});
|
|
53
52
|
const NAL_TYPE_IDR = 5;
|
|
@@ -146,10 +145,9 @@ var __webpack_modules__ = {
|
|
|
146
145
|
try {
|
|
147
146
|
debugScrcpy('Starting scrcpy connection...');
|
|
148
147
|
const { AdbScrcpyClient } = await import("@yume-chan/adb-scrcpy");
|
|
149
|
-
const { ReadableStream } = await import("@yume-chan/stream-extra");
|
|
150
148
|
const { DefaultServerPath } = await import("@yume-chan/scrcpy");
|
|
151
149
|
const serverBinPath = this.resolveServerBinPath();
|
|
152
|
-
await
|
|
150
|
+
await this.pushServer(serverBinPath, DefaultServerPath);
|
|
153
151
|
const scrcpyOptions = await this.createScrcpyOptions();
|
|
154
152
|
await this.ensureFrameClockCalibration();
|
|
155
153
|
this.scrcpyClient = await AdbScrcpyClient.start(this.adb, DefaultServerPath, scrcpyOptions);
|
|
@@ -233,12 +231,12 @@ var __webpack_modules__ = {
|
|
|
233
231
|
return output.filter((line)=>'string' == typeof line);
|
|
234
232
|
}
|
|
235
233
|
resolveServerBinPath() {
|
|
236
|
-
const androidPkgJson = (0,
|
|
237
|
-
return (0,
|
|
234
|
+
const androidPkgJson = (0, node_module__rspack_import_0.createRequire)(import.meta.url).resolve('@midscene/android/package.json');
|
|
235
|
+
return (0, _resource_path__rspack_import_3.J)(node_path__rspack_import_1["default"].join(node_path__rspack_import_1["default"].dirname(androidPkgJson), 'bin', 'scrcpy-server'));
|
|
238
236
|
}
|
|
239
237
|
getFfmpegPath() {
|
|
240
238
|
try {
|
|
241
|
-
const dynamicRequire = (0,
|
|
239
|
+
const dynamicRequire = (0, node_module__rspack_import_0.createRequire)(import.meta.url);
|
|
242
240
|
const ffmpegInstaller = dynamicRequire('@ffmpeg-installer/ffmpeg');
|
|
243
241
|
debugScrcpy(`Using ffmpeg from npm package: ${ffmpegInstaller.path}`);
|
|
244
242
|
return ffmpegInstaller.path;
|
|
@@ -777,36 +775,63 @@ var __webpack_modules__ = {
|
|
|
777
775
|
isConnected() {
|
|
778
776
|
return this.isInitialized && null !== this.scrcpyClient;
|
|
779
777
|
}
|
|
780
|
-
constructor(adb, options = {}){
|
|
778
|
+
constructor(adb, pushServer, options = {}){
|
|
779
|
+
_define_property(this, "pushServer", void 0);
|
|
781
780
|
_define_property(this, "adb", void 0);
|
|
782
|
-
_define_property(this, "scrcpyClient",
|
|
783
|
-
_define_property(this, "videoStream",
|
|
784
|
-
_define_property(this, "spsHeader",
|
|
785
|
-
_define_property(this, "idleTimer",
|
|
786
|
-
_define_property(this, "connectionPromise",
|
|
787
|
-
_define_property(this, "disposed",
|
|
788
|
-
_define_property(this, "isInitialized",
|
|
781
|
+
_define_property(this, "scrcpyClient", void 0);
|
|
782
|
+
_define_property(this, "videoStream", void 0);
|
|
783
|
+
_define_property(this, "spsHeader", void 0);
|
|
784
|
+
_define_property(this, "idleTimer", void 0);
|
|
785
|
+
_define_property(this, "connectionPromise", void 0);
|
|
786
|
+
_define_property(this, "disposed", void 0);
|
|
787
|
+
_define_property(this, "isInitialized", void 0);
|
|
789
788
|
_define_property(this, "options", void 0);
|
|
790
|
-
_define_property(this, "ffmpegAvailable",
|
|
791
|
-
_define_property(this, "keyframeResolvers",
|
|
792
|
-
_define_property(this, "keyframeListeners",
|
|
793
|
-
_define_property(this, "lastRawKeyframe",
|
|
794
|
-
_define_property(this, "lastRawKeyframeAt", 0);
|
|
789
|
+
_define_property(this, "ffmpegAvailable", void 0);
|
|
790
|
+
_define_property(this, "keyframeResolvers", void 0);
|
|
791
|
+
_define_property(this, "keyframeListeners", void 0);
|
|
792
|
+
_define_property(this, "lastRawKeyframe", void 0);
|
|
793
|
+
_define_property(this, "lastRawKeyframeAt", void 0);
|
|
795
794
|
_define_property(this, "lastRawKeyframePtsUs", void 0);
|
|
796
795
|
_define_property(this, "lastRawKeyframeEstimatedAgeMs", void 0);
|
|
797
|
-
_define_property(this, "videoResolution",
|
|
798
|
-
_define_property(this, "streamReader",
|
|
799
|
-
_define_property(this, "frameFreshnessBarrierPtsUs",
|
|
800
|
-
_define_property(this, "frameFreshnessBarrierReason",
|
|
801
|
-
_define_property(this, "streamStartupWindow",
|
|
802
|
-
_define_property(this, "frameFreshnessBarrierPending",
|
|
803
|
-
_define_property(this, "frameFreshnessBarrierGeneration", 0);
|
|
804
|
-
_define_property(this, "deviceClockCalibration",
|
|
805
|
-
_define_property(this, "deviceClockCalibrationPromise",
|
|
806
|
-
_define_property(this, "lastFramePtsUs",
|
|
807
|
-
_define_property(this, "frameFreshnessError",
|
|
808
|
-
_define_property(this, "lastFrameFreshnessWarningAt", 0);
|
|
809
|
-
_define_property(this, "disposePromise",
|
|
796
|
+
_define_property(this, "videoResolution", void 0);
|
|
797
|
+
_define_property(this, "streamReader", void 0);
|
|
798
|
+
_define_property(this, "frameFreshnessBarrierPtsUs", void 0);
|
|
799
|
+
_define_property(this, "frameFreshnessBarrierReason", void 0);
|
|
800
|
+
_define_property(this, "streamStartupWindow", void 0);
|
|
801
|
+
_define_property(this, "frameFreshnessBarrierPending", void 0);
|
|
802
|
+
_define_property(this, "frameFreshnessBarrierGeneration", void 0);
|
|
803
|
+
_define_property(this, "deviceClockCalibration", void 0);
|
|
804
|
+
_define_property(this, "deviceClockCalibrationPromise", void 0);
|
|
805
|
+
_define_property(this, "lastFramePtsUs", void 0);
|
|
806
|
+
_define_property(this, "frameFreshnessError", void 0);
|
|
807
|
+
_define_property(this, "lastFrameFreshnessWarningAt", void 0);
|
|
808
|
+
_define_property(this, "disposePromise", void 0);
|
|
809
|
+
this.pushServer = pushServer;
|
|
810
|
+
this.scrcpyClient = null;
|
|
811
|
+
this.videoStream = null;
|
|
812
|
+
this.spsHeader = null;
|
|
813
|
+
this.idleTimer = null;
|
|
814
|
+
this.connectionPromise = null;
|
|
815
|
+
this.disposed = false;
|
|
816
|
+
this.isInitialized = false;
|
|
817
|
+
this.ffmpegAvailable = null;
|
|
818
|
+
this.keyframeResolvers = [];
|
|
819
|
+
this.keyframeListeners = new Set();
|
|
820
|
+
this.lastRawKeyframe = null;
|
|
821
|
+
this.lastRawKeyframeAt = 0;
|
|
822
|
+
this.videoResolution = null;
|
|
823
|
+
this.streamReader = null;
|
|
824
|
+
this.frameFreshnessBarrierPtsUs = null;
|
|
825
|
+
this.frameFreshnessBarrierReason = null;
|
|
826
|
+
this.streamStartupWindow = null;
|
|
827
|
+
this.frameFreshnessBarrierPending = false;
|
|
828
|
+
this.frameFreshnessBarrierGeneration = 0;
|
|
829
|
+
this.deviceClockCalibration = null;
|
|
830
|
+
this.deviceClockCalibrationPromise = null;
|
|
831
|
+
this.lastFramePtsUs = null;
|
|
832
|
+
this.frameFreshnessError = null;
|
|
833
|
+
this.lastFrameFreshnessWarningAt = 0;
|
|
834
|
+
this.disposePromise = null;
|
|
810
835
|
this.adb = adb;
|
|
811
836
|
const requestedBitRate = options.videoBitRate ?? DEFAULT_VIDEO_BIT_RATE;
|
|
812
837
|
const clampedBitRate = Math.min(requestedBitRate, MAX_VIDEO_BIT_RATE);
|
|
@@ -946,10 +971,6 @@ function formatScrcpyFreshFrameFailure(error) {
|
|
|
946
971
|
const bitRateDescription = Number.isFinite(videoBitRate) ? ` Current videoBitRate: ${videoBitRate} bps (${videoBitRate / 1000000} Mbps).` : '';
|
|
947
972
|
return `No usable scrcpy frame crossed the active freshness target within ${timeoutMs}ms. This can happen when a static screen emits no new encoded frame, or when the video stream or transport is interrupted. This timeout does not by itself identify bandwidth or the configured video bitrate as the cause.${bitRateDescription}`;
|
|
948
973
|
}
|
|
949
|
-
const DEFAULT_ADB_SERVER_ENDPOINT = {
|
|
950
|
-
host: '127.0.0.1',
|
|
951
|
-
port: 5037
|
|
952
|
-
};
|
|
953
974
|
class ScrcpyDeviceAdapter {
|
|
954
975
|
isEnabled() {
|
|
955
976
|
if (!this.isConfigured()) return false;
|
|
@@ -1014,13 +1035,18 @@ class ScrcpyDeviceAdapter {
|
|
|
1014
1035
|
const { Adb, AdbServerClient } = await import("@yume-chan/adb");
|
|
1015
1036
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
1016
1037
|
const { ScrcpyScreenshotManager: ScrcpyManager } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/scrcpy-manager.ts"));
|
|
1017
|
-
const
|
|
1018
|
-
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector(
|
|
1038
|
+
const adbBackend = await this.resolveAdbBackend();
|
|
1039
|
+
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector({
|
|
1040
|
+
host: adbBackend.adbHost ?? '127.0.0.1',
|
|
1041
|
+
port: adbBackend.adbPort ?? 5037
|
|
1042
|
+
}));
|
|
1019
1043
|
adb = new Adb(await adbClient.createTransport({
|
|
1020
1044
|
serial: this.deviceId
|
|
1021
1045
|
}));
|
|
1022
1046
|
const config = this.resolveConfig();
|
|
1023
|
-
manager = new ScrcpyManager(adb, {
|
|
1047
|
+
manager = new ScrcpyManager(adb, async (localPath, remotePath)=>{
|
|
1048
|
+
await adbBackend.push(localPath, remotePath);
|
|
1049
|
+
}, {
|
|
1024
1050
|
maxSize: config.maxSize,
|
|
1025
1051
|
videoBitRate: config.videoBitRate,
|
|
1026
1052
|
idleTimeoutMs: config.idleTimeoutMs
|
|
@@ -1214,10 +1240,13 @@ class ScrcpyDeviceAdapter {
|
|
|
1214
1240
|
this.resolvedConfig = null;
|
|
1215
1241
|
this.clearFailure();
|
|
1216
1242
|
}
|
|
1217
|
-
constructor(deviceId, scrcpyConfig,
|
|
1243
|
+
constructor(deviceId, scrcpyConfig, resolveAdbBackend = ()=>createAndroidAdb({
|
|
1244
|
+
adbExecTimeout: 60000,
|
|
1245
|
+
deviceId
|
|
1246
|
+
})){
|
|
1218
1247
|
_define_property(this, "deviceId", void 0);
|
|
1219
1248
|
_define_property(this, "scrcpyConfig", void 0);
|
|
1220
|
-
_define_property(this, "
|
|
1249
|
+
_define_property(this, "resolveAdbBackend", void 0);
|
|
1221
1250
|
_define_property(this, "manager", void 0);
|
|
1222
1251
|
_define_property(this, "managerPromise", void 0);
|
|
1223
1252
|
_define_property(this, "resolvedConfig", void 0);
|
|
@@ -1230,7 +1259,7 @@ class ScrcpyDeviceAdapter {
|
|
|
1230
1259
|
_define_property(this, "keyframeUnsubscribers", void 0);
|
|
1231
1260
|
this.deviceId = deviceId;
|
|
1232
1261
|
this.scrcpyConfig = scrcpyConfig;
|
|
1233
|
-
this.
|
|
1262
|
+
this.resolveAdbBackend = resolveAdbBackend;
|
|
1234
1263
|
this.manager = null;
|
|
1235
1264
|
this.managerPromise = null;
|
|
1236
1265
|
this.resolvedConfig = null;
|
|
@@ -1730,13 +1759,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1730
1759
|
return adapter.getStatus();
|
|
1731
1760
|
}
|
|
1732
1761
|
getScrcpyAdapter() {
|
|
1733
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig,
|
|
1734
|
-
const adb = await this.getAdb();
|
|
1735
|
-
return {
|
|
1736
|
-
host: adb.adbHost ?? '127.0.0.1',
|
|
1737
|
-
port: adb.adbPort ?? 5037
|
|
1738
|
-
};
|
|
1739
|
-
});
|
|
1762
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig, ()=>this.getAdb());
|
|
1740
1763
|
return this.scrcpyAdapter;
|
|
1741
1764
|
}
|
|
1742
1765
|
async openScrcpyFrameSource() {
|
package/dist/lib/cli.js
CHANGED
|
@@ -19,12 +19,11 @@ var __webpack_modules__ = {
|
|
|
19
19
|
ScrcpyScreenshotManager: ()=>ScrcpyScreenshotManager,
|
|
20
20
|
ov: ()=>DEFAULT_SCRCPY_CONFIG
|
|
21
21
|
});
|
|
22
|
-
var
|
|
23
|
-
var
|
|
24
|
-
var
|
|
25
|
-
var
|
|
26
|
-
var
|
|
27
|
-
var _resource_path__rspack_import_4 = __webpack_require__("./src/resource-path.ts");
|
|
22
|
+
var node_module__rspack_import_0 = __webpack_require__("node:module");
|
|
23
|
+
var node_path__rspack_import_1 = __webpack_require__("node:path");
|
|
24
|
+
var node_path__rspack_import_1_default = /*#__PURE__*/ __webpack_require__.n(node_path__rspack_import_1);
|
|
25
|
+
var _midscene_shared_logger__rspack_import_2 = __webpack_require__("@midscene/shared/logger");
|
|
26
|
+
var _resource_path__rspack_import_3 = __webpack_require__("./src/resource-path.ts");
|
|
28
27
|
function _define_property(obj, key, value) {
|
|
29
28
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
30
29
|
value: value,
|
|
@@ -35,8 +34,8 @@ var __webpack_modules__ = {
|
|
|
35
34
|
else obj[key] = value;
|
|
36
35
|
return obj;
|
|
37
36
|
}
|
|
38
|
-
const debugScrcpy = (0,
|
|
39
|
-
const warnScrcpy = (0,
|
|
37
|
+
const debugScrcpy = (0, _midscene_shared_logger__rspack_import_2.getDebug)('android:scrcpy');
|
|
38
|
+
const warnScrcpy = (0, _midscene_shared_logger__rspack_import_2.getDebug)('android:scrcpy', {
|
|
40
39
|
console: true
|
|
41
40
|
});
|
|
42
41
|
const NAL_TYPE_IDR = 5;
|
|
@@ -135,10 +134,9 @@ var __webpack_modules__ = {
|
|
|
135
134
|
try {
|
|
136
135
|
debugScrcpy('Starting scrcpy connection...');
|
|
137
136
|
const { AdbScrcpyClient } = await import("@yume-chan/adb-scrcpy");
|
|
138
|
-
const { ReadableStream } = await import("@yume-chan/stream-extra");
|
|
139
137
|
const { DefaultServerPath } = await import("@yume-chan/scrcpy");
|
|
140
138
|
const serverBinPath = this.resolveServerBinPath();
|
|
141
|
-
await
|
|
139
|
+
await this.pushServer(serverBinPath, DefaultServerPath);
|
|
142
140
|
const scrcpyOptions = await this.createScrcpyOptions();
|
|
143
141
|
await this.ensureFrameClockCalibration();
|
|
144
142
|
this.scrcpyClient = await AdbScrcpyClient.start(this.adb, DefaultServerPath, scrcpyOptions);
|
|
@@ -222,12 +220,12 @@ var __webpack_modules__ = {
|
|
|
222
220
|
return output.filter((line)=>'string' == typeof line);
|
|
223
221
|
}
|
|
224
222
|
resolveServerBinPath() {
|
|
225
|
-
const androidPkgJson = (0,
|
|
226
|
-
return (0,
|
|
223
|
+
const androidPkgJson = (0, node_module__rspack_import_0.createRequire)(__rslib_import_meta_url__).resolve('@midscene/android/package.json');
|
|
224
|
+
return (0, _resource_path__rspack_import_3.J)(node_path__rspack_import_1_default().join(node_path__rspack_import_1_default().dirname(androidPkgJson), 'bin', 'scrcpy-server'));
|
|
227
225
|
}
|
|
228
226
|
getFfmpegPath() {
|
|
229
227
|
try {
|
|
230
|
-
const dynamicRequire = (0,
|
|
228
|
+
const dynamicRequire = (0, node_module__rspack_import_0.createRequire)(__rslib_import_meta_url__);
|
|
231
229
|
const ffmpegInstaller = dynamicRequire('@ffmpeg-installer/ffmpeg');
|
|
232
230
|
debugScrcpy(`Using ffmpeg from npm package: ${ffmpegInstaller.path}`);
|
|
233
231
|
return ffmpegInstaller.path;
|
|
@@ -766,36 +764,63 @@ var __webpack_modules__ = {
|
|
|
766
764
|
isConnected() {
|
|
767
765
|
return this.isInitialized && null !== this.scrcpyClient;
|
|
768
766
|
}
|
|
769
|
-
constructor(adb, options = {}){
|
|
767
|
+
constructor(adb, pushServer, options = {}){
|
|
768
|
+
_define_property(this, "pushServer", void 0);
|
|
770
769
|
_define_property(this, "adb", void 0);
|
|
771
|
-
_define_property(this, "scrcpyClient",
|
|
772
|
-
_define_property(this, "videoStream",
|
|
773
|
-
_define_property(this, "spsHeader",
|
|
774
|
-
_define_property(this, "idleTimer",
|
|
775
|
-
_define_property(this, "connectionPromise",
|
|
776
|
-
_define_property(this, "disposed",
|
|
777
|
-
_define_property(this, "isInitialized",
|
|
770
|
+
_define_property(this, "scrcpyClient", void 0);
|
|
771
|
+
_define_property(this, "videoStream", void 0);
|
|
772
|
+
_define_property(this, "spsHeader", void 0);
|
|
773
|
+
_define_property(this, "idleTimer", void 0);
|
|
774
|
+
_define_property(this, "connectionPromise", void 0);
|
|
775
|
+
_define_property(this, "disposed", void 0);
|
|
776
|
+
_define_property(this, "isInitialized", void 0);
|
|
778
777
|
_define_property(this, "options", void 0);
|
|
779
|
-
_define_property(this, "ffmpegAvailable",
|
|
780
|
-
_define_property(this, "keyframeResolvers",
|
|
781
|
-
_define_property(this, "keyframeListeners",
|
|
782
|
-
_define_property(this, "lastRawKeyframe",
|
|
783
|
-
_define_property(this, "lastRawKeyframeAt", 0);
|
|
778
|
+
_define_property(this, "ffmpegAvailable", void 0);
|
|
779
|
+
_define_property(this, "keyframeResolvers", void 0);
|
|
780
|
+
_define_property(this, "keyframeListeners", void 0);
|
|
781
|
+
_define_property(this, "lastRawKeyframe", void 0);
|
|
782
|
+
_define_property(this, "lastRawKeyframeAt", void 0);
|
|
784
783
|
_define_property(this, "lastRawKeyframePtsUs", void 0);
|
|
785
784
|
_define_property(this, "lastRawKeyframeEstimatedAgeMs", void 0);
|
|
786
|
-
_define_property(this, "videoResolution",
|
|
787
|
-
_define_property(this, "streamReader",
|
|
788
|
-
_define_property(this, "frameFreshnessBarrierPtsUs",
|
|
789
|
-
_define_property(this, "frameFreshnessBarrierReason",
|
|
790
|
-
_define_property(this, "streamStartupWindow",
|
|
791
|
-
_define_property(this, "frameFreshnessBarrierPending",
|
|
792
|
-
_define_property(this, "frameFreshnessBarrierGeneration", 0);
|
|
793
|
-
_define_property(this, "deviceClockCalibration",
|
|
794
|
-
_define_property(this, "deviceClockCalibrationPromise",
|
|
795
|
-
_define_property(this, "lastFramePtsUs",
|
|
796
|
-
_define_property(this, "frameFreshnessError",
|
|
797
|
-
_define_property(this, "lastFrameFreshnessWarningAt", 0);
|
|
798
|
-
_define_property(this, "disposePromise",
|
|
785
|
+
_define_property(this, "videoResolution", void 0);
|
|
786
|
+
_define_property(this, "streamReader", void 0);
|
|
787
|
+
_define_property(this, "frameFreshnessBarrierPtsUs", void 0);
|
|
788
|
+
_define_property(this, "frameFreshnessBarrierReason", void 0);
|
|
789
|
+
_define_property(this, "streamStartupWindow", void 0);
|
|
790
|
+
_define_property(this, "frameFreshnessBarrierPending", void 0);
|
|
791
|
+
_define_property(this, "frameFreshnessBarrierGeneration", void 0);
|
|
792
|
+
_define_property(this, "deviceClockCalibration", void 0);
|
|
793
|
+
_define_property(this, "deviceClockCalibrationPromise", void 0);
|
|
794
|
+
_define_property(this, "lastFramePtsUs", void 0);
|
|
795
|
+
_define_property(this, "frameFreshnessError", void 0);
|
|
796
|
+
_define_property(this, "lastFrameFreshnessWarningAt", void 0);
|
|
797
|
+
_define_property(this, "disposePromise", void 0);
|
|
798
|
+
this.pushServer = pushServer;
|
|
799
|
+
this.scrcpyClient = null;
|
|
800
|
+
this.videoStream = null;
|
|
801
|
+
this.spsHeader = null;
|
|
802
|
+
this.idleTimer = null;
|
|
803
|
+
this.connectionPromise = null;
|
|
804
|
+
this.disposed = false;
|
|
805
|
+
this.isInitialized = false;
|
|
806
|
+
this.ffmpegAvailable = null;
|
|
807
|
+
this.keyframeResolvers = [];
|
|
808
|
+
this.keyframeListeners = new Set();
|
|
809
|
+
this.lastRawKeyframe = null;
|
|
810
|
+
this.lastRawKeyframeAt = 0;
|
|
811
|
+
this.videoResolution = null;
|
|
812
|
+
this.streamReader = null;
|
|
813
|
+
this.frameFreshnessBarrierPtsUs = null;
|
|
814
|
+
this.frameFreshnessBarrierReason = null;
|
|
815
|
+
this.streamStartupWindow = null;
|
|
816
|
+
this.frameFreshnessBarrierPending = false;
|
|
817
|
+
this.frameFreshnessBarrierGeneration = 0;
|
|
818
|
+
this.deviceClockCalibration = null;
|
|
819
|
+
this.deviceClockCalibrationPromise = null;
|
|
820
|
+
this.lastFramePtsUs = null;
|
|
821
|
+
this.frameFreshnessError = null;
|
|
822
|
+
this.lastFrameFreshnessWarningAt = 0;
|
|
823
|
+
this.disposePromise = null;
|
|
799
824
|
this.adb = adb;
|
|
800
825
|
const requestedBitRate = options.videoBitRate ?? DEFAULT_VIDEO_BIT_RATE;
|
|
801
826
|
const clampedBitRate = Math.min(requestedBitRate, MAX_VIDEO_BIT_RATE);
|
|
@@ -1058,10 +1083,6 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
1058
1083
|
const bitRateDescription = Number.isFinite(videoBitRate) ? ` Current videoBitRate: ${videoBitRate} bps (${videoBitRate / 1000000} Mbps).` : '';
|
|
1059
1084
|
return `No usable scrcpy frame crossed the active freshness target within ${timeoutMs}ms. This can happen when a static screen emits no new encoded frame, or when the video stream or transport is interrupted. This timeout does not by itself identify bandwidth or the configured video bitrate as the cause.${bitRateDescription}`;
|
|
1060
1085
|
}
|
|
1061
|
-
const DEFAULT_ADB_SERVER_ENDPOINT = {
|
|
1062
|
-
host: '127.0.0.1',
|
|
1063
|
-
port: 5037
|
|
1064
|
-
};
|
|
1065
1086
|
class ScrcpyDeviceAdapter {
|
|
1066
1087
|
isEnabled() {
|
|
1067
1088
|
if (!this.isConfigured()) return false;
|
|
@@ -1126,13 +1147,18 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
1126
1147
|
const { Adb, AdbServerClient } = await import("@yume-chan/adb");
|
|
1127
1148
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
1128
1149
|
const { ScrcpyScreenshotManager: ScrcpyManager } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/scrcpy-manager.ts"));
|
|
1129
|
-
const
|
|
1130
|
-
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector(
|
|
1150
|
+
const adbBackend = await this.resolveAdbBackend();
|
|
1151
|
+
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector({
|
|
1152
|
+
host: adbBackend.adbHost ?? '127.0.0.1',
|
|
1153
|
+
port: adbBackend.adbPort ?? 5037
|
|
1154
|
+
}));
|
|
1131
1155
|
adb = new Adb(await adbClient.createTransport({
|
|
1132
1156
|
serial: this.deviceId
|
|
1133
1157
|
}));
|
|
1134
1158
|
const config = this.resolveConfig();
|
|
1135
|
-
manager = new ScrcpyManager(adb, {
|
|
1159
|
+
manager = new ScrcpyManager(adb, async (localPath, remotePath)=>{
|
|
1160
|
+
await adbBackend.push(localPath, remotePath);
|
|
1161
|
+
}, {
|
|
1136
1162
|
maxSize: config.maxSize,
|
|
1137
1163
|
videoBitRate: config.videoBitRate,
|
|
1138
1164
|
idleTimeoutMs: config.idleTimeoutMs
|
|
@@ -1326,10 +1352,13 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
1326
1352
|
this.resolvedConfig = null;
|
|
1327
1353
|
this.clearFailure();
|
|
1328
1354
|
}
|
|
1329
|
-
constructor(deviceId, scrcpyConfig,
|
|
1355
|
+
constructor(deviceId, scrcpyConfig, resolveAdbBackend = ()=>adb_createAndroidAdb({
|
|
1356
|
+
adbExecTimeout: 60000,
|
|
1357
|
+
deviceId
|
|
1358
|
+
})){
|
|
1330
1359
|
_define_property(this, "deviceId", void 0);
|
|
1331
1360
|
_define_property(this, "scrcpyConfig", void 0);
|
|
1332
|
-
_define_property(this, "
|
|
1361
|
+
_define_property(this, "resolveAdbBackend", void 0);
|
|
1333
1362
|
_define_property(this, "manager", void 0);
|
|
1334
1363
|
_define_property(this, "managerPromise", void 0);
|
|
1335
1364
|
_define_property(this, "resolvedConfig", void 0);
|
|
@@ -1342,7 +1371,7 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
1342
1371
|
_define_property(this, "keyframeUnsubscribers", void 0);
|
|
1343
1372
|
this.deviceId = deviceId;
|
|
1344
1373
|
this.scrcpyConfig = scrcpyConfig;
|
|
1345
|
-
this.
|
|
1374
|
+
this.resolveAdbBackend = resolveAdbBackend;
|
|
1346
1375
|
this.manager = null;
|
|
1347
1376
|
this.managerPromise = null;
|
|
1348
1377
|
this.resolvedConfig = null;
|
|
@@ -1842,13 +1871,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1842
1871
|
return adapter.getStatus();
|
|
1843
1872
|
}
|
|
1844
1873
|
getScrcpyAdapter() {
|
|
1845
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig,
|
|
1846
|
-
const adb = await this.getAdb();
|
|
1847
|
-
return {
|
|
1848
|
-
host: adb.adbHost ?? '127.0.0.1',
|
|
1849
|
-
port: adb.adbPort ?? 5037
|
|
1850
|
-
};
|
|
1851
|
-
});
|
|
1874
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig, ()=>this.getAdb());
|
|
1852
1875
|
return this.scrcpyAdapter;
|
|
1853
1876
|
}
|
|
1854
1877
|
async openScrcpyFrameSource() {
|
|
@@ -3220,7 +3243,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
3220
3243
|
const tools = new AndroidMidsceneTools();
|
|
3221
3244
|
(0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-android', {
|
|
3222
3245
|
stripPrefix: 'android_',
|
|
3223
|
-
version: "1.12.2
|
|
3246
|
+
version: "1.12.2",
|
|
3224
3247
|
extraCommands: (0, core_namespaceObject.createReportCliCommands)()
|
|
3225
3248
|
}).catch((e)=>{
|
|
3226
3249
|
process.exit((0, cli_namespaceObject.reportCLIError)(e));
|
package/dist/lib/index.js
CHANGED
|
@@ -19,12 +19,11 @@ var __webpack_modules__ = {
|
|
|
19
19
|
ScrcpyScreenshotManager: ()=>ScrcpyScreenshotManager,
|
|
20
20
|
ov: ()=>DEFAULT_SCRCPY_CONFIG
|
|
21
21
|
});
|
|
22
|
-
var
|
|
23
|
-
var
|
|
24
|
-
var
|
|
25
|
-
var
|
|
26
|
-
var
|
|
27
|
-
var _resource_path__rspack_import_4 = __webpack_require__("./src/resource-path.ts");
|
|
22
|
+
var node_module__rspack_import_0 = __webpack_require__("node:module");
|
|
23
|
+
var node_path__rspack_import_1 = __webpack_require__("node:path");
|
|
24
|
+
var node_path__rspack_import_1_default = /*#__PURE__*/ __webpack_require__.n(node_path__rspack_import_1);
|
|
25
|
+
var _midscene_shared_logger__rspack_import_2 = __webpack_require__("@midscene/shared/logger");
|
|
26
|
+
var _resource_path__rspack_import_3 = __webpack_require__("./src/resource-path.ts");
|
|
28
27
|
function _define_property(obj, key, value) {
|
|
29
28
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
30
29
|
value: value,
|
|
@@ -35,8 +34,8 @@ var __webpack_modules__ = {
|
|
|
35
34
|
else obj[key] = value;
|
|
36
35
|
return obj;
|
|
37
36
|
}
|
|
38
|
-
const debugScrcpy = (0,
|
|
39
|
-
const warnScrcpy = (0,
|
|
37
|
+
const debugScrcpy = (0, _midscene_shared_logger__rspack_import_2.getDebug)('android:scrcpy');
|
|
38
|
+
const warnScrcpy = (0, _midscene_shared_logger__rspack_import_2.getDebug)('android:scrcpy', {
|
|
40
39
|
console: true
|
|
41
40
|
});
|
|
42
41
|
const NAL_TYPE_IDR = 5;
|
|
@@ -135,10 +134,9 @@ var __webpack_modules__ = {
|
|
|
135
134
|
try {
|
|
136
135
|
debugScrcpy('Starting scrcpy connection...');
|
|
137
136
|
const { AdbScrcpyClient } = await import("@yume-chan/adb-scrcpy");
|
|
138
|
-
const { ReadableStream } = await import("@yume-chan/stream-extra");
|
|
139
137
|
const { DefaultServerPath } = await import("@yume-chan/scrcpy");
|
|
140
138
|
const serverBinPath = this.resolveServerBinPath();
|
|
141
|
-
await
|
|
139
|
+
await this.pushServer(serverBinPath, DefaultServerPath);
|
|
142
140
|
const scrcpyOptions = await this.createScrcpyOptions();
|
|
143
141
|
await this.ensureFrameClockCalibration();
|
|
144
142
|
this.scrcpyClient = await AdbScrcpyClient.start(this.adb, DefaultServerPath, scrcpyOptions);
|
|
@@ -222,12 +220,12 @@ var __webpack_modules__ = {
|
|
|
222
220
|
return output.filter((line)=>'string' == typeof line);
|
|
223
221
|
}
|
|
224
222
|
resolveServerBinPath() {
|
|
225
|
-
const androidPkgJson = (0,
|
|
226
|
-
return (0,
|
|
223
|
+
const androidPkgJson = (0, node_module__rspack_import_0.createRequire)(__rslib_import_meta_url__).resolve('@midscene/android/package.json');
|
|
224
|
+
return (0, _resource_path__rspack_import_3.J)(node_path__rspack_import_1_default().join(node_path__rspack_import_1_default().dirname(androidPkgJson), 'bin', 'scrcpy-server'));
|
|
227
225
|
}
|
|
228
226
|
getFfmpegPath() {
|
|
229
227
|
try {
|
|
230
|
-
const dynamicRequire = (0,
|
|
228
|
+
const dynamicRequire = (0, node_module__rspack_import_0.createRequire)(__rslib_import_meta_url__);
|
|
231
229
|
const ffmpegInstaller = dynamicRequire('@ffmpeg-installer/ffmpeg');
|
|
232
230
|
debugScrcpy(`Using ffmpeg from npm package: ${ffmpegInstaller.path}`);
|
|
233
231
|
return ffmpegInstaller.path;
|
|
@@ -766,36 +764,63 @@ var __webpack_modules__ = {
|
|
|
766
764
|
isConnected() {
|
|
767
765
|
return this.isInitialized && null !== this.scrcpyClient;
|
|
768
766
|
}
|
|
769
|
-
constructor(adb, options = {}){
|
|
767
|
+
constructor(adb, pushServer, options = {}){
|
|
768
|
+
_define_property(this, "pushServer", void 0);
|
|
770
769
|
_define_property(this, "adb", void 0);
|
|
771
|
-
_define_property(this, "scrcpyClient",
|
|
772
|
-
_define_property(this, "videoStream",
|
|
773
|
-
_define_property(this, "spsHeader",
|
|
774
|
-
_define_property(this, "idleTimer",
|
|
775
|
-
_define_property(this, "connectionPromise",
|
|
776
|
-
_define_property(this, "disposed",
|
|
777
|
-
_define_property(this, "isInitialized",
|
|
770
|
+
_define_property(this, "scrcpyClient", void 0);
|
|
771
|
+
_define_property(this, "videoStream", void 0);
|
|
772
|
+
_define_property(this, "spsHeader", void 0);
|
|
773
|
+
_define_property(this, "idleTimer", void 0);
|
|
774
|
+
_define_property(this, "connectionPromise", void 0);
|
|
775
|
+
_define_property(this, "disposed", void 0);
|
|
776
|
+
_define_property(this, "isInitialized", void 0);
|
|
778
777
|
_define_property(this, "options", void 0);
|
|
779
|
-
_define_property(this, "ffmpegAvailable",
|
|
780
|
-
_define_property(this, "keyframeResolvers",
|
|
781
|
-
_define_property(this, "keyframeListeners",
|
|
782
|
-
_define_property(this, "lastRawKeyframe",
|
|
783
|
-
_define_property(this, "lastRawKeyframeAt", 0);
|
|
778
|
+
_define_property(this, "ffmpegAvailable", void 0);
|
|
779
|
+
_define_property(this, "keyframeResolvers", void 0);
|
|
780
|
+
_define_property(this, "keyframeListeners", void 0);
|
|
781
|
+
_define_property(this, "lastRawKeyframe", void 0);
|
|
782
|
+
_define_property(this, "lastRawKeyframeAt", void 0);
|
|
784
783
|
_define_property(this, "lastRawKeyframePtsUs", void 0);
|
|
785
784
|
_define_property(this, "lastRawKeyframeEstimatedAgeMs", void 0);
|
|
786
|
-
_define_property(this, "videoResolution",
|
|
787
|
-
_define_property(this, "streamReader",
|
|
788
|
-
_define_property(this, "frameFreshnessBarrierPtsUs",
|
|
789
|
-
_define_property(this, "frameFreshnessBarrierReason",
|
|
790
|
-
_define_property(this, "streamStartupWindow",
|
|
791
|
-
_define_property(this, "frameFreshnessBarrierPending",
|
|
792
|
-
_define_property(this, "frameFreshnessBarrierGeneration", 0);
|
|
793
|
-
_define_property(this, "deviceClockCalibration",
|
|
794
|
-
_define_property(this, "deviceClockCalibrationPromise",
|
|
795
|
-
_define_property(this, "lastFramePtsUs",
|
|
796
|
-
_define_property(this, "frameFreshnessError",
|
|
797
|
-
_define_property(this, "lastFrameFreshnessWarningAt", 0);
|
|
798
|
-
_define_property(this, "disposePromise",
|
|
785
|
+
_define_property(this, "videoResolution", void 0);
|
|
786
|
+
_define_property(this, "streamReader", void 0);
|
|
787
|
+
_define_property(this, "frameFreshnessBarrierPtsUs", void 0);
|
|
788
|
+
_define_property(this, "frameFreshnessBarrierReason", void 0);
|
|
789
|
+
_define_property(this, "streamStartupWindow", void 0);
|
|
790
|
+
_define_property(this, "frameFreshnessBarrierPending", void 0);
|
|
791
|
+
_define_property(this, "frameFreshnessBarrierGeneration", void 0);
|
|
792
|
+
_define_property(this, "deviceClockCalibration", void 0);
|
|
793
|
+
_define_property(this, "deviceClockCalibrationPromise", void 0);
|
|
794
|
+
_define_property(this, "lastFramePtsUs", void 0);
|
|
795
|
+
_define_property(this, "frameFreshnessError", void 0);
|
|
796
|
+
_define_property(this, "lastFrameFreshnessWarningAt", void 0);
|
|
797
|
+
_define_property(this, "disposePromise", void 0);
|
|
798
|
+
this.pushServer = pushServer;
|
|
799
|
+
this.scrcpyClient = null;
|
|
800
|
+
this.videoStream = null;
|
|
801
|
+
this.spsHeader = null;
|
|
802
|
+
this.idleTimer = null;
|
|
803
|
+
this.connectionPromise = null;
|
|
804
|
+
this.disposed = false;
|
|
805
|
+
this.isInitialized = false;
|
|
806
|
+
this.ffmpegAvailable = null;
|
|
807
|
+
this.keyframeResolvers = [];
|
|
808
|
+
this.keyframeListeners = new Set();
|
|
809
|
+
this.lastRawKeyframe = null;
|
|
810
|
+
this.lastRawKeyframeAt = 0;
|
|
811
|
+
this.videoResolution = null;
|
|
812
|
+
this.streamReader = null;
|
|
813
|
+
this.frameFreshnessBarrierPtsUs = null;
|
|
814
|
+
this.frameFreshnessBarrierReason = null;
|
|
815
|
+
this.streamStartupWindow = null;
|
|
816
|
+
this.frameFreshnessBarrierPending = false;
|
|
817
|
+
this.frameFreshnessBarrierGeneration = 0;
|
|
818
|
+
this.deviceClockCalibration = null;
|
|
819
|
+
this.deviceClockCalibrationPromise = null;
|
|
820
|
+
this.lastFramePtsUs = null;
|
|
821
|
+
this.frameFreshnessError = null;
|
|
822
|
+
this.lastFrameFreshnessWarningAt = 0;
|
|
823
|
+
this.disposePromise = null;
|
|
799
824
|
this.adb = adb;
|
|
800
825
|
const requestedBitRate = options.videoBitRate ?? DEFAULT_VIDEO_BIT_RATE;
|
|
801
826
|
const clampedBitRate = Math.min(requestedBitRate, MAX_VIDEO_BIT_RATE);
|
|
@@ -979,10 +1004,6 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
979
1004
|
const bitRateDescription = Number.isFinite(videoBitRate) ? ` Current videoBitRate: ${videoBitRate} bps (${videoBitRate / 1000000} Mbps).` : '';
|
|
980
1005
|
return `No usable scrcpy frame crossed the active freshness target within ${timeoutMs}ms. This can happen when a static screen emits no new encoded frame, or when the video stream or transport is interrupted. This timeout does not by itself identify bandwidth or the configured video bitrate as the cause.${bitRateDescription}`;
|
|
981
1006
|
}
|
|
982
|
-
const DEFAULT_ADB_SERVER_ENDPOINT = {
|
|
983
|
-
host: '127.0.0.1',
|
|
984
|
-
port: 5037
|
|
985
|
-
};
|
|
986
1007
|
class ScrcpyDeviceAdapter {
|
|
987
1008
|
isEnabled() {
|
|
988
1009
|
if (!this.isConfigured()) return false;
|
|
@@ -1047,13 +1068,18 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
1047
1068
|
const { Adb, AdbServerClient } = await import("@yume-chan/adb");
|
|
1048
1069
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
1049
1070
|
const { ScrcpyScreenshotManager: ScrcpyManager } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/scrcpy-manager.ts"));
|
|
1050
|
-
const
|
|
1051
|
-
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector(
|
|
1071
|
+
const adbBackend = await this.resolveAdbBackend();
|
|
1072
|
+
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector({
|
|
1073
|
+
host: adbBackend.adbHost ?? '127.0.0.1',
|
|
1074
|
+
port: adbBackend.adbPort ?? 5037
|
|
1075
|
+
}));
|
|
1052
1076
|
adb = new Adb(await adbClient.createTransport({
|
|
1053
1077
|
serial: this.deviceId
|
|
1054
1078
|
}));
|
|
1055
1079
|
const config = this.resolveConfig();
|
|
1056
|
-
manager = new ScrcpyManager(adb, {
|
|
1080
|
+
manager = new ScrcpyManager(adb, async (localPath, remotePath)=>{
|
|
1081
|
+
await adbBackend.push(localPath, remotePath);
|
|
1082
|
+
}, {
|
|
1057
1083
|
maxSize: config.maxSize,
|
|
1058
1084
|
videoBitRate: config.videoBitRate,
|
|
1059
1085
|
idleTimeoutMs: config.idleTimeoutMs
|
|
@@ -1247,10 +1273,13 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
1247
1273
|
this.resolvedConfig = null;
|
|
1248
1274
|
this.clearFailure();
|
|
1249
1275
|
}
|
|
1250
|
-
constructor(deviceId, scrcpyConfig,
|
|
1276
|
+
constructor(deviceId, scrcpyConfig, resolveAdbBackend = ()=>createAndroidAdb({
|
|
1277
|
+
adbExecTimeout: 60000,
|
|
1278
|
+
deviceId
|
|
1279
|
+
})){
|
|
1251
1280
|
_define_property(this, "deviceId", void 0);
|
|
1252
1281
|
_define_property(this, "scrcpyConfig", void 0);
|
|
1253
|
-
_define_property(this, "
|
|
1282
|
+
_define_property(this, "resolveAdbBackend", void 0);
|
|
1254
1283
|
_define_property(this, "manager", void 0);
|
|
1255
1284
|
_define_property(this, "managerPromise", void 0);
|
|
1256
1285
|
_define_property(this, "resolvedConfig", void 0);
|
|
@@ -1263,7 +1292,7 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
1263
1292
|
_define_property(this, "keyframeUnsubscribers", void 0);
|
|
1264
1293
|
this.deviceId = deviceId;
|
|
1265
1294
|
this.scrcpyConfig = scrcpyConfig;
|
|
1266
|
-
this.
|
|
1295
|
+
this.resolveAdbBackend = resolveAdbBackend;
|
|
1267
1296
|
this.manager = null;
|
|
1268
1297
|
this.managerPromise = null;
|
|
1269
1298
|
this.resolvedConfig = null;
|
|
@@ -1763,13 +1792,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1763
1792
|
return adapter.getStatus();
|
|
1764
1793
|
}
|
|
1765
1794
|
getScrcpyAdapter() {
|
|
1766
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig,
|
|
1767
|
-
const adb = await this.getAdb();
|
|
1768
|
-
return {
|
|
1769
|
-
host: adb.adbHost ?? '127.0.0.1',
|
|
1770
|
-
port: adb.adbPort ?? 5037
|
|
1771
|
-
};
|
|
1772
|
-
});
|
|
1795
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig, ()=>this.getAdb());
|
|
1773
1796
|
return this.scrcpyAdapter;
|
|
1774
1797
|
}
|
|
1775
1798
|
async openScrcpyFrameSource() {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -23,11 +23,6 @@ import { UITreeSnapshot } from '@midscene/core';
|
|
|
23
23
|
|
|
24
24
|
declare type ActionArgs<T extends DeviceAction> = [ActionParam<T>] extends [undefined] ? [] : [ActionParam<T>];
|
|
25
25
|
|
|
26
|
-
declare interface AdbServerEndpoint {
|
|
27
|
-
host: string;
|
|
28
|
-
port: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
26
|
export declare function agentFromAdbDevice(deviceId?: string, opts?: AndroidAgentOpt & AndroidDeviceOpt): Promise<AndroidAgent>;
|
|
32
27
|
|
|
33
28
|
export declare class AndroidAgent extends Agent<AndroidDevice> {
|
|
@@ -393,8 +388,6 @@ declare interface RawKeyframe {
|
|
|
393
388
|
capturedAt: number;
|
|
394
389
|
}
|
|
395
390
|
|
|
396
|
-
declare type ResolveAdbServerEndpoint = () => AdbServerEndpoint | Promise<AdbServerEndpoint>;
|
|
397
|
-
|
|
398
391
|
declare interface ResolvedScrcpyConfig {
|
|
399
392
|
enabled: boolean;
|
|
400
393
|
maxSize: number;
|
|
@@ -402,6 +395,8 @@ declare interface ResolvedScrcpyConfig {
|
|
|
402
395
|
idleTimeoutMs: number;
|
|
403
396
|
}
|
|
404
397
|
|
|
398
|
+
export declare type ResolveScrcpyAdbBackend = () => ScrcpyAdbBackend | Promise<ScrcpyAdbBackend>;
|
|
399
|
+
|
|
405
400
|
declare type RunAdbShellOpt = {
|
|
406
401
|
/**
|
|
407
402
|
* ADB shell command execution timeout in milliseconds.
|
|
@@ -409,6 +404,13 @@ declare type RunAdbShellOpt = {
|
|
|
409
404
|
timeout?: number;
|
|
410
405
|
};
|
|
411
406
|
|
|
407
|
+
/** ADB capabilities scrcpy needs from the canonical Appium transport. */
|
|
408
|
+
export declare interface ScrcpyAdbBackend {
|
|
409
|
+
adbHost?: string;
|
|
410
|
+
adbPort?: number;
|
|
411
|
+
push(localPath: string, remotePath: string): Promise<unknown>;
|
|
412
|
+
}
|
|
413
|
+
|
|
412
414
|
declare interface ScrcpyConfig {
|
|
413
415
|
enabled?: boolean;
|
|
414
416
|
maxSize?: number;
|
|
@@ -423,7 +425,7 @@ declare interface ScrcpyConfig {
|
|
|
423
425
|
export declare class ScrcpyDeviceAdapter {
|
|
424
426
|
private deviceId;
|
|
425
427
|
private scrcpyConfig;
|
|
426
|
-
private
|
|
428
|
+
private resolveAdbBackend;
|
|
427
429
|
private manager;
|
|
428
430
|
private managerPromise;
|
|
429
431
|
private resolvedConfig;
|
|
@@ -434,7 +436,7 @@ export declare class ScrcpyDeviceAdapter {
|
|
|
434
436
|
private pendingActionBarrierAtHostUs;
|
|
435
437
|
private keyframeListeners;
|
|
436
438
|
private keyframeUnsubscribers;
|
|
437
|
-
constructor(deviceId: string, scrcpyConfig: ScrcpyConfig | undefined,
|
|
439
|
+
constructor(deviceId: string, scrcpyConfig: ScrcpyConfig | undefined, resolveAdbBackend?: ResolveScrcpyAdbBackend);
|
|
438
440
|
isEnabled(): boolean;
|
|
439
441
|
getStatus(): ScrcpyStatus;
|
|
440
442
|
private isConfigured;
|
|
@@ -527,6 +529,7 @@ declare interface ScrcpyFreshnessBarrierOptions {
|
|
|
527
529
|
}
|
|
528
530
|
|
|
529
531
|
declare class ScrcpyScreenshotManager {
|
|
532
|
+
private readonly pushServer;
|
|
530
533
|
private adb;
|
|
531
534
|
private scrcpyClient;
|
|
532
535
|
private videoStream;
|
|
@@ -556,7 +559,7 @@ declare class ScrcpyScreenshotManager {
|
|
|
556
559
|
private frameFreshnessError;
|
|
557
560
|
private lastFrameFreshnessWarningAt;
|
|
558
561
|
private disposePromise;
|
|
559
|
-
constructor(adb: Adb, options?: ScrcpyScreenshotOptions);
|
|
562
|
+
constructor(adb: Adb, pushServer: ScrcpyServerPusher, options?: ScrcpyScreenshotOptions);
|
|
560
563
|
/**
|
|
561
564
|
* Validate environment prerequisites (ffmpeg, scrcpy-server, etc.)
|
|
562
565
|
* Must be called once after construction, before any screenshot operations.
|
|
@@ -719,6 +722,9 @@ declare interface ScrcpyScreenshotOptions {
|
|
|
719
722
|
idleTimeoutMs?: number;
|
|
720
723
|
}
|
|
721
724
|
|
|
725
|
+
/** Transfers the local scrcpy server binary to its device path. */
|
|
726
|
+
declare type ScrcpyServerPusher = (localPath: string, remotePath: string) => Promise<void>;
|
|
727
|
+
|
|
722
728
|
export declare interface ScrcpyStatus {
|
|
723
729
|
enabled: boolean;
|
|
724
730
|
connected: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midscene/android",
|
|
3
|
-
"version": "1.12.2
|
|
3
|
+
"version": "1.12.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/web-infra-dev/midscene.git",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"@yume-chan/stream-extra": "2.6.1",
|
|
42
42
|
"appium-adb": "12.12.1",
|
|
43
43
|
"sharp": "^0.34.3",
|
|
44
|
-
"@midscene/
|
|
45
|
-
"@midscene/
|
|
44
|
+
"@midscene/shared": "1.12.2",
|
|
45
|
+
"@midscene/core": "1.12.2"
|
|
46
46
|
},
|
|
47
47
|
"optionalDependencies": {
|
|
48
48
|
"@ffmpeg-installer/ffmpeg": "^1.1.0"
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"typescript": "^5.8.3",
|
|
57
57
|
"undici": "^6.0.0",
|
|
58
58
|
"zod": "^3.25.1",
|
|
59
|
-
"@midscene/playground": "1.12.2
|
|
59
|
+
"@midscene/playground": "1.12.2"
|
|
60
60
|
},
|
|
61
61
|
"license": "MIT",
|
|
62
62
|
"scripts": {
|