Import: item.metadata counts a WebM's last frame when its duration is not a whole millisecond (fixes #1429)

This commit is contained in:
Marco Arnulfo committed 2026-10-14 14:22:15 +02:00
1 parent 549e9d8251
commit cc29d90943
2 files changed
+31 -1

No files matched your search

+3 -1
View File
@@ -312,7 +312,9 @@ pub fn footage_metadata(f: &Footage) -> Value {
"pixelAspect": f.pixel_aspect,
"frameRate": f.frame_rate.as_f64(),
"duration": f.duration.seconds(),
"frames": if f.has_video && f.kind != FootageKind::Still { Some(f.frame_rate.frame_at(f.duration)) } else { None },
// Rounded like the media pool's frame count: Matroska stores the duration in whole
// milliseconds, a hair short of the last frame at 30 fps (#1429).
"frames": if f.has_video && f.kind != FootageKind::Still { Some(f.frame_rate.frame_at(f.frame_rate.snap_nearest(f.duration))) } else { None },
"hasVideo": f.has_video,
"hasAudio": f.has_audio,
"alpha": format!("{:?}", f.alpha),
+28
View File
@@ -0,0 +1,28 @@
//! A WebM rendered by EffectCraft and imported back reports its real frame count (#1429). Matroska
//! stores the duration in whole milliseconds, so at 30 fps a 1-frame file lasts 33 ms, just short
//! of one 33.33 ms frame.
use serde_json::json;
fn tmp(name: &str) -> String {
let d = std::env::temp_dir().join(format!("effectcraft-host-webm-meta-{}", std::process::id()));
std::fs::create_dir_all(&d).unwrap();
d.join(name).to_string_lossy().to_string()
}
#[test]
fn a_webm_at_30_fps_reports_every_frame() {
for n in [1u32, 4] {
let mut s = effectcraft_host::session();
let out = tmp(&format!("frames-{n}.webm"));
s.execute("comp.new", json!({"name": "Main", "width": 64, "height": 36, "frameRate": 30, "duration": 1})).unwrap();
s.execute("layer.newSolid", json!({"name": "BG", "color": "#142850"})).unwrap();
let span = json!({"comp": "Main", "format": "webm", "output": out, "timeSpan": "custom", "start": 0, "duration": n as f64 / 30.0});
s.execute("renderQueue.add", span).unwrap();
s.execute("renderQueue.render", json!({"wait": true})).unwrap();
let r = s.execute("file.import", json!({"paths": [out.clone()]})).unwrap();
let m = s.execute("item.metadata", json!({"item": r["items"][0]})).unwrap();
let _ = std::fs::remove_file(&out);
assert_eq!(m["item"]["frames"], json!(n), "{n} rendered frames: {m}");
}
}