[FFmpeg] Démarrez rapidement avec FFmpeg en un seul article

FFmpeg

1. Configuration de l'environnement

git clone https://github.com/tanersener/ffmpeg-video-slideshow-scripts.git

Pour plus de commodité, l'environnement conda (py10) est utilisé directement ici, et le code suivant est utilisé pour télécharger ffmpeg :

conda install -c conda-forge ffmpeg

À ce stade, la configuration de l’environnement est terminée. Vous pouvez vérifier la version de ffmpeg et d'autres informations.

2. Partie du code de base

2.1 Paramètres des hyperparamètres

WIDTH=576
HEIGHT=1024
FPS=30
TRANSITION_DURATION=1
IMAGE_DURATION=2

2.2 Chargement des données

# FILES=`find ../media/*.jpg | sort -r`             # USE ALL IMAGES UNDER THE media FOLDER SORTED
# FILES=('../media/1.jpg' '../media/2.jpg')         # USE ONLY THESE IMAGE FILES
FILES=`find ../media/*.jpg`                         # USE ALL IMAGES UNDER THE media FOLDER

2.3 Quelques paramètres

TRANSITION_FRAME_COUNT=$(( TRANSITION_DURATION*FPS ))
IMAGE_FRAME_COUNT=$(( IMAGE_DURATION*FPS ))
TOTAL_DURATION=$(( (IMAGE_DURATION+TRANSITION_DURATION)*IMAGE_COUNT - TRANSITION_DURATION ))
TOTAL_FRAME_COUNT=$(( TOTAL_DURATION*FPS ))

2.4 Opération spécifique de traitement du flux d'entrée

for IMAGE in ${
    
    FILES[@]}; do
    FULL_SCRIPT+="-loop 1 -i '${IMAGE}' "
done
......
Opérations courantes
[${c}:v]  # 对某个进行输入流进行操作

setpts=PTS-STARTPTS  # 时间戳从0开始

scale=w='if(gte(iw/ih,${WIDTH}/${HEIGHT}),min(iw,${WIDTH}),-1)':h='if(gte(iw/ih,${WIDTH}/${HEIGHT}),-1,min(ih,${HEIGHT}))'  # 进行缩放,宽高比大于或等于目标宽高比 ${WIDTH}/${HEIGHT},则将宽度调整为 ${WIDTH},高度根据宽高比进行自适应;否则,将高度调整为 ${HEIGHT},宽度根据宽高比进行自适应。

scale=trunc(iw/2)*2:trunc(ih/2)*2  # 将图像的宽度和高度调整为偶数值

pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2:color=#00000000  # 填充达到指定的宽高(${WIDTH}和${HEIGHT}),填充颜色为透明黑色(#00000000)

setsar=sar=1/1  # 设置样纵横比为1:1,确保输出图像的纵横比与原始图像一致

crop=${WIDTH}:${HEIGHT}  # 一个视频处理操作,用于裁剪视频帧的尺寸。

concat=n=3:v=1:a=0  # 连接三个视频片段,只包含视频流而不包含音频流

scale=${WIDTH}*5:-1  # 将连接后的视频流缩放为 ${WIDTH}*5 的宽度,高度根据比例自动调整

zoompan=z='min(pzoom+0.001*${ZOOM_SPEED},2)':d=1:${POSITION_FORMULA}:fps=${FPS}:s=${WIDTH}x${HEIGHT}  # min(pzoom+0.001*${ZOOM_SPEED},2) 控制了缩放的速度;${ZOOM_SPEED} 是之前定义的缩放速度变量

[stream$((c+1))]  # 得到经过缩放/填充的图像流

2.5 Épissage du flux d'entrée

for (( c=1; c<${
    
    IMAGE_COUNT}; c++ ))
do
    FULL_SCRIPT+="[stream${c}overlaid][stream$((c+1))blended]"
done

3. Manipulation d'images

Imprimez directement le flux d'images dans des fichiers jpg/png via la carte.

Le format du modèle est le suivant :

ffmpeg -i 'the path of images' -filter_complex "the operations of ffmpeg" -map "streamout" output1.jpg

Par exemple:

ffmpeg -i '../images/aaa.jpg' -filter_complex "scale=w='if(gte(iw/ih,576/1024),-1,576)':h='if(gte(iw/ih,576/1024),1024,-1)',crop=576:1024,setsar=sar=1/1,fps=30,format=rgba,split=2[stream1out1][stream1out2]" -map "[stream1out1]" output1.jpg -map "[stream1out2]" output2.jpg

4. Opération vidéo (prenez Blured_background.sh comme exemple)

# 5张图片为例,总时长 5*2+5-1=14s
WIDTH=1280
HEIGHT=720
FPS=30
TRANSITION_DURATION=1
IMAGE_DURATION=2
# FILE OPTIONS
# FILES=`find ../media/*.jpg | sort -r`             # USE ALL IMAGES UNDER THE media FOLDER SORTED
# FILES=('../media/1.jpg' '../media/2.jpg')         # USE ONLY THESE IMAGE FILES
FILES=`find ../media/*.jpg`                         # USE ALL IMAGES UNDER THE media FOLDER

let IMAGE_COUNT=0
for IMAGE in ${
    
    FILES[@]}; do (( IMAGE_COUNT+=1 )); done

if [[ ${
    
    IMAGE_COUNT} -lt 2 ]]; then
    echo "Error: media folder should contain at least two images"
    exit 1;
fi
# 输出 图像数量/过渡时间/总时间
TRANSITION_FRAME_COUNT=$(( TRANSITION_DURATION*FPS ))
IMAGE_FRAME_COUNT=$(( IMAGE_DURATION*FPS ))
TOTAL_DURATION=$(( (IMAGE_DURATION+TRANSITION_DURATION)*IMAGE_COUNT - TRANSITION_DURATION ))
TOTAL_FRAME_COUNT=$(( TOTAL_DURATION*FPS ))

echo -e "\nVideo Slideshow Info\n------------------------\nImage count: ${
    
    IMAGE_COUNT}\nDimension: ${
    
    WIDTH}x${
    
    HEIGHT}\nFPS: ${
    
    FPS}\nImage duration: ${
    
    IMAGE_DURATION} s\n\
Transition duration: ${
    
    TRANSITION_DURATION} s\nTotal duration: ${
    
    TOTAL_DURATION} s\n"

# SECONDS 是一个内置的 Bash 变量
START_TIME=$SECONDS
所有的命令都是追加到 FULL_SCRIPT 中。
# 1. START COMMAND
FULL_SCRIPT="conda run -n py10 ffmpeg -y "  # 需要修改为自己的环境
# 2. ADD INPUTS
# -loop 1 表示将图像文件循环播放
# -i '${IMAGE}' 参数指定输入的图像文件路径
for IMAGE in ${
    
    FILES[@]}; do
    FULL_SCRIPT+="-loop 1 -i '${IMAGE}' "
done

Paramètre -filter_complex pour traiter plusieurs flux d'entrée et générer un ou plusieurs flux de sortie.
Dans ce script, le contenu après le paramètre -filter_complex sera utilisé pour définir une série d'opérations de filtrage, notamment la mise à l'échelle de l'image, le recadrage, la définition de la fréquence d'images, etc.

# 3. START FILTER COMPLEX
FULL_SCRIPT+="-filter_complex \""

À l'intérieur de la boucle, ${c}:v représente l'index du flux vidéo de l'image actuelle. Ce flux d'entrée (chaque image est un flux d'entrée) est ensuite traité en appliquant une série d'opérations de filtrage, notamment la mise à l'échelle de l'image, le réglage du rapport hauteur/largeur des pixels, la conversion au format RGBA, l'application d'un effet de flou et le réglage de la fréquence d'images.
Les opérations de filtrage spécifiques sont les suivantes :

  • échelle= LARGEUR x {LARGEUR}xW I D T H x {HEIGHT} : redimensionnez l'image à la largeur et à la hauteur spécifiées.
  • setsar=sar=1/1 : définissez le rapport hauteur/largeur des pixels sur 1:1 pour éviter la distorsion de l'image.
  • format=rgba : convertit le format d'image au format RGBA pour prendre en charge les canaux transparents.
  • boxblur=100 : Appliquez un effet de flou en utilisant un rayon de flou de 100.
  • setsar=sar=1/1 : Réglez à nouveau le rapport hauteur/largeur des pixels sur 1:1.
  • fps= FPS : définissez la fréquence d'images sur la valeur FPS spécifiée. Une fois le traitement terminé, nommez le flux de flux d'entrée {FPS} : définissez la fréquence d'images sur la valeur FPS spécifiée. Une fois le traitement terminé, nommez le flux de flux d'entréeFPS : définissez la fréquence d'images sur la valeur FPS spécifiée . Une fois le traitement terminé, le flux d'entrée est nommé s t re am ((c+1))identifiant unique flou, où (c+1) représente l'index de l'image actuelle plus 1 afin que le nom ne soit pas répété.
# 4. PREPARE BLURRED INPUTS
for (( c=0; c<${
    
    IMAGE_COUNT}; c++ ))
do
    FULL_SCRIPT+="[${c}:v]scale=${WIDTH}x${HEIGHT},setsar=sar=1/1,format=rgba,boxblur=100,setsar=sar=1/1,fps=${FPS}[stream$((c+1))blurred];"
done

La boucle parcourait chaque flux d'entrée d'image et effectuait les opérations suivantes pour chaque flux d'entrée :

  1. Opération de mise à l'échelle : utilisez le filtre d'échelle pour ajuster la largeur et la hauteur de l'image à la taille cible spécifiée WIDTH x {WIDTH} xLARGEUR X HAUTEUR } . _ _ Les expressions conditionnelles sont utilisées ici pour déterminer la méthode de mise à l'échelle afin de garantir que l'image correspond à la taille cible sans déformation. Si le rapport hauteur/largeur de l'image est supérieur ou égal au rapport hauteur/largeur cibleWIDTH/{WIDTH}/W I D T H / {HEIGHT}, puis la largeur est ajustée à ${WIDTH}, et la hauteur est adaptée en fonction du rapport hauteur/largeur ; sinon, la hauteur est ajustée à ${HEIGHT}, et la largeur est adaptée en fonction au rapport hauteur/largeur.
  2. Opération de mise à l'échelle à nouveau : pour garantir que la taille de l'image mise à l'échelle est un nombre pair, utilisez le filtre d'échelle pour arrondir la largeur et la hauteur au nombre pair le plus proche.
  3. Définir le rapport hauteur/largeur des pixels : utilisez le filtre setsar pour définir le rapport hauteur/largeur des pixels de l'image sur 1:1 afin d'éviter un étirement ou une compression anormale lors des étapes de traitement ultérieures.
  4. Conversion de format : utilisez le filtre de format pour convertir le format de pixel de l'image au format RGBA afin que la transparence de l'image puisse être correctement traitée lors du traitement et de la synthèse ultérieurs.
    Le flux d'entrée d'image converti est nommé [stream$((c+1))raw], où $((c+1)) est le numéro de séquence obtenu en incrémentant la variable d'index de boucle c. Ces flux d’entrée préparés seront utilisés dans les étapes de traitement ultérieures.
# 5. PREPARE INPUTS
for (( c=0; c<${
    
    IMAGE_COUNT}; c++ ))
do
    FULL_SCRIPT+="[${c}:v]scale=w='if(gte(iw/ih,${WIDTH}/${HEIGHT}),min(iw,${WIDTH}),-1)':h='if(gte(iw/ih,${WIDTH}/${HEIGHT}),-1,min(ih,${HEIGHT}))',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,format=rgba[stream$((c+1))raw];"
done

Dans cette partie, l'opération de superposition est effectuée sur l'image floue prétraitée et l'image mise à l'échelle.
J'ai parcouru les images floues et mises à l'échelle de chaque image et les ai empilées à l'aide du filtre de superposition. Les paramètres de l'opération de superposition incluent :

  • overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2 : Superposez l'image floue sur l'image mise à l'échelle afin qu'elles soient centrées dans l'écran.
  • format=rgb : Définissez le format de pixel de l'image superposée au format RVB.
    L'image superposée est nommée [stream cout 1], qui représente la sortie de la c-ième image superposée. De plus, le filtre divisé est utilisé pour diviser le flux de sortie de chaque image en deux flux [stream {c}out1], qui représente la sortie de la c-ième image après superposition. De plus, un filtre divisé est utilisé pour diviser le flux de sortie de chaque image en deux flux [streamc o u t 1 ] , représente la sortie après superposition de la c -ième image. De plus, un filtre divisé est utilisé pour diviser le flux de sortie de chaque image en deux flux [ s t re am { c}out1] et [stream cout 2] pour une utilisation dans les étapes de traitement ultérieures. Grâce à cette boucle, deux flux de sortie sont produits pour chaque image, [stream{c}out2], pour être utilisés dans les étapes de traitement ultérieures. Grâce à cette boucle, chaque image produit deux flux de sortie, où [streamc o u t 2 ] pour une utilisation dans les étapes de traitement ultérieures. Grâce à cette boucle, deux flux de sortie sont générés pour chaque image, où [ s t re am {c}out1] est l'image superposée et [stream${c}out2] est l'image mise à l'échelle d'origine.
# 6. OVERLAY BLURRED AND SCALED INPUTS
for (( c=1; c<=${
    
    IMAGE_COUNT}; c++ ))
do
    FULL_SCRIPT+="[stream${c}blurred][stream${c}raw]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2:format=rgb,setpts=PTS-STARTPTS,split=2[stream${c}out1][stream${c}out2];"
done

Dans cette partie, remplissez l'image superposée.
Parcourez le flux de sortie empilé pour chaque image et complétez l'image à l'aide d'un filtre tampon. Les paramètres de l'opération de remplissage incluent :

  • largeur= LARGEUR : hauteur = {LARGEUR}:hauteur=LARGEUR _ _ _ _:hauteur _ _ _ _ _= {HEIGHT} : définissez la largeur et la hauteur de l'image remplie sur la valeur spécifiée.
  • x=( LARGEUR − iw ) / 2 : y = ( {LARGEUR}-iw)/2:y=(LARGEUR _ _ _ _je w ) /2:oui=( {HEIGHT}-ih)/2 : Calcule la position du rembourrage pour que l'image soit centrée sur l'écran.
  • trim=duration=${IMAGE_DURATION} : définissez la durée de l'image sur IMAGE_DURATION, qui correspond à la durée d'affichage de l'image.
  • select=lte(n,${IMAGE_FRAME_COUNT}) : sélectionnez la plage de numéros d'image de l'image pour garantir que l'image n'est affichée que dans la plage de numéros d'image spécifiée.
    En fonction de l'emplacement et du nombre d'images, différents jugements et traitements sont également effectués :
  • S'il s'agit de la première image et que le nombre d'images est supérieur à 1, un flux de sortie supplémentaire [stream${c}ending] est généré pour représenter la fin de l'effet de transition.
  • S'il s'agit de la dernière image, fusionnez le début de l'effet de transition dans le flux de sortie de l'image [stream${c}starting].
  • S'il s'agit d'une image intermédiaire, divisez les parties de début et de fin de l'effet de transition en deux flux de sortie [stream cstarting] et [stream {c}starting] et [streamc s t a r t in g ] et [ s t re am {c}ending].
    Grâce à cette boucle, le flux de sortie de superposition de chaque image est rempli et le flux de sortie correspondant est généré pour représenter l'effet de superposition et l'effet de transition de l'image.
# 7. APPLY PADDING
for (( c=1; c<=${
    
    IMAGE_COUNT}; c++ ))
do
    FULL_SCRIPT+="[stream${c}out1]pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2,trim=duration=${IMAGE_DURATION},select=lte(n\,${IMAGE_FRAME_COUNT})[stream${c}overlaid];"
    if [[ ${
    
    c} -eq 1 ]]; then
        if  [[ ${
    
    IMAGE_COUNT} -gt 1 ]]; then
            FULL_SCRIPT+="[stream${c}out2]pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2,trim=duration=${TRANSITION_DURATION},select=lte(n\,${TRANSITION_FRAME_COUNT})[stream${c}ending];"
        fi
    elif [[ ${
    
    c} -lt ${
    
    IMAGE_COUNT} ]]; then
        FULL_SCRIPT+="[stream${c}out2]pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2,trim=duration=${TRANSITION_DURATION},select=lte(n\,${TRANSITION_FRAME_COUNT}),split=2[stream${c}starting][stream${c}ending];"
    elif [[ ${
    
    c} -eq ${
    
    IMAGE_COUNT} ]]; then
        FULL_SCRIPT+="[stream${c}out2]pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2,trim=duration=${TRANSITION_DURATION},select=lte(n\,${TRANSITION_FRAME_COUNT})[stream${c}starting];"
    fi
done

Dans cette partie, créez des images de transition.
Parcourez chaque paire d'images adjacentes, créant des images avec un effet de transition. Pour chaque paire d'images adjacentes, y compris l'image actuelle et l'image suivante :

  • Utilisez le filtre de fusion pour mélanger les deux images afin de générer une image de transition. Les paramètres de l'opération de mélange incluent :
    • all_expr='A*(if(gte(T, TRANSITIONDURATION ) , {TRANSITION_DURATION}),TR A NS I T I O NU R A T I ON ) , {TRANSITION_DURATION},T/TRANSITIONDURATION ) ) + B ∗ ( 1 − ( if ( gte ( T , {TRANSITION_DURATION}))+B*(1-(if(gte(T,TR A NS I T I O NU R A T I ON ))+B*( 1( je f ( g t e ( T , {TRANSITION_DURATION}),TRANSITIONDURATION , T / {TRANSITION_DURATION},T/TR A NS I T I O NU R A T I ON ,T / {TRANSITION_DURATION})))' : Mélangez deux images d'entrée via une expression, où A représente l'image actuelle et B représente l'image suivante. En fonction du changement de temps T, le poids des deux images est contrôlé pour obtenir l'effet de transition.
    • select=lte(n,${TRANSITION_FRAME_COUNT}) : sélectionnez la plage de numéros d'image de l'image de transition pour garantir que l'image de transition n'est générée que dans la plage de numéros d'image spécifiée.
      Grâce à cette boucle, un flux de sortie d'images de transition est créé pour chaque paire d'images adjacentes, utilisé pour représenter l'effet de transition entre les deux images.
# 8. CREATE TRANSITION FRAMES
for (( c=1; c<${
    
    IMAGE_COUNT}; c++ ))
do
    FULL_SCRIPT+="[stream$((c+1))starting][stream${c}ending]blend=all_expr='A*(if(gte(T,${TRANSITION_DURATION}),${TRANSITION_DURATION},T/${TRANSITION_DURATION}))+B*(1-(if(gte(T,${TRANSITION_DURATION}),${TRANSITION_DURATION},T/${TRANSITION_DURATION})))',select=lte(n\,${TRANSITION_FRAME_COUNT})[stream$((c+1))blended];"
done

Dans cette partie, le collage des clips vidéo commence.
Parcourez chaque paire d'images adjacentes et combinez le flux de sortie de superposition de l'image précédente (flux recouvert) et le flux de sortie d'image de transition de l'image actuelle (flux {c}superposé) avec le flux de sortie d'image de transition de l'image actuelle ( fluxcover lai d ) est connecté au flux de sortie de l'image de transition de l'image actuelle ( stream ( (c + 1)) blended ) pour l'épissage des clips vidéo .
Grâce à cette boucle, pour chaque paire d'images adjacentes, un flux d'entrée est créé qui les connecte et est utilisé pour relier les clips vidéo entre elles.

# 9. BEGIN CONCAT
for (( c=1; c<${
    
    IMAGE_COUNT}; c++ ))
do
    FULL_SCRIPT+="[stream${c}overlaid][stream$((c+1))blended]"
done

Dans cette partie, le collage des clips vidéo est terminé.
Connectez le flux de sortie superposé de la dernière image (stream${IMAGE_COUNT}superposé) aux flux de sortie de toutes les images de transition connectées précédentes pour fusionner tous les clips vidéo.
Grâce à cette étape, le flux vidéo final (vidéo) est généré, qui contient les effets de superposition et de transition de toutes les images.

# 10. END CONCAT
FULL_SCRIPT+="[stream${IMAGE_COUNT}overlaid]concat=n=$((2*IMAGE_COUNT-1)):v=1:a=0,format=yuv420p[video]\""

Dans cette dernière partie, le flux vidéo (vidéo) est mappé sur le fichier de sortie et le format et les paramètres d'encodage du fichier de sortie sont spécifiés.
Définissez le chemin d'accès au fichier de sortie sur .../advanced_blurred_background.mp4 et utilisez l'encodeur libx264 pour l'encodage vidéo.
Les autres paramètres sont les suivants :

  • -vsync 2 : Définissez le mode de synchronisation audio et vidéo sur "passthrough".
  • -async 1 : définissez le mode de synchronisation audio sur « audio d'abord » pour garantir que l'audio et la vidéo sont synchronisés.
  • -rc-lookahead 0 : désactive la prédiction dans le contrôle du débit.
  • -g 0 : désactive la limite de taille du GOP (Groupe d'images).
  • -profile:v main -level 42 : Définit le profil et le niveau d'encodage vidéo.
  • -c:v libx264 : spécifiez l'encodeur vidéo comme libx264.
  • -r FPS : définit la fréquence d'images de la vidéo de sortie {FPS} : définit la fréquence d'images de la vidéo de sortieFPS : définissez la fréquence d'images de la vidéo de sortie sur {FPS} (facultatif, ajoutez si nécessaire).
    Enfin, FULL_SCRIPT contient la commande FFmpeg complète utilisée pour générer le fichier vidéo final.
# 11. END
# FULL_SCRIPT+=" -map [video] -vsync 2 -async 1 -rc-lookahead 0 -g 0 -profile:v main -level 42 -c:v libx264 -r ${FPS} ../advanced_blurred_background.mp4"
FULL_SCRIPT+=" -map [video] -vsync 2 -async 1 -rc-lookahead 0 -g 0 -profile:v main -level 42 -c:v libx264 ../advanced_blurred_background.mp4"

#FULL_SCRIPT+=" -map [video] -async 1 -rc-lookahead 0 -g 0 -profile:v main -level 42 -c:v libx264 -r ${FPS} ../advanced_blurred_background.mp4"

eval ${
    
    FULL_SCRIPT}

ELAPSED_TIME=$(($SECONDS - $START_TIME))

echo -e '\nSlideshow created in '$ELAPSED_TIME' seconds\n'

unset $IFS

Je suppose que tu aimes

Origine blog.csdn.net/qq_44824148/article/details/128842259
conseillé
Classement