Export: AV1 WebM refuses an explicit RGB + Alpha request (#166)

apply_output checked `channels: rgba` against the module before it
applied `webmCodec`, and choosing AV1 then quietly dropped the channels
to RGB. So `render --format webm --webm-codec av1 --channels rgba` (one
renderQueue.add) succeeded and wrote an opaque AV1 stream.

The codec options now apply before Channels, so the existing "has no
alpha channel" error (how an unsupported channels request is already
reported) refuses the combination; for WebM it names the codec: "AV1
WebM has no alpha channel (VP9 WebM has)". Choosing AV1 alone on an
RGBA VP9 item still switches it to RGB, as changing the format does.

Test: av1_webm_refuses_requested_alpha.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
bflatasticandClaude Opus 5.5 committed 2026-10-07 15:48:17 +07:00
1 parent 247187872d
commit c1e13b1f66
2 files changed
+27 -2

No files matched your search

+8 -2
View File
@@ -219,6 +219,9 @@ fn apply_output(om: &mut OutputModule, templates: &RenderTemplates, roi: Option<
let f = OutputFormat::from_name(f).ok_or_else(|| bad(cmd, FORMATS))?;
om.set_format(f);
}
// Before Channels: the WebM codec decides whether alpha can be written, so an explicit
// `channels: rgba` with `webmCodec: av1` is refused rather than dropped (#166).
any |= apply_codec_options(om, p, cmd)?;
if let Some(c) = str_p(p, "channels") {
om.channels = match c.to_ascii_lowercase().replace([' ', '+'], "").as_str() {
"rgb" => Channels::Rgb,
@@ -227,7 +230,11 @@ fn apply_output(om: &mut OutputModule, templates: &RenderTemplates, roi: Option<
_ => return Err(bad(cmd, "channels: rgb|rgba|alpha")),
};
if om.channels == Channels::Rgba && !om.supports_alpha() {
return Err(bad(cmd, format!("{} has no alpha channel", om.format.label())));
let what = match om.format {
OutputFormat::WebM => format!("{} WebM has no alpha channel (VP9 WebM has)", om.webm_codec.label()),
f => format!("{} has no alpha channel", f.label()),
};
return Err(bad(cmd, what));
}
}
if let Some(c) = str_p(p, "color").or(str_p(p, "alphaMode")) {
@@ -237,7 +244,6 @@ fn apply_output(om: &mut OutputModule, templates: &RenderTemplates, roi: Option<
_ => return Err(bad(cmd, "color: straight|premultiplied")),
};
}
any |= apply_codec_options(om, p, cmd)?;
if let Some(q) = f_p(p, "quality") {
om.quality = q.clamp(1.0, 100.0) as u8;
}
+19
View File
@@ -110,6 +110,25 @@ fn output_to_keeps_a_format_that_writes_the_extension() {
assert_eq!((&r["output"]["format"], &r["outputModuleSummary"]), (&json!("Hevc"), &a["outputModuleSummary"]), "{r}");
}
/// An explicit RGB + Alpha request with the AV1 WebM codec (which has no alpha) is refused, not
/// silently rendered opaque (#166). Without `channels`, AV1 WebM renders RGB as before.
#[test]
fn av1_webm_refuses_requested_alpha() {
let mut s = session();
let n = s.project.render_queue.len();
let e = s.execute("renderQueue.add", json!({"format": "webm", "webmCodec": "av1", "channels": "rgba"})).unwrap_err().to_string();
assert!(e.contains("AV1 WebM has no alpha channel"), "{e}");
assert_eq!(s.project.render_queue.len(), n, "nothing queued");
let a = s.execute("renderQueue.add", json!({"format": "webm", "channels": "rgba"})).unwrap();
let e = s.execute("renderQueue.setOutputModule", json!({"item": a["item"], "webmCodec": "av1", "channels": "rgba"})).unwrap_err().to_string();
assert!(e.contains("AV1 WebM has no alpha channel"), "{e}");
let om = &s.project.render_queue.last().unwrap().output;
assert_eq!((om.webm_codec, om.channels), (WebmVideoCodec::Vp9, Channels::Rgba), "unchanged");
s.execute("renderQueue.add", json!({"format": "webm", "webmCodec": "av1"})).unwrap();
let om = &s.project.render_queue.last().unwrap().output;
assert_eq!((om.webm_codec, om.channels), (WebmVideoCodec::Av1, Channels::Rgb));
}
/// A `comp` that names no composition says so, rather than "no active composition" (#155).
#[test]
fn an_unknown_comp_is_named_in_the_error() {