1#[cfg(not(target_arch = "wasm32"))]
9pub mod decode_bench;
10#[cfg(not(target_arch = "wasm32"))]
12#[doc(hidden)]
13pub use engine::inter_pred::narrow_interp;
14#[cfg(not(target_arch = "wasm32"))]
16#[doc(hidden)]
17pub use engine::profile as decode_profile;
18#[doc(hidden)]
20pub use zvidlib_hevc::engine;
21
22#[allow(unused_imports)]
23use zvidlib_color::color_convert;
24#[allow(unused_imports)]
25use zvidlib_core::*;
26#[cfg(all(
28 feature = "hardware",
29 any(windows, target_os = "linux"),
30 target_pointer_width = "64"
31))]
32use zvidlib_hardware::nvdec;
33#[cfg(all(feature = "hardware", not(target_arch = "wasm32")))]
36#[allow(unused_imports)]
37#[doc(hidden)]
38pub use zvidlib_hardware::readback;
39#[cfg(all(feature = "hardware", target_os = "macos"))]
40use zvidlib_hardware::videotoolbox;
41#[cfg(all(feature = "hardware", windows))]
42use zvidlib_hardware::windows_mf;
43#[cfg(test)]
45#[allow(unused_imports)]
46use zvidlib_container::*;
47
48use std::cmp::Reverse;
49use std::collections::BinaryHeap;
50use std::panic::{AssertUnwindSafe, catch_unwind};
51
52use crate::{
53 CancellationToken, Codec, CodecImplementation, CodecProfile, CodecSupport, ColorRange,
54 DecodedVideoFrame, EncodedVideoSample, Error, ErrorKind, FrameIndex, HardwarePreference,
55 Limits, PixelFormat, Plane, Result, VideoDecoder, VideoDecoderConfig, VideoDecoderFactory,
56 VideoFrame,
57};
58use engine::hvcc::{HvccRecord, parse_hvcc, split_length_prefixed};
59use engine::picture::{Picture, Plane as HevcPlane, sub_wh_c};
60use engine::sequence::{DecodedFrame, SequenceDecoder, SequenceError};
61use engine::sps::SeqParameterSet;
62
63const DEFAULT_REORDER_DEPTH: usize = 8;
64
65pub fn native_hevc_video_decoder_factory() -> impl VideoDecoderFactory {
73 HevcDecoderFactory
74}
75
76#[derive(Clone, Copy, Debug)]
77struct HevcDecoderFactory;
78
79impl VideoDecoderFactory for HevcDecoderFactory {
80 fn capability(&self, configuration: &VideoDecoderConfig) -> CodecSupport {
81 if let unsupported @ (CodecSupport::UnsupportedCodec
82 | CodecSupport::UnsupportedProfile
83 | CodecSupport::InvalidConfiguration { .. }) =
84 self.capability_without_hardware(configuration)
85 {
86 return unsupported;
87 }
88 let parsed = match ParsedConfiguration::parse(configuration, &Limits::default()) {
89 Ok(parsed) => parsed,
90 Err(error) => return invalid_configuration(error.message()),
91 };
92 if configuration.hardware != HardwarePreference::Avoid
93 && !parsed.high_bit_depth
94 && hardware_available(configuration)
95 {
96 return CodecSupport::Supported {
97 implementation: CodecImplementation::Hardware,
98 };
99 }
100 if configuration.hardware == HardwarePreference::Require {
101 return CodecSupport::HardwareUnavailable;
102 }
103 CodecSupport::Supported {
104 implementation: CodecImplementation::Software,
105 }
106 }
107
108 fn create(
109 &self,
110 configuration: &VideoDecoderConfig,
111 limits: &Limits,
112 ) -> Result<Box<dyn VideoDecoder>> {
113 match self.capability_without_hardware(configuration) {
114 CodecSupport::Supported { .. } => {}
115 CodecSupport::UnsupportedCodec => {
116 return Err(Error::new(
117 ErrorKind::Unsupported,
118 "native HEVC decoder requires HEVC",
119 ));
120 }
121 CodecSupport::UnsupportedProfile => {
122 return Err(Error::new(
123 ErrorKind::Unsupported,
124 "native HEVC decoder supports the Main and Main 10 profiles",
125 ));
126 }
127 CodecSupport::HardwareUnavailable => unreachable!("hardware is not checked here"),
128 CodecSupport::InvalidConfiguration { reason } => {
129 return Err(Error::new(ErrorKind::InvalidInput, reason));
130 }
131 _ => {
132 return Err(Error::new(
133 ErrorKind::Unsupported,
134 "native HEVC decoder does not support this configuration",
135 ));
136 }
137 }
138 let parsed = ParsedConfiguration::parse(configuration, limits)?;
139 if configuration.hardware == HardwarePreference::Require && parsed.high_bit_depth {
140 return Err(Error::new(
143 ErrorKind::Unsupported,
144 "hardware HEVC decoding is unavailable (no accelerated backend decodes more than 8 bits per sample)",
145 ));
146 }
147 if configuration.hardware != HardwarePreference::Avoid && !parsed.high_bit_depth {
148 #[cfg_attr(
149 any(target_arch = "wasm32", not(feature = "hardware")),
150 allow(unused_mut)
151 )]
152 let mut hardware_errors = Vec::<String>::new();
153 #[cfg(all(
154 feature = "hardware",
155 any(windows, target_os = "linux"),
156 target_pointer_width = "64"
157 ))]
158 match nvdec::create(configuration, limits, &parsed.record) {
159 Ok(decoder) => return Ok(decoder),
160 Err(error) => hardware_errors.push(format!("NVDEC: {}", error.message())),
161 }
162 #[cfg(all(feature = "hardware", windows))]
163 match windows_mf::create(configuration, limits, &parsed.record) {
164 Ok(decoder) => return Ok(decoder),
165 Err(error) => {
166 hardware_errors.push(format!("Media Foundation: {}", error.message()));
167 }
168 }
169 #[cfg(all(feature = "hardware", target_os = "macos"))]
170 match videotoolbox::create(configuration, limits, &parsed.record) {
171 Ok(decoder) => return Ok(decoder),
172 Err(error) => {
173 hardware_errors.push(format!("VideoToolbox: {}", error.message()));
174 }
175 }
176 if configuration.hardware == HardwarePreference::Require {
177 let detail = if hardware_errors.is_empty() {
178 "no accelerated backend exists for this target".to_owned()
179 } else {
180 hardware_errors.join("; ")
181 };
182 return Err(Error::new(
183 ErrorKind::Unsupported,
184 format!("hardware HEVC decoding is unavailable ({detail})"),
185 ));
186 }
187 }
188 Ok(Box::new(HevcDecoder::new(
189 configuration.clone(),
190 *limits,
191 parsed,
192 )?))
193 }
194}
195
196impl HevcDecoderFactory {
197 fn capability_without_hardware(&self, configuration: &VideoDecoderConfig) -> CodecSupport {
198 if configuration.codec != Codec::Hevc {
199 return CodecSupport::UnsupportedCodec;
200 }
201 if !matches!(
202 configuration.profile,
203 CodecProfile::HevcMain | CodecProfile::HevcMain10
204 ) {
205 return CodecSupport::UnsupportedProfile;
206 }
207 if configuration.output_format != PixelFormat::Rgba8 {
208 return invalid_configuration("native HEVC decoding currently outputs RGBA8");
209 }
210 if configuration.color_range != ColorRange::Limited {
211 return invalid_configuration("native HEVC RGBA output requires limited-range input");
212 }
213 CodecSupport::Supported {
214 implementation: CodecImplementation::Software,
215 }
216 }
217}
218
219fn hardware_available(_configuration: &VideoDecoderConfig) -> bool {
220 #[cfg(all(
221 feature = "hardware",
222 any(windows, target_os = "linux"),
223 target_pointer_width = "64"
224 ))]
225 if nvdec::is_available(_configuration.coded_dimensions) {
226 return true;
227 }
228 #[cfg(all(feature = "hardware", windows))]
229 if windows_mf::is_available(_configuration.coded_dimensions) {
230 return true;
231 }
232 #[cfg(all(feature = "hardware", target_os = "macos"))]
233 if videotoolbox::is_available(_configuration.coded_dimensions) {
234 return true;
235 }
236 false
237}
238
239fn invalid_configuration(reason: impl Into<String>) -> CodecSupport {
240 CodecSupport::InvalidConfiguration {
241 reason: reason.into(),
242 }
243}
244
245#[derive(Debug)]
246struct ParsedConfiguration {
247 record: HvccRecord,
248 high_bit_depth: bool,
251}
252
253impl ParsedConfiguration {
254 fn parse(configuration: &VideoDecoderConfig, limits: &Limits) -> Result<Self> {
255 if configuration.configuration.len() as u64 > limits.max_allocation_bytes {
256 return Err(limit("HEVC configuration exceeds the allocation limit"));
257 }
258 let payload = hvcc_payload(&configuration.configuration)?;
259 let record = parse_hvcc(payload)
260 .map_err(|error| malformed(format!("invalid hvcC configuration: {error}")))?;
261 let max_bit_depth_minus8 = max_bit_depth_minus8(configuration.profile);
262 let profile_idc_allowed = match configuration.profile {
263 CodecProfile::HevcMain10 => matches!(record.general_profile_idc, 1 | 2),
265 _ => record.general_profile_idc == 1,
266 };
267 if !profile_idc_allowed
268 || record.bit_depth_luma_minus8 > max_bit_depth_minus8
269 || record.bit_depth_chroma_minus8 > max_bit_depth_minus8
270 || record.chroma_format_idc != 1
271 {
272 return Err(Error::new(
273 ErrorKind::Unsupported,
274 match configuration.profile {
275 CodecProfile::HevcMain10 => {
276 "native HEVC decoder requires an 8- to 10-bit 4:2:0 Main or Main 10 hvcC configuration"
277 }
278 _ => {
279 "native HEVC decoder requires an 8-bit 4:2:0 Main-profile hvcC configuration"
280 }
281 },
282 ));
283 }
284 let sps_unit = record
285 .nal_units
286 .iter()
287 .find(|unit| unit.header.nal_unit_type == 33)
288 .ok_or_else(|| malformed("hvcC configuration does not contain an SPS"))?;
289 let sps = SeqParameterSet::parse(&sps_unit.rbsp)
290 .map_err(|error| malformed(format!("invalid HEVC SPS: {error}")))?;
291 validate_sps(&sps, configuration, limits)?;
292 let high_bit_depth = record.bit_depth_luma_minus8 != 0
293 || record.bit_depth_chroma_minus8 != 0
294 || sps.bit_depth_luma_minus8 != 0
295 || sps.bit_depth_chroma_minus8 != 0;
296 Ok(Self {
297 record,
298 high_bit_depth,
299 })
300 }
301}
302
303fn max_bit_depth_minus8(profile: CodecProfile) -> u8 {
306 match profile {
307 CodecProfile::HevcMain10 => 2,
308 _ => 0,
309 }
310}
311
312fn hvcc_payload(configuration: &[u8]) -> Result<&[u8]> {
313 if configuration.len() >= 8 && &configuration[4..8] == b"hvcC" {
314 let declared = u32::from_be_bytes(configuration[..4].try_into().expect("four bytes"));
315 if usize::try_from(declared).ok() != Some(configuration.len()) {
316 return Err(malformed(
317 "hvcC box size does not match its configuration bytes",
318 ));
319 }
320 return Ok(&configuration[8..]);
321 }
322 if configuration.first() == Some(&1) {
323 return Ok(configuration);
324 }
325 Err(malformed("HEVC configuration is not an hvcC record"))
326}
327
328fn validate_sps(
329 sps: &SeqParameterSet,
330 configuration: &VideoDecoderConfig,
331 limits: &Limits,
332) -> Result<()> {
333 let max_bit_depth_minus8 = max_bit_depth_minus8(configuration.profile);
334 if sps.chroma_format_idc != 1
335 || sps.separate_color_plane_flag
336 || sps.bit_depth_luma_minus8 > max_bit_depth_minus8
337 || sps.bit_depth_chroma_minus8 > max_bit_depth_minus8
338 {
339 return Err(Error::new(
340 ErrorKind::Unsupported,
341 match configuration.profile {
342 CodecProfile::HevcMain10 => {
343 "native HEVC decoder requires SPS 8- to 10-bit 4:2:0 syntax"
344 }
345 _ => "native HEVC decoder requires SPS Main-profile 8-bit 4:2:0 syntax",
346 },
347 ));
348 }
349 if sps.pic_width_in_luma_samples > limits.max_width
350 || sps.pic_height_in_luma_samples > limits.max_height
351 {
352 return Err(limit("HEVC SPS coded dimensions exceed configured limits"));
353 }
354 let (sub_width, sub_height) = sub_wh_c(sps.chroma_format_idc);
355 let cropped_width = usize::try_from(sps.pic_width_in_luma_samples)
356 .ok()
357 .and_then(|width| {
358 let crop = usize::try_from(
359 sps.conformance_window.left_offset + sps.conformance_window.right_offset,
360 )
361 .ok()?
362 .checked_mul(sub_width)?;
363 width.checked_sub(crop)
364 })
365 .ok_or_else(|| malformed("HEVC SPS horizontal conformance window is invalid"))?;
366 let cropped_height = usize::try_from(sps.pic_height_in_luma_samples)
367 .ok()
368 .and_then(|height| {
369 let crop = usize::try_from(
370 sps.conformance_window.top_offset + sps.conformance_window.bottom_offset,
371 )
372 .ok()?
373 .checked_mul(sub_height)?;
374 height.checked_sub(crop)
375 })
376 .ok_or_else(|| malformed("HEVC SPS vertical conformance window is invalid"))?;
377 if cropped_width != configuration.coded_dimensions.width as usize
378 || cropped_height != configuration.coded_dimensions.height as usize
379 {
380 return Err(malformed(
381 "HEVC SPS display dimensions do not match decoder configuration",
382 ));
383 }
384 let pixels = u64::from(sps.pic_width_in_luma_samples)
385 .checked_mul(u64::from(sps.pic_height_in_luma_samples))
386 .ok_or_else(|| limit("HEVC picture dimensions overflow"))?;
387 let picture_bytes = pixels
388 .checked_mul(6)
389 .ok_or_else(|| limit("HEVC picture allocation overflows"))?;
390 let output_bytes = u64::from(configuration.coded_dimensions.width)
391 .checked_mul(u64::from(configuration.coded_dimensions.height))
392 .and_then(|value| value.checked_mul(4))
393 .ok_or_else(|| limit("HEVC RGBA output allocation overflows"))?;
394 let ordering = sps.sub_layer_ordering_info[usize::from(sps.max_sub_layers_minus1)];
395 let retained_pictures = u64::from(ordering.max_dec_pic_buffering_minus1)
396 .checked_add(2)
397 .ok_or_else(|| limit("HEVC decoded-picture-buffer size overflows"))?;
398 let decoder_bytes = picture_bytes
399 .checked_mul(retained_pictures)
400 .and_then(|value| value.checked_add(output_bytes))
401 .ok_or_else(|| limit("HEVC decoder allocation budget overflows"))?;
402 if decoder_bytes > limits.max_allocation_bytes {
403 return Err(limit(
404 "HEVC decoded-picture buffer exceeds the allocation limit",
405 ));
406 }
407 Ok(())
408}
409
410#[derive(Debug)]
411struct HevcDecoder {
412 configuration: VideoDecoderConfig,
413 limits: Limits,
414 configuration_nals: Vec<engine::nal::NalUnit>,
415 nal_length_size: usize,
416 sequence: SequenceDecoder,
417 reorder: Vec<DecodedFrame>,
418 presentation_indexes: BinaryHeap<Reverse<FrameIndex>>,
419 output_wanted: bool,
421}
422
423impl HevcDecoder {
424 fn new(
425 configuration: VideoDecoderConfig,
426 limits: Limits,
427 parsed: ParsedConfiguration,
428 ) -> Result<Self> {
429 let configuration_nals = parsed.record.nal_units;
430 let nal_length_size = parsed.record.length_size;
431 let mut decoder = Self {
432 configuration,
433 limits,
434 configuration_nals,
435 nal_length_size,
436 sequence: SequenceDecoder::new(),
437 reorder: Vec::new(),
438 presentation_indexes: BinaryHeap::new(),
439 output_wanted: true,
440 };
441 decoder.reinitialize()?;
442 Ok(decoder)
443 }
444
445 fn reinitialize(&mut self) -> Result<()> {
446 self.sequence = SequenceDecoder::new();
447 for unit in self.configuration_nals.clone() {
448 self.sequence.push_nal_unit(unit).map_err(sequence_error)?;
449 }
450 self.reorder.clear();
451 self.presentation_indexes.clear();
452 Ok(())
453 }
454
455 fn collect(&mut self, flush: bool) -> Result<Vec<DecodedVideoFrame>> {
456 let pictures = self.collect_pictures(flush)?;
457 if !self.output_wanted {
458 return Ok(Vec::new());
461 }
462 let mut output = Vec::with_capacity(pictures.len());
463 for (presentation_index, picture) in pictures {
464 output.push(DecodedVideoFrame {
465 presentation_index,
466 frame: picture_to_rgba(&picture, &self.configuration, &self.limits)?,
467 });
468 }
469 Ok(output)
470 }
471
472 fn collect_pictures(&mut self, flush: bool) -> Result<Vec<(FrameIndex, Picture)>> {
483 self.reorder.extend(self.sequence.take_decoded());
484 self.reorder
485 .sort_by_key(|frame| (frame.cvs_index, frame.poc));
486 let depth = if flush {
487 0
488 } else {
489 self.sequence
490 .max_num_reorder_pics()
491 .map_or(DEFAULT_REORDER_DEPTH, |depth| depth as usize)
492 };
493 let mut output = Vec::new();
494 while self.reorder.len() > depth {
495 let decoded = self.reorder.remove(0);
496 if !decoded.output {
497 continue;
498 }
499 let presentation_index = self
500 .presentation_indexes
501 .pop()
502 .ok_or_else(|| {
503 malformed("HEVC decoder produced a picture without a submitted identity")
504 })?
505 .0;
506 output.push((presentation_index, decoded.picture));
507 }
508 Ok(output)
509 }
510
511 fn push_sample(
518 &mut self,
519 sample: &EncodedVideoSample,
520 cancellation: &CancellationToken,
521 ) -> Result<()> {
522 check_canceled(cancellation)?;
523 if sample.data.len() as u64 > self.limits.max_allocation_bytes {
524 return Err(limit("HEVC access unit exceeds the allocation limit"));
525 }
526 let units = split_length_prefixed(&sample.data, self.nal_length_size)
527 .map_err(|error| malformed(format!("invalid HEVC access unit: {error}")))?;
528 self.presentation_indexes
529 .push(Reverse(sample.presentation_index));
530 let skipped_before = self.sequence.skipped_pictures();
531 let result = catch_unwind(AssertUnwindSafe(|| {
532 for unit in units {
533 self.sequence.push_nal_unit(unit)?;
534 }
535 Ok::<(), SequenceError>(())
536 }));
537 match result {
538 Ok(Ok(())) => {
539 if self.sequence.skipped_pictures() != skipped_before {
543 self.presentation_indexes
544 .retain(|Reverse(index)| *index != sample.presentation_index);
545 }
546 Ok(())
547 }
548 Ok(Err(error)) => Err(sequence_error(error)),
549 Err(_) => Err(malformed(
550 "HEVC bitstream triggered an invalid decoder state",
551 )),
552 }
553 }
554
555 #[cfg(not(target_arch = "wasm32"))]
560 fn submit_pictures(
561 &mut self,
562 sample: &EncodedVideoSample,
563 cancellation: &CancellationToken,
564 ) -> Result<Vec<Picture>> {
565 self.push_sample(sample, cancellation)?;
566 Ok(self
567 .collect_pictures(false)?
568 .into_iter()
569 .map(|(_, picture)| picture)
570 .collect())
571 }
572}
573
574impl VideoDecoder for HevcDecoder {
575 fn submit(
576 &mut self,
577 sample: &EncodedVideoSample,
578 cancellation: &CancellationToken,
579 ) -> Result<Vec<DecodedVideoFrame>> {
580 self.push_sample(sample, cancellation)?;
581 self.collect(false)
582 }
583
584 fn drain(&mut self, cancellation: &CancellationToken) -> Result<Vec<DecodedVideoFrame>> {
585 check_canceled(cancellation)?;
586 match catch_unwind(AssertUnwindSafe(|| self.sequence.flush())) {
587 Ok(Ok(())) => self.collect(true),
588 Ok(Err(error)) => Err(sequence_error(error)),
589 Err(_) => Err(malformed(
590 "HEVC bitstream triggered an invalid decoder state while draining",
591 )),
592 }
593 }
594
595 fn reset(&mut self) -> Result<()> {
596 self.reinitialize()
597 }
598
599 fn set_output_wanted(&mut self, wanted: bool) {
600 self.output_wanted = wanted;
601 }
602}
603
604fn picture_to_rgba(
608 picture: &Picture,
609 configuration: &VideoDecoderConfig,
610 limits: &Limits,
611) -> Result<VideoFrame> {
612 let _profile = engine::profile::scope(engine::profile::Stage::ColorConvert);
613 let max_bit_depth = 8 + max_bit_depth_minus8(configuration.profile);
614 if picture.chroma_array_type() != 1
615 || !(8..=max_bit_depth).contains(&picture.bit_depth_luma())
616 || !(8..=max_bit_depth).contains(&picture.bit_depth_chroma())
617 {
618 return Err(Error::new(
619 ErrorKind::Unsupported,
620 "decoded HEVC picture is not 4:2:0 at a bit depth its profile allows",
621 ));
622 }
623 let width = configuration.coded_dimensions.width as usize;
624 let height = configuration.coded_dimensions.height as usize;
625 if picture.width_luma() < width || picture.height_luma() < height {
626 return Err(malformed(
627 "decoded HEVC picture is smaller than its display dimensions",
628 ));
629 }
630 let stride = width
631 .checked_mul(4)
632 .ok_or_else(|| limit("HEVC RGBA stride overflows"))?;
633 let length = stride
634 .checked_mul(height)
635 .ok_or_else(|| limit("HEVC RGBA allocation overflows"))?;
636 if length as u64 > limits.max_allocation_bytes {
637 return Err(limit("HEVC RGBA output exceeds the allocation limit"));
638 }
639 let luma = picture.plane(HevcPlane::Luma);
640 let cb = picture.plane(HevcPlane::Cb);
641 let cr = picture.plane(HevcPlane::Cr);
642 let chroma_width = picture.plane_dims(HevcPlane::Cb).0;
643 let mut rgba = vec![0_u8; length];
644 if picture.bit_depth_luma() == 8 && picture.bit_depth_chroma() == 8 {
645 color_convert::convert_yuv420_to_rgba(
646 luma,
647 picture.width_luma(),
648 cb,
649 cr,
650 chroma_width,
651 width,
652 height,
653 &mut rgba,
654 stride,
655 );
656 } else {
657 color_convert::convert_high_bit_depth_yuv420_to_rgba(
658 luma,
659 picture.width_luma(),
660 picture.bit_depth_luma(),
661 cb,
662 cr,
663 chroma_width,
664 picture.bit_depth_chroma(),
665 width,
666 height,
667 &mut rgba,
668 stride,
669 );
670 }
671 VideoFrame::new(
672 configuration.coded_dimensions,
673 PixelFormat::Rgba8,
674 configuration.color_range,
675 vec![Plane { data: rgba, stride }],
676 limits,
677 )
678}
679
680fn check_canceled(cancellation: &CancellationToken) -> Result<()> {
681 if cancellation.is_canceled() {
682 Err(Error::new(ErrorKind::Canceled, "codec operation canceled"))
683 } else {
684 Ok(())
685 }
686}
687
688fn sequence_error(error: SequenceError) -> Error {
689 match error {
690 SequenceError::Unsupported(message) => Error::new(ErrorKind::Unsupported, message),
691 other => malformed(format!("invalid HEVC bitstream: {other}")),
692 }
693}
694
695fn malformed(message: impl Into<String>) -> Error {
696 Error::new(ErrorKind::MalformedMedia, message)
697}
698
699fn limit(message: impl Into<String>) -> Error {
700 Error::new(ErrorKind::ResourceLimit, message)
701}
702
703#[doc(hidden)]
707#[must_use]
708pub fn simd_sites() -> Vec<(&'static str, SimdIsa)> {
709 #[cfg(target_arch = "wasm32")]
712 return Vec::new();
713 #[cfg(not(target_arch = "wasm32"))]
714 vec![
715 (
716 "hevc_prediction_filters",
717 engine::simd::detected_isa().as_simd_isa(),
718 ),
719 (
720 "hevc_transforms",
721 engine::transform_simd::detected().as_simd_isa(),
722 ),
723 ]
724}
725
726#[cfg(test)]
727mod tests {
728 use super::*;
729
730 #[test]
731 fn rejects_truncated_configuration_without_panicking() {
732 let limits = Limits::default();
733 let configuration = VideoDecoderConfig {
734 codec: Codec::Hevc,
735 profile: CodecProfile::HevcMain,
736 coded_dimensions: crate::VideoDimensions::new(16, 16, &limits).unwrap(),
737 output_format: PixelFormat::Rgba8,
738 color_range: ColorRange::Limited,
739 hardware: HardwarePreference::Avoid,
740 configuration: vec![1, 2, 3],
741 };
742 let support = native_hevc_video_decoder_factory().capability(&configuration);
743 assert!(matches!(support, CodecSupport::InvalidConfiguration { .. }));
744 }
745
746 fn block_on<T>(future: impl std::future::Future<Output = T>) -> T {
747 use std::task::{Context, Poll, Waker};
748 let mut future = Box::pin(future);
749 let mut context = Context::from_waker(Waker::noop());
750 loop {
751 if let Poll::Ready(value) = future.as_mut().poll(&mut context) {
752 return value;
753 }
754 }
755 }
756
757 fn main10_fixture(
759 profile: CodecProfile,
760 hardware: HardwarePreference,
761 ) -> (VideoDecoderConfig, Vec<EncodedVideoSample>) {
762 use crate::Mp4DemuxerOptions;
763 use crate::io::MemorySource;
764 use crate::mp4_demux::Mp4Demuxer;
765
766 let limits = Limits::default();
767 let source = MemorySource::new(
768 include_bytes!("../tests/fixtures/bbb_hevc_main10_128x72.mp4").to_vec(),
769 );
770 let movie = block_on(Mp4Demuxer::open(&source, Mp4DemuxerOptions::default())).unwrap();
771 let track = &movie.tracks[0];
772 let samples = block_on(track.to_encoded_video_samples(&source, &limits)).unwrap();
773 let configuration = VideoDecoderConfig {
774 codec: Codec::Hevc,
775 profile,
776 coded_dimensions: track.dimensions.unwrap(),
777 output_format: PixelFormat::Rgba8,
778 color_range: ColorRange::Limited,
779 hardware,
780 configuration: track.decoder_config.clone(),
781 };
782 (configuration, samples)
783 }
784
785 #[test]
789 fn main10_pictures_match_an_independent_reference_decode() {
790 let digests = |fixture: &str| {
791 fixture
792 .lines()
793 .map(|line| line.split_once(' ').unwrap().1.to_owned())
794 .collect::<Vec<_>>()
795 };
796 let expected = digests(include_str!(
797 "../tests/fixtures/bbb_hevc_main10_128x72_yuv420p10le.sha256"
798 ));
799 let expected_rgba = digests(include_str!(
800 "../tests/fixtures/bbb_hevc_main10_128x72_rgba.sha256"
801 ));
802 let limits = Limits::default();
803 let (configuration, samples) =
804 main10_fixture(CodecProfile::HevcMain10, HardwarePreference::Avoid);
805 let parsed = ParsedConfiguration::parse(&configuration, &limits).unwrap();
806 assert!(parsed.high_bit_depth);
807 let mut decoder = HevcDecoder::new(configuration.clone(), limits, parsed).unwrap();
808 let cancellation = CancellationToken::new();
809 let mut pictures = Vec::new();
810 for sample in &samples {
811 decoder.push_sample(sample, &cancellation).unwrap();
812 pictures.extend(decoder.collect_pictures(false).unwrap());
813 }
814 decoder.sequence.flush().unwrap();
815 pictures.extend(decoder.collect_pictures(true).unwrap());
816 pictures.sort_by_key(|(index, _)| *index);
817
818 assert_eq!(pictures.len(), expected.len());
819 for (((index, picture), expected), expected_rgba) in
820 pictures.iter().zip(&expected).zip(&expected_rgba)
821 {
822 assert_eq!(picture.bit_depth_luma(), 10);
823 assert_eq!(picture.bit_depth_chroma(), 10);
824 let digest = crate::conformance::FrameDigest(crate::conformance::sha256(
825 &picture.to_planar_le16(),
826 ));
827 assert_eq!(&digest.to_hex(), expected, "frame {}", index.0);
828 let rgba = picture_to_rgba(picture, &configuration, &limits).unwrap();
829 let digest = crate::conformance::FrameDigest::from_frame(&rgba).unwrap();
830 assert_eq!(&digest.to_hex(), expected_rgba, "frame {} RGBA", index.0);
831 }
832 }
833
834 #[test]
835 fn main10_is_decoded_in_software_whatever_the_hardware_preference() {
836 let factory = native_hevc_video_decoder_factory();
837 for hardware in [HardwarePreference::Avoid, HardwarePreference::Prefer] {
838 let (configuration, _) = main10_fixture(CodecProfile::HevcMain10, hardware);
839 assert_eq!(
840 factory.capability(&configuration),
841 CodecSupport::Supported {
842 implementation: CodecImplementation::Software
843 },
844 "{hardware:?}"
845 );
846 }
847 let (configuration, _) =
848 main10_fixture(CodecProfile::HevcMain10, HardwarePreference::Require);
849 assert_eq!(
850 factory.capability(&configuration),
851 CodecSupport::HardwareUnavailable
852 );
853 let error = factory
854 .create(&configuration, &Limits::default())
855 .err()
856 .expect("no accelerated backend decodes Main 10");
857 assert_eq!(error.kind(), ErrorKind::Unsupported);
858 }
859
860 #[test]
861 fn a_main_profile_configuration_still_refuses_a_10_bit_track() {
862 let (configuration, _) = main10_fixture(CodecProfile::HevcMain, HardwarePreference::Avoid);
863 let factory = native_hevc_video_decoder_factory();
864 assert!(matches!(
865 factory.capability(&configuration),
866 CodecSupport::InvalidConfiguration { .. }
867 ));
868 let error = factory
869 .create(&configuration, &Limits::default())
870 .err()
871 .expect("HEVC Main is 8-bit");
872 assert_eq!(error.kind(), ErrorKind::Unsupported);
873 }
874
875 #[test]
876 fn canonical_conversion_uses_decoder_integer_rounding() {
877 use color_convert::{clip_u8, multiply_high};
878
879 assert_eq!(multiply_high(-8, -3_209), 0);
880 assert_eq!(clip_u8(multiply_high(0, 9_539)), 0);
881 }
882
883 #[test]
891 fn open_gop_leading_pictures_decode_from_every_starting_point() {
892 use crate::io::MemorySource;
893 use crate::{
894 ExactFrameReader, FrameDigest, FrameIndex, Mp4DemuxerOptions,
895 VideoDecoderConformanceVector, VideoDimensions, verify_video_decoder_conformance,
896 };
897
898 let expected = include_str!("../tests/fixtures/bbb_hevc_512x288_gop32_rgba.sha256")
899 .lines()
900 .map(|line| FrameDigest::from_hex(line.split_once(' ').unwrap().1).unwrap())
901 .collect::<Vec<_>>();
902 let limits = Limits::default();
903 let source = MemorySource::new(
904 include_bytes!("../tests/fixtures/bbb_hevc_512x288_gop32.mp4").to_vec(),
905 );
906 let vector = block_on(VideoDecoderConformanceVector::from_mp4(
907 "open-GOP HEVC Main fixture",
908 &source,
909 Mp4DemuxerOptions::default(),
910 1,
911 VideoDecoderConfig {
912 codec: Codec::Hevc,
913 profile: CodecProfile::HevcMain,
914 coded_dimensions: VideoDimensions::new(512, 288, &limits).unwrap(),
915 output_format: PixelFormat::Rgba8,
916 color_range: ColorRange::Limited,
917 hardware: HardwarePreference::Avoid,
918 configuration: Vec::new(),
919 },
920 &expected,
921 ))
922 .unwrap();
923 assert_eq!(vector.samples.len(), 768);
924 let order = vector.samples[29..33]
927 .iter()
928 .map(|sample| (sample.presentation_index.0, sample.random_access))
929 .collect::<Vec<_>>();
930 assert_eq!(order, [(32, true), (30, false), (29, false), (31, false)]);
931
932 let factory = native_hevc_video_decoder_factory();
933 let report = verify_video_decoder_conformance(&factory, &vector, limits).unwrap();
934 assert_eq!(report.frames_verified, 3 * 768);
935
936 let cancellation = CancellationToken::new();
939 for index in (20..=72).chain([413, 415, 416, 767]) {
940 let expected = &expected[index];
941 let mut reader = ExactFrameReader::new(
942 &factory,
943 vector.configuration.clone(),
944 vector.samples.clone(),
945 limits,
946 )
947 .unwrap();
948 let frame = reader
949 .get(FrameIndex(index as u64), &cancellation)
950 .unwrap_or_else(|error| panic!("frame {index} from a fresh reader: {error}"));
951 assert_eq!(
952 FrameDigest::from_frame(&frame).unwrap(),
953 *expected,
954 "frame {index} from a fresh reader"
955 );
956 if index == 40 {
959 assert!(
960 reader.statistics().samples_submitted <= 16,
961 "frame 40 is decoded from the CRA at frame 32: {:?}",
962 reader.statistics()
963 );
964 }
965 }
966 }
967}