T6.1: AV1 encoder/decoder with HW probe + SVT-AV1 SW fallback

- New: av1_obu.rs — OBU framer, depacketizer, keyframe detection, LEB128 helpers
- New: dav1d.rs — SW AV1 decoder wrapper (shiguredo_dav1d)
- New: svt_av1.rs — SW AV1 encoder wrapper (shiguredo_svt_av1)
- Add CodecId::Av1Main = 12 with match-arm fixes in downstream crates
- Add VideoToolboxAv1Decoder for macOS M3+ HW decode
- Add MediaCodecAv1Encoder/Decoder for Android (video/av01)
- Add extract_sequence_header_obu() helper for AV1 decoder CSD
- Add 10-frame encode-decode roundtrip test (svt_av1 + dav1d)
- Fix clippy unused import in dav1d.rs
- 15 tests; all workspace tests pass; cargo fmt clean
This commit is contained in:
Siavash Sameni
2026-05-12 18:33:43 +04:00
parent 553c8a4ce1
commit 9334aa5ccd
14 changed files with 1318 additions and 12 deletions

View File

@@ -30,8 +30,9 @@ pub enum CodecId {
// Reserved for video codecs; implementations land in PRD-video-multicodec.
// 10 => H264 main
// 11 => H265 main
// 12 => AV1
// 13 => VP9
/// AV1 main profile (video).
Av1Main = 12,
/// H.265 main profile (video).
H265Main = 11,
}
@@ -49,7 +50,7 @@ impl CodecId {
Self::Codec2_3200 => 3_200,
Self::Codec2_1200 => 1_200,
Self::ComfortNoise => 0,
Self::H264Baseline | Self::H265Main => 2_000_000,
Self::H264Baseline | Self::H265Main | Self::Av1Main => 2_000_000,
}
}
@@ -61,7 +62,7 @@ impl CodecId {
Self::Codec2_3200 => 20,
Self::Codec2_1200 => 40,
Self::ComfortNoise => 20,
Self::H264Baseline | Self::H265Main => 33,
Self::H264Baseline | Self::H265Main | Self::Av1Main => 33,
}
}
@@ -76,7 +77,7 @@ impl CodecId {
| Self::Opus64k => 48_000,
Self::Codec2_3200 | Self::Codec2_1200 => 8_000,
Self::ComfortNoise => 48_000,
Self::H264Baseline | Self::H265Main => 48_000,
Self::H264Baseline | Self::H265Main | Self::Av1Main => 48_000,
}
}
@@ -94,6 +95,7 @@ impl CodecId {
8 => Some(Self::Opus64k),
9 => Some(Self::H264Baseline),
11 => Some(Self::H265Main),
12 => Some(Self::Av1Main),
_ => None,
}
}
@@ -105,7 +107,7 @@ impl CodecId {
/// Returns true if this is a video codec variant.
pub const fn is_video(self) -> bool {
matches!(self, Self::H264Baseline | Self::H265Main)
matches!(self, Self::H264Baseline | Self::H265Main | Self::Av1Main)
}
/// Returns true if this is an Opus variant.
@@ -234,7 +236,7 @@ mod tests {
#[test]
fn codec_id_unknown_values_rejected() {
for v in [10u8, 12, 13].iter().copied().chain(14u8..=255) {
for v in [10u8, 13].iter().copied().chain(14u8..=255) {
assert!(CodecId::from_wire(v).is_none(), "v={v}");
}
}
@@ -248,6 +250,15 @@ mod tests {
assert_eq!(CodecId::H265Main.frame_duration_ms(), 33);
}
#[test]
fn av1_main_roundtrips() {
assert_eq!(CodecId::Av1Main.to_wire(), 12);
assert_eq!(CodecId::from_wire(12), Some(CodecId::Av1Main));
assert!(CodecId::Av1Main.is_video());
assert_eq!(CodecId::Av1Main.bitrate_bps(), 2_000_000);
assert_eq!(CodecId::Av1Main.frame_duration_ms(), 33);
}
#[test]
fn quality_profile_backward_compat_old_json() {
// Old JSON emitted before T5.1 has no priority_mode or video fields.