Why does doubling the frame rate generate inconsistent frames? - ffmpeg

So I have a constant 59.94 fps (i.e. 60000/1001) video and when I attempt to extract one second:
ffmpeg -y -i input60fps.avi -ss 0 -t 1 -c:v huffyuv -an output60fps.avi
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ./output60fps.avi
it returns 1.001 seconds, which I now understand is appropriate since it is the multiple of the video's frame time (60000/1001 seconds) nearest to the requested duration.
Repeating this experiment on a 59.94 fps video that was previously converted from a 29.97 fps source, I'd expect similar results, but I'm seeing that's not actually the case:
ffmpeg -i input30fps.avi -vcodec huffyuv -r 60000/1001 -an output60fps.avi
ffmpeg -i ./output60fps.avi -ss 0 -t 1 -c:v huffyuv -an test.avi
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ./test.avi
This actually results in 0.984317 seconds.
Why is this behavior different? Is there some way for me to control how the total frames get distributed evenly such that each boundary occurs every 60000/1001 milliseconds? My expectation here is that since I'm just doubling the frame rate, I should expect a frame every 1001/60000 seconds.

AVI is a variable frame rate muxer. In addition to -r, you need to enable constant frame rate so
ffmpeg -i input30fps.avi -vcodec huffyuv -r 60000/1001 -vsync cfr -an output60fps.avi

Related

FFMPEG changing output framerate issues

I want to take the input, blend N frames, decimate the other frames and use those for the output with the fps of my choice.
I used this line:
ffmpeg -y -i input.mp4 -vf tmix=frames=15:weights="1",select='not(mod(n\,15))' -vsync vfr frames/output-%05d.tif
That generated images, which I combined into the video. So far, so good.
But I'd like to skip the image output and go straight to video, so I tried this:
ffmpeg -y -i input.mp4 -vf tmix=frames=15:weights="1",select='not(mod(n\,15))' -vsync vfr -r 30 -c:v prores_ks -profile:v 3 -vendor apl0 -bits_per_mb 8000 -pix_fmt yuv422p10le output.mov
That produces 1.62 fps video, instead of 30 fps.
I'm at a loss on how to get it to output 30fps without the intermediate step of outputting images.
Thanks
I think the simplest way to achieve this is to feed the input at the 15-times the desired rate and drop all intermediate frames with -r 30:
ffmpeg -y -r 450 -i input.mp4 \
-vf tmix=frames=15:weights="1" \
-r 30 sandbox/out.mp4
However, a tmix solution is somewhat inefficient for your use case because it's mixing for all frames, including those dropped. If you don't mind a longer expression, you can try:
ffmpeg -i in.mp4 \
-vf
setpts=\'floor(N/15)/(30*TB)\',select=\'mod(n,15)+1\':n=15[v0][v1][v2][v3][v4][v5][v6][v7][v8][v9][v10][v11][v12][v13][v14];\
[v0][v1][v2][v3][v4][v5][v6][v7][v8][v9][v10][v11][v12][v13][v14]mix=inputs=15:weights=1 \
-r 30 sandbox/out.mp4
[edit] setpts expression should be floor(N/15)/(30*TB) not mod(n,15)+1 for 15 successive frames to have the same pts.

Convert a file to match another's specification exactly with FFMPEG?

I have a series of files from the same source and so of the exact same format in every way that I'm concatenating with FFMPEG
file1.mov
file2.mov
file3.mov
This is very fast and is working fine however no I want to take optional intro file (from many different source and of many different types) and convert that to match the others before joining.
intro.mp4
How do I do this with FFMPEG?
Does this give me everything I need?
ffprobe -select_streams a:0 -show_entries \
stream=codec_name,channels -of default=nw=1:nk=1 -v 0 ./file1.mov
ffprobe -select_streams v:0 -show_entries \
stream=codec_name,width,height,r_frame_rate,pix_fmt \
-of default=nw=1:nk=1 -v 0 ./file1.mov
So with that I can just:
ffmpeg -i intro.mp4 \
-c:v h264 -s 1280x720 -pix_fmt yuv420p -framerate 30/1 \
-c:a pcm_s16le -ca 1 intro.mov
and then merge it seamlessly to the rest?
ffmpeg -f concat -safe 0 -i videos.txt -c copy merged.mov -y
The answer is of course "no", hence the request for your support.
The audio is fine when files 1, 2 & 3 are merged but is too fast when the intro + 1, 2 & 3 are merged. The converted intro file always plays fine on it's own after the conversion and after the merge, but the others play audio too fast after the merge.
What am I missing?
UPDATE:
So in the end this worked for the intro:
ffmpeg -i intro.mp4 \
-c:v h264 -s 1280x720 -pix_fmt yuv420p -framerate 30 \
-c:a pcm_s16le -ac 1 -b:a 512k -ar 32000 intro.mov -y
I suspect intro.mp4 has higher audio sampling rate than the rest, and -f concat is setting the virtual input file's audio sampling rate to that of the intro.mov and running with it.
To fix this run your probe again and check the audio sampling rate (I can't remember off the top of head what it's called in the output, but could be "sample_rate"). Let this number of fs then transcode intro.mp4
ffmpeg -i intro.mp4 \
-c:v h264 -s 1280x720 -pix_fmt yuv420p -framerate 30/1 \
-c:a pcm_s16le -ar fs -ca 1 intro.mov
Replace fs with the rate you found. If it just played faster, all the other audio formats are compatible.

how to make ffmpeg output frames at correct rate down to millisecond

using ffmpeg, i am fetching frames from udp stream (hd264 at 25 fps) using following command:
ffmpeg -loglevel debug -i udp://231.20.20.146:2005 -fflags nobuffer -r 1 -preset ultrafast -vf scale=432:243 -f image2pipe -vcodec ppm pipe:1
at the other end of the pipe i am running a very simple binary which outputs the time at which it receives a frame.
however, even though i specify fps 1, frames enter the pipe with some delay, with 100-200ms delay.
what causes this delay? is it the decoding of the frames and encoding of ppm image? and how can i force ffmpeg to send images at least at the correct distance between each other, so that each frame would arrive exactly 1000ms after previous frame?
ps. the first 6 frames are buffered and enter the pipe almost at once. here is an example of recorded ms values :
5350
5368
5385
5493
5599
5676
5785
6221
7243
8235
9218
10219
11227
12268
13268
14242
15288
16219
17297
18222
19284
20272
closest to solution was using fps in video filter:
ffmpeg -analyzeduration 100000 -i udp://231.20.20.146:2005 -r 8 -fflags nobuffer -preset ultrafast -vf "scale=432:243, fps=8, realtime" -f image2pipe -vcodec ppm pipe:1
there still are some occurences with 1-3 ms off, but that might be due to cpu scaling or converting stream to ppm..

ffpmeg vs ffprobe performance

I wanted to try extracting frames at scene changes with ffmpeg, vs. getting the frame numbers with ffprobe and extracting them later.
But I had a surprise: ffprobe seems to be much slower than ffmpeg, while ffmpeg is taking the frames, resizing and saving them as well.
ffmpeg command line:
ffmpeg -hide_banner -y -i d:/test/m/long.mkv -vf "select=gt(scene\,0.4), showinfo, scale=320:-1, tile=12x200" -vsync 0 thumbnails%03d.png
this takes: 488 seconds
ffprobe command line:
ffprobe -show_frames -of compact=p=0 -f lavfi "movie=/test/m/long.mkv,select=gt(scene\,.4)"
this takes: 899 seconds
I am missing something?

ffmpeg convertation image<->video causes artefacts

I want to convert video to images, do some image processing and convert images back to video.
Here is my commands:
./ffmpeg -r 30 -i $VIDEO_NAME "image%d.png"
./ffmpeg -r 30 -y -i "image%d.png" output.mpg
But in output.mpg video I have some artefacts like in jpeg.
Also I don't know how to detrmine fps, I set fps=30 (-r 30).
When I use above first command without -r it produces a lot of images > 1kk, but than I use -r 30 option it produce same number of images as this command calculationg number of frames:
FRAME_COUNT=`./ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 $VIDEO_NAME`
So my questions are:
How to determine frame rate ?
How to convert images to video and don't reduce initial quality?
UPDATE:
Seems this helped, after I removed -r option
Image sequence to video quality
so resulting command is :
./ffmpeg -y -i "image%d.png" -vcodec mpeg4 -b $BITRATE output_$BITRATE.avi
but I'm still not sure how to select bitrate.
How can I see bitrate of original .mp4 file?
You can use the qscale parameter instead of bitrate e.g.
ffmpeg -y -i "image%d.png" -vcodec mpeg4 -q:v 1 output_1.avi
q:v is short for qscale:v. 1 may produce too large files. 4-6 is a decent range to use.

Resources