191 lines
6.7 KiB
JavaScript
191 lines
6.7 KiB
JavaScript
"use strict";
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
// file that has been converted to a CommonJS file using a Babel-
|
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
mod
|
|
));
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
var screencast_exports = {};
|
|
__export(screencast_exports, {
|
|
Screencast: () => Screencast
|
|
});
|
|
module.exports = __toCommonJS(screencast_exports);
|
|
var import_path = __toESM(require("path"));
|
|
var import_utils = require("../utils");
|
|
var import_utils2 = require("../utils");
|
|
var import_videoRecorder = require("./videoRecorder");
|
|
var import_page = require("./page");
|
|
var import_registry = require("./registry");
|
|
class Screencast {
|
|
constructor(page) {
|
|
this._videoRecorder = null;
|
|
this._videoId = null;
|
|
this._screencastClients = /* @__PURE__ */ new Set();
|
|
// Aiming at 25 fps by default - each frame is 40ms, but we give some slack with 35ms.
|
|
// When throttling for tracing, 200ms between frames, except for 10 frames around the action.
|
|
this._frameThrottler = new FrameThrottler(10, 35, 200);
|
|
this._frameListener = null;
|
|
this._page = page;
|
|
}
|
|
stopFrameThrottler() {
|
|
this._frameThrottler.dispose();
|
|
}
|
|
setOptions(options) {
|
|
this._setOptions(options).catch((e) => import_utils2.debugLogger.log("error", e));
|
|
this._frameThrottler.setThrottlingEnabled(!!options);
|
|
}
|
|
throttleFrameAck(ack) {
|
|
this._frameThrottler.ack(ack);
|
|
}
|
|
temporarilyDisableThrottling() {
|
|
this._frameThrottler.recharge();
|
|
}
|
|
launchVideoRecorder() {
|
|
const recordVideo = this._page.browserContext._options.recordVideo;
|
|
if (!recordVideo)
|
|
return void 0;
|
|
(0, import_utils.assert)(!this._videoId);
|
|
this._videoId = (0, import_utils.createGuid)();
|
|
const outputFile = import_path.default.join(recordVideo.dir, this._videoId + ".webm");
|
|
const videoOptions = {
|
|
// validateBrowserContextOptions ensures correct video size.
|
|
...recordVideo.size,
|
|
outputFile
|
|
};
|
|
const ffmpegPath = import_registry.registry.findExecutable("ffmpeg").executablePathOrDie(this._page.browserContext._browser.sdkLanguage());
|
|
this._videoRecorder = new import_videoRecorder.VideoRecorder(ffmpegPath, videoOptions);
|
|
this._frameListener = import_utils.eventsHelper.addEventListener(this._page, import_page.Page.Events.ScreencastFrame, (frame) => this._videoRecorder.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3));
|
|
this._page.waitForInitializedOrError().then((p) => {
|
|
if (p instanceof Error)
|
|
this.stopVideoRecording().catch(() => {
|
|
});
|
|
});
|
|
return videoOptions;
|
|
}
|
|
async startVideoRecording(options) {
|
|
const videoId = this._videoId;
|
|
(0, import_utils.assert)(videoId);
|
|
this._page.once(import_page.Page.Events.Close, () => this.stopVideoRecording().catch(() => {
|
|
}));
|
|
const gotFirstFrame = new Promise((f) => this._page.once(import_page.Page.Events.ScreencastFrame, f));
|
|
await this._startScreencast(this._videoRecorder, {
|
|
quality: 90,
|
|
width: options.width,
|
|
height: options.height
|
|
});
|
|
gotFirstFrame.then(() => {
|
|
this._page.browserContext._browser._videoStarted(this._page.browserContext, videoId, options.outputFile, this._page.waitForInitializedOrError());
|
|
});
|
|
}
|
|
async stopVideoRecording() {
|
|
if (!this._videoId)
|
|
return;
|
|
if (this._frameListener)
|
|
import_utils.eventsHelper.removeEventListeners([this._frameListener]);
|
|
this._frameListener = null;
|
|
const videoId = this._videoId;
|
|
this._videoId = null;
|
|
const videoRecorder = this._videoRecorder;
|
|
this._videoRecorder = null;
|
|
await this._stopScreencast(videoRecorder);
|
|
await videoRecorder.stop();
|
|
const video = this._page.browserContext._browser._takeVideo(videoId);
|
|
video?.reportFinished();
|
|
}
|
|
async _setOptions(options) {
|
|
if (options)
|
|
await this._startScreencast(this, options);
|
|
else
|
|
await this._stopScreencast(this);
|
|
}
|
|
async _startScreencast(client, options) {
|
|
this._screencastClients.add(client);
|
|
if (this._screencastClients.size === 1) {
|
|
await this._page.delegate.startScreencast({
|
|
width: options.width,
|
|
height: options.height,
|
|
quality: options.quality
|
|
});
|
|
}
|
|
}
|
|
async _stopScreencast(client) {
|
|
this._screencastClients.delete(client);
|
|
if (!this._screencastClients.size)
|
|
await this._page.delegate.stopScreencast();
|
|
}
|
|
}
|
|
class FrameThrottler {
|
|
constructor(nonThrottledFrames, defaultInterval, throttlingInterval) {
|
|
this._acks = [];
|
|
this._throttlingEnabled = false;
|
|
this._nonThrottledFrames = nonThrottledFrames;
|
|
this._budget = nonThrottledFrames;
|
|
this._defaultInterval = defaultInterval;
|
|
this._throttlingInterval = throttlingInterval;
|
|
this._tick();
|
|
}
|
|
dispose() {
|
|
if (this._timeoutId) {
|
|
clearTimeout(this._timeoutId);
|
|
this._timeoutId = void 0;
|
|
}
|
|
}
|
|
setThrottlingEnabled(enabled) {
|
|
this._throttlingEnabled = enabled;
|
|
}
|
|
recharge() {
|
|
for (const ack of this._acks)
|
|
ack();
|
|
this._acks = [];
|
|
this._budget = this._nonThrottledFrames;
|
|
if (this._timeoutId) {
|
|
clearTimeout(this._timeoutId);
|
|
this._tick();
|
|
}
|
|
}
|
|
ack(ack) {
|
|
if (!this._timeoutId) {
|
|
ack();
|
|
return;
|
|
}
|
|
this._acks.push(ack);
|
|
}
|
|
_tick() {
|
|
const ack = this._acks.shift();
|
|
if (ack) {
|
|
--this._budget;
|
|
ack();
|
|
}
|
|
if (this._throttlingEnabled && this._budget <= 0) {
|
|
this._timeoutId = setTimeout(() => this._tick(), this._throttlingInterval);
|
|
} else {
|
|
this._timeoutId = setTimeout(() => this._tick(), this._defaultInterval);
|
|
}
|
|
}
|
|
}
|
|
// Annotate the CommonJS export names for ESM import in node:
|
|
0 && (module.exports = {
|
|
Screencast
|
|
});
|