ffmpeg を使用して .avi ファイルを .flv ファイルに変換しています。ファイルの変換に時間がかかるため、進行状況バーを表示したいと考えています。誰かが同じことをする方法を教えてください。
ffmpeg は何らかの形で進行状況をテキスト ファイルに出力する必要があり、ajax 呼び出しを使用してそれを読み取る必要があることを知っています。しかし、ffmpeg で進行状況をテキスト ファイルに出力するにはどうすればよいでしょうか。
ffmpeg を使用して .avi ファイルを .flv ファイルに変換しています。ファイルの変換に時間がかかるため、進行状況バーを表示したいと考えています。誰かが同じことをする方法を教えてください。
ffmpeg は何らかの形で進行状況をテキスト ファイルに出力する必要があり、ajax 呼び出しを使用してそれを読み取る必要があることを知っています。しかし、ffmpeg で進行状況をテキスト ファイルに出力するにはどうすればよいでしょうか。
私はこれで数日間遊んでいます。その「ffmpegprogress」は役に立ちましたが、私のセットアップで作業するのは非常に難しく、コードを読むのは困難でした.
ffmpeg の進行状況を表示するには、次の手順を実行する必要があります。
これが私が各部分をどのように解決したかです:
1. 「ffmpegprogress」から次のアイデアを得ました。これが彼のやったことです: ある PHP ファイルが http ソケットを介して別のファイルを呼び出します。2番目のファイルは実際に「exec」を実行し、最初のファイルはハングアップします。私にとって、彼の実装は複雑すぎました。彼は「fsockopen」を使用していました。カールが好きです。だからここに私がしたことがあります:
$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;
これが誰かに役立つことを願っています。
問題を解決する方法を説明するロシア語の記事があります。
ポイントは、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--
pipeview コマンドを使えばとても簡単です。これを行うには、変換します
ffmpeg -i input.avi {arguments}
に
pv input.avi | ffmpeg -i pipe:0 -v warning {arguments}
コーディングに入る必要はありません!
FFmpeg は、メディア データの出力に stdout を使用し、ロギング/進行状況情報の出力に stderr を使用します。stderr をファイルまたはそれを処理できるプロセスの stdin にリダイレクトするだけです。
UNIX シェルでは、これは次のようなものです。
ffmpeg {ffmpeg arguments} 2> logFile
また
ffmpeg {ffmpeg arguments} 2| processFFmpegLog
とにかく、別のスレッドまたはプロセスとして ffmpeg を実行する必要があります。
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
javascript は、[1] の変換を開始するように php に指示し、[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 やバニラなどを使用することもできます。
[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);
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 " から " 1>path " または " 2> path " から " 2>path "
それを理解するのにしばらく時間がかかりました。FFMPEG は失敗し続けました。スペースなしに変更したときに機能しました。
php のシステム関数を呼び出すとそのスレッドがブロックされるため、変換を実行するために 1 つの HTTP リクエストを生成し、生成されている txt ファイルを読み取るために別のポーリング リクエストを生成する必要があります。
または、クライアントが変換のためにビデオを送信し、別のプロセスが変換の実行を担当するようにすることをお勧めします。そうすれば、システム コールが終了するのを待っている間にクライアントの接続がタイムアウトすることはありません。ポーリングは上記と同じ方法で行われます。
複数のツール/コンソールを使用するこれらの回答は、物事を複雑にしすぎています。
pv
は良いオプションですが、重要でないデータが欠落しているという欠点があります。
ユーティリティを使用するだけprogress
です:通常どおりffmpegを実行してから、別のコンソールモニターでprogress -m -c ffmpeg