2019-02-08 17:46:58 +00:00
|
|
|
'use strict';
|
2019-06-09 00:51:00 +00:00
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
2020-02-21 18:34:22 +00:00
|
|
|
/** @module ffmpeg **/
|
2019-06-18 20:57:35 +00:00
|
|
|
const path_1 = require("path");
|
2019-06-09 01:43:14 +00:00
|
|
|
const fs_extra_1 = require("fs-extra");
|
|
|
|
const exec_1 = require("exec");
|
2019-08-04 21:42:27 +00:00
|
|
|
/** @class FFMPEG **/
|
|
|
|
class FFMPEG {
|
2019-08-04 21:54:38 +00:00
|
|
|
/**
|
|
|
|
* @constructor
|
|
|
|
* Creates an ffmpeg class
|
|
|
|
*
|
|
|
|
* @param {object} sys System object to be used to get temp directory
|
|
|
|
**/
|
2019-08-04 21:42:27 +00:00
|
|
|
constructor(sys) {
|
|
|
|
this.id = 'ffmpeg';
|
2019-08-23 19:39:38 +00:00
|
|
|
this.bin = sys.deps.ffmpeg;
|
2020-01-20 06:15:20 +00:00
|
|
|
this.convert = sys.deps.convert;
|
2019-08-23 19:39:38 +00:00
|
|
|
this.TMPDIR = path_1.join(sys.tmp, 'mcopy_digital');
|
2019-08-04 21:42:27 +00:00
|
|
|
this.init();
|
|
|
|
}
|
2019-08-04 21:54:38 +00:00
|
|
|
/**
|
|
|
|
* Async method to call async functions from constructor
|
|
|
|
**/
|
2019-08-04 21:42:27 +00:00
|
|
|
async init() {
|
|
|
|
const Log = require('log');
|
|
|
|
this.log = await Log({ label: this.id });
|
|
|
|
await this.checkDir();
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Add padding to a number to 5 places. Return a string.
|
|
|
|
*
|
|
|
|
* @param {integer} i Integer to pad
|
|
|
|
*
|
|
|
|
* @returns {string} Padded string
|
|
|
|
**/
|
|
|
|
padded_frame(i) {
|
|
|
|
let len = (i + '').length;
|
|
|
|
let str = i + '';
|
2020-02-21 06:58:56 +00:00
|
|
|
for (let x = 0; x < 8 - len; x++) {
|
2019-08-04 21:42:27 +00:00
|
|
|
str = '0' + str;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Render a single frame from a video or image to a png.
|
|
|
|
*
|
|
|
|
* @param {object} state State object containing file data
|
|
|
|
* @param {object} light Object containing color information for frame
|
|
|
|
*
|
|
|
|
* @returns {string} Path of frame
|
|
|
|
**/
|
|
|
|
async frame(state, light) {
|
|
|
|
const frameNum = state.frame;
|
|
|
|
const video = state.path;
|
|
|
|
const w = state.info.width;
|
|
|
|
const h = state.info.height;
|
|
|
|
const padded = this.padded_frame(frameNum);
|
2020-02-21 18:34:22 +00:00
|
|
|
let ext = 'png';
|
|
|
|
//let rgb : any[] = light.color;
|
2019-08-04 21:42:27 +00:00
|
|
|
let tmpoutput;
|
|
|
|
let cmd;
|
|
|
|
let output;
|
2020-02-21 18:34:22 +00:00
|
|
|
//let cmd2 : string;
|
|
|
|
//let output2 : any;
|
|
|
|
let fileExists = false;
|
2019-08-04 21:42:27 +00:00
|
|
|
let scale = '';
|
|
|
|
if (w && h) {
|
|
|
|
scale = `,scale=${w}:${h}`;
|
|
|
|
}
|
2020-02-21 18:34:22 +00:00
|
|
|
tmpoutput = path_1.join(this.TMPDIR, `${state.hash}-export-${padded}.${ext}`);
|
|
|
|
try {
|
|
|
|
fileExists = await fs_extra_1.exists(tmpoutput);
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
if (fileExists) {
|
|
|
|
this.log.info(`File ${tmpoutput} exists`);
|
|
|
|
return tmpoutput;
|
|
|
|
}
|
|
|
|
//rgb = rgb.map((e : string) => {
|
|
|
|
// return parseInt(e);
|
|
|
|
//});
|
2019-08-04 21:42:27 +00:00
|
|
|
//
|
2020-02-21 18:34:22 +00:00
|
|
|
cmd = `${this.bin} -y -i "${video}" -vf "select='gte(n\\,${frameNum})'${scale}" -vframes 1 -compression_algo raw -pix_fmt rgb24 -crf 0 "${tmpoutput}"`;
|
|
|
|
//cmd2 = `${this.convert} "${tmpoutput}" -resize ${w}x${h} -size ${w}x${h} xc:"rgb(${rgb[0]},${rgb[1]},${rgb[2]})" +swap -compose Darken -composite "${tmpoutput}"`;
|
2019-08-04 21:42:27 +00:00
|
|
|
//ffmpeg -i "${video}" -ss 00:00:07.000 -vframes 1 "export-${time}.jpg"
|
|
|
|
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "export-%05d.tiff"
|
|
|
|
//-vf "select=gte(n\,${frame})" -compression_algo raw -pix_fmt rgb24 "export-${padded}.png"
|
2019-06-09 00:51:00 +00:00
|
|
|
try {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.info(cmd);
|
2019-08-04 21:42:27 +00:00
|
|
|
output = await exec_1.exec(cmd);
|
2019-06-09 00:51:00 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error(err);
|
2019-06-09 00:51:00 +00:00
|
|
|
}
|
2019-08-04 21:42:27 +00:00
|
|
|
if (output && output.stdout)
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.info(`"${output.stdout}"`);
|
2020-02-21 18:34:22 +00:00
|
|
|
/*if (this.convert && (rgb[0] !== 255 || rgb[1] !== 255 || rgb[2] !== 255)) {
|
2019-06-09 00:51:00 +00:00
|
|
|
try {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.info(cmd2);
|
2020-02-21 18:34:22 +00:00
|
|
|
output2 = await exec(cmd2);
|
|
|
|
} catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error(err);
|
2019-06-09 00:51:00 +00:00
|
|
|
}
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
2020-02-21 18:34:22 +00:00
|
|
|
|
|
|
|
if (output2 && output2.stdout) this.log.info(`"${output2.stdout}"`);*/
|
2019-08-04 21:42:27 +00:00
|
|
|
return tmpoutput;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Render all frames in a video to the temp directory.
|
|
|
|
* Not in use.
|
|
|
|
*
|
|
|
|
* @param {string} video Path to video
|
|
|
|
* @param {object} obj Not sure
|
|
|
|
*
|
|
|
|
* @returns {?}
|
|
|
|
**/
|
2020-02-21 18:34:22 +00:00
|
|
|
async frames(state) {
|
|
|
|
const video = state.path;
|
|
|
|
const w = state.info.width;
|
|
|
|
const h = state.info.height;
|
2019-08-04 21:42:27 +00:00
|
|
|
const tmppath = this.TMPDIR;
|
2020-02-21 18:34:22 +00:00
|
|
|
let ext = 'png';
|
|
|
|
let tmpoutput = path_1.join(tmppath, `${state.hash}-export-%08d.${ext}`);
|
|
|
|
let cmd;
|
|
|
|
let output;
|
|
|
|
let scale = '';
|
|
|
|
if (w && h) {
|
|
|
|
scale = `scale=${w}:${h}`;
|
|
|
|
}
|
|
|
|
cmd = `${this.bin} -y -i "${video}" -vf "${scale}" -compression_algo raw -pix_fmt rgb24 -crf 0 "${tmpoutput}"`;
|
2019-06-09 00:51:00 +00:00
|
|
|
try {
|
2019-08-04 21:42:27 +00:00
|
|
|
await fs_extra_1.mkdir(tmppath);
|
2019-06-09 00:51:00 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
|
|
|
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"
|
2020-02-21 18:34:22 +00:00
|
|
|
try {
|
|
|
|
this.log.info(cmd);
|
|
|
|
output = await exec_1.exec(cmd);
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
this.log.error(err);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
return true;
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Clears a specific frame from the tmp directory
|
|
|
|
*
|
|
|
|
* @param {integer} frame Integer of frame to clear
|
|
|
|
*
|
|
|
|
* @returns {boolean} True if successful, false if not
|
|
|
|
**/
|
2020-02-21 18:34:22 +00:00
|
|
|
async clear(state) {
|
|
|
|
const padded = this.padded_frame(state.frame);
|
|
|
|
let ext = 'png';
|
2019-08-04 21:42:27 +00:00
|
|
|
let tmppath;
|
|
|
|
let fileExists;
|
2020-02-21 18:34:22 +00:00
|
|
|
tmppath = path_1.join(this.TMPDIR, `${state.hash}-export-${padded}.${ext}`);
|
2019-08-04 21:42:27 +00:00
|
|
|
try {
|
|
|
|
fileExists = await fs_extra_1.exists(tmppath);
|
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
2020-02-21 18:34:22 +00:00
|
|
|
if (!fileExists)
|
2019-08-04 21:42:27 +00:00
|
|
|
return false;
|
|
|
|
try {
|
|
|
|
await fs_extra_1.unlink(tmppath);
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.info(`Cleared frame ${tmppath}`);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
2020-02-21 06:58:56 +00:00
|
|
|
* Deletes all frames in temp directory.
|
2019-08-04 21:42:27 +00:00
|
|
|
*
|
|
|
|
**/
|
|
|
|
async clearAll() {
|
|
|
|
const tmppath = this.TMPDIR;
|
|
|
|
let files;
|
|
|
|
try {
|
|
|
|
files = await fs_extra_1.readdir(tmppath);
|
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
2020-02-21 18:34:22 +00:00
|
|
|
files = files.filter((file) => {
|
|
|
|
if (file.indexOf('-export-') !== -1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
2019-08-04 21:42:27 +00:00
|
|
|
if (files) {
|
|
|
|
files.forEach(async (file, index) => {
|
|
|
|
try {
|
|
|
|
await fs_extra_1.unlink(path_1.join(tmppath, file));
|
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
|
|
|
});
|
2019-06-09 00:51:00 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-04 21:42:27 +00:00
|
|
|
/**
|
|
|
|
* Checks if mcopy temp directory exists. If it doesn't,
|
2020-02-21 06:58:56 +00:00
|
|
|
* creates it.
|
2019-08-04 21:42:27 +00:00
|
|
|
**/
|
|
|
|
async checkDir() {
|
|
|
|
let fileExists;
|
|
|
|
try {
|
|
|
|
fileExists = await fs_extra_1.exists(this.TMPDIR);
|
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error('Error checking for tmp dir', err);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
|
|
|
if (!fileExists) {
|
|
|
|
try {
|
|
|
|
await fs_extra_1.mkdir(this.TMPDIR);
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.info(`Created tmpdir ${this.TMPDIR}`);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error('Error creating tmp dir', err);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
await this.clearAll();
|
|
|
|
}
|
|
|
|
catch (err) {
|
2019-08-04 21:54:38 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 21:42:27 +00:00
|
|
|
}
|
2019-06-09 00:51:00 +00:00
|
|
|
}
|
2019-02-08 17:46:58 +00:00
|
|
|
}
|
|
|
|
module.exports = (sys) => {
|
2019-08-04 21:42:27 +00:00
|
|
|
return new FFMPEG(sys);
|
2019-06-09 00:51:00 +00:00
|
|
|
};
|
|
|
|
//# sourceMappingURL=index.js.map
|