Open26
ffmpeg でよく利用するコマンド群
- 指定フレーム番号から指定フレーム番号までの範囲を抽出・切り出す
ffmpeg \
-i in.mp4 \
-vf trim=start_frame=1168:end_frame=1178,setpts=PTS-STARTPTS \
-an \
out.mp4
- MP4 の Twitter対応MP4変換
ffmpeg \
-i output.mp4 \
-pix_fmt yuv420p \
-vcodec h264_nvenc \
output_.mp4
- GIF to MP4
ffmpeg \
-i demo.gif \
-movflags faststart \
-pix_fmt yuv420p \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
-q:v 1 \
demo.mp4
- MP4 の時間カット+フレームレート変更+リサイズ
ffmpeg \
-ss 00:00:00 \
-to 00:00:25 \
-i input.mp4 \
-r 15 \
-q:v 1 \
-vf scale=768:-1 \
output.mp4
- MP4 to GIF
ffmpeg -i input.mp4 output.gif
- MP4 to WAV
ffmpeg \
-i input.mp4 \
-vn \
-acodec pcm_s16le \
-ar 44100 \
-ac 2 \
output.wav
ffmpeg \
-i input.mp4 \
-vn \
-acodec pcm_s16le \
-ar 16000 \
-ac 2 \
output.wav
- mp4 アスペクト比を維持したサイズ変更
ffmpeg \
-i in.mp4 \
-vf scale=1280:-1 \
-vcodec mpeg4 \
-q:v 1 \
out.mp4
又は
ffmpeg \
-i in.mp4 \
-vf scale=1280:-1 \
-vcodec h264_nvenc \
out.mp4
- 動画から連番画像生成
ffmpeg -i 01.avi -vcodec png %06d.png
ffmpeg -i 01.avi -vcodec mjpeg %06d.jpg
ffmpeg -i input.mp4 -vf "fps=5" frameE_%06d.png
- mov to mp4
ffmpeg -i input.mov output.mp4
- 動画のパディング
- 640x360 の動画に縦方向に16px足して、余白をグレーにし、表示位置を6px下にずらして中央寄せ
- 生成される動画は 640x480
ffmpeg \
-i input_720x1280.mp4 \
-vf pad=h=ih+16:y=6:color=7d7d7d \
-q:v 1 \
output_736x1280.mp4
- 横128px足して、縦 24px足して、表示位置を横64px目、縦12px目にずらして中央寄せ
ffmpeg \
-i 360x640.mp4 \
-vf pad=w=iw+128:h=ih+24:x=64:y=12:color=7d7d7d \
-q:v 1 \
384x768.mp4
- 640x360 の動画に対して縦方向に120px足して、余白を黒にし、表示位置を60px下にずらして中央寄せ
- 生成される動画は 640x480
ffmpeg \
-i test10_360x640.mp4 \
-vf pad=h=ih+120:y=60:color=000000 \
-q:v 1 \
test10_480x640.mp4
- 2枚の動画を水平方向に結合
ffmpeg \
-i left.mp4 \
-i right.mp4 \
-filter_complex "hstack" \
-vcodec h264_nvenc \
output_merge.mp4
- 2枚の動画を垂直方向に結合
ffmpeg \
-i left.mp4 \
-i right.mp4 \
-filter_complex "vstack" \
-vcodec h264_nvenc \
output_merge.mp4
- 指定時間範囲でMP4をカット, 0秒から47秒をカット
ffmpeg \
-ss 00:00:00 \
-to 00:00:47 \
-i input.mp4 \
-c copy \
-q:v 1 \
output.mp4
- ハードウェアアクセラレーションを使用しつつ webm to mp4 無劣化変換
ffmpeg -y -hwaccel cuda -i xxx.webm -vcodec copy xxx.mp4
- ハードウェアアクセラレーションを使用しつつ webm to mp4 無劣化変換 + フォルダ内サブフォルダ含めた再帰一括処理 (Ubuntu)
convert.sh
#!/bin/sh
for FILE in `find . -name "*.webm"`
do
FILENAME=`echo ${FILE} | sed 's/\.[^\.]*$//'`
ffmpeg \
-y \
-hwaccel cuda \
-i ${FILENAME}.webm \
-vcodec copy \
-loglevel error \
${FILENAME}.mp4
done
- ffmpeg のGPUアクセラレーション
- mp4 のフレームレートを無劣化で変更するスクリプト, 0.5倍速の例
ffmpeg -y \
-i 20231220073655.mp4 \
-af "atempo=0.5" \
-bsf:v setts=ts=TS*2.0 \
-c:v copy \
20231220073655_.mp4
- mp4 のフレームレートを無劣化で一括変更するスクリプト + サブフォルダを含めた再帰一括処理 (Ubuntu), 0.66倍速の例
convert.sh
#!/bin/sh
for FILE in `find . -name "*.mp4"`
do
FILENAME=`echo ${FILE} | sed 's/\.[^\.]*$//'`
ffmpeg -y -i ${FILENAME}.mp4 -af "atempo=0.66" -bsf:v setts=ts=TS*1.5 -c:v copy ${FILENAME}_30.mp4
done
- 指定したフレームレートに変更するスクリプト
ffmpeg -i out.mp4 -filter:v "fps=30" -c:a copy -q:v 1 out_30fps.mp4
- MP4 の動画長 (時:分:秒) を調べる (
HH:MM:SS
形式)
FILE="2023-0712-1824-08_xxxx.mp4"
ffmpeg -i $FILE 2>&1 | grep Duration | awk '{print $2}' | tr -d ,
00:05:32.69
- MP4 の動画長 (時:分:秒) を調べる (秒換算形式)
FILE="2023-0712-1824-08_xxxx.mp4"
echo $(ffmpeg -i "$FILE" 2>&1 | grep Duration | awk '{print $2}' | tr -d , | awk -F: '{ if (NF==3) { print ($1*3600)+($2*60)+$3 } else { print ($1*60)+$2 } }')
332.69
- MP4 の生成日時、動画長、最終更新日時を取得する
get_end_time.sh
#!/bin/bash
# MP4 ファイルのパスを引数として受け取る
FILE="$1"
# creation_time を取得
CREATION_TIME=$(ffmpeg -i "$FILE" 2>&1 | grep creation_time | head -1 | awk -F 'creation_time=' '{print $2}' | cut -d' ' -f1)
# ファイルの持つ duration(秒)を取得
DURATION=$(ffmpeg -i "$FILE" 2>&1 | grep Duration | awk '{print $2}' | tr -d , | awk -F: '{ if (NF==3) { print ($1*3600)+($2*60)+$3 } else { print ($1*60)+$2 } }')
# 最終の録画時刻を算出(CREATION_TIME から DURATION を加算)
END_TIME=$(date -d "$CREATION_TIME + $DURATION seconds" +"%Y-%m-%d %H:%M:%S")
echo "Creation Time: $CREATION_TIME"
echo "Duration: $DURATION seconds"
echo "End Time: $END_TIME"
sudo chmod +x get_end_time.sh
./get_end_time.sh "xxxx.mp4"
Creation Time:
Duration: 135.66 seconds
End Time: 2023-08-19 12:10:17
- MP4 ファイル名が録画終了日時を表している場合に、ファイル名から正規表現を使用して録画終了時分秒を抽出する
-
BASH_REMATCH
は正規表現にマッチした文字列を参照する
- サンプルのファイル名 :
2023-0712-1824-08_xyz100-12.mp4
#!/bin/bash
# MP4 ファイルのパスを引数として受け取る
FILE="$1"
# 正規表現を使用してファイル名を分解する
if [[ $FILE =~ ([0-9]{2})([0-9]{2})-([0-9]{2})_xyz ]]; then
HOUR="${BASH_REMATCH[1]}"
MINUTE="${BASH_REMATCH[2]}"
SECOND="${BASH_REMATCH[3]}"
fi
echo "Hour: $HOUR"
echo "Minute: $MINUTE"
echo "Second: $SECOND"
- 00時00分00秒からの経過秒に変換するパターン (
#0
は変数の先頭のゼロを削除する)
#!/bin/bash
# MP4 ファイルのパスを引数として受け取る
FILE="$1"
# 正規表現を使用してファイル名を分解する
if [[ $FILE =~ ([0-9]{2})([0-9]{2})-([0-9]{2})_xyz ]]; then
HOUR=${BASH_REMATCH[1]}
MINUTE=${BASH_REMATCH[2]}
SECOND=${BASH_REMATCH[3]}
fi
echo $((${HOUR#0}*3600+${MINUTE#0}*60+${SECOND#0}))
66248
- 時分秒ミリ秒をあらわす
00:02:15.66
からbashを使用して 0, 2, 15, 66 を抽出する方法 (#0
は変数の先頭のゼロを削除する)
TIME_STR="00:02:15.66"
if [[ $TIME_STR =~ ([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]{2}) ]]; then
HOUR=${BASH_REMATCH[1]}
MINUTE=${BASH_REMATCH[2]}
SECOND=${BASH_REMATCH[3]}
MILLISECOND=${BASH_REMATCH[4]}
fi
echo "Hours: ${HOUR#0}"
echo "Minutes: ${MINUTE#0}"
echo "Seconds: ${SECOND#0}"
echo "Milliseconds: ${MILLISECOND#0}"
Hours: 0
Minutes: 2
Seconds: 15
Milliseconds: 66
- 任意のフォルダのサブフォルダが日付のような
MM_DD
形式で複数保存されている - 各
MM_DD
フォルダの配下には、あらかじめファイル名に日時分秒の情報が含まれていて、あらかじめ時系列にソートしやすい形式で複数の MP4 ファイルが保存されている - 上記の前提で、ディレクトリは順番通りに(変更しないで)、そのディレクトリ内のファイルを降順で処理するbashスクリプト
フォルダ例:
./07_12/0001.mp4
./07_12/0002.mp4
./07_12/0003.mp4
./07_12/0004.mp4
./07_12/0005.mp4
./07_13/0001.mp4
./07_13/0002.mp4
./07_13/0003.mp4
./07_13/0004.mp4
./07_13/0005.mp4
スクリプト例:
#!/bin/bash
# ディレクトリを取得(ディレクトリの順序はそのまま)
for dir in $(ls -d */ | grep '^[0-9][0-9]_[0-9][0-9]/$'); do
# ディレクトリ内のファイルを降順で取得して処理
for file in $(ls -r ./$dir*.mp4); do
# ここでファイルに対して何らかの処理を行います。
echo "Processing $file"
# 例: ファイルの内容を表示
# cat $file
# 他の処理が必要な場合は上記の処理を置き換えまたは追加してください。
done
done
- 秒数を
HH:MM:SS.mmm
形式に変換する方法
#!/bin/bash
# 428秒150ミリ秒
SECONDS=428.150
# 整数部分の秒数を取得
INT_SECONDS=${SECONDS%.*}
# ミリ秒を取得
MILLISECONDS=$(printf "%03d" $(echo "$SECONDS" | awk -F. '{if ($2) print $2; else print "0"}'))
HOURS=$((INT_SECONDS / 3600))
MINUTES=$(( (INT_SECONDS % 3600) / 60 ))
SECONDS=$((INT_SECONDS % 60))
printf "%02d:%02d:%02d.%s\n" $HOURS $MINUTES $SECONDS $MILLISECONDS
- 動画ファイルの末尾に
HH:MM:SS.mmm
分のブランク映像をパディングする
- 無音音声ストリームを生成する: これは、動画に音声が含まれている場合に、ブランク映像の間も音声が必要となるためです。
- ブランクの映像フィルタを使用してブランクの映像を生成する。
- 元の動画とブランクの映像および音声を結合する。
# 処理対象のファイル
FILE="xxxx.mp4"
# パディングする HH:MM:SS.mmm
PADDING_TIME=00:00:02.500
DIRPATH=$(dirname ${FILE})
FILENAME=$(basename ${FILE})
# 元動画の情報取得
WIDTH=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of default=noprint_wrappers=1:nokey=1 "${FILE}")
HEIGHT=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of default=noprint_wrappers=1:nokey=1 "${FILE}")
# FRAME_RATE=$(ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of default=noprint_wrappers=1:nokey=1 "${FILE}")
# DURATION=$(ffprobe -v error -select_streams v:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 "${FILE}")
TIMEBASE=$(ffprobe -v error -select_streams v:0 -show_entries stream=time_base -of default=noprint_wrappers=1:nokey=1 "${FILE}" | awk -F'/' '{print $2}')
# -q:v 0: 最高品質(しかし、ビットレートは最も高い)
# -q:v 23: デフォルトの品質
# -q:v 31: 最低品質(しかし、ビットレートは最も低い)
# ブランク映像の生成
ffmpeg \
-y \
-f lavfi \
-i "color=black:r=50/3:s=${WIDTH}x${HEIGHT}" \
-f lavfi \
-i "anullsrc=r=44100:cl=stereo" \
-t ${PADDING_DULATION_HHMMSSMMM} blank_video.mp4
# 元動画とブランク映像をマージ
ffmpeg \
-y \
-i ${FILE} \
-i blank_video.mp4 \
-filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[vtemp][aout];[vtemp]fps=50/3[vout]" \
-q:v 20 \
-map "[vout]" \
-map "[aout]" \
-video_track_timescale ${TIMEBASE} \
${DIRPATH}/pad_${FILENAME}
rm blank_video.mp4
- ffmpeg のログを非表示にする
- ログの出力レベルを変更する:
ffmpeg には -loglevel オプションがあり、これを使用してログの詳細度を制御できます。quiet モードに設定することで、ほとんどのメッセージを非表示にできます。ffmpeg -i input.mp4 -loglevel quiet output.avi
- 標準エラー出力を無視する:
すべてのログメッセージを完全に無視したい場合は、bash で標準エラー出力を /dev/null にリダイレクトすることで、エラーメッセージを非表示にすることができます。ffmpeg -i input.mp4 output.avi 2>/dev/null
-
MM_DD
という規則で月日ごとに作成された複数のフォルダの配下にある.mp4
ファイルは基本的に10分間隔で連続して保存されている - しかし、録画が失敗している場合もあり、動画長が10分ちょうどになっていなかったり、前後の動画が連続していないときがある
- 上記条件において、各フォルダ内の最新の日付の
.mp4
から逆順に処理をしていき、撮影時間が欠落している部分をブランクの動画を自動生成してパディングする
#!/bin/bash
GREEN="\033[32m"
RESET="\033[0m"
# パディング後に生成される動画の品質設定
# -q:v 0: 最高品質(しかし、ビットレートは最も高い)
# -q:v 23: デフォルトの品質
# -q:v 31: 最低品質(しかし、ビットレートは最も低い)
MOVIE_QUALITY=20
FRAME_RATE="50/3"
# ディレクトリを取得(ディレクトリの順序はそのまま)
for FOLDER in $(ls -d */ | grep '^[0-9][0-9]_[0-9][0-9]/$'); do
# パディング算出用変数の初期化
TMP_PREV_ESTIMATED_TIME=0
# ディレクトリ内のファイルを降順で取得して処理
for FILE in $(ls -r ./${FOLDER}*.mp4); do
# 動画終了時刻(秒) の取得 ##########################################################
if [[ ${FILE} =~ ([0-9]{2})([0-9]{2})-([0-9]{2})_ ]]; then
HOUR=${BASH_REMATCH[1]}
MINUTE=${BASH_REMATCH[2]}
SECOND=${BASH_REMATCH[3]}
fi
MOVIE_END_TIME=$((${HOUR#0}*3600+${MINUTE#0}*60+${SECOND#0}))
################################################################################
# 動画長(秒) の取得 ##############################################################
MOVIE_DULATION=$(ffmpeg -i "$FILE" 2>&1 | grep Duration | \
awk '{print $2}' | tr -d , | \
awk -F: '{ if (NF==3) { print ($1*3600)+($2*60)+$3 } else { print ($1*60)+$2 } }')
# 現在動画の末尾にパディングすべき時間幅秒の算出 #######################################
# TMP_PREV_ESTIMATED_TIME > 0 なおかつ MOVIE_END_TIME < TMP_PREV_ESTIMATED_TIME のときのみ計算
# パディングすべき秒 = (ひとつ前の動画から推定した終了時刻秒 - 今の動画の終了時刻秒)
PADDING_DULATION=0.000
if [ "$(echo "${TMP_PREV_ESTIMATED_TIME} > 0" | bc -l)" -eq 1 ] && [ "$(echo "${MOVIE_END_TIME} < ${TMP_PREV_ESTIMATED_TIME}" | bc -l)" -eq 1 ]; then
PADDING_DULATION_CALCULATED=$(echo ${TMP_PREV_ESTIMATED_TIME} - ${MOVIE_END_TIME} | bc)
PADDING_DULATION=$(printf "%.3f" ${PADDING_DULATION_CALCULATED})
fi
################################################################################
# 現在動画ファイルの dulation と 動画終了時刻 を基準にして算出した1つ前の動画のあるべき撮影終了時刻を計算
PREV_ESTIMATED_TIME_CALCULATED=$(echo ${MOVIE_END_TIME} - ${MOVIE_DULATION} | bc)
PREV_ESTIMATED_TIME=$(printf "%.3f" ${PREV_ESTIMATED_TIME_CALCULATED})
printf "${GREEN}FILE: ${FILE} MOVIE_DULATION: ${MOVIE_DULATION}, MOVIE_END_TIME: ${MOVIE_END_TIME}, PREV_ESTIMATED_TIME: ${PREV_ESTIMATED_TIME}, PADDING_DULATION: ${PADDING_DULATION}${RESET}\n"
TMP_PREV_ESTIMATED_TIME=${PREV_ESTIMATED_TIME}
################################################################################
# 動画の末尾にパディング ###########################################################
# PADDING_DULATION > 0.000 のときのみ
# PADDING_DULATION = 0.000 のときはファイル名の先頭に pad_ と付与してファイルコピーするだけ
DIRPATH=$(dirname ${FILE})
FILENAME=$(basename ${FILE})
if [ "$(echo "${PADDING_DULATION} > 0.000" | bc -l)" -eq 1 ]; then
# PADDING_DULATION (秒表現) を HH:MM:SS.mmm 形式に変換する
# 整数部分の秒数を取得
INT_SECONDS=${PADDING_DULATION%.*}
# ミリ秒を取得
HOURS=$((INT_SECONDS / 3600))
MINUTES=$(( (INT_SECONDS % 3600) / 60 ))
SECONDS=$((INT_SECONDS % 60))
MILLISECONDS=$(echo ${PADDING_DULATION} | cut -d "." -f 2)
PADDING_DULATION_HHMMSSMMM=$(printf "%02d:%02d:%02d.%s" ${HOURS} ${MINUTES} ${SECONDS} ${MILLISECONDS})
# 動画の末尾に PADDING_DULATION_HHMMSSMMM 分のブランク映像を追加する
# 元動画の情報取得
WIDTH=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of default=noprint_wrappers=1:nokey=1 "${FILE}")
HEIGHT=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of default=noprint_wrappers=1:nokey=1 "${FILE}")
# FRAME_RATE=$(ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of default=noprint_wrappers=1:nokey=1 "${FILE}") # 正確な値が取得できないときがある
TIMEBASE=$(ffprobe -v error -select_streams v:0 -show_entries stream=time_base -of default=noprint_wrappers=1:nokey=1 "${FILE}" | awk -F'/' '{print $2}')
# -q:v 0: 最高品質(しかし、ビットレートは最も高い)
# -q:v 23: デフォルトの品質
# -q:v 31: 最低品質(しかし、ビットレートは最も低い)
# ブランク映像の生成
printf "${GREEN}@@@@@@@@@@@@@@@@@@@ Make blank video FRAME_RATE: ${FRAME_RATE}, WIDTHxHEIGHT: ${WIDTH}x${HEIGHT}, PADDING_DULATION_HHMMSSMMM: ${PADDING_DULATION_HHMMSSMMM}, TIMEBASE: ${TIMEBASE}${RESET}\n"
ffmpeg \
-y \
-f lavfi \
-i "color=black:r=${FRAME_RATE}:s=${WIDTH}x${HEIGHT}" \
-f lavfi \
-i "anullsrc=r=44100:cl=stereo" \
-loglevel error \
-t ${PADDING_DULATION_HHMMSSMMM} blank_video.mp4
# 元動画とブランク映像をマージ
printf "${GREEN}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Videos merge${RESET}\n"
ffmpeg \
-y \
-i ${FILE} \
-i blank_video.mp4 \
-filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[vtemp][aout];[vtemp]fps=${FRAME_RATE}[vout]" \
-q:v ${MOVIE_QUALITY} \
-map "[vout]" \
-map "[aout]" \
-video_track_timescale ${TIMEBASE} \
-loglevel error \
${DIRPATH}/pad_${FILENAME}
rm blank_video.mp4
else
printf "${GREEN}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Videos file copy${RESET}\n"
cp ${FILE} ${DIRPATH}/pad_${FILENAME}
fi
################################################################################
done
done
MP4動画を 10FPS のレートで .png
に変換するコマンド
ffmpeg -i your_video.mp4 -vf "fps=1.5,scale=672:384" frame_%05d.png
rename 's/frame_0/frame_1/' frame_0*.png
rename 's/frame_0/frame_2/' frame_0*.png
6桁の連番がファイル名になった .jpg
ファイルを 30 FPS で MP4 に変換するコマンド
ffmpeg \
-framerate 30 \
-i %06d.jpg \
-c:v mpeg4 \
-pix_fmt yuv420p \
-q:v 0 \
-vf "scale=trunc(iw/2):trunc(ih/2)" \
-vcodec h264_nvenc \
output.mp4