1use super::descriptor_body;
11use crate::error::{Error, Result};
12use dvb_common::{Parse, Serialize};
13
14pub const TAG: u8 = 0x32;
16const HEADER_LEN: usize = 2;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21pub struct J2kStripe {
22 pub strp_max_idx: u8,
24 pub strp_height: u16,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub struct J2kBlock {
32 pub full_horizontal_size: u32,
34 pub full_vertical_size: u32,
36 pub blk_width: u16,
38 pub blk_height: u16,
40 pub max_blk_idx_h: u8,
42 pub max_blk_idx_v: u8,
44 pub blk_idx_h: u8,
46 pub blk_idx_v: u8,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize))]
53pub struct J2kMdm {
54 pub x_c0: u16,
56 pub y_c0: u16,
58 pub x_c1: u16,
60 pub y_c1: u16,
62 pub x_c2: u16,
64 pub y_c2: u16,
66 pub x_wp: u16,
68 pub y_wp: u16,
70 pub l_max: u32,
72 pub l_min: u32,
74 pub max_cll: u16,
76 pub max_fall: u16,
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize))]
83pub struct J2kExtendedCapability {
84 pub stripe_flag: bool,
86 pub block_flag: bool,
88 pub mdm_flag: bool,
90 pub colour_primaries: u8,
92 pub transfer_characteristics: u8,
94 pub matrix_coefficients: u8,
96 pub video_full_range_flag: bool,
98 pub stripe: Option<J2kStripe>,
100 pub block: Option<J2kBlock>,
102 pub mdm: Option<J2kMdm>,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize))]
109#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
110pub struct J2kVideoDescriptor<'a> {
111 pub extended_capability_flag: bool,
113 pub profile_and_level: u16,
115 pub horizontal_size: u32,
117 pub vertical_size: u32,
119 pub max_bit_rate: u32,
121 pub max_buffer_size: u32,
123 pub den_frame_rate: u16,
125 pub num_frame_rate: u16,
127 pub extended_capability: Option<J2kExtendedCapability>,
129 pub color_specification: Option<u8>,
131 pub still_mode: bool,
133 pub interlaced_video: bool,
135 #[cfg_attr(feature = "serde", serde(borrow))]
137 pub private_data: &'a [u8],
138}
139
140fn parse_extended_capability(
141 body: &[u8],
142 mut pos: usize,
143) -> Result<(J2kExtendedCapability, usize)> {
144 if body.len() < pos + 1 {
146 return Err(Error::InvalidDescriptor {
147 tag: TAG,
148 reason: "J2K_video_descriptor too short for stripe/block/mdm flags",
149 });
150 }
151 let sb = body[pos];
152 let stripe_flag = (sb & 0x80) != 0;
153 let block_flag = (sb & 0x40) != 0;
154 let mdm_flag = (sb & 0x20) != 0;
155 pos += 1;
156
157 if body.len() < pos + 4 {
159 return Err(Error::InvalidDescriptor {
160 tag: TAG,
161 reason: "J2K_video_descriptor too short for colour parameters",
162 });
163 }
164 let colour_primaries = body[pos];
165 let transfer_characteristics = body[pos + 1];
166 let matrix_coefficients = body[pos + 2];
167 let vfr = body[pos + 3];
168 let video_full_range_flag = (vfr & 0x80) != 0;
169 pos += 4;
170
171 let stripe = if stripe_flag {
173 if body.len() < pos + 3 {
174 return Err(Error::InvalidDescriptor {
175 tag: TAG,
176 reason: "J2K_video_descriptor too short for stripe sub-block",
177 });
178 }
179 let strp_max_idx = body[pos];
180 let strp_height = u16::from_be_bytes([body[pos + 1], body[pos + 2]]);
181 pos += 3;
182 Some(J2kStripe {
183 strp_max_idx,
184 strp_height,
185 })
186 } else {
187 None
188 };
189
190 let block = if block_flag {
192 if body.len() < pos + 16 {
193 return Err(Error::InvalidDescriptor {
194 tag: TAG,
195 reason: "J2K_video_descriptor too short for block sub-block",
196 });
197 }
198 let full_horizontal_size =
199 u32::from_be_bytes([body[pos], body[pos + 1], body[pos + 2], body[pos + 3]]);
200 let full_vertical_size =
201 u32::from_be_bytes([body[pos + 4], body[pos + 5], body[pos + 6], body[pos + 7]]);
202 let blk_width = u16::from_be_bytes([body[pos + 8], body[pos + 9]]);
203 let blk_height = u16::from_be_bytes([body[pos + 10], body[pos + 11]]);
204 let max_blk_idx_h = body[pos + 12];
205 let max_blk_idx_v = body[pos + 13];
206 let blk_idx_h = body[pos + 14];
207 let blk_idx_v = body[pos + 15];
208 pos += 16;
209 Some(J2kBlock {
210 full_horizontal_size,
211 full_vertical_size,
212 blk_width,
213 blk_height,
214 max_blk_idx_h,
215 max_blk_idx_v,
216 blk_idx_h,
217 blk_idx_v,
218 })
219 } else {
220 None
221 };
222
223 let mdm = if mdm_flag {
225 if body.len() < pos + 28 {
226 return Err(Error::InvalidDescriptor {
227 tag: TAG,
228 reason: "J2K_video_descriptor too short for MDM sub-block",
229 });
230 }
231 let x_c0 = u16::from_be_bytes([body[pos], body[pos + 1]]);
232 let y_c0 = u16::from_be_bytes([body[pos + 2], body[pos + 3]]);
233 let x_c1 = u16::from_be_bytes([body[pos + 4], body[pos + 5]]);
234 let y_c1 = u16::from_be_bytes([body[pos + 6], body[pos + 7]]);
235 let x_c2 = u16::from_be_bytes([body[pos + 8], body[pos + 9]]);
236 let y_c2 = u16::from_be_bytes([body[pos + 10], body[pos + 11]]);
237 let x_wp = u16::from_be_bytes([body[pos + 12], body[pos + 13]]);
238 let y_wp = u16::from_be_bytes([body[pos + 14], body[pos + 15]]);
239 let l_max = u32::from_be_bytes([
240 body[pos + 16],
241 body[pos + 17],
242 body[pos + 18],
243 body[pos + 19],
244 ]);
245 let l_min = u32::from_be_bytes([
246 body[pos + 20],
247 body[pos + 21],
248 body[pos + 22],
249 body[pos + 23],
250 ]);
251 let max_cll = u16::from_be_bytes([body[pos + 24], body[pos + 25]]);
252 let max_fall = u16::from_be_bytes([body[pos + 26], body[pos + 27]]);
253 pos += 28;
254 Some(J2kMdm {
255 x_c0,
256 y_c0,
257 x_c1,
258 y_c1,
259 x_c2,
260 y_c2,
261 x_wp,
262 y_wp,
263 l_max,
264 l_min,
265 max_cll,
266 max_fall,
267 })
268 } else {
269 None
270 };
271
272 Ok((
273 J2kExtendedCapability {
274 stripe_flag,
275 block_flag,
276 mdm_flag,
277 colour_primaries,
278 transfer_characteristics,
279 matrix_coefficients,
280 video_full_range_flag,
281 stripe,
282 block,
283 mdm,
284 },
285 pos,
286 ))
287}
288
289fn serialize_extended_capability(
290 ext: &J2kExtendedCapability,
291 buf: &mut [u8],
292 mut pos: usize,
293) -> usize {
294 let mut sb = 0u8;
295 if ext.stripe_flag {
296 sb |= 0x80;
297 }
298 if ext.block_flag {
299 sb |= 0x40;
300 }
301 if ext.mdm_flag {
302 sb |= 0x20;
303 }
304 buf[pos] = sb;
305 pos += 1;
306
307 buf[pos] = ext.colour_primaries;
308 buf[pos + 1] = ext.transfer_characteristics;
309 buf[pos + 2] = ext.matrix_coefficients;
310 buf[pos + 3] = if ext.video_full_range_flag {
311 0x80
312 } else {
313 0x00
314 };
315 pos += 4;
316
317 if let Some(ref s) = ext.stripe {
318 buf[pos] = s.strp_max_idx;
319 buf[pos + 1..pos + 3].copy_from_slice(&s.strp_height.to_be_bytes());
320 pos += 3;
321 }
322
323 if let Some(ref b) = ext.block {
324 buf[pos..pos + 4].copy_from_slice(&b.full_horizontal_size.to_be_bytes());
325 buf[pos + 4..pos + 8].copy_from_slice(&b.full_vertical_size.to_be_bytes());
326 buf[pos + 8..pos + 10].copy_from_slice(&b.blk_width.to_be_bytes());
327 buf[pos + 10..pos + 12].copy_from_slice(&b.blk_height.to_be_bytes());
328 buf[pos + 12] = b.max_blk_idx_h;
329 buf[pos + 13] = b.max_blk_idx_v;
330 buf[pos + 14] = b.blk_idx_h;
331 buf[pos + 15] = b.blk_idx_v;
332 pos += 16;
333 }
334
335 if let Some(ref m) = ext.mdm {
336 buf[pos..pos + 2].copy_from_slice(&m.x_c0.to_be_bytes());
337 buf[pos + 2..pos + 4].copy_from_slice(&m.y_c0.to_be_bytes());
338 buf[pos + 4..pos + 6].copy_from_slice(&m.x_c1.to_be_bytes());
339 buf[pos + 6..pos + 8].copy_from_slice(&m.y_c1.to_be_bytes());
340 buf[pos + 8..pos + 10].copy_from_slice(&m.x_c2.to_be_bytes());
341 buf[pos + 10..pos + 12].copy_from_slice(&m.y_c2.to_be_bytes());
342 buf[pos + 12..pos + 14].copy_from_slice(&m.x_wp.to_be_bytes());
343 buf[pos + 14..pos + 16].copy_from_slice(&m.y_wp.to_be_bytes());
344 buf[pos + 16..pos + 20].copy_from_slice(&m.l_max.to_be_bytes());
345 buf[pos + 20..pos + 24].copy_from_slice(&m.l_min.to_be_bytes());
346 buf[pos + 24..pos + 26].copy_from_slice(&m.max_cll.to_be_bytes());
347 buf[pos + 26..pos + 28].copy_from_slice(&m.max_fall.to_be_bytes());
348 pos += 28;
349 }
350
351 pos
352}
353
354fn extended_capability_serialized_len(ext: &J2kExtendedCapability) -> usize {
355 let mut len: usize = 5; if ext.stripe.is_some() {
357 len += 3;
358 }
359 if ext.block.is_some() {
360 len += 16;
361 }
362 if ext.mdm.is_some() {
363 len += 28;
364 }
365 len
366}
367
368impl<'a> Parse<'a> for J2kVideoDescriptor<'a> {
369 type Error = crate::error::Error;
370
371 fn parse(bytes: &'a [u8]) -> Result<Self> {
372 let body = descriptor_body(
373 bytes,
374 TAG,
375 "J2kVideoDescriptor",
376 "unexpected tag for J2K_video_descriptor",
377 )?;
378
379 if body.len() < 22 {
383 return Err(Error::InvalidDescriptor {
384 tag: TAG,
385 reason: "J2K_video_descriptor too short (< 22 body bytes)",
386 });
387 }
388
389 let b01 = u16::from_be_bytes([body[0], body[1]]);
391 let extended_capability_flag = (b01 & 0x8000) != 0;
392 let profile_and_level = b01 & 0x7FFF;
393
394 let horizontal_size = u32::from_be_bytes([body[2], body[3], body[4], body[5]]);
395 let vertical_size = u32::from_be_bytes([body[6], body[7], body[8], body[9]]);
396 let max_bit_rate = u32::from_be_bytes([body[10], body[11], body[12], body[13]]);
397 let max_buffer_size = u32::from_be_bytes([body[14], body[15], body[16], body[17]]);
398 let den_frame_rate = u16::from_be_bytes([body[18], body[19]]);
399 let num_frame_rate = u16::from_be_bytes([body[20], body[21]]);
400 let mut pos = 22;
401
402 let (extended_capability, color_specification, mut pos) = if extended_capability_flag {
403 let (ext, new_pos) = parse_extended_capability(body, pos)?;
404 (Some(ext), None, new_pos)
405 } else {
406 if body.len() < pos + 1 {
408 return Err(Error::InvalidDescriptor {
409 tag: TAG,
410 reason: "J2K_video_descriptor too short for color_specification",
411 });
412 }
413 let cs = body[pos];
414 pos += 1;
415 (None, Some(cs), pos)
416 };
417
418 if body.len() < pos + 1 {
420 return Err(Error::InvalidDescriptor {
421 tag: TAG,
422 reason: "J2K_video_descriptor too short for still_mode/interlaced byte",
423 });
424 }
425 let sm_iv = body[pos];
426 let still_mode = (sm_iv & 0x80) != 0;
427 let interlaced_video = (sm_iv & 0x40) != 0;
428 pos += 1;
429
430 let private_data = &body[pos..];
431
432 Ok(Self {
433 extended_capability_flag,
434 profile_and_level,
435 horizontal_size,
436 vertical_size,
437 max_bit_rate,
438 max_buffer_size,
439 den_frame_rate,
440 num_frame_rate,
441 extended_capability,
442 color_specification,
443 still_mode,
444 interlaced_video,
445 private_data,
446 })
447 }
448}
449
450impl Serialize for J2kVideoDescriptor<'_> {
451 type Error = crate::error::Error;
452
453 fn serialized_len(&self) -> usize {
454 let mut len: usize = HEADER_LEN + 23; if let Some(ref ext) = self.extended_capability {
456 len += extended_capability_serialized_len(ext);
457 } else {
458 len += 1; }
460 len += self.private_data.len();
461 len
462 }
463
464 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
465 let len = self.serialized_len();
466 if buf.len() < len {
467 return Err(Error::OutputBufferTooSmall {
468 need: len,
469 have: buf.len(),
470 });
471 }
472 buf[0] = TAG;
473 buf[1] = (len - HEADER_LEN) as u8;
474
475 let mut b01 = self.profile_and_level & 0x7FFF;
477 if self.extended_capability_flag {
478 b01 |= 0x8000;
479 }
480 buf[HEADER_LEN] = (b01 >> 8) as u8;
481 buf[HEADER_LEN + 1] = b01 as u8;
482 buf[HEADER_LEN + 2..HEADER_LEN + 6].copy_from_slice(&self.horizontal_size.to_be_bytes());
483 buf[HEADER_LEN + 6..HEADER_LEN + 10].copy_from_slice(&self.vertical_size.to_be_bytes());
484 buf[HEADER_LEN + 10..HEADER_LEN + 14].copy_from_slice(&self.max_bit_rate.to_be_bytes());
485 buf[HEADER_LEN + 14..HEADER_LEN + 18].copy_from_slice(&self.max_buffer_size.to_be_bytes());
486 buf[HEADER_LEN + 18..HEADER_LEN + 20].copy_from_slice(&self.den_frame_rate.to_be_bytes());
487 buf[HEADER_LEN + 20..HEADER_LEN + 22].copy_from_slice(&self.num_frame_rate.to_be_bytes());
488 let mut pos = HEADER_LEN + 22;
489
490 if let Some(ref ext) = self.extended_capability {
491 pos = serialize_extended_capability(ext, buf, pos);
492 } else if let Some(cs) = self.color_specification {
493 buf[pos] = cs;
494 pos += 1;
495 }
496
497 let mut sm_iv = 0u8;
498 if self.still_mode {
499 sm_iv |= 0x80;
500 }
501 if self.interlaced_video {
502 sm_iv |= 0x40;
503 }
504 buf[pos] = sm_iv;
505 pos += 1;
506
507 buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
508 Ok(len)
509 }
510}
511
512impl<'a> crate::traits::DescriptorDef<'a> for J2kVideoDescriptor<'a> {
513 const TAG: u8 = TAG;
514 const NAME: &'static str = "J2K_VIDEO";
515}
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520
521 fn serialize_round_trip(d: &J2kVideoDescriptor<'_>) {
522 let mut buf = vec![0u8; d.serialized_len()];
523 let written = d.serialize_into(&mut buf).unwrap();
524 assert_eq!(written, d.serialized_len());
525 let reparsed = J2kVideoDescriptor::parse(&buf).unwrap();
526 assert_eq!(*d, reparsed, "round-trip mismatch");
527 }
528
529 #[test]
530 fn round_trip_no_extended_capability() {
531 let d = J2kVideoDescriptor {
532 extended_capability_flag: false,
533 profile_and_level: 0x0102,
534 horizontal_size: 1920,
535 vertical_size: 1080,
536 max_bit_rate: 10_000_000,
537 max_buffer_size: 8_000_000,
538 den_frame_rate: 1001,
539 num_frame_rate: 24000,
540 extended_capability: None,
541 color_specification: Some(0x03),
542 still_mode: false,
543 interlaced_video: true,
544 private_data: &[],
545 };
546 serialize_round_trip(&d);
547 }
548
549 #[test]
550 fn round_trip_extended_capability_basic() {
551 let d = J2kVideoDescriptor {
552 extended_capability_flag: true,
553 profile_and_level: 0x0307,
554 horizontal_size: 3840,
555 vertical_size: 2160,
556 max_bit_rate: 30_000_000,
557 max_buffer_size: 15_000_000,
558 den_frame_rate: 1001,
559 num_frame_rate: 60000,
560 extended_capability: Some(J2kExtendedCapability {
561 stripe_flag: false,
562 block_flag: false,
563 mdm_flag: false,
564 colour_primaries: 1,
565 transfer_characteristics: 16,
566 matrix_coefficients: 0,
567 video_full_range_flag: true,
568 stripe: None,
569 block: None,
570 mdm: None,
571 }),
572 color_specification: None,
573 still_mode: false,
574 interlaced_video: false,
575 private_data: &[],
576 };
577 serialize_round_trip(&d);
578 }
579
580 #[test]
581 fn round_trip_extended_capability_stripe_only() {
582 let d = J2kVideoDescriptor {
583 extended_capability_flag: true,
584 profile_and_level: 0x0307,
585 horizontal_size: 3840,
586 vertical_size: 2160,
587 max_bit_rate: 30_000_000,
588 max_buffer_size: 15_000_000,
589 den_frame_rate: 1,
590 num_frame_rate: 60,
591 extended_capability: Some(J2kExtendedCapability {
592 stripe_flag: true,
593 block_flag: false,
594 mdm_flag: false,
595 colour_primaries: 9,
596 transfer_characteristics: 14,
597 matrix_coefficients: 0,
598 video_full_range_flag: false,
599 stripe: Some(J2kStripe {
600 strp_max_idx: 3,
601 strp_height: 1024,
602 }),
603 block: None,
604 mdm: None,
605 }),
606 color_specification: None,
607 still_mode: true,
608 interlaced_video: false,
609 private_data: &[0xAA],
610 };
611 serialize_round_trip(&d);
612 }
613
614 #[test]
615 fn round_trip_extended_capability_block_only() {
616 let d = J2kVideoDescriptor {
617 extended_capability_flag: true,
618 profile_and_level: 0x0307,
619 horizontal_size: 1920,
620 vertical_size: 1080,
621 max_bit_rate: 20_000_000,
622 max_buffer_size: 10_000_000,
623 den_frame_rate: 1001,
624 num_frame_rate: 30000,
625 extended_capability: Some(J2kExtendedCapability {
626 stripe_flag: false,
627 block_flag: true,
628 mdm_flag: false,
629 colour_primaries: 1,
630 transfer_characteristics: 1,
631 matrix_coefficients: 1,
632 video_full_range_flag: true,
633 stripe: None,
634 block: Some(J2kBlock {
635 full_horizontal_size: 3840,
636 full_vertical_size: 2160,
637 blk_width: 1920,
638 blk_height: 1080,
639 max_blk_idx_h: 1,
640 max_blk_idx_v: 1,
641 blk_idx_h: 0,
642 blk_idx_v: 0,
643 }),
644 mdm: None,
645 }),
646 color_specification: None,
647 still_mode: false,
648 interlaced_video: false,
649 private_data: &[],
650 };
651 serialize_round_trip(&d);
652 }
653
654 #[test]
655 fn round_trip_extended_capability_mdm_only() {
656 let d = J2kVideoDescriptor {
657 extended_capability_flag: true,
658 profile_and_level: 0x0307,
659 horizontal_size: 1920,
660 vertical_size: 1080,
661 max_bit_rate: 15_000_000,
662 max_buffer_size: 8_000_000,
663 den_frame_rate: 1,
664 num_frame_rate: 25,
665 extended_capability: Some(J2kExtendedCapability {
666 stripe_flag: false,
667 block_flag: false,
668 mdm_flag: true,
669 colour_primaries: 9,
670 transfer_characteristics: 16,
671 matrix_coefficients: 9,
672 video_full_range_flag: false,
673 stripe: None,
674 block: None,
675 mdm: Some(J2kMdm {
676 x_c0: 1,
677 y_c0: 2,
678 x_c1: 3,
679 y_c1: 4,
680 x_c2: 5,
681 y_c2: 6,
682 x_wp: 7,
683 y_wp: 8,
684 l_max: 1000,
685 l_min: 5,
686 max_cll: 800,
687 max_fall: 400,
688 }),
689 }),
690 color_specification: None,
691 still_mode: false,
692 interlaced_video: true,
693 private_data: &[],
694 };
695 serialize_round_trip(&d);
696 }
697
698 #[test]
699 fn round_trip_extended_capability_all() {
700 let d = J2kVideoDescriptor {
701 extended_capability_flag: true,
702 profile_and_level: 0x0307,
703 horizontal_size: 7680,
704 vertical_size: 4320,
705 max_bit_rate: 100_000_000,
706 max_buffer_size: 50_000_000,
707 den_frame_rate: 1001,
708 num_frame_rate: 60000,
709 extended_capability: Some(J2kExtendedCapability {
710 stripe_flag: true,
711 block_flag: true,
712 mdm_flag: true,
713 colour_primaries: 9,
714 transfer_characteristics: 16,
715 matrix_coefficients: 9,
716 video_full_range_flag: true,
717 stripe: Some(J2kStripe {
718 strp_max_idx: 7,
719 strp_height: 540,
720 }),
721 block: Some(J2kBlock {
722 full_horizontal_size: 15360,
723 full_vertical_size: 8640,
724 blk_width: 7680,
725 blk_height: 4320,
726 max_blk_idx_h: 1,
727 max_blk_idx_v: 1,
728 blk_idx_h: 0,
729 blk_idx_v: 1,
730 }),
731 mdm: Some(J2kMdm {
732 x_c0: 6800,
733 y_c0: 3200,
734 x_c1: 2650,
735 y_c1: 6900,
736 x_c2: 1500,
737 y_c2: 600,
738 x_wp: 3127,
739 y_wp: 3290,
740 l_max: 4_000_000,
741 l_min: 50,
742 max_cll: 10_000,
743 max_fall: 5_000,
744 }),
745 }),
746 color_specification: None,
747 still_mode: true,
748 interlaced_video: true,
749 private_data: &[0xDD, 0xEE, 0xFF],
750 };
751 serialize_round_trip(&d);
752 }
753
754 #[test]
755 fn parse_rejects_wrong_tag() {
756 let buf = [
757 0x02, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
758 ];
759 let err = J2kVideoDescriptor::parse(&buf).unwrap_err();
760 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
761 }
762
763 #[test]
764 fn parse_rejects_too_short() {
765 let err = J2kVideoDescriptor::parse(&[TAG, 0]).unwrap_err();
766 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
767 }
768}