将视频批量转换至 AV1 编码
运行环境:
单独使用 FFmpeg
1 2 3
| ffmpeg -i "input.mp4" \ -vf "colorspace=iall=bt709:all=bt709:fast=1" \ -c:v libsvtav1 -crf 30 -preset 8 -c:a copy "output.mkv"
|
参数 | 含义说明 |
---|
-i "input.mp4" | 指定输入文件。这里是 "input.mp4" 。 |
-vf "colorspace=iall=bt709:all=bt709:fast=1" | 使用 colorspace 视频滤镜: |
| - iall=bt709 :假设输入是 bt709 色彩空间(不信任输入的 metadata)。 |
| - all=bt709 :将输出转换为 bt709 色彩空间。 |
| - fast=1 :快速模式,提高性能,牺牲一点精度。 |
-c:v libsvtav1 | 设置视频编码器为 libsvtav1 ,即使用 SVT-AV1 编码器编码视频。 |
-crf 30 | Constant Rate Factor,质量控制参数,数值越大体积越小,画质越低(范围一般为 0-63)。 |
-preset 8 | 编码速度预设,8 通常是 veryslow ,压缩效率高但编码速度慢。 |
-c:a copy | 音频流直接复制,不重新编码。 |
"output.mkv" | 指定输出文件名为 output.mkv 。 |
使用 Node.js 进行批量转换
1
| node toAV1.js /dir-to-your-videos/
|
运行完成转换后的 AV1 编码视频文件在原视频目录的 dist 文件夹中。
toAV1.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| const fs = require("fs"); const path = require("path"); const { spawn } = require("child_process"); const trash = require("trash");
const inputDir = process.argv[2];
if (!inputDir || !fs.existsSync(inputDir)) { console.error("❌ 请传入有效的目录路径"); process.exit(1); }
const VIDEO_EXTS = [".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v"];
const distDir = path.join(inputDir, "dist"); if (!fs.existsSync(distDir)) { fs.mkdirSync(distDir); }
async function encodeVideo(filePath, indexStr) { const fileName = path.basename(filePath); const baseName = path.parse(fileName).name; const outputPath = path.join(distDir, `${baseName}.mkv`);
return new Promise((resolve, reject) => { console.log( `🎬 正在编码 ${indexStr} :${fileName} → ${path.relative(inputDir, outputPath)}` );
const ffmpeg = spawn( "ffmpeg", [ "-i", filePath, "-vf", "colorspace=iall=bt709:all=bt709:fast=1", "-c:v", "libsvtav1", "-crf", "30", "-preset", "8", "-c:a", "copy", outputPath, ], { stdio: "inherit" } );
ffmpeg.on("close", async (code) => { if (code === 0) { console.log(`✅ ${indexStr} 编码完成:${fileName}`); try { await trash([filePath]); console.log(`🗑️ 已将原始文件移至废纸篓:${fileName}`); } catch (err) { console.error(`⚠️ 移动到废纸篓失败:${err.message}`); } resolve(); } else { console.error(`❌ 编码失败:${fileName}`); reject(new Error(`FFmpeg 退出码 ${code}`)); } }); }); }
(async () => { const files = fs .readdirSync(inputDir) .filter((file) => VIDEO_EXTS.includes(path.extname(file).toLowerCase())) .map((file) => path.join(inputDir, file));
if (files.length === 0) { console.log("📭 没有找到视频文件"); return; }
for (const [index, file] of files.entries()) { try { const indexStr = `${index}/${files.length}` await encodeVideo(file, indexStr); } catch (err) { console.error(`❌ 出错:${err.message}`); } }
console.log("🏁 所有文件处理完毕"); })();
|