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:
@@ -2082,6 +2082,16 @@ int main() {
|
||||
const std::string base = "{\"chain\": [{\"effect\": \"blur\"}], \"video\": {\"duration\": ";
|
||||
check(!effector::projectFromJson(base + "2, \"preset\": \"cinema\"}}").video.preset.size(),
|
||||
"an unknown preset reads as custom");
|
||||
check(effector::projectToJson(project).find("\"quality\"") == std::string::npos,
|
||||
"the standard quality is not written");
|
||||
project.video.quality = "small";
|
||||
check(effector::projectFromJson(effector::projectToJson(project)).video.quality == "small",
|
||||
"another quality is kept");
|
||||
check(effector::projectFromJson(base + "2, \"quality\": \"lossless\"}}").video.quality.empty(),
|
||||
"an unknown quality reads as the standard one");
|
||||
check(effector::videoQualityOrDefault("").key == effector::kDefaultVideoQuality &&
|
||||
effector::videoQualityByKey(effector::kDefaultVideoQuality) != nullptr,
|
||||
"an empty quality is the standard one, which the table holds");
|
||||
bool rejected = false;
|
||||
try {
|
||||
effector::projectFromJson(base + "2, \"fps\": 500}}");
|
||||
@@ -2106,6 +2116,9 @@ int main() {
|
||||
check(!parsedOptions({"--fps", "0"}) && !parsedOptions({"--duration", "abc"}) &&
|
||||
!parsedOptions({"--duration", "400"}) && !parsedOptions({"--video-preset", "vhs"}),
|
||||
"a bad rate, length or preset is refused");
|
||||
const auto quality = parsedOptions({"--duration", "2", "--video-quality", "high"});
|
||||
check(quality && quality->video.quality == "high", "--video-quality picks the encoding");
|
||||
check(!parsedOptions({"--video-quality", "best"}), "and refuses one it does not know");
|
||||
check(!parsedOptions({"-o", "x.png"})->video.enabled, "without them the output is a still");
|
||||
|
||||
bool wrongName = false;
|
||||
@@ -2128,6 +2141,18 @@ int main() {
|
||||
}
|
||||
check(std::filesystem::exists(file) && std::filesystem::file_size(file) > 0,
|
||||
"ffmpeg writes an .mp4 of odd size");
|
||||
// Every quality, through every codec.
|
||||
bool everyQuality = true;
|
||||
for (const auto& quality : effector::videoQualities()) {
|
||||
for (const char* extension : {".mp4", ".webm", ".gif"}) {
|
||||
const auto encoded = (dir / (quality.key + extension)).string();
|
||||
effector::VideoWriter writer(encoded, 16, 16, 12, quality.key);
|
||||
for (int i = 0; i < 2; ++i) writer.write(gradient(16, 16, 60 * i));
|
||||
writer.finish();
|
||||
everyQuality = everyQuality && std::filesystem::file_size(encoded) > 0;
|
||||
}
|
||||
}
|
||||
check(everyQuality, "every quality writes .mp4, .webm and .gif");
|
||||
const auto dropped = (dir / "dropped.webm").string();
|
||||
{
|
||||
effector::VideoWriter writer(dropped, 16, 16, 12);
|
||||
|
||||
@@ -553,6 +553,11 @@ void testVideo(Gtk::Window& window, const std::filesystem::path& dir) {
|
||||
stack->set_visible_child("setup");
|
||||
pump(100);
|
||||
check(preset->is_drawable() && duration->is_drawable(), "and its preset and length");
|
||||
auto* quality = named<Gtk::DropDown>(window, "video-quality");
|
||||
check(quality != nullptr && quality->is_drawable() &&
|
||||
effector::videoQualities().at(quality->get_selected()).key ==
|
||||
effector::kDefaultVideoQuality,
|
||||
"and its file quality, standard to begin with");
|
||||
|
||||
preset->set_selected(1); // Instagram story, the first preset
|
||||
pump(200);
|
||||
|
||||
Reference in New Issue
Block a user