I would like to display the number of frames in an MP4 video. It appears that the duration of a video is stored in seconds as a floating point value. I see there is a $FrameCount tag, but it does not appear in the MP4 files I have. There is $VideoFrameRate, so I could calculate that by multiplying that with $Duration. This is where I get stuck.
I've read the Advanced Formatting part of the docs, but can't figure out how to multiply the two tags inside a -printConv. I figure this is a pretty basic operation, so I must be missing something obvious.
I've tried variations like '${Duration;$Duration*$VideoFrameRate}' and '${Duration;GetValue("Duration")*GetValue("VideoFrameRate")}' with no success. However, even a dummy operation like '${Duration;$_+1000}' does not produce expected output:
$ exiftool -m -n -p '$filename ${Duration;$_+1000}' 0501-*
0501-093939.mp4 2.112
0501-173812.mp4 5.005
0501-175421.mp4 5
0501-233527.mp4 5.005
What am I missing here? Ultimately, my goal is to have exiftool generate a list of filenames of videos that are within a range of frames (1828±2 frames, in my specific case).
A quick look over the Tags Names pages (https://exiftool.org/TagNames/), it looks like the
FrameCount is specific to some cameras and not a general tag for videos.
Quote from: traycer on May 21, 2023, 06:01:23 AMI've read the Advanced Formatting part of the docs, but can't figure out how to multiply the two tags inside a -printConv. I figure this is a pretty basic operation, so I must be missing something obvious.
It is not. You have to call directly into the exiftool functions directly. The construct would along these lines
${TAGNAME;$_=<somecalculation>+$self->GetValue('OTHERTAG')}I haven't tested it, but I think you want this
${VideoFrameRate;$_*=$self->GetValue('Duration','ValueConv')}The
*= operator takes the current value of
VideoFrameRate and multiplies it by what follows.
For the
Duration, in order to get the raw number, you need to include the
'ValueConv'I do something similar in a command I use with downloaded videos. The
FileModifyDate ends up as the time the video ended and I want it to be when the video started.
"-FileModifyDate<${FileModifyDate;ShiftTime('-0:0:'.$self->GetValue('Duration','ValueConv'))}"
Yikes! Yeah, I would not have figured that one out on my own. ;D This does indeed work, and is much faster than the ffprobe loop I was using before.
$ exiftool -m -n -p '$FileName $FileSize $Duration $VideoFrameRate ${VideoFrameRate;$_*=$self->GetValue("Duration","ValueConv")}' *
0501-093434.mp4 675237120 61.061 29.97002997003 1830
0501-093535.mp4 675237120 61.0943666666667 29.97002997003 1831
0501-093636.mp4 675237120 61.061 29.97002997003 1830
0501-093737.mp4 675237120 60.8941666666667 29.97002997003 1825
0501-093838.mp4 675237120 61.061 29.97002997003 1830
0501-093939.mp4 22827237 2.1021 29.97002997003 63
0501-173812.mp4 482816500 48.1481 29.97002997003 1443
[...]
Apparently the number of frames is stored in the metadata of an MPEG-4 file, at least in a way that ffmpeg can retrieve it . But that metadata does not exist for MKV containers, while exiftool can still calculate it, so I'll stick with this.
Thanks again for your great insight into some of the trickier aspects of exiftool!
$ for i in *.mp4 ; do ffprobe -v error -select_streams v -show_entries stream=nb_frames $i ; done
[STREAM]
nb_frames=1830
[/STREAM]
[STREAM]
nb_frames=1831
[/STREAM]
[STREAM]
nb_frames=1830
[/STREAM]
[STREAM]
nb_frames=1825
[/STREAM]
[STREAM]
nb_frames=1830
[/STREAM]
[STREAM]
nb_frames=63
[/STREAM]
[STREAM]
nb_frames=1443
[/STREAM]
[...]
Quote from: traycer on May 21, 2023, 07:42:03 PMThis does indeed work, and is much faster than the ffprobe loop I was using before.
...
Apparently the number of frames is stored in the metadata of an MPEG-4 file, at least in a way that ffmpeg can retrieve it .
Yes, exiftool does not read the streams to make sure of the number of frames, just the header. So sometimes exiftool's results can be inaccurate. See this GitHub post (https://github.com/exiftool/exiftool/issues/160#issuecomment-1293946184)
If you need complete accuracy, then you have to use ffprobe, as that actually reads the streams.