ffmpeg progress-bar

¿Puede ffmpeg mostrar una barra de progreso?



progress-bar (8)

Estoy convirtiendo un archivo .avi a un archivo .flv usando ffmpeg. Como lleva mucho tiempo convertir un archivo, me gustaría mostrar una barra de progreso. ¿Puede alguien por favor guiarme sobre cómo hacer lo mismo?

Sé que ffmpeg de alguna manera tiene que generar el progreso en un archivo de texto y tengo que leerlo usando las llamadas ajax. Pero, ¿cómo obtengo ffmpeg para enviar el progreso al archivo de texto?

Muchas gracias.


Es muy simple si usa el comando pipeview. Para hacer esto, transforma

ffmpeg -i input.avi {arguments}

a

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

¡No es necesario entrar en la codificación!


FFmpeg usa stdout para generar datos multimedia y stderr para la información de registro / progreso. Solo tiene que redirigir stderr a un archivo o al stdin de un proceso capaz de manejarlo.

Con un shell Unix esto es algo así como:

ffmpeg {ffmpeg arguments} 2> logFile

o

ffmpeg {ffmpeg arguments} 2| processFFmpegLog

De todos modos, debes ejecutar ffmpeg como un hilo o proceso separado.


Hay un article en ruso que describe cómo resolver su problema.

El punto es capturar el valor de Duration antes de la codificación y capturar los valores de time=... durante la codificación.

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


Llamar a la función del sistema de php bloquea esa cadena, por lo que necesitarás generar 1 solicitud HTTP para realizar la conversión, y otra votación para leer el archivo txt, que se está generando.

O, mejor aún, los clientes envían el video para la conversión y luego otro proceso se hace responsable de realizar la conversión. De esta forma, la conexión del cliente no se agotará mientras espera que finalice la llamada al sistema. El sondeo se hace de la misma manera que arriba.


Puedes hacerlo con el argumento -progress ffmpeg y 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


Tuve problemas con la segunda parte php. Así que estoy usando esto en su lugar:

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

Salidas perfectamente.

Me gustaría ver algo para cargas múltiples para otra barra de progreso. Esto pasa para el archivo actual por un porcentaje. Luego, una barra de progreso general. Casi ahí.

Además, si la gente está teniendo dificultades para obtener:

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

Trabajar.

Tratar:

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

" 1> ruta " a " 1> ruta " O " 2> ruta " a " 2> ruta "

Me tomó un tiempo resolverlo. FFMPEG siguió fallando. Funcionó cuando cambié a ningún espacio.


javascript debería decirle a php que comience a convertir [1] y luego haga [2] ...

[1] php: inicia la conversión y escribe el estado en el archivo (ver arriba):

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

Para la segunda parte, solo necesitamos javascript para leer el archivo. El siguiente ejemplo usa dojo.request para AJAX, pero también puedes usar jQuery o vainilla o lo que sea:

[2] js: toma el progreso del archivo:

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


He estado jugando con esto durante unos días. Esa cosa "ffmpegprogress" ayudó, pero fue muy difícil llegar a trabajar con mi configuración, y es difícil leer el código.

Para mostrar el progreso de ffmpeg, debe hacer lo siguiente:

  1. ejecuta el comando ffmpeg desde php sin esperar una respuesta (para mí, esta fue la parte más difícil)
  2. dile a ffmpeg que envíe su salida a un archivo
  3. desde el extremo frontal (AJAX, Flash, lo que sea), acierta ese archivo directamente o un archivo php que puede extraer el progreso de la salida de ffmpeg.

Así es como resolví cada parte:

1. Obtuve la siguiente idea de "ffmpegprogress". Esto es lo que hizo: un archivo PHP llama a otro a través de un socket http. El segundo en realidad ejecuta el "ejecutivo" y el primer archivo simplemente cuelga en él. Para mí, su implementación fue demasiado compleja. Él estaba usando "fsockopen". Me gusta CURL. Entonces esto es lo que hice:

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

Establecer CURLOPT_TIMEOUT en 1 significa que esperará 1 segundo para una respuesta. Preferiblemente eso sería más bajo. También está el CURLOPT_TIMEOUT_MS que demora milisegundos, pero no funcionó para mí.

Después de 1 segundo, CURL cuelga, pero el comando exec aún se ejecuta. Parte 1 resuelta.

Por cierto: algunas personas sugirieron usar el comando "nohup" para esto. Pero eso no pareció funcionar para mí.

*¡ADEMÁS! Tener un archivo php en su servidor que pueda ejecutar código directamente en la línea de comandos es un riesgo obvio de seguridad. Debe tener una contraseña o codificar los datos de la publicación de alguna manera.

2. El script "exec.php" de arriba también debe decirle a ffmpeg que salga a un archivo. Aquí hay un código para eso:

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

Tenga en cuenta la "1> ruta / a / salida.txt 2> & 1". No soy un experto en línea de comandos, pero por lo que puedo decir, esta línea dice "envíe salida normal a este archivo Y envíe errores al mismo lugar". Consulte esta url para obtener más información: http://tldp.org/LDP/abs/html/io-redirection.html

3. Desde la interfaz, llame a un script php dándole la ubicación del archivo output.txt. Ese archivo php luego sacará el progreso del archivo de texto. Así es como lo hice:

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

Espero que esto ayude a alguien.