1use std::ffi::{CStr, c_int, c_void};
18use std::ptr;
19
20use turbojpeg::{Decompressor, Image, PixelFormat, Subsamp, YuvImage};
21
22use super::convert::{I420Buffer, I420View};
23
24pub use crate::encoders::codec::Codec;
27use crate::encoders::codec::{
28 FRAME_KEY, annexb_nals, av1_is_key, h264_frame_type, h265_frame_type, vp8_is_key, vp9_is_key,
29};
30use crate::encoders::sps;
31
32pub fn sniff_keyframe(codec: Codec, data: &[u8]) -> Option<bool> {
35 Some(match codec {
36 Codec::Jpeg => true,
37 Codec::H264 => h264_frame_type(data) == FRAME_KEY,
38 Codec::H265 => h265_frame_type(data) == FRAME_KEY,
39 Codec::Vp8 => vp8_is_key(data),
40 Codec::Vp9 => vp9_is_key(data),
41 Codec::Av1 => av1_is_key(data),
42 })
43}
44
45#[derive(Debug)]
46pub enum DecodeError {
47 Corrupt(String),
49 Fatal(String),
51}
52
53pub trait Decoder {
54 fn codec(&self) -> Codec;
55 fn decode(&mut self, data: &[u8]) -> Result<bool, DecodeError>;
57 fn frame(&self) -> Option<I420View<'_>>;
58}
59
60pub fn new_decoder(codec: Codec) -> Result<Box<dyn Decoder>, String> {
61 match codec {
62 Codec::Jpeg => Ok(Box::new(JpegDecoder::new()?)),
63 _ => Ok(Box::new(VideoDecoder::new(codec)?)),
64 }
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub struct ColorTags {
71 pub matrix: u8,
72 pub full_range: bool,
73}
74
75impl ColorTags {
76 pub const BT709_LIMITED: Self = Self {
77 matrix: 1,
78 full_range: false,
79 };
80 pub const BT709_FULL: Self = Self {
81 matrix: 1,
82 full_range: true,
83 };
84 pub const BT470BG_LIMITED: Self = Self {
85 matrix: 5,
86 full_range: false,
87 };
88 pub const BT470BG_FULL: Self = Self {
89 matrix: 5,
90 full_range: true,
91 };
92}
93
94#[derive(Clone, Copy, PartialEq, Eq)]
96enum Chroma {
97 C420,
98 C422,
99 C444,
100}
101
102struct Picture {
105 width: usize,
106 height: usize,
107 chroma: Chroma,
108 planes: [*const u8; 3],
109 strides: [usize; 3],
110 tags: Option<ColorTags>,
111}
112
113impl Picture {
114 unsafe fn plane(&self, i: usize, rows: usize, cols: usize) -> &[u8] {
115 unsafe { std::slice::from_raw_parts(self.planes[i], self.strides[i] * (rows - 1) + cols) }
116 }
117}
118
119pub struct VideoDecoder {
121 codec: Codec,
122 backend: Backend,
123 picture: Option<Picture>,
124 converted: I420Buffer,
126 from_converted: bool,
127}
128
129unsafe impl Send for VideoDecoder {}
130
131enum Backend {
132 H264(H264Backend),
133 Vpx(VpxBackend),
134 Av1(Box<Dav1dBackend>),
135 Hevc(De265Backend),
136}
137
138impl VideoDecoder {
139 pub fn new(codec: Codec) -> Result<Self, String> {
140 let backend = match codec {
141 Codec::H264 => Backend::H264(H264Backend::new()?),
142 Codec::Vp8 | Codec::Vp9 => Backend::Vpx(VpxBackend::new(codec)?),
143 Codec::Av1 => Backend::Av1(Box::new(Dav1dBackend::new()?)),
144 Codec::H265 => Backend::Hevc(De265Backend::new()?),
145 Codec::Jpeg => return Err("MJPEG decodes through the JPEG decoder".into()),
146 };
147 Ok(Self {
148 codec,
149 backend,
150 picture: None,
151 converted: I420Buffer::new(2, 2),
152 from_converted: false,
153 })
154 }
155
156 pub fn color_tags(&self) -> Option<ColorTags> {
159 self.picture.as_ref()?.tags
160 }
161
162 fn convert(&mut self) {
164 let Some(p) = &self.picture else { return };
165 let (w, h) = (p.width, p.height);
166 let (cw, ch) = (w.div_ceil(2), h.div_ceil(2));
167 self.converted.resize(w, h);
168 let y_len = self.converted.y_len();
169 let uv_len = self.converted.uv_len();
170 let (yp, rest) = self.converted.data.split_at_mut(y_len);
171 let (up, vp) = rest.split_at_mut(uv_len);
172 let y = unsafe { p.plane(0, h, w) };
173 for row in 0..h {
174 yp[row * w..row * w + w]
175 .copy_from_slice(&y[row * p.strides[0]..row * p.strides[0] + w]);
176 }
177 let (src_cw, src_ch) = match p.chroma {
178 Chroma::C420 => (cw, ch),
179 Chroma::C422 => (cw, h),
180 Chroma::C444 => (w, h),
181 };
182 for (i, dst) in [(1usize, &mut *up), (2usize, &mut *vp)] {
183 let src = unsafe { p.plane(i, src_ch, src_cw) };
184 let s = p.strides[i];
185 for row in 0..ch {
186 for x in 0..cw {
187 let sum: u32 = match p.chroma {
188 Chroma::C420 => src[row * s + x] as u32 * 4,
189 Chroma::C422 => {
190 let (r0, r1) =
191 ((2 * row).min(src_ch - 1), (2 * row + 1).min(src_ch - 1));
192 (src[r0 * s + x] as u32 + src[r1 * s + x] as u32) * 2
193 }
194 Chroma::C444 => {
195 let (r0, r1) =
196 ((2 * row).min(src_ch - 1), (2 * row + 1).min(src_ch - 1));
197 let (x0, x1) = (2 * x, (2 * x + 1).min(src_cw - 1));
198 src[r0 * s + x0] as u32
199 + src[r0 * s + x1] as u32
200 + src[r1 * s + x0] as u32
201 + src[r1 * s + x1] as u32
202 }
203 };
204 dst[row * cw + x] = ((sum + 2) / 4) as u8;
205 }
206 }
207 }
208 }
209}
210
211impl Decoder for VideoDecoder {
212 fn codec(&self) -> Codec {
213 self.codec
214 }
215
216 fn decode(&mut self, data: &[u8]) -> Result<bool, DecodeError> {
217 self.picture = None;
218 let picture = match &mut self.backend {
219 Backend::H264(b) => b.decode(data)?,
220 Backend::Vpx(b) => b.decode(data)?,
221 Backend::Av1(b) => b.decode(data)?,
222 Backend::Hevc(b) => b.decode(data)?,
223 };
224 let Some(picture) = picture else {
225 return Ok(false);
226 };
227 self.from_converted = picture.chroma != Chroma::C420;
228 self.picture = Some(picture);
229 if self.from_converted {
230 self.convert();
231 }
232 Ok(true)
233 }
234
235 fn frame(&self) -> Option<I420View<'_>> {
236 let p = self.picture.as_ref()?;
237 let full_range = p.tags.is_some_and(|t| t.full_range);
238 if self.from_converted {
239 return Some(self.converted.view(full_range));
240 }
241 let (w, h) = (p.width, p.height);
242 let (cw, ch) = (w.div_ceil(2), h.div_ceil(2));
243 Some(I420View {
244 width: w,
245 height: h,
246 y: unsafe { p.plane(0, h, w) },
247 u: unsafe { p.plane(1, ch, cw) },
248 v: unsafe { p.plane(2, ch, cw) },
249 y_stride: p.strides[0],
250 uv_stride: p.strides[1].min(p.strides[2]),
251 full_range,
252 })
253 }
254}
255
256struct H264Backend {
260 decoder: *mut openh264_sys2::ISVCDecoder,
261 tags: Option<ColorTags>,
262}
263
264impl Drop for H264Backend {
265 fn drop(&mut self) {
266 unsafe {
267 if let Some(uninit) = self.api().and_then(|a| a.Uninitialize) {
268 uninit(self.decoder);
269 }
270 openh264_sys2::source::APILoader::WelsDestroyDecoder(self.decoder);
271 }
272 }
273}
274
275impl H264Backend {
276 fn api(&self) -> Option<&openh264_sys2::ISVCDecoderVtbl> {
278 unsafe { self.decoder.as_ref().and_then(|d| d.as_ref()) }
279 }
280
281 fn new() -> Result<Self, String> {
282 use openh264_sys2::*;
283 let mut decoder: *mut ISVCDecoder = ptr::null_mut();
284 if unsafe { source::APILoader::WelsCreateDecoder(&mut decoder) } != 0 {
285 return Err("OpenH264 created no decoder".into());
286 }
287 let me = Self {
288 decoder,
289 tags: None,
290 };
291 let initialize = me
292 .api()
293 .and_then(|a| a.Initialize)
294 .ok_or("OpenH264 exposes no Initialize")?;
295 let mut param: SDecodingParam = unsafe { std::mem::zeroed() };
296 param.eEcActiveIdc = ERROR_CON_SLICE_MV_COPY_CROSS_IDR;
297 param.sVideoProperty.size = std::mem::size_of::<SVideoProperty>() as u32;
298 param.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC;
299 if unsafe { initialize(decoder, ¶m) } != 0 {
300 return Err("OpenH264 refused to initialize".into());
301 }
302 Ok(me)
303 }
304
305 fn decode(&mut self, data: &[u8]) -> Result<Option<Picture>, DecodeError> {
306 use openh264_sys2::*;
307 let mut sps = None;
308 let mut scalable = false;
309 for nal in annexb_nals(data) {
310 match nal[0] & 0x1f {
311 7 if sps.is_none() => sps = Some(nal),
312 14 | 15 | 20 => scalable = true,
313 _ => {}
314 }
315 }
316 if let Some(sps) = sps {
317 self.tags = Some(match sps::read_color(sps) {
318 Some(signal) => ColorTags {
319 matrix: signal.matrix,
320 full_range: signal.full_range,
321 },
322 None => ColorTags {
323 matrix: 2,
324 full_range: false,
325 },
326 });
327 }
328 let mut avc = Vec::new();
333 let data = if scalable {
334 for nal in annexb_nals(data).filter(|n| !matches!(n[0] & 0x1f, 14 | 15 | 20)) {
335 avc.extend_from_slice(&[0, 0, 0, 1]);
336 avc.extend_from_slice(nal);
337 }
338 &avc[..]
339 } else {
340 data
341 };
342 let mut dst: [*mut u8; 3] = [ptr::null_mut(); 3];
343 let mut info: SBufferInfo = unsafe { std::mem::zeroed() };
344 let decode = self
345 .api()
346 .and_then(|a| a.DecodeFrameNoDelay)
347 .ok_or_else(|| DecodeError::Fatal("OpenH264 exposes no DecodeFrameNoDelay".into()))?;
348 let mut state = unsafe {
349 decode(
350 self.decoder,
351 data.as_ptr(),
352 data.len() as c_int,
353 dst.as_mut_ptr(),
354 &mut info,
355 )
356 };
357 if info.iBufferStatus != 1
363 && let Some(flush) = self.api().and_then(|a| a.FlushFrame)
364 {
365 state |= unsafe { flush(self.decoder, dst.as_mut_ptr(), &mut info) };
366 }
367 if info.iBufferStatus != 1 || dst.iter().any(|p| p.is_null()) {
368 if state != dsErrorFree {
369 return Err(DecodeError::Corrupt(format!(
370 "OpenH264 decoding state {state:#x}"
371 )));
372 }
373 return Ok(None);
374 }
375 let system = unsafe { info.UsrData.sSystemBuffer };
376 Ok(Some(Picture {
377 width: system.iWidth as usize,
378 height: system.iHeight as usize,
379 chroma: Chroma::C420,
380 planes: [dst[0], dst[1], dst[2]],
381 strides: [
382 system.iStride[0] as usize,
383 system.iStride[1] as usize,
384 system.iStride[1] as usize,
385 ],
386 tags: self.tags,
387 }))
388 }
389}
390
391struct VpxBackend {
394 codec: Codec,
395 ctx: codec_sys::vpx::vpx_codec_ctx_t,
396 tags: Option<ColorTags>,
397}
398
399impl Drop for VpxBackend {
400 fn drop(&mut self) {
401 unsafe { codec_sys::vpx::vpx_codec_destroy(&mut self.ctx) };
402 }
403}
404
405fn vp8_color_bits(frame: &[u8]) -> Option<(bool, bool)> {
408 if !vp8_is_key(frame) || frame.len() < 12 {
409 return None;
410 }
411 let data = &frame[10..];
412 let mut value = ((data[0] as u32) << 8) | data[1] as u32;
413 let mut range = 255u32;
414 let mut bit_count = 0;
415 let mut at = 2;
416 let mut read = || {
417 let split = 1 + (((range - 1) * 128) >> 8);
418 let big = split << 8;
419 let bit = value >= big;
420 if bit {
421 range -= split;
422 value -= big;
423 } else {
424 range = split;
425 }
426 while range < 128 {
427 value <<= 1;
428 range <<= 1;
429 bit_count += 1;
430 if bit_count == 8 {
431 bit_count = 0;
432 value |= data.get(at).copied().unwrap_or(0) as u32;
433 at += 1;
434 }
435 }
436 bit
437 };
438 let color_space = read();
439 let clamping = read();
440 Some((color_space, clamping))
441}
442
443impl VpxBackend {
444 fn new(codec: Codec) -> Result<Self, String> {
445 use codec_sys::vpx::*;
446 let iface = unsafe {
447 if codec == Codec::Vp8 {
448 vpx_codec_vp8_dx()
449 } else {
450 vpx_codec_vp9_dx()
451 }
452 };
453 let mut ctx: vpx_codec_ctx_t = unsafe { std::mem::zeroed() };
454 if unsafe {
455 vpx_codec_dec_init_ver(
456 &mut ctx,
457 iface,
458 ptr::null(),
459 0,
460 VPX_DECODER_ABI_VERSION as c_int,
461 )
462 } != VPX_CODEC_OK
463 {
464 return Err(format!("libvpx opened no {} decoder", codec.name()));
465 }
466 Ok(Self {
467 codec,
468 ctx,
469 tags: None,
470 })
471 }
472
473 fn decode(&mut self, data: &[u8]) -> Result<Option<Picture>, DecodeError> {
474 use codec_sys::vpx::*;
475 if self.codec == Codec::Vp8
476 && let Some((reserved, clamping)) = vp8_color_bits(data)
477 {
478 self.tags = Some(ColorTags {
479 matrix: if reserved { 2 } else { 5 },
480 full_range: clamping,
481 });
482 }
483 let res = unsafe {
484 vpx_codec_decode(
485 &mut self.ctx,
486 data.as_ptr(),
487 data.len() as u32,
488 ptr::null_mut(),
489 0,
490 )
491 };
492 if res != VPX_CODEC_OK {
493 let detail = unsafe { CStr::from_ptr(vpx_codec_err_to_string(res)) }
494 .to_string_lossy()
495 .into_owned();
496 return Err(DecodeError::Corrupt(detail));
497 }
498 let mut iter: vpx_codec_iter_t = ptr::null_mut();
499 let mut image = None;
500 while let Some(next) =
501 ptr::NonNull::new(unsafe { vpx_codec_get_frame(&mut self.ctx, &mut iter) })
502 {
503 image = Some(next);
504 }
505 let Some(image) = image else { return Ok(None) };
506 let img = unsafe { image.as_ref() };
507 if img.bit_depth != 8 {
508 return Err(DecodeError::Fatal(format!(
509 "unsupported libvpx bit depth {}",
510 img.bit_depth
511 )));
512 }
513 let chroma = match (img.x_chroma_shift, img.y_chroma_shift) {
514 (1, 1) => Chroma::C420,
515 (1, 0) => Chroma::C422,
516 (0, 0) => Chroma::C444,
517 _ => {
518 return Err(DecodeError::Fatal(format!(
519 "unsupported libvpx image format {}",
520 img.fmt
521 )));
522 }
523 };
524 if self.codec == Codec::Vp9 {
525 let matrix = match img.cs {
526 VPX_CS_BT_601 => 5,
527 VPX_CS_BT_709 => 1,
528 VPX_CS_SMPTE_170 => 6,
529 VPX_CS_SMPTE_240 => 7,
530 VPX_CS_BT_2020 => 9,
531 VPX_CS_SRGB => 0,
532 _ => 2,
533 };
534 self.tags = Some(ColorTags {
535 matrix,
536 full_range: img.range == VPX_CR_FULL_RANGE,
537 });
538 }
539 Ok(Some(Picture {
540 width: img.d_w as usize,
541 height: img.d_h as usize,
542 chroma,
543 planes: [img.planes[0], img.planes[1], img.planes[2]],
544 strides: [
545 img.stride[0] as usize,
546 img.stride[1] as usize,
547 img.stride[2] as usize,
548 ],
549 tags: self.tags,
550 }))
551 }
552}
553
554struct Dav1dBackend {
556 ctx: *mut codec_sys::dav1d::Dav1dContext,
557 picture: codec_sys::dav1d::Dav1dPicture,
558 held: bool,
559}
560
561impl Drop for Dav1dBackend {
562 fn drop(&mut self) {
563 unsafe {
564 if self.held {
565 codec_sys::dav1d::dav1d_picture_unref(&mut self.picture);
566 }
567 codec_sys::dav1d::dav1d_close(&mut self.ctx);
568 }
569 }
570}
571
572impl Dav1dBackend {
573 fn new() -> Result<Self, String> {
574 use codec_sys::dav1d::*;
575 let mut settings: Dav1dSettings = unsafe { std::mem::zeroed() };
576 unsafe { dav1d_default_settings(&mut settings) };
577 settings.n_threads = 2;
578 settings.max_frame_delay = 1;
579 settings.logger.callback = None;
580 let mut ctx: *mut Dav1dContext = ptr::null_mut();
581 if unsafe { dav1d_open(&mut ctx, &settings) } != 0 || ctx.is_null() {
582 return Err("dav1d opened no decoder".into());
583 }
584 Ok(Self {
585 ctx,
586 picture: unsafe { std::mem::zeroed() },
587 held: false,
588 })
589 }
590
591 fn decode(&mut self, data: &[u8]) -> Result<Option<Picture>, DecodeError> {
592 use codec_sys::dav1d::*;
593 const AGAIN: c_int = -(libc::EAGAIN);
594 let mut input: Dav1dData = unsafe { std::mem::zeroed() };
595 let buffer = unsafe { dav1d_data_create(&mut input, data.len()) };
596 if buffer.is_null() {
597 return Err(DecodeError::Fatal("dav1d allocated no input buffer".into()));
598 }
599 unsafe { ptr::copy_nonoverlapping(data.as_ptr(), buffer, data.len()) };
600 let mut got = false;
601 loop {
602 let sent = unsafe { dav1d_send_data(self.ctx, &mut input) };
603 if sent < 0 && sent != AGAIN {
604 unsafe { dav1d_data_unref(&mut input) };
605 return Err(DecodeError::Corrupt(format!(
606 "dav1d refused the data ({sent})"
607 )));
608 }
609 loop {
610 let mut next: Dav1dPicture = unsafe { std::mem::zeroed() };
611 let res = unsafe { dav1d_get_picture(self.ctx, &mut next) };
612 if res == AGAIN {
613 break;
614 }
615 if res < 0 {
616 unsafe { dav1d_data_unref(&mut input) };
617 return Err(DecodeError::Corrupt(format!(
618 "dav1d decoded no picture ({res})"
619 )));
620 }
621 if self.held {
622 unsafe { dav1d_picture_unref(&mut self.picture) };
623 }
624 self.picture = next;
625 self.held = true;
626 got = true;
627 }
628 if sent == 0 {
629 break;
630 }
631 }
632 unsafe { dav1d_data_unref(&mut input) };
633 if !got {
634 return Ok(None);
635 }
636 let p = &self.picture;
637 let chroma = match p.p.layout {
638 DAV1D_PIXEL_LAYOUT_I420 => Chroma::C420,
639 DAV1D_PIXEL_LAYOUT_I422 => Chroma::C422,
640 DAV1D_PIXEL_LAYOUT_I444 => Chroma::C444,
641 other => {
642 return Err(DecodeError::Fatal(format!(
643 "unsupported dav1d layout {other}"
644 )));
645 }
646 };
647 if p.p.bpc != 8 {
648 return Err(DecodeError::Fatal(format!(
649 "unsupported dav1d bit depth {}",
650 p.p.bpc
651 )));
652 }
653 let tags = unsafe { p.seq_hdr.as_ref() }.map(|s| ColorTags {
654 matrix: s.mtrx as u8,
655 full_range: s.color_range != 0,
656 });
657 Ok(Some(Picture {
658 width: p.p.w as usize,
659 height: p.p.h as usize,
660 chroma,
661 planes: [
662 p.data[0] as *const u8,
663 p.data[1] as *const u8,
664 p.data[2] as *const u8,
665 ],
666 strides: [
667 p.stride[0] as usize,
668 p.stride[1] as usize,
669 p.stride[1] as usize,
670 ],
671 tags,
672 }))
673 }
674}
675
676struct De265Backend {
679 ctx: *mut codec_sys::de265::de265_decoder_context,
680 planes: Vec<u8>,
681}
682
683impl Drop for De265Backend {
684 fn drop(&mut self) {
685 unsafe { codec_sys::de265::de265_free_decoder(self.ctx) };
686 }
687}
688
689impl De265Backend {
690 fn new() -> Result<Self, String> {
691 use codec_sys::de265::*;
692 let ctx = unsafe { de265_new_decoder() };
693 if ctx.is_null() {
694 return Err("libde265 opened no decoder".into());
695 }
696 unsafe {
697 de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_BOOL_SEI_CHECK_HASH, 0);
698 de265_start_worker_threads(ctx, 2);
699 }
700 Ok(Self {
701 ctx,
702 planes: Vec::new(),
703 })
704 }
705
706 fn decode(&mut self, data: &[u8]) -> Result<Option<Picture>, DecodeError> {
707 use codec_sys::de265::*;
708 let text = |err: de265_error| {
709 unsafe { CStr::from_ptr(de265_get_error_text(err)) }
710 .to_string_lossy()
711 .into_owned()
712 };
713 unsafe {
714 let err = de265_push_data(
715 self.ctx,
716 data.as_ptr() as *const c_void,
717 data.len() as c_int,
718 0,
719 ptr::null_mut(),
720 );
721 if err != DE265_OK {
722 return Err(DecodeError::Corrupt(text(err)));
723 }
724 de265_push_end_of_frame(self.ctx);
725 let mut image: *const de265_image = ptr::null();
728 let mut more: c_int = 1;
729 while more != 0 && image.is_null() {
730 let err = de265_decode(self.ctx, &mut more);
731 if err != DE265_OK && err != DE265_ERROR_WAITING_FOR_INPUT_DATA {
732 return Err(DecodeError::Corrupt(text(err)));
733 }
734 loop {
735 let next = de265_get_next_picture(self.ctx);
736 if next.is_null() {
737 break;
738 }
739 image = next;
740 }
741 if err == DE265_ERROR_WAITING_FOR_INPUT_DATA {
742 break;
743 }
744 }
745 if image.is_null() {
746 return Ok(None);
747 }
748 let (w, h) = (
749 de265_get_image_width(image, 0) as usize,
750 de265_get_image_height(image, 0) as usize,
751 );
752 let format = de265_get_chroma_format(image);
753 let chroma = if format == de265_chroma_420 {
754 Chroma::C420
755 } else if format == de265_chroma_422 {
756 Chroma::C422
757 } else if format == de265_chroma_444 {
758 Chroma::C444
759 } else {
760 return Err(DecodeError::Fatal(format!(
761 "unsupported libde265 chroma format {format}"
762 )));
763 };
764 if (0..3).any(|c| de265_get_bits_per_pixel(image, c) != 8) {
765 return Err(DecodeError::Fatal("unsupported libde265 bit depth".into()));
766 }
767 let tags = Some(ColorTags {
768 matrix: de265_get_image_matrix_coefficients(image) as u8,
769 full_range: de265_get_image_full_range_flag(image) != 0,
770 });
771 let (cw, ch) = match chroma {
774 Chroma::C420 => (w.div_ceil(2), h.div_ceil(2)),
775 Chroma::C422 => (w.div_ceil(2), h),
776 Chroma::C444 => (w, h),
777 };
778 self.planes.resize(w * h + 2 * cw * ch, 0);
779 let (yd, rest) = self.planes.split_at_mut(w * h);
780 let (ud, vd) = rest.split_at_mut(cw * ch);
781 for (c, dst, cols, rows) in [(0, yd, w, h), (1, ud, cw, ch), (2, vd, cw, ch)] {
782 let mut stride: c_int = 0;
783 let src = de265_get_image_plane(image, c, &mut stride);
784 for row in 0..rows {
785 ptr::copy_nonoverlapping(
786 src.add(row * stride as usize),
787 dst.as_mut_ptr().add(row * cols),
788 cols,
789 );
790 }
791 }
792 Ok(Some(Picture {
793 width: w,
794 height: h,
795 chroma,
796 planes: [
797 self.planes.as_ptr(),
798 self.planes[w * h..].as_ptr(),
799 self.planes[w * h + cw * ch..].as_ptr(),
800 ],
801 strides: [w, cw, cw],
802 tags,
803 }))
804 }
805 }
806}
807
808pub struct JpegDecoder {
811 dec: Decompressor,
812 out: I420Buffer,
813 rgb: Vec<u8>,
814 have_frame: bool,
815}
816
817impl JpegDecoder {
818 pub fn new() -> Result<Self, String> {
819 Ok(JpegDecoder {
820 dec: Decompressor::new().map_err(|e| format!("turbojpeg: {}", e))?,
821 out: I420Buffer::new(2, 2),
822 rgb: Vec::new(),
823 have_frame: false,
824 })
825 }
826}
827
828impl Decoder for JpegDecoder {
829 fn codec(&self) -> Codec {
830 Codec::Jpeg
831 }
832
833 fn decode(&mut self, data: &[u8]) -> Result<bool, DecodeError> {
834 self.have_frame = false;
835 let hdr = self
836 .dec
837 .read_header(data)
838 .map_err(|e| DecodeError::Corrupt(format!("jpeg header: {}", e)))?;
839 if hdr.width == 0 || hdr.height == 0 {
840 return Err(DecodeError::Corrupt("empty jpeg".into()));
841 }
842 self.out.resize(hdr.width, hdr.height);
843 if hdr.subsamp == Subsamp::Sub2x2 {
844 let img = YuvImage {
845 pixels: &mut self.out.data[..],
846 width: hdr.width,
847 align: 1,
848 height: hdr.height,
849 subsamp: Subsamp::Sub2x2,
850 };
851 self.dec
852 .decompress_to_yuv(data, img)
853 .map_err(|e| DecodeError::Corrupt(format!("jpeg: {}", e)))?;
854 } else {
855 self.rgb.resize(hdr.width * hdr.height * 4, 0);
856 let img = Image {
857 pixels: &mut self.rgb[..],
858 width: hdr.width,
859 pitch: hdr.width * 4,
860 height: hdr.height,
861 format: PixelFormat::RGBA,
862 };
863 self.dec
864 .decompress(data, img)
865 .map_err(|e| DecodeError::Corrupt(format!("jpeg: {}", e)))?;
866 let y_len = self.out.y_len();
867 let uv_len = self.out.uv_len();
868 let cw = hdr.width.div_ceil(2);
869 let (yp, rest) = self.out.data.split_at_mut(y_len);
870 let (up, vp) = rest.split_at_mut(uv_len);
871 let mut planar = yuv::YuvPlanarImageMut {
872 y_plane: yuv::BufferStoreMut::Borrowed(yp),
873 y_stride: hdr.width as u32,
874 u_plane: yuv::BufferStoreMut::Borrowed(up),
875 u_stride: cw as u32,
876 v_plane: yuv::BufferStoreMut::Borrowed(vp),
877 v_stride: cw as u32,
878 width: hdr.width as u32,
879 height: hdr.height as u32,
880 };
881 yuv::rgba_to_yuv420(
882 &mut planar,
883 &self.rgb,
884 (hdr.width * 4) as u32,
885 yuv::YuvRange::Full,
886 yuv::YuvStandardMatrix::Bt601,
887 yuv::YuvConversionMode::Fast,
888 )
889 .map_err(|e| DecodeError::Corrupt(format!("rgb->i420: {:?}", e)))?;
890 }
891 self.have_frame = true;
892 Ok(true)
893 }
894
895 fn frame(&self) -> Option<I420View<'_>> {
896 if self.have_frame {
897 Some(self.out.view(true))
898 } else {
899 None
900 }
901 }
902}
903
904#[cfg(test)]
905mod tests {
906 use super::*;
907
908 #[test]
909 fn codec_ids_round_trip() {
910 for id in 0..6 {
911 let c = Codec::from_id(id).unwrap();
912 assert_eq!(c as u32, id);
913 }
914 assert!(Codec::from_id(6).is_none());
915 }
916
917 #[test]
918 fn h264_keyframe_sniff() {
919 let idr = [
920 0, 0, 0, 1, 0x67, 0x42, 0, 0, 0, 1, 0x68, 0xCE, 0, 0, 1, 0x65, 0x88,
921 ];
922 assert_eq!(sniff_keyframe(Codec::H264, &idr), Some(true));
923 let p = [0, 0, 0, 1, 0x41, 0x9A];
924 assert_eq!(sniff_keyframe(Codec::H264, &p), Some(false));
925 assert_eq!(sniff_keyframe(Codec::Vp8, &[0x10, 0, 0]), Some(true));
926 assert_eq!(sniff_keyframe(Codec::Vp8, &[0x11, 0, 0]), Some(false));
927 assert_eq!(sniff_keyframe(Codec::Vp9, &[0x82, 0x49, 0x83]), Some(true));
928 assert_eq!(sniff_keyframe(Codec::Vp9, &[0x86, 0]), Some(false));
929 assert_eq!(
930 sniff_keyframe(Codec::H265, &[0, 0, 1, 0x26, 0x01]),
931 Some(true)
932 );
933 assert_eq!(
934 sniff_keyframe(Codec::H265, &[0, 0, 1, 0x02, 0x01]),
935 Some(false)
936 );
937 assert_eq!(
938 sniff_keyframe(Codec::Av1, &[0x12, 0, 0x32, 0x01, 0x10]),
939 Some(true)
940 );
941 assert_eq!(sniff_keyframe(Codec::Av1, &[0x32, 0x01, 0x30]), Some(false));
942 }
943
944 #[test]
945 fn jpeg_round_trip() {
946 let w = 64;
947 let h = 48;
948 let mut img = I420Buffer::new(w, h);
949 let yl = img.y_len();
950 let ul = img.uv_len();
951 img.data[..yl].fill(200);
952 img.data[yl..yl + ul].fill(100);
953 img.data[yl + ul..].fill(150);
954 let src = YuvImage {
955 pixels: &img.data[..],
956 width: w,
957 align: 1,
958 height: h,
959 subsamp: Subsamp::Sub2x2,
960 };
961 let jpeg = turbojpeg::compress_yuv(src, 90).unwrap();
962 let mut dec = JpegDecoder::new().unwrap();
963 assert!(dec.decode(&jpeg).unwrap());
964 let v = dec.frame().unwrap();
965 assert_eq!((v.width, v.height), (w, h));
966 assert!(v.full_range);
967 let mid = v.y[(h / 2) * v.y_stride + w / 2];
968 assert!((mid as i32 - 200).abs() <= 3, "luma {}", mid);
969 let cu = v.u[(h / 4) * v.uv_stride + w / 4];
970 assert!((cu as i32 - 100).abs() <= 3, "cb {}", cu);
971 }
972
973 #[test]
975 fn a_decoder_constructs_for_every_video_codec() {
976 for c in Codec::VIDEO {
977 let d = VideoDecoder::new(c).unwrap();
978 assert_eq!(d.codec(), c);
979 assert!(d.frame().is_none());
980 assert_eq!(d.color_tags(), None);
981 }
982 assert!(VideoDecoder::new(Codec::Jpeg).is_err());
983 }
984
985 #[test]
986 fn h264_round_trip_through_openh264() {
987 use openh264::encoder::Encoder;
988 use openh264::formats::YUVBuffer;
989 let (w, h) = (64usize, 48usize);
990 let mut yuv = vec![0u8; w * h * 3 / 2];
991 yuv[..w * h].fill(145);
992 yuv[w * h..w * h + w * h / 4].fill(54);
993 yuv[w * h + w * h / 4..].fill(34);
994 let source = YUVBuffer::from_vec(yuv, w, h);
995 let mut enc = Encoder::new().unwrap();
996 let mut dec = VideoDecoder::new(Codec::H264).unwrap();
997 let mut decoded = 0;
998 for _ in 0..6 {
999 let bitstream = enc.encode(&source).unwrap().to_vec();
1000 assert!(!bitstream.is_empty());
1001 if dec.decode(&bitstream).unwrap() {
1002 decoded += 1;
1003 let v = dec.frame().unwrap();
1004 assert_eq!((v.width, v.height), (w, h));
1005 assert!(!v.full_range);
1006 let y = v.y[(h / 2) * v.y_stride + w / 2];
1007 let u = v.u[(h / 4) * v.uv_stride + w / 4];
1008 let vv = v.v[(h / 4) * v.uv_stride + w / 4];
1009 assert!(
1010 (y as i32 - 145).abs() <= 6
1011 && (u as i32 - 54).abs() <= 6
1012 && (vv as i32 - 34).abs() <= 6,
1013 "yuv {} {} {}",
1014 y,
1015 u,
1016 vv
1017 );
1018 }
1019 }
1020 assert!(decoded >= 5, "decoded {} of 6 frames", decoded);
1021 }
1022
1023 fn h264_skipping_stream(mbs: u32, frames: u32, sample: u8) -> Vec<Vec<u8>> {
1028 struct Bits(Vec<u8>, usize);
1029 impl Bits {
1030 fn u(&mut self, value: u32, count: usize) {
1031 for shift in (0..count).rev() {
1032 if self.1.is_multiple_of(8) {
1033 self.0.push(0);
1034 }
1035 if (value >> shift) & 1 != 0 {
1036 *self.0.last_mut().unwrap() |= 0x80 >> (self.1 % 8);
1037 }
1038 self.1 += 1;
1039 }
1040 }
1041 fn ue(&mut self, value: u32) {
1042 let coded = value + 1;
1043 let length = 32 - coded.leading_zeros() as usize;
1044 self.u(0, length - 1);
1045 self.u(coded, length);
1046 }
1047 fn align(&mut self) {
1048 while !self.1.is_multiple_of(8) {
1049 self.u(0, 1);
1050 }
1051 }
1052 fn nal(mut self, header: u8) -> Vec<u8> {
1053 self.u(1, 1);
1054 self.align();
1055 let mut out = vec![0, 0, 0, 1, header];
1056 let mut zeros = 0;
1057 for byte in self.0 {
1058 if zeros >= 2 && byte <= 3 {
1059 out.push(3);
1060 zeros = 0;
1061 }
1062 zeros = if byte == 0 { zeros + 1 } else { 0 };
1063 out.push(byte);
1064 }
1065 out
1066 }
1067 }
1068 let mut sps = Bits(Vec::new(), 0);
1069 sps.u(66, 8);
1070 sps.u(0xc0, 8);
1071 sps.u(40, 8);
1072 for value in [0, 0, 0, 0, 2] {
1073 sps.ue(value);
1074 }
1075 sps.u(0, 1);
1076 sps.ue(mbs - 1);
1077 sps.ue(mbs - 1);
1078 sps.u(0b1100, 4);
1079 let mut pps = Bits(Vec::new(), 0);
1080 pps.ue(0);
1081 pps.ue(0);
1082 pps.u(0, 2);
1083 for value in [0, 0, 0] {
1084 pps.ue(value);
1085 }
1086 pps.u(0, 3);
1087 for value in [0, 0, 0] {
1088 pps.ue(value);
1089 }
1090 pps.u(0, 3);
1091 let mut idr = Bits(Vec::new(), 0);
1092 for value in [0, 7, 0] {
1093 idr.ue(value);
1094 }
1095 idr.u(0, 4);
1096 idr.ue(0);
1097 idr.u(0, 4);
1098 idr.u(0, 2);
1099 idr.ue(0);
1100 for _ in 0..mbs * mbs {
1101 idr.ue(25);
1102 idr.align();
1103 for _ in 0..384 {
1104 idr.u(sample as u32, 8);
1105 }
1106 }
1107 let mut units = vec![[sps.nal(0x67), pps.nal(0x68), idr.nal(0x65)].concat()];
1108 for n in 1..frames {
1109 let mut p = Bits(Vec::new(), 0);
1110 for value in [0, 5, 0] {
1111 p.ue(value);
1112 }
1113 p.u(n % 16, 4);
1114 p.u(2 * n % 16, 4);
1115 p.u(0, 3);
1116 p.ue(0);
1117 p.ue(mbs * mbs);
1118 units.push(p.nal(0x41));
1119 }
1120 units
1121 }
1122
1123 #[test]
1126 fn h264_scalable_extension_units_never_retarget_the_decoder() {
1127 let mut units = h264_skipping_stream(2, 12, 0x60);
1128 units[4].splice(0..0, [0, 0, 0, 1, 0x4e, 0x80, 0x90, 0x07, 0x20]);
1129 let mut dec = VideoDecoder::new(Codec::H264).unwrap();
1130 for (n, unit) in units.iter().chain(&units).enumerate() {
1131 match dec.decode(unit) {
1132 Ok(true) => {}
1133 other => panic!("access unit {n}: {other:?}"),
1134 }
1135 }
1136 }
1137
1138 #[test]
1140 fn a_decoder_rejects_garbage_without_fatal() {
1141 for c in Codec::VIDEO {
1142 let mut d = VideoDecoder::new(c).unwrap();
1143 match d.decode(&[1, 2, 3, 4, 5, 6, 7, 8]) {
1144 Ok(false) | Err(DecodeError::Corrupt(_)) => {}
1145 Ok(true) => panic!("{c:?}: garbage decoded to a frame"),
1146 Err(DecodeError::Fatal(e)) => panic!("{c:?}: fatal: {}", e),
1147 }
1148 }
1149 }
1150
1151 #[test]
1157 fn a_10_bit_vp9_frame_never_decodes_as_8_bit() {
1158 const KEY: [u8; 77] = [
1159 0x92, 0x49, 0x83, 0x42, 0x00, 0x09, 0xf8, 0x07, 0x7b, 0x03, 0x1c, 0x12, 0x0e, 0x0c,
1160 0x2c, 0x00, 0x00, 0x48, 0x47, 0xa8, 0x3d, 0x8f, 0xc8, 0xc0, 0x0c, 0x30, 0x00, 0x6b,
1161 0x41, 0x13, 0x20, 0x95, 0xe2, 0x6a, 0x41, 0xdc, 0xb8, 0x00, 0x11, 0x50, 0x1b, 0x55,
1162 0x82, 0x76, 0x20, 0x4f, 0xd6, 0xa9, 0x82, 0x10, 0xc5, 0xe9, 0x8f, 0xe0, 0xa3, 0xf7,
1163 0xcd, 0x66, 0x00, 0xbc, 0xd3, 0xeb, 0x11, 0x3a, 0xb5, 0xed, 0x25, 0x7b, 0x25, 0x6d,
1164 0xa9, 0x8e, 0xf7, 0xf7, 0xfa, 0x2b, 0x10,
1165 ];
1166 let mut d = VideoDecoder::new(Codec::Vp9).unwrap();
1167 assert!(
1168 !matches!(d.decode(&KEY), Ok(true)),
1169 "a 10-bit picture was handed on as an 8-bit one"
1170 );
1171 }
1172
1173 #[test]
1174 fn vp8_color_bits_read_from_the_first_partition() {
1175 let mut key = vec![0x10, 0, 0, 0x9d, 0x01, 0x2a, 64, 0, 48, 0];
1176 key.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
1177 assert_eq!(vp8_color_bits(&key), Some((false, false)));
1178 key[10] = 0xff;
1179 key[11] = 0xff;
1180 assert_eq!(vp8_color_bits(&key), Some((true, true)));
1181 assert_eq!(
1182 vp8_color_bits(&[0x11, 0, 0]),
1183 None,
1184 "an inter frame carries no header"
1185 );
1186 }
1187}