Skip to content

Instantly share code, notes, and snippets.

@mkyt
Created October 19, 2024 14:43
Show Gist options
  • Save mkyt/ad981dd3d69ecb1176a3ae3a2142d696 to your computer and use it in GitHub Desktop.
Save mkyt/ad981dd3d69ecb1176a3ae3a2142d696 to your computer and use it in GitHub Desktop.
EPGStationのエンコーダー設定例(QSV, h.264, q=23)
const spawn = require('child_process').spawn;
const execFile = require('child_process').execFile;
const ffmpeg = process.env.FFMPEG;
const ffprobe = process.env.FFPROBE;
const input = process.env.INPUT;
const output = process.env.OUTPUT;
const isDualMono = parseInt(process.env.AUDIOCOMPONENTTYPE, 10) == 2;
const args = ['-y'];
// my settings
const video_quality = '23';
const audio_quality = '3';
const videoFilter = 'deinterlace_qsv,fps=30000/1001';
/**
* 動画長取得関数
* @param {string} filePath ファイルパス
* @return number 動画長を返す (秒)
*/
const getDuration = filePath => {
return new Promise((resolve, reject) => {
execFile(ffprobe, ['-v', '0', '-show_format', '-of', 'json', filePath], (err, stdout) => {
if (err) {
reject(err);
return;
}
try {
const result = JSON.parse(stdout);
resolve(parseFloat(result.format.duration));
} catch (err) {
reject(err);
}
});
});
};
// QSV
Array.prototype.push.apply(args, [
'-hwaccel', 'qsv', '-hwaccel_output_format', 'qsv',
'-init_hw_device', 'qsv=hw', '-filter_hw_device', 'hw',
'-c:v', 'mpeg2_qsv'
]);
// 字幕用
//Array.prototype.push.apply(args, ['-fix_sub_duration']);
// input 設定
Array.prototype.push.apply(args, ['-i', input, '-map', '0:v']);
// インターレス解除
Array.prototype.push.apply(args, ['-vf', videoFilter]);
// ビデオストリーム設定
Array.prototype.push.apply(args, ['-c:v', 'h264_qsv', '-q', video_quality]);
// オーディオストリーム設定
if (isDualMono) {
Array.prototype.push.apply(args, [
'-filter_complex',
'channelsplit[FL][FR]',
'-map', '[FL]',
'-map', '[FR]',
'-metadata:s:a:0', 'language=jpn',
'-metadata:s:a:1', 'language=eng',
]);
} else {
Array.prototype.push.apply(args, ['-map', '0:a']);
}
Array.prototype.push.apply(args, ['-c:a', 'libfdk_aac', '-vbr', audio_quality]);
// 字幕ストリーム設定
//Array.prototype.push.apply(args, ['-map', '0:s?', '-c:s', 'mov_text']);
// 出力ファイル
Array.prototype.push.apply(args, [output]);
(async () => {
// 進捗計算のために動画の長さを取得
const duration = await getDuration(input);
const child = spawn(ffmpeg, args);
/**
* エンコード進捗表示用に標準出力に進捗情報を吐き出す
* 出力する JSON
* {"type":"progress","percent": 0.8, "log": "view log" }
*/
child.stderr.on('data', data => {
let strbyline = String(data).split('\n');
for (let i = 0; i < strbyline.length; i++) {
let str = strbyline[i];
if (str.startsWith('frame')) {
// 想定log
// frame= 5159 fps= 11 q=29.0 size= 122624KiB time=00:02:51.84 bitrate=5845.8kbits/s dup=19 drop=0 speed=0.372x
const progress = {};
const ffmpeg_reg = /frame=\s*(?<frame>\d+)\sfps=\s*(?<fps>\d+(?:\.\d+)?)\sq=\s*(?<q>[+-]?\d+(?:\.\d+)?)\sL?size=\s*(?<size>\d+(?:\.\d+)?)KiB\stime=\s*(?<time>\d+[:\.\d+]*)\sbitrate=\s*(?<bitrate>\d+(?:\.\d+)?)kbits\/s(?:\sdup=\s*(?<dup>\d+))?(?:\sdrop=\s*(?<drop>\d+))?\sspeed=\s*(?<speed>\d+(?:\.\d+)?)x/;
let ffmatch =str.match(ffmpeg_reg);
/**
* match結果
* [
* 'frame= 5159 fps= 11 q=29.0 size= 122624KiB time=00:02:51.84 bitrate=5845.8kbits/s dup=19 drop=0 speed=0.372x',
* '5159',
* '11',
* '29.0',
* '122624',
* '00:02:51.84',
* '5845.8',
* '19',
* '0',
* '0.372',
* index: 0,
* input: 'frame= 5159 fps= 11 q=29.0 size= 122624KiB time=00:02:51.84 bitrate=5845.8kbits/s dup=19 drop=0 speed=0.372x \r',
* groups: [Object: null prototype] {
* frame: '5159',
* fps: '11',
* q: '29.0',
* size: '122624',
* time: '00:02:51.84',
* bitrate: '5845.8',
* dup: '19',
* drop: '0',
* speed: '0.372'
* }
* ]
*/
if (ffmatch === null) continue;
progress['frame'] = parseInt(ffmatch.groups.frame);
progress['fps'] = parseFloat(ffmatch.groups.fps);
progress['q'] = parseFloat(ffmatch.groups.q);
progress['size'] = parseInt(ffmatch.groups.size);
progress['time'] = ffmatch.groups.time;
progress['bitrate'] = parseFloat(ffmatch.groups.bitrate);
progress['dup'] = ffmatch.groups.dup == null ? 0 : parseInt(ffmatch.groups.dup);
progress['drop'] = ffmatch.groups.drop == null ? 0 : parseInt(ffmatch.groups.drop);
progress['speed'] = parseFloat(ffmatch.groups.speed);
let current = 0;
const times = progress.time.split(':');
for (let i = 0; i < times.length; i++) {
if (i == 0) {
current += parseFloat(times[i]) * 3600;
} else if (i == 1) {
current += parseFloat(times[i]) * 60;
} else if (i == 2) {
current += parseFloat(times[i]);
}
}
// 進捗率 1.0 で 100%
const percent = current / duration;
const log =
'frame= ' +
progress.frame +
' fps=' +
progress.fps +
' size=' +
progress.size +
' time=' +
progress.time +
' bitrate=' +
progress.bitrate +
' drop=' +
progress.drop +
' speed=' +
progress.speed;
console.log(JSON.stringify({ type: 'progress', percent: percent, log: log }));
}
}
});
child.on('error', err => {
console.error(err);
throw new Error(err);
});
child.on('close', (code) => {
process.exitCode = code;
});
process.on('SIGINT', () => {
child.kill('SIGINT');
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment