Trying to identify both thumbnails and timestamps of keyframes on a set of videos, I'm getting different results from ffmpeg and ffprobe.
Taking a 1 min. long video as an example:
youtube-dl -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4' 'https://www.youtube.com/watch?v=BHlAlN3z4ss' --output "test.mp4"
1/ I extract thumbnails and write on the image the timestamp at which it was extracted:
ffmpeg -i test.mp4 -q:v 2 -vf select="eq(pict_type\,PICT_TYPE_I)","drawtext=fontfile=/path/to/Arial.ttf:fontsize=45:fontcolor=yellow:box=1:boxcolor=black:x=(W-tw)/2:y=H-th-10:text='Time\: %{pts\:hms}'" -vsync 0 thumbs/preview%05d.jpg
2/ I extract and save the timestamps of all keyframes:
ffprobe -v error -skip_frame nokey -show_entries frame=pkt_pts_time -select_streams v -of csv=p=0 test.mp4 | sort -n > keyframes_timestamps.txt
3/ Comparing results, I figure ffprobe found 29 keyframes, while ffmpeg found only 32. Comparing manually, we can see that specific keyframes are not detected by ``ffprobe` while most are very similar.
ffprobe_ts ffmpeg_ts
0.000000 00:00:00.00
5.366667 00:00:05.367
7.200000 00:00:07.200
8.666667 00:00:08.667
10.100000 00:00:10.100
11.500000 00:00:11.500
14.233333 00:00:14.233
15.333333 00:00:15.333
17.366667 00:00:17.367
NO_TS 00:00:18.833
20.800000 00:00:20.800
24.533333 00:00:24.533
25.700000 00:00:25.700
26.033333 00:00:26.033
On larger videos, this happens for around less that 5% of the keyframes.
I can't find an explanation about that, does anyone have a clue ? or an advice on where/what I should inquire further ?
Thanks for your help !
Not all I-frames are keyframes. -skip_frame nokey will skip non-KF I-frames.
Related
when I use ffmpeg to convert video to images there's one problem,the total number of pictures is not equal to the number of frames.
first, I used ffprobe cmd to get the total number of frames:
ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 in.mp4
and got the number is 278
then,I used ffmpeg cmd to convert video to images:
ffmpeg -i 'in.mp4' -f image2 -qscale:v 2 'out_%05d.png'
but I got 281 pictures.
I checked out the ffmpeg's documentation but found nothing about this
so how can I solve this problem
I have confused one thing for Video Frame extraction.
I have sample.mp4 video, 15FPS.
I tried extracting Keyframe with FFMPEG.
ffmpeg -skip_frame nokey -i sample.mp4 -vsync 0 -frame_pts true out%d.png
I get 29 Pictures and I believe I have got 29 I frames. (If I have wrong, Please correct me)
Then I tried extracting specific timeline(10 sec) frame with picture
ffmpeg -i sample.mp4 -ss 00:00:10 -frames:v 1 test1.png
This output picture, I can not find same one among my Keyframes I get.
Question: test1.png, What is frame type? Does it one of P or B frame not I?
I get 29 Pictures and I believe I have got 29 I frames. (If I have wrong, Please correct me)
Correct. See Checking keyframe interval? to verify.
Question: test1.png, What is frame type? Does it one of P or B frame not I ?
Use ffprobe:
ffprobe -select_streams v -show_entries "frame=pkt_pts_time,pict_type" -of csv input.mp4
Example output:
frame,9.968000,B
frame,10.010000,B <---
frame,10.052000,P
Note that in this example -ss 00:00:10 rounded up to the nearest frame (10.010000). This is a B-frame in this example (input.mp4).
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?
I use this to get frames from video and concatenate them in one image:
ffmpeg -i output.mp4 -vf 'fps=2,tile=1000x1' out.jpg
But there is a problem: I do not know number of frames that will be fetched. Here I hardcoded tile size 1000x1, but if there will be more than 1000 frames, then will be an error. Before starting ffmpeg I do not know actual size of tile.
So I want use command like:
ffmpeg -i output.mp4 -vf 'fps=2,tile=*x1' out.jpg
That means: I want you to concatenate ALL images that will be fetched in one row, but I cannot use * as an argument for tile.
Is there some way to solve my problem?
I got an idea:
$ FRAMES=`ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 xxx.mp4`
$ FFMPEG="ffmpeg -i xxx.mp4 -vf 'fps=2,tile=\$FRAMESx1' out.jpg"
$ `echo "${FFMPEG//\$FRAMES/$FRAMES}"`
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.