Euquid Blog.

WEBエンジニアの技術ブログ。主に個人開発で学んだことの備忘録です。

Node.jsでffmpegのシェルを定期実行する(node-cronを利用)

Cover Image for Node.jsでffmpegのシェルを定期実行する(node-cronを利用)

概要

過去記事で、ffmpeg を実行するシェルスクリプトを作成した。

しかしシェルスクリプトの独特な記法は扱いづらく、今後の拡張性を考えると別の方法を考えたい。ということで、今回は馴染みのある node.js で同じ処理を作る。

幸いにも、node.js には node-cron という、cron と同等の処理ができるパッケージがあるので、これを使って定期的に ffmpeg を動かしてみる。

構成は下記 2 つ。node-cron は他でも使えそうなので独立させる。

  • m2ts -> mp4 へのエンコードの実行ファイルを作成する
    • fs モジュールでファイル名の取得
    • child_process モジュール で ffmpeg コマンドを実行
  • node-cron で上記の実行ファイルを定期実行を設定する

実行ファイルを作成する

以前作成した mp4encode.sh を元に、 mp4encode.js を作成する。 作成した js ファイルは、m2ts ファイルの保存フォルダに格納する。

'use strict';
const fs = require('fs');
const { execSync } = require('child_process');

// 出力先フォルダ
const out = __dirname + "/mp4/";

// 全ファイル名を取得
const filenames = fs.readdirSync(__dirname);

// ビットレート
const bitrate = "4000k";

// 番組数のオフセット
let count = 0;

// モジュール作成
function encode() {
  // mapで1ファイルずつ取り出す
  filenames.map(fn => {
    // 拡張子が.m2ts かつ 出力先に.mp4が存在しない場合にのみ実行
    if ( fn.match(/.m2ts/) && !fs.existsSync(out + fn.substr(0, fn.length - 5) + ".mp4") ) {
      // ffmpegのコマンドを作成
      let command = "ffmpeg -fflags +discardcorrupt -i " + __dirname + "/'" + fn + "' -c:a copy -bsf:a aac_adtstoasc -vsync 1 -c:v h264_omx -b:v " + bitrate + " " + out + "'" + fn.substr(0, fn.length - 5) + "'.mp4";
      // 開始ログ
      console.log(`[*** ${fn} の変換開始:${new Date()} ***]`)
      // シェルの実行(同期処理)
      let stdout = execSync(command);
      console.log(stdout.toString());
      count++;
    }
  });
  // 完了ログ
  console.log(`[**** 変換した番組数:${count} ****]`);
  console.log(`[**** 完了時刻:${new Date()} ****]`);
}

// 外部から利用
module.exports = { encode };

※追記:ストリーム処理の経過を見る&バッファ逼迫のため spawn を使った方が良さそう

'use strict';
const fs = require('fs');
const { spawn } = require('child_process');

// 出力先フォルダ
const out = __dirname + "/mp4/";

// 全ファイル名を取得
const filenames = fs.readdirSync(__dirname);

// ビットレート
const bitrate = "4000k";

// 番組数のオフセット
let count = 0;

// モジュール作成
function encode() {
  // mapで1ファイルずつ取り出す
  filenames.map(fn => {
    // 拡張子が.m2ts かつ 出力先に.mp4が存在しない場合にのみ実行
    if ( fn.match(/.m2ts/) && !fs.existsSync(out + fn.substr(0, fn.length - 5) + ".mp4") ) {
      // 開始ログ
      console.log(`[*** ${fn} の変換開始:${new Date()} ***]`)
      // シェルの実行(ストリーム処理)
      let input = __dirname + "/" + fn;
      let output = out + fn.substr(0, fn.length - 5) + ".mp4";
      let ff = spawn('ffmpeg', ['-fflags', '+discardcorrupt', '-i', input, '-c:a', 'copy', '-bsf:a', 'aac_adtstoasc', '-vsync', '1', '-c:v', 'h264_omx', '-b:v', bitrate, output]);  // (**)
      ff.stdout.on('data', (data) => {
        console.log(data);
      });
      ff.stderr.on('data', (data) => {
        console.log(`stderr: ${data}`);
      });
      ff.on('close', (code) => {
        console.log("****************************");
        count++;
        // 完了ログ
        console.log(`[**** 変換した番組数:${count} ****]`);
        console.log(`[**** 完了時刻:${new Date()} ****]`);
      });
    }
  });
}

// 外部から利用
module.exports = { encode };

(**) コマンドのオプションは、spawn の第 2 引数に配列として渡す必要がある。

const ff = spawn('ffmpeg', command.split(" ")) でも行けそうだったが、ファイル名に半角がある場合を想定し手打ちでカンマ区切りにした。

node-cron を設定する

// 準備
$ mkdir nodecron
$ cd nodecron

// パッケージの取得
$ npm install --save node-cron

package-lock.json と同じ階層に app.js を作成する。

'use strict';
const cron = require('node-cron');
const encode = require('/[m2tsの保存先]/mp4encode').encode;

// エンコードファイルを実行 -> 毎日 3:00 & 15:00 に実行
cron.schedule('0 0 3,15 * * *', () => encode());
// 実行
$ node app.js

参考