How do I capture all keyframes and scale down to 320px wide? - ffmpeg

I'm trying to use ffmpeg to output all key-frames from a video file and scale them down to 320px wide while maintaining aspect ratio.
I know I could do this with two separate commands but I am trying to find a way to do it tidily in one.
I've already succeeded in each of the steps individually using the following commands.
Output the keyframes:
.\ffmpeg -i input.mp4 -q:v 2 -vf select="eq(pict_type\,PICT_TYPE_I)" -vsync 0 thumb%07d.png
Scale images:
.\ffmpeg -i input.mp4 -vf scale=320:-1 thumb%07d.png
I won't share everything i've tried, but here's three failures at combining them.
//fail, not just keyframes, scaled
.\ffmpeg -i input.mp4 -q:v 2 -vf select="eq(pict_type\,PICT_TYPE_I)" -vsync 0 -vf scale=320:-1 thumb%07d.png -hide_banner
//fail, can't find suitable output format for scale command, invalid argument
.\ffmpeg -i input.mp4 -q:v 2 -vf select="eq(pict_type\,PICT_TYPE_I)" -vsync 0, scale=320:-1 thumb%07d.png -hide_banner
//fail
.\ffmpeg -i input.mp4 -q:v 2 -vf scale=320:-1, -vf select="eq(pict_type\,PICT_TYPE_I)" -vsync 0 thumb%07d.png -hide_banner
I've tried many different things, moving commands, combining using commas etc... But I have not had any success at combining the get key-frames and scale commands.
So how would I go about combining the get key-frames and scale commands so that it works?
thanks.

The select and scale filters here make for a linear sequence of filters, so the are to be specified one after the other. See http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction
So, you can use
ffmpeg -i in -vf "select='eq(pict_type\,PICT_TYPE_I)',scale=320:-1" -vsync 0 -q:v 2 out%07d.png
but the below command will be quicker, as it drops non-keyframes at the decoding stage.
ffmpeg -skip_frame nokey -i in -vf "scale=320:-1" -vsync 0 -q:v 2 out%07d.png

Related

Fastest way to output frames between 34885 and 34891 as images in ffmpeg?

I am trying to extract frames between a certain number as images
Currently I am doing it like this
ffmpeg
-i input.mp4
-vf select='eq(n\,34885)+eq(n\,34886)+eq(n\,34887)+eq(n\,34888)+eq(n\,34889)+eq(n\,34890)+eq(n\,34891)'
-vsync 0 output_frames_%d.png
Not only this the command above very cumbersome but also takes a lot of time to run, is there an easier and faster way to do this?
You may try this
ffmpeg -i input.mp4 -vf "select='between(n\,34885\,34891)'" -vsync 0 -start_number 34885 out-%02d.png
You could try this:
ffmpeg -i input.mp4 -vf select='between(n\,34885\,34891)' -vsync passthrough -frames 7 frame_%d.png
It's less cumbersome and stops once it has hit the target number of frames (7).

I want to pipe these two ffmpeg commands for to convert a video to grayscale frames

Please, I want to pipe these two commands.
ffmpeg -i input.flv -vf fps=1 out%d.png | ffmpeg -i input -vf format=gray output
If you just need frames, try this:
ffmpeg -i input.flv -r 1 -pix_fmt gray out%d.png
There is no need to call it twice
-r sets the output frame rate (1 frame/sec) dropping excess frames
pix_fmt sets the output pixel format
[edit]
Try this to output both grayscale video and images:
ffmpeg -i input.flv \
-filter_complex format=gray,split[v0][v1]
-map [v0] -r 1 out%d.png
-map [v1] output.mp4

Extract Less Keyframes (ffmpeg)

I'm trying to extract keyframes from a large video I have. The problem I'm seeing is that it is extracting far too many, leaving me with many very similar images.
Below is what I am currently using (from terminal)
ffmpeg -i video.mov -vf "select=eq(pict_type\,I)" -vsync vfr thumb%04d.png -hide_banner
What would be great is if there was a way I can either make it only output 1 in 5 keyframes. Or what would be even better is if there is a way I can make it only output if the frame is over x% different from the previous one.
1 in 5 keyframes:
ffmpeg -i video.mov -vf "select=eq(pict_type\,I),select='not(mod(n\,5))'" -vsync vfr thumb%04d.png
frame is over x% different from the previous one:
ffmpeg -i video.mov -vf "select=eq(pict_type\,I),select='gt(scene\,x/100)'" -vsync vfr thumb%04d.png

Trying to limit output of ffmpeg

I have the following command line:
ffmpeg -hide_banner -ss 5 -i test.mp4 -y -vf
"select='eq(pict_type\,PICT_TYPE_I)',
mpdecimate,showinfo,scale=320:240,tile=12x25" -vsync 2 out%%03d.png
As you can see, I make a mosaic of 12x25 (=300) tiles per output image. But I'd like to cap the output to a single image.
Is there a way to have ffmpeg stop processing the video after it found 300 frames?
Additionally, when grabbin the I-frames, is there a way to just keep 1/x for example
After playing with different options, I couldn't find any way to do this.
Use
ffmpeg -hide_banner -ss 5 -skip_frame nokey -i test.mp4 -y -vf "framestep=7,mpdecimate,showinfo,scale=320:240,tile=12x25" -vsync 0 -vframes 1 out.png
framestep value sets x in 1/x. You probably don't need mpdecimate if you're skipping x-1 keyframes. I've added -skip_frame nokey to avoid using the select filter. This method is much faster.

Quicky output selection of video frames using ffmpeg

I would like to extract, say, 10 video frames from a given video file using ffmpeg, ideally uniformly distributed throughout the video. I know this can be done in a few ways, for example
ffmpeg -i input.mp4 -vf fps=1/10 out%03d.jpg
will output one image every 10 seconds. However, this is too slow for my liking and scales proportionally with the length of the video. I have read a bit about ffmpeg's seeking capability, for example
ffmpeg -ss 00:00:05 -i input.mp4 -frames:v 1 out001.jpg
will very quickly seek to the 5th second of the video and extract one frame. However I haven't come across a way to seek to multiple locations in the video without calling the above command repeatedly at various times.
Is there a quicker way of accomplishing this?
Using a long command, this can be done
e.g.
ffmpeg -ss 00:00:05 -i input.mp4
-ss 00:01:05 -i input.mp4
-ss 00:03:05 -i input.mp4
-ss 00:40:05 -i input.mp4
-map 0:v -frames:v 1 out001.jpg
-map 1:v -frames:v 1 out002.jpg
-map 2:v -frames:v 1 out003.jpg
-map 3:v -frames:v 1 out004.jpg

Resources