Reputation: 31
I'm looking a way to extract every second (2nd) I-Frame using ffmpeg and save them as a new timelapse video.
So far I managed to save all I-frames by the following command:
ffmpeg -i $FILE -vf "select='eq(pict_type,I)',setpts=N/FRAME_RATE/TB" -r 29.97 -vcodec libx264 -b:v 62M -an ./enc/${FILE}_cnv.mp4
but I need twice less I-frames in the resulting video. For example, if the I-frames in the original video are 1-8-16-24-32-40..., I need only 1-16-32-48... Is there a way to extract them without making a temporary video with all keyframes?
Update: since no universal solution was found, I decided to cheat with: -vf "select='eq(pict_type,I)*not(mod(n,16))',setpts=N/FRAME_RATE/TB"
Upvotes: 2
Views: 3049
Reputation: 374
At first glance it seems like you should be able to use a second step in your filter to select only even or odd numbered I frames, but after testing it doesn't seem like this is actually possible.
When looking at frame numbers we have two choices: n
, which seems to be the absolute frame number, and selected_n
, which seems to be the filtered frame number. The ffmpeg filter docs are not very clear on how these values are defined.
In my tests, it looks like if you use a filter like this:
select='eq(pict_type,I)*not(mod(n,2))'
You will get the same number of frames as you would if you omitted the *not(mod(n,2))
, which I assume is because I frames will always fall on odd-numbered n
due to how the encoding works.
If we try using selected_n
instead, though, I find that only a single frame is ever selected. This makes sense because the filter can't ever match an even-number of selected frames, so it will only ever match the first one.
Given this, I think your only choice here is to use a 2-stage approach, where you use one run of ffmpeg to get your iframes, then a second run of ffmpeg to drop every other frame. Something like:
ffmpeg -i $FILE -vf "select='eq(pict_type,I)' $TMPFILE
ffmpeg -i $TMPFILE -vf "select='not(mod(n,2))' $CONVERTED_FILE
Upvotes: 1
Reputation: 93319
Tell the decoder to only emit keyframes and then select every other frame.
ffmpeg -skip_frame nokey -i $FILE -vf "select='not(mod(n\,2))',setpts=N/FRAME_RATE/TB" -c:v libx264 -b:v 62M -an ./enc/${FILE}_cnv.mp4
Upvotes: 0