rokkonet

PC・Androidソフトウェア・アプリの開発・使い方に関するメモ

EPGStation : エンコードモード追加

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');
});