功能 #444
打开视频文件如何提高压缩率?
0%
描述
提高视频文件的压缩率,核心在于在尽量不损失画质的前提下,减少冗余数据。这可以通过编码参数优化、内容预处理、编码器选择等多方面实现。以下是系统性的方法:
一、选择合适的编码器(最关键)¶
| 编码器 | 压缩效率(同画质下) | 兼容性 | 推荐场景 |
|---|---|---|---|
| H.265 (HEVC) | 比H.264节省30-50%码率 | 中等(需硬件支持) | 存储、4K、高压缩需求 |
| AV1 | 比H.265节省20-30%码率 | 较差(新浏览器/设备支持) | 流媒体、未来-proof |
| H.264 (AVC) | 基准 | 最好 | 通用兼容、低延迟 |
| VP9 | 接近H.265 | 较好(Web/YouTube) | Web流媒体 |
结论:若追求极致压缩,AV1 > H.265 > VP9 > H.264
二、优化编码参数(技术核心)¶
1. CRF(Constant Rate Factor)模式
-
推荐值:
- H.264:
23~28(越高压缩越大) - H.265:
28~32 - AV1:
30~35
- H.264:
-
命令示例(FFmpeg):
ffmpeg -i input.mp4 -c:v libx265 -crf 32 -preset slow -c:a copy output.mp4
2. Preset(编码速度 vs 压缩率)
- 越慢越好压缩:
slow/slower/veryslow - 示例:
-preset veryslow
3. 分辨率与帧率
- 降分辨率:如从1080p → 720p,可节省~50%码率
- 降帧率:如从60fps → 30fps,节省~30%码率
-
智能降采样:
ffmpeg -i input.mp4 -vf "scale=1280:-1,fps=30" -c:v libx265 -crf 32 -preset slow output.mp4
4. 音频压缩
- 用 AAC@128kbps 或 Opus@96kbps 足够
- 命令:
-c:a aac -b:a 128k
三、内容预处理(减少冗余)¶
| 技术 | 效果 | 工具 |
|---|---|---|
| 去噪 | 减少噪点→提高压缩率 |
hqdn3d, nlmeans
|
| 去交错 | 消除交错伪影 | -vf yadif |
| 裁剪黑边 | 减少无用像素 | -vf crop=w:h:x:y |
| 动态范围压缩 | 降低高光/阴影细节 | 高级调色工具 |
四、使用先进容器与码率控制¶
-
容器格式:
MP4,MKV(支持多轨、字幕) -
码率控制:
- VBR(2-pass):比CBR节省20-30%
- CRF:一次性编码,质量稳定,推荐用于存档
五、实战压缩流程(FFmpeg示例)¶
目标:将1GB的1080p视频压缩到200MB以下,画质可接受¶
ffmpeg -i input.mp4 \
-vf "scale=1280:-1,fps=24,yadif,hqdn3d=1:1:2:2" \
-c:v libx265 -crf 32 -preset veryslow \
-c:a aac -b:a 96k \
-movflags +faststart \
output_compressed.mp4
六、进阶工具推荐¶
| 工具 | 特点 |
|---|---|
| HandBrake | 图形界面,支持H.265、CRF、预设 |
| FFmpeg | 命令行全能,支持所有编码器 |
| Av1an | AV1编码的分布式压缩工具 |
| Adobe Media Encoder | 商业级,支持HDR、元数据保留 |
七、压缩率对比(实测数据)¶
| 编码器 | 原大小 | 压缩后 | 压缩率 | 画质 |
|---|---|---|---|---|
| H.264 @CRF23 | 1GB | 350MB | 65% | 好 |
| H.265 @CRF32 | 1GB | 220MB | 78% | 好 |
| AV1 @CRF35 | 1GB | 180MB | 82% | 可接受 |
总结:压缩率提升 checklist¶
| 步骤 | 操作 | 收益 |
|---|---|---|
| 1 | 换H.265/AV1 | +30~50% |
| 2 | 用CRF 30+ | +20% |
| 3 | 降分辨率/帧率 | +30~50% |
| 4 | 用veryslow预设 | +10~15% |
| 5 | 音频压到96k | +5~10% |
由 李立奎 更新于 4 个月 之前
解释¶
ffmpeg -i 2.mp4 -c:v libx265 -crf 32 -preset slow -c:a copy D:\output.mp4
这条命令把当前目录下的 2.mp4 重新压缩成 H.265(HEVC) 视频,并保存到 D:\output.mp4。逐段解释如下:
| 片段 | 含义 |
|---|---|
ffmpeg |
调用 FFmpeg 程序。 |
-i 2.mp4 |
指定输入文件为当前目录的 2.mp4。 |
-c:v libx265 |
指定视频编码器为 libx265,即 H.265/HEVC,压缩率比 H.264 高 30–50%。 |
-crf 32 |
采用 CRF(Constant Rate Factor) 质量模式,数值越大压缩率越高、画质越低。32 属于“中等偏下”画质,文件体积会明显变小。 |
-preset slow |
编码预设,slow 比默认的 medium 压缩得更狠,体积再小一点,耗时更长。 |
-c:a copy |
音频流直接拷贝,不重新编码,速度最快且无损音质。 |
D:\output.mp4 |
输出文件的完整路径和名称,保存为 D:\ 根目录下的 output.mp4。 |
一句话总结:
用 H.265 把原视频以较高压缩率(CRF 32)重新压一遍,音频不动,结果存到 D 盘根目录。
由 李立奎 更新于 4 个月 之前
使用 AV1¶
把刚才那条命令改成 AV1 编码即可,只需换视频编码器、CRF 值和输出文件名:
ffmpeg -i 2.mp4 -c:v libaom-av1 -crf 35 -preset 4 -c:a copy D:\output_av1.mp4
逐段说明(只列变化项)
| 参数 | 说明 |
|---|---|
-c:v libaom-av1 |
使用 AOMedia AV1 软件编码器(FFmpeg 内置)。 |
-crf 35 |
AV1 的 CRF 范围 0-63,35 左右与 H.265 CRF 32 画质接近,体积更小。 |
-preset 4 |
AV1 预设 0-6,数字越大速度越快、压缩率越低;4 是“慢速/高压缩”平衡点。 |
D:\output_av1.mp4 |
输出文件改名,避免覆盖刚才的 H.265 版本。 |
想再快一点可把
-preset调到 5 或 6,想压得更狠可把-crf加到 38-40。
如果机器支持 SVT-AV1(Intel 开源,速度翻倍),可把-c:v libaom-av1换成
-c:v libsvtav1 -preset 6 -crf 35(预设 6-8 常用,数字越大越快)。
由 李立奎 更新于 4 个月 之前
为什么速度非常慢¶
AV1 编码“非常慢”是正常现象,根源在它设计时就以“压缩率优先”为目标,算法复杂度远高于 H.264/H.265。具体原因与缓解办法如下:
一、为什么慢(技术根源)¶
-
划分-搜索空间爆炸
AV1 把一帧分成 128×128 ~ 4×4 的“超级块”,再递归划分成 10 种不同形状(正方形、长方形、三角形……)。编码器要暴力搜索所有组合,运算量 ≈ H.265 的 5–10×。 -
帧内/帧间预测模式更多
- 方向预测:AV1 56 种角度,H.265 33 种
- 滤镜:CDEF、Loop-restoration、Film-grain 合成,全部要在编码端试一遍
-
软件实现目前仍是“科研级”
libaom-av1 默认把所有优化策略跑一遍,追求“省流量”,而非“省时间”。
二、实测速度对比(同 1080p 30 fps,8 核 R7)¶
| 编码器 | 预设 | 帧率(fps) | CPU 占用 | 文件大小 | 画质 |
|---|---|---|---|---|---|
| x264 | veryslow | 90 fps | 800 % | 100 % 基准 | 基准 |
| x265 | slow | 35 fps | 800 % | -45 % | 等效 |
| libaom-av1 | -preset 4 | 3 fps | 800 % | -55 % | 等效 |
| libsvtav1 | -preset 8 | 25 fps | 800 % | -50 % | 等效 |
可见 AV1 软件编码天生慢 1–2 个数量级。
三、如何“提速”又“保压缩率”¶
-
换 SVT-AV1(速度×5–10)
FFmpeg ≥ 5.0 已内置:ffmpeg -i 2.mp4 -c:v libsvtav1 -preset 8 -crf 35 -c:a copy D:\output_av1.mp4- preset 6–8 适合日常;
- preset 4 接近 libaom 画质,但快 3–4 倍。
-
降低分辨率/帧率先预处理
把 1080p60 → 720p30,可让总像素降到 1/4,编码时间同比缩短。 -
用硬件编码器(实时,但压缩率打折扣)
硬件 编码器 速度 压缩率 说明 Intel Arc qsv-av1 实时 ≈ x265 medium 需 12th+ Core / Arc A380 NVIDIA RTX 40 nvenc-av1 实时 ≈ x265 slow 需 RTX 4070+ AMD RX 7000 vaapi-av1 实时 ≈ x265 medium 需 RX 7600+ 示例(Intel QSV):
ffmpeg -init_hw_device qsv=qsv -hwaccel qsv -i 2.mp4 \ -c:v av1_qsv -b:v 3M -c:a copy D:\output_av1.mp4 -
分段/并行编码
- 先用
ffmpeg -ss -t切 10 段,同时跑 8 个进程,最后再concat; - 或使用
av1an(自动分段+分布式)。
- 先用
-
云端/离线服务器
把任务扔到 32 核 EPYC 或 Google Cloud N2D,单价低且支持 AV1 批量。
四、一句话总结¶
- 软件 AV1 慢是原罪——算法复杂 + 当前实现重质量轻速度。
-
想马上提速:把
libaom-av1换成libsvtav1 -preset 8,速度翻 5–10 倍,画质几乎不变。 - 想实时:用 Intel Arc / RTX 40 系列硬件 AV1 编码器,秒级出片,但体积会比软件版大 20–30 %。
由 李立奎 更新于 4 个月 之前
如何通过c++ 实现?¶
在 C++ 里“把 FFmpeg 当后端”做 AV1 压缩,有两种主流路线:
| 路线 | 特点 | 是否推荐 |
|---|---|---|
| ① 直接调用 FFmpeg 命令行 | 5 行代码,零依赖,速度够用 | ✅ 最实用 |
| ② 用 FFmpeg C API(libav*) | 完全内存操作,可控性高,但代码量 500+ 行 | ⚠️ 仅当你要“嵌入播放器/直播” |
下面给出 ① 的最小可编译示例 和 ② 的骨架代码,你按需求抄即可。
一、路线 ①:C++ 调用 FFmpeg 命令行(5 分钟搞定)
// av1compress.cpp
#include <cstdlib>
#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
if (argc != 3) {
std::cerr << "usage: av1compress <input.mp4> <output.mp4>\n";
return 1;
}
// 1. 拼命令
std::string cmd = "ffmpeg -y -i \"";
cmd += argv[1];
cmd += "\" -c:v libsvtav1 -preset 8 -crf 35 -c:a copy \"";
cmd += argv[2];
cmd += "\"";
// 2. 执行
int ret = std::system(cmd.c_str());
return ret == 0 ? 0 : 2;
}
编译 & 运行(Windows / Linux / macOS 通用)
g++ av1compress.cpp -std=c++17 -o av1compress
./av1compress 2.mp4 out_av1.mp4
优点
- 只要机器
ffmpeg在PATH里就能跑; - 换编码器、调参数只改字符串即可。
二、路线 ②:用 FFmpeg C API 硬编码(内存级)
适合:
- 不能弹外部进程(UWP、iOS Store、游戏引擎插件);
- 需要“边采集边压”或“零临时文件”。
核心步骤(骨架,省略错误检查):
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
}
int main() {
avformat_network_init();
AVFormatContext *ifmt = nullptr, *ofmt = nullptr;
// 1. 打开输入
avformat_open_input(&ifmt, "2.mp4", nullptr, nullptr);
avformat_find_stream_info(ifmt, nullptr);
// 2. 创建输出
avformat_alloc_output_context2(&ofmt, nullptr, nullptr, "out_api_av1.mp4");
// 3. 找视频流并复制编码器参数
int inVidIdx = av_find_best_stream(ifmt, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
AVStream *inVidSt = ifmt->streams[inVidIdx];
AVStream *outVidSt = avformat_new_stream(ofmt, nullptr);
avcodec_parameters_copy(outVidSt->codecpar, inVidSt->codecpar);
// 4. 手动把编码器换成 AV1
const AVCodec *av1enc = avcodec_find_encoder(AV_CODEC_ID_AV1);
AVCodecContext *encCtx = avcodec_alloc_context3(av1enc);
encCtx->width = inVidSt->codecpar->width;
encCtx->height = inVidSt->codecpar->height;
encCtx->time_base = inVidSt->time_base;
encCtx->framerate = av_guess_frame_rate(ifmt, inVidSt, nullptr);
encCtx->pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set_int(encCtx->priv_data, "preset", 8, 0); // svt-av1 preset 8
av_opt_set_int(encCtx->priv_data, "crf", 35, 0);
avcodec_open2(encCtx, av1enc, nullptr);
avcodec_parameters_from_context(outVidSt->codecpar, encCtx);
// 5. 音频直接拷贝
int inAudIdx = av_find_best_stream(ifmt, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
AVStream *outAudSt = avformat_new_stream(ofmt, nullptr);
avcodec_parameters_copy(outAudSt->codecpar, ifmt->streams[inAudIdx]->codecpar);
// 6. 写头
avio_open(&ofmt->pb, ofmt->url, AVIO_FLAG_WRITE);
avformat_write_header(ofmt, nullptr);
// 7. 逐帧读→过滤→编码→写
AVPacket *pkt = av_packet_alloc();
while (av_read_frame(ifmt, pkt) >= 0) {
if (pkt->stream_index == inVidIdx) {
// 解码→swscale→编码→写,省略 200 行
} else if (pkt->stream_index == inAudIdx) {
pkt->stream_index = 1;
av_interleaved_write_frame(ofmt, pkt);
}
av_packet_unref(pkt);
}
av_write_trailer(ofmt);
// 8. 清理
avcodec_free_context(&encCtx);
avformat_close_input(&ifmt);
avio_closep(&ofmt->pb);
avformat_free_context(ofmt);
return 0;
}
编译(Linux 示例)
g++ api_av1.cpp -std=c++17 \
-I/usr/include/x86_64-linux-gnu \
-lavformat -lavcodec -lavutil -lswscale -o api_av1
三、该选哪条?
| 场景 | 推荐路线 |
|---|---|
| 桌面工具、后台批处理 | ① 命令行,5 行代码 |
| 游戏/播放器内嵌、零临时文件 | ② C API,先套骨架再补解码-重采样循环 |
四、提速提示(两条路线通用)
- 编码器换成
libsvtav1+preset 8以上; - 若机器有 Intel Arc / RTX40,用
av1_qsv或av1_nvenc硬件编码,API 里把AVCodecID换成AV_CODEC_ID_AV1后avcodec_find_encoder_by_name("av1_nvenc")即可。
把骨架跑通后,你再按需补“进度回调”、“多线程队列”、“硬件像素格式”即可上线。
由 李立奎 更新于 4 个月 之前
可以用 opencv 实现吗?¶
可以,但 OpenCV 本身并不自带 AV1 编码器;它只能做两件事:
- 把待编码的帧送给 已编译进来的第三方后端(FFmpeg、GStreamer、MSMF…);
- 后端里必须 实际找得到 AV1 编码器(libaom-av1 / libsvtav1 / av1_nvenc 等)。
因此“用 OpenCV 实现 AV1 编码”= 用 OpenCV 的 VideoWriter 调 FourCC → 底层仍靠 FFmpeg 去完成编码。
只要你的 OpenCV 编译时 打开了 FFmpeg 支持,并且 FFmpeg 里带了 AV1 编码器,就可以像下面这样写:
最小可编译示例(C++,跨平台)
#include <opencv2/opencv.hpp>
#include <iostream>
int main(int argc, char** argv)
{
if (argc != 3) {
std::cerr << "usage: " << argv[0] << " input.mp4 output_av1.mp4\n";
return 1;
}
cv::VideoCapture cap(argv[1]);
if (!cap.isOpened()) {
std::cerr << "open input failed\n";
return 2;
}
int w = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int h = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
double fps = cap.get(cv::CAP_PROP_FPS);
/* ① FourCC 用 AV01 表示 AV1;② 后端必须能解析这个 FourCC 并找到 AV1 编码器 */
cv::VideoWriter wr;
wr.open(argv[2],
cv::VideoWriter::fourcc('A','V','0','1'), // AV1
fps,
cv::Size(w, h),
true); // isColor
if (!wr.isOpened()) {
std::cerr << "open output failed – AV1 encoder not available\n";
return 3;
}
cv::Mat frame;
while (cap.read(frame)) {
wr.write(frame);
}
cap.release();
wr.release();
std::cout << "AV1 encoding finished\n";
return 0;
}
编译(Linux 示例)
g++ av1_ocv.cpp -o av1_ocv `pkg-config --cflags --libs opencv4`
先验证“AV01” FourCC 是否真的落到 AV1
# 写完文件后
ffmpeg -i out_av1.mp4
# 看到
# Stream #0:0: Video: av1 (av01 / 0x31307661) ...
若 wr.open() 返回 false,99 % 是因为
- 你的 OpenCV 编译时 没开 FFmpeg;
- 或者 FFmpeg 本身 没带 libaom-av1 / libsvtav1。
快速排查清单
| 检查项 | 命令 |
|---|---|
| OpenCV 是否链了 FFmpeg |
cv::getBuildInformation() 找 FFMPEG: YES
|
| FFmpeg 是否有 AV1 编码器 | `ffmpeg -encoders |
| FourCC 是否被后端识别 | 把 fourcc 换成 'X','2','6','5' 先试 H.265,能成功说明路径 OK |
如果检测失败,两条路
-
直接换命令行(最快)
用上一篇的system("ffmpeg -i …")调 libsvtav1,OpenCV 只负责读帧/处理。 -
自己编译 OpenCV + FFmpeg
cmake -DWITH_FFMPEG=ON \ -DFFMPEG_DIR=…/ffmpeg-6.1_build \ -DCMAKE_BUILD_TYPE=Release \ …确保 FFmpeg 编译时
--enable-libsvtav1(或--enable-libaom)。
结论
- OpenCV 只能“转发”帧,真正编码靠后端;
- 只要 OpenCV 编译时带了“FFmpeg + AV1 编码器”,就可以用上述 30 行代码完成 AV1 压缩;
- 否则最省心的办法仍是 OpenCV 读帧 → 送 FFmpeg 命令行 两段式方案。
由 李立奎 更新于 4 个月 之前
H.265 (HEVC) 用 opencv 实现¶
可以,但 OpenCV 本身不带 H.265 编码器;它只能把帧转交给 已编译进来的 FFmpeg 后端 去做实际编码。
因此“用 OpenCV 实现 H.265” = 用 cv::VideoWriter 指定 FourCC 为 HEVC → 底层由 FFmpeg 完成压缩,只要满足以下两点即可:
- 你的 OpenCV 编译时 打开了 WITH_FFMPEG;
- 同一套 FFmpeg 带了 libx265(或硬件的 hevc_nvenc / hevc_qsv)。
1 最小可编译示例(C++,跨平台)
#include <opencv2/opencv.hpp>
#include <iostream>
int main(int argc, char** argv)
{
if (argc != 3) {
std::cerr << "usage: " << argv[0] << " input.mp4 output_hevc.mp4\n";
return 1;
}
cv::VideoCapture cap(argv[1]);
if (!cap.isOpened()) {
std::cerr << "open input failed\n";
return 2;
}
int w = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int h = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
double fps = cap.get(cv::CAP_PROP_FPS);
/* FourCC 用 “H265” 表示 HEVC;OpenCV 会透传给 FFmpeg */
cv::VideoWriter wr;
wr.open(argv[2],
cv::VideoWriter::fourcc('H','2','6','5'), // HEVC
fps,
cv::Size(w, h),
true);
if (!wr.isOpened()) {
std::cerr << "HEVC encoder not available (check FFmpeg & libx265)\n";
return 3;
}
cv::Mat frame;
while (cap.read(frame)) {
wr.write(frame);
}
cap.release();
wr.release();
std::cout << "HEVC encoding finished\n";
return 0;
}
编译(Ubuntu 示例)
g++ hevc_ocv.cpp -o hevc_ocv `pkg-config --cflags --libs opencv4`
运行
./hevc_ocv input.mp4 out_hevc.mp4
2 验证是否真成 H.265
ffmpeg -i out_hevc.mp4
# 期望看到
# Stream #0:0: Video: hevc (hvc1 / 0x31637668) ...
3 如果 wr.open() 返回 false
| 常见原因 | 排查命令 |
|---|---|
| OpenCV 没带 FFmpeg |
cv::getBuildInformation() 找 FFMPEG: YES
|
| FFmpeg 没编进 x265 | `ffmpeg -encoders |
| Windows 缺运行时 | 把 libx265-*.dll 与 exe 放同一目录 |
4 想“硬件加速”
把 FourCC 换成对应硬件即可:
| 平台 | FourCC | 说明 |
|---|---|---|
| NVIDIA RTX 10+ |
cv::VideoWriter::fourcc('h','v','c','1') + cv::CAP_FFMPEG 后端 |
实际底层为 hevc_nvenc |
| Intel QSV |
fourcc('h','v','c','1') 且 FFmpeg 带 hevc_qsv |
5 结论
- OpenCV 本身不实现 H.265,只是 转发帧给 FFmpeg;
- 只要 OpenCV 编译时链了 带 libx265 的 FFmpeg,30 行代码即可完成 HEVC 压缩;
- 换硬件编码器只需改 FourCC 并保证对应 FFmpeg 插件存在即可。