it-swarm.com.de

Kann ffmpeg einen Fortschrittsbalken anzeigen?

Ich konvertiere eine .avi-Datei in eine .flv-Datei mit ffmpeg. Da das Konvertieren einer Datei sehr lange dauert, würde ich gerne einen Fortschrittsbalken anzeigen. Kann mir bitte jemand eine Anleitung geben, wie ich ungefähr so ​​vorgehen soll.

Ich weiß, dass ffmpeg den Fortschritt irgendwie in einer Textdatei ausgeben muss, und ich muss es mit ajax-Aufrufen lesen. Aber wie bekomme ich, dass ffmpeg den Fortschritt in die Textdatei ausgibt?

Vielen Dank.

52
Pawan Rao

Ich habe ein paar Tage damit rumgespielt. Diese "ffmpegprogress" Sache hat geholfen, aber es war sehr schwierig mit meinem Setup zu arbeiten und den Code schwer zu lesen.

Um den Fortschritt von ffmpeg anzuzeigen, müssen Sie Folgendes tun:

  1. führe den ffmpeg-Befehl von PHP aus, ohne dass er auf eine Antwort wartet (für mich war dies der schwierigste Teil)
  2. sagt ffmpeg, dass es seine Ausgabe in eine Datei senden soll
  3. vom Frontend aus (AJAX, Flash oder was auch immer) schlagen Sie entweder diese Datei direkt oder eine PHP-Datei, die den Fortschritt aus der ffmpeg-Ausgabe abrufen kann.

So habe ich jeden Teil gelöst:

Ich habe folgende Idee von "ffmpegprogress". Das hat er getan: Eine PHP -Datei ruft eine andere über einen http-Socket an. Die zweite führt tatsächlich den "exec" aus und die erste Datei hängt einfach daran. Für mich war seine Implementierung zu komplex. Er benutzte "fsockopen". Ich mag CURL. Also hier ist was ich getan habe:

$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);

Wenn Sie CURLOPT_TIMEOUT auf 1 setzen, wird 1 Sekunde auf eine Antwort gewartet. Am liebsten wäre das niedriger. Es gibt auch die CURLOPT_TIMEOUT_MS, die Millisekunden dauert, aber für mich nicht funktioniert hat.

Nach 1 Sekunde legt CURL auf, der exec-Befehl wird jedoch weiterhin ausgeführt. Teil 1 gelöst.

Übrigens - Einige Leute haben vorgeschlagen, dafür den Befehl "Nohup" zu verwenden. Aber das schien für mich nicht zu funktionieren.

*EBENFALLS! Eine PHP-Datei auf Ihrem Server zu haben, die Code direkt in der Befehlszeile ausführen kann, ist ein offensichtliches Sicherheitsrisiko. Sie sollten ein Kennwort haben oder die Postdaten auf irgendeine Weise kodieren.

2 ..__ Das obige Skript "exec.php" muss auch angeben, dass ffmpeg in eine Datei ausgegeben wird. Hier ist der Code dafür:

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

Beachten Sie das "1> Pfad/zu/output.txt 2> & 1". Ich bin kein Kommandozeilen-Experte, aber von dem, was ich sagen kann, sagt diese Zeile "normale Ausgabe an diese Datei senden UND Fehler an dieselbe Stelle senden". Weitere Informationen finden Sie in dieser URL: http://tldp.org/LDP/abs/html/io-redirection.html

3 . Rufen Sie vom Frontend aus ein PHP-Skript auf, in dem die Position der Datei output.txt angegeben ist. Diese PHP-Datei zieht dann den Fortschritt aus der Textdatei. So habe ich das gemacht:

// # 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;

Hoffe das hilft jemandem.

31
Mike Kellogg

Es gibt einen Artikel auf Russisch, der beschreibt, wie Sie Ihr Problem lösen können.

Der Punkt ist, den Duration-Wert vor der Codierung abzufangen und time=...-Werte während der Codierung abzufangen.

--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--
24
baltazar

FFmpeg verwendet stdout zur Ausgabe von Mediendaten und stderr für Protokollierung/Fortschrittsinformationen. Sie müssen nur stderr auf eine Datei umleiten oder einen Prozess ausführen, der sie verarbeiten kann.

Bei einer Unix-Shell ist das etwa so:

ffmpeg {ffmpeg arguments} 2> logFile

oder 

ffmpeg {ffmpeg arguments} 2| processFFmpegLog

Auf jeden Fall müssen Sie ffmpeg als separaten Thread oder Prozess ausführen.

19
mouviciel

Es ist sehr einfach, wenn Sie den Befehl pipeview verwenden. Um dies zu tun, verwandeln Sie

ffmpeg -i input.avi {arguments}

zu

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

Keine Notwendigkeit, in die Codierung einzusteigen!

13
istefani

Sie können dies mit dem -progress-Argument von ffmpeg und nc tun.

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
8
iluvcapra

javascript sollte php anweisen, mit der Konvertierung zu beginnen [1] und dann [2] zu tun ...


[1] php: Konvertierung starten und Status in Datei schreiben (siehe oben):

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

Für den zweiten Teil benötigen wir nur Javascript , um die Datei zu lesen. Das folgende Beispiel verwendet dojo.request für AJAX, Sie können jedoch auch jQuery oder Vanilla oder was auch immer verwenden:

[2] js: holt den Fortschritt aus der Datei:

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
sebilasse

Hatte Probleme mit dem zweiten PHP-Teil. Also verwende ich das stattdessen:

    $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)));

Gibt perfekt aus.

Möchte etwas für mehrere Uploads für einen anderen Fortschrittsbalken sehen. Diese Übergabe für die aktuelle Datei um einen Prozentwert. Dann eine allgemeine Fortschrittsanzeige. Fast dort.

Auch wenn es den Leuten schwer fällt, zu bekommen:

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

Arbeiten.

Versuchen:

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

" 1> Pfad " nach " 1> Pfad " OR " 2> Pfad " nach " 2> Pfad "

Ich habe eine Weile gebraucht, um es herauszufinden. FFMPEG fiel immer wieder aus. Arbeitete, als ich zu keinem Platz wechselte.

1
Neotropic

Wenn Sie die Systemfunktion von php aufrufen, wird dieser Thread blockiert. Daher müssen Sie 1 HTTP-Anforderung für die Konvertierung und eine weitere Abfrage zum Lesen der TXT-Datei generieren.

Oder noch besser: Kunden senden das Video zur Konvertierung ein, und dann wird ein anderer Prozess für die Konvertierung verantwortlich gemacht. Auf diese Weise läuft die Verbindung des Clients nicht ab, während auf den Systemaufruf gewartet wird. Die Abfrage erfolgt wie oben beschrieben. 

1
Allain Lalonde

Leider kann ffmpeg selbst noch keinen Fortschrittsbalken anzeigen - auch viele der zuvor genannten bash- oder pythonbasierten Stop-gap-Lösungen sind veraltet und nicht mehr funktionsfähig.

Daher empfehle ich, das brandneue ffmpeg-progressbar-cli a zu versuchen:

 ffmpeg-progressbar-cli screencast

Es ist ein Wrapper für die ffmpeg-Programmdatei, die einen farbigen, zentrierten Fortschrittsbalken und die verbleibende Zeit anzeigt.

Außerdem handelt es sich um Open Source, das auf Node.js basiert und aktiv entwickelt wurde und die meisten der erwähnten Macken behandelt (vollständige Offenlegung: Ich bin der derzeitige Hauptentwickler).

0
sidneys

Wenn Sie nur alle Informationen ausblenden und den Standardfortschritt wie ffmpeg in der letzten Zeile anzeigen möchten, können Sie die Option -stats verwenden:

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