1use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum MbPartPredMode {
7 Intra4x4,
8 Intra8x8,
9 Intra16x16,
10 PredL0,
11 Direct,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum MbTypeError {
16 InvalidIMbType(u32),
17 InvalidPMbType(u32),
18 InvalidPSubMbType(u32),
19 InvalidCodedBlockPatternCodeNum(u32),
20}
21impl std::error::Error for MbTypeError {}
22impl fmt::Display for MbTypeError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 MbTypeError::InvalidIMbType(v) => write!(f, "invalid I mb_type value {}", v),
26 MbTypeError::InvalidPMbType(v) => write!(f, "invalid P mb_type value {}", v),
27 MbTypeError::InvalidPSubMbType(v) => write!(f, "invalid P sub_mb_type value {}", v),
28 MbTypeError::InvalidCodedBlockPatternCodeNum(v) => {
29 write!(f, "invalid coded_block_pattern code_num {}", v)
30 }
31 }
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum IMbTypeInfo {
37 INxN,
38 I16x16 {
39 intra16x16_pred_mode: u8,
40 coded_block_pattern_chroma: u8,
41 coded_block_pattern_luma: u8,
42 },
43 IPCM,
44}
45
46impl IMbTypeInfo {
47 pub fn pred_mode(&self) -> MbPartPredMode {
48 match self {
49 IMbTypeInfo::INxN => MbPartPredMode::Intra4x4,
50 IMbTypeInfo::I16x16 { .. } => MbPartPredMode::Intra16x16,
51 IMbTypeInfo::IPCM => MbPartPredMode::Intra4x4,
52 }
53 }
54
55 pub fn num_parts(&self) -> u8 {
56 1
57 }
58
59 pub fn part_width(&self) -> u8 {
60 16
61 }
62
63 pub fn part_height(&self) -> u8 {
64 16
65 }
66}
67
68pub fn i_mb_type_info(mb_type: u32) -> Result<IMbTypeInfo, MbTypeError> {
69 match mb_type {
70 0 => Ok(IMbTypeInfo::INxN),
71 1..=24 => {
72 let val = mb_type - 1;
73 let (coded_block_pattern_luma, idx) = if val < 12 { (0, val) } else { (15, val - 12) };
74 let intra16x16_pred_mode = (idx % 4) as u8;
75 let coded_block_pattern_chroma = (idx / 4) as u8;
76 Ok(IMbTypeInfo::I16x16 {
77 intra16x16_pred_mode,
78 coded_block_pattern_chroma,
79 coded_block_pattern_luma,
80 })
81 }
82 25 => Ok(IMbTypeInfo::IPCM),
83 _ => Err(MbTypeError::InvalidIMbType(mb_type)),
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub enum PMbTypeInfo {
89 P {
90 num_parts: u8,
91 pred_mode: MbPartPredMode,
92 part_width: u8,
93 part_height: u8,
94 ref_idx_forced_zero: bool,
95 },
96 I(IMbTypeInfo),
97}
98
99impl PMbTypeInfo {
100 pub fn num_parts(&self) -> u8 {
101 match self {
102 PMbTypeInfo::P { num_parts, .. } => *num_parts,
103 PMbTypeInfo::I(info) => info.num_parts(),
104 }
105 }
106
107 pub fn part_width(&self) -> u8 {
108 match self {
109 PMbTypeInfo::P { part_width, .. } => *part_width,
110 PMbTypeInfo::I(info) => info.part_width(),
111 }
112 }
113
114 pub fn part_height(&self) -> u8 {
115 match self {
116 PMbTypeInfo::P { part_height, .. } => *part_height,
117 PMbTypeInfo::I(info) => info.part_height(),
118 }
119 }
120
121 pub fn pred_mode(&self) -> MbPartPredMode {
122 match self {
123 PMbTypeInfo::P { pred_mode, .. } => *pred_mode,
124 PMbTypeInfo::I(info) => info.pred_mode(),
125 }
126 }
127}
128
129pub fn p_mb_type_info(mb_type: u32) -> Result<PMbTypeInfo, MbTypeError> {
130 match mb_type {
131 0 => Ok(PMbTypeInfo::P {
132 num_parts: 1,
133 pred_mode: MbPartPredMode::PredL0,
134 part_width: 16,
135 part_height: 16,
136 ref_idx_forced_zero: false,
137 }),
138 1 => Ok(PMbTypeInfo::P {
139 num_parts: 2,
140 pred_mode: MbPartPredMode::PredL0,
141 part_width: 16,
142 part_height: 8,
143 ref_idx_forced_zero: false,
144 }),
145 2 => Ok(PMbTypeInfo::P {
146 num_parts: 2,
147 pred_mode: MbPartPredMode::PredL0,
148 part_width: 8,
149 part_height: 16,
150 ref_idx_forced_zero: false,
151 }),
152 3 => Ok(PMbTypeInfo::P {
153 num_parts: 4,
154 pred_mode: MbPartPredMode::PredL0,
155 part_width: 8,
156 part_height: 8,
157 ref_idx_forced_zero: false,
158 }),
159 4 => Ok(PMbTypeInfo::P {
160 num_parts: 4,
161 pred_mode: MbPartPredMode::PredL0,
162 part_width: 8,
163 part_height: 8,
164 ref_idx_forced_zero: true,
165 }),
166 5..=30 => {
167 let i_type =
168 i_mb_type_info(mb_type - 5).map_err(|_| MbTypeError::InvalidPMbType(mb_type))?;
169 Ok(PMbTypeInfo::I(i_type))
170 }
171 _ => Err(MbTypeError::InvalidPMbType(mb_type)),
172 }
173}
174
175#[derive(Debug, Clone, PartialEq, Eq)]
176pub struct SubMbTypeInfo {
177 pub num_sub_parts: u8,
178 pub sub_part_width: u8,
179 pub sub_part_height: u8,
180 pub pred_mode: MbPartPredMode,
181}
182
183pub fn p_sub_mb_type_info(sub_mb_type: u32) -> Result<SubMbTypeInfo, MbTypeError> {
184 match sub_mb_type {
185 0 => Ok(SubMbTypeInfo {
186 num_sub_parts: 1,
187 sub_part_width: 8,
188 sub_part_height: 8,
189 pred_mode: MbPartPredMode::PredL0,
190 }),
191 1 => Ok(SubMbTypeInfo {
192 num_sub_parts: 2,
193 sub_part_width: 8,
194 sub_part_height: 4,
195 pred_mode: MbPartPredMode::PredL0,
196 }),
197 2 => Ok(SubMbTypeInfo {
198 num_sub_parts: 2,
199 sub_part_width: 4,
200 sub_part_height: 8,
201 pred_mode: MbPartPredMode::PredL0,
202 }),
203 3 => Ok(SubMbTypeInfo {
204 num_sub_parts: 4,
205 sub_part_width: 4,
206 sub_part_height: 4,
207 pred_mode: MbPartPredMode::PredL0,
208 }),
209 _ => Err(MbTypeError::InvalidPSubMbType(sub_mb_type)),
210 }
211}
212
213const CBP_INTRA_MAP: [u8; 48] = [
214 47, 31, 15, 0, 23, 27, 29, 30, 7, 11, 13, 14, 39, 43, 45, 46, 16, 3, 5, 10, 12, 19, 21, 26, 28,
215 35, 37, 42, 44, 1, 2, 4, 8, 17, 18, 20, 24, 6, 9, 22, 25, 32, 33, 34, 36, 40, 38, 41,
216];
217
218const CBP_INTER_MAP: [u8; 48] = [
219 0, 16, 1, 2, 4, 8, 32, 3, 5, 10, 12, 15, 47, 7, 11, 13, 14, 6, 9, 31, 35, 37, 42, 44, 33, 34,
220 36, 40, 39, 43, 45, 46, 17, 18, 20, 24, 19, 21, 26, 28, 23, 27, 29, 30, 22, 25, 38, 41,
221];
222
223pub fn coded_block_pattern_from_me(code_num: u32, is_intra: bool) -> Option<(u8, u8)> {
231 if code_num > 47 {
232 return None;
233 }
234 let table = if is_intra {
235 &CBP_INTRA_MAP
236 } else {
237 &CBP_INTER_MAP
238 };
239 let cbp = table[code_num as usize];
240 let coded_block_pattern_luma = cbp % 16;
241 let coded_block_pattern_chroma = cbp / 16;
242 Some((coded_block_pattern_luma, coded_block_pattern_chroma))
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn i_mb_type_0_is_inxn() {
251 let info = i_mb_type_info(0).unwrap();
252 assert_eq!(info, IMbTypeInfo::INxN);
253 assert_eq!(info.pred_mode(), MbPartPredMode::Intra4x4);
254 }
255
256 #[test]
257 fn i_mb_type_1_is_i16x16_0_0_0() {
258 let info = i_mb_type_info(1).unwrap();
259 assert_eq!(
260 info,
261 IMbTypeInfo::I16x16 {
262 intra16x16_pred_mode: 0,
263 coded_block_pattern_chroma: 0,
264 coded_block_pattern_luma: 0,
265 }
266 );
267 assert_eq!(info.pred_mode(), MbPartPredMode::Intra16x16);
268 }
269
270 #[test]
271 fn i_mb_type_2_is_i16x16_1_0_0() {
272 let info = i_mb_type_info(2).unwrap();
273 assert_eq!(
274 info,
275 IMbTypeInfo::I16x16 {
276 intra16x16_pred_mode: 1,
277 coded_block_pattern_chroma: 0,
278 coded_block_pattern_luma: 0,
279 }
280 );
281 }
282
283 #[test]
284 fn i_mb_type_5_is_i16x16_0_1_0() {
285 let info = i_mb_type_info(5).unwrap();
286 assert_eq!(
287 info,
288 IMbTypeInfo::I16x16 {
289 intra16x16_pred_mode: 0,
290 coded_block_pattern_chroma: 1,
291 coded_block_pattern_luma: 0,
292 }
293 );
294 }
295
296 #[test]
297 fn i_mb_type_13_is_i16x16_0_0_1() {
298 let info = i_mb_type_info(13).unwrap();
299 assert_eq!(
300 info,
301 IMbTypeInfo::I16x16 {
302 intra16x16_pred_mode: 0,
303 coded_block_pattern_chroma: 0,
304 coded_block_pattern_luma: 15,
305 }
306 );
307 }
308
309 #[test]
310 fn i_mb_type_24_is_i16x16_3_2_1() {
311 let info = i_mb_type_info(24).unwrap();
312 assert_eq!(
313 info,
314 IMbTypeInfo::I16x16 {
315 intra16x16_pred_mode: 3,
316 coded_block_pattern_chroma: 2,
317 coded_block_pattern_luma: 15,
318 }
319 );
320 }
321
322 #[test]
323 fn i_mb_type_25_is_ipcm() {
324 let info = i_mb_type_info(25).unwrap();
325 assert_eq!(info, IMbTypeInfo::IPCM);
326 }
327
328 #[test]
329 fn i_mb_type_26_is_invalid() {
330 assert_eq!(i_mb_type_info(26), Err(MbTypeError::InvalidIMbType(26)));
331 }
332
333 #[test]
334 fn p_mb_type_0_is_p_l0_16x16() {
335 let info = p_mb_type_info(0).unwrap();
336 assert_eq!(
337 info,
338 PMbTypeInfo::P {
339 num_parts: 1,
340 pred_mode: MbPartPredMode::PredL0,
341 part_width: 16,
342 part_height: 16,
343 ref_idx_forced_zero: false,
344 }
345 );
346 assert_eq!(info.num_parts(), 1);
347 }
348
349 #[test]
350 fn p_mb_type_1_is_p_l0_l0_16x8() {
351 let info = p_mb_type_info(1).unwrap();
352 assert_eq!(info.num_parts(), 2);
353 assert_eq!(info.part_width(), 16);
354 assert_eq!(info.part_height(), 8);
355 }
356
357 #[test]
358 fn p_mb_type_2_is_p_l0_l0_8x16() {
359 let info = p_mb_type_info(2).unwrap();
360 assert_eq!(info.num_parts(), 2);
361 assert_eq!(info.part_width(), 8);
362 assert_eq!(info.part_height(), 16);
363 }
364
365 #[test]
366 fn p_mb_type_3_is_p_8x8() {
367 let info = p_mb_type_info(3).unwrap();
368 assert_eq!(info.num_parts(), 4);
369 assert_eq!(info.part_width(), 8);
370 assert_eq!(info.part_height(), 8);
371 }
372
373 #[test]
374 fn p_mb_type_4_is_p_8x8ref0() {
375 let info = p_mb_type_info(4).unwrap();
376 match info {
377 PMbTypeInfo::P {
378 ref_idx_forced_zero,
379 ..
380 } => assert!(ref_idx_forced_zero),
381 _ => panic!("expected P type"),
382 }
383 }
384
385 #[test]
386 fn p_mb_type_5_maps_to_inxn() {
387 let info = p_mb_type_info(5).unwrap();
388 assert_eq!(info, PMbTypeInfo::I(IMbTypeInfo::INxN));
389 }
390
391 #[test]
392 fn p_mb_type_6_maps_to_i16x16_0_0_0() {
393 let info = p_mb_type_info(6).unwrap();
394 assert_eq!(
395 info,
396 PMbTypeInfo::I(IMbTypeInfo::I16x16 {
397 intra16x16_pred_mode: 0,
398 coded_block_pattern_chroma: 0,
399 coded_block_pattern_luma: 0,
400 })
401 );
402 }
403
404 #[test]
405 fn p_mb_type_30_maps_to_ipcm() {
406 let info = p_mb_type_info(30).unwrap();
407 assert_eq!(info, PMbTypeInfo::I(IMbTypeInfo::IPCM));
408 }
409
410 #[test]
411 fn p_mb_type_31_is_invalid() {
412 assert_eq!(p_mb_type_info(31), Err(MbTypeError::InvalidPMbType(31)));
413 }
414
415 #[test]
416 fn p_sub_mb_type_0_is_8x8() {
417 let info = p_sub_mb_type_info(0).unwrap();
418 assert_eq!(
419 info,
420 SubMbTypeInfo {
421 num_sub_parts: 1,
422 sub_part_width: 8,
423 sub_part_height: 8,
424 pred_mode: MbPartPredMode::PredL0,
425 }
426 );
427 }
428
429 #[test]
430 fn p_sub_mb_type_1_is_8x4() {
431 let info = p_sub_mb_type_info(1).unwrap();
432 assert_eq!(info.num_sub_parts, 2);
433 assert_eq!(info.sub_part_width, 8);
434 assert_eq!(info.sub_part_height, 4);
435 }
436
437 #[test]
438 fn p_sub_mb_type_2_is_4x8() {
439 let info = p_sub_mb_type_info(2).unwrap();
440 assert_eq!(info.num_sub_parts, 2);
441 assert_eq!(info.sub_part_width, 4);
442 assert_eq!(info.sub_part_height, 8);
443 }
444
445 #[test]
446 fn p_sub_mb_type_3_is_4x4() {
447 let info = p_sub_mb_type_info(3).unwrap();
448 assert_eq!(info.num_sub_parts, 4);
449 assert_eq!(info.sub_part_width, 4);
450 assert_eq!(info.sub_part_height, 4);
451 }
452
453 #[test]
454 fn p_sub_mb_type_4_is_invalid() {
455 assert_eq!(
456 p_sub_mb_type_info(4),
457 Err(MbTypeError::InvalidPSubMbType(4))
458 );
459 }
460
461 #[test]
462 fn cbp_intra_code_num_0_gives_15_2() {
463 let (luma, chroma) = coded_block_pattern_from_me(0, true).unwrap();
464 assert_eq!(luma, 15);
465 assert_eq!(chroma, 2);
466 }
467
468 #[test]
469 fn cbp_inter_code_num_0_gives_0_0() {
470 let (luma, chroma) = coded_block_pattern_from_me(0, false).unwrap();
471 assert_eq!(luma, 0);
472 assert_eq!(chroma, 0);
473 }
474
475 #[test]
476 fn cbp_intra_code_num_3_gives_0_0() {
477 let (luma, chroma) = coded_block_pattern_from_me(3, true).unwrap();
478 assert_eq!(luma, 0);
479 assert_eq!(chroma, 0);
480 }
481
482 #[test]
483 fn cbp_inter_code_num_1_gives_0_1() {
484 let (luma, chroma) = coded_block_pattern_from_me(1, false).unwrap();
485 assert_eq!(luma, 0);
486 assert_eq!(chroma, 1);
487 }
488
489 #[test]
490 fn cbp_inter_code_num_11_gives_15_0() {
491 let (luma, chroma) = coded_block_pattern_from_me(11, false).unwrap();
492 assert_eq!(luma, 15);
493 assert_eq!(chroma, 0);
494 }
495
496 #[test]
497 fn cbp_code_num_47_is_valid() {
498 assert!(coded_block_pattern_from_me(47, true).is_some());
499 assert!(coded_block_pattern_from_me(47, false).is_some());
500 }
501
502 #[test]
503 fn cbp_code_num_48_is_invalid() {
504 assert!(coded_block_pattern_from_me(48, true).is_none());
505 assert!(coded_block_pattern_from_me(48, false).is_none());
506 }
507
508 #[test]
509 fn all_i_mb_types_valid() {
510 for mb_type in 0..=25 {
511 assert!(
512 i_mb_type_info(mb_type).is_ok(),
513 "mb_type {} should be valid",
514 mb_type
515 );
516 }
517 }
518
519 #[test]
520 fn all_i16x16_pred_modes_in_range() {
521 for mb_type in 1..=24 {
522 if let IMbTypeInfo::I16x16 {
523 intra16x16_pred_mode,
524 coded_block_pattern_chroma,
525 ..
526 } = i_mb_type_info(mb_type).unwrap()
527 {
528 assert!(
529 intra16x16_pred_mode <= 3,
530 "pred_mode out of range for mb_type {}",
531 mb_type
532 );
533 assert!(
534 coded_block_pattern_chroma <= 2,
535 "cbp_chroma out of range for mb_type {}",
536 mb_type
537 );
538 }
539 }
540 }
541}