"use strict"; /** * Creates a new instance of VideoRecorder. * * @class * @name VideoRecorder * @classdesc contains methods to record video for tests * @arg {object} [opts] - Options. * @arg {object} [opts._process] - Injected `process` module. * @arg {object} [opts._which] - Injected `which` module. * @arg {object} [opts._spawn] - Injected `spawn` module. * @property {boolean} [isRunning=false] - flag is video recorder running or no * @property {?string} [filePath=null] - path video file */ var path = require("path"); var spawn = require("cross-spawn"); var which = require("which"); var U = require("glace-utils"); var LOG = U.logger; var VideoRecorder = function (opts) { opts = U.defVal(opts, {}); this.isRunning = false; this.filePath = null; this._process = null; this._recordOpts = null; this.__process = U.defVal(opts._process, process); this.__which = U.defVal(opts._which, which); this.__spawn = U.defVal(opts._spawn, spawn); }; /** * Configures video recorder. * * @method * @arg {object} [opts] - recorder configuration * @arg {string} [opts.path] - video file path * @arg {number} [opts.fps=30] - video framerate * @arg {number} [opts.width=1024] - video width * @arg {number} [opts.height=768] - video height * @arg {number} [opts.x=0] - `X`-offset on display * @arg {number} [opts.y=0] - `Y`-offset on display */ VideoRecorder.prototype.configure = function (opts) { opts = U.defVal(opts, {}); opts.fps = U.defVal(opts.fps, 30); opts.width = U.defVal(opts.width, 1024); opts.height = U.defVal(opts.height, 768); opts.x = U.defVal(opts.x, 0); opts.y = U.defVal(opts.y, 0); opts.size = opts.width + "x" + opts.height; this.filePath = U.defVal(opts.path, path.resolve(U.cwd, "out.avi")); if (this.__process.platform === "win32") { this.__which.sync("ffmpeg"); this._recordCmd = "ffmpeg"; this._recordOpts = [ "-y", "-loglevel", "quiet", "-video_size", opts.size, "-offset_x", opts.x, "-offset_y", opts.y, "-draw_mouse", 0, "-framerate", opts.fps, "-f", "gdigrab", "-i", "desktop", "-vcodec", "libx264", this.filePath ]; } else if (this.__process.platform === "linux") { if (this.__which.sync("avconv", { nothrow: true })) { this._recordCmd = "avconv"; } else if (this.__which.sync("ffmpeg", { nothrow: true })) { this._recordCmd = "ffmpeg"; } else { throw new Error("not found: avconv or ffmpeg"); }; this._recordOpts = [ "-y", "-loglevel", "quiet", "-f", "x11grab", "-r", opts.fps, "-s", opts.size, "-i", `${process.env.DISPLAY}+${opts.x},${opts.y}`, "-codec", "libx264", this.filePath ]; } else if (this.__process.platform === "darwin") { this.__which.sync("ffmpeg"); this._recordCmd = "ffmpeg"; this._recordOpts = [ "-y", "-loglevel", "quiet", "-f", "avfoundation", "-r", opts.fps, "-i", "1:", "-vsync", 2, this.filePath ]; } else { throw new Error("Video capture isn't supported " + `on platform '${this.__process.platform}'`); }; }; /** * Starts video recorder. * * @method * @throws {Error} if video recorder is started already * @throws {Error} if video recorder isn't configured yet */ VideoRecorder.prototype.start = function () { if (this.isRunning) return; if (!this._recordOpts) throw new Error("Video recorder isn't configured yet"); this._process = this.__spawn(this._recordCmd, this._recordOpts, { killSignal: "SIGINT" }); this.isRunning = true; }; /** * Stops video recorder. * * @method * @throws {Error} if video recorder isn't started yet */ VideoRecorder.prototype.stop = function () { if (!this.isRunning) return; return new Promise((resolve, reject) => { this._process.once("exit", (code, signal) => { LOG.debug(`${this._recordCmd} was stopped with code ${code} and signal ${signal}`); this.isRunning = false; resolve(); }); this._process.once("error", reject); var result = this._process.kill("SIGINT"); if (!result) reject(new Error(`Oops! Can't kill ${this._recordCmd}`)); }); }; module.exports = VideoRecorder;