0

I've been scratching my head over this for some time now; maybe one of you can help me.

I am trying to extract video frames from certain positions in a video. For that purpose, the locations are stored in a text file in the format 'hh:mm:ss' with one entry per line. A bash script reads those lines from the file and executes ffmpeg for each line.

The problem is that the input read from the text file changes depending on whether ffmpeg runs 'properly' or not. Without executing ffmpeg, running a different command such as ls, running ffmpeg with invalid settings (so that it immediately exits), everything works as indented. With ffmpeg, the file read from the time stamp file is invalid.

My best guess is that ffmpeg somehow messes with the environment the bash script runs in, however, OS design teaches us that this should be impossible.

Here is a minimal example and its output:

Script 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

Timestamp file test.txt:

00:00:42
00:01:20
00:02:20
00:04:20
00:05:56
00:06:40

The first ffmpeg command in the script is an incorrect command (invalid video file). If activated, the input is read correctly from 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'

After disabling the first (incorrect) ffmpeg comand and enabling the second (correct) one, the timestamps read from test.txt are incorrect:

$ ./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'

Any ideas?

  • Please would you ls -ld samples. Specifically, is it a symbolic link? – Chris Davies Jul 22 '22 at 12:52
  • Nope, it's a regular directory and all permissions are fine: "drwxr-xr-x 2 someone users 65536 Jul 22 22:53 samples" The script is run under the owner uid. – Barnoid Jul 22 '22 at 13:55

2 Answers2

0
  1. Double-quote your variables when you use them. For example,

    cd "$OUTDIR"
    
  2. ffmpeg does not like : in the output file name. So, strip them from $ss (${ss//:/}) or change them to something else (${ss//:/_}):

    ffmpeg -i "../$VIDEO" -vsync 0 -ss "$ss" -t 00:00:01 "$BASE-${ss//:/}-%02d.png" >> "$BASE.log" 2>&1 </dev/null
    
  3. The ../ parent directory construct can break if you're processing across a symbolic link. For that reason it would be better to work into the samples directory rather than from it.

  4. Generally, try to avoid variable names that are all upper-case. They can conflict with variable names managed by the shell ($PATH, $SECONDS, etc.)

Putting this all together,

#!/bin/bash
outdir=samples

for vt in video-*.txt do base=${vt%.txt} video=$base.mkv

while read ss
do
    echo &quot;ffmpeg -i '$video' -vsync 0 -ss '$ss' -t 00:00:01 '$outdir/$base-${ss//:/}-%02d.png'&quot; &gt;&amp;2
    ffmpeg -i &quot;$video&quot; -vsync 0 -ss &quot;$ss&quot; -t 00:00:01 &quot;$outdir/$base-${ss//:/}-%02d.png&quot; &gt;&gt; &quot;$outdir/$base.log&quot; 2&gt;&amp;1
done &lt;&quot;$vt&quot;

done

On my own example file this generates 50 PNG files for each sample point.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • Thanks for the tips, however, you missed the point: why is it that running ffmpeg changes the behavior of the surrounding bash script?

    To make my point, I applied your suggestions, but bash still fails to read the input properly:

    $ ./extract.sh ffmpeg -i ../test.mkv -vsync 0 -ss 00:00:30 -t 00:00:01 'test-000030-%02d.png' ffmpeg -i ../test.mkv -vsync 0 -ss 00:01:20 -t 00:00:01 'test-000120-%02d.png' ffmpeg -i ../test.mkv -vsync 0 -ss 05:56 -t 00:00:01 'test-0556-%02d.png'

    In conclusion, the problem lies somewhere else.

    – Barnoid Jul 22 '22 at 12:07
  • Very strange. My script worked for me – Chris Davies Jul 22 '22 at 12:50
  • Indeed very strange - things like that shouldn't happen. I solved the problem with a Python script for now and may investigate the bash/ffmpeg problem later if I find the time. Thanks again. – Barnoid Jul 22 '22 at 13:53
-1

Maybe a little late, but the problem with ffmpeg is that it reads from standard input as well, consuming data meant for read in the while-read-loop.

I had some strange timestamp messup. To solve this you can redirect standard input in the ffmpeg command in two ways:

Option1: put -nostdin right behind ffmpeg in the commandline

Example:

while read f; do ffmpeg -nostdin  -i "$f" -codec copy "${f%.*}.mp4" ; done

Option2: put < /dev/null at the end of the ffmpeg commandline

Example:

while read f; do ffmpeg -i "$f" -codec copy "${f%.*}.mp4" < /dev/null; done 

Found this solution here:

Problem with ffmpeg in bash loop

Peregrino69
  • 2,417
  • Does this really add something not already answered in existing answers to the already answered question? It might be harsh to flag this as low quality, but that's what I'm doing given the no-addition-of-new-info, sloppy inconsistent use of capitals in sentence, non-formatted command examples and examples not being based on original posters commands. – sampi Mar 24 '23 at 16:47