2012/09/09

並列処理を行うBATでエンコードやCMカットを高速化する

はじめに

エンコード等の処理を高速化する手段としてはマルチスレッド化が一般的だと思います。

しかしAvisynthでのフィルタ処理をMT等でマルチスレッド化してもそれほど効果がない場合も多く、またx264はスレッド数を増やすと(僅かですが)ファイルサイズが増加したり画質が低下したりします。
デュアルコアの時代は簡単にCPUを使い切る事ができましたが、最近のCPUでは8スレッドや12スレッドを実行できるのでスレッドを埋めるだけでも大変です。

そこで、古典的ですがメモリやHDD等のリソースの限り何本も同時に処理する事で高速化を図ります。一本あたりのスレッド数を極端に増やす必要がないので効率が良いです。またlogoGuillo等のシングルスレッドアプリも高速化します。
では以下にBATの機能とサンプルを記載します。

主な機能と処理内容

・指定本数(2本以上)毎にペアを作成し並列エンコードする
・m2vファイルを探し、bat,aac,CMカットファイル(txt)があるか確認
・既にmp4ファイルやlogが生成されている場合は処理しない
・フォルダ名に"noenc"が含まれるディレクトリは処理しない

以下のように処理する動画毎にBATファイルが生成されている状態を想定しています。
*私の環境ではマスタBATとAVSを動画毎に自動コピーして処理しています。

・動画A.m2v
・動画A.bat
・動画B.m2v
・動画B.bat

このBATをn本ずつ纏めてパイプで繋いで実行することで並列処理します。

(call 動画A.bat) | (call 動画B.bat)

片方が早く終わるとパイプが存在しないとの警告が出ますが、今のところ100本以上処理した中では正常に動作しています。
また、組み合わせる動画の長さに差があると効率が落ちますが、ファイル名順に処理する場合は大抵同じ番組でペアが生成されるので問題ないと思います。

サンプルBATコード

このBATは私が使用している物を出来るだけ分かりやすく簡易化した物です。
環境に合わせて多少手直しするだけで使用可能だと思います。

当然ですが並列処理される各々のBATはテンポラリファイル名が重複しないように対策されている必要があります。
テンポラリファイル名は"元ファイル名+タイムスタンプ"等にすると良いと思います。

-----------

rem 並列エンコードBATサンプル by Wirewriggle

@echo off
setlocal ENABLEDELAYEDEXPANSION

rem 【各種設定】
rem *環境に合わせて変更してください

  rem 処理対象ディレクトリのパス
  set ROOTPASS=C:\EncodeVideos

  rem 一時ファイル保存ディレクトリ
  set TEMP_DIR=C:\EncodeTemp\

  rem 並列エンコード用に生成するテンポラリBATファイル名
  set EXEBAT=ExeMaster.bat

  rem 同時にエンコードする本数(並列数-1、2本並列する場合は1にする。n>=1)
  set ENC_THREAD=1

rem ROOTPASS内の動画ファイルを処理
rem m2vファイルをn本ずつエンコードする

  rem カウンタ類を初期化
  set THREADCNT=0
  set LINETEMP=
  
  rem 指定ディレクトリ以下を処理
  cd /d "%ROOTPASS%"
  for /D /R %%d in (*) do (
    cd /d "%%d"

    rem 【エンコード対象かどうかの判定処理1】
    rem *環境に合わせて変更してください
    
    rem #フォルダ名にnoencが入っているフォルダはエンコード対象外#
    echo "%%d" | find /i "noenc" > nul
    IF errorlevel 1 (
    
      rem #m2vファイル探索#
      for %%i in (*.m2v) do (

        rem 【エンコード対象かどうかの判定処理2】
        rem *環境に合わせて変更してください
        
        rem #フォルダ名にnoencが入っているフォルダはエンコード対象外#
        echo "%%d" | find /i "noenc" > nul
        IF errorlevel 1 (
        rem #BATファイルが存在#
        IF EXIST "%%~ni.bat" (
        rem #AACファイルが存在#
        IF EXIST "%%~ni*.aac" (
        rem #本CMカットファイルが存在#
        IF EXIST "%%~ni.txt" (
        rem #mp4ファイルが無い#
        IF NOT EXIST "%%~ni*.mp4" (
        rem #エンコードログが無い#
        IF NOT EXIST "%%~ni*_result.log" (

          set PPD_EN=%%d\
          
          rem 指定本数-1本溜まったら実行
          IF "!THREADCNT!" == "%ENC_THREAD%" (
          
            rem 最後のスレッドを追加
            set LINETEMP= !LINETEMP! ^| ^( call "%%d\%%~ni.bat" "!PPD_EN!" ^)
            
            rem 並列実行用BATを書き出して実行
            (echo !LINETEMP! > "%TEMP_DIR%%EXEBAT%")
            call "%TEMP_DIR%%EXEBAT%"
            
            rem カウンタ類を初期化
            set THREADCNT=0
            set LINETEMP=
            
            rem 気休めに2秒wait
            ping localhost -n 2 > nul
            
          rem 実行リストに追加
          )ELSE (
            rem 1本目
            IF "!THREADCNT!" == "0" (
              set LINETEMP= ( call "%%d\%%~ni.bat" "!PPD_EN!" ^)
              
            rem 2本目以降
            )ELSE (
              set LINETEMP= !LINETEMP! ^| ^( call "%%d\%%~ni.bat" "!PPD_EN!" ^)
            )
            
            rem 追加した本数をカウントアップ
            set /a THREADCNT+=1
          )
        ))))))
      )
    )
    cd ..
  )

rem 指定本数毎に処理できなかった端数分のBATファイルが残っていれば処理する
  IF "!LINETEMP!" == "" (
    echo ;
  ) ELSE (
    echo !LINETEMP! > "%TEMP_DIR%%EXEBAT%"
    call "%TEMP_DIR%%EXEBAT%"
  )
  
rem 並列実行用のテンポラリBATを削除
  del "%TEMP_DIR%%EXEBAT%"

endlocal

-----------

最後に

余談になりますが、フォルダに含まれる文字列で処理を変えると何かと便利です。私は以下のようにしています。

通常  :仮CMカットファイルのみ生成(ファイル名.m2v.txt、半自動CMカット用)
fullauto : 本CMカットファイルも自動作成する(ファイル名.txt、自動CMカット用)
nocut  : 仮CMカットファイルを生成しない(m2v+aacへのdemuxのみ)
noenc  : エンコードしない

上記の前処理後、本CMカットファイルが生成されているものをエンコードします。

関連記事

エンコード関連記事一覧