Bash - ffmpegが本文で実行されたときに行の読み取りが失敗する間

Bash - ffmpegが本文で実行されたときに行の読み取りが失敗する間

私はこれについてしばらく心配してきました。たぶんあなたの一人が私を助けてくれるでしょう。

ビデオの特定の位置からビデオフレームを抽出しようとしています。これを行うには、場所は「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

  1. 変数を使用するときは二重引用符を使用してください。例えば、

    cd "$OUTDIR"
    
  2. 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
    
  3. ../シンボリックリンクを介して処理すると、親ディレクトリの構成が中断される可能性があります。したがって、samplesディレクトリで作業するよりもそのディレクトリに移動する方がよいでしょう。

  4. 通常、変数名をすべて大文字として使用しないでください。シェル管理変数名(など)と競合する可能性が$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 

ここでこのソリューションを見つけました。

Bashループのffmpegの問題

関連情報