1#[cfg(test)]
6mod dummy;
7#[cfg(feature = "vaapi")]
8mod vaapi;
9
10use std::cell::RefCell;
11use std::io::Cursor;
12use std::os::fd::AsFd;
13use std::os::fd::BorrowedFd;
14use std::rc::Rc;
15
16use anyhow::anyhow;
17use anyhow::Context;
18
19use crate::codec::h265::dpb::Dpb;
20use crate::codec::h265::dpb::DpbEntry;
21use crate::codec::h265::parser::Nalu;
22use crate::codec::h265::parser::NaluType;
23use crate::codec::h265::parser::Parser;
24use crate::codec::h265::parser::Pps;
25use crate::codec::h265::parser::ShortTermRefPicSet;
26use crate::codec::h265::parser::Slice;
27use crate::codec::h265::parser::SliceHeader;
28use crate::codec::h265::parser::Sps;
29use crate::codec::h265::picture::PictureData;
30use crate::codec::h265::picture::Reference;
31use crate::decoder::stateless::DecodeError;
32use crate::decoder::stateless::DecodingState;
33use crate::decoder::stateless::NewPictureResult;
34use crate::decoder::stateless::PoolLayer;
35use crate::decoder::stateless::StatelessBackendResult;
36use crate::decoder::stateless::StatelessCodec;
37use crate::decoder::stateless::StatelessDecoder;
38use crate::decoder::stateless::StatelessDecoderBackend;
39use crate::decoder::stateless::StatelessDecoderBackendPicture;
40use crate::decoder::stateless::StatelessVideoDecoder;
41use crate::decoder::stateless::TryFormat;
42use crate::decoder::BlockingMode;
43use crate::decoder::DecodedHandle;
44use crate::decoder::DecoderEvent;
45use crate::decoder::StreamInfo;
46use crate::Resolution;
47
48const MAX_DPB_SIZE: usize = 16;
49
50pub(crate) fn clip3(x: i32, y: i32, z: i32) -> i32 {
52 if z < x {
53 x
54 } else if z > y {
55 y
56 } else {
57 z
58 }
59}
60
61const fn up_right_diagonal<const N: usize, const ROWS: usize>() -> [usize; N] {
63 assert!(ROWS * ROWS == N);
66
67 let mut i = 0;
68 let mut x = 0i32;
69 let mut y = 0i32;
70 let mut ret = [0; N];
71
72 loop {
73 while y >= 0 {
74 if x < (ROWS as i32) && y < (ROWS as i32) {
75 ret[i] = (x + ROWS as i32 * y) as usize;
76 i += 1;
77 }
78 y -= 1;
79 x += 1;
80 }
81
82 y = x;
83 x = 0;
84 if i >= N {
85 break;
86 }
87 }
88
89 ret
90}
91
92const UP_RIGHT_DIAGONAL_4X4: [usize; 16] = up_right_diagonal::<16, 4>();
93const UP_RIGHT_DIAGONAL_8X8: [usize; 64] = up_right_diagonal::<64, 8>();
94
95fn get_raster_from_up_right_diagonal_8x8(src: [u8; 64], dst: &mut [u8; 64]) {
96 for i in 0..64 {
97 dst[UP_RIGHT_DIAGONAL_8X8[i]] = src[i];
98 }
99}
100
101fn get_raster_from_up_right_diagonal_4x4(src: [u8; 16], dst: &mut [u8; 16]) {
102 for i in 0..16 {
103 dst[UP_RIGHT_DIAGONAL_4X4[i]] = src[i];
104 }
105}
106
107pub trait StatelessH265DecoderBackend:
109 StatelessDecoderBackend + StatelessDecoderBackendPicture<H265>
110{
111 fn new_sequence(&mut self, sps: &Sps) -> StatelessBackendResult<()>;
113
114 fn new_picture(
116 &mut self,
117 coded_resolution: Resolution,
118 timestamp: u64,
119 ) -> NewPictureResult<Self::Picture>;
120
121 #[allow(clippy::too_many_arguments)]
123 fn begin_picture(
124 &mut self,
125 picture: &mut Self::Picture,
126 picture_data: &PictureData,
127 sps: &Sps,
128 pps: &Pps,
129 dpb: &Dpb<Self::Handle>,
130 rps: &RefPicSet<Self::Handle>,
131 slice: &Slice,
132 ) -> StatelessBackendResult<()>;
133
134 #[allow(clippy::too_many_arguments)]
136 fn decode_slice(
137 &mut self,
138 picture: &mut Self::Picture,
139 slice: &Slice,
140 sps: &Sps,
141 pps: &Pps,
142 ref_pic_list0: &[Option<RefPicListEntry<Self::Handle>>; 16],
143 ref_pic_list1: &[Option<RefPicListEntry<Self::Handle>>; 16],
144 ) -> StatelessBackendResult<()>;
145
146 fn submit_picture(&mut self, picture: Self::Picture) -> StatelessBackendResult<Self::Handle>;
150}
151
152#[derive(Clone)]
155pub enum RefPicListEntry<T> {
156 CurrentPicture(PictureData),
157 DpbEntry(DpbEntry<T>),
158}
159
160enum BumpingType {
161 BeforeDecoding,
162 AfterDecoding,
163}
164
165enum RenegotiationType<'a> {
166 CurrentSps,
167 NewSps(&'a Sps),
168}
169
170#[derive(Clone, Debug, Default, PartialEq, Eq)]
172struct NegotiationInfo {
173 coded_resolution: Resolution,
175 general_profile_idc: u8,
176 bit_depth_luma_minus8: u8,
177 bit_depth_chroma_minus8: u8,
178 chroma_format_idc: u8,
179}
180
181impl From<&Sps> for NegotiationInfo {
182 fn from(sps: &Sps) -> Self {
183 NegotiationInfo {
184 coded_resolution: Resolution {
185 width: sps.width().into(),
186 height: sps.height().into(),
187 },
188 general_profile_idc: sps.profile_tier_level.general_profile_idc,
189 bit_depth_luma_minus8: sps.bit_depth_luma_minus8,
190 bit_depth_chroma_minus8: sps.bit_depth_chroma_minus8,
191 chroma_format_idc: sps.chroma_format_idc,
192 }
193 }
194}
195
196#[derive(Clone, Debug)]
198pub struct RefPicSet<T> {
199 curr_delta_poc_msb_present_flag: [bool; MAX_DPB_SIZE],
200 foll_delta_poc_msb_present_flag: [bool; MAX_DPB_SIZE],
201
202 num_poc_st_curr_before: usize,
203 num_poc_st_curr_after: usize,
204 num_poc_st_foll: usize,
205 num_poc_lt_curr: usize,
206 num_poc_lt_foll: usize,
207
208 poc_st_curr_before: [i32; MAX_DPB_SIZE],
209 poc_st_curr_after: [i32; MAX_DPB_SIZE],
210 poc_st_foll: [i32; MAX_DPB_SIZE],
211 poc_lt_curr: [i32; MAX_DPB_SIZE],
212 poc_lt_foll: [i32; MAX_DPB_SIZE],
213
214 ref_pic_set_lt_curr: [Option<DpbEntry<T>>; MAX_DPB_SIZE],
215 ref_pic_set_st_curr_after: [Option<DpbEntry<T>>; MAX_DPB_SIZE],
216 ref_pic_set_st_curr_before: [Option<DpbEntry<T>>; MAX_DPB_SIZE],
217 ref_pic_set_st_foll: [Option<DpbEntry<T>>; MAX_DPB_SIZE],
218 ref_pic_set_lt_foll: [Option<DpbEntry<T>>; MAX_DPB_SIZE],
219}
220
221impl<T: Clone> Default for RefPicSet<T> {
222 fn default() -> Self {
223 Self {
224 curr_delta_poc_msb_present_flag: Default::default(),
225 foll_delta_poc_msb_present_flag: Default::default(),
226 num_poc_st_curr_before: Default::default(),
227 num_poc_st_curr_after: Default::default(),
228 num_poc_st_foll: Default::default(),
229 num_poc_lt_curr: Default::default(),
230 num_poc_lt_foll: Default::default(),
231 poc_st_curr_before: Default::default(),
232 poc_st_curr_after: Default::default(),
233 poc_st_foll: Default::default(),
234 poc_lt_curr: Default::default(),
235 poc_lt_foll: Default::default(),
236 ref_pic_set_lt_curr: Default::default(),
237 ref_pic_set_st_curr_after: Default::default(),
238 ref_pic_set_st_curr_before: Default::default(),
239 ref_pic_set_st_foll: Default::default(),
240 ref_pic_set_lt_foll: Default::default(),
241 }
242 }
243}
244
245struct CurrentPicState<H: DecodedHandle, P> {
249 pic: PictureData,
251 backend_pic: P,
253 ref_pic_lists: ReferencePicLists<H>,
255}
256
257struct ReferencePicLists<T> {
259 ref_pic_list0: [Option<RefPicListEntry<T>>; MAX_DPB_SIZE],
263 ref_pic_list1: [Option<RefPicListEntry<T>>; MAX_DPB_SIZE],
267}
268
269impl<T> Default for ReferencePicLists<T> {
270 fn default() -> Self {
271 Self {
272 ref_pic_list0: Default::default(),
273 ref_pic_list1: Default::default(),
274 }
275 }
276}
277
278pub struct H265DecoderState<H: DecodedHandle, P> {
279 parser: Parser,
281
282 negotiation_info: NegotiationInfo,
284 rps: RefPicSet<H>,
286
287 dpb: Dpb<H>,
289
290 cur_sps_id: u8,
292
293 first_picture_after_eos: bool,
296
297 first_picture_in_bitstream: bool,
299 prev_tid_0_pic: Option<PictureData>,
301
302 max_pic_order_cnt_lsb: i32,
304 irap_no_rasl_output_flag: bool,
306
307 last_independent_slice_header: Option<SliceHeader>,
310
311 current_pic: Option<CurrentPicState<H, P>>,
315
316 pending_pps: Vec<Nalu<'static>>,
317}
318
319impl<H, P> Default for H265DecoderState<H, P>
320where
321 H: DecodedHandle + Clone,
322{
323 fn default() -> Self {
324 H265DecoderState {
325 parser: Default::default(),
326 negotiation_info: Default::default(),
327 rps: Default::default(),
328 dpb: Default::default(),
329 cur_sps_id: Default::default(),
330 first_picture_after_eos: true,
331 first_picture_in_bitstream: true,
332 prev_tid_0_pic: Default::default(),
333 max_pic_order_cnt_lsb: Default::default(),
334 irap_no_rasl_output_flag: Default::default(),
335 last_independent_slice_header: Default::default(),
336 current_pic: Default::default(),
337 pending_pps: Default::default(),
338 }
339 }
340}
341
342pub struct H265;
352
353impl StatelessCodec for H265 {
354 type FormatInfo = Sps;
355 type DecoderState<H: DecodedHandle, P> = H265DecoderState<H, P>;
356}
357
358impl<B> StatelessDecoder<H265, B>
359where
360 B: StatelessH265DecoderBackend + TryFormat<H265>,
361 B::Handle: Clone,
362{
363 fn negotiation_possible(
365 sps: &Sps,
366 dpb: &Dpb<B::Handle>,
367 old_negotiation_info: &NegotiationInfo,
368 ) -> bool {
369 let negotiation_info = NegotiationInfo::from(sps);
370 let max_dpb_size = std::cmp::min(sps.max_dpb_size(), 16);
371 let prev_max_dpb_size = dpb.max_num_pics();
372
373 *old_negotiation_info != negotiation_info || prev_max_dpb_size != max_dpb_size
374 }
375
376 fn apply_sps(&mut self, sps: &Sps) -> anyhow::Result<()> {
378 self.drain()?;
379 self.codec.negotiation_info = NegotiationInfo::from(sps);
380
381 let max_dpb_size = std::cmp::min(sps.max_dpb_size(), 16);
382 self.codec.dpb.set_max_num_pics(max_dpb_size);
383 self.coded_resolution = Resolution {
384 width: u32::from(sps.width()),
385 height: u32::from(sps.height()),
386 };
387 Ok(())
388 }
389
390 fn st_ref_pic_set<'a>(hdr: &'a SliceHeader, sps: &'a Sps) -> &'a ShortTermRefPicSet {
392 if hdr.curr_rps_idx == sps.num_short_term_ref_pic_sets {
393 &hdr.short_term_ref_pic_set
394 } else {
395 &sps.short_term_ref_pic_set[usize::from(hdr.curr_rps_idx)]
396 }
397 }
398
399 fn decode_rps(&mut self, slice: &Slice, cur_pic: &PictureData) -> anyhow::Result<()> {
401 let hdr = &slice.header;
402
403 if cur_pic.nalu_type.is_irap() && cur_pic.no_rasl_output_flag {
404 self.codec.dpb.mark_all_as_unused_for_ref();
405 }
406
407 if slice.nalu.header.type_.is_idr() {
408 self.codec.rps.poc_st_curr_before = Default::default();
409 self.codec.rps.poc_st_curr_after = Default::default();
410 self.codec.rps.poc_st_foll = Default::default();
411 self.codec.rps.poc_lt_curr = Default::default();
412 self.codec.rps.poc_lt_foll = Default::default();
413
414 self.codec.rps.num_poc_st_curr_before = 0;
415 self.codec.rps.num_poc_st_curr_after = 0;
416 self.codec.rps.num_poc_st_foll = 0;
417 self.codec.rps.num_poc_lt_curr = 0;
418 self.codec.rps.num_poc_lt_foll = 0;
419 } else {
420 let sps = self
421 .codec
422 .parser
423 .get_sps(self.codec.cur_sps_id)
424 .context("Invalid SPS")?;
425
426 let curr_st_rps = Self::st_ref_pic_set(hdr, sps);
427 let mut j = 0;
428 let mut k = 0;
429 for i in 0..usize::from(curr_st_rps.num_negative_pics) {
430 let poc = cur_pic.pic_order_cnt_val + curr_st_rps.delta_poc_s0[i];
431
432 if curr_st_rps.used_by_curr_pic_s0[i] {
433 self.codec.rps.poc_st_curr_before[j] = poc;
434 j += 1;
435 } else {
436 self.codec.rps.poc_st_foll[k] = poc;
437 k += 1;
438 }
439 }
440
441 self.codec.rps.num_poc_st_curr_before = j as _;
442
443 let mut j = 0;
444 for i in 0..usize::from(curr_st_rps.num_positive_pics) {
445 let poc = cur_pic.pic_order_cnt_val + curr_st_rps.delta_poc_s1[i];
446
447 if curr_st_rps.used_by_curr_pic_s1[i] {
448 self.codec.rps.poc_st_curr_after[j] = poc;
449 j += 1;
450 } else {
451 self.codec.rps.poc_st_foll[k] = poc;
452 k += 1;
453 }
454 }
455
456 self.codec.rps.num_poc_st_curr_after = j as _;
457 self.codec.rps.num_poc_st_foll = k as _;
458
459 let mut j = 0;
460 let mut k = 0;
461 for i in 0..usize::from(hdr.num_long_term_sps + hdr.num_long_term_pics) {
462 let mut poc_lt = hdr.poc_lsb_lt[i] as i32;
463 if hdr.delta_poc_msb_present_flag[i] {
464 poc_lt += cur_pic.pic_order_cnt_val;
465 let delta_poc =
466 hdr.delta_poc_msb_cycle_lt[i] as i32 * self.codec.max_pic_order_cnt_lsb;
467
468 poc_lt -= delta_poc;
469 poc_lt -= cur_pic.pic_order_cnt_val & (self.codec.max_pic_order_cnt_lsb - 1);
470 }
471
472 if hdr.used_by_curr_pic_lt[i] {
473 self.codec.rps.poc_lt_curr[j] = poc_lt;
474 self.codec.rps.curr_delta_poc_msb_present_flag[j] =
475 hdr.delta_poc_msb_present_flag[i];
476 j += 1;
477 } else {
478 self.codec.rps.poc_lt_foll[k] = poc_lt;
479 self.codec.rps.foll_delta_poc_msb_present_flag[k] =
480 hdr.delta_poc_msb_present_flag[i];
481 k += 1;
482 }
483 }
484
485 self.codec.rps.num_poc_lt_curr = j as _;
486 self.codec.rps.num_poc_lt_foll = k as _;
487 }
488
489 self.derive_and_mark_rps()?;
490 Ok(())
491 }
492
493 fn derive_and_mark_rps(&mut self) -> anyhow::Result<()> {
495 let max_pic_order_cnt_lsb = self.codec.max_pic_order_cnt_lsb;
496
497 for i in 0..self.codec.rps.num_poc_lt_curr {
499 if !self.codec.rps.curr_delta_poc_msb_present_flag[i] {
500 let poc = self.codec.rps.poc_lt_curr[i];
501 let mask = max_pic_order_cnt_lsb - 1;
502 let reference = self.codec.dpb.find_ref_by_poc_masked(poc, mask);
503
504 if reference.is_none() {
505 log::warn!("No reference found for poc {} and mask {}", poc, mask);
506 }
507
508 self.codec.rps.ref_pic_set_lt_curr[i] = reference;
509 } else {
510 let poc = self.codec.rps.poc_lt_curr[i];
511 let reference = self.codec.dpb.find_ref_by_poc(poc);
512
513 if reference.is_none() {
514 log::warn!("No reference found for poc {}", poc);
515 }
516
517 self.codec.rps.ref_pic_set_lt_curr[i] = reference;
518 }
519 }
520
521 for i in 0..self.codec.rps.num_poc_lt_foll {
522 if !self.codec.rps.foll_delta_poc_msb_present_flag[i] {
523 let poc = self.codec.rps.poc_lt_foll[i];
524 let mask = max_pic_order_cnt_lsb - 1;
525 let reference = self.codec.dpb.find_ref_by_poc_masked(poc, mask);
526
527 if reference.is_none() {
528 log::warn!("No reference found for poc {} and mask {}", poc, mask);
529 }
530
531 self.codec.rps.ref_pic_set_lt_foll[i] = reference;
532 } else {
533 let poc = self.codec.rps.poc_lt_foll[i];
534 let reference = self.codec.dpb.find_ref_by_poc(poc);
535
536 if reference.is_none() {
537 log::warn!("No reference found for poc {}", poc);
538 }
539
540 self.codec.rps.ref_pic_set_lt_foll[i] = reference;
541 }
542 }
543
544 for pic in self.codec.rps.ref_pic_set_lt_curr.iter().flatten() {
545 pic.0.borrow_mut().set_reference(Reference::LongTerm);
546 }
547
548 for pic in self.codec.rps.ref_pic_set_lt_foll.iter().flatten() {
549 pic.0.borrow_mut().set_reference(Reference::LongTerm);
550 }
551
552 for i in 0..self.codec.rps.num_poc_st_curr_before {
554 let poc = self.codec.rps.poc_st_curr_before[i];
555 let reference = self.codec.dpb.find_short_term_ref_by_poc(poc);
556
557 if reference.is_none() {
558 log::warn!("No reference found for poc {}", poc);
559 }
560
561 self.codec.rps.ref_pic_set_st_curr_before[i] = reference;
562 }
563
564 for i in 0..self.codec.rps.num_poc_st_curr_after {
565 let poc = self.codec.rps.poc_st_curr_after[i];
566 let reference = self.codec.dpb.find_short_term_ref_by_poc(poc);
567
568 if reference.is_none() {
569 log::warn!("No reference found for poc {}", poc);
570 }
571
572 self.codec.rps.ref_pic_set_st_curr_after[i] = reference;
573 }
574
575 for i in 0..self.codec.rps.num_poc_st_foll {
576 let poc = self.codec.rps.poc_st_foll[i];
577 let reference = self.codec.dpb.find_short_term_ref_by_poc(poc);
578
579 if reference.is_none() {
580 log::warn!("No reference found for poc {}", poc);
581 }
582
583 self.codec.rps.ref_pic_set_st_foll[i] = reference;
584 }
585
586 for dpb_pic in self.codec.dpb.entries() {
591 let find_predicate = |p: &Option<DpbEntry<B::Handle>>| match p {
592 Some(p) => p.0.borrow().pic_order_cnt_val == dpb_pic.0.borrow().pic_order_cnt_val,
593 None => false,
594 };
595
596 if !self.codec.rps.ref_pic_set_lt_curr[0..self.codec.rps.num_poc_lt_curr]
597 .iter()
598 .any(find_predicate)
599 && !self.codec.rps.ref_pic_set_lt_foll[0..self.codec.rps.num_poc_lt_foll]
600 .iter()
601 .any(find_predicate)
602 && !self.codec.rps.ref_pic_set_st_curr_after
603 [0..self.codec.rps.num_poc_st_curr_after]
604 .iter()
605 .any(find_predicate)
606 && !self.codec.rps.ref_pic_set_st_curr_before
607 [0..self.codec.rps.num_poc_st_curr_before]
608 .iter()
609 .any(find_predicate)
610 && !self.codec.rps.ref_pic_set_st_foll[0..self.codec.rps.num_poc_st_foll]
611 .iter()
612 .any(find_predicate)
613 {
614 dpb_pic.0.borrow_mut().set_reference(Reference::None);
615 }
616 }
617
618 let total_rps_len = self.codec.rps.ref_pic_set_lt_curr[0..self.codec.rps.num_poc_lt_curr]
619 .len()
620 + self.codec.rps.ref_pic_set_lt_foll[0..self.codec.rps.num_poc_lt_foll].len()
621 + self.codec.rps.ref_pic_set_st_curr_after[0..self.codec.rps.num_poc_st_curr_after]
622 .len()
623 + self.codec.rps.ref_pic_set_st_curr_before[0..self.codec.rps.num_poc_st_curr_before]
624 .len()
625 + self.codec.rps.ref_pic_set_st_foll[0..self.codec.rps.num_poc_st_foll].len();
626
627 let dpb_len = self.codec.dpb.entries().len();
628 if dpb_len != total_rps_len {
629 log::warn!(
630 "The total RPS length {} is not the same as the DPB length {}",
631 total_rps_len,
632 dpb_len
633 );
634 log::warn!("A reference pic may be in more than one RPS list. This is against the specification. See 8.3.2. NOTE 5")
635 }
636
637 Ok(())
640 }
641
642 fn build_ref_pic_lists(
645 &self,
646 hdr: &SliceHeader,
647 cur_pic: &PictureData,
648 ) -> anyhow::Result<ReferencePicLists<B::Handle>> {
649 let mut ref_pic_lists = ReferencePicLists::default();
650
651 if !hdr.type_.is_p() && !hdr.type_.is_b() {
653 return Ok(ref_pic_lists);
654 }
655
656 let pps = self
657 .codec
658 .parser
659 .get_pps(hdr.pic_parameter_set_id)
660 .context("Invalid PPS in build_ref_pic_lists")?;
661
662 if self.codec.rps.num_poc_st_curr_before == 0
663 && self.codec.rps.num_poc_st_curr_after == 0
664 && self.codec.rps.num_poc_lt_curr == 0
665 && pps.scc_extension_flag
666 && !pps.scc_extension.curr_pic_ref_enabled_flag
667 {
668 log::error!("Bug or broken stream: out of pictures and can't build ref pic lists.");
672 return Ok(ref_pic_lists);
673 }
674
675 let rplm = &hdr.ref_pic_list_modification;
676
677 let num_rps_curr_temp_list0 = std::cmp::max(
678 u32::from(hdr.num_ref_idx_l0_active_minus1) + 1,
679 hdr.num_pic_total_curr,
680 );
681
682 let mut ref_pic_list_temp0: [Option<RefPicListEntry<B::Handle>>; MAX_DPB_SIZE] =
685 Default::default();
686
687 let mut r_idx = 0;
689 assert!(num_rps_curr_temp_list0 as usize <= MAX_DPB_SIZE);
690 while r_idx < num_rps_curr_temp_list0 {
691 let mut i = 0;
692 while i < self.codec.rps.num_poc_st_curr_before && r_idx < num_rps_curr_temp_list0 {
693 ref_pic_list_temp0[r_idx as usize] = self.codec.rps.ref_pic_set_st_curr_before[i]
694 .clone()
695 .map(RefPicListEntry::DpbEntry);
696
697 i += 1;
698 r_idx += 1;
699 }
700
701 let mut i = 0;
702 while i < self.codec.rps.num_poc_st_curr_after && r_idx < num_rps_curr_temp_list0 {
703 ref_pic_list_temp0[r_idx as usize] = self.codec.rps.ref_pic_set_st_curr_after[i]
704 .clone()
705 .map(RefPicListEntry::DpbEntry);
706
707 i += 1;
708 r_idx += 1;
709 }
710
711 let mut i = 0;
712 while i < self.codec.rps.num_poc_lt_curr && r_idx < num_rps_curr_temp_list0 {
713 ref_pic_list_temp0[r_idx as usize] = self.codec.rps.ref_pic_set_lt_curr[i]
714 .clone()
715 .map(RefPicListEntry::DpbEntry);
716
717 i += 1;
718 r_idx += 1;
719 }
720
721 if pps.scc_extension.curr_pic_ref_enabled_flag {
722 ref_pic_list_temp0[r_idx as usize] =
723 Some(RefPicListEntry::CurrentPicture(cur_pic.clone()));
724
725 r_idx += 1;
726 }
727 }
728
729 for r_idx in 0..=usize::from(hdr.num_ref_idx_l0_active_minus1) {
731 let entry = if rplm.ref_pic_list_modification_flag_l0 {
732 let idx = rplm.list_entry_l0[r_idx];
733 ref_pic_list_temp0[idx as usize].clone()
734 } else {
735 ref_pic_list_temp0[r_idx].clone()
736 };
737
738 ref_pic_lists.ref_pic_list0[r_idx] = entry;
739 }
740
741 if pps.scc_extension.curr_pic_ref_enabled_flag
742 && !rplm.ref_pic_list_modification_flag_l0
743 && num_rps_curr_temp_list0 > (u32::from(hdr.num_ref_idx_l0_active_minus1) + 1)
744 {
745 ref_pic_lists.ref_pic_list0[r_idx as usize] =
746 Some(RefPicListEntry::CurrentPicture(cur_pic.clone()));
747 }
748
749 if hdr.type_.is_b() {
750 let mut ref_pic_list_temp1: [Option<RefPicListEntry<B::Handle>>; MAX_DPB_SIZE] =
751 Default::default();
752
753 let num_rps_curr_temp_list1 = std::cmp::max(
754 u32::from(hdr.num_ref_idx_l1_active_minus1) + 1,
755 hdr.num_pic_total_curr,
756 );
757
758 let mut r_idx = 0;
760 assert!(num_rps_curr_temp_list1 as usize <= MAX_DPB_SIZE);
761 while r_idx < num_rps_curr_temp_list1 {
762 let mut i = 0;
763 while i < self.codec.rps.num_poc_st_curr_after && r_idx < num_rps_curr_temp_list1 {
764 ref_pic_list_temp1[r_idx as usize] = self.codec.rps.ref_pic_set_st_curr_after
765 [i]
766 .clone()
767 .map(RefPicListEntry::DpbEntry);
768 i += 1;
769 r_idx += 1;
770 }
771
772 let mut i = 0;
773 while i < self.codec.rps.num_poc_st_curr_before && r_idx < num_rps_curr_temp_list1 {
774 ref_pic_list_temp1[r_idx as usize] = self.codec.rps.ref_pic_set_st_curr_before
775 [i]
776 .clone()
777 .map(RefPicListEntry::DpbEntry);
778 i += 1;
779 r_idx += 1;
780 }
781
782 let mut i = 0;
783 while i < self.codec.rps.num_poc_lt_curr && r_idx < num_rps_curr_temp_list1 {
784 ref_pic_list_temp1[r_idx as usize] = self.codec.rps.ref_pic_set_lt_curr[i]
785 .clone()
786 .map(RefPicListEntry::DpbEntry);
787 i += 1;
788 r_idx += 1;
789 }
790
791 if pps.scc_extension.curr_pic_ref_enabled_flag {
792 ref_pic_list_temp1[r_idx as usize] =
793 Some(RefPicListEntry::CurrentPicture(cur_pic.clone()));
794
795 r_idx += 1;
796 }
797 }
798
799 for r_idx in 0..=usize::from(hdr.num_ref_idx_l1_active_minus1) {
801 let entry = if rplm.ref_pic_list_modification_flag_l1 {
802 let idx = rplm.list_entry_l1[r_idx];
803 ref_pic_list_temp1[idx as usize].clone()
804 } else {
805 ref_pic_list_temp1[r_idx].clone()
806 };
807
808 ref_pic_lists.ref_pic_list1[r_idx] = entry;
809 }
810 }
811
812 Ok(ref_pic_lists)
813 }
814
815 fn drain(&mut self) -> anyhow::Result<()> {
817 log::debug!("Draining the decoder");
818
819 if let Some(cur_pic) = self.codec.current_pic.take() {
821 self.finish_picture(cur_pic)?;
822 }
823
824 let pics = self.codec.dpb.drain();
825
826 log::debug!(
827 "Adding POCs {:?} to the ready queue while draining",
828 pics.iter()
829 .map(|p| p.0.borrow().pic_order_cnt_val)
830 .collect::<Vec<_>>()
831 );
832
833 log::trace!(
834 "{:#?}",
835 pics.iter().map(|p| p.0.borrow()).collect::<Vec<_>>()
836 );
837
838 self.ready_queue.extend(pics.into_iter().map(|h| h.1));
839 self.codec.dpb.clear();
840
841 Ok(())
842 }
843
844 fn clear_ref_lists(&mut self) {
845 if let Some(pic) = self.codec.current_pic.as_mut() {
846 pic.ref_pic_lists = Default::default();
847 }
848
849 self.codec.rps.ref_pic_set_lt_curr = Default::default();
850 self.codec.rps.ref_pic_set_st_curr_after = Default::default();
851 self.codec.rps.ref_pic_set_st_curr_before = Default::default();
852
853 self.codec.rps.num_poc_lt_curr = Default::default();
854 self.codec.rps.num_poc_lt_foll = Default::default();
855 self.codec.rps.num_poc_st_curr_after = Default::default();
856 self.codec.rps.num_poc_st_curr_before = Default::default();
857 self.codec.rps.num_poc_st_foll = Default::default();
858 }
859
860 fn bump_as_needed(
862 &mut self,
863 bumping_type: BumpingType,
864 ) -> anyhow::Result<Vec<DpbEntry<B::Handle>>> {
865 let mut pics = vec![];
866
867 let needs_bumping = match bumping_type {
868 BumpingType::BeforeDecoding => Dpb::<B::Handle>::needs_bumping,
869 BumpingType::AfterDecoding => Dpb::<B::Handle>::needs_additional_bumping,
870 };
871
872 let sps = self
873 .codec
874 .parser
875 .get_sps(self.codec.cur_sps_id)
876 .context("Invalid SPS id")?;
877
878 while needs_bumping(&mut self.codec.dpb, sps) {
879 match self.codec.dpb.bump(false) {
880 Some(pic) => pics.push(pic),
881 None => return Ok(pics),
882 }
883 }
884
885 Ok(pics)
886 }
887
888 fn update_dpb_before_decoding(&mut self, cur_pic: &PictureData) -> anyhow::Result<()> {
890 if cur_pic.is_irap && cur_pic.no_rasl_output_flag && !self.codec.first_picture_after_eos {
891 if cur_pic.no_output_of_prior_pics_flag {
892 self.codec.dpb.clear();
893 } else {
894 self.drain()?;
895 }
896 } else {
897 self.codec.dpb.remove_unused();
898 let bumped = self.bump_as_needed(BumpingType::BeforeDecoding)?;
899
900 log::debug!(
901 "Adding POCs {:?} to the ready queue before decoding",
902 bumped
903 .iter()
904 .map(|p| p.0.borrow().pic_order_cnt_val)
905 .collect::<Vec<_>>()
906 );
907
908 log::trace!(
909 "{:#?}",
910 bumped.iter().map(|p| p.0.borrow()).collect::<Vec<_>>()
911 );
912
913 let bumped = bumped.into_iter().map(|p| p.1).collect::<Vec<_>>();
914 self.ready_queue.extend(bumped);
915 }
916
917 Ok(())
918 }
919
920 #[allow(clippy::type_complexity)]
922 fn begin_picture(
923 &mut self,
924 timestamp: u64,
925 slice: &Slice,
926 ) -> Result<Option<CurrentPicState<B::Handle, B::Picture>>, DecodeError> {
927 self.update_current_set_ids(slice.header.pic_parameter_set_id)?;
928 self.renegotiate_if_needed(RenegotiationType::CurrentSps)?;
929 if matches!(self.decoding_state, DecodingState::AwaitingFormat(_)) {
931 return Err(DecodeError::CheckEvents);
932 }
933
934 let mut backend_pic = self.backend.new_picture(self.coded_resolution, timestamp)?;
935
936 let pic = PictureData::new_from_slice(
937 slice,
938 self.codec
939 .parser
940 .get_pps(slice.header.pic_parameter_set_id)
941 .context("Invalid PPS")?,
942 self.codec.first_picture_in_bitstream,
943 self.codec.first_picture_after_eos,
944 self.codec.prev_tid_0_pic.as_ref(),
945 self.codec.max_pic_order_cnt_lsb,
946 );
947
948 self.codec.first_picture_after_eos = false;
949 self.codec.first_picture_in_bitstream = false;
950
951 if pic.is_irap {
952 self.codec.irap_no_rasl_output_flag = pic.no_rasl_output_flag;
953 } else if pic.nalu_type.is_rasl() && self.codec.irap_no_rasl_output_flag {
954 log::debug!(
962 "Dropping POC {}, as it may not be decodable according to the specification",
963 pic.pic_order_cnt_val
964 );
965
966 return Ok(None);
967 }
968
969 log::debug!("Decode picture POC {}", pic.pic_order_cnt_val);
970
971 self.decode_rps(slice, &pic)?;
972 self.update_dpb_before_decoding(&pic)?;
973
974 let pps = self
975 .codec
976 .parser
977 .get_pps(slice.header.pic_parameter_set_id)
978 .context("invalid PPS ID")?;
979 let sps = self
980 .codec
981 .parser
982 .get_sps(pps.seq_parameter_set_id)
983 .context("invalid SPS ID")?;
984 self.backend.begin_picture(
985 &mut backend_pic,
986 &pic,
987 sps,
988 pps,
989 &self.codec.dpb,
990 &self.codec.rps,
991 slice,
992 )?;
993
994 Ok(Some(CurrentPicState {
995 pic,
996 backend_pic,
997 ref_pic_lists: Default::default(),
998 }))
999 }
1000
1001 fn update_current_set_ids(&mut self, pps_id: u8) -> anyhow::Result<()> {
1002 let pps = self.codec.parser.get_pps(pps_id).context("Invalid PPS")?;
1003
1004 self.codec.cur_sps_id = pps.seq_parameter_set_id;
1005 Ok(())
1006 }
1007
1008 fn handle_slice(
1010 &mut self,
1011 pic: &mut CurrentPicState<B::Handle, B::Picture>,
1012 slice: &Slice,
1013 ) -> anyhow::Result<()> {
1014 self.update_current_set_ids(slice.header.pic_parameter_set_id)?;
1017
1018 let sps = self
1019 .codec
1020 .parser
1021 .get_sps(self.codec.cur_sps_id)
1022 .context("Invalid SPS")?;
1023
1024 assert!(!Self::negotiation_possible(
1027 sps,
1028 &self.codec.dpb,
1029 &self.codec.negotiation_info,
1030 ));
1031
1032 pic.ref_pic_lists = self.build_ref_pic_lists(&slice.header, &pic.pic)?;
1033
1034 let pps = self
1035 .codec
1036 .parser
1037 .get_pps(slice.header.pic_parameter_set_id)
1038 .context("invalid PPS ID")?;
1039 let sps = self
1040 .codec
1041 .parser
1042 .get_sps(pps.seq_parameter_set_id)
1043 .context("invalid SPS ID")?;
1044 self.backend.decode_slice(
1045 &mut pic.backend_pic,
1046 slice,
1047 sps,
1048 pps,
1049 &pic.ref_pic_lists.ref_pic_list0,
1050 &pic.ref_pic_lists.ref_pic_list1,
1051 )?;
1052
1053 Ok(())
1054 }
1055
1056 fn finish_picture(
1057 &mut self,
1058 pic: CurrentPicState<B::Handle, B::Picture>,
1059 ) -> anyhow::Result<()> {
1060 log::debug!("Finishing picture POC {:?}", pic.pic.pic_order_cnt_val);
1061
1062 let handle = self.submit_picture(pic.backend_pic)?;
1064 let pic = pic.pic;
1065
1066 if pic.valid_for_prev_tid0_pic {
1068 self.codec.prev_tid_0_pic = Some(pic.clone());
1069 }
1070
1071 self.clear_ref_lists();
1072
1073 self.codec
1076 .dpb
1077 .store_picture(Rc::new(RefCell::new(pic)), handle)?;
1078 let bumped = self.bump_as_needed(BumpingType::AfterDecoding)?;
1079
1080 log::debug!(
1081 "Adding POCs {:?} to the ready queue after decoding",
1082 bumped
1083 .iter()
1084 .map(|p| p.0.borrow().pic_order_cnt_val)
1085 .collect::<Vec<_>>()
1086 );
1087
1088 log::trace!(
1089 "{:#?}",
1090 bumped.iter().map(|p| p.0.borrow()).collect::<Vec<_>>()
1091 );
1092
1093 let bumped = bumped.into_iter().map(|p| p.1).collect::<Vec<_>>();
1094 self.ready_queue.extend(bumped);
1095
1096 Ok(())
1097 }
1098
1099 fn renegotiate_if_needed(
1100 &mut self,
1101 renegotiation_type: RenegotiationType,
1102 ) -> anyhow::Result<()> {
1103 let sps = match renegotiation_type {
1104 RenegotiationType::CurrentSps => self
1105 .codec
1106 .parser
1107 .get_sps(self.codec.cur_sps_id)
1108 .context("Invalid SPS")?,
1109 RenegotiationType::NewSps(sps) => sps,
1110 };
1111
1112 if Self::negotiation_possible(sps, &self.codec.dpb, &self.codec.negotiation_info) {
1113 self.drain()?;
1115 let sps = match renegotiation_type {
1116 RenegotiationType::CurrentSps => self
1117 .codec
1118 .parser
1119 .get_sps(self.codec.cur_sps_id)
1120 .context("Invalid SPS")?,
1121 RenegotiationType::NewSps(sps) => sps,
1122 };
1123 self.backend.new_sequence(sps)?;
1124 self.await_format_change(sps.clone());
1125 }
1126
1127 Ok(())
1128 }
1129
1130 fn process_nalu(&mut self, timestamp: u64, nalu: Nalu) -> Result<(), DecodeError> {
1131 log::debug!(
1132 "Processing NALU {:?}, length is {}",
1133 nalu.header.type_,
1134 nalu.size
1135 );
1136
1137 match nalu.header.type_ {
1138 NaluType::VpsNut => {
1139 self.codec.parser.parse_vps(&nalu)?;
1140 }
1141 NaluType::SpsNut => {
1142 let sps = self.codec.parser.parse_sps(&nalu)?;
1143 self.codec.max_pic_order_cnt_lsb = 1 << (sps.log2_max_pic_order_cnt_lsb_minus4 + 4);
1144
1145 let pending_pps = std::mem::take(&mut self.codec.pending_pps);
1146
1147 for pps in pending_pps.into_iter() {
1149 if self.codec.parser.parse_pps(&pps).is_err() {
1150 self.codec.pending_pps.push(pps);
1152 }
1153 }
1154 }
1155
1156 NaluType::PpsNut => {
1157 if self.codec.parser.parse_pps(&nalu).is_err() {
1158 self.codec.pending_pps.push(nalu.into_owned())
1159 }
1160 }
1161
1162 NaluType::BlaWLp
1163 | NaluType::BlaWRadl
1164 | NaluType::BlaNLp
1165 | NaluType::IdrWRadl
1166 | NaluType::IdrNLp
1167 | NaluType::TrailN
1168 | NaluType::TrailR
1169 | NaluType::TsaN
1170 | NaluType::TsaR
1171 | NaluType::StsaN
1172 | NaluType::StsaR
1173 | NaluType::RadlN
1174 | NaluType::RadlR
1175 | NaluType::RaslN
1176 | NaluType::RaslR
1177 | NaluType::CraNut => {
1178 let mut slice = self.codec.parser.parse_slice_header(nalu)?;
1179
1180 let first_slice_segment_in_pic_flag = slice.header.first_slice_segment_in_pic_flag;
1181
1182 if slice.header.dependent_slice_segment_flag {
1183 let previous_independent_header = self.codec.last_independent_slice_header.as_ref().ok_or(anyhow!("Cannot process an dependent slice without first processing and independent one"))?.clone();
1184 slice.replace_header(previous_independent_header)?;
1185 } else {
1186 self.codec.last_independent_slice_header = Some(slice.header.clone());
1187 }
1188
1189 let cur_pic = match self.codec.current_pic.take() {
1190 None => self.begin_picture(timestamp, &slice)?,
1192 Some(cur_pic) if first_slice_segment_in_pic_flag => {
1193 self.finish_picture(cur_pic)?;
1194 self.begin_picture(timestamp, &slice)?
1195 }
1196 Some(cur_pic) => Some(cur_pic),
1197 };
1198
1199 if let Some(mut cur_pic) = cur_pic {
1201 self.handle_slice(&mut cur_pic, &slice)?;
1202 self.codec.current_pic = Some(cur_pic);
1203 }
1204 }
1205
1206 NaluType::EosNut => {
1207 self.codec.first_picture_after_eos = true;
1208 }
1209
1210 NaluType::EobNut => {
1211 self.codec.first_picture_in_bitstream = true;
1212 }
1213
1214 other => {
1215 log::debug!("Unsupported NAL unit type {:?}", other,);
1216 }
1217 }
1218
1219 Ok(())
1220 }
1221
1222 fn submit_picture(&mut self, backend_pic: B::Picture) -> Result<B::Handle, DecodeError> {
1224 let handle = self.backend.submit_picture(backend_pic)?;
1225
1226 if self.blocking_mode == BlockingMode::Blocking {
1227 handle.sync()?;
1228 }
1229
1230 Ok(handle)
1231 }
1232}
1233
1234impl<B> StatelessVideoDecoder for StatelessDecoder<H265, B>
1235where
1236 B: StatelessH265DecoderBackend + TryFormat<H265>,
1237 B::Handle: Clone + 'static,
1238{
1239 type Handle = B::Handle;
1240 type FramePool = B::FramePool;
1241
1242 fn decode(&mut self, timestamp: u64, bitstream: &[u8]) -> Result<usize, DecodeError> {
1243 let mut cursor = Cursor::new(bitstream);
1244 let nalu = Nalu::next(&mut cursor)?;
1245
1246 if nalu.header.type_ == NaluType::SpsNut {
1247 let sps = self.codec.parser.parse_sps(&nalu)?.clone();
1248 if matches!(self.decoding_state, DecodingState::AwaitingStreamInfo) {
1249 self.renegotiate_if_needed(RenegotiationType::NewSps(&sps))?;
1251 } else if matches!(self.decoding_state, DecodingState::Reset) {
1252 self.decoding_state = DecodingState::Decoding;
1254 }
1255 } else if matches!(self.decoding_state, DecodingState::Reset) {
1256 let mut cursor = Cursor::new(bitstream);
1257
1258 while let Ok(nalu) = Nalu::next(&mut cursor) {
1259 if nalu.header.type_.is_idr() {
1261 self.decoding_state = DecodingState::Decoding;
1262 break;
1263 }
1264 }
1265 }
1266
1267 let nalu_len = nalu.offset + nalu.size;
1268
1269 match &mut self.decoding_state {
1270 DecodingState::AwaitingStreamInfo | DecodingState::Reset => {
1273 if matches!(
1274 nalu.header.type_,
1275 NaluType::VpsNut | NaluType::SpsNut | NaluType::PpsNut
1276 ) {
1277 self.process_nalu(timestamp, nalu)?;
1278 }
1279 }
1280 DecodingState::AwaitingFormat(_) => return Err(DecodeError::CheckEvents),
1282 DecodingState::Decoding => {
1283 self.process_nalu(timestamp, nalu)?;
1284 }
1285 }
1286
1287 Ok(nalu_len)
1288 }
1289
1290 fn flush(&mut self) -> Result<(), DecodeError> {
1291 self.drain()?;
1292 self.decoding_state = DecodingState::Reset;
1293
1294 Ok(())
1295 }
1296
1297 fn next_event(&mut self) -> Option<DecoderEvent<B::Handle>> {
1298 self.query_next_event(|decoder, sps| {
1299 decoder.apply_sps(sps).unwrap();
1303 })
1304 }
1305
1306 fn frame_pool(&mut self, layer: PoolLayer) -> Vec<&mut B::FramePool> {
1307 self.backend.frame_pool(layer)
1308 }
1309
1310 fn stream_info(&self) -> Option<&StreamInfo> {
1311 self.backend.stream_info()
1312 }
1313
1314 fn poll_fd(&self) -> BorrowedFd {
1315 self.epoll_fd.0.as_fd()
1316 }
1317}
1318
1319#[cfg(test)]
1320pub mod tests {
1321
1322 use crate::codec::h265::parser::Nalu;
1323 use crate::decoder::stateless::h265::H265;
1324 use crate::decoder::stateless::tests::test_decode_stream;
1325 use crate::decoder::stateless::tests::TestStream;
1326 use crate::decoder::stateless::StatelessDecoder;
1327 use crate::decoder::BlockingMode;
1328 use crate::utils::simple_playback_loop;
1329 use crate::utils::simple_playback_loop_owned_frames;
1330 use crate::utils::NalIterator;
1331 use crate::DecodedFormat;
1332
1333 fn test_decoder_dummy(test: &TestStream, blocking_mode: BlockingMode) {
1335 let decoder = StatelessDecoder::<H265, _>::new_dummy(blocking_mode).unwrap();
1336
1337 test_decode_stream(
1338 |d, s, f| {
1339 simple_playback_loop(
1340 d,
1341 NalIterator::<Nalu>::new(s),
1342 f,
1343 &mut simple_playback_loop_owned_frames,
1344 DecodedFormat::NV12,
1345 blocking_mode,
1346 )
1347 },
1348 decoder,
1349 test,
1350 false,
1351 false,
1352 );
1353 }
1354
1355 pub const DECODE_64X64_PROGRESSIVE_I: TestStream = TestStream {
1361 stream: include_bytes!("../../codec/h265/test_data/64x64-I.h265"),
1362 crcs: include_str!("../../codec/h265/test_data/64x64-I.h265.crc"),
1363 };
1364
1365 #[test]
1366 fn test_64x64_progressive_i_block() {
1367 test_decoder_dummy(&DECODE_64X64_PROGRESSIVE_I, BlockingMode::Blocking);
1368 }
1369
1370 #[test]
1371 fn test_64x64_progressive_i_nonblock() {
1372 test_decoder_dummy(&DECODE_64X64_PROGRESSIVE_I, BlockingMode::NonBlocking);
1373 }
1374
1375 pub const DECODE_64X64_PROGRESSIVE_I_P: TestStream = TestStream {
1380 stream: include_bytes!("../../codec/h265/test_data/64x64-I-P.h265"),
1381 crcs: include_str!("../../codec/h265/test_data/64x64-I-P.h265.crc"),
1382 };
1383
1384 #[test]
1385 fn test_64x64_progressive_i_p_block() {
1386 test_decoder_dummy(&DECODE_64X64_PROGRESSIVE_I_P, BlockingMode::Blocking);
1387 }
1388
1389 #[test]
1390 fn test_64x64_progressive_i_p_nonblock() {
1391 test_decoder_dummy(&DECODE_64X64_PROGRESSIVE_I_P, BlockingMode::NonBlocking);
1392 }
1393
1394 pub const DECODE_64X64_PROGRESSIVE_I_P_B_P: TestStream = TestStream {
1399 stream: include_bytes!("../../codec/h265/test_data/64x64-I-P-B-P.h265"),
1400 crcs: include_str!("../../codec/h265/test_data/64x64-I-P-B-P.h265.crc"),
1401 };
1402
1403 #[test]
1404 fn test_64x64_progressive_i_p_b_p_block() {
1405 test_decoder_dummy(&DECODE_64X64_PROGRESSIVE_I_P_B_P, BlockingMode::Blocking);
1406 }
1407
1408 #[test]
1409 fn test_64x64_progressive_i_p_b_p_nonblock() {
1410 test_decoder_dummy(&DECODE_64X64_PROGRESSIVE_I_P_B_P, BlockingMode::NonBlocking);
1411 }
1412
1413 pub const DECODE_TEST_25FPS: TestStream = TestStream {
1415 stream: include_bytes!("../../codec/h265/test_data/test-25fps.h265"),
1416 crcs: include_str!("../../codec/h265/test_data/test-25fps.h265.crc"),
1417 };
1418
1419 #[test]
1420 fn test_25fps_block() {
1421 test_decoder_dummy(&DECODE_TEST_25FPS, BlockingMode::Blocking);
1422 }
1423
1424 #[test]
1425 fn test_25fps_nonblock() {
1426 test_decoder_dummy(&DECODE_TEST_25FPS, BlockingMode::NonBlocking);
1427 }
1428
1429 pub const DECODE_BEAR: TestStream = TestStream {
1431 stream: include_bytes!("../../codec/h265/test_data/bear.h265"),
1432 crcs: include_str!("../../codec/h265/test_data/bear.h265.crc"),
1433 };
1434
1435 #[test]
1436 fn test_bear_block() {
1437 test_decoder_dummy(&DECODE_BEAR, BlockingMode::Blocking);
1438 }
1439
1440 #[test]
1441 fn test_bear_nonblock() {
1442 test_decoder_dummy(&DECODE_BEAR, BlockingMode::NonBlocking);
1443 }
1444
1445 pub const DECODE_BBB: TestStream = TestStream {
1447 stream: include_bytes!("../../codec/h265/test_data/bbb.h265"),
1448 crcs: include_str!("../../codec/h265/test_data/bbb.h265.crc"),
1449 };
1450
1451 #[test]
1452 fn test_bbb_block() {
1453 test_decoder_dummy(&DECODE_BBB, BlockingMode::Blocking);
1454 }
1455
1456 #[test]
1457 fn test_bbb_nonblock() {
1458 test_decoder_dummy(&DECODE_BBB, BlockingMode::NonBlocking);
1459 }
1460}