[RESOLU] Création Barre de progression à partir d'une conversion video FFMPEG

Petit nouveau ! | 6 Messages

08 mai 2023, 12:22

Bonjour à tous,
Je souhaite créer une barre de progression qui suis l'évolution de ma conversion vidéo via FFMPEG.

Ma première page est un formulaire pour téléverser la vidéo où s'afficherait la barre de progression, la page sert également à créer une entrée base mysql puis renvoie vers ma page de conversion qui récupère également des infos de durée (via getID3) de la vidéo qui mais à jour la base mysql.
Tous ça fonctionne.

il ne me reste plus qu'à afficher la barre de progression voir pourquoi pas le pourcentage et la durée restante.

Voici le code de la conversion vidéo :

Code : Tout sélectionner

$cmd = "ffmpeg -i $movie -vcodec libx264 -maxrate 8000k -bufsize 1000K -minrate 10k -crf 24 -preset slow -ab 192k $movieC 2>&1"; $output = exec($cmd);
Si besoin par la suite, je pourrais afficher mais 2 pages.

Je fais du htlm/php à mes heures perdu et la partie javaScript/Ajax ne me parle pas beaucoup et là ça fait 2 semaines où je me casse les dents.

Merci pour votre aide charitable :D

Mammouth du PHP | 2493 Messages

08 mai 2023, 14:06

https://www.w3schools.com/howto/howto_j ... essbar.asp
et https://api.jquery.com/jquery.get/ pour récupérer les données du php qui récupère les données de progression.

Petit nouveau ! | 6 Messages

08 mai 2023, 17:41

Merci Mammouth pour tes informations

Voila où j'en suis, j'avoue je me suis fait un peu aider par chatGPT :-*
Partie Formulaire :

Code : Tout sélectionner

<style> /* Barre de Progression */ #myProgress { width: 100%; background-color: grey; } #myBar { width: 1%; height: 30px; background-color: green; } </style>

Code : Tout sélectionner

<div id="myProgress"> <div id="myBar"></div> </div> <script> $(document).ready(function() { var interval = setInterval(function() { $.ajax({ url: "analyse_media.php", // Chemin vers le script PHP qui récupère les informations de progression type: "GET", dataType: "json", success: function(data) { var progress = parseFloat(data.progress); $("#myBar").css("width", progress + "%").attr("aria-valuenow", progress); if (progress >= 100) { clearInterval(interval); } } }); }, 1000); // Effectuer une requête AJAX toutes les secondes }); </script>
Partie Conversion :

Code : Tout sélectionner

$cmd = "ffmpeg -i $movie -vcodec libx264 -maxrate 8000k -bufsize 1000K -minrate 10k -crf 24 -preset slow -ab 192k $movieC 2>&1"; $output = exec($cmd); // Le code suivant extrait les informations de progression de la sortie de la commande FFmpeg preg_match_all('/Duration: ([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]{2})/i', $output, $duration); preg_match_all('/time=([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]{2})/i', $output, $time); if (isset($duration[1][0]) && isset($duration[2][0]) && isset($duration[3][0]) && isset($duration[4][0])) { $total_seconds = ($duration[1][0] * 3600) + ($duration[2][0] * 60) + $duration[3][0] + ($duration[4][0] / 100); if (isset($time[1][0]) && isset($time[2][0]) && isset($time[3][0]) && isset($time[4][0])) { $current_seconds = ($time[1][0] * 3600) + ($time[2][0] * 60) + $time[3][0] + ($time[4][0] / 100); $progress = round(($current_seconds / $total_seconds) * 100, 2); echo json_encode(['progress' => $progress]); // Renvoie les informations de progression sous forme de JSON } }
J'ai bien la barre de progression qui s'affiche mais la partie qui devrais bougeais et au milieu et reste fixe..

Mammouth du PHP | 2493 Messages

08 mai 2023, 21:27

il faut debuger, afficher la console pour voir si les appels ajax sont bien faits à intervalle régulier, si le retour du php est bien celui attendu ...

Avatar du membre
Mammouth du PHP | 1541 Messages

09 mai 2023, 13:23

Salut, à priori le problème est que php va bloquer sur la ligne $output = exec($cmd); tant que la conversion ne sera pas terminée et le retour du json ne se fera donc qu'une fois à la fin de la conversion.

Sinon on voit le code de conversion mais quel est le code de analyse_media.php ? c'est lui qui serait sensé retourner la progression.

PS : je ne crois pas qu'il y ait en php de véritable moyen d'obtenir la sortie d'une commande et de pousser les informations vers le client au fur et à mesure de l'exécution. Il faut plutôt pousser la sortie de la commande dans un fichier et avoir un script php appelé en ajax régulièrement qui va lire le contenu de ce fichier pour savoir où ça en est.
Développeur web depuis + de 20 ans

Petit nouveau ! | 6 Messages

09 mai 2023, 21:41

Bonsoir,
Merci pour vos retour,
il faut debuger, afficher la console pour voir si les appels ajax sont bien faits à intervalle régulier, si le retour du php est bien celui attendu ...
Je n'ai rien vu de particulier ou en tout cas qui a attiré mon attention, pour savoir si ça fonctionné ou pas, si tu peux m'indiquer de quelle manière je pourrais voir les remontés ajax. Pour moi à première vu il n'y a pas de remonté.
Y a t il une solution afficher le retour de type echo...

Salut, à priori le problème est que php va bloquer sur la ligne $output = exec($cmd); tant que la conversion ne sera pas terminée et le retour du json ne se fera donc qu'une fois à la fin de la conversion.

Sinon on voit le code de conversion mais quel est le code de analyse_media.php ? c'est lui qui serait sensé retourner la progression.

PS : je ne crois pas qu'il y ait en php de véritable moyen d'obtenir la sortie d'une commande et de pousser les informations vers le client au fur et à mesure de l'exécution. Il faut plutôt pousser la sortie de la commande dans un fichier et avoir un script php appelé en ajax régulièrement qui va lire le contenu de ce fichier pour savoir où ça en est.
Le code de conversion est bien sur la page analyse_media.php. Si ça peut simplifier le fonctionnement, je peux unifier ces 2 pages en 1... le formulaire + la conversion (analyse_media.php).

Chat GPT ma donné ce code pour lire le fichier mais pas pour l'écrire... Je vais essayé de faire des recherches la dessus, merci.

Code : Tout sélectionner

<?php $log_file = 'ffmpeg.log'; function read_log($log_file) { $lines = file($log_file); $last_line = end($lines); $matches = array(); preg_match('/time=([0-9:.]+)/', $last_line, $matches); if (!empty($matches)) { $time_parts = explode(':', $matches[1]); $total_seconds = $time_parts[0] * 3600 + $time_parts[1] * 60 + $time_parts[2]; return array('progress' => round($total_seconds / $total_duration * 100)); } else { return array('progress' => 0); } } $total_duration = ...; // Durée totale de la vidéo à convertir echo json_encode(read_log($log_file)); ?>
est ce un début de réponse ??


Voici un 2éme code qu'il m'a donné

Code : Tout sélectionner

$handle = popen("ffmpeg -i ".$movie." -vcodec libx264 -maxrate 8000k -bufsize 1000K -minrate 10k -crf 24 -preset slow -ab 192k ".$movieC." 2>&1", "r"); // Traitez la sortie standard de la commande ffmpeg while (!feof($handle)) { $buffer = fgets($handle); if (preg_match('/frame=\s*(\d+)\s+fps=\s*([\d\.]+)\s+q=\s*([\d\.]+)\s+size=\s*(\d+)\s+time=\s*([\d:\.]+)\s+bitrate=\s*([\d\.]+[kM]?bits\/s)\s+speed=\s*([\d\.]+x)?/', $buffer, $matches)) { $frame = $matches[1]; $fps = $matches[2]; $q = $matches[3]; $size = $matches[4]; $time = $matches[5]; $bitrate = $matches[6]; $speed = $matches[7]; // Calculez le pourcentage d'avancement et mettez à jour la barre de progression $percent = round($frame / 1000 * 100, 2); echo "<script>updateProgressBar(".$percent.");</script>"; } } // Fermez le handle de la commande ffmpeg pclose($handle);
Je n'ai pas compris le fonctionnent de cette commande "popen"


Voici un 3éme code qu'il m'a donné

Code : Tout sélectionner

// Créer une nouvelle requête AJAX var xhr = new XMLHttpRequest(); // Configurer la requête avec la commande ffmpeg xhr.open('GET', 'analyse_media.php?movie=' + encodeURIComponent(movie) + '&movieC=' + encodeURIComponent(movieC)); // Envoyer la requête AJAX xhr.send(); // Mettre à jour la barre de progression en fonction de la sortie de la commande ffmpeg xhr.addEventListener('progress', function(event) { if (event.lengthComputable) { var percentComplete = event.loaded / event.total * 100; document.getElementById('progress').style.width = percentComplete + '%'; } });
Là, il m'a perdu


Voici un 4éme code qu'il m'a donné

Code : Tout sélectionner

<script> function startConversion() { var fileInput = document.getElementById("fileInput"); var progressBar = document.getElementById("progressBar"); if (fileInput.files.length == 0) { alert("Veuillez sélectionner un fichier vidéo."); return; } var fileName = fileInput.files[0].name; var filePath = fileInput.files[0].webkitRelativePath || fileInput.files[0].name; var fileExt = fileName.split('.').pop(); var destFileName = 'converted_video.' + fileExt; var cmd = 'ffmpeg -i ' + filePath + ' -vcodec libx264 -maxrate 8000k -bufsize 1000K -minrate 10k -crf 24 -preset slow -ab 192k ' + destFileName + ' 2>&1'; var duration = 0; var progress = 0; // Exécuter la commande ffmpeg var output = exec(cmd); // Récupérer les informations de progression var regexDuration = /Duration: (\d{2}):(\d{2}):(\d{2})/; var regexTime = /time=(\d{2}):(\d{2}):(\d{2})/; var matchesDuration = output.match(regexDuration); var matchesTime = output.match(new RegExp(regexTime, 'g')); if (matchesDuration != null) { duration = parseInt(matchesDuration[1]) * 3600 + parseInt(matchesDuration[2]) * 60 + parseInt(matchesDuration[3]); } if (matchesTime != null) { var lastTime = matchesTime[matchesTime.length - 1].match(regexTime); progress = Math.floor(parseInt(lastTime[1]) * 3600 + parseInt(lastTime[2]) * 60 + parseInt(lastTime[3]) / duration * 100); } // Mettre à jour la barre de progression progressBar.value = progress; // Continuer à exécuter la commande jusqu'à ce qu'elle soit terminée if (progress < 100) { setTimeout(startConversion, 1000); } } </script>
Je vous met à disposition ces quelques lignes... de codes, si ça parle mieux à vous qu'à moi, voir donné des idées..

Merci pour votre aide

Avatar du membre
Mammouth du PHP | 1541 Messages

10 mai 2023, 12:10

Salut en regardant vite fait (ça serait quand même plus agréable avec les tabulations...), coupler le premier et le troisième script pourrait fonctionner à condition de les adapter pour que les paramètres correspondent et cela implique aussi la màj du script de conversion pour écrire le fichier de log.

Le deuxième script est intéressant, à priori le popen permettrait justement de faire ce que je ne pensais pas possible, à savoir lire la sortie de la commande au fur et à mesure de son exécution et envoyer des données au navigateur au fur et à mesure avec le echo updateProgressBar. En revanche il me semble que ça fonctionne pas toujours très bien (le echo n'est pas toujours transmis directement au navigteur), le ob_flush peut peut être aidé à pousser la sortie vers le navigateur (à mettre du coup après le echo).

Le quatrième script je pense que tu peux le laisser de côté car tu n’exécuteras pas la commande depuis le navigateur via javascript. Ça serait plus cohérent avec un serveur nodejs et encore dans ce cas pas sur que la balise script soit cohérente.

Y a de la matière pour faire des tests en tout cas. Perso je tenterai d'abord avec le popen voir si le navigateur reçoit bien régulièrement la ligne <script>updateProgressBar(".$percent.");</script> avec le pourcentage adéquat (j'ai pas vu la déclaration du updateProgressBar, si elle n'y est pas, il faut l'ajouter, une petite fonction js qui prend le pourcentage et update la barre de progression, c'est basique). Si ça passe bien bingo. Sinon je tenterais avec le fichier de log.

PS : en fait avec le popen je pense que tu vas avoir un problème pour la réception des données. J'ai quand même trouvé ça en cherchant vite fait. L'évènement on progress pourrait peut être permettre de pousser les updateProgressBar au fur et à mesure des réceptions.
https://stackoverflow.com/questions/774 ... rementally
Développeur web depuis + de 20 ans

Petit nouveau ! | 6 Messages

17 mai 2023, 20:29

Bonjour,
Après avoir fait quelques tests

Le formulaire et la conversion se fait sur une seule page est se nomme ajouterVideo.php et j’envoie dorénavant la sorti de la commande dans le fichier ./progress.txt, mais je n'ai pas l'impression qu'il reçoit la sorti, je le vois toujours vide durant la conversion...

Conversion et récupération des données

Code : Tout sélectionner

$progressFile = "./progress.txt"; $handle = popen("ffmpeg -i ".$movie." -vcodec libx264 -maxrate 8000k -bufsize 1000K -minrate 10k -crf 24 -preset slow -ab 192k ".$movieC." 2>&1 > " . $progressFile, "r"); //au cas ou ça ne converti plus la commande suivante fera apparaître les erreurs /* // Le echo s'affiche à la fin de la conversion while (!feof($handle)) { $output = fgets($handle); //Affiche la sortie echo $output; } */ // Lecture du contenu du fichier de progression $progressData = file_get_contents($progressFile); $progressArray = explode("\n", $progressData); $totalDuration = 1; // si je mais Zero ça me mais "Fatal error: Uncaught DivisionByZeroError: Division by zero" foreach ($progressArray as $lineD) { if (preg_match('/Duration: (.*?),/', $lineD, $matchesD)) { $duration = $matchesD[1]; $timeArrayD = explode(':', $duration); $totalDuration = intval($timeArrayD[0]) * 3600 + intval($timeArrayD[1]) * 60 + intval($timeArrayD[2]); break; } } // Extraire la valeur de progression $progress = 0; foreach ($progressArray as $line) { if (preg_match("/time=(\d+:\d+:\d+)/", $line, $matches)) { $time = $matches[1]; $timeArray = explode(":", $time); $progress = intval($timeArray[0]) * 3600 + intval($timeArray[1]) * 60 + intval($timeArray[2]); } } // Calculer le pourcentage de progression $percentage = round(($progress / $totalDuration) * 100, 2); // Préparer la réponse JSON $response = array( 'progress' => $percentage, 'status' => 'in_progress' ); pclose($handle); // Envoyer la réponse en JSON header('Content-Type: application/json'); echo json_encode($response);
Le CSS

Code : Tout sélectionner

<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html"; charset= "utf-8"> <script src="jquery-3.6.0.min"></script> <style> .progress { width: 100%; height: 20px; background-color: #f2f2f2; border-radius: 10px; } .progress-bar { width: 0; height: 100%; background-color: #4caf50; border-radius: 10px; transition: width 0.5s ease-in-out; text-align: center; color: #ffffff; font-weight: bold; } </style> </head>
La Barre de progression

Code : Tout sélectionner

<div class="progress"> <div class="progress-bar"></div> </div> <script> $(document).ready(function() { function updateProgress() { $.ajax({ url: 'ajouterVideo.php', type: 'GET', dataType: 'json', success: function(response) { var progress = response.progress; var status = response.status; // Mettre à jour la barre de progression $('.progress-bar').css('width', progress + '%'); $('.progress-bar').text(progress + '%'); if (status === 'in_progress') { // L'opération est en cours, planifier la prochaine mise à jour setTimeout(updateProgress, 1000); } else if (status === 'complete') { // L'opération est terminée, effectuer les actions finales ici console.log('Opération terminée'); } } }); } // Lancer la mise à jour de la barre de progression updateProgress(); }); </script>
Avec ce code la barre de progression reste désespérément vide, en affichant la console sous Firefox durant la conversion rien ne bouge que ça soit console, débogueur ou encore réseau.

Si vous avez une idée...

Petit nouveau ! | 6 Messages

17 mai 2023, 20:33

J'ai mi des tabulations, mais elles ne sont pas resté...

Mammouth du PHP | 2493 Messages

17 mai 2023, 22:42

pour que cela fonctionne, il faut que le fichier appelé en ajax réponde de suite. si ajouterVideo.php ne répond qu'une fois le traitement fini, cela ne peut pas marcher. si progress.txt n'est pas mis à jour pendant le traitement, alors, cela ne peut pas marcher, même en appelant en ajax un autre fichier php qui va lire progress.txt

Petit nouveau ! | 6 Messages

19 mai 2023, 19:59

Bonjour,
Je viens de résoudre le problème en m'appuyant sur cette page https://write.corbpie.com/ffmpeg-encode ... -with-php/.
Merci pour votre aide et de m'avoir mi sur la bonne piste.