使用ffmpeg获取帧数

188

有人知道如何使用ffmpeg从视频文件中提取总帧数吗? ffmpeg的渲染输出显示当前帧,我需要帧数来计算百分比进度。


1
http://superuser.com/questions/84631/how-do-i-get-the-number-of-frames-in-a-video-on-the-linux-command-line - Ciro Santilli OurBigBook.com
17个回答

289

ffprobe

ffprobe -v error -select_streams v:0 -count_packets \
    -show_entries stream=nb_read_packets -of csv=p=0 input.mp4

实际上这个计数的是数据包而不是帧,但速度更快。结果应该是一样的。如果你想通过计算帧数来验证,请将-count_packets更改为-count_frames并将nb_read_packets更改为nb_read_frames

ffprobe选项的含义

  • -v error 这会隐藏“info”输出(版本信息等),这使得解析更容易(但如果您请求帮助,则会隐藏重要信息使得难度增加)。

  • -count_frames 计算每个流的数据包数量并在相应的流部分中报告它。

  • -select_streams v:0 只选择第一个视频流。

  • -show_entries stream=nb_read_packets 只显示nb_read_frames的条目。

  • -of csv=p=0 设置输出格式。在这种情况下,它隐藏了描述并仅显示值。有关其他格式(包括JSON)的信息,请参见FFprobe Writers

仅计算关键帧

请参见Checking keyframe interval?

MP4编辑列表

在MP4/M4V/M4A/MOV中存在编辑列表可能会影响帧数。

另请参阅


mediainfo

众所周知的mediainfo工具可以输出帧数:

mediainfo --Output="Video;%FrameCount%" input.avi

MP4Box

用于处理 MP4/M4V/M4A 文件。

MP4Box 可以显示 gpac 中的帧数:

MP4Box -info input.mp4

请参考输出中与所询问的视频流相关的媒体信息行:

Media Info: Language "Undetermined (und)" - Type "vide:avc1" - 2525 samples

在这个例子中,视频流有2525个帧。


boxdumper

适用于MP4/M4V/M4A/MOV文件。

boxdumper是来自l-smash的一个简单工具。它将输出大量信息。在stsz样本大小框部分下,参考sample_count以获取帧数。在这个例子中,输入有1900个视频帧:

boxdumper input.mp4
  ...
  [stsz: Sample Size Box]
    position = 342641
    size = 7620
    version = 0
    flags = 0x000000
    sample_size = 0 (variable)
    sample_count = 1900
  • 请注意,一个文件可能有多个stsz原子。

7
如果你想要更快的速度,并且nb_frames足够可靠,可以简化为:ffprobe -v error -select_streams v:0 -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1 input.mkv - juanitogan
这个程序对我输出了两次答案(即2600 \n 2600)。有什么特别的原因吗? - jbodily
@jbodily 是指我的例子还是juanitogan的例子?我都无法使用复制。这儿没有太多可以操作的。 - llogan
1
+1,这个回答不仅仅是因为它涉及编程,更重要的是,与其他关于命令行工具的回答不同,这个回答实际上解释了所有的命令行选项。谢谢。 - Ray
1
请注意,第一个选项查询容器时,由于count_frames的存在,实际上会处理文件。请参考@juanitogan的评论。 - aggieNick02
显示剩余2条评论

35

在Unix中,这个代码运行得非常好:

ffmpeg -i 00000.avi -vcodec copy -acodec copy -f null /dev/null 2>&1 \
                                          | grep 'frame=' | cut -f 2 -d ' '

4
非常好的建议。你不需要复制音频流,可以使用 -an 参数来代替。 - rekire
1
@PrakharMohanSrivastava 请查看此答案 - Antonio
13
实际上,这个命令似乎快速可靠:ffmpeg -i 00000.avi -map 0:v:0 -c copy -f null -y /dev/null 2>&1 | grep -Eo 'frame= *[0-9]+ *' | grep -Eo '[0-9]+' | tail -1 - Timothy Zorn
1
@Michael 谢谢你让我在早晨喝咖啡时微笑 :-) - Lloyd Moore
1
@TimothyZorn 你让我开心极了! - Mladen Danic
显示剩余4条评论

15

基于时间进行计算。

这是我和许多人都在使用的方法,效果非常好。首先,在以下代码片段中找到视频的长度:

Seems stream 0 codec frame rate differs from container frame rate: 5994.00 
(5994/1) -> 29.97 (30000/1001)
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Users/stu/Movies/District9.mov':
  Duration: 00:02:32.20, start: 0.000000, bitrate: 9808 kb/s
    Stream #0.0(eng): Video: h264, yuv420p, 1920x1056, 29.97tbr, 2997tbn, 5994tbc
    Stream #0.1(eng): Audio: aac, 44100 Hz, 2 channels, s16
    Stream #0.2(eng): Data: tmcd / 0x64636D74

你应该能够持续并安全地找到 Duration: hh:mm:ss.nn来确定源视频剪辑的大小。然后,对于每个更新行(CR,无LF),您可以解析文本以查找其所在的当前时间标记:

frame=   84 fps= 18 q=10.0 size=       5kB time=1.68 bitrate=  26.1kbits/s    
frame=   90 fps= 17 q=10.0 size=       6kB time=1.92 bitrate=  23.8kbits/s    
frame=   94 fps= 16 q=10.0 size=     232kB time=2.08 bitrate= 913.0kbits/s    

只要注意不要总是期望从这些状态行中得到完美的输出即可。它们可能包含像这里一样的错误消息:

frame=   24 fps= 24 q=-1.0 size=       0kB time=1.42 bitrate=   0.3kbits/s    
frame=   41 fps= 26 q=-1.0 size=       0kB time=2.41 bitrate=   0.2kbits/s    
[h264 @ 0x1013000]Cannot parallelize deblocking type 1, decoding such frames in
sequential order
frame=   49 fps= 24 q=26.0 size=       4kB time=0.28 bitrate= 118.1kbits/s    
frame=   56 fps= 22 q=23.0 size=       4kB time=0.56 bitrate=  62.9kbits/s    

一旦你有了时间,这是简单的数学问题:时间÷持续时间×100%=完成率


1
不好意思,我可能有点傻,但是当持续时间以hh:mm:ss.nn格式给出,而时间始终以xx.yy格式给出时,我该如何计算时间/持续时间? - Omar Ali
3
@Omar,作为一名.NET开发者,我的做法是创建一个TimeSpan,然后使用"currentDurationTimeSpan.Ticks / (totalDurationTimeSpan.Ticks / 100)"。TimeSpan还提供了强大的解析函数,请查看这里 - Shimmy Weitzhandler
优秀的解决方案,我的时间格式是hh:mm:ss:ms,所以我认为在这三年中FFMPEG改进了输出时间格式。 - ElektroStudios
1
请注意,控制台输出可能会显示29.97,但这是30000/1001的简写。同样,23.98是24000/1001,而59.94是60000/1001。 - llogan
3
作为一条注释,这对于可变帧率视频是不起作用的(显然)。 - Timothy Zorn
我正在尝试匹配VirtualDub的帧计数,其为178253。23.976fps 02:03:54.71持续时间。我接近了但从未完全匹配成功! 178254.7852147852是我能够达到的最接近结果。有人知道如何获得那个帧计数吗? - David Graham

15

由于我的评论得到了一些赞,我觉得我会把它作为答案留下来:

ffmpeg -i 00000.avi -map 0:v:0 -c copy -f null -y /dev/null 2>&1 | grep -Eo 'frame= *[0-9]+ *' | grep -Eo '[0-9]+' | tail -1

这应该很快,因为没有进行编码。 ffmpeg 只需解复用文件并尽可能快地读取(解码)第一个视频流。第一个 grep 命令将获取显示帧的文本。第二个 grep 命令将仅获取其中的数字。tail 命令将只显示最后一行(最终帧数)。


13

您可以使用 ffprobe 命令获取帧数,以下是两种方法:

  1. 第一种方法

ffprobe.exe -i video_name -print_format json -loglevel fatal -show_streams -count_frames -select_streams v

该命令用来以 json 格式输出数据

select_streams v 选项告诉 ffprobe 只输出 video 流的数据,如果去掉此选项,将同时输出 audio 的信息。

输出结果如下:

{
    "streams": [
        {
            "index": 0,
            "codec_name": "mpeg4",
            "codec_long_name": "MPEG-4 part 2",
            "profile": "Simple Profile",
            "codec_type": "video",
            "codec_time_base": "1/25",
            "codec_tag_string": "mp4v",
            "codec_tag": "0x7634706d",
            "width": 640,
            "height": 480,
            "coded_width": 640,
            "coded_height": 480,
            "has_b_frames": 1,
            "sample_aspect_ratio": "1:1",
            "display_aspect_ratio": "4:3",
            "pix_fmt": "yuv420p",
            "level": 1,
            "chroma_location": "left",
            "refs": 1,
            "quarter_sample": "0",
            "divx_packed": "0",
            "r_frame_rate": "10/1",
            "avg_frame_rate": "10/1",
            "time_base": "1/3000",
            "start_pts": 0,
            "start_time": "0:00:00.000000",
            "duration_ts": 256500,
            "duration": "0:01:25.500000",
            "bit_rate": "261.816000 Kbit/s",
            "nb_frames": "855",
            "nb_read_frames": "855",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0
            },
            "tags": {
                "creation_time": "2005-10-17 22:54:33",
                "language": "eng",
                "handler_name": "Apple Video Media Handler",
                "encoder": "3ivx D4 4.5.1"
            }
        }
    ]
}
ffprobe -v error -show_format -show_streams video_name

如果您希望获取流数据,可以使用以下命令。如果您需要选择性信息,例如帧率,请使用此命令。

ffprobe -v error -select_streams v:0 -show_entries stream=avg_frame_rate -of default=noprint_wrappers=1:nokey=1 video_name

根据您的视频信息提供一个数字,但使用此方法时可能会得到 N/A 作为输出。

有关更多信息,请查看此页面:FFProbe技巧


10

并不是所有格式都存储它们的帧数或总时长 - 即使存储了,文件也可能不完整 - 因此默认情况下 ffmpeg 检测这两个值时不会准确。

相反地,尝试跳到文件末尾并读取时间,然后边读边计算当前时间。

或者,您可以尝试使用 AVFormatContext->nb_index_entries 或检测到的时长,在至少未损坏的 AVI/MOV 上应该可以正常工作,或者使用 FFMS2 库,但进度条可能会过慢而不值得使用。


9
尝试类似以下的内容:
ffmpeg -i "path to file" -f null /dev/null

它将帧编号写入stderr,因此您可以从中检索最后一帧。

5
抱歉回答有些迟了,但或许您需要这个(因为我没有找到最近的ffmpeg版本的解决方案)。
我发现在ffmpeg 3.3.4中可以使用以下命令查找:
ffprobe -i video.mp4 -show_streams -hide_banner | grep "nb_frames"

最终输出的是帧数。这个针对带有音频的视频也适用。虽然会给出两行"nb_frames"的输出,但第一行是我测试过的视频中实际的帧数。


感谢@acidrums4。我已验证此方法可与我今天从GitHub构建的最新版本一起使用。 - Paul J
使用 ffprobe -i video.mp4 -show_streams -hide_banner | grep "nb_frames" | head -n1 | cut -d"=" -f2 对我很有效,它可以将输出减少到只有数字。 - jgraup
谢谢,这个可行。如果有音频流和视频流,则会显示两个数字,一个用于音频,一个用于视频。 - Jacko

5

试试这个:

ffmpeg -i "path to file" -f null /dev/null 2>&1 | grep 'frame=' | cut -f 2 -d ' '

无法与 *.ts 文件一起使用。输出为空行。 - Victor Polevoy

3
我唯一能做到的精确翻译是以下内容:

我唯一能做到的精确翻译是以下内容:

ffprobe -i my_video.mp4 -show_frames 2>&1|grep -c '^\[FRAME'

为了确保这个功能可以与视频一起使用:
ffprobe -i my_video.mp4 -show_frames 2>&1 | grep -c media_type=video

我给你的答案点了个赞,但只有在视频没有音频的情况下才能起作用。如果视频包含音频,可以试试这个命令:ffprobe -i my_video.mp4 -show_frames 2>&1 | grep -c media_type=video - Gobe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接