2024年,将相册里的视频压缩为AV1编码

@Ta 02-07 12:14 6664点击

手机相册里拍摄的视频越来越多,占用了大量储存空间。为了释放空间,我将拍摄的所有视频压缩为AV1编码,同时尽可能避免视频中的信息损失。在此简要记录过程。


我选择使用libsvtav1软件编码器进行AV1编码,因为:

  • 大部分设备和软件都已经支持解码AV1视频;
  • libaomlibrav1eAV1编码器速度太慢;
  • 我没有设备支持硬件AV1编码,而libsvtav1软件编码器的速度已经可以接受:在我的笔记本(CPU:AMD R7 6800H)上,使用中等预设编码1080P视频,平均速度可达18.7fps;
  • 同等耗时和画质,libsvtav1的码率明显小于libx264libx265

音频使用libopus编码器,这是普遍认为音质最佳的有损编码器,并且受大部分设备和软件支持。

我没有启用硬件加速编解码,因为我感觉AMD的集显很不可靠(在我的电脑上,有时硬解播放2K H.264的视频,画面都可能出现马赛克/花屏;软解正常)。


我使用的转码脚本(Windows 命令提示符):

for %%i in (*.mp4) do ffmpeg -hide_banner -benchmark -i "%%i" -c:v libsvtav1 -crf 31 -preset 4 -pix_fmt yuv420p10le -svtav1-params tune=0:keyint=10s:film-grain=8 -c:a libopus -b:a 128k -movflags use_metadata_tags+faststart -map_metadata 0 "%%~ni.mkv"

使用方法:在配置好FFmpeg环境变量的情况下,将上方的代码保存为cmd格式,放入所有视频文件所在的文件夹并执行。

使用此脚本压缩我拍摄的所有视频,最后统计得出:文件体积累计减小了63.5%


下面是参数的详细解析:

for %%i in (*.mp4) do

遍历当前文件夹内的所有mp4格式视频。

-crf 31

默认值:35;取值区间:[1, 63];值越大,码率越低。

为了减少压缩带来的信息损失,我选择的值是31。

-preset 4

默认值:10;取值区间:[-1, 13];值越大,编码速度越快,但画质越低,码率越高。

对于视频压缩,可取的值是4和6。低于4的值太慢,高于6的值损失较大。为了更好的画质和更小的体积,我选择的是4。

6相比4禁用了这五个特性:Filter intraGlobal motion compensationWedge predictionDifference-weighted predictionDistance-weighted prediction;但收益是:6的编码速度大约是4的2.5倍。

-pix_fmt yuv420p10le

如果不填此项,则像素格式会尽可能保持与原视频一致。yuv420p10le意味着强制将输出视频的像素格式设为10bit的yuv420p,这样做是因为根据文档和实测,即使原视频像素格式是8bit,输出为10bit相比输出为8bit能得到更好的色彩品质和更高的VMAF指标,同时文件体积只会增大约5%。

tune=0

可选择的值有:[0 = 优化视觉质量, 1 = 优化PSNR指标, 2 = 优化SSIM指标],默认值为1。我选择的值是0。

keyint=10s

指定关键帧距离。如果不填,默认值为“大约5秒”。更长的关键帧距离可以提高压缩率,所以我选择10秒。

film-grain=8

根据文档:

众所周知,与胶片颗粒(或 CCD 噪声)相关的随机图案很难压缩。当原始源中存在颗粒时,通过删除胶片颗粒并将其替换为不占用文件中大量空间的合成颗粒,可以显着提高效率。启用film-grain后,SVT-AV1 对图像进行降噪,将生成的图像与原始图像进行比较,并分析颗粒的性质。然后它会插入具有类似特性的合成颗粒。这可以极大地提高效率,同时保留视觉质量和特征。

去除颗粒的过程有时会删除原始图像中非常精细的细节,因此不应过度使用。传递给film-grain参数的级别控制该过程的使用程度。一般来说,film-grain对于具有正常颗粒量的实景视频来说,8 左右的级别就足够了。噪声较大的视频受益于 10-15 范围内的较高级别。 2-D 动画通常具有较少的颗粒,因此 4 左右的级别适用于标准(手绘)动画。颗粒动画可以受益于高达 10 左右的更高级别。

由于我压缩的视频都是通过手机拍摄的,通常存在噪点,所以启用此功能。实测此功能能明显降低码率,同时保持良好的画质,但会减慢编码速度。

-c:a libopus -b:a 128k

对于立体声音频,编码为opus格式、码率128kbps足以达到感知上无损。

-movflags use_metadata_tags+faststart -map_metadata 0

保留原视频中的元数据(比如拍摄时间、机型、地理位置);以及优化视频文件,使视频可以被快速加载。


参考资料:
https://gitlab.com/AOMediaCodec/SVT-AV1
https://trac.ffmpeg.org/wiki/Encode/AV1
https://ottverse.com/analysis-of-svt-av1-presets-and-crf-values/
https://gist.github.com/mrintrepide/b3009f5d0f08d437ebbb4c17cbf36e18

回复列表(21|隐藏机器人聊天)
  • @Ta / 02-07 13:51 / /

    @tasy5kg,以前我转 AVIF 照片时,意外发现,像素格式是 12bit 的话,体积会比 8bit、10bit 下降很多。。

    但是 Windows 只支持 8bit 的。。

  • @Ta / 02-07 14:49 / /
    @无名啊,我手机拍了很多动态照片(jpg格式但包含一小段视频),如果当做正常图片压缩,视频会丢失,所以暂时舍不得压缩
  • @Ta / 02-07 16:17 / /

    @tasy5kg,我的意思是,AVIF 也是 AV1 编码的。

    说不定你转视频时,也用 12bit,会发现体积又瞬间小一些了呢?(好像有 20% ?)

  • @Ta / 02-07 16:18 / /

    @tasy5kg,另外,好像 webp、HEIF、AVIF,都支持动图?

  • @Ta / 02-07 16:28 / /
    @无名啊,你可以用K40开启动态照片拍照试试,拍出来是一个大分辨率的图像+较低分辨率的视频
  • @Ta / 02-07 18:55 / /

    @tasy5kg,诶,K40 不是天生支持 HEIF 吗?直接用这个,压缩率就能挺高了的吧?

    另外,尝试 12bit 了吗?体积有进一步下降吗?

  • @Ta / 02-08 17:11 / /
    @无名啊,HEIF不能与动态照片同时开启。libsvtav1只支持yuv420p和yuv420p10le像素格式
  • @Ta / 02-08 22:13 / /

    @tasy5kg维基说:

    Motion JPEG(……)是一种影像压缩格式,其中每一帧图像都分别使用JPEG编码。

    看来那个动态照片,就是一堆 jpg 绑在一起。。怪不得尺寸这么大。。

    我找了半个钟,没找到怎么转成视频的办法。。

  • @Ta / 02-08 23:00 / /

    @tasy5kg,看了看文档,libsvtav1 确实只支持 8/10 bit。。

    但 avifenc 支持 12 bit。我搜索论坛里的 mp4 文件,选了其中三个,试了试转成动图,确实 12 bit 能比 8 bit 再小 20% 左右。

    image.png(13.77 KB)

    转码脚本(Windows 下可用 600KB 的 busybox-w32 来运行,要求 PATH 处能找到 ffmpegavifenc

    speed=5
    quality=50
    
    for file in *.mp4; do
        for depth in 8 10 12; do
            ffmpeg -v quiet -i "$file" -strict -1 -pix_fmt "yuv420p$( [[ $depth == 8 ]] && echo '' || echo "${depth}le" )" -f yuv4mpegpipe - |
            avifenc -q "$quality" -s "$speed" --min 0 --max 63 --stdin "${depth}bit_${speed}s_${quality}q_${file%.*}.avif"
        done
    done
    

    文件所在帖子:

  • @Ta / 02-08 23:10 / /

    @tasy5kg,对了,我有一些 HEVC 视频(假设都是 30 fps,1h 的视频),但仅开头处有 1 个 I 帧,其余全是 B 帧,导致无法随意拖动进度条。

    你知道,如何每 10 秒,重编码一个 B 帧为 I 帧(仅需重编码 360 帧 ),而不是全部重编码(要重编码 10.8W 帧),来修复视频吗?

  • @Ta / 02-09 05:25 / /
    @无名啊,回复8楼,我研究了下,目前动态照片各个厂商的具体实现细节略有差异,但没有用 Motion JPEG做的,都是单帧jpg+mp4文件拼接(h264或h265均有)。

    小米和谷歌的动态照片的文件结构是,前面的字节流是正常的一帧的jpg高清照片,尾部直接拼接一个mp4文件,mp4文件的偏移地址在jpg文件的元数据里保存;

    三星的动态照片的文件结构是,一个正常的一帧jpg高清照片,在照片的元数据内,嵌入了一个mp4视频二进制。

    这里有一个bash脚本,它直接查找文件中mp4格式头标识"ftypmp42",提取动态照片中的mp4视频:https://github.com/keith-turner/motion-photos/blob/master/mvimg_jpg_extract.sh
  • @Ta / 02-16 17:49 / /
    想要释放空间不如上传网盘,上传速度和网盘的空间,都比花时间做压缩要好。
    IP地址:火星
  • @Ta / 02-16 19:50 / /
    @小候鸟,你在使用什么网盘储存自己拍摄的照片和视频
  • @Ta / 02-16 21:05 / /
    被锁定
    层主 @滴的里滴 于 2024-03-02 09:07 删除了该楼层。
  • @Ta / 02-16 21:48 / /

    @滴的里滴,使用 -c:v copy 时,无法精确地控制剪辑时间点

  • @Ta / 02-16 22:15 / /
    被锁定
    层主 @滴的里滴 于 2024-03-02 09:07 删除了该楼层。
  • @Ta / 02-17 19:06 / /
    @tasy5kg,百度网盘和阿里云盘
    IP地址:火星
  • @Ta / 02-19 12:00 / /

    在我的电脑上,有时硬解播放2K H.264的视频,画面都可能出现马赛克/花屏

    如果是播放网络视频出现这问题,是Chrome和EDGE的Bug,用英伟达显卡有时也会这样。
    可能和微软正在修改的MediaFoundation功能有关。

  • @Ta / 02-19 12:04 / /

    使用 -c:v copy 时,无法精确地控制剪辑时间点

    在不重编码的情况下,只能在关键帧(I帧)处进行切分。非关键帧(P帧、B帧)需要使用帧间预测,需要I帧的存在才能解码,所以无法在这些帧上切分。

添加新回复
回复需要登录