私はこれについてしばらく心配してきました。たぶんあなたの一人が私を助けてくれるでしょう。
ビデオの特定の位置からビデオフレームを抽出しようとしています。これを行うには、場所は「hh:mm:ss」形式でテキストファイルに1行に1つずつ保存されます。 bashスクリプトはファイルの行を読み取り、各行に対してffmpegを実行します。
問題は、ffmpegが「正常に」実行されているかどうかによって、テキストファイルから読み取られた入力が変更されることです。 ffmpegを実行しない場合は、他のコマンド(たとえばls)を実行し、誤った設定でffmpegを実行すると(即座に終了するように)、すべてがインデントに進みます。 ffmpegを使用すると、タイムスタンプファイルから読み取ることは無効です。
私の推測では、ffmpegはbashスクリプトが実行される環境を乱すようですが、オペレーティングシステムの設計によればこれは不可能です。
以下は最小限の例とその出力です。
スクリプトextract.sh
:
OUTDIR=samples
for f in test.txt; do # in reality a proper filter such as "f in video-*.txt"
BASE=${f%%.txt}
VIDEO=${BASE}.mkv
cd $OUTDIR
while read ss; do
echo "ffmpeg -i ../${VIDEO} -vsync 0 -ss ${ss} -t 00:00:01 '${BASE}-${ss}-%02d.png'"
(ffmpeg -i ../invalid.mkv -vsync 0 -ss ${ss} -t 00:00:01 "${BASE}-${ss}-%02d.png" 2>&1) >> ${BASE}.log
#(ffmpeg -i ../${VIDEO} -vsync 0 -ss ${ss} -t 00:00:01 "${BASE}-${ss}-%02d.png" 2>&1) >> ${BASE}.log
done < ../${f}
cd ..
done
タイムスタンプファイルtest.txt
:
00:00:42
00:01:20
00:02:20
00:04:20
00:05:56
00:06:40
スクリプトの最初のffmpegコマンドは無効なコマンドです(無効なビデオファイル)。有効にすると、次の入力が正しく読み取られますtest.txt
。
$ ./extract.sh
ffmpeg -i ../test.mkv -vsync 0 -ss 00:00:30 -t 00:00:01 'test-00:00:30-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:00:42 -t 00:00:01 'test-00:00:42-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:01:20 -t 00:00:01 'test-00:01:20-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:02:20 -t 00:00:01 'test-00:02:20-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:04:20 -t 00:00:01 'test-00:04:20-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:05:56 -t 00:00:01 'test-00:05:56-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:06:40 -t 00:00:01 'test-00:06:40-%02d.png'
最初の(無効な)ffmpegコマンドを無効にし、2番目の(正しい)コマンドを有効にした後、読み取ったタイムスタンプがtest.txt
正しくありません。
$ ./extract.sh
ffmpeg -i ../test.mkv -vsync 0 -ss 00:00:30 -t 00:00:01 'test-00:00:30-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss -t 00:00:01 'test--%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:01:20 -t 00:00:01 'test-00:01:20-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 05:56 -t 00:00:01 'test-05:56-%02d.png'
どんなアイデアがありますか?
答え1
変数を使用するときは二重引用符を使用してください。例えば、
cd "$OUTDIR"
ffmpeg
:
出力ファイル名が気に入らない。したがって、$ss
()からその項目を削除するか、別の項目()${ss//:/}
に変更してください。${ss//:/_}
ffmpeg -i "../$VIDEO" -vsync 0 -ss "$ss" -t 00:00:01 "$BASE-${ss//:/}-%02d.png" >> "$BASE.log" 2>&1 </dev/null
../
シンボリックリンクを介して処理すると、親ディレクトリの構成が中断される可能性があります。したがって、samples
ディレクトリで作業するよりもそのディレクトリに移動する方がよいでしょう。通常、変数名をすべて大文字として使用しないでください。シェル管理変数名(など)と競合する可能性が
$PATH
あります$SECONDS
。
一緒に集めて、
#!/bin/bash
outdir=samples
for vt in video-*.txt
do
base=${vt%.txt}
video=$base.mkv
while read ss
do
echo "ffmpeg -i '$video' -vsync 0 -ss '$ss' -t 00:00:01 '$outdir/$base-${ss//:/}-%02d.png'" >&2
ffmpeg -i "$video" -vsync 0 -ss "$ss" -t 00:00:01 "$outdir/$base-${ss//:/}-%02d.png" >> "$outdir/$base.log" 2>&1
done <"$vt"
done
私のサンプルファイルは、各サンプルポイントに対して50個のPNGファイルを生成します。
答え2
少し遅れるかもしれませんが、問題はffmpeg
標準入力でも読み取ってwhile-read-loopから読み取ったデータを消費することです。
奇妙なタイムスタンプの混乱に陥った。この問題を解決するには、ffmpeg
次の2つの方法でコマンドの標準入力をリダイレクトできます。
オプション1:コマンドラインでffmpegの後に-nostdinと入力します。
例:
while read f; do ffmpeg -nostdin -i "$f" -codec copy "${f%.*}.mp4" ; done
オプション2:ffmpegコマンドラインの最後に< /dev/nullを配置する
例:
while read f; do ffmpeg -i "$f" -codec copy "${f%.*}.mp4" < /dev/null; done
ここでこのソリューションを見つけました。