web-dev-qa-db-ja.com

forループでファイル数を増やす方法(ffmpeg)

私は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`

どこ:

  • D-ffmpeg -iのみ、または素敵なJSON出力ライターを備えたffprobeから読み取られたビデオの長さ
  • N-必要なサムネイルの総数
  • X-1からNまでのサムネイル番号
  • T-サムネイルの時点

私はこの「擬似コード」に基づいて実用的なソリューションを考え出し、それを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

ご覧のとおり、先行ゼロはありますが、ファイルは順番に並んでいません。
私はここで迷子になっています。
それから適切に増加するファイル名を取得するにはどうすればよいですか?

1
G.P.M

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出力を別のコマンドの一部として使用できます。
  • printfprintfコマンドを実行します。
  • _'%03d' defines the format of_ printf`の出力。
    • _%_は、フォーマット指定子を使用することを意味します。
    • _0_は、パッドをゼロにしたいことを意味します。
    • _3_は、パディングする長さです。
    • dはフォーマット指定子です。特に、printfに別のパラメーターを渡すことを意味し、そのパラメーターは符号付き10進整数として使用する必要があります。
  • _$c_は、printfに送信するパラメーターです。
1
8bittree

ファイルをサムネイルごとに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倍速く実行されます。

1
Gyan