私はffmpegでビデオからスクリーンを作るための解決策を得ようとしています。見つかった例のほとんどは、画像を取得するためにビデオ全体をデコードすることを含みます。これは-大きなビデオの場合-かなり遅いです。
より良い試みは大まかに次のように説明されています: meaningful-thumbnails-for-a-video-using-ffmpeg
そこにあった擬似コード:
for X in 1..N
T = integer( (X - 0.5) * D / N )
run `ffmpeg -ss <T> -i <movie>
-vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`
どこ:
私はこの「擬似コード」に基づいて実用的なソリューションを考え出し、それをimagemagick
のサムネイルモンタージュと組み合わせました。
#!/bin/bash
# some of them not used here
MOVIE=$1
D= # D - video duration
N=30 # N - wanted number of thumbnails
X=1 # X - thumbnail number
T= # T - time point for thumbnail
Y= # Y - nth frame to take
Z= # Z - total number of frames
W= # W - fps of the video
SP= # SP - starting point of extraction
OUTPUT=$2
# some of them from another approach - setting defaults
if [ -z "$N" ]; then N=30; fi
if [ -z "$COLS" ]; then COLS=3; fi
if [ -z "$ROWS" ]; then ROWS=10; fi
if [ -z "$HEIGHT" ]; then HEIGHT=360; fi
if [ -z "$SIZE" ]; then SIZE=3600; fi
# get video name without the path and extension
MOVIE_NAME=$(basename $MOVIE)
OUT_DIR=$(pwd)
if [ -z "$OUTPUT" ]; then OUTPUT=$(echo ${MOVIE_NAME%.*}_preview.jpg); fi
# get duration of input:
D=$(echo "$(ffprobe -hide_banner -i $MOVIE 2>&1 | sed -n "s/.*: \(.*\), start:.*/\1/p")" | sed 's/:/*60+/g;s/*60/&&/' | bc)
D=$(echo "$D/1" | bc) # get rid of the floating point part (make integer)
# get fps of input:
W=$(ffprobe $MOVIE 2>&1| grep ",* fps" | cut -d "," -f 5 | cut -d " " -f 2)
# get frame number of input:
Z=$(ffprobe -hide_banner -show_streams $MOVIE 2>&1 | grep nb_frames | head -n1 | sed 's/.*=//')
# as a fallback we'll calculate the frame count
# from duration and framerate, very unprecise, though
if [ "$Z" = "N/A" ]; then Z=$(echo "$D * $W / 1" | bc); fi
echo "Duration is: $D seconds / $Z frames @ $W fps"
# generate thumbnails in the /tmp folder
TMPDIR=/tmp/thumbnails-${RANDOM}/
mkdir $TMPDIR
for (( c=X; c<=N; c++ ))
do
Y=$(echo "($Z / $N)/1" | bc)
T=$(echo "(($c - 0.5) * $Y/1)" | bc)
SP=$(echo "$T / $W" | bc)
ffmpeg -hide_banner -ss $SP -i $MOVIE -an -sn -vf select="eq(pict_type\,I),scale=-1:360" -vframes 1 ${TMPDIR}thumb00$c.jpg
done
# mount the pics in one page neatly
montage ${TMPDIR}thumb*.jpg -background white -geometry +5+5 -tile ${COLS}x ${TMPDIR}output.jpg
rm -R $TMPDIR
echo $OUT_FILEPATH
これは機能しますが、作成されたファイル名に苦労しています。
ffmpegの呼び出しは「forループ」で発生するため、明らかなname%03d.jpg
パターンは出力ファイルでは機能しません。
最後の反復の出力:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf54.59.106
Duration: 00:08:41.17, start: 0.023220, bitrate: 1866 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720, 1731 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc
(default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 127 kb/s (default)
Metadata:
handler_name : SoundHandler
[swscaler @ 0x15a85a0] deprecated pixel format used, make sure you did set range correctly
Output #0, image2, to '/tmp/thumbnails-13957/thumb0030.jpg':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf56.40.101
Stream #0:0(und): Video: mjpeg, yuvj420p(pc), 640x360, q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc (default)
Metadata:
handler_name : VideoHandler
encoder : Lavc56.60.100 mjpeg
Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> mjpeg (native))
Press [q] to stop, [?] for help
frame= 1 fps=0.0 q=3.2 Lsize=N/A time=00:00:00.04 bitrate=N/A dup=1 drop=1
video:8kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
出力ファイル名に先行ゼロを含む反復変数を入れてみましたthumb00$c.jpg
。これはこれまでのところ機能しますが、繰り返しが10を超えるため、ファイル名の順序が正しくなくなります。つまり、imagemagickの次のモンタージュコマンドによって、ファイル名が間違った順序になります。これは私が得るものです:
-rw-rw-r-- 1 gpm gpm 17303 Sep 8 19:32 thumb0010.jpg
-rw-rw-r-- 1 gpm gpm 16474 Sep 8 19:32 thumb0011.jpg
- - " -
-rw-rw-r-- 1 gpm gpm 6323 Sep 8 19:32 thumb001.jpg
-rw-rw-r-- 1 gpm gpm 14789 Sep 8 19:32 thumb0020.jpg
-rw-rw-r-- 1 gpm gpm 18429 Sep 8 19:32 thumb0021.jpg
- - " -
-rw-rw-r-- 1 gpm gpm 18870 Sep 8 19:32 thumb002.jpg
-rw-rw-r-- 1 gpm gpm 7926 Sep 8 19:32 thumb0030.jpg
-rw-rw-r-- 1 gpm gpm 18312 Sep 8 19:32 thumb003.jpg
-rw-rw-r-- 1 gpm gpm 18274 Sep 8 19:32 thumb004.jpg
ご覧のとおり、先行ゼロはありますが、ファイルは順番に並んでいません。
私はここで迷子になっています。
それから適切に増加するファイル名を取得するにはどうすればよいですか?
printf
コマンドを使用して、数値を固定幅にゼロパディングできます。
_printf [-v var] format [arguments] Write the formatted arguments to the standard output under the control of the format. The -v option causes the output to be assigned to the variable var rather than being printed to the standard output. The format is a character string which contains three types of objects: plain characters, which are simply copied to standard output, character escape sequences, which are converted and copied to the standard output, and format specifications, each of which causes printing of the next successive argument.
_
Bashの実装は、幅と_0
_フラグを Cのprintf()
function から持つd
指定子を受け入れます。
例:
_$ for c in {8..11}; do
> printf -v result '%03d' $c
> echo $result
> done
008
009
010
011
_
したがって、_thumb00$c.jpg
_のように出力ファイル名のゼロの数をハードコーディングする代わりに、printfを使用して、数値を指定された幅にするために必要なゼロの数を計算できます。
_thumb$(printf '%03d' $c).jpg
_
説明:
$()
はプロセス置換です。コマンドのstd出力を別のコマンドの一部として使用できます。printf
はprintf
コマンドを実行します。'%03d' defines the format of
_ printf`の出力。%
_は、フォーマット指定子を使用することを意味します。0
_は、パッドをゼロにしたいことを意味します。3
_は、パディングする長さです。d
はフォーマット指定子です。特に、printf
に別のパラメーターを渡すことを意味し、そのパラメーターは符号付き10進整数として使用する必要があります。$c
_は、printf
に送信するパラメーターです。ファイルをサムネイルごとに1つずつ、N個の間隔に分割し、各間隔の中間点の後の最初のキーフレームを選択するように見えます。これは、各間隔の期間(D/N; Iと呼びましょう)を指定して、ffmpegを1回実行するだけですばやく実行できます。
ffmpeg -discard nokey -skip_frame nokey -i input -vf select='eq(n,0)+gte(mod(t,I),I/2)*gte(t-prev_selected_t,I/2)',trim=1 -vsync 0 -vframes N image%d.jpg
-discard nokey
非キーフレームをスキップするようにデマルチプレクサに指示します。 -skip_frame nokey
デコーダーに対してそれを行います。 MP4..etcの場合、これらのオプションを省略した場合よりも約15〜20倍速く実行されます。