27

PHP でシステム全体を作成し、サーバー上で bash して、VPS で HTML5 のビデオを変換およびストリーミングしました。変換はバックグラウンドで ffmpeg によって行われ、内容はblock.txtに出力されます。

次の投稿を見たところ:

ffmpeg は進行状況バーを表示できますか?

ffmpeg ビデオ エンコーディング プログレス バー

とりわけ、実用的な例が見つかりません。

現在エンコードされている進行状況をパーセンテージで取得する必要があります。

上でリンクした最初の投稿は次のとおりです。

$log = @file_get_contents('block.txt');

preg_match("/Duration:([^,]+)/", $log, $matches);
list($hours,$minutes,$seconds,$mili) = split(":",$matches[1]);
$seconds = (($hours * 3600) + ($minutes * 60) + $seconds);
$seconds = round($seconds);

$page = join("",file("$txt"));
$kw = explode("time=", $page);
$last = array_pop($kw);
$values = explode(' ', $last);
$curTime = round($values[0]);
$percent_extracted = round((($curTime * 100)/($seconds)));

echo $percent_extracted;

$percent_extracted 変数はゼロを返します。数学は私の得意分野ではないため、ここでどのように進めればよいかわかりません。

これは、block.txt からの ffmpeg 出力からの 1 行です (役立つ場合)。

time=00:19:25.16 bitrate= 823.0kbits/s frame=27963 fps= 7 q=0.0 size= 117085kB time=00:19:25.33 bitrate= 823.1kbits/s frame=27967 fps= 7 q=0.0 size= 117085kB time=00:19:25.49 ビットレート= 823.0kbits/s フレーム=27971 fps= 7 q=0.0 サイズ= 117126kB

このパーセンテージを出力するのを手伝ってください。完了したら、独自の進行状況バーを作成できます。ありがとう。

4

3 に答える 3

48

Okay, I've found what I needed - and hopefully this helps someone else as well!

First and foremost, you want to output the ffmpeg data to a text file on the server.

ffmpeg -i path/to/input.mov -vcodec videocodec -acodec audiocodec path/to/output.flv 1> block.txt 2>&1

So, the ffmpeg output is block.txt. Now in PHP, let's do this!

$content = @file_get_contents('../block.txt');

if($content){
    //get duration of source
    preg_match("/Duration: (.*?), start:/", $content, $matches);

    $rawDuration = $matches[1];

    //rawDuration is in 00:00:00.00 format. This converts it to seconds.
    $ar = array_reverse(explode(":", $rawDuration));
    $duration = floatval($ar[0]);
    if (!empty($ar[1])) $duration += intval($ar[1]) * 60;
    if (!empty($ar[2])) $duration += intval($ar[2]) * 60 * 60;

    //get the time in the file that is already encoded
    preg_match_all("/time=(.*?) bitrate/", $content, $matches);

    $rawTime = array_pop($matches);

    //this is needed if there is more than one match
    if (is_array($rawTime)){$rawTime = array_pop($rawTime);}

    //rawTime is in 00:00:00.00 format. This converts it to seconds.
    $ar = array_reverse(explode(":", $rawTime));
    $time = floatval($ar[0]);
    if (!empty($ar[1])) $time += intval($ar[1]) * 60;
    if (!empty($ar[2])) $time += intval($ar[2]) * 60 * 60;

    //calculate the progress
    $progress = round(($time/$duration) * 100);

    echo "Duration: " . $duration . "<br>";
    echo "Current Time: " . $time . "<br>";
    echo "Progress: " . $progress . "%";

}

This outputs the percentage of time left.

You can have this as the only piece of text echoed out to a page, and from another page you can perform an AJAX request using jQuery to grab this piece of text and output it into a div, for example, to update on your page every 10 seconds. :)

于 2012-07-12T15:57:31.870 に答える
8

ffmpeg に進行オプションが追加され、出力がより簡単に解析できるようになりました。

ffmpeg -progress block.txt -i path/to/input.mov -vcodec videocodec -acodec audiocodec path/to/output.flv 2>&1

エンコーディングを開始する前に、これを使用して合計フレームとその他の多くの情報を取得できます (これは bash で行うことです。私は Perl プログラマーなので、どのようにして情報をPHP スクリプト)。

eval $(ffprobe -of flat=s=_ -show_entries stream=height,width,nb_frames,duration,codec_name path/to/input.mov);
width=${streams_stream_0_width};
height=${streams_stream_0_height};
frames=${streams_stream_0_nb_frames};
videoduration=${streams_stream_0_duration};
audioduration=${streams_stream_1_duration};
codec=${streams_stream_0_codec_name};
echo $width,$height,$frames,$videoduration,$audioduration,$codec;

-of flate=s=_ は、各 name=value を別の行に配置するように指示します。-show_entries は、次のものからエントリを表示するように指示します (-show_streams のストリーム、-show_format のフォーマットなど)。 stream=... は、-show_streams 出力からそれらの項目を表示するように指示します。利用可能なものを確認するには、次のことを試してください。

ffprobe -show_streams path/to/input.mov

進行状況ファイルへの出力は、約 1 秒に 1 回追加されます。エンコードが完了すると、コンテンツは次のようになります。私のスクリプトでは、1 秒に 1 回、ファイルを配列に入れ、逆の順序で配列をトラバースし、見つけた最初の [反転前の最後] 2 つの「進行状況」行の間にあるものだけを使用して、ファイルの最後から最新の情報。もっと良い方法があるかもしれません。これはオーディオのない mp4 からのものであるため、ストリームは 1 つしかありません。

frame=86
fps=0.0
stream_0_0_q=23.0
total_size=103173
out_time_ms=1120000 out_time= 00
:00:01.120000
dup_frames=0
drop_frames=0
progress=継続
フレーム=142
fps=
140.9 stream_0_0_q=23.0
total_size=415000
out_time_ms=0_3360
00:03.360000
dup_frames=0
drop_frames=0
progress=継続
frame=185
fps=121.1
stream_0_0_q=23.0
total_size=1268982
out_time_ms=5080000
out_time=00:00:05.080000
dup_frames=0
drop_frames=0
progress=継続
frame=225
fps=110.9
stream_0_0_q=23.0
total_size=2366000
out_time_ms=6680000
out_time=00:00:06.680000
dup_frames=0
drop_frames=0
progress=継続
frame=262
fps=103.4
stream_0_0_q=23.0
total_size0=3810570
out_time_0
: 8_time_0ms 00:08.160000
dup_frames=0
drop_frames=0
progress=継続
フレーム=299
fps=84.9
stream_0_0_q=-1.0
total_size=6710373
out_time_ms=11880000
out_time=00:00:11.880000
dup_frames=0
drop_frames=0
progress=end

于 2015-01-05T17:10:47.097 に答える
1

JavaScript が進行状況バーを更新する場合、JavaScript はステップ 2 を「直接」実行できます。

[この例ではDojoが必要です]


1 php:変換を開始し、ステータスをテキストファイルに書き込みます - 構文例:

exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");

2 番目の部分では、ファイルを読み取るために JavaScript だけが必要です。次の例では、AJAX に dojo.request を使用していますが、jQuery やバニラなどを使用することもできます。

[2] js:ファイルから進行状況を取得します:

var _progress = function(i){
    i++;
    // THIS MUST BE THE PATH OF THE .txt FILE SPECIFIED IN [1] : 
    var logfile = 'path/to/output.txt';

/* (example requires dojo) */

request.post(logfile).then( function(content){
// AJAX success
    var duration = 0, time = 0, progress = 0;
    var result = {};

    // get duration of source
    var matches = (content) ? content.match(/Duration: (.*?), start:/) : [];
    if( matches.length>0 ){
        var rawDuration = matches[1];
        // convert rawDuration from 00:00:00.00 to seconds.
        var ar = rawDuration.split(":").reverse();
        duration = parseFloat(ar[0]);
        if (ar[1]) duration += parseInt(ar[1]) * 60;
        if (ar[2]) duration += parseInt(ar[2]) * 60 * 60;

        // get the time 
        matches = content.match(/time=(.*?) bitrate/g);
        console.log( matches );

        if( matches.length>0 ){
            var rawTime = matches.pop();
            // needed if there is more than one match
            if (lang.isArray(rawTime)){ 
                rawTime = rawTime.pop().replace('time=','').replace(' bitrate',''); 
            } else {
                rawTime = rawTime.replace('time=','').replace(' bitrate','');
            }

            // convert rawTime from 00:00:00.00 to seconds.
            ar = rawTime.split(":").reverse();
            time = parseFloat(ar[0]);
            if (ar[1]) time += parseInt(ar[1]) * 60;
            if (ar[2]) time += parseInt(ar[2]) * 60 * 60;

            //calculate the progress
            progress = Math.round((time/duration) * 100);
        }

        result.status = 200;
        result.duration = duration;
        result.current  = time;
        result.progress = progress;

        console.log(result);

        /* UPDATE YOUR PROGRESSBAR HERE with above values ... */

        if(progress==0 && i>20){
            // TODO err - giving up after 8 sec. no progress - handle progress errors here
            console.log('{"status":-400, "error":"there is no progress while we tried to encode the video" }'); 
            return;
        } else if(progress<100){ 
            setTimeout(function(){ _progress(i); }, 400);
        }
    } else if( content.indexOf('Permission denied') > -1) {
        // TODO - err - ffmpeg is not executable ...
        console.log('{"status":-400, "error":"ffmpeg : Permission denied, either for ffmpeg or upload location ..." }');    
    } 
},
function(err){
// AJAX error
    if(i<20){
        // retry
        setTimeout(function(){ _progress(0); }, 400);
    } else {
        console.log('{"status":-400, "error":"there is no progress while we tried to encode the video" }');
        console.log( err ); 
    }
    return; 
});
}
setTimeout(function(){ _progress(0); }, 800);
于 2013-06-26T08:09:09.740 に答える