2011年12月19日

FFmpegで動画変換時のProgressBarを表示する(2/4)

FFmpeg実行結果をPHPで取得する

 さてブラウザーにffmpegで動画を変換する時のログを表示させるには、PHPでこれらの情報を取得しなければいけません。まずはPHPでプログラムする為の流れを見てみましょう。

  1. ffmpegのコマンドを実行する。
  2. ffmpegコマンドの実行結果を取得する。
  3. 取得した結果を一行ずつ表示する。

 簡単かと思いますが、これが基本的な流れです。ではこれをプログラムで見ていきましょう。

1.ffmpegのコマンドを実行する

 まずは以下のコードを見てください。

$command = "ffmpeg -i test.mpeg test.flv 2>&1";
$handle = popen($command,'r');

 PHPからffmpegのログを取得するにはコマンドの最後に"2>&1"をつける必要があります。これがないとログを取得することができないので注意してください。

 PHPから外部プログラムを実行する場合、system()やexec()という関数が用いられます。しかしこれらの関数を使うと、取得したログは一度バッファに貯めてから出力されます。つまり変換が終ってしまった後に一斉にログが取得されるので、プログレスバーを作る目的には適していません。ですからその代わりにpopen()使っています。popen()を使えばログをリアルタイムで取得できるようになります。

2.ffmpegコマンドの実行結果を取得する

 続いてコマンドの実行結果を取得しますが、これはファイル操作で使うfopen()と同様にハンドルを使ってfgets()で取得できます。

$log = fgets($handle);
3.取得した結果を一行ずつ表示する

 最後は取得したログ一行分を順番に出力していけば完成です。

while(!feof($handle)) {

      $log = fgets($handle);
      echo $log."\n";
      ob_flush();
      flush();

}   

 実はブラウザやwebサーバーはプログラム上の一連の計算を一度バッファに貯め、結果が出れば出力するという形になっている為折角popen()でコマンドを実行しても、ログは変換が完了した後にブラウザに出力されるという結果になってしまいます。それを防ぐ為にこのobflush()とflush()が必要になっています。

 ではプログラム全体を通してみてみましょう。

<?php

$command = "ffmpeg -i test.mpeg test.flv 2>&1";
$handle = popen($command,'r');

echo "<pre>";
while(!feof($handle)) {

      $log = fgets($handle);
      echo $log."\n";
      ob_flush();
      flush();

}

pclose($handle);
echo "</pre>";

?>

 popen()で開いたら最後は必ずpclose()で閉じる事を忘れないようにしましょう。

 さてこのコードを適当な名前でphpファイルとしブラウザからアクセスしてみてください。すると図3にあるように"Press"以降のframeから始まる部分が動画の変換後に一気に出力されたかと思います。実はこれ、fgets()による改行コードの扱いが問題となっています。

 fgets()は「CR+LF」か「LF」を改行コードとして扱っており、この改行コードが見つかれば一行分として取得します。しかしframesから始まる文の最後は「CR」というコードで終っています。

ただしphp.iniのauto_detect_line_endingsがonの場合は「CR」を行として認識します。
frame= 125 fps= 0 q=22.6 size= 212kB time=00:00:03.60 bitrate= 481.0kbits/s ¥r

 これを解決するのがstream_get_line()です。これは指定された文字の位置までのデータを一行分として読み込みます。つまり「CR」を指定してその位置までのデータを一行分として取得すればいい事になります。

$log = stream_get_line($handle,1024,"¥r")

 改行コードについてはここでは詳しくは述べませんが、「CR」は「¥r」、「LF」は「¥n」を表しています。

 frameから始まるログは"Press [q] to stop, [?] for help"という文が表示された直後に始まり、"video:9993kB audio:6287kB global headers:0kB muxing overhead 2.637443% "という文が表示される直前で終ります。ですから"Press"という文字に一致した後のログはstream_get_line()で取得し、"overhead"が出てくるまで繰り返すようにします。

ログをブラウザで順番に表示する

 では先程のコードを書き換えてみます。

コード1:ブラウザでログを表示1
<?php

$command = "ffmpeg -i test.mpeg test.flv 2>&1";
$handle = popen($command,'r');

echo "<pre>";
while(!feof($handle)) {

      $log = fgets($handle);

      if(preg_match('/Press/',$log)){

           echo $log."¥n";
           ob_flush();
           flush();
      
           while($log = stream_get_line($handle,1024,"¥r")){
                if((preg_match('/overhead/',$log)) == 1){

                     echo $log."¥n";
                     ob_flush();
                     flush();
                     break;
             
                }else{

                     echo $log."¥n";
                     ob_flush();
                     flush();
               }
           } 

      }else{

        echo $log."¥n";
        ob_flush();
        flush();

      }      
}
pclose($handle);
echo "</pre>";

?>

 これを適当な名前のphpファイルとして保存し、このファイルと同じ階層にtest.mpegを配置してブラウザからアクセスしてみてください。すると"frame"からのログも一行ずつ表示されていくのが分かると思います。

実行結果ブラウザ表示
図5:ffmpegのコマンドの結果をブラウザで表示1

 実は以下のコードの部分はなくてもログの表示に関しては問題ないのですが、最後のページで説明する変換の完了時に必ず100%にする為に必要になります。

if((preg_match('/overhead/',$log)) == 1){

      echo $log."\n";
      ob_flush();
      flush();
      break;
             
}

 次のページからはこのPHPで取得したログを使って、変換の進行状況をパーセンテージで表すために必要なデータと計算方法を解説していきます。

posted by よしお | Comment(0) | TrackBack(0) | ffmpeg | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/241604473

この記事へのトラックバック