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
Related
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.
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.
I am planning to use ffmpeg to ensure all video files uploaded to my website are encoded as mp4 h264.
Rather than automatically processing every file I would like to minimise the processing overhead by only processing those files that are not already mp4 h264. Is there an easy way to do this either with ffmpeg or with another command line utility?
Use ffprobe
Example command
$ ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 input.mp4
Result
h264
Option descriptions
-v error Omit extra information except for fatal errors.
-select_streams v:0 Select only the first video stream. Otherwise the codec_name for all other streams in the file, such as audio, will be shown as well.
-show_entries stream=codec_name Only output the codec_name instead of all stream info.
-of default=nokey=1:noprint_wrappers=1 Select the default output format style and omit the key and wrapper info. Otherwise, without these options, it will output:
[STREAM]
codec_name=h264
[/STREAM]
Also see
ffprobe documentation
FFmpeg Wiki: FFprobe Tips
Bash script example
Only re-encode if video is not H.264:
#!/bin/bash
mkdir h264vids
for f in *.mkv
do
audioformat=$(ffprobe -loglevel error -select_streams v:0 -show_entries stream=codec_name -of default=nw=1:nk=1 "$f")
if [ "$audioformat" = "h264" ]; then
ffmpeg -i "$f" -c:v copy -c:a aac -movflags +faststart h264vids/"${f%.*}.mp4"
else
ffmpeg -i "$f" -c:v libx264 -c:a aac -movflags +faststart h264vids/"${f%.*}.mp4"
fi
done
This is a simple script and will ignore additional video streams if you input has more than one.
If you pass an input file to ffmpeg, without other parameters, it will give you information about the video source:
ffmpeg -i myfile.avi
Another way would be the -identify option of mplayer, which might be slightly easier to parse. There is a wrapper script midentify which gives you even better output. See this example.
An alternative is to use ffprobe which is included with ffmpeg. The following will give the most terse output I can find from the ffmpeg tools:
ffprobe -hide_banner -stats -i myfile.avi
As llogan said above, let me simplify it for other new leaners,video265.mp4 is your video file:
ffprobe -v error -show_entries stream=codec_name video265.mp4
Result:
[STREAM]
codec_name=hevc
[/STREAM]