Add a video export quality choice: Small file, Standard or High
A Quality dropdown under Video on Setup, --video-quality on the command line, and "quality" in a project's video settings (written only when it is not Standard). One table, videoQualities(), holds the ffmpeg settings per codec; Standard is what every video was written with before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+35
-4
@@ -51,6 +51,31 @@ const VideoPreset* videoPresetByKey(const std::string& key) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<VideoQuality>& videoQualities() {
|
||||
// Standard is what every video was written with before there was a
|
||||
// choice: CRF 18 is close to what the eye can tell from the frames.
|
||||
static const std::vector<VideoQuality> qualities = {
|
||||
{"small", "Small file", "smaller and quicker to share, with visible softening", 26, "fast",
|
||||
40, 128},
|
||||
{kDefaultVideoQuality, "Standard", "close to the rendered frames at a moderate size", 18,
|
||||
"medium", 32, 256},
|
||||
{"high", "High", "as faithful as it gets, larger and slower to write", 12, "slow", 24, 256},
|
||||
};
|
||||
return qualities;
|
||||
}
|
||||
|
||||
const VideoQuality* videoQualityByKey(const std::string& key) {
|
||||
for (const auto& quality : videoQualities()) {
|
||||
if (quality.key == key) return &quality;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const VideoQuality& videoQualityOrDefault(const std::string& key) {
|
||||
if (const auto* quality = videoQualityByKey(key)) return *quality;
|
||||
return *videoQualityByKey(kDefaultVideoQuality);
|
||||
}
|
||||
|
||||
// In the C locale whatever the user's is, for the same reason as the
|
||||
// millimetres of a paper size: "7.5 s", never "7,5 s".
|
||||
std::string formatSeconds(double seconds) {
|
||||
@@ -106,7 +131,8 @@ bool ffmpegAvailable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
VideoWriter::VideoWriter(const std::string& path, int width, int height, int fps)
|
||||
VideoWriter::VideoWriter(const std::string& path, int width, int height, int fps,
|
||||
const std::string& quality)
|
||||
: path_(path), width_(width), height_(height) {
|
||||
const std::string muxer = muxerFor(lowerExtension(path));
|
||||
if (muxer.empty()) {
|
||||
@@ -120,6 +146,7 @@ VideoWriter::VideoWriter(const std::string& path, int width, int height, int fps
|
||||
static std::once_flag ignorePipe;
|
||||
std::call_once(ignorePipe, [] { ::signal(SIGPIPE, SIG_IGN); });
|
||||
|
||||
const VideoQuality& squeeze = videoQualityOrDefault(quality);
|
||||
std::vector<std::string> args = {
|
||||
"ffmpeg", "-hide_banner", "-loglevel", "error", "-y",
|
||||
"-f", "rawvideo", "-pix_fmt", "rgb24",
|
||||
@@ -129,16 +156,20 @@ VideoWriter::VideoWriter(const std::string& path, int width, int height, int fps
|
||||
};
|
||||
if (muxer == "gif") {
|
||||
// One palette made for the whole clip, rather than the fixed web one.
|
||||
args.insert(args.end(), {"-vf", "split[a][b];[b]palettegen[p];[a][p]paletteuse",
|
||||
args.insert(args.end(), {"-vf", "split[a][b];[b]palettegen=max_colors=" +
|
||||
std::to_string(squeeze.gifColors) +
|
||||
"[p];[a][p]paletteuse",
|
||||
"-loop", "0"});
|
||||
} else {
|
||||
// 4:2:0 wants even sides; a pixel of padding beats refusing the canvas.
|
||||
args.insert(args.end(), {"-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2"});
|
||||
if (muxer == "webm") {
|
||||
args.insert(args.end(), {"-c:v", "libvpx-vp9", "-crf", "32", "-b:v", "0",
|
||||
args.insert(args.end(), {"-c:v", "libvpx-vp9", "-crf", std::to_string(squeeze.vp9Crf),
|
||||
"-b:v", "0",
|
||||
"-row-mt", "1"});
|
||||
} else {
|
||||
args.insert(args.end(), {"-c:v", "libx264", "-preset", "medium", "-crf", "18"});
|
||||
args.insert(args.end(), {"-c:v", "libx264", "-preset", squeeze.h264Preset, "-crf",
|
||||
std::to_string(squeeze.h264Crf)});
|
||||
}
|
||||
args.insert(args.end(), {"-pix_fmt", "yuv420p"});
|
||||
if (muxer == "mp4" || muxer == "mov") args.insert(args.end(), {"-movflags", "+faststart"});
|
||||
|
||||
Reference in New Issue
Block a user