1pub mod alf;
79pub mod alf_tables;
80pub mod aps;
81pub mod bitreader;
82pub mod cabac;
83pub mod cabac_init;
84pub mod deblock;
85pub mod decoder;
86pub mod dequant;
87pub mod dra;
88pub mod hmvp;
89pub mod ibc;
90pub mod inter;
91pub mod intra;
92pub mod nal;
93pub mod neighbour;
94pub mod picture;
95pub mod pps;
96pub mod rpl;
97pub mod scan;
98pub mod slice_data;
99pub mod slice_header;
100pub mod sps;
101pub mod transform;
102
103use oxideav_core::{CodecCapabilities, CodecId, CodecTag};
104use oxideav_core::{CodecInfo, CodecRegistry};
105
106pub const CODEC_ID_STR: &str = "evc";
108
109#[derive(Clone, Copy, Debug, PartialEq, Eq)]
112pub struct EvcFileInfo {
113 pub width: u32,
114 pub height: u32,
115 pub profile_idc: u8,
116 pub level_idc: u8,
117 pub bit_depth_luma: u32,
118 pub bit_depth_chroma: u32,
119 pub chroma_format_idc: u32,
120}
121
122pub fn probe(input: &[u8]) -> Option<EvcFileInfo> {
130 if let Ok(nals) = nal::iter_length_prefixed(input) {
132 for nal_ref in nals {
133 if let Some(info) = info_from_nal(&nal_ref) {
134 return Some(info);
135 }
136 }
137 }
138 for nal_ref in nal::iter_annex_b(input) {
140 if let Some(info) = info_from_nal(&nal_ref) {
141 return Some(info);
142 }
143 }
144 None
145}
146
147fn info_from_nal(nal_ref: &nal::NalRef<'_>) -> Option<EvcFileInfo> {
148 if nal_ref.header.nal_unit_type != nal::NalUnitType::Sps {
149 return None;
150 }
151 let sps = sps::parse(nal_ref.rbsp()).ok()?;
152 Some(EvcFileInfo {
153 width: sps.pic_width_in_luma_samples,
154 height: sps.pic_height_in_luma_samples,
155 profile_idc: sps.profile_idc,
156 level_idc: sps.level_idc,
157 bit_depth_luma: sps.bit_depth_y(),
158 bit_depth_chroma: sps.bit_depth_c(),
159 chroma_format_idc: sps.chroma_format_idc,
160 })
161}
162
163pub fn walk_idr_slice(
174 sps: &sps::Sps,
175 pps: &pps::Pps,
176 slice_nal_rbsp: &[u8],
177) -> oxideav_core::Result<slice_data::SliceWalkStats> {
178 use oxideav_core::Error;
179 let ctx = slice_header::SliceParseContext {
181 single_tile_in_pic_flag: pps.single_tile_in_pic_flag,
182 arbitrary_slice_present_flag: pps.arbitrary_slice_present_flag,
183 tile_id_len_minus1: pps.tile_id_len_minus1,
184 num_tile_columns_minus1: pps.num_tile_columns_minus1,
185 num_tile_rows_minus1: pps.num_tile_rows_minus1,
186 sps_pocs_flag: sps.sps_pocs_flag,
187 sps_rpl_flag: sps.sps_rpl_flag,
188 sps_alf_flag: sps.sps_alf_flag,
189 sps_mmvd_flag: sps.sps_mmvd_flag,
190 sps_admvp_flag: sps.sps_admvp_flag,
191 sps_addb_flag: sps.sps_addb_flag,
192 log2_max_pic_order_cnt_lsb_minus4: sps.log2_max_pic_order_cnt_lsb_minus4,
193 chroma_array_type: sps.chroma_array_type(),
194 num_ref_pic_lists_in_sps_l0: sps.num_ref_pic_lists_in_sps_l0,
195 num_ref_pic_lists_in_sps_l1: sps.num_ref_pic_lists_in_sps_l1,
196 rpl1_idx_present_flag: pps.rpl1_idx_present_flag,
197 long_term_ref_pics_flag: sps.long_term_ref_pics_flag,
198 additional_lt_poc_lsb_len: pps.additional_lt_poc_lsb_len,
199 };
200 if sps.sps_btt_flag
205 || sps.sps_suco_flag
206 || sps.sps_admvp_flag
207 || sps.sps_eipd_flag
208 || sps.sps_addb_flag
209 || sps.sps_dquant_flag
210 || sps.sps_ats_flag
211 || sps.sps_adcc_flag
212 || sps.sps_cm_init_flag
213 || sps.sps_amvr_flag
214 || sps.sps_mmvd_flag
215 || sps.sps_affine_flag
216 || sps.sps_dmvr_flag
217 || sps.sps_hmvp_flag
218 {
219 return Err(Error::unsupported(
220 "evc walk_idr_slice: round-2 only supports Baseline-profile toolset",
221 ));
222 }
223 if !pps.single_tile_in_pic_flag {
224 return Err(Error::unsupported(
225 "evc walk_idr_slice: round-2 requires single_tile_in_pic_flag == 1",
226 ));
227 }
228 let mut hdr_br = crate::bitreader::BitReader::new(slice_nal_rbsp);
232 parse_slice_header_consume(&mut hdr_br, nal::NalUnitType::Idr, &ctx)?;
233 hdr_br.align_to_byte();
235 let consumed_bits = hdr_br.bit_position();
236 if consumed_bits % 8 != 0 {
237 return Err(Error::invalid(
238 "evc walk_idr_slice: slice header not byte-aligned after parse",
239 ));
240 }
241 let consumed_bytes = (consumed_bits / 8) as usize;
242 if consumed_bytes >= slice_nal_rbsp.len() {
243 return Err(Error::invalid(
244 "evc walk_idr_slice: no slice_data bytes after header",
245 ));
246 }
247 let slice_data_bytes = &slice_nal_rbsp[consumed_bytes..];
248 let ctb_log2_size_y = sps.log2_ctu_size_minus5 + 5;
252 let min_cb_log2_size_y = sps.log2_min_cb_size_minus2 + 2;
253 let max_tb_log2_size_y = ctb_log2_size_y.min(6);
254 let log2_max_ibc_cand_size = sps.log2_max_ibc_cand_size().unwrap_or(0);
255 let inputs = slice_data::SliceWalkInputs {
256 pic_width: sps.pic_width_in_luma_samples,
257 pic_height: sps.pic_height_in_luma_samples,
258 ctb_log2_size_y,
259 min_cb_log2_size_y,
260 max_tb_log2_size_y,
261 chroma_format_idc: sps.chroma_format_idc,
262 cu_qp_delta_enabled: pps.cu_qp_delta_enabled_flag,
263 sps_ibc_flag: sps.sps_ibc_flag,
264 log2_max_ibc_cand_size,
265 slice_alf_enabled_flag: false,
270 slice_alf_map_flag: false,
271 slice_chroma_alf_enabled_flag: false,
272 slice_alf_chroma_map_flag: false,
273 slice_chroma2_alf_enabled_flag: false,
274 slice_alf_chroma2_map_flag: false,
275 };
276 slice_data::walk_baseline_idr_slice(slice_data_bytes, inputs)
277}
278
279pub fn decode_idr_slice(
288 sps: &sps::Sps,
289 pps: &pps::Pps,
290 slice_nal_rbsp: &[u8],
291) -> oxideav_core::Result<(picture::YuvPicture, slice_data::SliceDecodeStats)> {
292 use oxideav_core::Error;
293 if sps.sps_btt_flag
294 || sps.sps_suco_flag
295 || sps.sps_admvp_flag
296 || sps.sps_eipd_flag
297 || sps.sps_addb_flag
298 || sps.sps_dquant_flag
299 || sps.sps_ats_flag
300 || sps.sps_adcc_flag
301 || sps.sps_cm_init_flag
302 {
303 return Err(Error::unsupported(
304 "evc decode_idr_slice: round-3 only supports Baseline-profile toolset",
305 ));
306 }
307 if !pps.single_tile_in_pic_flag {
310 return Err(Error::unsupported(
311 "evc decode_idr_slice: round-3 requires single_tile_in_pic_flag == 1",
312 ));
313 }
314 let mut hdr_br = crate::bitreader::BitReader::new(slice_nal_rbsp);
315 let slice_qp = parse_slice_header_for_decode(&mut hdr_br, sps, pps)?;
316 hdr_br.align_to_byte();
317 let consumed_bits = hdr_br.bit_position();
318 if consumed_bits % 8 != 0 {
319 return Err(Error::invalid(
320 "evc decode_idr_slice: slice header not byte-aligned after parse",
321 ));
322 }
323 let consumed_bytes = (consumed_bits / 8) as usize;
324 if consumed_bytes >= slice_nal_rbsp.len() {
325 return Err(Error::invalid(
326 "evc decode_idr_slice: no slice_data bytes after header",
327 ));
328 }
329 let slice_data_bytes = &slice_nal_rbsp[consumed_bytes..];
330 let ctb_log2_size_y = sps.log2_ctu_size_minus5 + 5;
331 let min_cb_log2_size_y = sps.log2_min_cb_size_minus2 + 2;
332 let max_tb_log2_size_y = ctb_log2_size_y.min(5); let log2_max_ibc_cand_size = sps.log2_max_ibc_cand_size().unwrap_or(0);
334 let walk = slice_data::SliceWalkInputs {
335 pic_width: sps.pic_width_in_luma_samples,
336 pic_height: sps.pic_height_in_luma_samples,
337 ctb_log2_size_y,
338 min_cb_log2_size_y,
339 max_tb_log2_size_y,
340 chroma_format_idc: sps.chroma_format_idc,
341 cu_qp_delta_enabled: pps.cu_qp_delta_enabled_flag,
342 sps_ibc_flag: sps.sps_ibc_flag,
343 log2_max_ibc_cand_size,
344 slice_alf_enabled_flag: false,
346 slice_alf_map_flag: false,
347 slice_chroma_alf_enabled_flag: false,
348 slice_alf_chroma_map_flag: false,
349 slice_chroma2_alf_enabled_flag: false,
350 slice_alf_chroma2_map_flag: false,
351 };
352 let decode = slice_data::SliceDecodeInputs {
353 slice_qp,
354 bit_depth_luma: sps.bit_depth_y(),
355 bit_depth_chroma: sps.bit_depth_c(),
356 enable_deblock: false, slice_cb_qp_offset: 0,
358 slice_cr_qp_offset: 0,
359 sps_ibc_flag: sps.sps_ibc_flag,
360 log2_max_ibc_cand_size,
361 };
362 slice_data::decode_baseline_idr_slice(slice_data_bytes, walk, decode)
363}
364
365fn parse_slice_header_for_decode(
370 br: &mut crate::bitreader::BitReader,
371 _sps: &sps::Sps,
372 _pps: &pps::Pps,
373) -> oxideav_core::Result<i32> {
374 use oxideav_core::Error;
375 let _slice_pps_id = br.ue()?;
376 let slice_type = br.ue()?;
377 if slice_type != 2 {
378 return Err(Error::invalid(format!(
379 "evc decode_idr_slice: IDR slice_type must be 2 (got {slice_type})"
380 )));
381 }
382 let _no_output = br.u1()?;
383 let _slice_deblocking_filter_flag = br.u1()?;
384 let slice_qp = br.u(6)?;
385 if slice_qp > 51 {
386 return Err(Error::invalid(format!(
387 "evc decode_idr_slice: slice_qp {slice_qp} > 51"
388 )));
389 }
390 let _slice_cb_qp_offset = br.se()?;
391 let _slice_cr_qp_offset = br.se()?;
392 Ok(slice_qp as i32)
393}
394
395fn parse_slice_header_consume(
401 br: &mut crate::bitreader::BitReader,
402 _nal_unit_type: nal::NalUnitType,
403 _ctx: &slice_header::SliceParseContext,
404) -> oxideav_core::Result<()> {
405 use oxideav_core::Error;
406 let _slice_pps_id = br.ue()?;
407 let slice_type = br.ue()?;
409 if slice_type != 2 {
410 return Err(Error::invalid(format!(
411 "evc walk_idr_slice: IDR slice_type must be 2 (got {slice_type})"
412 )));
413 }
414 let _no_output = br.u1()?;
415 let _slice_deblocking_filter_flag = br.u1()?;
420 let slice_qp = br.u(6)?;
422 if slice_qp > 51 {
423 return Err(Error::invalid(format!(
424 "evc walk_idr_slice: slice_qp {slice_qp} > 51"
425 )));
426 }
427 let _slice_cb_qp_offset = br.se()?;
428 let _slice_cr_qp_offset = br.se()?;
429 Ok(())
430}
431
432pub fn register(reg: &mut CodecRegistry) {
436 let caps = CodecCapabilities::video("evc_sw")
437 .with_lossy(true)
438 .with_intra_only(false)
439 .with_max_size(sps::MAX_DIMENSION, sps::MAX_DIMENSION);
440 reg.register(
446 CodecInfo::new(CodecId::new(CODEC_ID_STR))
447 .capabilities(caps)
448 .decoder(decoder::make_decoder)
449 .tags([
450 CodecTag::fourcc(b"evc1"),
451 CodecTag::fourcc(b"evcC"),
452 CodecTag::fourcc(b"EVC1"),
453 ]),
454 );
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use crate::sps::tests::BitEmitter;
461
462 fn build_minimal_sps_stream() -> Vec<u8> {
465 let mut body = BitEmitter::new();
467 body.ue(0); body.u(8, 1); body.u(8, 30); body.u(32, 0); body.u(32, 0); body.ue(1); body.ue(320); body.ue(240); body.ue(0); body.ue(0); for _ in 0..13 {
478 body.u(1, 0);
479 }
480 body.ue(1); body.ue(1); body.u(1, 0); body.u(1, 0); body.u(1, 0); body.finish_with_trailing_bits();
486 let sps_rbsp = body.into_bytes();
487
488 let nut_plus1: u16 = 24 + 1;
490 let mut hdr_word: u16 = 0;
491 hdr_word |= (nut_plus1 & 0x3F) << 9;
492 let hdr = [(hdr_word >> 8) as u8, (hdr_word & 0xFF) as u8];
494
495 let nal_len = (hdr.len() + sps_rbsp.len()) as u32;
497 let mut out = Vec::new();
498 out.extend_from_slice(&nal_len.to_be_bytes());
499 out.extend_from_slice(&hdr);
500 out.extend_from_slice(&sps_rbsp);
501 out
502 }
503
504 #[test]
505 fn probe_minimal_sps_stream() {
506 let bs = build_minimal_sps_stream();
507 let info = probe(&bs).expect("probe must recover SPS dimensions");
508 assert_eq!(info.width, 320);
509 assert_eq!(info.height, 240);
510 assert_eq!(info.bit_depth_luma, 8);
511 assert_eq!(info.bit_depth_chroma, 8);
512 assert_eq!(info.chroma_format_idc, 1);
513 assert_eq!(info.profile_idc, 1);
514 assert_eq!(info.level_idc, 30);
515 }
516
517 #[test]
518 fn probe_returns_none_on_empty() {
519 assert!(probe(&[]).is_none());
520 }
521
522 #[test]
523 fn probe_returns_none_on_pps_only() {
524 let nut_plus1: u16 = 25 + 1;
526 let mut hdr_word: u16 = 0;
527 hdr_word |= (nut_plus1 & 0x3F) << 9;
528 let hdr = [(hdr_word >> 8) as u8, (hdr_word & 0xFF) as u8];
529 let mut pps_body = BitEmitter::new();
530 pps_body.ue(0); pps_body.ue(0); pps_body.ue(0);
533 pps_body.ue(0);
534 pps_body.ue(0);
535 pps_body.u(1, 0);
536 pps_body.u(1, 1); pps_body.ue(0); pps_body.u(1, 0);
539 pps_body.u(1, 0);
540 pps_body.u(1, 0);
541 pps_body.u(1, 0);
542 pps_body.u(1, 0);
543 pps_body.finish_with_trailing_bits();
544 let body = pps_body.into_bytes();
545 let nal_len = (hdr.len() + body.len()) as u32;
546 let mut out = Vec::new();
547 out.extend_from_slice(&nal_len.to_be_bytes());
548 out.extend_from_slice(&hdr);
549 out.extend_from_slice(&body);
550 assert!(probe(&out).is_none());
551 }
552
553 #[test]
554 fn register_creates_factory() {
555 let mut reg = CodecRegistry::default();
556 register(&mut reg);
557 assert!(reg.has_decoder(&CodecId::new(CODEC_ID_STR)));
560 }
561
562 #[test]
563 fn make_decoder_handles_empty_packet() {
564 use oxideav_core::{CodecParameters, Packet, TimeBase};
565 let params = CodecParameters::video(CodecId::new(CODEC_ID_STR));
566 let mut dec = decoder::make_decoder(¶ms).unwrap();
567 let pkt = Packet::new(0, TimeBase::new(1, 90_000), Vec::new());
568 dec.send_packet(&pkt).unwrap();
570 let err = dec.receive_frame().unwrap_err();
571 assert!(matches!(err, oxideav_core::Error::NeedMore));
573 }
574
575 #[test]
594 fn end_to_end_walk_baseline_idr_slice() {
595 use crate::cabac::CabacEncoder;
596 let mut sps_body = BitEmitter::new();
600 sps_body.ue(0); sps_body.u(8, 0); sps_body.u(8, 30); sps_body.u(32, 0); sps_body.u(32, 0); sps_body.ue(1); sps_body.ue(64); sps_body.ue(64); sps_body.ue(0); sps_body.ue(0); for _ in 0..13 {
611 sps_body.u(1, 0);
612 }
613 sps_body.ue(1); sps_body.ue(1); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.finish_with_trailing_bits();
619 let sps_rbsp = sps_body.into_bytes();
620 let sps = sps::parse(&sps_rbsp).expect("SPS parses");
621
622 let mut pps_body = BitEmitter::new();
625 pps_body.ue(0); pps_body.ue(0); pps_body.ue(0); pps_body.ue(0); pps_body.ue(0); pps_body.u(1, 0); pps_body.u(1, 1); pps_body.ue(0); pps_body.u(1, 0); pps_body.u(1, 0); pps_body.u(1, 0); pps_body.u(1, 0); pps_body.u(1, 0); pps_body.finish_with_trailing_bits();
639 let pps_rbsp = pps_body.into_bytes();
640 let pps = pps::parse(&pps_rbsp).expect("PPS parses");
641
642 let mut hdr = BitEmitter::new();
644 hdr.ue(0); hdr.ue(2); hdr.u(1, 0); hdr.u(1, 0); hdr.u(6, 22); hdr.ue(0); hdr.ue(0); while hdr.bit_position() % 8 != 0 {
652 hdr.u(1, 0);
653 }
654 let mut slice_rbsp = hdr.into_bytes();
655
656 let mut enc = CabacEncoder::new();
662 enc.encode_decision(0, 0, 1); for _ in 0..4 {
664 enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); }
670 enc.encode_terminate(true);
671 let slice_data_bytes = enc.finish();
672 slice_rbsp.extend_from_slice(&slice_data_bytes);
673
674 let stats = walk_idr_slice(&sps, &pps, &slice_rbsp).expect("walk succeeds");
675 assert_eq!(stats.ctus, 1);
676 assert_eq!(stats.split_cu_flag_bins, 5, "1 CTB split + 4 child splits");
677 assert_eq!(stats.coding_units, 8, "4 leaves × (luma + chroma)");
678 assert_eq!(stats.intra_pred_mode_bins, 4);
679 assert_eq!(stats.cbf_luma_bins, 4);
680 assert_eq!(stats.cbf_chroma_bins, 8);
681 }
682
683 #[test]
686 fn walk_idr_slice_rejects_main_profile_sps() {
687 let mut sps_body = BitEmitter::new();
689 sps_body.ue(0);
690 sps_body.u(8, 1); sps_body.u(8, 41);
692 sps_body.u(32, 1); sps_body.u(32, 1);
694 sps_body.ue(1);
695 sps_body.ue(1920);
696 sps_body.ue(1080);
697 sps_body.ue(2);
698 sps_body.ue(2);
699 sps_body.u(1, 1); sps_body.ue(1);
701 sps_body.ue(0);
702 sps_body.ue(2);
703 sps_body.ue(1);
704 sps_body.ue(0);
705 sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.ue(1);
718 sps_body.ue(1);
719 sps_body.u(1, 0);
720 sps_body.u(1, 0);
721 sps_body.u(1, 0);
722 sps_body.finish_with_trailing_bits();
723 let sps_rbsp = sps_body.into_bytes();
724 let sps = sps::parse(&sps_rbsp).expect("SPS parses");
725
726 let mut pps_body = BitEmitter::new();
728 pps_body.ue(0);
729 pps_body.ue(0);
730 pps_body.ue(0);
731 pps_body.ue(0);
732 pps_body.ue(0);
733 pps_body.u(1, 0);
734 pps_body.u(1, 1);
735 pps_body.ue(0);
736 pps_body.u(1, 0);
737 pps_body.u(1, 0);
738 pps_body.u(1, 0);
739 pps_body.u(1, 0);
740 pps_body.u(1, 0);
741 pps_body.finish_with_trailing_bits();
742 let pps_rbsp = pps_body.into_bytes();
743 let pps = pps::parse(&pps_rbsp).unwrap();
744
745 let res = walk_idr_slice(&sps, &pps, &[0u8; 16]);
746 assert!(res.is_err());
747 let err_text = format!("{}", res.unwrap_err());
748 assert!(
749 err_text.contains("Baseline-profile toolset"),
750 "got: {err_text}"
751 );
752 }
753
754 fn build_baseline_sps_rbsp(width: u32, height: u32) -> Vec<u8> {
761 let mut sps_body = BitEmitter::new();
762 sps_body.ue(0); sps_body.u(8, 0); sps_body.u(8, 30); sps_body.u(32, 0); sps_body.u(32, 0); sps_body.ue(1); sps_body.ue(width);
769 sps_body.ue(height);
770 sps_body.ue(0); sps_body.ue(0); for _ in 0..13 {
773 sps_body.u(1, 0);
774 }
775 sps_body.ue(1); sps_body.ue(1); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.finish_with_trailing_bits();
781 sps_body.into_bytes()
782 }
783
784 fn build_rpl_sps_rbsp(width: u32, height: u32) -> Vec<u8> {
790 let mut sps_body = sps::tests::BitEmitter::new();
791 sps_body.ue(0); sps_body.u(8, 0); sps_body.u(8, 30); sps_body.u(32, 0); sps_body.u(32, 0); sps_body.ue(1); sps_body.ue(width);
798 sps_body.ue(height);
799 sps_body.ue(0); sps_body.ue(0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 1); sps_body.u(1, 1); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.ue(4); sps_body.ue(1); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.ue(0); sps_body.ue(0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.u(1, 0); sps_body.finish_with_trailing_bits();
831 sps_body.into_bytes()
832 }
833
834 fn build_baseline_pps_rbsp() -> Vec<u8> {
836 let mut pps_body = BitEmitter::new();
837 pps_body.ue(0); pps_body.ue(0); pps_body.ue(0);
840 pps_body.ue(0);
841 pps_body.ue(0);
842 pps_body.u(1, 0); pps_body.u(1, 1); pps_body.ue(0); pps_body.u(1, 0); pps_body.u(1, 0); pps_body.u(1, 0); pps_body.u(1, 0); pps_body.u(1, 0); pps_body.finish_with_trailing_bits();
851 pps_body.into_bytes()
852 }
853
854 #[test]
863 fn round3_end_to_end_decode_grey_idr() {
864 use crate::cabac::CabacEncoder;
865 let sps = sps::parse(&build_baseline_sps_rbsp(64, 64)).unwrap();
869 let pps = pps::parse(&build_baseline_pps_rbsp()).unwrap();
870 let mut hdr = BitEmitter::new();
872 hdr.ue(0);
873 hdr.ue(2); hdr.u(1, 0);
875 hdr.u(1, 0); hdr.u(6, 22);
877 hdr.ue(0);
878 hdr.ue(0);
879 while hdr.bit_position() % 8 != 0 {
880 hdr.u(1, 0);
881 }
882 let mut slice_rbsp = hdr.into_bytes();
883 let mut enc = CabacEncoder::new();
887 enc.encode_decision(0, 0, 1); for _ in 0..4 {
889 enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); }
898 enc.encode_terminate(true);
899 slice_rbsp.extend_from_slice(&enc.finish());
900
901 let (pic, stats) = decode_idr_slice(&sps, &pps, &slice_rbsp).unwrap();
902 assert_eq!(pic.width, 64);
904 assert_eq!(pic.height, 64);
905 assert_eq!(pic.y.len(), 64 * 64);
906 assert_eq!(pic.cb.len(), 32 * 32);
907 assert_eq!(pic.cr.len(), 32 * 32);
908 assert_eq!(stats.ctus, 1);
912 assert_eq!(stats.split_cu_flag_bins, 5);
913 assert_eq!(stats.coding_units, 8);
914 assert_eq!(stats.intra_pred_mode_bins, 4);
915 assert_eq!(stats.cbf_luma_bins, 4);
916 assert_eq!(stats.cbf_chroma_bins, 8);
917 let mismatched_y = pic.y.iter().filter(|&&v| v != 128).count();
923 assert_eq!(
924 mismatched_y, 0,
925 "Y plane should be uniform 128 (got {mismatched_y} non-128 samples)"
926 );
927 let mismatched_cb = pic.cb.iter().filter(|&&v| v != 128).count();
928 let mismatched_cr = pic.cr.iter().filter(|&&v| v != 128).count();
929 assert_eq!(mismatched_cb, 0);
930 assert_eq!(mismatched_cr, 0);
931 let mse: f64 = pic
934 .y
935 .iter()
936 .map(|&v| (v as f64 - 128.0).powi(2))
937 .sum::<f64>()
938 / pic.y.len() as f64;
939 assert_eq!(mse, 0.0);
940 }
941
942 #[test]
948 fn round3_make_decoder_decodes_idr_to_grey_frame() {
949 use crate::cabac::CabacEncoder;
950 use oxideav_core::{CodecParameters, Packet, TimeBase};
951
952 let sps_rbsp = build_baseline_sps_rbsp(64, 64);
953 let pps_rbsp = build_baseline_pps_rbsp();
954
955 let mut hdr = BitEmitter::new();
957 hdr.ue(0);
958 hdr.ue(2);
959 hdr.u(1, 0);
960 hdr.u(1, 0);
961 hdr.u(6, 22);
962 hdr.ue(0);
963 hdr.ue(0);
964 while hdr.bit_position() % 8 != 0 {
965 hdr.u(1, 0);
966 }
967 let mut idr_rbsp = hdr.into_bytes();
968 let mut enc = CabacEncoder::new();
969 enc.encode_decision(0, 0, 1); for _ in 0..4 {
971 enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); enc.encode_decision(0, 0, 0); }
977 enc.encode_terminate(true);
978 idr_rbsp.extend_from_slice(&enc.finish());
979
980 fn nal_envelope(nut: u8, rbsp: &[u8]) -> Vec<u8> {
982 let nut_plus1: u16 = (nut as u16) + 1;
983 let mut hdr_word: u16 = 0;
984 hdr_word |= (nut_plus1 & 0x3F) << 9;
985 let hdr = [(hdr_word >> 8) as u8, (hdr_word & 0xFF) as u8];
986 let nal_len = (hdr.len() + rbsp.len()) as u32;
987 let mut out = Vec::new();
988 out.extend_from_slice(&nal_len.to_be_bytes());
989 out.extend_from_slice(&hdr);
990 out.extend_from_slice(rbsp);
991 out
992 }
993 let mut bs = Vec::new();
994 bs.extend_from_slice(&nal_envelope(24, &sps_rbsp)); bs.extend_from_slice(&nal_envelope(25, &pps_rbsp)); bs.extend_from_slice(&nal_envelope(1, &idr_rbsp)); let params = CodecParameters::video(CodecId::new(CODEC_ID_STR));
999 let mut dec = decoder::make_decoder(¶ms).unwrap();
1000 let pkt = Packet::new(0, TimeBase::new(1, 90_000), bs).with_pts(0);
1001 dec.send_packet(&pkt).unwrap();
1002 let frame = dec.receive_frame().unwrap();
1003 let video = match frame {
1004 oxideav_core::Frame::Video(v) => v,
1005 _ => panic!("expected video frame"),
1006 };
1007 assert_eq!(video.planes.len(), 3);
1008 assert_eq!(video.planes[0].stride, 64);
1009 assert_eq!(video.planes[0].data.len(), 64 * 64);
1010 assert!(video.planes[0].data.iter().all(|&v| v == 128));
1011 assert_eq!(video.planes[1].stride, 32);
1012 assert_eq!(video.planes[1].data.len(), 32 * 32);
1013 assert!(video.planes[1].data.iter().all(|&v| v == 128));
1014 assert_eq!(video.planes[2].data.len(), 32 * 32);
1015 }
1016
1017 #[test]
1025 fn round4_make_decoder_decodes_idr_plus_p_to_two_frames() {
1026 use crate::cabac::CabacEncoder;
1027 use oxideav_core::{CodecParameters, Packet, TimeBase};
1028 let sps_rbsp = build_baseline_sps_rbsp(32, 32);
1029 let pps_rbsp = build_baseline_pps_rbsp();
1030
1031 let mut idr_hdr = BitEmitter::new();
1033 idr_hdr.ue(0);
1034 idr_hdr.ue(2); idr_hdr.u(1, 0); idr_hdr.u(1, 0); idr_hdr.u(6, 22); idr_hdr.ue(0);
1039 idr_hdr.ue(0);
1040 while idr_hdr.bit_position() % 8 != 0 {
1041 idr_hdr.u(1, 0);
1042 }
1043 let mut idr_rbsp = idr_hdr.into_bytes();
1044 let mut idr_enc = CabacEncoder::new();
1045 idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_terminate(true);
1058 idr_rbsp.extend_from_slice(&idr_enc.finish());
1059
1060 let mut p_hdr = BitEmitter::new();
1062 p_hdr.ue(0); p_hdr.ue(1); p_hdr.u(1, 0); p_hdr.u(1, 0); p_hdr.u(6, 22); p_hdr.ue(0);
1071 p_hdr.ue(0);
1072 while p_hdr.bit_position() % 8 != 0 {
1073 p_hdr.u(1, 0);
1074 }
1075 let mut p_rbsp = p_hdr.into_bytes();
1076 let mut p_enc = CabacEncoder::new();
1077 p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 1); for _ in 0..3 {
1081 p_enc.encode_decision(0, 0, 1); }
1083 p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 0); p_enc.encode_terminate(true);
1087 p_rbsp.extend_from_slice(&p_enc.finish());
1088
1089 fn nal_envelope(nut: u8, rbsp: &[u8]) -> Vec<u8> {
1090 let nut_plus1: u16 = (nut as u16) + 1;
1091 let mut hdr_word: u16 = 0;
1092 hdr_word |= (nut_plus1 & 0x3F) << 9;
1093 let hdr = [(hdr_word >> 8) as u8, (hdr_word & 0xFF) as u8];
1094 let nal_len = (hdr.len() + rbsp.len()) as u32;
1095 let mut out = Vec::new();
1096 out.extend_from_slice(&nal_len.to_be_bytes());
1097 out.extend_from_slice(&hdr);
1098 out.extend_from_slice(rbsp);
1099 out
1100 }
1101 let mut bs = Vec::new();
1102 bs.extend_from_slice(&nal_envelope(24, &sps_rbsp)); bs.extend_from_slice(&nal_envelope(25, &pps_rbsp)); bs.extend_from_slice(&nal_envelope(1, &idr_rbsp)); bs.extend_from_slice(&nal_envelope(0, &p_rbsp)); let params = CodecParameters::video(CodecId::new(CODEC_ID_STR));
1108 let mut dec = decoder::make_decoder(¶ms).unwrap();
1109 let pkt = Packet::new(0, TimeBase::new(1, 90_000), bs).with_pts(0);
1110 dec.send_packet(&pkt).unwrap();
1111 let f0 = dec.receive_frame().unwrap();
1113 let f1 = dec.receive_frame().unwrap();
1114 let v0 = match f0 {
1115 oxideav_core::Frame::Video(v) => v,
1116 _ => panic!("not video"),
1117 };
1118 let v1 = match f1 {
1119 oxideav_core::Frame::Video(v) => v,
1120 _ => panic!("not video"),
1121 };
1122 assert!(v0.planes[0].data.iter().all(|&v| v == 128));
1125 assert!(v1.planes[0].data.iter().all(|&v| v == 128));
1126 assert_eq!(
1127 v0.planes[0].data, v1.planes[0].data,
1128 "P frame must equal IDR"
1129 );
1130 assert_eq!(v0.planes[1].data, v1.planes[1].data);
1131 assert_eq!(v0.planes[2].data, v1.planes[2].data);
1132 let mse: f64 = v0.planes[0]
1134 .data
1135 .iter()
1136 .zip(v1.planes[0].data.iter())
1137 .map(|(&a, &b)| (a as f64 - b as f64).powi(2))
1138 .sum::<f64>()
1139 / v0.planes[0].data.len() as f64;
1140 assert_eq!(mse, 0.0, "PSNR must be infinite for identical frames");
1141 }
1142
1143 #[test]
1156 fn round8_rpl_non_idr_decodes_to_two_frames() {
1157 use crate::cabac::CabacEncoder;
1158 use oxideav_core::{CodecParameters, Packet, TimeBase};
1159 let sps_rbsp = build_rpl_sps_rbsp(32, 32);
1160 let pps_rbsp = build_baseline_pps_rbsp();
1161
1162 let mut idr_hdr = BitEmitter::new();
1164 idr_hdr.ue(0);
1165 idr_hdr.ue(2); idr_hdr.u(1, 0); idr_hdr.u(1, 0); idr_hdr.u(6, 22); idr_hdr.ue(0);
1170 idr_hdr.ue(0);
1171 while idr_hdr.bit_position() % 8 != 0 {
1172 idr_hdr.u(1, 0);
1173 }
1174 let mut idr_rbsp = idr_hdr.into_bytes();
1175 let mut idr_enc = CabacEncoder::new();
1176 idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_terminate(true);
1182 idr_rbsp.extend_from_slice(&idr_enc.finish());
1183
1184 let mut p_hdr = BitEmitter::new();
1186 p_hdr.ue(0); p_hdr.ue(1); p_hdr.u(8, 1);
1190 p_hdr.ue(1); p_hdr.ue(1); p_hdr.u(1, 0); p_hdr.ue(1);
1202 p_hdr.ue(1);
1203 p_hdr.u(1, 0);
1204 p_hdr.u(1, 0); p_hdr.u(1, 0); p_hdr.u(6, 22); p_hdr.ue(0);
1210 p_hdr.ue(0);
1211 while p_hdr.bit_position() % 8 != 0 {
1212 p_hdr.u(1, 0);
1213 }
1214 let mut p_rbsp = p_hdr.into_bytes();
1215 let mut p_enc = CabacEncoder::new();
1216 p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 1); for _ in 0..3 {
1220 p_enc.encode_decision(0, 0, 1); }
1222 p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 0); p_enc.encode_terminate(true);
1226 p_rbsp.extend_from_slice(&p_enc.finish());
1227
1228 fn nal_envelope(nut: u8, rbsp: &[u8]) -> Vec<u8> {
1229 let nut_plus1: u16 = (nut as u16) + 1;
1230 let mut hdr_word: u16 = 0;
1231 hdr_word |= (nut_plus1 & 0x3F) << 9;
1232 let hdr = [(hdr_word >> 8) as u8, (hdr_word & 0xFF) as u8];
1233 let nal_len = (hdr.len() + rbsp.len()) as u32;
1234 let mut out = Vec::new();
1235 out.extend_from_slice(&nal_len.to_be_bytes());
1236 out.extend_from_slice(&hdr);
1237 out.extend_from_slice(rbsp);
1238 out
1239 }
1240 let mut bs = Vec::new();
1241 bs.extend_from_slice(&nal_envelope(24, &sps_rbsp));
1242 bs.extend_from_slice(&nal_envelope(25, &pps_rbsp));
1243 bs.extend_from_slice(&nal_envelope(1, &idr_rbsp));
1244 bs.extend_from_slice(&nal_envelope(0, &p_rbsp));
1245
1246 let params = CodecParameters::video(CodecId::new(CODEC_ID_STR));
1247 let mut dec = decoder::make_decoder(¶ms).unwrap();
1248 let pkt = Packet::new(0, TimeBase::new(1, 90_000), bs).with_pts(0);
1249 dec.send_packet(&pkt).unwrap();
1250 let f0 = dec.receive_frame().unwrap();
1251 let f1 = dec.receive_frame().unwrap();
1252 let v0 = match f0 {
1253 oxideav_core::Frame::Video(v) => v,
1254 _ => panic!("not video"),
1255 };
1256 let v1 = match f1 {
1257 oxideav_core::Frame::Video(v) => v,
1258 _ => panic!("not video"),
1259 };
1260 assert!(v0.planes[0].data.iter().all(|&v| v == 128));
1262 assert!(v1.planes[0].data.iter().all(|&v| v == 128));
1263 assert_eq!(v0.planes[0].data, v1.planes[0].data);
1264 let mse: f64 = v0.planes[0]
1265 .data
1266 .iter()
1267 .zip(v1.planes[0].data.iter())
1268 .map(|(&a, &b)| (a as f64 - b as f64).powi(2))
1269 .sum::<f64>()
1270 / v0.planes[0].data.len() as f64;
1271 assert_eq!(mse, 0.0, "RPL P-slice must be bit-identical to the IDR");
1272 }
1273
1274 #[test]
1282 fn round9_three_frame_idr_p_p_with_dpb() {
1283 use crate::cabac::CabacEncoder;
1284 use oxideav_core::{CodecParameters, Packet, TimeBase};
1285 let sps_rbsp = build_rpl_sps_rbsp(32, 32);
1286 let pps_rbsp = build_baseline_pps_rbsp();
1287
1288 let mut idr_hdr = BitEmitter::new();
1290 idr_hdr.ue(0);
1291 idr_hdr.ue(2); idr_hdr.u(1, 0); idr_hdr.u(1, 0); idr_hdr.u(6, 22); idr_hdr.ue(0);
1296 idr_hdr.ue(0);
1297 while idr_hdr.bit_position() % 8 != 0 {
1298 idr_hdr.u(1, 0);
1299 }
1300 let mut idr_rbsp = idr_hdr.into_bytes();
1301 let mut idr_enc = CabacEncoder::new();
1302 idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_decision(0, 0, 0); idr_enc.encode_terminate(true);
1308 idr_rbsp.extend_from_slice(&idr_enc.finish());
1309
1310 fn build_p_slice(cur_poc_lsb: u32) -> Vec<u8> {
1312 use crate::cabac::CabacEncoder;
1313 let mut p_hdr = BitEmitter::new();
1314 p_hdr.ue(0); p_hdr.ue(1); p_hdr.u(8, cur_poc_lsb); p_hdr.ue(1);
1319 p_hdr.ue(1);
1320 p_hdr.u(1, 0);
1321 p_hdr.ue(1);
1323 p_hdr.ue(1);
1324 p_hdr.u(1, 0);
1325 p_hdr.u(1, 0); p_hdr.u(1, 0); p_hdr.u(6, 22); p_hdr.ue(0);
1329 p_hdr.ue(0);
1330 while p_hdr.bit_position() % 8 != 0 {
1331 p_hdr.u(1, 0);
1332 }
1333 let mut p_rbsp = p_hdr.into_bytes();
1334 let mut p_enc = CabacEncoder::new();
1335 p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 1); for _ in 0..3 {
1338 p_enc.encode_decision(0, 0, 1); }
1340 p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 0); p_enc.encode_decision(0, 0, 0); p_enc.encode_terminate(true);
1344 p_rbsp.extend_from_slice(&p_enc.finish());
1345 p_rbsp
1346 }
1347 let p1_rbsp = build_p_slice(1);
1348 let p2_rbsp = build_p_slice(2);
1349
1350 fn nal_envelope(nut: u8, rbsp: &[u8]) -> Vec<u8> {
1351 let nut_plus1: u16 = (nut as u16) + 1;
1352 let mut hdr_word: u16 = 0;
1353 hdr_word |= (nut_plus1 & 0x3F) << 9;
1354 let hdr = [(hdr_word >> 8) as u8, (hdr_word & 0xFF) as u8];
1355 let nal_len = (hdr.len() + rbsp.len()) as u32;
1356 let mut out = Vec::new();
1357 out.extend_from_slice(&nal_len.to_be_bytes());
1358 out.extend_from_slice(&hdr);
1359 out.extend_from_slice(rbsp);
1360 out
1361 }
1362 let mut bs = Vec::new();
1363 bs.extend_from_slice(&nal_envelope(24, &sps_rbsp));
1364 bs.extend_from_slice(&nal_envelope(25, &pps_rbsp));
1365 bs.extend_from_slice(&nal_envelope(1, &idr_rbsp));
1366 bs.extend_from_slice(&nal_envelope(0, &p1_rbsp));
1367 bs.extend_from_slice(&nal_envelope(0, &p2_rbsp));
1368
1369 let params = CodecParameters::video(CodecId::new(CODEC_ID_STR));
1370 let mut dec = decoder::make_decoder(¶ms).unwrap();
1371 let pkt = Packet::new(0, TimeBase::new(1, 90_000), bs).with_pts(0);
1372 dec.send_packet(&pkt).unwrap();
1373 let f0 = dec.receive_frame().unwrap();
1374 let f1 = dec.receive_frame().unwrap();
1375 let f2 = dec.receive_frame().unwrap();
1376 let v0 = match f0 {
1377 oxideav_core::Frame::Video(v) => v,
1378 _ => panic!("not video"),
1379 };
1380 let v1 = match f1 {
1381 oxideav_core::Frame::Video(v) => v,
1382 _ => panic!("not video"),
1383 };
1384 let v2 = match f2 {
1385 oxideav_core::Frame::Video(v) => v,
1386 _ => panic!("not video"),
1387 };
1388 assert!(v0.planes[0].data.iter().all(|&v| v == 128));
1389 assert!(v1.planes[0].data.iter().all(|&v| v == 128));
1390 assert!(v2.planes[0].data.iter().all(|&v| v == 128));
1391 assert_eq!(v0.planes[0].data, v1.planes[0].data);
1392 assert_eq!(v1.planes[0].data, v2.planes[0].data);
1393 }
1394
1395 #[test]
1401 fn round10_flush_after_receive_is_idempotent() {
1402 use crate::cabac::CabacEncoder;
1403 use oxideav_core::{CodecParameters, Packet, TimeBase};
1404 let sps_rbsp = build_rpl_sps_rbsp(32, 32);
1405 let pps_rbsp = build_baseline_pps_rbsp();
1406 let mut idr_hdr = BitEmitter::new();
1408 idr_hdr.ue(0);
1409 idr_hdr.ue(2);
1410 idr_hdr.u(1, 0);
1411 idr_hdr.u(1, 0);
1412 idr_hdr.u(6, 22);
1413 idr_hdr.ue(0);
1414 idr_hdr.ue(0);
1415 while idr_hdr.bit_position() % 8 != 0 {
1416 idr_hdr.u(1, 0);
1417 }
1418 let mut idr_rbsp = idr_hdr.into_bytes();
1419 let mut idr_enc = CabacEncoder::new();
1420 idr_enc.encode_decision(0, 0, 0);
1421 idr_enc.encode_decision(0, 0, 0);
1422 idr_enc.encode_decision(0, 0, 0);
1423 idr_enc.encode_decision(0, 0, 0);
1424 idr_enc.encode_decision(0, 0, 0);
1425 idr_enc.encode_terminate(true);
1426 idr_rbsp.extend_from_slice(&idr_enc.finish());
1427 let mut p_hdr = BitEmitter::new();
1429 p_hdr.ue(0);
1430 p_hdr.ue(1);
1431 p_hdr.u(8, 1);
1432 p_hdr.ue(1);
1433 p_hdr.ue(1);
1434 p_hdr.u(1, 0);
1435 p_hdr.ue(1);
1436 p_hdr.ue(1);
1437 p_hdr.u(1, 0);
1438 p_hdr.u(1, 0);
1439 p_hdr.u(1, 0);
1440 p_hdr.u(6, 22);
1441 p_hdr.ue(0);
1442 p_hdr.ue(0);
1443 while p_hdr.bit_position() % 8 != 0 {
1444 p_hdr.u(1, 0);
1445 }
1446 let mut p_rbsp = p_hdr.into_bytes();
1447 let mut p_enc = CabacEncoder::new();
1448 p_enc.encode_decision(0, 0, 0);
1449 p_enc.encode_decision(0, 0, 1);
1450 for _ in 0..3 {
1451 p_enc.encode_decision(0, 0, 1);
1452 }
1453 p_enc.encode_decision(0, 0, 0);
1454 p_enc.encode_decision(0, 0, 0);
1455 p_enc.encode_decision(0, 0, 0);
1456 p_enc.encode_terminate(true);
1457 p_rbsp.extend_from_slice(&p_enc.finish());
1458
1459 fn nal_envelope(nut: u8, rbsp: &[u8]) -> Vec<u8> {
1460 let nut_plus1: u16 = (nut as u16) + 1;
1461 let mut hdr_word: u16 = 0;
1462 hdr_word |= (nut_plus1 & 0x3F) << 9;
1463 let hdr = [(hdr_word >> 8) as u8, (hdr_word & 0xFF) as u8];
1464 let nal_len = (hdr.len() + rbsp.len()) as u32;
1465 let mut out = Vec::new();
1466 out.extend_from_slice(&nal_len.to_be_bytes());
1467 out.extend_from_slice(&hdr);
1468 out.extend_from_slice(rbsp);
1469 out
1470 }
1471 let mut bs = Vec::new();
1472 bs.extend_from_slice(&nal_envelope(24, &sps_rbsp));
1473 bs.extend_from_slice(&nal_envelope(25, &pps_rbsp));
1474 bs.extend_from_slice(&nal_envelope(1, &idr_rbsp));
1475 bs.extend_from_slice(&nal_envelope(0, &p_rbsp));
1476
1477 let params = CodecParameters::video(CodecId::new(CODEC_ID_STR));
1478 let mut dec = decoder::make_decoder(¶ms).unwrap();
1479 let pkt = Packet::new(0, TimeBase::new(1, 90_000), bs).with_pts(0);
1480 dec.send_packet(&pkt).unwrap();
1481 let _f0 = dec.receive_frame().unwrap();
1483 let _f1 = dec.receive_frame().unwrap();
1484 dec.flush().unwrap();
1486 let next = dec.receive_frame();
1487 assert!(matches!(next, Err(oxideav_core::Error::NeedMore)));
1488 }
1489}