Peut ffmpeg afficher une barre de progression?
je suis en train de convertir un .avi fichier .fichier flv à l'aide de ffmpeg. Comme il faut beaucoup de temps pour convertir un fichier je voudrais afficher une barre de progression. Est-ce que quelqu'un peut me guider sur la façon de faire la même chose?
je sais que ffmpeg doit en quelque sorte sortir la progression dans un fichier texte et je dois la lire en utilisant des appels ajax. Mais comment faire pour que ffmpeg affiche la progression dans le fichier texte?
Merci beaucoup.
9 réponses
j'ai joué avec ça pendant quelques jours. Ce truc de "ffmpegprogress" m'a aidé, mais c'était très dur de travailler avec mon appareil, et de lire le code.
pour montrer les progrès de ffmpeg vous devez faire ce qui suit:
- exécuter la commande ffmpeg à partir de php sans attendre une réponse (pour moi, ce fut la partie la plus difficile)
- dis à ffmpeg d'envoyer sa sortie dans un fichier
- de l'avant (AJAX, Flash, n'importe quoi) a frappé soit ce fichier directement ou un fichier php qui peut tirer la progression de la sortie de ffmpeg.
Voici comment j'ai résolu chaque partie:
1. J'ai eu l'idée suivante de "ffmpegprogress". C'est ce qu'il a fait: un fichier PHP en appelle un autre via une socket http. Le 2e exécute le "exec" et le premier fichier raccroche. Pour moi sa mise en œuvre est trop complexe. Il a l'aide de "fsockopen". J'aime 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);
mettre CURLOPT_TIMEOUT à 1 signifie qu'il attendra 1 seconde pour une réponse. De préférence, ce serait plus faible. Il y a aussi le CURLOPT_TIMEOUT_MS qui prend des millisecondes, mais ça n'a pas marché pour moi.
après 1 seconde, CURL raccroche, mais la commande exec fonctionne toujours. Partie 1 résolue.
BTW-quelques personnes suggéraient d'utiliser la commande" nohup " pour cela. Mais cela ne semblait pas fonctionner pour moi.
* ALSO! 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 encoder les données de poste d'une certaine façon.
2. Le " exec.php " script ci-dessus doit aussi dire ffmpeg à la sortie d'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");
Note le " 1 > path/to / output.txt 2>&1". Je ne suis pas expert en ligne de commande, mais de ce que je peut dire que cette ligne dit "envoyer la sortie normale à 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. À partir de la fin de l'appel un script php lui donnant l'emplacement de la sortie.fichier txt. Ce fichier php va ensuite 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 ça aidera quelqu'un.
il y a un article en russe qui décrit comment résoudre votre problème.
le point est de saisir la valeur Duration
avant l'encodage et de saisir les valeurs 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--
FFmpeg utilise stdout pour outputing media data et stderr pour logging / progress information. Vous avez juste à rediriger stderr vers un fichier ou à stdin d'un processus capable de le gérer.
avec un shell unix c'est quelque chose comme:
ffmpeg {ffmpeg arguments} 2> logFile
ou
ffmpeg {ffmpeg arguments} 2| processFFmpegLog
de toute façon, vous devez exécuter ffmpeg comme un fil ou un processus séparé.
c'est très simple si vous utilisez la commande pipeview. Pour ce faire, transformez
ffmpeg -i input.avi {arguments}
à
pv input.avi | ffmpeg -i pipe:0 -v warning {arguments}
Pas besoin d'entrer dans le codage!
vous pouvez le faire avec ffmpeg
's -progress
argument et nc
WATCHER_PORT=9998
DURATION= $(ffprobe -select_streams v:0 -show_entries "stream=duration" \
-of compact $INPUT_FILE | sed 's!.*=\(.*\)!!g')
nc -l $WATCHER_PORT | while read; do
sed -n 's/out_time=\(.*\)/ of $DURATION/p')
done &
ffmpeg -y -i $INPUT_FILE -progress localhost:$WATCHER_PORT $OUTPUT_ARGS
appeler la fonction système de php bloque ce thread, vous aurez donc besoin de lancer 1 requête HTTP pour effectuer la conversion, et un autre polling pour lire le fichier txt, qui est généré.
ou, mieux encore, les clients soumettent la vidéo pour la conversion et alors un autre processus est rendu responsable de l'exécution de la conversion. De cette façon, la connexion du client ne sera pas temporisée en attendant que l'appel système se termine. Le sondage se fait dans le même manière que ci-dessus.
a eu des problèmes avec la deuxième partie de php. Donc j'utilise 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)));
sort parfaitement.
aimerait voir quelque chose pour plusieurs téléchargements pour une autre barre de progression. Ce passage pour le fichier courant 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 d'accès "" 1>chemin d'accès " OU " 2> chemin d'accès "" 2>chemin d'accès "
m'a pris un moment pour le comprendre. FFMPEG n'arrêtait pas d'échouer. A travaillé quand j'ai changé pour de l'espace.
javascript devrait dire à php de commencer à convertir [1] et ensuite de faire [2] ...
[1] php: démarrer la conversion et de l'écriture dans un fichier (voir ci-dessus):
exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");
pour la deuxième partie nous avons besoin de juste javascript pour lire le fichier. L'exemple suivant utilise le dojo.demande pour AJAX, mais vous pourriez utiliser jQuery ou vanille ou n'importe quoi aussi bien :
[2] js: saisir l'avancement du dossier:
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);
malheureusement, ffmpeg
lui – même ne peut toujours pas montrer une barre de progrès - aussi, beaucoup des susnommés bash-ou python-basé des solutions de stop-gap sont devenus désuets et non fonctionnelle.
ainsi, je recommande de donner le tout nouveau ffmpeg-progressbar-cli un essai:
c'est un wrapper pour l'exécutable ffmpeg
, montrant un coloré, barre de progression centrée et le temps restant.
aussi, c'est open-source, basé sur Node.js et activement développé, en traitant la plupart des bizarreries mentionnées (divulgation complète: je suis son développeur principal actuel).