"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");
var import_browserContext = require("./browserContext");
class Screencast {
  constructor(page) {
    this._videoRecorder = null;
    this._videoId = null;
    this._listeners = /* @__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._videoFrameListener = null;
    this._page = page;
  }
  stopFrameThrottler() {
    this._frameThrottler.dispose();
  }
  startForTracing(listener) {
    this.startScreencast(listener, { width: 800, height: 800, quality: 90 }).catch((e) => import_utils2.debugLogger.log("error", e));
    this._frameThrottler.setThrottlingEnabled(true);
  }
  stopForTracing(listener) {
    this.stopScreencast(listener).catch((e) => import_utils2.debugLogger.log("error", e));
    this._frameThrottler.setThrottlingEnabled(false);
  }
  throttleFrameAck(ack) {
    this._frameThrottler.ack(ack);
  }
  temporarilyDisableThrottling() {
    this._frameThrottler.recharge();
  }
  // Note: it is important to start video recorder before sending Screencast.startScreencast,
  // and it is equally important to send Screencast.startScreencast before sending Target.resume.
  launchAutomaticVideoRecorder() {
    const recordVideo = this._page.browserContext._options.recordVideo;
    if (!recordVideo)
      return;
    return this._launchVideoRecorder(recordVideo.dir, recordVideo.size);
  }
  _launchVideoRecorder(dir, size) {
    (0, import_utils.assert)(!this._videoId);
    const ffmpegPath = import_registry.registry.findExecutable("ffmpeg").executablePathOrDie(this._page.browserContext._browser.sdkLanguage());
    this._videoId = (0, import_utils.createGuid)();
    const outputFile = import_path.default.join(dir, this._videoId + ".webm");
    const videoOptions = {
      ...size,
      outputFile
    };
    this._videoRecorder = new import_videoRecorder.VideoRecorder(ffmpegPath, videoOptions);
    this._videoFrameListener = (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(() => {
    }));
    await this.startScreencast(this._videoFrameListener, {
      quality: 90,
      width: options.width,
      height: options.height
    });
    return this._page.browserContext._browser._videoStarted(this._page, videoId, options.outputFile);
  }
  async stopVideoRecording() {
    if (!this._videoId)
      return;
    const videoFrameListener = this._videoFrameListener;
    this._videoFrameListener = null;
    const videoId = this._videoId;
    this._videoId = null;
    const videoRecorder = this._videoRecorder;
    this._videoRecorder = null;
    await this.stopScreencast(videoFrameListener);
    await videoRecorder.stop();
    const video = this._page.browserContext._browser._takeVideo(videoId);
    video?.reportFinished();
  }
  async startExplicitVideoRecording(options = {}) {
    if (this._videoId)
      throw new Error("Video is already being recorded");
    const size = (0, import_browserContext.validateVideoSize)(options.size, this._page.emulatedSize()?.viewport);
    const videoOptions = this._launchVideoRecorder(this._page.browserContext._browser.options.artifactsDir, size);
    return await this.startVideoRecording(videoOptions);
  }
  async stopExplicitVideoRecording() {
    if (!this._videoId)
      throw new Error("Video is not being recorded");
    await this.stopVideoRecording();
  }
  async startScreencast(listener, options) {
    this._listeners.add(listener);
    if (this._listeners.size === 1)
      await this._page.delegate.startScreencast(options);
  }
  async stopScreencast(listener) {
    this._listeners.delete(listener);
    if (!this._listeners.size)
      await this._page.delegate.stopScreencast();
  }
  onScreencastFrame(frame) {
    for (const listener of this._listeners)
      listener(frame);
  }
}
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
});
