web-dev-qa-db-ja.com

ffmpegはプログレスバーを表示できますか?

Ffmpegを使用して.aviファイルを.flvファイルに変換しています。ファイルの変換には時間がかかるため、進行状況バーを表示したいと思います。誰かが同じことをする方法を教えてくれますか?.

私は、ffmpegが何らかの形で進行状況をテキストファイルに出力しなければならず、ajax呼び出しを使用してそれを読み取らなければならないことを知っています。しかし、ffmpegでテキストファイルに進行状況を出力するにはどうすればよいですか?

どうもありがとうございました。

54
Pawan Rao

私は数日間これで遊んでいます。その「ffmpegprogress」の問題は解決しましたが、私のセットアップで作業するのは非常に難しく、コードを読むのは困難でした。

Ffmpegの進行状況を表示するには、以下を実行する必要があります。

  1. 応答を待たずにphpからffmpegコマンドを実行します(私にとって、これが最も難しい部分でした)
  2. 出力をファイルに送信するようにffmpegに指示します
  3. フロントエンド(AJAX、Flashなど)から、そのファイルを直接ヒットするか、ffmpegの出力から進行状況を引き出すことができるphpファイルをヒットします。

各部分の解決方法は次のとおりです。

1.「ffmpegprogress」から次のアイデアを得ました。これは彼がやったことです:1つのPHPファイルはhttpソケットを介して別のファイルを呼び出します。彼は「fsockopen」を使用していました。私はCURLが好きです。

$url = "http://".$_SERVER["HTTP_Host"]."/path/to/exec/exec.php";
curl_setopt($curlH, CURLOPT_URL, $url);
$postData = "&cmd=".urlencode($cmd);
$postData .= "&outFile=".urlencode("path/to/output.txt");
curl_setopt($curlH, CURLOPT_POST, TRUE);
curl_setopt($curlH, CURLOPT_POSTFIELDS, $postData);

curl_setopt($curlH, CURLOPT_RETURNTRANSFER, TRUE);

// # this is the key!
curl_setopt($curlH, CURLOPT_TIMEOUT, 1);
$result = curl_exec($curlH);

CURLOPT_TIMEOUTを1に設定すると、応答を1秒待機します。できればそれは低いでしょう。ミリ秒かかるCURLOPT_TIMEOUT_MSもありますが、私には機能しませんでした。

1秒後、CURLは電話を切りますが、execコマンドは引き続き実行されます。パート1が解決しました。

ところで-少数の人々はこれに「Nohup」コマンドを使用することを提案していました。しかし、それは私にはうまくいかないようでした。

*また!コマンドラインで直接コードを実行できるphpファイルをサーバー上に置くことは、明らかなセキュリティリスクです。パスワードを持っているか、何らかの方法で投稿データをエンコードする必要があります。

2.上記の「exec.php」スクリプトも、ffmpegにファイルへの出力を指示する必要があります。そのためのコードは次のとおりです。

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

「1> path/to/output.txt 2>&1」に注意してください。私はコマンドラインの専門家ではありませんが、この行に伝えることができるのは「このファイルに通常の出力を送信し、同じ場所にエラーを送信する」ということです。詳細については、次のURLを参照してください。 http://tldp.org/LDP/abs/html/io-redirection.html

3.フロントエンドからphpスクリプトを呼び出して、output.txtファイルの場所を指定します。そのphpファイルは、テキストファイルから進行状況を引き出します。以下がその方法です。

// # 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 current time
preg_match_all("/time=(.*?) bitrate/", $content, $matches); 

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

$curTime = floatval($last);


// # finally, progress is easy
$progress = $curTime/$duration;

これが誰かを助けることを願っています。

32
Mike Kellogg

ロシア語には 記事 があり、問題の解決方法を説明しています。

ポイントは、エンコード前にDuration値をキャッチし、time=...エンコード中の値。

--skipped--
Duration: 00:00:24.9, start: 0.000000, bitrate: 331 kb/s
--skipped--
frame=   41 q=7.0 size=     116kB time=1.6 bitrate= 579.7kbits/s
frame=   78 q=12.0 size=     189kB time=3.1 bitrate= 497.2kbits/s
frame=  115 q=13.0 size=     254kB time=4.6 bitrate= 452.3kbits/s
--skipped--
25
baltazar

FFmpegは、メディアデータの出力にstdoutを使用し、ログ/進行情報にstderrを使用します。 stderrをファイルまたはそれを処理できるプロセスのstdinにリダイレクトする必要があります。

UNIXシェルでは、これは次のようなものです。

ffmpeg {ffmpeg arguments} 2> logFile

または

ffmpeg {ffmpeg arguments} 2| processFFmpegLog

とにかく、ffmpegを別のスレッドまたはプロセスとして実行する必要があります。

19
mouviciel

Pipeviewコマンドを使用すると非常に簡単です。これを行うには、変換します

ffmpeg -i input.avi {arguments}

pv input.avi | ffmpeg -i pipe:0 -v warning {arguments}

コーディングする必要はありません!

14
istefani

ffmpeg-progress引数とnc

WATCHER_PORT=9998

DURATION= $(ffprobe -select_streams v:0 -show_entries "stream=duration" \
    -of compact $INPUT_FILE | sed 's!.*=\(.*\)!\1!g')

nc -l $WATCHER_PORT | while read; do
    sed -n 's/out_time=\(.*\)/\1 of $DURATION/p')
done &

ffmpeg -y -i $INPUT_FILE -progress localhost:$WATCHER_PORT $OUTPUT_ARGS
9
iluvcapra

残念ながら、ffmpeg自体はまだ進行状況バーを表示できません。また、前述のbashベースまたはpythonベースのストップギャップソリューションの多くは、時代遅れで機能しなくなりました。

したがって、新しい ffmpeg-progressbar-cli を試すことをお勧めします:

ffmpeg-progressbar-cli screencast

これはffmpeg実行可能ファイルのラッパーであり、色付きの中央の進行状況バーと残り時間を示します。

また、Node.jsに基づいて開発されたオープンソースであり、言及された癖のほとんどを処理します(完全な開示:現在の主な開発者です)。

3
sidneys

javascriptはphpに変換を開始し[1]してから[2]するように指示する必要があります...


[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やVanillaなども使用できます。

[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 resArr = [];

    // 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);
        }

        resArr['status'] = 200;
        resArr['duration'] = duration;
        resArr['current']  = time;
        resArr['progress'] = progress;

        console.log(resArr);

        /* 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);
3
sebilasse

すべての情報を非表示にし、最終行にffmpegのようなデフォルトの進行状況を表示する必要がある場合は、-statsオプション:

ffmpeg -v warning -hide_banner -stats ${your_params}
1
Geograph

2番目のphp部分に問題がありました。だから私は代わりにこれを使用しています:

    $log = @file_get_contents($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)));

完全に出力します。

別のプログレスバーの複数のアップロードについて何かを見たいです。これは、現在のファイルを1パーセント渡します。次に、全体的な進行状況バー。ほぼそこに。

また、人々が取得するのに苦労している場合:

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

働くために。

試してください:

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

"1> path" to "1> path" OR "2> path" to "- 2> path "

それを理解するためにしばらくかかりました。 FFMPEGは失敗し続けました。スペースなしに変更したときに働いた。

1
Neotropic

Phpのシステム関数を呼び出すとそのスレッドがブロックされるため、変換を実行するために1つのHTTPリクエストを生成し、txtファイルを読み込むために別のポーリングリクエストを生成する必要があります。

または、さらに良いことに、クライアントは変換のためにビデオを送信し、別のプロセスが変換の実行を担当します。これにより、システムコールの終了を待機している間、クライアントの接続がタイムアウトすることはありません。ポーリングは上記と同じ方法で行われます。

1
Allain Lalonde