web-dev-qa-db-fra.com

Comment extraire des segments vidéo précis avec le temps avec ffmpeg?

Ce n'est pas un domaine de questions particulièrement nouveau ici, mais j'ai essayé ce qui a été suggéré là-bas sans beaucoup de chance. Donc, mon histoire:

J'ai un morceau de 15 secondes de vidéo directement à partir de la caméra. Dont je veux extraire un morceau spécifique, que je peux identifier par heure de début et heure d'arrêt, en secondes. J'ai commencé par essayer de faire ce que j'appellerai une "extraction de copie": pour obtenir les secondes 9 à 12,

ffmpeg -i test.mov -vcodec copy -acodec copy -ss 9 -to 12 test-copy.mov

Ce n'était pas un mauvais début, mais il y a des cadres noirs au début et à la fin du clip, ce que je ne peux pas avoir - ce doit être un montage net de l'original. J'ai donc essayé de recoder l'original dans un nouveau clip découpé:

ffmpeg -i test.mov -ss 00:00:09 -t 00:00:03 test-out.mov

C'est mieux, mais pas tout à fait: il n'y a plus de cadres noirs au début du clip, mais ils sont toujours là à la fin.

Après un peu plus de navigation et de lecture, je soupçonnais alors que le problème était que ffmpeg avait du mal à trouver les bons points en raison d'un manque d'images clés dans la vidéo originale. J'ai donc recodé la vidéo originale pour (vraisemblablement) ajouter des images clés, de deux manières différentes. Comme je veux pouvoir choisir une vidéo à des limites d'une seconde ("de 9 secondes à 12 secondes"), j'ai essayé, en copiant diverses suggestions sur le Web,

ffmpeg -i test.mov -force_key_frames "expr:gte(t, n_forced)" test-forced.mp4

et

ffmpeg -i test.mov -g 1 test-g-inserted.mp4

(Je les ai construits en tant que mp4 sur la base de certains commentaires sur la nécessité d'un conteneur mp4 pour prendre en charge la recherche d'images clés, mais je suis juste en train de pirater ici.) J'ai ensuite essayé l'extraction comme auparavant, mais sur ces nouvelles vidéos qui ont probablement des images clés. en eux. Pas de chance - les deux semblent être à peu près les mêmes; le début est OK mais il y a encore des cadres noirs à la fin. (FWIW, test-forcé.mp4 et test-g-insert.mp4 ont également des cadres noirs de fin.)

Donc: je suis toujours coincé, et j'aimerais ne pas l'être. Avez-vous une idée de ce que je fais mal? J'ai l'impression d'être proche, mais j'ai vraiment besoin de me débarrasser de ces cadres noirs qui traînent ....

31
Jim Miller

D'accord, tout d'abord en supposant que vous connaissez la durée de début et de fin; nous ajouterons des images clés à cette durée.

ffmpeg -i a.mp4 -force_key_frames 00:00:09,00:00:12 out.mp4

La plupart du temps, vous pouvez directement couper la vidéo à la perfection, mais dans votre cas, cela ne vous aide pas; nous l'avons donc pris en charge par la commande ci-dessus. Ici, soyez prudent de ne pas ajouter trop d'images clés car cela peut être un problème lors de l'encodage selon Ffmpeg Docs .

Vous pouvez maintenant essayer à nouveau de couper la vidéo à une heure précise.

ffmpeg -ss 00:00:09 -i out.mp4 -t 00:00:03 -vcodec copy -acodec copy -y final.mp4

Cela résoudra le problème car nous avons ajouté manuellement les images clés aux points de début et de fin de la coupe. Ça a marché pour moi.

À votre santé.:)

33
BlueSword

Je pense que le problème que posent la question et les autres réponses est qu'elles utilisent -ss en option sur le fichier de sortie, pas sur le fichier d'entrée. La plupart des options ffmpeg ne sont pas globales, mais s'appliquent uniquement au fichier qu'elles précèdent. Il n'est souvent pas évident où une option doit aller, donc parfois des essais et des erreurs sont nécessaires.

Utilisé correctement, avant le fichier d'entrée auquel il doit s'appliquer, -ss et -t a bien fonctionné pour moi. Lors de l'inclusion de l'audio dans la sortie, j'ai dû utiliser -shortest en option pour le fichier de sortie, ou j'obtiendrais 2 minutes d'audio avec 2 secondes de vidéo.

ffmpeg version N-67413-g2a88c74 (essentiellement une source git du 14 décembre 2014)

Voici une ligne de commande que j'ai utilisée pour faire un clip récemment. (En fait, peaufiné pour être un meilleur exemple, car je suis parti en audio pour cela, et je ne l'ai pas ralenti.)

ffmpeg -ss 120.2 -t 0.75 -i ../mcdeint.60p.lossless264.slow.mkv -c:a libopus -shortest -aspect 16:9 -preset veryslow -x264-params nr=250:ref=6 -crf 22 -movflags +faststart clip2.mkv

Avec -c:a copy (la source a un son AC3), la lecture démarre de manière bancale avec mplayer. Il saisit probablement l'audio depuis le début de la trame audio contenant le début, puis doit utiliser un décalage A/V dans le conteneur. Au démarrage, il faut une fraction de seconde à l'audio pour devancer la vidéo par ce décalage, et jusque-là, la vidéo est lue à un FPS très faible. J'ai donc xcodé l'audio. Ni opus ni pcm_s16le ne peuvent aller en mp4, j'ai donc utilisé un conteneur mkv pour cet exemple.

La source est un encodage x264 sans perte (-qp 0) de la sortie d'un yadif très lent = 3: 1, mcdeint = 3: 1: 10 (sur certaines vidéos entrelacées BFF d'un DVD NTSC, probablement d'une caméra DV). Ce ne sont PAS toutes les images I, ce sont des images P avec un intervalle d'images clés normal.

L'ajustement de -ss de 0,2 seconde a fait exactement ce que j'espérais, donc ffmpeg doit gérer le décodage jusqu'au point requis. Ce n'était pas seulement une coïncidence d'un cadre I où j'en voulais un. Peut être -accurate_seek est la valeur par défaut? J'obtiens également le même résultat (sortie gif identique octet par octet) que lorsque j'utilise une source sans perte ffvhuff en entrée. (mais il s'exécute plus rapidement, car il n'a pas à décoder jusqu'au point demandé.)

Une autre option éventuellement pertinente est -seek2any, mais il indique "rechercher une image non clé au niveau du démultiplexeur", ce qui semble vous permettre de rechercher de manière à produire une sortie tronquée. (c'est-à-dire commencer le décodage sans générer réellement les références requises par la trame actuelle, utilisez simplement du gris?)

Je n'ai pas essayé d'utiliser -c:v copy, puisque je coupe un clip très court à boucler, donc je sais qu'il n'y aura pas d'images I là où j'en ai besoin.

Il s'agit de la ligne de commande que j'ai utilisée pour créer un court clip au ralenti sans son.

ffmpeg -ss 120.2 -t 0.75 -i ../vid.yadif3.1,mcdeint3.1.10.ffvhuff.mkv -an -shortest -aspect 16:9 -c:v libx264 -preset veryslow -x264-params nr=250:ref=6 -crf 22 -filter:v "setpts=3.0*PTS" -movflags +faststart -r 20 clip.mp4

Notez que le -r 20 était important, car contrairement à mkv, la sortie MP4 de ffmpeg est à fréquence d'images constante uniquement (modification: parce que -vsync vfr n'est pas la valeur par défaut avec le multiplexeur mp4). Sans le dire différemment, il définirait le FPS de sortie = FPS d'entrée et dupliquerait les trames si nécessaire pour y arriver. x264 et gif animé (avec transparence) peuvent tous deux encoder des images en double très efficacement, mais c'est toujours idiot.

Avant de cuisiner ceci comme exemple, je l'avais fait en 2 étapes, une sortie en mkv, puis ffmpeg -i clip.mkv -c:v copy -movflags +faststart -r 20 clip.mp4 au remux. BTW, il est possible de changer les fps d'une vidéo lors du remuxage, sans xcoding, mais pas avec ffmpeg. https://superuser.com/questions/740196/reducing-video-size-with-avconv-why-does-the-size-increase . Mais de toute façon, ffmpeg n'a envoyé que 45 images à libx264 lors de la réalisation du mkv, même s'il pensait qu'il faisait une vidéo de 2,2 secondes à 60 images par seconde. N'utilisez pas ffmpeg avec mp4 pour travailler avec des choses FPS variables.

modifier: il s'avère que ffmpeg par défaut est -vsync vfr pour la sortie mkv, mais pas pour mp4. Avec -vsync vfr, ffmpeg peut très bien écrire VFR dans la sortie mp4.

Et encore une fois pour la sortie gif, au cas où je déciderais de ne pas le mettre avec la vidéo HTML5 (<video controls autoplay loop> <source src="clip.mp4" type="video/mp4"> </video>)

ffmpeg -ss 120.2 -t 0.75 -i ../vid.yadif3.1,mcdeint3.1.10.ffvhuff.mkv -an -shortest -aspect 16:9 -filter:v "setpts=3.0*PTS,scale=854x480" -r 20 clip.gif

Je devais utiliser scale= car le conteneur gif ne stocke pas de rapport hauteur/largeur, il ne peut donc pas être mis à l'échelle automatiquement lors de la lecture. (ma vidéo 720x480 pixel 16: 9 est mise à l'échelle à 854x480 lors de la lecture. En fait, devrait être 853,333, mais cela est arrondi, puis ffmpeg stocke 853x480 dans le conteneur mkv, donc en utilisant -aspect 16: 9 tout le temps, donc mon mp4 sera mémoriser le format d'image approprié [SAR 32:27 DAR 16:9], au lieu de [SAR 186:157 DAR 279:157])

10
Peter Cordes

Il n'est pas nécessaire d'ajouter des images clés; comme le dit Peter, il s'agit simplement de placer les options dans le bon ordre. Voir cependant https://trac.ffmpeg.org/wiki/Seeking pour le guide officiel définitif sur la façon de bien faire les choses.

6
Adam Spiers

Piquer un ours endormi dans les bois ???? ‍♂️. Je n'ai vu aucune mention du -to flag mentionné dans ce fil, et je l'ai trouvé plus utile que le -t drapeau. Un exemple de commande qui fonctionne bien pour moi,

ffmpeg -y -i [INPUT.file] -ss 00:42:42 -to 00:84:84 -codec copy [OUTPUT.file]
2
ipatch

J'aimerais aussi savoir. Jusqu'à présent, j'ai converti mes vidéos sans perte, extrait un segment et les ré-encodé après le montage:

ffmpeg -i input -ss 9.48 -t 3.52 -c:v libx264 -crf 0 -g 1 -c:a copy TempIframe.mp4

Mais c'est un processus long et coûteux ...

J'ai essayé ce qui suit sans succès:

ffmpeg -i source.mp4 -ss 1 -c:v copy -seek2any 1 -avoid_negative_ts 1 -safe 1 -c:a copy segment.mp4
0
AJ29