To work accurately, first convert the video to RAW YUV bitstream (if it is not already) by:
ffmpeg -i input.mp4 -an -vcodec rawvideo -pix_fmt yuv420p rawbitstream.yuv
Next step: The select filter takes an expression, where n is the frame number.
ffmpeg -r 2 -s WxH -i rawbitstream.yuv -filter:v select="mod(n-1\,2)" \
-c:v rawvideo -r 1 -format rawvideo -pix_fmt yuv420p -an odd.yuv
ffmpeg -r 2 -s WxH -i rawbitstream.yuv -filter:v select="not(mod(n-1\,2))" \
-c:v rawvideo -r 1 -format rawvideo -pix_fmt yuv420p -an even.yuv
To have ffmpeg not duplicate frames, you have to force half of the framerate of your input - so you set "2" as the input and "1" to the output. Don't forget to replace the WxH with the actual dimensions of your clip because the raw bitstream doesn't have a header that carries this information.
Instead of the above, another possibility would be to add the setpts filter to set new timestamps for the output. But be careful since it drops frames not accurately. Here, 25 is the actual output frame rate you want:
ffmpeg -i input.mp4 -filter:v select="mod(n-1\,2)",setpts="N/(25*TB)" \
-c:v rawvideo -r 12.5 -format rawvideo -pix_fmt yuv420p -an odd.yuv
ffmpeg -i input.mp4 -filter:v select="not(mod(n-1\,2))",setpts="N/(25*TB)" \
-c:v rawvideo -r 12.5 -format rawvideo -pix_fmt yuv420p -an even.yuv
You can of course choose another pixel format (any of ffmpeg -pix_fmts). Make sure that when reading the file you know the pixel size and pixel format:
ffmpeg -f rawvideo -s:v 1280x720 -pix_fmt yuv420p input.yuv …
Answer from slhck on Stack ExchangeI am making a script that applies a filter to every N-th frame of the video to gather some metrics about the stream.
Now I am using the framestep filter to achieve this. But I noticed that it doesn't matter if I process every 10-th or 1000-th frames, ffmpeg reads to whole file anyway, and different framestep intervals don't really have an effect on the speed of the script. There is no significant performance difference between framestep 10 or 1000.
So I am looking for a way to speed this whole thing up. I would like to make ffmpeg to skip reading all frames from the input, and only read the necessary data from the source.
How is it possible the achieve that?
FFMPEG: how to extract every 2-nd I-frame from a video file - Stack Overflow
ffmpeg - How to re-encode leaving only every Nth frame in? - Video Production Stack Exchange
php - How to skip start frames with ffmpeg - Stack Overflow
editing - How would I remove a specific pattern (say, every n-n+59th frame) of frames from a lengthy video file? - Video Production Stack Exchange
To work accurately, first convert the video to RAW YUV bitstream (if it is not already) by:
ffmpeg -i input.mp4 -an -vcodec rawvideo -pix_fmt yuv420p rawbitstream.yuv
Next step: The select filter takes an expression, where n is the frame number.
ffmpeg -r 2 -s WxH -i rawbitstream.yuv -filter:v select="mod(n-1\,2)" \
-c:v rawvideo -r 1 -format rawvideo -pix_fmt yuv420p -an odd.yuv
ffmpeg -r 2 -s WxH -i rawbitstream.yuv -filter:v select="not(mod(n-1\,2))" \
-c:v rawvideo -r 1 -format rawvideo -pix_fmt yuv420p -an even.yuv
To have ffmpeg not duplicate frames, you have to force half of the framerate of your input - so you set "2" as the input and "1" to the output. Don't forget to replace the WxH with the actual dimensions of your clip because the raw bitstream doesn't have a header that carries this information.
Instead of the above, another possibility would be to add the setpts filter to set new timestamps for the output. But be careful since it drops frames not accurately. Here, 25 is the actual output frame rate you want:
ffmpeg -i input.mp4 -filter:v select="mod(n-1\,2)",setpts="N/(25*TB)" \
-c:v rawvideo -r 12.5 -format rawvideo -pix_fmt yuv420p -an odd.yuv
ffmpeg -i input.mp4 -filter:v select="not(mod(n-1\,2))",setpts="N/(25*TB)" \
-c:v rawvideo -r 12.5 -format rawvideo -pix_fmt yuv420p -an even.yuv
You can of course choose another pixel format (any of ffmpeg -pix_fmts). Make sure that when reading the file you know the pixel size and pixel format:
ffmpeg -f rawvideo -s:v 1280x720 -pix_fmt yuv420p input.yuv …
If your ffmpeg was built with the AviSynth flag, then I believe you can pass an .avs file.
You can check by running ffmpeg and looking for --enable-avisynth in the configuration data.

If it's there you can use it like so: ffmpeg -i blahEven.avs blahEven.yuv.
Where blahEven.avs is simply:
ffvideosource("blah.yuv").SelectEven()
For odd frames, use SelectOdd().
For more advanded usage, see the SelectEvery documentation.
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
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
I ended up writing a python script to do this
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
'''
converts sequence of images to video
uses glob pattern and allows skipping images
---
uses sk-video
```> pip install sk-video```
backend uses ffmpeg or libav
TODO:
- add more input & output dict opts
For full list of flags see
- https://ffmpeg.org/ffmpeg.html
- https://trac.ffmpeg.org/wiki/Encode/H.264
Note: Not using opencv2.VideoWriter because it fails silently on Anaconda
'''
from __future__ import print_function
from __future__ import division
import argparse
import glob
import os
from imageio import imread
from skvideo.io import FFmpegWriter
from tqdm import tqdm
from pprint import pprint
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input_folder', default='.', help='path to folder containing images')
parser.add_argument('-p', '--file_pattern', default='*.png', help='Unix filename pattern. For details see https://docs.python.org/2/library/fnmatch.html')
parser.add_argument('-o', '--output_path', default='output.mp4', help='path to output video file')
parser.add_argument('-n', '--every', default=10, type=int, help='include every nth image. i.e. every==1 uses every image. every==2 takes every other image ')
parser.add_argument('-m', '--max_count', default=None, type=int, help='if > 0, cap at this many frames')
parser.add_argument('-v', '--verbose', default=False, type=int, help='dump a lot of stuff to the console')
# ffmpeg args
parser.add_argument('-r', '--fps', default='30', type=str, help='frames per second')
parser.add_argument('-vcodec', default='libx264', type=str, help='output video codec')
parser.add_argument('-b', '--bitrate', default=None, type=str, help='bitrate')
parser.add_argument('-crf', default=None, type=str, help='Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible. A lower value generally leads to higher quality, and a subjectively sane range is 17–28. Consider 17 or 18 to be visually lossless or nearly so; it should look the same or nearly the same as the input but it isnt technically lossless')
args = parser.parse_args()
#%% Get input file list
glob_path = os.path.join(os.path.expanduser(os.path.expandvars(args.input_folder)), args.file_pattern)
# read all files in folder matching pattern
print('Reading folder "{}"'.format(glob_path))
paths = sorted(glob.glob(glob_path))
args.stats = {'file_count_total' : len(paths) }
print(args.stats['file_count_total'], 'files found')
# skip
if args.every > 1:
paths = [x for i,x in enumerate(paths) if i % args.every == 0]
args.stats['file_count_with_skip'] = len(paths)
print(args.stats['file_count_with_skip'], 'files with skip')
# cap to max_count
if args.max_count:
paths = paths[:args.max_count]
args.stats['file_count_capped'] = len(paths)
print(args.stats['file_count_capped'], 'files after cap')
#%% init video writer with parameters
inputdict = {}
outputdict = {}
if args.fps:
inputdict['-r'] = args.fps
outputdict['-r'] = args.fps
if args.vcodec: outputdict['-vcodec'] = args.vcodec
if args.bitrate: outputdict['-b'] = args.bitrate
if args.crf: outputdict['-crf'] = args.crf
video_writer = FFmpegWriter(args.output_path, inputdict=inputdict, outputdict=outputdict)
#%%
print('Starting with args:')
pprint(vars(args))
for path in tqdm(paths):
img = imread(path)
if args.verbose: print('processing file', i, path)
video_writer.writeFrame(img)
print('\n-')
print('Finished. Video saved at', args.output_path)
video_writer.close()
The easiest way to do this is to submit the list of filenames you want in your video using a file - combined with using awk or sed to select specific files.
So, let's create 101 frames each with recognisable content, i.e. their frame number:
#!/bin/bash
for f in {0..100}; do
printf -v out "%04d.png" $f
magick -background red -pointsize 32 label:"$f" -gravity center -extent 100x100 $out
done
That creates 0000.png through 0100.png with 0078.png for example, looking like this:

Now we want to generate a file containing all the filenames, and massage it into a format accepted by ffmpeg:
ls ????.png | sed -e "s/^/file '/" -e "s/$/'/" > files.txt
The file files.txt looks like this:
file '0000.png'
file '0001.png'
file '0002.png'
file '0003.png'
file '0004.png'
...
...
file '0099.png'
file '0100.png'
And we can pass that to ffmpeg with:
ffmpeg -y -r 2 -f concat -i files.txt animated.gif

Now, if you want every 3rd record/frame from that file, use:
awk 'NR%3 == 0' files.txt > subset.txt
and pass the subset into ffmpeg:
ffmpeg -y -r 2 -f concat -i subset.txt animated.gif

This select filter should skip the first 5 frames:
$ ffmpeg -i input_file -vf select="gte(n\, 5)" ...
For timespan it's even easier. Here's how you skip the first 30 seconds:
$ ffmpeg -i input_file -ss 30 ...
To skip the first 30 seconds and keep the next 120 seconds of the original video, add -t:
$ ffmpeg -t input_file -ss 30 -t 120 ...
-ss time_off set the start time offset
-t duration record or transcode "duration" seconds of audio/video
Both -ss and -t take a time duration:
The following examples are all valid time duration:
5555 seconds
12:03:4512 hours, 03 minutes and 45 seconds
23.18923.189 seconds
None of these methods will be perfectly accurate due to the very mechanics of how video compression works (P and B frames can not stand on their own). ffmpeg will try to adjust accordingly:
Note that in most formats it is not possible to seek exactly, so ffmpeg will seek to the closest seek point before position. When transcoding and
-accurate_seekis enabled (the default), this extra segment between the seek point and position will be decoded and discarded. When doing stream copy or when-noaccurate_seekis used, it will be preserved.
Refer to the ffmpeg documentation for more.
ffmpeg -i {videofile} -vf select=gte(n\,{skip}) -c:v libx264 -c:a aac {outfile}
Where {skip} = number of frames to skip at atart.
Unlike previous solution, there's no loss in quality here.
Let's say I have a 50,325 frame video and I want to remove every xth frame. How would I write this? I want the end result to be a shorter video(since the frames are removed). I've only seen answers online for odd frames. Some of the answers don't shorten the video length. One issue is that the frames might not be divisible by the 'xth frame' and thus i'm not sure what to do in that case.
I have a short bit of code I want to optimize for %%f in ("*.mkv") do ( ffmpeg -v error -i "%%f" -f null - 2>error.log
It basically goes through the video and spits out any error messages to the "error.log" file. I use it as a basic file health check. However, this takes forever on longer / higher resolution videos. (My storage medium is not the bottleneck, tested on an NVME ssd and ram disks)
I'd like to process every 2 or 3 frames since 1~2 corrupted frames won't make the video unplayable. I can't figure out the best way to go about this. Any tips are welcome thanks in advance!
To speed up a video file, by removing frames, the following syntax can be used:
ffmpeg -r:v "Fin*M/1" -i input.mp4 -an -r:v "Fout/1" output.mp4
- "
Fout": This is nothing more and less than the number of FPS (Frames Per Second) you want for output.mp4, e.g. 30 FPS. - "
Fin": The number of FPS of input.mp4, for example 25 FPS. You can find this out with:ffmpeg -i input.mp4 - "
M": Multiplier, the speed factor you want, e.g. 15 to make the video 15 times faster. - "
Fin * M": So in this example, that's 25 * 15 = 375
The final result of this example is:
ffmpeg -r:v "375/1" -i input.mp4 -an -r:v "30/1" output.mp4
I had a video that was originally 16m06s long with a frame rate of 29.97, but I wanted to speed it up (by dropping frames) so that it played back at about 16x the normal speed. This is the command I used:
ffmpeg -r:v "480/1" -i input.avi -an -r:v "12/1" output.avi
When I convert png images to avi video ffmpeg is dropping frames. At the end I have over 130 dropped frames and with the 24fps video that's 5 second difference at the end. How can I prevent ffmpeg from dropping frames I tried every -vsync option and -frame_drop_threshold 0 but nothing worked. I run
ffmpeg -i ./pictures/asp_%04d.png -vsync 0/1/2/drop -r 24 video.avi ffmpeg -i ./pictures/asp_%04d.png -frame_drop_threshold 0 -r 24 video.avi
but neither worked, for -vsync 0/drop I got errors
Hi all,
Long time lurker, first time poster.
I need some help from some ffmpeg legends on this problem im having.
So I am trying to split a video at a certain second value. The thing is, the split videos when put back together is frame skipping! Expected i guess, but just want to see if anyone here has a solution to this.
Cheers!!
Using ffmpeg within nodejs script.
execSync(`ffmpeg -y -i ${inputVideo} -t 2.001 -c:v copy -an -r 30 output1.mp4`, { stdio: 'inherit' });
execSync(`ffmpeg -y -i ${inputVideo} -ss 2.001 -t 1 -c:v copy -an -r 30 output2.mp4`, { stdio: 'inherit' });
execSync(`ffmpeg -y -i ${inputVideo} -ss 3.001 -t 1 -c:v copy -an -r 30 output3.mp4`, { stdio: 'inherit' });