View Full Version : Using ffmpeg to capture frame by number
hellgauss
9th December 2023, 17:55
I need to capture several video frames as .png from .mkv files in an automated way, starting from the frame numbers.
I use the following .bat file (windows) which extracts e.g. 4 frames from a .mkv with yuv video.
set "f1=12345"
set "f2=55555"
set "f3=111111"
set "f4=141414"
ffmpeg -y -bitexact -i "myvideo.mkv" -map 0:v -vf "select='eq(n,%f1%)+eq(n,%f2%)+eq(n,%f3%)+eq(n,%f4%)'" -fps_mode passthrough -sws_flags accurate_rnd+full_chroma_int -bitexact "im_%%d.png"
The output seems to be ok for me. The problem is that ffmpeg parse (re-encode?) the whole video and it takes *a lot* of time, especially if I use the -sws_flags which I need for precision.
Is there a way to do that in a fast way without calculating the exact timestamp of the frames?
If needed I can also switch to another command-line windows utility, as long as it is well tested and the yuv-->rgb conversion is accurate.
Thanks
HG
Emulgator
10th December 2023, 23:43
You want precisely that frame you are specifying decoded, and bitexact, so ffmpeg needs to precisely parse the unknown .mkv, which can only mean indexing, searching, decoding.
Given the complexity of codecs in ffmpeg toolbox I can not imagine any fast way hoping to take shortcuts and assuming a hopefully correct frame.
Any other software would have to perform the same.
hellgauss
11th December 2023, 22:07
It is not true. Virtualdub2 opens a .mkv in less than a second, and can jump immediately to a given frame by number (just click on the "Frame" label at bottom). This is the standard way I take screenshots when I have to compare two same frames of different encodings, but I also have to copy the frame to clipboard, open an Image utility, paste and save, then open a new file in VD2. If I can do it automatically it is better, especially if I have several different files to compare.
PS: the 'bitexact' option, as a general ffmpeg flag and outside the -sws_flags parameter, is only for deterministic output (and input/decoding). It allows me to check if an option has any effect by hash, and remove any unwanted and potentially personal information from the output, such as timestamps.
Emulgator
12th December 2023, 20:15
Well, VD2 uses avlib-1.vdplugin as decoder, and behind that sits: The very same mother ffmpeg, what else. ;-)
https://forum.doom9.org/showthread.php?p=1994989#post1994989
Just no lengthy indexing, rather trusting the container- if that hits the correct frame, one may use that.
(Here it was not, BTW. DGDecoders are the gold standard here, and they do need indexing.)
BTW, if one prefers VD: no image utility needed.
VD can export frames: single, sequences, and this can be scripted.
And AvsPmod can do that too, with Macros.
hellgauss
13th December 2023, 21:04
Thanks a lot for info and tips! :thanks:
I cannot find the way to script single image export with VD2, bit i managed it with export image sequence. The following script seems to do a good job. Perhaps it needs some tweak with strange characters in file name.
It needs Virtualdub2 folder in windows PATH
set "f1=12345"
set "f2=55555"
set "f3=111111"
set "f4=141414"
set "input=test_(test)\myvideo.mkv"
call :vdgrab %input%, %f1%, 1
call :vdgrab %input%, %f2%, 2
call :vdgrab %input%, %f3%, 3
call :vdgrab %input%, %f4%, 4
pause
exit
:vdgrab
rem usage (3 params): vdgrab input_video frame_number output
rem virtualdub2 folder must be in windows PATH
rem output is without .png extension
SETLOCAL
set "input=%1"
set "input=%input:\=\\%"
set /A n1=%2
set /A n2=n1+1
(
echo VirtualDub.Open^("%input%","",0^);
echo VirtualDub.audio.SetSource^(1^);
echo VirtualDub.audio.SetMode^(0^);
echo VirtualDub.audio.SetInterleave^(1,500,1,0,0^);
echo VirtualDub.audio.SetClipMode^(1,1^);
echo VirtualDub.audio.SetEditMode^(1^);
echo VirtualDub.audio.SetConversion^(0,0,0,0,0^);
echo VirtualDub.audio.SetVolume^(^);
echo VirtualDub.audio.SetCompression^(^);
echo VirtualDub.audio.EnableFilterGraph^(0^);
echo VirtualDub.video.SetInputFormat^(0^);
echo VirtualDub.video.SetOutputFormat^(7^);
echo VirtualDub.video.SetMode^(3^);
echo VirtualDub.video.SetSmartRendering^(0^);
echo VirtualDub.video.SetPreserveEmptyFrames^(0^);
echo VirtualDub.video.SetFrameRate2^(0,0,1^);
echo VirtualDub.video.SetIVTC^(0, 0, 0, 0^);
echo VirtualDub.video.SetCompression^(^);
echo VirtualDub.SaveFormatAVI^(^);
echo VirtualDub.SaveAudioFormat^(""^);
echo VirtualDub.video.filters.BeginUpdate^(^);
echo VirtualDub.video.filters.Clear^(^);
echo VirtualDub.video.filters.EndUpdate^(^);
echo VirtualDub.audio.filters.Clear^(^);
echo VirtualDub.video.SetRangeFrames^(%n1%,%n2%^);
echo VirtualDub.project.ClearTextInfo^(^);
echo VirtualDub.SaveImageSequence2^("%3_", ".png", 1, 0, 3, 100^);
echo VirtualDub.Close^(^);
) > %1_%2_%3.tmp
start /min /wait virtualdub64.exe /x /s %1_%2_%3.tmp
rename "%3_0.png" "%3.png"
del %1_%2_%3.tmp
ENDLOCAL
exit /B 0
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.