[FFmpeg] 1 つの記事で FFmpeg をすぐに始めることができます

FFmpeg

1. 環境設定

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

便宜上、ここでは conda 環境 (py10) を直接使用し、ffmpeg をダウンロードするために次のコードを使用します。

conda install -c conda-forge ffmpeg

これで環境構築は完了です。ffmpegのバージョンなどを確認できます。

2. 基本コード部分

2.1 ハイパーパラメータの設定

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

2.2 データのロード

# 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 いくつかのパラメータ

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 入力ストリームの具体的な演算処理

for IMAGE in ${
    
    FILES[@]}; do
    FULL_SCRIPT+="-loop 1 -i '${IMAGE}' "
done
......
共通操作
[${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 入力ストリームのスプライシング

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

3. 画像加工

マップを通じて画像ストリームを jpg/png ファイルに直接印刷します。

テンプレートの形式は次のとおりです。

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

例えば:

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. ビデオ操作 (blurred_background.sh を例にします)

# 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

-filter_complex パラメーターを使用して、複数の入力ストリームを処理し、1 つ以上の出力ストリームを生成します。
このスクリプトでは、-filter_complex パラメーターの後の内容を使用して、画像のスケーリング、トリミング、フレーム レートの設定などの一連のフィルター操作を定義します。

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

ループ内では、${c}:v は現在の画像のビデオ ストリーム インデックスを表します。この入力ストリーム (各画像は入力ストリームです) は、画像のスケーリング、ピクセル アスペクト比の設定、RGBA 形式への変換、ぼかし効果の適用、フレーム レートの設定などの一連のフィルター操作を適用することによって処理されます。
具体的なフィルタ操作は次のとおりです。

  • スケール=幅 x {幅}xW I D T H x {HEIGHT}: 指定された幅と高さに画像を拡大縮小します。
  • setar=sar=1/1: 画像の歪みを避けるために、ピクセルの縦横比を 1:1 に設定します。
  • format=rgba: 透明チャネルをサポートするために画像形式を RGBA 形式に変換します。
  • boxblur=100: ぼかし半径 100 を使用して、ぼかし効果を適用します。
  • setar=sar=1/1: ピクセルの縦横比を 1:1 に再度設定します。
  • fps= FPS: フレーム レートを指定した FPS 値に設定します。処理が完了したら、入力ストリームに stream {FPS} という名前を付けます。フレーム レートを指定した FPS 値に設定します。処理が完了したら、入力ストリームに stream という名前を付けますFPS : フレーム レートを指定したFPS値に設定します。処理が完了すると、入力ストリームはs t re am ((c+1))blurred unique identifier という名前になります。ここで、(c+1) は現在のイメージのインデックスに 1 を加えたものを表すため、名前は繰り返されません。
# 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

このループは各画像入力ストリームをループし、各入力ストリームに対して次の操作を実行しました。

  1. スケール操作: スケール フィルターを使用して、画像の幅と高さを指定されたターゲット サイズWIDTH x {WIDTH}xに調整します。W I D T H x {高さ}。ここでは条件式を使用して、画像が変形せずにターゲット サイズに適合するようにスケーリング方法を決定します。画像のアスペクト比がターゲット アスペクト比以上の場合WIDTH/{WIDTH}/WID T H / {HEIGHT} の場合、幅は ${WIDTH} に調整され、高さはアスペクト比に従って調整されます。それ以外の場合、高さは ${HEIGHT} に調整され、幅はアスペクト比に従って調整さますアスペクト比に合わせて。
  2. 再度スケール操作を行う: スケール後の画像サイズが偶数になるようにするには、スケール フィルターを使用して幅と高さを最も近い偶数に丸めます。
  3. ピクセル アスペクト比の設定: setar フィルターを使用して画像のピクセル アスペクト比を 1:1 に設定し、後続の処理ステップでの異常な伸縮や縮小を回避します。
  4. 形式変換: 形式フィルターを使用して画像のピクセル形式を RGBA 形式に変換し、後続の処理と合成で画像の透明度を正しく処理できるようにします。
    変換されたイメージ入力ストリームは [stream$((c+1))raw] という名前になります。$((c+1)) は、ループ インデックス変数 c をインクリメントすることによって取得されるシーケンス番号です。これらの準備された入力ストリームは、後続の処理ステップで使用されます。
# 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

この部分では、前処理されたぼやけた画像とスケーリングされた画像に対してオーバーレイ操作が実行されます。
各画像のぼかした画像と拡大縮小した画像をループし、オーバーレイ フィルターを使用してそれらを重ね合わせました。オーバーレイ操作のパラメータには次のものがあります。

  • overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2:拡大縮小した画像の上にぼかした画像を画面中央になるように重ねます。
  • format=rgb: 重畳画像のピクセル形式をRGB形式に設定します。
    重畳画像の名前は [stream cout 1] で、c 番目の重畳画像の出力を表します。さらに、分割フィルタを使用して、各画像の出力ストリームを 2 つのストリーム [stream {c}out1] に分割します。これは、重ね合わせ後の c 番目の画像の出力を表します。さらに、分割フィルタを使用して、各画像の出力ストリームを 2 つのストリームに分割します [ストリームc o u t 1 ] はc番目の画像の重ね合わせ後の出力を表します。さらに、分割フィルターを使用して、各画像の出力ストリームを 2 つのストリーム [s tre am {c}out1] と [stream cout 2] に分割し後続処理ステップ使用ますこのループを通じて、後続の処理ステップで使用するために、画像ごとに 2 つの出力ストリーム [stream{c}out2] が生成されます。このループを通じて、各イメージは 2 つの出力ストリームを生成します。c o u t 2 ]は、後続の処理ステップで使用されます。このループを通じて、画像ごとに 2 つの出力ストリームが生成されます。ここで、[ s tre am {c}out1]重ね合わされた画像、[stream${c}out2] は元のスケーリングされた画像です。
# 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

この部分では、重ね合わせた画像を塗りつぶします。
画像ごとにスタックされた出力ストリームをループし、パッド フィルターを使用して画像をパディングします。塗りつぶし操作のパラメータには次のものがあります。

  • 幅=幅 : 高さ = {幅}:高さ=_ _ _ _:身長_ _ _ _ _= {HEIGHT}: 塗りつぶされた画像の幅と高さを指定された値に設定します。
  • x=( WIDTH − iw ) / 2 : y = ( {WIDTH}-iw)/2:y=(_ _ _ _私はw /2:y=( {HEIGHT}-ih)/2: 画像が画面の中央に配置されるようにパディングの位置を計算します。
  • trim=duration=${IMAGE_DURATION}: 画像の継続時間を IMAGE_DURATION に設定します。これは、画像が表示される時間の長さです。
  • select=lte(n,${IMAGE_FRAME_COUNT}): 画像のフレーム番号範囲を選択して、指定されたフレーム番号範囲内でのみ画像が表示されるようにします。
    画像の位置や枚数によっても異なる判定や処理が行われます。
  • それが最初の画像であり、画像の数が 1 より大きい場合、トランジション効果の終わりを表す追加の出力ストリーム [stream${c}ending] が生成されます。
  • これが最後の画像の場合は、トランジション効果の開始部分を画像の出力ストリーム [stream${c}starting] にマージします。
  • 中間イメージの場合、トランジション効果の開始部分と終了部分を 2 つの出力ストリーム [stream cstarting] と [stream {c}starting] と [streamc開始]および[ストリーミング{ c } ending ]_ _ _ _
    このループを通じて、各画像のオーバーレイ出力ストリームが埋められ、画像のオーバーレイ効果とトランジション効果を表す対応する出力ストリームが生成されます。
# 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

この部分ではトランジションフレームを作成します。
隣接する画像の各ペアをループして、トランジション効果のあるフレームを作成します。現在の画像と次の画像を含む、隣接する画像のペアごとに次のようにします。

  • ブレンド フィルターを使用して 2 つの画像をブレンドし、トランジション フレームを生成します。ブレンド操作のパラメータには次のものがあります。
    • all_expr='A*(if(gte(T, TRANSITIONDURATION ) , {TRANSITION_DURATION}),交通_ _ _ _ _ _ _DU RAT I ON ) , {TRANSITION_DURATION},T/TRANSITION_DURATION ) ) + B ∗ ( 1 − ( if ( gte ( T , {TRANSITION_DURATION}))+B*(1-(if(gte( T ,交通_ _ _ _ _ _ _D使用) ) _ _ _+B( 1( if ( g te ( T , {TRANSITION_DURATION}),TRANSITIONDURATION , T / {TRANSITION_DURATION}, T /交通_ _ _ _ _ _ _D使用_ _ _ _T / {TRANSITION_DURATION})))': エクスプレッションを通じて 2 つの入力イメージをミックスします。ここで、A は現在のイメージを表し、B は次のイメージを表します。時間 T の変化に従って、2 つの画像の重みが制御され、トランジション効果が実現されます。
    • select=lte(n,${TRANSITION_FRAME_COUNT}): 遷移フレームが指定されたフレーム番号範囲内でのみ生成されるようにするには、遷移フレームのフレーム番号範囲を選択します。
      このループを通じて、隣接する画像のペアごとにトランジション フレームの出力ストリームが作成され、2 つの画像間のトランジション効果を表すために使用されます。
# 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

この部分では、ビデオ クリップの結合が始まります。
隣接するイメージの各ペアをループし、前のイメージのオーバーレイ出力ストリーム (ストリームカバーレイ) と現在のイメージのトランジション フレーム出力ストリーム (ストリーム {c}overlaid) を現在のイメージのトランジション フレーム出力ストリームと結合します (ストリームcover l aid )は、ビデオ クリップを接合するため現在のイメージのトランジション フレーム出力ストリーム (ストリーム ((c+1))blended) に接続ます
このループを通じて、隣接する画像のペアごとに、それらを接続する入力ストリームが作成され、それらの間でビデオ クリップを接続するために使用されます。

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

このパートでは、ビデオ クリップの結合が完了します。
最後の画像 (stream${IMAGE_COUNT} オーバーレイ) のオーバーレイ出力ストリームを、以前に接続されたすべてのトランジション フレームの出力ストリームに接続して、すべてのビデオ クリップをつなぎ合わせます。
このステップを通じて、すべての画像のオーバーレイおよびトランジション効果を含む最終的なビデオ ストリーム (ビデオ) が生成されます。

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

この最後の部分では、ビデオ ストリーム (ビデオ) が出力ファイルにマッピングされ、出力ファイルの形式とエンコード パラメーターが指定されます。
出力ファイルへのパスを .../advanced_blurred_background.mp4 に設定し、ビデオ エンコードに libx264 エンコーダを使用します。
その他のパラメータは次のとおりです。

  • -vsync 2: オーディオとビデオの同期モードを「パススルー」に設定します。
  • -async 1: オーディオ同期モードを「オーディオファースト」に設定して、オーディオとビデオが確実に同期されるようにします。
  • -rc-lookahead 0: レート制御での予測を無効にします。
  • -g 0: GOP (Group of Pictures) サイズ制限を無効にします。
  • -profile:v main -level 42: ビデオ エンコーディングのプロファイルとレベルを設定します。
  • -c:v libx264: ビデオ エンコーダーを libx264 として指定します。
  • -r FPS: 出力ビデオのフレーム レートを設定します。 {FPS}: 出力ビデオのフレーム レートを設定します。FPS : 出力ビデオのフレーム レートを{FPS} に設定します (オプション、必要に応じて追加)。
    最後に、FULL_SCRIPT には、最終ビデオ ファイルの生成に使用される完全な FFmpeg コマンドが含まれています。
# 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

おすすめ

転載: blog.csdn.net/qq_44824148/article/details/128842259