项目

一般

简介

行为

功能 #444

打开

视频文件如何提高压缩率?

李立奎4 个月 之前添加. 更新于 4 个月 之前.

状态:
新建
优先级:
普通
指派给:
-
开始日期:
2025-09-24
计划完成日期:
% 完成:

0%

预期时间:
#2:

描述

提高视频文件的压缩率,核心在于在尽量不损失画质的前提下,减少冗余数据。这可以通过编码参数优化、内容预处理、编码器选择等多方面实现。以下是系统性的方法:


一、选择合适的编码器(最关键)

编码器 压缩效率(同画质下) 兼容性 推荐场景
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
  • 命令示例(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@128kbpsOpus@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%
行为 #1

李立奎 更新于 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 盘根目录。

行为 #2

李立奎 更新于 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 常用,数字越大越快)。

行为 #3

李立奎 更新于 4 个月 之前

为什么速度非常慢

AV1 编码“非常慢”是正常现象,根源在它设计时就以“压缩率优先”为目标,算法复杂度远高于 H.264/H.265。具体原因与缓解办法如下:


一、为什么慢(技术根源)

  1. 划分-搜索空间爆炸
    AV1 把一帧分成 128×128 ~ 4×4 的“超级块”,再递归划分成 10 种不同形状(正方形、长方形、三角形……)。编码器要暴力搜索所有组合,运算量 ≈ H.265 的 5–10×。

  2. 帧内/帧间预测模式更多

    • 方向预测:AV1 56 种角度,H.265 33 种
    • 滤镜:CDEF、Loop-restoration、Film-grain 合成,全部要在编码端试一遍
  3. 软件实现目前仍是“科研级”
    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 个数量级


三、如何“提速”又“保压缩率”

  1. 换 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 倍。
  2. 降低分辨率/帧率先预处理
    把 1080p60 → 720p30,可让总像素降到 1/4,编码时间同比缩短。

  3. 用硬件编码器(实时,但压缩率打折扣)

    硬件 编码器 速度 压缩率 说明
    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
    
  4. 分段/并行编码

    • 先用 ffmpeg -ss -t 切 10 段,同时跑 8 个进程,最后再 concat
    • 或使用 av1an(自动分段+分布式)。
  5. 云端/离线服务器
    把任务扔到 32 核 EPYC 或 Google Cloud N2D,单价低且支持 AV1 批量。


四、一句话总结

  • 软件 AV1 慢是原罪——算法复杂 + 当前实现重质量轻速度。
  • 想马上提速:把 libaom-av1 换成 libsvtav1 -preset 8,速度翻 5–10 倍,画质几乎不变。
  • 想实时:用 Intel Arc / RTX 40 系列硬件 AV1 编码器,秒级出片,但体积会比软件版大 20–30 %。
行为 #4

李立奎 更新于 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

优点

  • 只要机器 ffmpegPATH 里就能跑;
  • 换编码器、调参数只改字符串即可。

二、路线 ②:用 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,先套骨架再补解码-重采样循环

四、提速提示(两条路线通用)

  1. 编码器换成 libsvtav1 + preset 8 以上;
  2. 若机器有 Intel Arc / RTX40,用 av1_qsvav1_nvenc 硬件编码,API 里把 AVCodecID 换成 AV_CODEC_ID_AV1avcodec_find_encoder_by_name("av1_nvenc") 即可。

把骨架跑通后,你再按需补“进度回调”、“多线程队列”、“硬件像素格式”即可上线。

行为 #5

李立奎 更新于 4 个月 之前

可以用 opencv 实现吗?

可以,但 OpenCV 本身并不自带 AV1 编码器;它只能做两件事:

  1. 把待编码的帧送给 已编译进来的第三方后端(FFmpeg、GStreamer、MSMF…);
  2. 后端里必须 实际找得到 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

如果检测失败,两条路

  1. 直接换命令行(最快)
    用上一篇的 system("ffmpeg -i …") 调 libsvtav1,OpenCV 只负责读帧/处理。

  2. 自己编译 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 命令行 两段式方案。
行为 #6

李立奎 更新于 4 个月 之前

H.265 (HEVC) 用 opencv 实现

可以,但 OpenCV 本身不带 H.265 编码器;它只能把帧转交给 已编译进来的 FFmpeg 后端 去做实际编码。
因此“用 OpenCV 实现 H.265” = 用 cv::VideoWriter 指定 FourCC 为 HEVC → 底层由 FFmpeg 完成压缩,只要满足以下两点即可:

  1. 你的 OpenCV 编译时 打开了 WITH_FFMPEG
  2. 同一套 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 插件存在即可。
行为

导出 Atom PDF