85 votes

Est-ce que ffmpeg peut afficher une barre de progression ?

Je suis en train de convertir un fichier .avi en fichier .flv en utilisant ffmpeg. Comme il faut beaucoup de temps pour convertir un fichier, je voudrais afficher une barre de progression. Quelqu'un peut-il me guider sur la façon de procéder ?

Je sais que ffmpeg doit en quelque sorte sortir la progression dans un fichier texte et que je dois le lire en utilisant des appels ajax. Mais comment faire pour que ffmpeg affiche la progression dans le fichier texte ?

37voto

Mike Kellogg Points 171

J'ai joué avec ça pendant quelques jours. Ce truc "ffmpegprogress" m'a aidé, mais il était très difficile de le faire fonctionner avec mon installation, et difficile de lire le code.

Afin de montrer la progression de ffmpeg, vous devez faire ce qui suit :

  1. exécuter la commande ffmpeg depuis php sans qu'elle attende une réponse (pour moi, c'était la partie la plus difficile)
  2. dire à ffmpeg d'envoyer sa sortie vers un fichier
  3. à partir de l'interface (AJAX, Flash, etc.), tapez directement sur ce fichier ou sur un fichier php qui peut extraire la progression de la sortie de ffmpeg.

Voici comment j'ai résolu chaque partie :

1. L'idée suivante m'est venue de "ffmpegprogress". Voici ce qu'il a fait : un fichier PHP en appelle un autre via un socket http. Le second exécute le "exec" et le premier fichier ne fait que s'y accrocher. Pour moi, son implémentation était trop complexe. Il utilisait "fsockopen". J'aime bien CURL. Alors voici ce que j'ai fait :

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

En fixant CURLOPT_TIMEOUT à 1, cela signifie qu'il attendra 1 seconde pour une réponse. Il serait préférable que cette valeur soit inférieure. Il existe aussi le CURLOPT_TIMEOUT_MS qui prend des millisecondes, mais il n'a pas fonctionné pour moi.

Après 1 seconde, CURL raccroche, mais la commande exec continue de fonctionner. Partie 1 résolue.

BTW - Quelques personnes ont suggéré d'utiliser la commande "nohup" pour cela. Mais cela n'a pas semblé fonctionner pour moi.

*Avoir un fichier php sur votre serveur qui peut exécuter du code directement sur la ligne de commande est un risque de sécurité évident. Vous devriez avoir un mot de passe, ou coder les données du message d'une manière ou d'une autre.

2. Le script "exec.php" ci-dessus doit aussi dire à ffmpeg de sortir dans un fichier. Voici le code pour cela :

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

Notez le "1> path/to/output.txt 2>&1". Je ne suis pas un expert en ligne de commande, mais d'après ce que je peux dire, cette ligne dit "envoyer la sortie normale dans ce fichier, ET envoyer les erreurs au même endroit". Consultez cette url pour plus d'informations : http://tldp.org/LDP/abs/html/io-redirection.html

3. Depuis le front-end, appelez un script php script en lui donnant l'emplacement du fichier output.txt. Ce fichier php va alors extraire la progression du fichier texte. Voici comment j'ai fait :

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

J'espère que cela aidera quelqu'un.

1 votes

Je sais que c'est un vieux fil de discussion mais j'aimerais ajouter quelque chose que j'ai découvert aujourd'hui. Il semble qu'avec ffmpeg, il y ait ffprobe qui peut être utilisé pour obtenir des informations dans la sortie std ("ffmpeg -i {file}" sort en fait avec un code d'erreur, donc vous devrez utiliser 2>1 pour obtenir les informations) : ffprobe -v quiet -print_format json -show_format -i file.mp4 -show_streams

35voto

baltazar Points 471

Il existe un article en russe qui décrit comment résoudre votre problème.

Le but est d'attraper Duration avant l'encodage et pour attraper time=... pendant l'encodage.

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

19 votes

J'ai lu l'article en m'attendant à avoir un peu de mal avec le russe, mais il était étonnamment facile à lire.

0 votes

Je pense que la commande de l'article est la suivante ffmpeg -y -i input.avi -vcodec xvid -acodec mp3 -ab 96 output.avi > output.txt

20voto

mouviciel Points 36624

FFmpeg utilise stdout pour la sortie des données média et stderr pour les informations de journalisation/progression. Il suffit de rediriger stderr vers un fichier ou vers stdin d'un processus capable de le gérer.

Avec un shell unix, cela donne quelque chose comme :

ffmpeg {ffmpeg arguments} 2> logFile

ou

ffmpeg {ffmpeg arguments} 2| processFFmpegLog

Quoi qu'il en soit, vous devez exécuter ffmpeg en tant que thread ou processus séparé.

3 votes

Si je posais la question, j'accepterais volontiers votre réponse.

0 votes

@mouviciel, savez-vous comment je peux réaliser cela en .NET ? J'ai essayé ce que vous avez dit et ça n'imprime pas de fichier, aussi comment puis-je être notifié lorsque le fichier est modifié ?

0 votes

Merci de préciser la sortie par rapport à stdout et stderr

2voto

Neotropic Points 11

J'ai eu des problèmes avec la deuxième partie de php. J'utilise donc ceci à la place :

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

Les sorties sont parfaites.

J'aimerais voir quelque chose pour les téléchargements multiples pour une autre barre de progression. Ceci en passant pour le fichier actuel pour un pourcentage. Puis une barre de progression globale. On y est presque.

Aussi, si les gens ont du mal à obtenir :

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

Pour travailler.

Essayez :

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

" 1> chemin " à " 1>chemin " OU " 2> chemin " à " 2>chemin "

Ça m'a pris du temps pour le comprendre. FFMPEG échouait toujours. Ça a fonctionné quand j'ai changé pour sans espace.

1voto

Allain Lalonde Points 28717

L'appel à la fonction système de php bloque ce fil, et vous devrez donc lancer une requête HTTP pour effectuer la conversion, et une autre pour lire le fichier txt qui est généré.

Ou, mieux encore, les clients soumettent la vidéo à convertir, puis un autre processus est chargé d'effectuer la conversion. Ainsi, la connexion du client ne sera pas interrompue en attendant la fin de l'appel système. L'interrogation se fait de la même manière que ci-dessus.

3 votes

Mais pourquoi utiliser un fichier texte ? Il doit y avoir un meilleur moyen. Ne faudrait-il pas créer un nouveau fichier texte pour chaque téléchargement ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X