技術記事に戻る

Node.jsのchild_processとFFmpegで作る!非対応動画ファイルのMP4自動一括変換ツール

Node.jsFFmpeg

Node.jsのchild_processとFFmpegで作る!非対応動画ファイルのMP4自動一括変換ツール

ソースコード: https://github.com/hayate-hu6/local-media-viewer.git

はじめに

古いPCの画像や動画を整理していたところ、ブラウザで再生できない動画ファイルが多数見つかりました。何とかwebアプリで再生できないかと考えた結果、多様な動画形式に対応するのは困難なため、ブラウザ対応の形式に変換することにしました。

上記の通りWebブラウザは全ての動画形式を再生できるわけではありません。MP4 (H.264/AAC) や WebM は問題ありませんが、古いデジカメで撮影した .avi.mpg、昔のネット動画 .flv、DVDデータの .vob などは、そのままではブラウザで再生できません。

そこで、これらの動画をブラウザで再生可能な形式(MP4)に自動変換するスクリプト convert_media.js を作成しました。

Node.jsから外部コマンドを実行する

Node.jsで動画変換を行う場合、JavaScriptだけで処理するのは現実的ではありません(処理速度の問題)。通常は、動画処理のデファクトスタンダードである FFmpeg を外部コマンドとして呼び出します。

const { execSync } = require('child_process');

// Check FFmpeg
try {
    execSync('ffmpeg -version', { stdio: 'ignore' });
} catch (e) {
    console.error('CRITICAL ERROR: FFmpeg is not installed...');
    process.exit(1);
}

child_process モジュールの execSync を使うと、シェルコマンドを実行し、その終了を待つことができます。まず最初に ffmpeg -version を叩いて、FFmpegがシステムにインストールされているかを確認しています。

変換ディレクトリの再帰スキャン

サーバー側のファイルスキャンと同様に、再帰的にフォルダを巡回して変換対象ファイルを探します。

const EXTS_TO_CONVERT = ['.avi', '.flv', '.3gp', '.mkv', '.mpg', '.mpeg', '.rm', '.wmv'];

function processDirectory(directory) {
    // ...出力先フォルダはスキップする処理...

    const items = fs.readdirSync(directory);
    for (const item of items) {
        // ...再帰処理...
        const ext = path.extname(item).toLowerCase();
        if (EXTS_TO_CONVERT.includes(ext)) {
            convertFile(fullPath);
        }
    }
}

ここでのポイントは、変換されたファイルを保存するための「出力ディレクトリ(converted)」もスキャン対象に含まれてしまうと、無限ループ(変換したファイルをまた変換しようとする)に陥る可能性がある点です。そのため、ディレクトリパスの解決 (path.resolve) を行って同一判定をし、スキップするガード処理を入れています。

FFmpegコマンドの構築と実行

ここが核心部分です。見つけたファイルをMP4に変換します。

function convertFile(filePath) {
    const outputPath = getOutputFilePath(filePath); // filename.avi -> output/filename.mp4

    // 既に変換済みならスキップ(二重変換防止)
    if (fs.existsSync(outputPath)) {
        console.log(`[SKIP] Already converted: ${path.basename(filePath)}`);
        return;
    }

    // FFmpegコマンドの組み立て
    const cmd = `ffmpeg -y -hide_banner -loglevel error -i "${filePath}" -c:v libx264 -preset fast -crf 23 -c:a aac -b:a 128k "${outputPath}"`;
    
    try {
        execSync(cmd, { stdio: 'inherit' });
        console.log(`   -> Success`);
    } catch (err) {
        console.error(`   -> FAILED: ${err.message}`);
    }
}

FFmpegオプション解説

  • -y: 出力ファイルが既に存在していても強制上書き(このコードでは事前にチェックしているので安全策)
  • -i "input": 入力ファイルパス
  • -c:v libx264: 映像コーデックを H.264 に指定(Web互換性最高)
  • -preset fast: 変換速度を優先(画質とのトレードオフ)
  • -crf 23: 画質設定(数値が低いほど高画質、23はバランスが良いデフォルト値)
  • -c:a aac: 音声コーデックを AAC に指定
  • -b:a 128k: 音声ビットレート

execSync{ stdio: 'inherit' } を渡すことで、FFmpegの進捗ログ(プログレスバーなど)をそのままコンソールに表示させることができ、ユーザーは変換の進行状況を目視できます。

運用フロー

このスクリプトは、サーバー(server.js)とは独立しています。 ユーザーは以下のフローで運用します。

  1. メディアフォルダに古い動画ファイルを入れる。
  2. node convert_media.js を実行する。
  3. スクリプトがフォルダを巡回し、非互換ファイルを converted フォルダにMP4として生成する。
  4. Local Media Viewerを開くと、元のファイルと変換後のMP4ファイルが表示される(変換後はMP4の方を見れば良い)。

まとめ

Node.jsの強力なファイルシステム操作能力と、FFmpegという外部ツールを組み合わせることで、実用的なメディアコンバーターを簡単に作成できました。 child_process を使いこなすことで、Node.js単体では不可能な重い処理(動画変換、画像加工、システム操作など)を既存のCLIツールに委譲する設計パターンは、バックエンド開発において非常に強力な武器になります。


全7回にわたり、Local Media Viewerのソースコードを解説してきました。 一見単純なビューアーアプリですが、Expressの基礎、セキュリティ、ファイルシステム、アルゴリズム、Vanilla JS、そして外部プロセス連携と、Webアプリケーション開発に必要なエッセンスが詰まっています。


同シリーズ記事