☰
Ubuntu 安装新版 FFmpeg 与视频抽帧实战指南
2026/10/13 17:48:46 网站建设 项目流程

1. 为什么我不推荐直接用 apt 装 FFmpeg

如果你在 Ubuntu 上敲过sudo apt install ffmpeg,大概率已经踩过这个坑:装完之后ffmpeg -version一看,版本号停在 4.x 甚至更早,而官方早就迭代到 7.x 了。对于只是转个格式、压个视频的日常需求,老版本够用;但一旦你要做视频抽帧、逐帧图像分析、批量导出图片序列这类活儿,老版本缺新滤镜、缺新编码器、缺硬件加速支持,很多参数直接报Unrecognized option,非常难受。

我自己的经历是这样的:早些年做一个视频数据预处理的活儿,需要把一批监控视频按固定间隔抽成图片,再喂给后面的图像处理流程。当时图省事用了系统自带的 FFmpeg,结果发现-vf fps抽帧出来的时间戳对不上,某些容器格式还识别不了,折腾了大半天才意识到是版本太老。从那以后,凡是涉及抽帧、转码这类对版本敏感的任务,我一律自己编译或者用官方静态包,绝不再碰系统源里的老古董。

这篇内容就是把这套流程完整讲清楚:在 Ubuntu 上装到较新的 FFmpeg,然后用它做视频和图片之间的数据帧提取。所谓"数据帧提取",说白了就是两个方向——把视频拆成一帧一帧的图片,或者把一堆图片合成一段视频。前者用于数据分析、训练素材准备、关键帧抓取;后者用于延时摄影、GIF 制作、序列帧回放。适合做计算机视觉、视频处理、多媒体开发的读者,也适合任何需要批量处理视频帧的人。

先说结论:装 FFmpeg 有三条路——系统源、官方静态包、源码编译。我推荐官方静态包,省事、版本新、依赖干净;追求极致可控或者要特定编译选项的,再走源码编译。下面逐个拆。

2. 三条安装路线的取舍与实操

2.1 系统源安装:最省事但版本最旧

sudo apt update && sudo apt install ffmpeg这条命令谁都会敲,优点是依赖自动解决、升级跟着系统走。但 Ubuntu 的 LTS 版本为了稳定性,软件包版本往往落后官方一到两年。你在 22.04 上装到的可能是 4.4,在 24.04 上大概是 6.x,而官方稳定版已经到 7.x 了。

判断要不要用这条路,看你的需求:

  • 只是偶尔转个格式、切个片段:系统源足够。
  • 要做抽帧、滤镜链、新编码器(比如 AV1 相关):系统源大概率不够。

如果你已经装了系统源版本,想升级,先卸载干净再走下面的路线,避免路径冲突:

sudo apt remove ffmpeg sudo apt autoremove

注意:卸载时留意有没有别的软件依赖它,比如某些录屏工具、媒体播放器。apt autoremove之前先看一眼它打算删什么,别把有用的东西一起带走。

2.2 官方静态包:我平时最常用的方案

FFmpeg 官方为 Linux 提供了预编译的静态构建包,解压即用,不污染系统依赖,版本还新。这是我最推荐的路线,尤其适合不想折腾编译的人。

操作步骤:

# 下载官方静态包(以 amd64 为例,具体链接以官方发布页为准) wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz # 解压 tar -xf ffmpeg-release-amd64-static.tar.xz # 进入解压出来的目录(目录名带版本号) cd ffmpeg-*-amd64-static # 把可执行文件复制到系统路径 sudo cp ffmpeg ffprobe /usr/local/bin/ # 验证 ffmpeg -version

这里有几个细节值得说。第一,ffprobe一定要一起复制,它是查看媒体信息的工具,后面排查抽帧问题全靠它。第二,复制到/usr/local/bin而不是/usr/bin,是为了和系统包管理器管理的文件区分开,将来想删直接删这两个文件就行,不会影响 apt。第三,如果/usr/local/bin不在你的 PATH 里(极少见),需要手动加一下。

静态包的好处是它把依赖的库都打包进去了,你不需要额外装一堆libavcodec-dev之类的东西。缺点是包体积大(几十 MB),而且更新要手动重新下载。我一般会写个小脚本记录当前版本,隔几个月看一眼官方有没有新版。

2.3 源码编译:要定制就选它

什么时候需要自己编译?两种情况:一是你要启用某些默认构建没开的编码器或滤镜;二是你要针对特定硬件做优化,比如启用某个 GPU 加速模块。编译流程大致如下:

# 装编译依赖 sudo apt update sudo apt install -y build-essential yasm nasm pkg-config \ libx264-dev libx265-dev libvpx-dev libfdk-aac-dev \ libmp3lame-dev libopus-dev libass-dev # 拉源码(用官方仓库) git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg cd ffmpeg # 配置,按需裁剪 ./configure --prefix=/usr/local \ --enable-gpl --enable-libx264 --enable-libx265 \ --enable-libvpx --enable-libmp3lame --enable-libopus # 编译,-j 后面跟 CPU 核心数 make -j$(nproc) # 安装 sudo make install

./configure这一步是灵魂。--enable-gpl会启用 GPL 授权的组件(比如 x264),如果你要把产物用于闭源分发,得注意授权问题。--prefix=/usr/local让安装路径和系统隔离。编译时间取决于机器,普通四核机器大概十几分钟到半小时。

提示:编译前务必确认yasm或nasm装好了,汇编优化没它俩会直接报错。另外libfdk-aac属于非自由授权,很多发行版默认源里没有,需要额外配置源,嫌麻烦可以先用内置的 aac 编码器顶着。

三条路线对比一下:

路线版本新旧操作难度依赖干净度适合人群
系统源旧极低一般轻度使用
官方静态包新低高大多数用户
源码编译最新高可控需要定制

选哪条,取决于你对版本和可控性的要求。我个人的默认选择是静态包,省心。

3. 抽帧之前,先用 ffprobe 把视频底细摸清楚

很多人一上来就ffmpeg -i input.mp4 output_%04d.png,结果抽出来的帧要么数量不对,要么尺寸不对,要么时间间隔不是自己想要的。问题出在没先搞清楚源视频的基本参数。抽帧这件事,七分靠前期分析,三分靠命令。

3.1 ffprobe 到底看什么

ffprobe是 FFmpeg 套件里专门用来分析媒体文件的工具。拿到一个视频,我习惯先跑这条命令:

ffprobe -v error -show_format -show_streams input.mp4

输出信息很多,重点看这几项:

  • r_frame_rate:帧率,比如30000/1001表示约 29.97 fps。注意它是分数形式,别直接当整数用。
  • avg_frame_rate:平均帧率,和上面可能略有差异。
  • nb_frames:总帧数(有些容器不写这个字段,可能显示 N/A)。
  • duration:时长,单位秒。
  • width/height:分辨率。
  • codec_name:编码格式,比如 h264、hevc。
  • pix_fmt:像素格式,比如 yuv420p。

这些参数直接决定你抽帧的策略。比如帧率是 29.97,你想每秒抽一帧,就不能简单按"每 30 帧取一帧"来算,否则时间会漂移。

3.2 用一条命令拿到干净的关键字段

-show_streams输出太啰嗦,我更喜欢用-select_streams配合-show_entries精准取值:

ffprobe -v error -select_streams v:0 \ -show_entries stream=width,height,r_frame_rate,nb_frames,duration \ -of default=noprint_wrappers=1 input.mp4

输出会是这样清爽的键值对:

width=1920 height=1080 r_frame_rate=30000/1001 nb_frames=5400 duration=180.180000

v:0表示第一条视频流。有些文件有多条视频流(比如带多角度的),指定v:0能避免歧义。-of default=noprint_wrappers=1去掉多余的分隔符,方便脚本解析。

3.3 帧率是分数,别踩这个坑

r_frame_rate=30000/1001这种写法,新手容易直接当成 30000 或者 30 来用。实际上它是 29.97002997...。如果你要按时间抽帧,用fps滤镜最稳妥,它内部会正确处理分数帧率,不需要你自己算。只有在你需要精确控制"每 N 帧取一帧"时,才需要关心这个分数。

我踩过一次坑:手动按nb_frames / duration算帧率,结果因为nb_frames在某些文件里不准,算出来的值偏了,抽帧间隔全乱。后来学乖了,能用滤镜就用滤镜,别自己造轮子。

4. 视频转图片序列:抽帧的几种姿势

这是"数据帧提取"最核心的部分。抽帧不是只有一种抽法,按目的不同,至少有四种常见策略:按固定帧间隔、按固定时间间隔、只抽关键帧、按场景变化抽。下面逐个讲,并说清楚各自适合什么场景。

4.1 按固定帧间隔抽:每 N 帧取一张

命令长这样:

ffmpeg -i input.mp4 -vf "select='not(mod(n\,10))'" -vsync vfr output_%05d.png

select滤镜里的n是帧序号,mod(n,10)表示对 10 取余,not(...)取反,合起来就是"帧序号能被 10 整除的帧保留"。-vsync vfr是关键,它告诉 FFmpeg 用可变帧率输出,避免它自作主张补帧或丢帧。

这条命令适合什么场景?比如你的视频帧率很高(60fps),但后续分析不需要那么密,每 10 帧取一张就够。优点是帧序号严格对齐,方便回溯原始位置。

注意:-vsync vfr在新版本里被标记为过时,推荐用-fps_mode vfr替代。如果你用的是 7.x,建议换成新写法,老写法虽然还能用但会打警告。

4.2 按固定时间间隔抽:每秒一张

这是最常用的抽帧方式,做缩略图、做时间序列分析都用它:

ffmpeg -i input.mp4 -vf "fps=1" output_%05d.png

fps=1表示输出帧率为每秒 1 帧。想要每 2 秒一张就写fps=1/2,想要每秒 5 张就写fps=5。这个滤镜的好处是它基于时间轴工作,不管源视频帧率是多少、是不是分数帧率,它都能正确按时间间隔取帧。

为什么我推荐用fps而不是select?因为fps处理的是时间,select处理的是帧序号。当源视频帧率不规则(比如可变帧率的录屏),按帧序号抽会导致时间间隔不均匀,而按时间抽则始终均匀。做数据分析时,时间均匀性往往比帧序号对齐更重要。

4.3 只抽关键帧:又快又省空间

关键帧(I 帧)是视频里可以独立解码的帧,抽它不需要解码整段视频,速度极快:

ffmpeg -skip_frame nokey -i input.mp4 -vsync vfr -frame_pts true keyframe_%05d.png

-skip_frame nokey让解码器跳过所有非关键帧。-frame_pts true让输出文件名带上原始帧的时间戳(PTS),方便你知道每张图对应视频里的哪个时刻。

这个方式适合快速浏览视频内容、生成预览图。缺点是关键帧的间隔不固定,取决于编码时的 GOP 设置,可能几秒一个,也可能十几秒一个,不能用来做严格的时间序列。

4.4 按场景变化抽:自动抓"有内容"的帧

如果你只想抓画面发生明显变化的帧,用select配合场景分数:

ffmpeg -i input.mp4 -vf "select='gt(scene,0.3)'" -vsync vfr scene_%05d.png

scene是 FFmpeg 计算出的场景变化分数,范围 0 到 1,越大表示变化越剧烈。gt(scene,0.3)表示分数大于 0.3 的帧才保留。阈值需要根据视频内容调,动作片调高些,访谈类调低些。

这个方式特别适合从长视频里挑出"有信息量"的画面,比如从监控录像里找有人经过的片段。但阈值调不好会漏帧或抽太多,建议先用小片段试。

四种方式对比:

抽帧方式命令核心时间均匀速度典型用途
固定帧间隔select=not(mod(n,N))否中高帧率降采样
固定时间间隔fps=1是中缩略图、时序分析
只抽关键帧-skip_frame nokey否快快速预览
场景变化select=gt(scene,x)否慢内容筛选

4.5 输出格式与命名:别小看这两个细节

输出图片格式直接写扩展名就行,.png无损但体积大,.jpg有损但小。做数据分析建议 PNG,做预览图 JPG 够用。JPG 还能加质量参数:

ffmpeg -i input.mp4 -vf "fps=1" -q:v 2 output_%05d.jpg

-q:v 2控制 JPG 质量,范围 1 到 31,数字越小质量越高。2 到 5 之间是肉眼几乎无损的甜区。

命名里的%05d是占位符,表示 5 位数字、不足补零,输出就是output_00001.png、output_00002.png。位数一定要够,否则帧数超过位数会乱序。比如你有 10 万帧,用%04d就只能到 9999,后面会覆盖。我一般用%06d保险。

提示:抽帧前先确认输出目录存在且可写,FFmpeg 不会自动建目录。另外磁盘空间要留够,1080p 的 PNG 单张可能好几 MB,抽几千帧就是几十 GB。

5. 图片序列合成视频:反向操作同样有讲究

抽帧的反向操作是把一堆图片合成视频。做延时摄影、把处理后的帧重新编码回视频,都要用到它。这块的坑不比抽帧少。

5.1 基础合成命令

ffmpeg -framerate 25 -i img_%05d.png -c:v libx264 -pix_fmt yuv420p output.mp4

-framerate 25指定输入图片序列的帧率,注意这里是-framerate而不是-r,两者在图片序列输入时含义不同。-c:v libx264指定用 H.264 编码。-pix_fmt yuv420p是兼容性关键,很多播放器和平台只认这个像素格式,不加的话可能生成出来别人打不开。

5.2 图片编号不连续怎么办

如果你的图片编号有跳号(比如img_00001.png、img_00003.png),FFmpeg 默认会在缺号处停止。解决办法是用-start_number指定起始,或者干脆用 glob 模式:

ffmpeg -framerate 25 -pattern_type glob -i "*.png" -c:v libx264 -pix_fmt yuv420p output.mp4

glob 模式会按文件名排序读取所有匹配的图片,跳号也不怕。但要注意排序是字典序,img_10.png会排在img_2.png前面,所以命名时位数一定要对齐。

5.3 控制输出质量和体积

H.264 的质量用-crf控制,范围 0 到 51,越小质量越高体积越大。18 到 28 是常用区间,23 是默认值:

ffmpeg -framerate 25 -i img_%05d.png \ -c:v libx264 -crf 20 -preset medium \ -pix_fmt yuv420p output.mp4

-preset控制编码速度和压缩率的平衡,从ultrafast到veryslow。medium是默认,追求速度用fast,追求体积用slow。我一般用medium,性价比最高。

5.4 合成时帧率对不上的排查

合成出来发现视频播放速度不对,通常是帧率设置和图片数量不匹配。比如你拍了 300 张延时照片,想让它播放 10 秒,那帧率应该是 30。如果设成 25,就会播放 12 秒。这个换算很简单:帧率 = 图片数 / 期望时长。

另一个常见问题是合成后视频时长和预期差很多,多半是-framerate和-r混用了。记住:输入图片序列用-framerate,输出视频帧率用-r,两者别搞反。

6. 抽帧实战里那些文档不会写的坑

前面讲的都是"标准操作",但真正干活时,问题往往出在细节上。这一节把我这些年踩过的坑集中说一下,都是文档里不会明写、但实际会遇到的。

6.1 时间戳漂移:为什么抽出来的帧对不上时间

用select按帧序号抽帧时,输出图片的 PTS 可能和原始时间对不上,尤其是可变帧率视频。表现是:你以为第 100 张图对应第 10 秒,实际可能是第 9.8 秒。解决办法是加-frame_pts true,让文件名带上真实 PTS,或者改用fps滤镜按时间抽。

6.2 内存爆掉:长视频抽帧的正确姿势

抽一个两小时的 4K 视频,如果一次性跑,FFmpeg 可能吃满内存。原因是它默认会缓冲。解决办法是加-threads限制线程数,或者分段处理:

# 只处理前 60 秒 ffmpeg -t 60 -i input.mp4 -vf "fps=1" output_%05d.png

-t 60表示只处理 60 秒。分段处理后用脚本拼接结果,比一次性硬扛稳得多。

6.3 色彩空间转换导致的偏色

抽出来的 PNG 颜色和原视频不一样,发灰或者偏色,通常是色彩空间转换没处理好。视频一般是 YUV,PNG 是 RGB,转换时如果没指定矩阵,颜色会偏。可以显式指定:

ffmpeg -i input.mp4 -vf "fps=1,scale=in_range=full:out_range=full" output_%05d.png

in_range和out_range控制色彩范围,full表示全范围。具体用哪个取决于源视频,不确定就先抽一张对比看看。

6.4 硬件加速:能快多少,值不值得

FFmpeg 支持用 GPU 做解码和缩放,抽帧速度能快好几倍。以常见的 N 卡为例:

ffmpeg -hwaccel cuda -i input.mp4 -vf "fps=1,hwdownload,format=rgb24" output_%05d.png

-hwaccel cuda启用硬件解码,hwdownload把帧从显存拉回内存,format=rgb24转成 PNG 需要的格式。但硬件加速不是万能的:它支持的编码格式有限,某些老视频用不了;而且配置麻烦,驱动版本不对就报错。我的建议是,先用软件解码跑通流程,确认结果正确,再考虑上硬件加速。为了快几倍而引入一堆环境问题,不划算。

6.5 批量处理的脚本化思路

实际项目里很少只处理一个视频。我一般写个 shell 脚本遍历目录:

#!/bin/bash for f in *.mp4; do name="${f%.mp4}" mkdir -p "frames/$name" ffmpeg -i "$f" -vf "fps=1" "frames/$name/%05d.png" done

要点是每个视频单独建目录,避免文件名冲突。${f%.mp4}是去掉扩展名。如果视频很多,可以加-loglevel error减少输出,或者用&并行跑(注意别把 CPU 跑满)。

注意:并行跑之前先估算磁盘占用。一个视频抽几百帧,几十个视频就是几万张图,很容易把磁盘写满。我习惯先跑一个视频看单帧大小,再乘以总数估算。

7. 几个高频问题的快速定位

最后整理一份排查清单,遇到问题按这个顺序查,基本能定位到原因。

现象可能原因排查方向
抽帧数量不对帧率是分数、用了 select改用 fps 滤镜,用 ffprobe 核对帧率
图片时间对不上PTS 漂移加 -frame_pts true
颜色偏灰色彩范围不匹配显式指定 in_range/out_range
合成视频打不开像素格式不兼容加 -pix_fmt yuv420p
合成速度不对帧率设置错核对 -framerate 和图片数
内存爆掉长视频一次性处理用 -t 分段,限制线程
命令报未知参数FFmpeg 版本旧升级到新版

我个人在实际操作中的体会是,抽帧这件事最容易被低估的就是"前期分析"。花两分钟用 ffprobe 看清楚源视频的帧率、时长、编码,能省下后面半小时的调试。很多人急着敲命令,结果在参数上反复试错,反而更慢。

另外一个小技巧:抽帧前先用-t 5只处理前 5 秒,快速验证命令和输出是否符合预期,确认没问题再去掉-t跑全片。这个习惯帮我避免过好几次"跑了一小时才发现参数错了"的惨剧。视频处理是典型的"慢工出细活",前期多花点时间,后面就少返工。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询