appium-ios-remotexpc 5.14.0 → 5.14.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/CHANGELOG.md +12 -0
- package/build/src/services/ios/display/audio/audio-stream-capture.d.ts.map +1 -1
- package/build/src/services/ios/display/audio/audio-stream-capture.js +16 -1
- package/build/src/services/ios/display/audio/audio-stream-capture.js.map +1 -1
- package/build/src/services/ios/display/recording/av-capture.d.ts.map +1 -1
- package/build/src/services/ios/display/recording/av-capture.js +24 -17
- package/build/src/services/ios/display/recording/av-capture.js.map +1 -1
- package/build/src/services/ios/display/video/screen-stream-capture.d.ts +33 -0
- package/build/src/services/ios/display/video/screen-stream-capture.d.ts.map +1 -1
- package/build/src/services/ios/display/video/screen-stream-capture.js +62 -13
- package/build/src/services/ios/display/video/screen-stream-capture.js.map +1 -1
- package/package.json +1 -1
- package/src/services/ios/display/audio/audio-stream-capture.ts +19 -1
- package/src/services/ios/display/recording/av-capture.ts +27 -18
- package/src/services/ios/display/video/screen-stream-capture.ts +66 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [5.14.2](https://github.com/appium/appium-ios-remotexpc/compare/v5.14.1...v5.14.2) (2026-08-11)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* **display:** stop captures when the recording's output file cannot be created ([#290](https://github.com/appium/appium-ios-remotexpc/issues/290)) ([18ad748](https://github.com/appium/appium-ios-remotexpc/commit/18ad7480bf5a82888a7ea9a574e937ce11b5eaba))
|
|
6
|
+
|
|
7
|
+
## [5.14.1](https://github.com/appium/appium-ios-remotexpc/compare/v5.14.0...v5.14.1) (2026-08-11)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **display:** surface screen-recording write errors instead of crashing ([#289](https://github.com/appium/appium-ios-remotexpc/issues/289)) ([2ef7852](https://github.com/appium/appium-ios-remotexpc/commit/2ef7852814d01bcc08202e82434ebea74424d8de))
|
|
12
|
+
|
|
1
13
|
## [5.14.0](https://github.com/appium/appium-ios-remotexpc/compare/v5.13.6...v5.14.0) (2026-08-09)
|
|
2
14
|
|
|
3
15
|
### Features
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-stream-capture.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/display/audio/audio-stream-capture.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,cAAc,EAAE,iBAAiB,EAAE,uBAAuB,EAAC,MAAM,aAAa,CAAC;AAI5F,OAAO,EAAiB,KAAK,YAAY,EAAmB,MAAM,cAAc,CAAC;AAKjF,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,oDAAoD;AACpD,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACxE,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,kBAAkB;IAiB3B,QAAQ,CAAC,MAAM,EAAE,iBAAiB;IAhBpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IAEzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IAErE,OAAO,CAAC,aAAa,CAAiF;IACtG,OAAO,CAAC,YAAY,CAAqB;IACzC,wEAAwE;IACxE,OAAO,CAAC,SAAS,CAA4B;IAC7C,kFAAkF;IAClF,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO;IASP;;;;;;;;OAQG;WACU,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,GAAE,yBAA8B,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuBjH,2EAA2E;IAC3E,IAAI,MAAM,IAAI,YAAY,CAEzB;IAED,0CAA0C;IAC1C,IAAI,KAAK,IAAI,gBAAgB,CAE5B;IAED;;;;OAIG;IACI,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,eAAe,EAAE,IAAI,EAAE,OAAO,CAAC;IAgCxF;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAKb,QAAQ;CAOvB;AAED,6CAA6C;AAC7C,MAAM,WAAW,kBAAmB,SAAQ,yBAAyB;IACnE,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,MAAM,EAAE,YAAY,CAAC;IACrB,oDAAoD;IACpD,KAAK,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"audio-stream-capture.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/display/audio/audio-stream-capture.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,cAAc,EAAE,iBAAiB,EAAE,uBAAuB,EAAC,MAAM,aAAa,CAAC;AAI5F,OAAO,EAAiB,KAAK,YAAY,EAAmB,MAAM,cAAc,CAAC;AAKjF,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,oDAAoD;AACpD,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACxE,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,kBAAkB;IAiB3B,QAAQ,CAAC,MAAM,EAAE,iBAAiB;IAhBpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IAEzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IAErE,OAAO,CAAC,aAAa,CAAiF;IACtG,OAAO,CAAC,YAAY,CAAqB;IACzC,wEAAwE;IACxE,OAAO,CAAC,SAAS,CAA4B;IAC7C,kFAAkF;IAClF,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO;IASP;;;;;;;;OAQG;WACU,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,GAAE,yBAA8B,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuBjH,2EAA2E;IAC3E,IAAI,MAAM,IAAI,YAAY,CAEzB;IAED,0CAA0C;IAC1C,IAAI,KAAK,IAAI,gBAAgB,CAE5B;IAED;;;;OAIG;IACI,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,eAAe,EAAE,IAAI,EAAE,OAAO,CAAC;IAgCxF;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAKb,QAAQ;CAOvB;AAED,6CAA6C;AAC7C,MAAM,WAAW,kBAAmB,SAAQ,yBAAyB;IACnE,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,MAAM,EAAE,YAAY,CAAC;IACrB,oDAAoD;IACpD,KAAK,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAgD5B"}
|
|
@@ -171,7 +171,22 @@ export async function recordAudioToFile(service, outputPath, options = {}) {
|
|
|
171
171
|
const controller = new AbortController();
|
|
172
172
|
const timer = setTimeout(() => controller.abort(), durationMs);
|
|
173
173
|
timer.unref?.();
|
|
174
|
-
|
|
174
|
+
let writer;
|
|
175
|
+
try {
|
|
176
|
+
writer = await M4aFileWriter.create(outputPath);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
// The capture is already streaming and never escapes this function, so a
|
|
180
|
+
// caller cannot stop it. Left running, the device keeps sending into a
|
|
181
|
+
// receiver whose queue nobody drains.
|
|
182
|
+
clearTimeout(timer);
|
|
183
|
+
// Swallowed so it cannot mask the error being thrown, but logged: a failed
|
|
184
|
+
// stop means the device is still streaming, which is the leak itself.
|
|
185
|
+
await capture.stop().catch((stopError) => {
|
|
186
|
+
log.debug(`Failed to stop the audio stream after the output file could not be created: ${stopError instanceof Error ? stopError.message : String(stopError)}`);
|
|
187
|
+
});
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
175
190
|
let written;
|
|
176
191
|
try {
|
|
177
192
|
for await (const unit of capture.accessUnits(controller.signal)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-stream-capture.js","sourceRoot":"","sources":["../../../../../../src/services/ios/display/audio/audio-stream-capture.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"audio-stream-capture.js","sourceRoot":"","sources":["../../../../../../src/services/ios/display/audio/audio-stream-capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"av-capture.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/display/recording/av-capture.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"av-capture.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/display/recording/av-capture.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,KAAK,YAAY,EAAmB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,kCAAkC,CAAC;AAE3F,OAAO,EAAC,KAAK,cAAc,EAAE,KAAK,uBAAuB,EAAC,MAAM,aAAa,CAAC;AAE9E,OAAO,EAAwC,KAAK,iBAAiB,EAAC,MAAM,mCAAmC,CAAC;AAChH,OAAO,EAAC,KAAK,UAAU,EAA0B,MAAM,kBAAkB,CAAC;AAI1E,uDAAuD;AACvD,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IAC1E,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,yDAAyD;AACzD,MAAM,WAAW,0BAA0B;IACzC,uBAAuB;IACvB,KAAK,EAAE;QACL,uDAAuD;QACvD,IAAI,EAAE,MAAM,CAAC;QACb,qCAAqC;QACrC,aAAa,EAAE,MAAM,CAAC;QACtB,qBAAqB;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,0EAA0E;QAC1E,UAAU,EAAE,MAAM,CAAC;QACnB;;;;;;WAMG;QACH,SAAS,EAAE,MAAM,CAAC;QAClB,mEAAmE;QACnE,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,wBAAwB;QACxB,KAAK,EAAE,iBAAiB,CAAC;KAC1B,CAAC;IACF,uBAAuB;IACvB,KAAK,EAAE;QACL,oCAAoC;QACpC,IAAI,EAAE,MAAM,CAAC;QACb,4BAA4B;QAC5B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,qBAAqB;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB;;;;;WAKG;QACH,UAAU,EAAE,MAAM,CAAC;QACnB,iCAAiC;QACjC,MAAM,EAAE,YAAY,CAAC;QACrB,wBAAwB;QACxB,KAAK,EAAE,gBAAgB,CAAC;KACzB,CAAC;IACF;;;;;;;OAOG;IACH,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,0BAA0B,CAAC,CA8HrC"}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { once } from 'node:events';
|
|
2
|
-
import { createWriteStream } from 'node:fs';
|
|
3
1
|
import { getLogger } from '../../../../lib/logger.js';
|
|
4
2
|
import { XPCUUID } from '../../../../lib/remote-xpc/xpc-uuid.js';
|
|
5
3
|
import { aacEldDurationMs } from '../audio/aac-eld.js';
|
|
6
4
|
import { AudioStreamCapture } from '../audio/audio-stream-capture.js';
|
|
7
5
|
import { M4aFileWriter } from '../audio/m4a-writer.js';
|
|
8
6
|
import { toAnnexB } from '../video/hevc.js';
|
|
9
|
-
import { ScreenStreamCapture } from '../video/screen-stream-capture.js';
|
|
7
|
+
import { AnnexBFileWriter, ScreenStreamCapture } from '../video/screen-stream-capture.js';
|
|
10
8
|
import { ffmpegMuxCommandBuilder } from './mux-command.js';
|
|
11
9
|
const log = getLogger('AvCapture');
|
|
12
10
|
/**
|
|
@@ -52,8 +50,27 @@ export async function recordScreenAndAudioToFiles(service, options) {
|
|
|
52
50
|
await videoCapture.stop().catch(() => undefined);
|
|
53
51
|
throw error;
|
|
54
52
|
}
|
|
55
|
-
const videoOut =
|
|
56
|
-
|
|
53
|
+
const videoOut = new AnnexBFileWriter(videoPath);
|
|
54
|
+
let audioOut;
|
|
55
|
+
try {
|
|
56
|
+
audioOut = await M4aFileWriter.create(audioPath);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
// Both captures are already streaming by now, and neither they nor the
|
|
60
|
+
// video writer escape this function, so a caller cannot release them. Left
|
|
61
|
+
// running, the device keeps encoding into a receiver whose queue nobody
|
|
62
|
+
// drains, and its RTCP keepalive keeps the session alive indefinitely.
|
|
63
|
+
// Each failure is swallowed so it cannot mask the error being thrown. The
|
|
64
|
+
// stop is still logged: if it fails the device is left streaming, which is
|
|
65
|
+
// the leak itself. One stop tears down both streams, so the second call and
|
|
66
|
+
// the file close are quiet.
|
|
67
|
+
await videoCapture.stop().catch((stopError) => {
|
|
68
|
+
log.debug(`Failed to stop cleanly after the audio file could not be created: ${stopError instanceof Error ? stopError.message : String(stopError)}`);
|
|
69
|
+
});
|
|
70
|
+
await audioCapture.stop().catch(() => undefined);
|
|
71
|
+
await videoOut.close().catch(() => undefined);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
57
74
|
let framesWritten = 0;
|
|
58
75
|
let videoBytes = 0;
|
|
59
76
|
let sawKeyFrame = false;
|
|
@@ -70,9 +87,7 @@ export async function recordScreenAndAudioToFiles(service, options) {
|
|
|
70
87
|
sawKeyFrame = true;
|
|
71
88
|
}
|
|
72
89
|
const chunk = toAnnexB(unit.nals);
|
|
73
|
-
|
|
74
|
-
await once(videoOut, 'drain');
|
|
75
|
-
}
|
|
90
|
+
await videoOut.write(chunk);
|
|
76
91
|
framesWritten += 1;
|
|
77
92
|
videoBytes += chunk.length;
|
|
78
93
|
}
|
|
@@ -102,15 +117,7 @@ export async function recordScreenAndAudioToFiles(service, options) {
|
|
|
102
117
|
});
|
|
103
118
|
await audioCapture.stop().catch(() => undefined);
|
|
104
119
|
audioWritten = await audioOut.close();
|
|
105
|
-
await
|
|
106
|
-
videoOut.end((error) => {
|
|
107
|
-
if (error) {
|
|
108
|
-
reject(error);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
resolve();
|
|
112
|
-
});
|
|
113
|
-
});
|
|
120
|
+
await videoOut.close();
|
|
114
121
|
}
|
|
115
122
|
const audioDurationMs = aacEldDurationMs(audioWritten.sampleCount);
|
|
116
123
|
// Measure against the video's own capture window, never the audio clock: if
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"av-capture.js","sourceRoot":"","sources":["../../../../../../src/services/ios/display/recording/av-capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"av-capture.js","sourceRoot":"","sources":["../../../../../../src/services/ios/display/recording/av-capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -98,6 +98,39 @@ export declare class ScreenStreamCapture {
|
|
|
98
98
|
*/
|
|
99
99
|
private consume;
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* The Annex-B output file, with the write stream's `'error'` event held instead
|
|
103
|
+
* of left to reach the process.
|
|
104
|
+
*
|
|
105
|
+
* A recording runs unattended for minutes and the stream never escapes the
|
|
106
|
+
* function that owns it, so a caller cannot attach a listener of its own.
|
|
107
|
+
* Without one here, a mistyped path or a write that fails after the fact (a full
|
|
108
|
+
* disk, an unplugged volume) arrives as an uncaught `'error'` event and takes
|
|
109
|
+
* the process down — `end(callback)` does not help, since the callback receives
|
|
110
|
+
* the error *and* the event is still emitted. Holding the first error and
|
|
111
|
+
* re-throwing it from the next `write` or `close` turns that into a rejection
|
|
112
|
+
* the caller can see, the guarantee {@link M4aFileWriter} already gives the
|
|
113
|
+
* audio track.
|
|
114
|
+
*
|
|
115
|
+
* Opening is deliberately not awaited, unlike `M4aFileWriter.create`: the
|
|
116
|
+
* capture is already streaming by the time the file is created, so a rejection
|
|
117
|
+
* before the caller holds the writer would strand it. An open failure surfaces
|
|
118
|
+
* from the first {@link write} instead.
|
|
119
|
+
*/
|
|
120
|
+
export declare class AnnexBFileWriter {
|
|
121
|
+
private readonly stream;
|
|
122
|
+
/** First stream error seen, re-thrown from the next `write` or `close`. */
|
|
123
|
+
private streamError;
|
|
124
|
+
/** @param path Destination file path; truncated if it exists. */
|
|
125
|
+
constructor(path: string);
|
|
126
|
+
/**
|
|
127
|
+
* Appends one chunk, resolving once the stream has room for more so an
|
|
128
|
+
* `await` per chunk applies backpressure.
|
|
129
|
+
*/
|
|
130
|
+
write(chunk: Buffer): Promise<void>;
|
|
131
|
+
/** Flushes and closes the file, re-throwing any error the stream saw. */
|
|
132
|
+
close(): Promise<void>;
|
|
133
|
+
}
|
|
101
134
|
/** Options for {@link recordScreenToFile}. */
|
|
102
135
|
export interface RecordScreenOptions extends ScreenStreamCaptureOptions {
|
|
103
136
|
/** How long to record, in milliseconds. Defaults to 5000. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen-stream-capture.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/display/video/screen-stream-capture.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,cAAc,EAAE,iBAAiB,EAAE,uBAAuB,EAAC,MAAM,aAAa,CAAC;AAG5F,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAC,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAC,CAAC;AAIpE,qDAAqD;AACrD,MAAM,WAAW,0BAA2B,SAAQ,uBAAuB;IACzE,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,mBAAmB;IAkB5B,QAAQ,CAAC,MAAM,EAAE,iBAAiB;IAjBpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,wEAAwE;IACxE,OAAO,CAAC,SAAS,CAA4B;IAE7C;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO;IASP;;;;;;;;OAQG;WACU,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,GAAE,0BAA+B,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAuBnH,+EAA+E;IAC/E,IAAI,aAAa,IAAI,iBAAiB,GAAG,SAAS,CAEjD;IAED;;;OAGG;IACH,IAAI,WAAW,IAAI,MAAM,GAAG,SAAS,CAEpC;IAED;;;OAGG;IACH,IAAI,0BAA0B,IAAI,MAAM,GAAG,SAAS,CAEnD;IAED,0CAA0C;IAC1C,IAAI,KAAK,IAAI,iBAAiB,CAE7B;IAED;;;;OAIG;IACI,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,eAAe,EAAE,IAAI,EAAE,OAAO,CAAC;IAexF;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAKb,QAAQ;IAQtB;;;OAGG;IACH,OAAO,CAAC,OAAO;CAQhB;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAoB,SAAQ,0BAA0B;IACrE,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"screen-stream-capture.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/display/video/screen-stream-capture.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,cAAc,EAAE,iBAAiB,EAAE,uBAAuB,EAAC,MAAM,aAAa,CAAC;AAG5F,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAC,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAC,CAAC;AAIpE,qDAAqD;AACrD,MAAM,WAAW,0BAA2B,SAAQ,uBAAuB;IACzE,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,mBAAmB;IAkB5B,QAAQ,CAAC,MAAM,EAAE,iBAAiB;IAjBpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,wEAAwE;IACxE,OAAO,CAAC,SAAS,CAA4B;IAE7C;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO;IASP;;;;;;;;OAQG;WACU,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,GAAE,0BAA+B,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAuBnH,+EAA+E;IAC/E,IAAI,aAAa,IAAI,iBAAiB,GAAG,SAAS,CAEjD;IAED;;;OAGG;IACH,IAAI,WAAW,IAAI,MAAM,GAAG,SAAS,CAEpC;IAED;;;OAGG;IACH,IAAI,0BAA0B,IAAI,MAAM,GAAG,SAAS,CAEnD;IAED,0CAA0C;IAC1C,IAAI,KAAK,IAAI,iBAAiB,CAE7B;IAED;;;;OAIG;IACI,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,eAAe,EAAE,IAAI,EAAE,OAAO,CAAC;IAexF;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAKb,QAAQ;IAQtB;;;OAGG;IACH,OAAO,CAAC,OAAO;CAQhB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,2EAA2E;IAC3E,OAAO,CAAC,WAAW,CAAoB;IAEvC,iEAAiE;gBACrD,IAAI,EAAE,MAAM;IAOxB;;;OAGG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYzC,yEAAyE;IACnE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAY7B;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAoB,SAAQ,0BAA0B;IACrE,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAkD7B"}
|
|
@@ -162,6 +162,65 @@ export class ScreenStreamCapture {
|
|
|
162
162
|
return this.assembler.push(packet);
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* The Annex-B output file, with the write stream's `'error'` event held instead
|
|
167
|
+
* of left to reach the process.
|
|
168
|
+
*
|
|
169
|
+
* A recording runs unattended for minutes and the stream never escapes the
|
|
170
|
+
* function that owns it, so a caller cannot attach a listener of its own.
|
|
171
|
+
* Without one here, a mistyped path or a write that fails after the fact (a full
|
|
172
|
+
* disk, an unplugged volume) arrives as an uncaught `'error'` event and takes
|
|
173
|
+
* the process down — `end(callback)` does not help, since the callback receives
|
|
174
|
+
* the error *and* the event is still emitted. Holding the first error and
|
|
175
|
+
* re-throwing it from the next `write` or `close` turns that into a rejection
|
|
176
|
+
* the caller can see, the guarantee {@link M4aFileWriter} already gives the
|
|
177
|
+
* audio track.
|
|
178
|
+
*
|
|
179
|
+
* Opening is deliberately not awaited, unlike `M4aFileWriter.create`: the
|
|
180
|
+
* capture is already streaming by the time the file is created, so a rejection
|
|
181
|
+
* before the caller holds the writer would strand it. An open failure surfaces
|
|
182
|
+
* from the first {@link write} instead.
|
|
183
|
+
*/
|
|
184
|
+
export class AnnexBFileWriter {
|
|
185
|
+
stream;
|
|
186
|
+
/** First stream error seen, re-thrown from the next `write` or `close`. */
|
|
187
|
+
streamError;
|
|
188
|
+
/** @param path Destination file path; truncated if it exists. */
|
|
189
|
+
constructor(path) {
|
|
190
|
+
this.stream = createWriteStream(path);
|
|
191
|
+
this.stream.on('error', (error) => {
|
|
192
|
+
this.streamError ??= error;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Appends one chunk, resolving once the stream has room for more so an
|
|
197
|
+
* `await` per chunk applies backpressure.
|
|
198
|
+
*/
|
|
199
|
+
async write(chunk) {
|
|
200
|
+
if (this.streamError) {
|
|
201
|
+
throw this.streamError;
|
|
202
|
+
}
|
|
203
|
+
if (!this.stream.write(chunk)) {
|
|
204
|
+
await once(this.stream, 'drain');
|
|
205
|
+
}
|
|
206
|
+
if (this.streamError) {
|
|
207
|
+
throw this.streamError;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/** Flushes and closes the file, re-throwing any error the stream saw. */
|
|
211
|
+
async close() {
|
|
212
|
+
// The held error is reported in preference to ending an already-failed
|
|
213
|
+
// stream, which answers with a generic `ERR_STREAM_DESTROYED` and buries the
|
|
214
|
+
// cause. That is the common path for a bad output path: the open fails
|
|
215
|
+
// before the first keyframe arrives, so no `write` ever ran to surface it.
|
|
216
|
+
if (this.streamError) {
|
|
217
|
+
throw this.streamError;
|
|
218
|
+
}
|
|
219
|
+
await new Promise((resolve, reject) => {
|
|
220
|
+
this.stream.end((error) => (error ? reject(error) : resolve()));
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
165
224
|
/**
|
|
166
225
|
* Records the device's screen to an Annex-B HEVC elementary stream — the
|
|
167
226
|
* `.h265` layout that ffmpeg and VLC can play or remux directly:
|
|
@@ -180,7 +239,7 @@ export class ScreenStreamCapture {
|
|
|
180
239
|
export async function recordScreenToFile(service, outputPath, options = {}) {
|
|
181
240
|
const { durationMs = 5000, maxFrames = Number.POSITIVE_INFINITY, ...captureOptions } = options;
|
|
182
241
|
const capture = await ScreenStreamCapture.start(service, captureOptions);
|
|
183
|
-
const output =
|
|
242
|
+
const output = new AnnexBFileWriter(outputPath);
|
|
184
243
|
let framesWritten = 0;
|
|
185
244
|
let bytesWritten = 0;
|
|
186
245
|
let sawKeyFrame = false;
|
|
@@ -200,9 +259,7 @@ export async function recordScreenToFile(service, outputPath, options = {}) {
|
|
|
200
259
|
sawKeyFrame = true;
|
|
201
260
|
}
|
|
202
261
|
const chunk = toAnnexB(unit.nals);
|
|
203
|
-
|
|
204
|
-
await once(output, 'drain');
|
|
205
|
-
}
|
|
262
|
+
await output.write(chunk);
|
|
206
263
|
framesWritten += 1;
|
|
207
264
|
bytesWritten += chunk.length;
|
|
208
265
|
if (framesWritten >= maxFrames || performance.now() >= deadline) {
|
|
@@ -215,15 +272,7 @@ export async function recordScreenToFile(service, outputPath, options = {}) {
|
|
|
215
272
|
await capture.stop().catch((error) => {
|
|
216
273
|
log.debug(`Failed to stop the media stream cleanly: ${error instanceof Error ? error.message : String(error)}`);
|
|
217
274
|
});
|
|
218
|
-
await
|
|
219
|
-
output.end((error) => {
|
|
220
|
-
if (error) {
|
|
221
|
-
reject(error);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
resolve();
|
|
225
|
-
});
|
|
226
|
-
});
|
|
275
|
+
await output.close();
|
|
227
276
|
}
|
|
228
277
|
return {
|
|
229
278
|
outputPath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen-stream-capture.js","sourceRoot":"","sources":["../../../../../../src/services/ios/display/video/screen-stream-capture.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"screen-stream-capture.js","sourceRoot":"","sources":["../../../../../../src/services/ios/display/video/screen-stream-capture.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -242,7 +242,25 @@ export async function recordAudioToFile(
|
|
|
242
242
|
const timer = setTimeout(() => controller.abort(), durationMs);
|
|
243
243
|
timer.unref?.();
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
let writer: M4aFileWriter;
|
|
246
|
+
try {
|
|
247
|
+
writer = await M4aFileWriter.create(outputPath);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
// The capture is already streaming and never escapes this function, so a
|
|
250
|
+
// caller cannot stop it. Left running, the device keeps sending into a
|
|
251
|
+
// receiver whose queue nobody drains.
|
|
252
|
+
clearTimeout(timer);
|
|
253
|
+
// Swallowed so it cannot mask the error being thrown, but logged: a failed
|
|
254
|
+
// stop means the device is still streaming, which is the leak itself.
|
|
255
|
+
await capture.stop().catch((stopError: unknown) => {
|
|
256
|
+
log.debug(
|
|
257
|
+
`Failed to stop the audio stream after the output file could not be created: ${
|
|
258
|
+
stopError instanceof Error ? stopError.message : String(stopError)
|
|
259
|
+
}`,
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
246
264
|
let written;
|
|
247
265
|
try {
|
|
248
266
|
for await (const unit of capture.accessUnits(controller.signal)) {
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {once} from 'node:events';
|
|
2
|
-
import {createWriteStream} from 'node:fs';
|
|
3
|
-
|
|
4
1
|
import {getLogger} from '../../../../lib/logger.js';
|
|
5
2
|
import {XPCUUID} from '../../../../lib/remote-xpc/xpc-uuid.js';
|
|
6
3
|
import {type AacEldFormat, aacEldDurationMs} from '../audio/aac-eld.js';
|
|
@@ -8,7 +5,7 @@ import {AudioStreamCapture, type AudioStreamStats} from '../audio/audio-stream-c
|
|
|
8
5
|
import {M4aFileWriter} from '../audio/m4a-writer.js';
|
|
9
6
|
import {type DisplayService, type StartVideoStreamOptions} from '../index.js';
|
|
10
7
|
import {toAnnexB} from '../video/hevc.js';
|
|
11
|
-
import {ScreenStreamCapture, type ScreenStreamStats} from '../video/screen-stream-capture.js';
|
|
8
|
+
import {AnnexBFileWriter, ScreenStreamCapture, type ScreenStreamStats} from '../video/screen-stream-capture.js';
|
|
12
9
|
import {type MuxCommand, ffmpegMuxCommandBuilder} from './mux-command.js';
|
|
13
10
|
|
|
14
11
|
const log = getLogger('AvCapture');
|
|
@@ -127,8 +124,30 @@ export async function recordScreenAndAudioToFiles(
|
|
|
127
124
|
throw error;
|
|
128
125
|
}
|
|
129
126
|
|
|
130
|
-
const videoOut =
|
|
131
|
-
|
|
127
|
+
const videoOut = new AnnexBFileWriter(videoPath);
|
|
128
|
+
let audioOut: M4aFileWriter;
|
|
129
|
+
try {
|
|
130
|
+
audioOut = await M4aFileWriter.create(audioPath);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
// Both captures are already streaming by now, and neither they nor the
|
|
133
|
+
// video writer escape this function, so a caller cannot release them. Left
|
|
134
|
+
// running, the device keeps encoding into a receiver whose queue nobody
|
|
135
|
+
// drains, and its RTCP keepalive keeps the session alive indefinitely.
|
|
136
|
+
// Each failure is swallowed so it cannot mask the error being thrown. The
|
|
137
|
+
// stop is still logged: if it fails the device is left streaming, which is
|
|
138
|
+
// the leak itself. One stop tears down both streams, so the second call and
|
|
139
|
+
// the file close are quiet.
|
|
140
|
+
await videoCapture.stop().catch((stopError: unknown) => {
|
|
141
|
+
log.debug(
|
|
142
|
+
`Failed to stop cleanly after the audio file could not be created: ${
|
|
143
|
+
stopError instanceof Error ? stopError.message : String(stopError)
|
|
144
|
+
}`,
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
await audioCapture.stop().catch((): void => undefined);
|
|
148
|
+
await videoOut.close().catch((): void => undefined);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
132
151
|
let framesWritten = 0;
|
|
133
152
|
let videoBytes = 0;
|
|
134
153
|
let sawKeyFrame = false;
|
|
@@ -147,9 +166,7 @@ export async function recordScreenAndAudioToFiles(
|
|
|
147
166
|
sawKeyFrame = true;
|
|
148
167
|
}
|
|
149
168
|
const chunk = toAnnexB(unit.nals);
|
|
150
|
-
|
|
151
|
-
await once(videoOut, 'drain');
|
|
152
|
-
}
|
|
169
|
+
await videoOut.write(chunk);
|
|
153
170
|
framesWritten += 1;
|
|
154
171
|
videoBytes += chunk.length;
|
|
155
172
|
}
|
|
@@ -180,15 +197,7 @@ export async function recordScreenAndAudioToFiles(
|
|
|
180
197
|
});
|
|
181
198
|
await audioCapture.stop().catch((): void => undefined);
|
|
182
199
|
audioWritten = await audioOut.close();
|
|
183
|
-
await
|
|
184
|
-
videoOut.end((error?: Error | null): void => {
|
|
185
|
-
if (error) {
|
|
186
|
-
reject(error);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
resolve();
|
|
190
|
-
});
|
|
191
|
-
});
|
|
200
|
+
await videoOut.close();
|
|
192
201
|
}
|
|
193
202
|
|
|
194
203
|
const audioDurationMs = aacEldDurationMs(audioWritten.sampleCount);
|
|
@@ -191,6 +191,69 @@ export class ScreenStreamCapture {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
/**
|
|
195
|
+
* The Annex-B output file, with the write stream's `'error'` event held instead
|
|
196
|
+
* of left to reach the process.
|
|
197
|
+
*
|
|
198
|
+
* A recording runs unattended for minutes and the stream never escapes the
|
|
199
|
+
* function that owns it, so a caller cannot attach a listener of its own.
|
|
200
|
+
* Without one here, a mistyped path or a write that fails after the fact (a full
|
|
201
|
+
* disk, an unplugged volume) arrives as an uncaught `'error'` event and takes
|
|
202
|
+
* the process down — `end(callback)` does not help, since the callback receives
|
|
203
|
+
* the error *and* the event is still emitted. Holding the first error and
|
|
204
|
+
* re-throwing it from the next `write` or `close` turns that into a rejection
|
|
205
|
+
* the caller can see, the guarantee {@link M4aFileWriter} already gives the
|
|
206
|
+
* audio track.
|
|
207
|
+
*
|
|
208
|
+
* Opening is deliberately not awaited, unlike `M4aFileWriter.create`: the
|
|
209
|
+
* capture is already streaming by the time the file is created, so a rejection
|
|
210
|
+
* before the caller holds the writer would strand it. An open failure surfaces
|
|
211
|
+
* from the first {@link write} instead.
|
|
212
|
+
*/
|
|
213
|
+
export class AnnexBFileWriter {
|
|
214
|
+
private readonly stream: ReturnType<typeof createWriteStream>;
|
|
215
|
+
/** First stream error seen, re-thrown from the next `write` or `close`. */
|
|
216
|
+
private streamError: Error | undefined;
|
|
217
|
+
|
|
218
|
+
/** @param path Destination file path; truncated if it exists. */
|
|
219
|
+
constructor(path: string) {
|
|
220
|
+
this.stream = createWriteStream(path);
|
|
221
|
+
this.stream.on('error', (error: Error) => {
|
|
222
|
+
this.streamError ??= error;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Appends one chunk, resolving once the stream has room for more so an
|
|
228
|
+
* `await` per chunk applies backpressure.
|
|
229
|
+
*/
|
|
230
|
+
async write(chunk: Buffer): Promise<void> {
|
|
231
|
+
if (this.streamError) {
|
|
232
|
+
throw this.streamError;
|
|
233
|
+
}
|
|
234
|
+
if (!this.stream.write(chunk)) {
|
|
235
|
+
await once(this.stream, 'drain');
|
|
236
|
+
}
|
|
237
|
+
if (this.streamError) {
|
|
238
|
+
throw this.streamError;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Flushes and closes the file, re-throwing any error the stream saw. */
|
|
243
|
+
async close(): Promise<void> {
|
|
244
|
+
// The held error is reported in preference to ending an already-failed
|
|
245
|
+
// stream, which answers with a generic `ERR_STREAM_DESTROYED` and buries the
|
|
246
|
+
// cause. That is the common path for a bad output path: the open fails
|
|
247
|
+
// before the first keyframe arrives, so no `write` ever ran to surface it.
|
|
248
|
+
if (this.streamError) {
|
|
249
|
+
throw this.streamError;
|
|
250
|
+
}
|
|
251
|
+
await new Promise<void>((resolve, reject) => {
|
|
252
|
+
this.stream.end((error?: Error | null): void => (error ? reject(error) : resolve()));
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
194
257
|
/** Options for {@link recordScreenToFile}. */
|
|
195
258
|
export interface RecordScreenOptions extends ScreenStreamCaptureOptions {
|
|
196
259
|
/** How long to record, in milliseconds. Defaults to 5000. */
|
|
@@ -239,7 +302,7 @@ export async function recordScreenToFile(
|
|
|
239
302
|
const {durationMs = 5000, maxFrames = Number.POSITIVE_INFINITY, ...captureOptions} = options;
|
|
240
303
|
|
|
241
304
|
const capture = await ScreenStreamCapture.start(service, captureOptions);
|
|
242
|
-
const output =
|
|
305
|
+
const output = new AnnexBFileWriter(outputPath);
|
|
243
306
|
let framesWritten = 0;
|
|
244
307
|
let bytesWritten = 0;
|
|
245
308
|
let sawKeyFrame = false;
|
|
@@ -262,9 +325,7 @@ export async function recordScreenToFile(
|
|
|
262
325
|
}
|
|
263
326
|
|
|
264
327
|
const chunk = toAnnexB(unit.nals);
|
|
265
|
-
|
|
266
|
-
await once(output, 'drain');
|
|
267
|
-
}
|
|
328
|
+
await output.write(chunk);
|
|
268
329
|
framesWritten += 1;
|
|
269
330
|
bytesWritten += chunk.length;
|
|
270
331
|
|
|
@@ -277,15 +338,7 @@ export async function recordScreenToFile(
|
|
|
277
338
|
await capture.stop().catch((error: unknown) => {
|
|
278
339
|
log.debug(`Failed to stop the media stream cleanly: ${error instanceof Error ? error.message : String(error)}`);
|
|
279
340
|
});
|
|
280
|
-
await
|
|
281
|
-
output.end((error?: Error | null): void => {
|
|
282
|
-
if (error) {
|
|
283
|
-
reject(error);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
resolve();
|
|
287
|
-
});
|
|
288
|
-
});
|
|
341
|
+
await output.close();
|
|
289
342
|
}
|
|
290
343
|
|
|
291
344
|
return {
|