2026 Nov. 10.
2026 Apr. 06.
2025 Mar. 16.
2025 Feb. 24.
以下の記事はEPGStation v2のもの。
EPGStation v3では、hoge.js(の冒頭)の require を import に書き換える。
出典: docker-mirakurun-epgstation/docs/migration.md at v3 · l3tnun/docker-mirakurun-epgstation · GitHub
音声欠落対策H.264エンコード設定
音声欠落を防ぐため、冒頭4秒間をカットして H.264にエンコードする設定を追加する設定
ストリームを指定しない。
通常の番組はこれで対応できる。
NHK総合19時ニュース、NHK BS世界のドキュメンタリーはこれでは音声が欠ける。
参考
EPGStationで使うFFmpeg 4.2.4のオプション設定を考える – Simple Life in the digital age
https://rasumus.hatenablog.com/entry/2020/05/30/221853
(1) エンコードスクリプトを作成する
次の内容で ${HOME}/docker/docker-mirakurun-epgstation/epgstation/config/enc-cut4secs.js をファイル属性644で作成する。
// 2025 Mar. 02.
const spawn = require('child_process').spawn;
const ffmpeg = process.env.FFMPEG;
const input = process.env.INPUT;
const output = process.env.OUTPUT;
const analyzedurationSize = '10M'; // Mirakurun の設定に応じて変更すること
const probesizeSize = '32M'; // Mirakurun の設定に応じて変更すること
const maxMuxingQueueSize = 1024;
const dualMonoMode = 'main';
const videoHeight = parseInt(process.env.VIDEORESOLUTION, 10);
const isDualMono = parseInt(process.env.AUDIOCOMPONENTTYPE, 10) == 2;
const audioBitrate = videoHeight > 720 ? '192k' : '128k';
const preset = 'veryfast';
const codec = 'libx264';
const crf = 23;
const args = ['-ss', '4', '-y', '-analyzeduration', analyzedurationSize, '-probesize', probesizeSize];
// dual mono 設定
if (isDualMono) {
Array.prototype.push.apply(args, ['-dual_mono_mode', dualMonoMode]);
}
// input 設定
Array.prototype.push.apply(args,['-i', input]);
// メタ情報を先頭に置く
Array.prototype.push.apply(args,['-movflags', 'faststart']);
// video filter 設定
let videoFilter = 'yadif';
if (videoHeight > 720) {
videoFilter += ',scale=-2:720';
}
Array.prototype.push.apply(args, ['-vf', videoFilter]);
// その他設定
Array.prototype.push.apply(args,[
'-preset', preset,
'-aspect', '16:9',
'-c:v', codec,
'-crf', crf,
'-f', 'mp4',
'-c:a', 'aac',
'-ar', '48000',
'-ab', audioBitrate,
'-ac', '2',
output
]);
let str = '';
for (let i of args) {
str += ` ${ i }`
}
console.error(str);
const child = spawn(ffmpeg, args);
child.stderr.on('data', (data) => { console.error(String(data)); });
child.on('error', (err) => {
console.error(err);
throw new Error(err);
});
process.on('SIGINT', () => {
child.kill('SIGINT');
});
(2) EPGStationに enc-cut4secs.js によるエンコード設定を組み込む
${HOME}/docker/docker-mirakurun-epgstation/epgstation/config/config.yml のencodeブロックに設定を記述する。
"- name:"で始め、変換コマンドjsファイル・拡張子・録画後タイムアウト時間(秒)を指定する。
設定例
encode:
#### デフォルトの設定 ####
- name: H.264
cmd: '%NODE% %ROOT%/config/enc.js'
suffix: .mp4
rate: 4.0
#### enc-cut4secs.js によるエンコード設定 ####
- name: H.264-cut4secs
cmd: '%NODE% %ROOT%/config/enc-cut4secs.js'
suffix: .mp4
rate: 4.0
NHK BS 世界のドキュメンタリー H.264エンコード設定
参考 https://rasumus.hatenablog.com/entry/2020/05/30/221853
第1音声ストリームに日本語、第2音声ストリームに英語が入った音声多重番組のトランスコード
(1) エンコードスクリプトを作成する
次の内容で ${HOME}/docker/docker-mirakurun-epgstation/epgstation/config/enc-bs_docu-cut4secs.js をファイル属性644で作成する。
// 2025 Mar. 08.
// NHK BS 世界のドキュメンタリー向けH.264トランスコード コード。
// 第1音声ストリームに日本語音声、第2音声ストリームに英語音声が入った動画をトランスコードする。
// ビデオは第1ビデオストリームをトランスコードする。
const spawn = require('child_process').spawn;
const ffmpeg = process.env.FFMPEG;
const input = process.env.INPUT;
const output = process.env.OUTPUT;
const analyzedurationSize = '10M'; // Mirakurun の設定に応じて変更すること
const probesizeSize = '32M'; // Mirakurun の設定に応じて変更すること
const maxMuxingQueueSize = 1024;
const dualMonoMode = 'main';
const videoHeight = parseInt(process.env.VIDEORESOLUTION, 10);
const isDualMono = parseInt(process.env.AUDIOCOMPONENTTYPE, 10) == 2;
const audioBitrate = videoHeight > 720 ? '192k' : '128k';
const preset = 'veryfast';
const codec = 'libx264';
const crf = 23;
const args = [
'-ss', '4',
'-y',
'-analyzeduration', analyzedurationSize,
'-probesize', probesizeSize
];
// input 設定
Array.prototype.push.apply(args,['-i', input]);
Array.prototype.push.apply(args,[
'-movflags', 'faststart',
'-preset', preset,
'-f', 'mp4',
'-aspect', '16:9',
'-c:v', codec,
'-crf', crf,
'-map', '0:v:0',
'-max_muxing_queue_size', maxMuxingQueueSize
]);
// video filter 設定
let videoFilter = 'yadif';
if (videoHeight > 720) {
videoFilter += ',scale=-2:720';
}
Array.prototype.push.apply(args, ['-vf', videoFilter]);
// 音声ストリームをマップ
Array.prototype.push.apply(args, [
'-map', '0:a:0',
'-map', '0:a:1',
'-metadata:s:a:0', 'title=Japanese',
'-metadata:s:a:0', 'language=jpn',
'-metadata:s:a:1', 'title=English',
'-metadata:s:a:1', 'language=eng'
]);
// 音声コード関連
Array.prototype.push.apply(args, [
'-c:a', 'aac',
'-ar', '48000',
'-ab', audioBitrate,
'-ac', '2'
]);
// 出力ファイル設定
Array.prototype.push.apply(args,[output]);
let str = '';
for (let i of args) {
str += ` ${ i }`
}
console.error(str);
const child = spawn(ffmpeg, args);
child.stderr.on('data', (data) => { console.error(String(data)); });
child.on('error', (err) => {
console.error(err);
throw new Error(err);
});
process.on('SIGINT', () => {
child.kill('SIGINT');
});
(2) EPGStationに enc-bs_docu-cut4secs.js によるエンコード設定を組み込む
${HOME}/docker/docker-mirakurun-epgstation/epgstation/config/config.yml のencodeブロックに設定を記述する。
"- name:"で始め、変換コマンドjsファイル・拡張子・録画後タイムアウト時間(秒)を指定する。
設定例
encode:
#### デフォルトの設定 ####
- name: H.264
cmd: '%NODE% %ROOT%/config/enc.js'
suffix: .mp4
rate: 4.0
#### enc-cut4secs.js によるエンコード設定 ####
- name: H.264-cut4secs
cmd: '%NODE% %ROOT%/config/enc-cut4secs.js'
suffix: .mp4
rate: 4.0
#### enc-bs_docu-cut4secs.js によるエンコード設定 ####
- name: H.264-nhk_bs_docu-cut4secs
cmd: '%NODE% %ROOT%/config/enc-bs_docu-cut4secs.js'
suffix: .mp4
rate: 4.0
NHKニュース( 日英2ヵ国語) H.264エンコード設定
第1音声ストリームに日本語と英語が入ったDual Mono Modeの音声多重番組を2つの音声ストリームにトランスコード
次の音声オプションを適用するように下記のenc-nhk_news-2audio_streams-cut5secs.js記事を訂正すること。
ffmpeg \ -filter_complex "[0:a:0]pan=mono|c0=c0[jp];[0:a:0]pan=mono|c0=c1[en]" \ -map "[jp]" -c:a:0 aac -b:a:0 128k -ac:a:0 1 -metadata:s:a:0 title="Japanese" -metadata:s:a:0 language=jpn \ -map "[en]" -c:a:1 aac -b:a:1 128k -ac:a:1 1 -metadata:s:a:1 title="English" -metadata:s:a:1 language=eng \ -ar 48000 \
(1) エンコードスクリプトを作成する
次の内容で ${HOME}/docker/docker-mirakurun-epgstation/epgstation/config/enc-nhk_news-2audio_streams-cut5secs.js をファイル属性644で作成する。
// 2025 Mar. 15.
// 第1ビデオストリーム、第1音声ストリームをトランスコードする。
// 第1音声ストリームはDual Mono Modeで日本語と英語が入っている。
// 変換後の第1音声ストリームに日本語、第2音声ストリームに英語が入る。
const spawn = require('child_process').spawn;
const ffmpeg = process.env.FFMPEG;
const input = process.env.INPUT;
const output = process.env.OUTPUT;
const analyzedurationSize = '10M'; // Mirakurun の設定に応じて変更すること
const probesizeSize = '32M'; // Mirakurun の設定に応じて変更すること
const maxMuxingQueueSize = 1024;
const dualMonoMode = 'main';
const videoHeight = parseInt(process.env.VIDEORESOLUTION, 10);
const isDualMono = parseInt(process.env.AUDIOCOMPONENTTYPE, 10) == 2;
const audioBitrate = videoHeight > 720 ? '192k' : '128k';
const preset = 'veryfast';
const codec = 'libx264';
const crf = 23;
const args = ['-ss', '5', '-y'];
// input 設定
Array.prototype.push.apply(args,['-i', input]);
Array.prototype.push.apply(args, ['-analyzeduration', analyzedurationSize, '-probesize', probesizeSize]);
// メタ情報
Array.prototype.push.apply(args,['-movflags', 'faststart']);
Array.prototype.push.apply(args,['-ignore_unknown']);
// コーデック等
Array.prototype.push.apply(args,[
'-preset', preset,
'-crf', crf,
'-f', 'mp4']);
// ビデオストリームをマップ
Array.prototype.push.apply(args, ['-map', '0:v:0', '-max_muxing_queue_size', maxMuxingQueueSize]);
// ビデオコード関連
Array.prototype.push.apply(args, ['-aspect', '16:9', '-c:v', codec]);
// video filter 設定
let videoFilter = 'yadif';
if (videoHeight > 720) {
videoFilter += ',scale=-2:720';
}
Array.prototype.push.apply(args, ['-vf', videoFilter]);
// 音声ストリームをマップ
Array.prototype.push.apply(args, ['-map', '0:a:0', '-map', '0:a:0', '-metadata:s:a:0', 'title=Japanese', '-metadata:s:a:0', 'language=jpn', '-metadata:s:a:1', 'title=English', '-metadata:s:a:1', 'language=eng']);
// 音声コード関連
Array.prototype.push.apply(args, ['-c:a:0', 'aac', '-c:a:1', 'aac', '-ar', '48000', '-ab', audioBitrate, '-ac', '2' ]);
// 出力先
Array.prototype.push.apply(args,[output]);
let str = '';
for (let i of args) {
str += ` ${ i }`
}
console.error(str);
const child = spawn(ffmpeg, args);
child.stderr.on('data', (data) => { console.error(String(data)); });
child.on('error', (err) => {
console.error(err);
throw new Error(err);
});
process.on('SIGINT', () => {
child.kill('SIGINT');
});
(2) EPGStationに enc-nhk_news-2audio_streams-cut5secs.js によるエンコード設定を組み込む
${HOME}/docker/docker-mirakurun-epgstation/epgstation/config/config.yml のencodeブロックに設定を記述する。
"- name:"で始め、変換コマンドjsファイル・拡張子・録画後タイムアウト時間(秒)を指定する。
設定例
encode:
#### デフォルトの設定 ####
- name: H.264
cmd: '%NODE% %ROOT%/config/enc.js'
suffix: .mp4
rate: 4.0
#### enc-cut4secs.js によるエンコード設定 ####
- name: H.264-cut4secs
cmd: '%NODE% %ROOT%/config/enc-cut4secs.js'
suffix: .mp4
rate: 4.0
#### enc-bs_docu-cut4secs.js によるエンコード設定 ####
- name: H.264-nhk_bs_docu-cut4secs
cmd: '%NODE% %ROOT%/config/enc-bs_docu-cut4secs.js'
suffix: .mp4
rate: 4.0
#### enc-nhk_news-cut4secs.js によるエンコード設定 ####
- name: H.264-nhk_news-cut4secs
cmd: '%NODE% %ROOT%/config/enc-nhk_news-cut4secs.js'
suffix: .mp4
rate: 4.0
(参考)デフォルトの ${HOME}/docker/docker-mirakurun-epgstation/epgstation/config/enc.js の内容
const spawn = require('child_process').spawn;
const ffmpeg = process.env.FFMPEG;
const input = process.env.INPUT;
const output = process.env.OUTPUT;
const analyzedurationSize = '10M'; // Mirakurun の設定に応じて変更すること
const probesizeSize = '32M'; // Mirakurun の設定に応じて変更すること
const maxMuxingQueueSize = 1024;
const dualMonoMode = 'main';
const videoHeight = parseInt(process.env.VIDEORESOLUTION, 10);
const isDualMono = parseInt(process.env.AUDIOCOMPONENTTYPE, 10) == 2;
const audioBitrate = videoHeight > 720 ? '192k' : '128k';
const preset = 'veryfast';
const codec = 'libx264';
const crf = 23;
const args = ['-y', '-analyzeduration', analyzedurationSize, '-probesize', probesizeSize];
// dual mono 設定
if (isDualMono) {
Array.prototype.push.apply(args, ['-dual_mono_mode', dualMonoMode]);
}
// input 設定
Array.prototype.push.apply(args,['-i', input]);
// メタ情報を先頭に置く
Array.prototype.push.apply(args,['-movflags', 'faststart']);
// 字幕データを含めたストリームをすべてマップ
// Array.prototype.push.apply(args, ['-map', '0', '-ignore_unknown', '-max_muxing_queue_size', maxMuxingQueueSize, '-sn']);
// video filter 設定
let videoFilter = 'yadif';
if (videoHeight > 720) {
videoFilter += ',scale=-2:720'
}
Array.prototype.push.apply(args, ['-vf', videoFilter]);
// その他設定
Array.prototype.push.apply(args,[
'-preset', preset,
'-aspect', '16:9',
'-c:v', codec,
'-crf', crf,
'-f', 'mp4',
'-c:a', 'aac',
'-ar', '48000',
'-ab', audioBitrate,
'-ac', '2',
output
]);
let str = '';
for (let i of args) {
str += ` ${ i }`
}
console.error(str);
const child = spawn(ffmpeg, args);
child.stderr.on('data', (data) => { console.error(String(data)); });
child.on('error', (err) => {
console.error(err);
throw new Error(err);
});
process.on('SIGINT', () => {
child.kill('SIGINT');
});