diff --git a/crates/buzz-media/src/error.rs b/crates/buzz-media/src/error.rs index 14ce4afe1e..5abbea6f58 100644 --- a/crates/buzz-media/src/error.rs +++ b/crates/buzz-media/src/error.rs @@ -72,8 +72,10 @@ pub enum MediaError { /// Video duration exceeds the 600-second limit. #[error("video too long: duration exceeds 600 seconds")] DurationTooLong, - /// Video resolution exceeds 3840×2160. - #[error("video resolution too high: maximum is 3840x2160")] + /// Video resolution exceeds the 2160 short-edge / 3840 long-edge envelope. + #[error( + "video resolution too high: maximum is 2160 on the short edge and 3840 on the long edge" + )] ResolutionTooHigh, /// MP4 moov atom appears after mdat — not fast-start. #[error("moov atom not at front of file (not fast-start)")] diff --git a/crates/buzz-media/src/validation.rs b/crates/buzz-media/src/validation.rs index dfc61c4275..706c354d04 100644 --- a/crates/buzz-media/src/validation.rs +++ b/crates/buzz-media/src/validation.rs @@ -294,7 +294,7 @@ pub fn validate_content(bytes: &[u8], config: &MediaConfig) -> Result Result 3840 || height > 2160 { + let short_edge = width.min(height); + let long_edge = width.max(height); + if short_edge > 2160 || long_edge > 3840 { return Err(MediaError::ResolutionTooHigh); } @@ -2547,6 +2551,43 @@ mod tests { ); } + #[test] + fn test_validate_video_accepts_portrait_resolution() { + let mp4_bytes = build_mp4_bytes(true, b"avc1", 1_000, 2160, 3840, false); + let tmp = tempfile::NamedTempFile::new().unwrap(); + std::fs::write(tmp.path(), &mp4_bytes).unwrap(); + + let meta = validate_video_file(tmp.path(), &test_config()) + .expect("portrait video within the 2160x3840 envelope should be accepted"); + assert_eq!((meta.width, meta.height), (2160, 3840)); + } + + #[test] + fn test_validate_video_rejects_resolution_above_short_edge_limit() { + let mp4_bytes = build_mp4_bytes(true, b"avc1", 1_000, 2161, 3840, false); + let tmp = tempfile::NamedTempFile::new().unwrap(); + std::fs::write(tmp.path(), &mp4_bytes).unwrap(); + + let result = validate_video_file(tmp.path(), &test_config()); + assert!( + matches!(result, Err(MediaError::ResolutionTooHigh)), + "expected ResolutionTooHigh, got {result:?}" + ); + } + + #[test] + fn test_validate_video_rejects_resolution_above_long_edge_limit() { + let mp4_bytes = build_mp4_bytes(true, b"avc1", 1_000, 2160, 3841, false); + let tmp = tempfile::NamedTempFile::new().unwrap(); + std::fs::write(tmp.path(), &mp4_bytes).unwrap(); + + let result = validate_video_file(tmp.path(), &test_config()); + assert!( + matches!(result, Err(MediaError::ResolutionTooHigh)), + "expected ResolutionTooHigh, got {result:?}" + ); + } + #[test] fn test_validate_video_resolution_too_high() { let config = test_config();