]> git.nihav.org Git - nihav.git/blob - nihav-realmedia/src/codecs/rv60.rs
54d3f2901f5e14e08f15fdcba07164083bb2239b
[nihav.git] / nihav-realmedia / src / codecs / rv60.rs
1 use nihav_core::formats::YUV420_FORMAT;
2 use nihav_core::frame::*;
3 use nihav_core::codecs::{NADecoder, NADecoderSupport, DecoderError, DecoderResult, FrameSkipMode};
4 use nihav_core::options::*;
5 use nihav_codec_support::codecs::{MV, ZERO_MV, IPBShuffler};
6 use nihav_core::io::byteio::{MemoryReader,ByteReader};
7 use nihav_core::io::bitreader::{BitReader,BitReaderMode};
8 use nihav_core::io::intcode::*;
9 use std::str::FromStr;
10
11 use super::rv60codes::*;
12 use super::rv60dsp::*;
13
14 struct UniqueList<A> {
15 list: [A; 4],
16 fill: usize,
17 max_size: usize,
18 }
19
20 impl<A:Copy+Default+PartialEq> UniqueList<A> {
21 fn new(max_size: usize) -> Self {
22 Self { list: [A::default(); 4], fill: 0, max_size }
23 }
24 fn add(&mut self, cand: A) {
25 if self.fill == self.max_size { return; }
26 let mut unique = true;
27 for el in self.list.iter().take(self.fill) {
28 if *el == cand {
29 unique = false;
30 break;
31 }
32 }
33 if unique {
34 self.list[self.fill] = cand;
35 self.fill += 1;
36 }
37 }
38 }
39
40 const RV60_FRAME_TYPES: [FrameType; 4] = [ FrameType::I, FrameType::P, FrameType::B, FrameType::Other ];
41 const MAX_IMODE: u8 = 34;
42
43 #[derive(Clone,Copy,Debug)]
44 #[allow(dead_code)]
45 struct FrameHeader {
46 profile: u8,
47 ftype: FrameType,
48 qp: u8,
49 osvquant: u8,
50 ts: u32,
51 width: usize,
52 awidth: usize,
53 height: usize,
54 aheight: usize,
55 two_f_refs: bool,
56 qp_off_type: u8,
57 deblock: bool,
58 deblock_chroma: bool,
59 }
60
61 const RV60_CUSTOM_MSG_LENS: [u32; 4] = [ 2, 4, 16, 32 ];
62 impl FrameHeader {
63 #[allow(clippy::identity_op)]
64 #[allow(clippy::needless_late_init)]
65 fn read(br: &mut BitReader) -> DecoderResult<Self> {
66 let marker = br.read(2)?;
67 validate!(marker == 3);
68 let profile = br.read(2)? as u8;
69 validate!(profile == 0);
70 let _someval = br.read(4)?;
71 let ftypeid = br.read(2)? as usize;
72 let ftype = RV60_FRAME_TYPES[ftypeid];
73 let qp = br.read(6)? as u8;
74 let marker = br.read(1)?;
75 validate!(marker == 0);
76 let toolset = br.read(2)?;
77 validate!(toolset == 0);
78 let osvquant = br.read(2)? as u8;
79 let _some_flag = br.read_bool()?;
80 let _some_val = br.read(2)?;
81 let ts = br.read(24)?;
82 let width = ((br.read(11)? as usize) + 1) * 4;
83 let height = ((br.read(11)? as usize) + 0) * 4;
84 validate!(height > 0);
85 let awidth = (width + 15) & !15;
86 let aheight = (height + 15) & !15;
87 let _some_flag = br.read_bool()?;
88 let two_f_refs;
89 if ftype == FrameType::I {
90 //byte17 = 0
91 two_f_refs = false;
92 } else {
93 let flag = br.read_bool()?;
94 if flag { // untested
95 br.skip(1)?;
96 br.skip(1)?;
97 br.skip(1)?;
98 }
99 //byte17 = flag?
100 two_f_refs = br.read_bool()?;
101 }
102 // if byte17 { dw40 = 2; dw3C = 2; } else { dw40 = 1; dw3C = 1; }
103 let _some_val = br.read_code(UintCodeType::Unary012)?; // luma_qp_diff?
104 let chroma_qp_diff = br.read(1)?;
105 validate!(chroma_qp_diff == 0);
106 let qp_off_type = br.read_code(UintCodeType::Unary012)? as u8;
107 let deblock = br.read_bool()?;
108 let deblock_chroma = deblock && !br.read_bool()?;
109 if br.read_bool()? {
110 let custom_msg_hdr_len = br.read(2)? as usize;
111 if custom_msg_hdr_len != 0 {
112 for &msg_len in RV60_CUSTOM_MSG_LENS[..custom_msg_hdr_len].iter() {
113 br.skip(msg_len * 8)?;
114 }
115 }
116 }
117
118 Ok(FrameHeader {
119 profile, ftype, qp, osvquant, ts, width, height, awidth, aheight,
120 two_f_refs, qp_off_type, deblock, deblock_chroma,
121 })
122 }
123 fn parse_slice_sizes(&self, br: &mut BitReader, sizes: &mut Vec<usize>) -> DecoderResult<()> {
124 let nslices = self.get_height_cu();
125 let nbits = (br.read(5)? as u8) + 1;
126 validate!(nbits < 32);
127 let mut signs: Vec<bool> = Vec::with_capacity(nslices);
128 for _ in 0..nslices {
129 let sign = br.read_bool()?;
130 signs.push(sign);
131 }
132 validate!(signs[0]);
133 sizes.clear();
134 let mut sum = 0;
135 let first_size = br.read(nbits)? as usize;
136 validate!(first_size > 0);
137 sum += first_size;
138 let mut lastsize = first_size;
139 sizes.push(first_size);
140 for &sign in signs[1..nslices].iter() {
141 let diff = br.read(nbits)? as isize;
142 let newsize = if sign {
143 (lastsize as isize).checked_add(diff)
144 } else {
145 (lastsize as isize).checked_sub(diff)
146 };
147 validate!(newsize.is_some());
148 let size = newsize.unwrap() as usize;
149 sizes.push(size);
150 sum += size;
151 lastsize = size;
152 }
153 br.align();
154 if ((br.left() >> 3) as usize) != sum {
155 println!(" left {} / {}", br.left() >> 3, sum);
156 }
157 validate!((br.left() >> 3) >= (sum as isize));
158 Ok(())
159 }
160 fn read_line_qp_offset(&self, br: &mut BitReader) -> DecoderResult<i8> {
161 match self.qp_off_type {
162 0 => Ok(0),
163 1 => {
164 let val = br.read_code(UintCodeType::Unary012)?;
165 if val != 2 {
166 Ok(val as i8)
167 } else {
168 Ok(-1)
169 }
170 },
171 _ => {
172 if br.read(1)? == 0 {
173 Ok(0)
174 } else {
175 let val = br.read(2)? as i8;
176 if (val & 2) == 0 {
177 Ok(val + 1)
178 } else {
179 Ok(-((val & 1) + 1))
180 }
181 }
182 },
183 }
184 }
185 fn get_width_cu(&self) -> usize {
186 (self.width + 63) >> 6
187 }
188 fn get_height_cu(&self) -> usize {
189 (self.height + 63) >> 6
190 }
191 fn has_top_block(&self, xpos: usize, ypos: usize, dx: usize, dy: usize, size: usize) -> bool {
192 if (ypos + dy) == 0 { return false; }
193 let xpos2 = xpos + dx;
194 if (xpos2 + size) > self.awidth { return false; }
195 true
196 }
197 fn has_top_right_block(&self, xpos: usize, ypos: usize, dx: usize, dy: usize, size: usize) -> bool {
198 if (ypos + dy) == 0 { return false; }
199 let xpos2 = xpos + dx;
200 if (xpos2 + size * 2) > self.awidth { return false; }
201 let cxpos = ((xpos + dx) & 63) >> RV60_BLOCK_LOG2[size];
202 let cypos = ((ypos + dy) & 63) >> RV60_BLOCK_LOG2[size];
203 ((cypos as u8) & RV60_AVAIL_MASK[cxpos]) == 0
204 }
205 fn has_top_right_block_mv(&self, xpos: usize, ypos: usize, dx: usize, dy: usize, size: usize) -> bool {
206 // the original Peter's proposed solution was
207 // (ypos + dy > 0) && (xpos + dx + size + 8) <= self.awidth
208 // but this should work too
209 if (ypos + dy) == 0 { return false; }
210 let xpos2 = xpos + dx;
211 if (xpos2 + size) >= self.awidth { return false; }
212 let cxpos = ((xpos + dx) & 63) >> RV60_BLOCK_LOG2[size];
213 let cypos = ((ypos + dy) & 63) >> RV60_BLOCK_LOG2[size];
214 ((cypos as u8) & RV60_AVAIL_MASK[cxpos]) == 0
215 }
216 fn has_left_block(&self, xpos: usize, ypos: usize, dx: usize, dy: usize, size: usize) -> bool {
217 if (xpos + dx) == 0 { return false; }
218 let ypos2 = ypos + dy;
219 if (ypos2 + size) > self.aheight { return false; }
220 true
221 }
222 fn has_left_down_block(&self, xpos: usize, ypos: usize, dx: usize, dy: usize, size: usize) -> bool {
223 if (xpos + dx) == 0 { return false; }
224 let ypos2 = ypos + dy;
225 if (ypos2 + size * 2) > self.aheight { return false; }
226 let cxpos = (!(xpos + dx) & 63) >> RV60_BLOCK_LOG2[size];
227 let cypos = (!(ypos + dy) & 63) >> RV60_BLOCK_LOG2[size];
228 ((cypos as u8) & RV60_AVAIL_MASK[cxpos]) >= 1
229 }
230 fn has_left_down_block_mv(&self, xpos: usize, ypos: usize, dx: usize, dy: usize, size: usize) -> bool {
231 // the original Peter's proposed solution was
232 // (xpos + dx > 0) && (ypos + dy + size + 8) <= self.aheight
233 // but this should work too
234 if (xpos + dx) == 0 { return false; }
235 let ypos2 = ypos + dy;
236 if (ypos2 + size) >= self.aheight { return false; }
237 let cxpos = (!(xpos + dx) & 63) >> RV60_BLOCK_LOG2[size];
238 let cypos = (!(ypos + dy) & 63) >> RV60_BLOCK_LOG2[size];
239 ((cypos as u8) & RV60_AVAIL_MASK[cxpos]) >= 1
240 }
241 }
242
243 const RV60_BLOCK_LOG2: [u8; 65] = [
244 0,
245 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4,
246 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
247 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
248 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6
249 ];
250 const RV60_AVAIL_MASK: [u8; 64] = [
251 0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 3, 0, 1, 0, 0xF,
252 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
253 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
254 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
255 ];
256
257 #[derive(Clone,Copy,PartialEq,Debug,Default)]
258 enum CUType {
259 #[default]
260 Intra,
261 InterMV,
262 Skip,
263 InterNoMV,
264 }
265
266 const RV60_CU_TYPES: [CUType; 4] = [ CUType::Intra, CUType::InterMV, CUType::Skip, CUType::InterNoMV ];
267
268 #[derive(Clone,Copy,PartialEq,Debug,Default)]
269 enum PUType {
270 #[default]
271 Full,
272 N2Hor,
273 N2Ver,
274 Quarters,
275 N4Hor,
276 N34Hor,
277 N4Ver,
278 N34Ver,
279 }
280
281 const RV60_PU_TYPES: [PUType; 8] = [
282 PUType::Full, PUType::N2Hor, PUType::N2Ver, PUType::Quarters,
283 PUType::N4Hor, PUType::N34Hor, PUType::N4Ver, PUType::N34Ver,
284 ];
285
286 impl PUType {
287 fn get_num_mvs(self) -> usize {
288 match self {
289 PUType::Full => 1,
290 PUType::Quarters => 4,
291 _ => 2,
292 }
293 }
294 fn get_mv_size(self, part_no: usize, size: usize) -> (usize, usize) {
295 let mv_size = size >> 2;
296 match self {
297 PUType::Full => (mv_size, mv_size),
298 PUType::N2Hor => (mv_size, mv_size >> 1),
299 PUType::N2Ver => (mv_size >> 1, mv_size),
300 PUType::Quarters => (mv_size >> 1, mv_size >> 1),
301 PUType::N4Hor => {
302 if part_no == 0 {
303 (mv_size, mv_size >> 2)
304 } else {
305 (mv_size, (3 * mv_size) >> 2)
306 }
307 },
308 PUType::N34Hor => {
309 if part_no == 0 {
310 (mv_size, (3 * mv_size) >> 2)
311 } else {
312 (mv_size, mv_size >> 2)
313 }
314 },
315 PUType::N4Ver => {
316 if part_no == 0 {
317 ( mv_size >> 2, mv_size)
318 } else {
319 ((3 * mv_size) >> 2, mv_size)
320 }
321 },
322 PUType::N34Ver => {
323 if part_no == 0 {
324 ((3 * mv_size) >> 2, mv_size)
325 } else {
326 ( mv_size >> 2, mv_size)
327 }
328 },
329 }
330 }
331 fn has_hor_split(self) -> bool {
332 matches!(self, PUType::N2Hor | PUType::N4Hor | PUType::N34Hor | PUType::Quarters)
333 }
334 fn has_ver_split(self) -> bool {
335 matches!(self, PUType::N2Ver | PUType::N4Ver | PUType::N34Ver | PUType::Quarters)
336 }
337 }
338
339 #[derive(Clone,Copy,Debug)]
340 enum IntraMode {
341 Index(u8),
342 Mode(u8),
343 DC64,
344 Plane64,
345 }
346
347 #[derive(Clone,Copy,PartialEq,Debug,Default)]
348 enum TransformType {
349 #[default]
350 None,
351 T4X4,
352 T8X8,
353 T16X16,
354 }
355
356 #[derive(Clone,Copy,PartialEq,Debug)]
357 enum MVRef {
358 None,
359 Ref0,
360 Ref1,
361 BRef,
362 Ref0AndBRef,
363 Skip0,
364 Skip1,
365 Skip2,
366 Skip3,
367 }
368
369 const SKIP_MV_REF: [MVRef; 4] = [ MVRef::Skip0, MVRef::Skip1, MVRef::Skip2, MVRef::Skip3 ];
370
371 impl MVRef {
372 fn get_skip_mv_num(self) -> usize {
373 match self {
374 MVRef::Skip1 => 1,
375 MVRef::Skip2 => 2,
376 MVRef::Skip3 => 3,
377 _ => 0,
378 }
379 }
380 fn is_ref0(self) -> bool {
381 matches!(self, MVRef::Ref0 | MVRef::Ref0AndBRef)
382 }
383 fn is_fwd(self) -> bool {
384 matches!(self, MVRef::Ref0 | MVRef::Ref1 | MVRef::Ref0AndBRef)
385 }
386 fn is_bwd(self) -> bool {
387 matches!(self, MVRef::BRef | MVRef::Ref0AndBRef)
388 }
389 }
390
391 #[derive(Clone,Copy,PartialEq,Debug)]
392 struct MVInfo {
393 f_mv: MV,
394 b_mv: MV,
395 mvref: MVRef,
396 }
397
398 impl MVInfo {
399 fn is_some(&self) -> bool { self.mvref != MVRef::None }
400 fn matches_fwd(&self, mvref: MVRef) -> bool {
401 (self.mvref == mvref) || (self.mvref.is_ref0() && mvref.is_ref0())
402 }
403 fn matches_bwd(&self, mvref: MVRef) -> bool {
404 self.mvref.is_bwd() && mvref.is_bwd()
405 }
406 fn is_deblock_cand(&self, other: &MVInfo) -> bool {
407 if self.mvref != other.mvref { return true; }
408 let mut mvdiff = 0;
409 if self.mvref.is_fwd() {
410 let diff = self.f_mv - other.f_mv;
411 mvdiff += diff.x.abs() + diff.y.abs();
412 }
413 if self.mvref.is_bwd() {
414 let diff = self.b_mv - other.b_mv;
415 mvdiff += diff.x.abs() + diff.y.abs();
416 }
417 mvdiff > 4
418 }
419 }
420
421 impl Default for MVInfo {
422 fn default() -> Self { Self { f_mv: ZERO_MV, b_mv: ZERO_MV, mvref: MVRef::None } }
423 }
424
425 #[derive(Clone,Copy,Debug)]
426 struct CBHeader {
427 cu_type: CUType,
428 pu_type: PUType,
429 ttype: TransformType,
430 imode: [IntraMode; 4],
431 mv: [MVInfo; 4],
432 }
433
434 impl CBHeader {
435 #[allow(clippy::collapsible_else_if)]
436 fn read(br: &mut BitReader, ftype: FrameType, two_f_refs: bool, size: usize) -> DecoderResult<Self> {
437 let pu_type;
438 let mut imode: [IntraMode; 4] = [IntraMode::Index(0); 4];
439 let mut mv: [MVInfo; 4] = [MVInfo::default(); 4];
440 let cu_type = if ftype == FrameType::I {
441 CUType::Intra
442 } else {
443 RV60_CU_TYPES[br.read(2)? as usize]
444 };
445 match cu_type {
446 CUType::Intra => {
447 if (size == 8) && br.read_bool()? {
448 pu_type = PUType::Quarters;
449 } else {
450 pu_type = PUType::Full;
451 }
452 if pu_type == PUType::Quarters {
453 for imode in imode.iter_mut() {
454 *imode = CBHeader::read_intra_mode(br)?;
455 }
456 } else if size <= 32 {
457 imode[0] = CBHeader::read_intra_mode(br)?;
458 } else {
459 if !br.read_bool()? {
460 imode[0] = IntraMode::DC64;
461 } else {
462 imode[0] = IntraMode::Plane64;
463 }
464 }
465 },
466 CUType::InterMV => {
467 let bits = if size == 8 { 2 } else { 3 };
468 pu_type = RV60_PU_TYPES[br.read(bits)? as usize];
469 CBHeader::read_mv_data(br, ftype, two_f_refs, size, pu_type, &mut mv)?;
470 },
471 _ => {
472 pu_type = PUType::Full;
473 let skip_mv_no = br.read_code(UintCodeType::LimitedUnary(3, 0))?;
474 mv[0].mvref = SKIP_MV_REF[skip_mv_no as usize];
475 },
476 };
477 let ttype;
478 if cu_type == CUType::Skip {
479 ttype = TransformType::None;
480 } else if size >= 32 {
481 ttype = TransformType::T16X16;
482 } else if size == 16 {
483 if (cu_type == CUType::Intra) || (pu_type == PUType::Full) {
484 ttype = TransformType::T16X16;
485 } else {
486 ttype = TransformType::T4X4;
487 }
488 } else {
489 if pu_type == PUType::Full {
490 ttype = TransformType::T8X8;
491 } else {
492 ttype = TransformType::T4X4;
493 }
494 }
495 Ok(Self {
496 cu_type, pu_type, ttype, imode, mv,
497 })
498 }
499 fn read_intra_mode(br: &mut BitReader) -> DecoderResult<IntraMode> {
500 if br.read_bool()? {
501 let idx = br.read_code(UintCodeType::Unary012)? as u8;
502 Ok(IntraMode::Index(idx))
503 } else {
504 let mode = br.read(5)? as u8;
505 Ok(IntraMode::Mode(mode))
506 }
507 }
508 fn read_mv_data(br: &mut BitReader, ftype: FrameType, two_f_refs: bool, size: usize, pu_type: PUType, mv: &mut [MVInfo; 4]) -> DecoderResult<()> {
509 let mv_count = pu_type.get_num_mvs();
510 for mv in mv[..mv_count].iter_mut() {
511 *mv = CBHeader::read_mv_info(br, ftype, two_f_refs, size, pu_type)?;
512 }
513 Ok(())
514 }
515 fn read_mv_info(br: &mut BitReader, ftype: FrameType, two_f_refs: bool, size: usize, pu_type: PUType) -> DecoderResult<MVInfo> {
516 let mut f_mv = ZERO_MV;
517 let mut b_mv = ZERO_MV;
518 let mvref;
519 if ftype != FrameType::B {
520 if two_f_refs && br.read_bool()? {
521 mvref = MVRef::Ref1;
522 } else {
523 mvref = MVRef::Ref0;
524 }
525 f_mv = CBHeader::read_mv(br)?;
526 Ok(MVInfo { f_mv, b_mv: ZERO_MV, mvref })
527 } else {
528 if ((size <= 8) && ((size != 8) || (pu_type != PUType::Full))) || br.read_bool()? {
529 if !br.read_bool()? {
530 mvref = MVRef::Ref0;
531 f_mv = CBHeader::read_mv(br)?;
532 } else {
533 mvref = MVRef::BRef;
534 b_mv = CBHeader::read_mv(br)?;
535 }
536 } else {
537 mvref = MVRef::Ref0AndBRef;
538 f_mv = CBHeader::read_mv(br)?;
539 b_mv = CBHeader::read_mv(br)?;
540 }
541 Ok(MVInfo { f_mv, b_mv, mvref })
542 }
543 }
544 fn read_mv(br: &mut BitReader) -> DecoderResult<MV> {
545 let x = br.read_code_signed(IntCodeType::Gamma)? as i16;
546 let y = br.read_code_signed(IntCodeType::Gamma)? as i16;
547 Ok(MV { x, y })
548 }
549 }
550
551 #[derive(Clone,Copy,Default)]
552 struct PUInfo {
553 cu_type: CUType,
554 ttype: TransformType,
555 pu_type: PUType,
556 }
557
558 impl PUInfo {
559 fn is_intra(self) -> bool { self.cu_type == CUType::Intra }
560 }
561
562 const RV60_CANDIDATE_INTRA_ANGLES: [u8; 6] = [ 0, 1, 10, 26, 18, 2 ];
563
564 #[derive(Clone,Copy,Default)]
565 struct BlockInfo {
566 mv: MVInfo,
567 imode: u8,
568 }
569
570 struct DeblockInfo {
571 left_str: Vec<u8>,
572 top_str: Vec<u8>,
573 stride: usize,
574 }
575
576 impl DeblockInfo {
577 fn new() -> Self {
578 Self { left_str: Vec::new(), top_str: Vec::new(), stride: 0 }
579 }
580 fn reinit(&mut self, w: usize, h: usize) {
581 self.left_str.clear();
582 self.top_str.clear();
583 self.stride = w >> 2;
584 let size = self.stride * (h >> 2);
585 self.left_str.resize(size, 0);
586 self.top_str.resize(size, 0);
587 }
588 fn set_strength(&mut self, xpos: usize, ypos: usize, size: usize, q: u8, strength: u8) {
589 let pos = self.get_pos(xpos, ypos);
590 let dsize = size >> 2;
591 let dval = (q << 2) | strength;
592 for x in 0..dsize {
593 self.top_str[pos + x] = dval;
594 self.top_str[pos + (dsize - 1) * self.stride + x] = dval;
595 }
596 for y in 0..dsize {
597 self.left_str[pos + y * self.stride] = dval;
598 self.left_str[pos + y * self.stride + dsize - 1] = dval;
599 }
600 }
601 fn get_pos(&self, xpos: usize, ypos: usize) -> usize {
602 (xpos >> 2) + (ypos >> 2) * self.stride
603 }
604 fn get_top_strength(&self, pos: usize) -> u8 {
605 self.top_str[pos] & 3
606 }
607 fn get_left_strength(&self, pos: usize) -> u8 {
608 self.left_str[pos] & 3
609 }
610 fn set_top_strength(&mut self, pos: usize, strength: u8) {
611 self.top_str[pos] |= strength;
612 }
613 fn set_left_strength(&mut self, pos: usize, strength: u8) {
614 self.left_str[pos] |= strength;
615 }
616 }
617
618 struct RealVideo60Decoder {
619 info: NACodecInfoRef,
620 cbs: RV60Codebooks,
621 ipbs: IPBShuffler,
622 dsp: RV60DSP,
623 ipred: IntraPredContext,
624 skip_mode: FrameSkipMode,
625
626 avg_buf: NAVideoBufferRef<u8>,
627
628 y_coeffs: [i16; 16 * 16],
629 u_coeffs: [i16; 8 * 8],
630 v_coeffs: [i16; 8 * 8],
631 qp: u8,
632 sel_qp: u8,
633
634 cu_splits: Vec<bool>,
635 coded_blk: [bool; 64],
636 dblk: DeblockInfo,
637
638 pu_info: Vec<PUInfo>,
639 pu_stride: usize,
640 pu_pos: usize,
641
642 blk_info: Vec<BlockInfo>,
643 blk_stride: usize,
644 blk_pos: usize,
645
646 xpos: usize,
647 ypos: usize,
648
649 ts_scale: u64,
650 ref0_pts: u64,
651 ref1_pts: u64,
652 ref0_ts: u64,
653 ref1_ts: u64,
654 }
655
656 impl RealVideo60Decoder {
657 fn new() -> Self {
658 let tmp_vinfo = NAVideoInfo::new(64, 64, false, YUV420_FORMAT);
659 let vt = alloc_video_buffer(tmp_vinfo, 4).unwrap();
660 let vb = vt.get_vbuf();
661 let avg_buf = vb.unwrap();
662 RealVideo60Decoder{
663 info: NACodecInfoRef::default(),
664 cbs: RV60Codebooks::init(),
665 ipbs: IPBShuffler::new(),
666 ipred: IntraPredContext::new(),
667 skip_mode: FrameSkipMode::default(),
668 dsp: RV60DSP::new(),
669 avg_buf,
670 y_coeffs: [0; 16 * 16],
671 u_coeffs: [0; 8 * 8],
672 v_coeffs: [0; 8 * 8],
673 qp: 0,
674 sel_qp: 0,
675 cu_splits: Vec::with_capacity(24),
676 coded_blk: [false; 64],
677 dblk: DeblockInfo::new(),
678 pu_info: Vec::new(),
679 pu_stride: 0,
680 pu_pos: 0,
681 blk_info: Vec::new(),
682 blk_stride: 0,
683 blk_pos: 0,
684 xpos: 0,
685 ypos: 0,
686
687 ts_scale: 1,
688 ref0_pts: 0,
689 ref1_pts: 0,
690 ref0_ts: 0,
691 ref1_ts: 0,
692 }
693 }
694 fn decode_cu_line(&mut self, buf: &mut NASimpleVideoFrame<u8>, hdr: &FrameHeader, src: &[u8], cu_y: usize) -> DecoderResult<()> {
695 let mut br = BitReader::new(src, BitReaderMode::BE);
696 let cu_w = hdr.get_width_cu();
697 for cu_x in 0..cu_w {
698 let dqp = hdr.read_line_qp_offset(&mut br)?;
699 let qps = (hdr.qp as i8) + dqp;
700 validate!((0..32).contains(&qps));
701 let qp = qps as u8;
702 self.qp = qp;
703 self.sel_qp = match hdr.osvquant {
704 0 => qp,
705 1 => {
706 if qp <= 25 {
707 qp + 5
708 } else {
709 qp
710 }
711 },
712 _ => {
713 if qp <= 18 {
714 qp + 10
715 } else if qp <= 25 {
716 qp + 5
717 } else {
718 qp
719 }
720 },
721 };
722
723 self.cu_splits.clear();
724 self.coded_blk = [false; 64];
725 self.decode_cb_tree(buf, hdr, &mut br, cu_x << 6, cu_y << 6, 6)?;
726 if hdr.deblock {
727 self.cu_splits.reverse();
728 self.deblock_cb_tree(buf, hdr, cu_x << 6, cu_y << 6, 6);
729 }
730 }
731 if br.left() >= 8 {
732 println!(" left {} bits", br.left());
733 }
734 Ok(())
735 }
736 #[allow(clippy::cognitive_complexity)]
737 #[allow(clippy::collapsible_else_if)]
738 #[allow(clippy::identity_op)]
739 fn decode_cb_tree(&mut self, buf: &mut NASimpleVideoFrame<u8>, hdr: &FrameHeader, br: &mut BitReader, xpos: usize, ypos: usize, log_size: u8) -> DecoderResult<()> {
740 if (xpos >= hdr.awidth) || (ypos >= hdr.aheight) { return Ok(()); }
741
742 let size = 1 << log_size;
743 let split = (xpos + size > hdr.awidth) || (ypos + size > hdr.aheight) || (size > 8 && br.read_bool()?);
744 self.cu_splits.push(split);
745 if split {
746 let hsize = size >> 1;
747 self.decode_cb_tree(buf, hdr, br, xpos, ypos, log_size - 1)?;
748 self.decode_cb_tree(buf, hdr, br, xpos + hsize, ypos, log_size - 1)?;
749 self.decode_cb_tree(buf, hdr, br, xpos, ypos + hsize, log_size - 1)?;
750 self.decode_cb_tree(buf, hdr, br, xpos + hsize, ypos + hsize, log_size - 1)?;
751 } else {
752 let cbh = CBHeader::read(br, hdr.ftype, hdr.two_f_refs, size)?;
753 self.pu_pos = (xpos >> 3) + (ypos >> 3) * self.pu_stride;
754 self.blk_pos = (xpos >> 2) + (ypos >> 2) * self.blk_stride;
755 self.xpos = xpos;
756 self.ypos = ypos;
757 self.reconstruct_info(hdr, &cbh, size)?;
758
759 let split_i4x4 = (cbh.cu_type == CUType::Intra) && (size == 8) && (cbh.pu_type == PUType::Quarters);
760 match cbh.cu_type {
761 CUType::Intra => {
762 let itype = self.blk_info[self.blk_pos].imode;
763 if !split_i4x4 {
764 let dstride = buf.stride[0];
765 let off = xpos + ypos * dstride;
766 let dst = &mut buf.data;
767 self.populate_ipred(hdr, dst, 0, dstride, 0, 0, size, true);
768 self.ipred.pred_angle(dst, off, dstride, size, itype as usize, true);
769 }
770 for comp in 1..3 {
771 let dstride = buf.stride[comp];
772 let soff = buf.offset[comp];
773 let off = soff + (xpos >> 1) + (ypos >> 1) * dstride;
774 let dst = &mut buf.data;
775 self.populate_ipred(hdr, dst, soff, dstride, 0, 0, size >> 1, false);
776 self.ipred.pred_angle(dst, off, dstride, size >> 1, itype as usize, false);
777 }
778 },
779 _ => {
780 let mut mv_x = xpos >> 2;
781 let mut mv_y = ypos >> 2;
782 let mut mv_pos = mv_x + mv_y * self.blk_stride;
783 for part_no in 0..cbh.pu_type.get_num_mvs() {
784 let (mv_w, mv_h) = cbh.pu_type.get_mv_size(part_no, size);
785 let mv = self.blk_info[mv_pos].mv;
786 let bw = mv_w << 2;
787 let bh = mv_h << 2;
788 let bx = mv_x << 2;
789 let by = mv_y << 2;
790 match mv.mvref {
791 MVRef::Ref0 => {
792 if hdr.ftype != FrameType::B {
793 if let Some(ref prevbuf) = self.ipbs.get_lastref() {
794 self.dsp.do_mc(buf, prevbuf, bx, by, bw, bh, mv.f_mv, false);
795 }
796 } else {
797 if let Some(ref prevbuf) = self.ipbs.get_b_fwdref() {
798 self.dsp.do_mc(buf, prevbuf, bx, by, bw, bh, mv.f_mv, false);
799 }
800 }
801 },
802 MVRef::Ref1 => {
803 if let Some(ref prevbuf) = self.ipbs.get_nextref() {
804 self.dsp.do_mc(buf, prevbuf, bx, by, bw, bh, mv.f_mv, false);
805 }
806 },
807 MVRef::BRef => {
808 validate!(hdr.ftype == FrameType::B);
809 if let Some(ref prevbuf) = self.ipbs.get_b_bwdref() {
810 self.dsp.do_mc(buf, prevbuf, bx, by, bw, bh, mv.b_mv, false);
811 }
812 },
813 MVRef::Ref0AndBRef => {
814 validate!(hdr.ftype == FrameType::B);
815 if let (Some(ref prevbuf), Some(ref nextbuf)) = (self.ipbs.get_b_fwdref(), self.ipbs.get_b_bwdref()) {
816 self.dsp.do_mc(buf, prevbuf, bx, by, bw, bh, mv.f_mv, false);
817 {
818 let mut avg_buf = NASimpleVideoFrame::from_video_buf(&mut self.avg_buf).unwrap();
819 self.dsp.do_mc(&mut avg_buf, nextbuf, bx, by, bw, bh, mv.b_mv, true);
820 }
821 self.dsp.do_avg(buf, &self.avg_buf, bx, by, bw, bh);
822 }
823 },
824 _ => unreachable!(),
825 };
826 if cbh.pu_type == PUType::Quarters {
827 if part_no != 1 {
828 mv_pos += mv_w;
829 mv_x += mv_w;
830 } else {
831 mv_pos += mv_h * self.blk_stride - mv_w;
832 mv_x -= mv_w;
833 mv_y += mv_h;
834 }
835 } else if cbh.pu_type.has_hor_split() {
836 mv_pos += mv_h * self.blk_stride;
837 mv_y += mv_h;
838 } else if cbh.pu_type.has_ver_split() {
839 mv_pos += mv_w;
840 mv_x += mv_w;
841 }
842 }
843 },
844 };
845 if cbh.ttype != TransformType::None {
846 self.y_coeffs = [0; 16 * 16];
847 self.u_coeffs = [0; 8 * 8];
848 self.v_coeffs = [0; 8 * 8];
849 }
850 let is_intra = cbh.cu_type == CUType::Intra;
851 let cb_pos = ((xpos & 63) >> 3) + ((ypos & 63) >> 3) * 8;
852 match cbh.ttype {
853 TransformType::T4X4 => {
854 let subset = if is_intra { 0 } else { 2 };
855 if size == 16 {
856 let cbp16 = if br.read_bool()? {
857 rv6_decode_cbp16(br, &self.cbs, subset, self.sel_qp)?
858 } else { 0 };
859 if cbp16 != 0 {
860 rv6_decode_cu_4x4in16x16(br, &self.cbs, is_intra, self.qp, self.sel_qp, &mut self.y_coeffs, &mut self.u_coeffs, &mut self.v_coeffs, cbp16)?;
861 for y in 0..4 {
862 for x in 0..4 {
863 let i = x + y * 4;
864 if ((cbp16 >> i) & 1) != 0 {
865 self.dsp.transform4x4(&mut self.y_coeffs[i * 16..][..16]);
866 let dstride = buf.stride[0];
867 let off = xpos + x * 4 + (ypos + y * 4) * dstride;
868 let dst = &mut buf.data;
869 self.dsp.add_block(dst, off, dstride, &self.y_coeffs[i*16..][..16], 4);
870 self.coded_blk[cb_pos + (y / 2) * 8 + (x / 2)] = true;
871 }
872 }
873 }
874 for y in 0..2 {
875 for x in 0..2 {
876 let i = x + y * 2;
877 let xoff = (xpos >> 1) + x * 4;
878 let yoff = (ypos >> 1) + y * 4;
879 if ((cbp16 >> (16 + i)) & 1) != 0 {
880 self.dsp.transform4x4(&mut self.u_coeffs[i * 16..][..16]);
881 let dstride = buf.stride[1];
882 let off = buf.offset[1] + xoff + yoff * dstride;
883 let dst = &mut buf.data;
884 self.dsp.add_block(dst, off, dstride, &self.u_coeffs[i * 16..][..16], 4);
885 self.coded_blk[cb_pos + y * 8 + x] = true;
886 }
887 if ((cbp16 >> (20 + i)) & 1) != 0 {
888 self.dsp.transform4x4(&mut self.v_coeffs[i * 16..][..16]);
889 let dstride = buf.stride[2];
890 let off = buf.offset[2] + xoff + yoff * dstride;
891 let dst = &mut buf.data;
892 self.dsp.add_block(dst, off, dstride, &self.v_coeffs[i * 16..][..16], 4);
893 self.coded_blk[cb_pos + y * 8 + x] = true;
894 }
895 }
896 }
897 }
898 } else {
899 let cbp8 = rv6_decode_cbp8(br, &self.cbs, subset, self.sel_qp)?;
900 if cbp8 != 0 {
901 self.coded_blk[cb_pos] = true;
902 rv6_decode_cu_8x8(br, &self.cbs, is_intra, self.qp, self.sel_qp, &mut self.y_coeffs, &mut self.u_coeffs, &mut self.v_coeffs, cbp8, true)?;
903 }
904 for i in 0..4 {
905 let xoff = (i & 1) * 4;
906 let yoff = (i & 2) * 2;
907 if split_i4x4 {
908 let dstride = buf.stride[0];
909 let off = xpos + xoff + (ypos + yoff) * dstride;
910 let dst = &mut buf.data;
911 self.populate_ipred(hdr, dst, 0, dstride, xoff, yoff, 4, true);
912 let itype = self.blk_info[self.blk_pos + (i & 1) + (i >> 1) * self.blk_stride].imode;
913 self.ipred.pred_angle(dst, off, dstride, 4, itype as usize, true);
914 }
915 if ((cbp8 >> i) & 1) != 0 {
916 let blk = &mut self.y_coeffs[i * 16..][..16];
917 self.dsp.transform4x4(blk);
918 let dstride = buf.stride[0];
919 let soff = buf.offset[0];
920 let off = soff + xpos + xoff + (ypos + yoff) * dstride;
921 self.dsp.add_block(buf.data, off, dstride, blk, 4);
922 }
923 }
924 if ((cbp8 >> 4) & 1) != 0 {
925 self.dsp.transform4x4(&mut self.u_coeffs);
926 let dstride = buf.stride[1];
927 let soff = buf.offset[1];
928 let off = soff + (xpos >> 1) + (ypos >> 1) * dstride;
929 self.dsp.add_block(buf.data, off, dstride, &self.u_coeffs, 4);
930 }
931 if ((cbp8 >> 5) & 1) != 0 {
932 self.dsp.transform4x4(&mut self.v_coeffs);
933 let dstride = buf.stride[2];
934 let soff = buf.offset[2];
935 let off = soff + (xpos >> 1) + (ypos >> 1) * dstride;
936 self.dsp.add_block(buf.data, off, dstride, &self.v_coeffs, 4);
937 }
938 }
939 },
940 TransformType::T8X8 => {
941 let subset = if is_intra { 1 } else { 3 };
942 let cbp8 = rv6_decode_cbp8(br, &self.cbs, subset, self.sel_qp)?;
943 if cbp8 != 0 {
944 self.coded_blk[cb_pos] = true;
945 rv6_decode_cu_8x8(br, &self.cbs, is_intra, self.qp, self.sel_qp, &mut self.y_coeffs, &mut self.u_coeffs, &mut self.v_coeffs, cbp8, false)?;
946 if (cbp8 & 0xF) != 0 {
947 self.dsp.transform8x8(&mut self.y_coeffs);
948 let dstride = buf.stride[0];
949 let off = xpos + ypos * dstride;
950 self.dsp.add_block(buf.data, off, dstride, &self.y_coeffs, 8);
951 }
952 if ((cbp8 >> 4) & 1) != 0 {
953 self.dsp.transform4x4(&mut self.u_coeffs);
954 let dstride = buf.stride[1];
955 let soff = buf.offset[1];
956 let off = soff + (xpos >> 1) + (ypos >> 1) * dstride;
957 self.dsp.add_block(buf.data, off, dstride, &self.u_coeffs, 4);
958 }
959 if ((cbp8 >> 5) & 1) != 0 {
960 self.dsp.transform4x4(&mut self.v_coeffs);
961 let dstride = buf.stride[2];
962 let soff = buf.offset[2];
963 let off = soff + (xpos >> 1) + (ypos >> 1) * dstride;
964 self.dsp.add_block(buf.data, off, dstride, &self.v_coeffs, 4);
965 }
966 }
967 },
968 TransformType::T16X16 => {
969 let subset = if is_intra { 1 } else { 3 };
970 let num_clusters = size >> 4;
971 let cl_cbp = br.read((num_clusters * num_clusters) as u8)?;
972 for y in 0..num_clusters {
973 for x in 0..num_clusters {
974 if ((cl_cbp >> (x + y * num_clusters)) & 1) == 0 { continue; }
975 self.coded_blk[cb_pos + x * 2 + y * 2 * 8 + 0] = true;
976 self.coded_blk[cb_pos + x * 2 + y * 2 * 8 + 1] = true;
977 self.coded_blk[cb_pos + x * 2 + y * 2 * 8 + 8] = true;
978 self.coded_blk[cb_pos + x * 2 + y * 2 * 8 + 9] = true;
979 let super_cbp = rv6_decode_cbp16(br, &self.cbs, subset, self.sel_qp)?;
980 if super_cbp != 0 {
981 self.y_coeffs = [0; 16 * 16];
982 self.u_coeffs = [0; 8 * 8];
983 self.v_coeffs = [0; 8 * 8];
984 rv6_decode_cu_16x16(br, &self.cbs, is_intra, self.qp, self.sel_qp, &mut self.y_coeffs, &mut self.u_coeffs, &mut self.v_coeffs, super_cbp)?;
985 if (super_cbp & 0xFFFF) != 0 {
986 self.dsp.transform16x16(&mut self.y_coeffs);
987 let dstride = buf.stride[0];
988 let off = xpos + x * 16 + (ypos + y * 16) * dstride;
989 self.dsp.add_block(buf.data, off, dstride, &self.y_coeffs, 16);
990 }
991 if ((super_cbp >> 16) & 0xF) != 0 {
992 self.dsp.transform8x8(&mut self.u_coeffs);
993 let dstride = buf.stride[1];
994 let soff = buf.offset[1];
995 let off = soff + (xpos >> 1) + x * 8 + ((ypos >> 1) + y * 8) * dstride;
996 self.dsp.add_block(buf.data, off, dstride, &self.u_coeffs, 8);
997 }
998 if ((super_cbp >> 20) & 0xF) != 0 {
999 self.dsp.transform8x8(&mut self.v_coeffs);
1000 let dstride = buf.stride[2];
1001 let soff = buf.offset[2];
1002 let off = soff + (xpos >> 1) + x * 8 + ((ypos >> 1) + y * 8) * dstride;
1003 self.dsp.add_block(buf.data, off, dstride, &self.v_coeffs, 8);
1004 }
1005 }
1006 }
1007 }
1008 },
1009 _ => {},
1010 };
1011 }
1012 Ok(())
1013 }
1014 fn reconstruct_info(&mut self, hdr: &FrameHeader, cbh: &CBHeader, size: usize) -> DecoderResult<()>{
1015 let mut pui = PUInfo::default();
1016 let pu_size = size >> 3;
1017 pui.cu_type = cbh.cu_type;
1018 pui.ttype = cbh.ttype;
1019 pui.pu_type = cbh.pu_type;
1020 if (cbh.cu_type == CUType::Intra) && (cbh.pu_type == PUType::Quarters) { // very special case
1021 self.pu_info[self.pu_pos] = pui;
1022 for y in 0..2 {
1023 for x in 0..2 {
1024 let imode = self.reconstruct_intra(hdr, cbh, 4, x + y * 2);
1025 validate!(imode <= MAX_IMODE);
1026 self.blk_info[self.blk_pos + x + y * self.blk_stride].imode = imode;
1027 self.blk_info[self.blk_pos + x + y * self.blk_stride].mv = MVInfo::default();
1028 }
1029 }
1030 return Ok(());
1031 }
1032 match cbh.cu_type {
1033 CUType::Intra => {
1034 self.pu_info[self.pu_pos] = pui;
1035 let imode = self.reconstruct_intra(hdr, cbh, size, 0);
1036 validate!(imode <= MAX_IMODE);
1037 for y in 0..(size >> 2) {
1038 for x in 0..(size >> 2) {
1039 self.blk_info[self.blk_pos + x + y * self.blk_stride].imode = imode;
1040 }
1041 }
1042 },
1043 CUType::InterMV => {
1044 let mut mv_x = self.xpos >> 2;
1045 let mut mv_y = self.ypos >> 2;
1046 let mut mv_pos = self.blk_pos;
1047 let pu_type = cbh.pu_type;
1048 for part_no in 0..pu_type.get_num_mvs() {
1049 let (mv_w, mv_h) = pu_type.get_mv_size(part_no, size);
1050 let mv = self.predict_mv(hdr, mv_x, mv_y, mv_w, &cbh.mv[part_no]);
1051 for y in 0..mv_h {
1052 for x in 0..mv_w {
1053 self.blk_info[mv_pos + x + y * self.blk_stride].mv = mv;
1054 }
1055 }
1056 if pu_type == PUType::Quarters {
1057 if part_no != 1 {
1058 mv_pos += mv_w;
1059 mv_x += mv_w;
1060 } else {
1061 mv_pos += mv_h * self.blk_stride - mv_w;
1062 mv_x -= mv_w;
1063 mv_y += mv_h;
1064 }
1065 } else if pu_type.has_hor_split() {
1066 mv_pos += mv_h * self.blk_stride;
1067 mv_y += mv_h;
1068 } else if pu_type.has_ver_split() {
1069 mv_pos += mv_w;
1070 mv_x += mv_w;
1071 }
1072 }
1073 },
1074 _ => {
1075 let skip_idx = cbh.mv[0].mvref.get_skip_mv_num();
1076 let mut skip_cand: UniqueList<MVInfo> = UniqueList::new(4);
1077 self.fill_skip_cand(hdr, &mut skip_cand, size);
1078 let mv = skip_cand.list[skip_idx];
1079
1080 let mv_size = size >> 2;
1081 for y in 0..mv_size {
1082 for x in 0..mv_size {
1083 self.blk_info[self.blk_pos + x + y * self.blk_stride].mv = mv;
1084 }
1085 }
1086 },
1087 };
1088 for y in 0..pu_size {
1089 for x in 0..pu_size {
1090 self.pu_info[self.pu_pos + x + y * self.pu_stride] = pui;
1091 }
1092 }
1093 Ok(())
1094 }
1095 fn reconstruct_intra(&self, hdr: &FrameHeader, cbh: &CBHeader, size: usize, sub: usize) -> u8 {
1096 match cbh.imode[0] {
1097 IntraMode::DC64 => { return 1; },
1098 IntraMode::Plane64 => { return 0; },
1099 _ => {},
1100 };
1101 // form list of predictors
1102 let blk_pos = self.blk_pos + (sub & 1) + (sub >> 1) * self.blk_stride;
1103 let mut ipm_cand: UniqueList<u8> = UniqueList::new(3);
1104 if hdr.has_top_block(self.xpos, self.ypos, (sub & 1) * 4, 0, size) {
1105 let pu = &self.pu_info[self.pu_pos - self.pu_stride];
1106 if pu.is_intra() {
1107 ipm_cand.add(self.blk_info[self.blk_pos + (sub & 1) - self.blk_stride].imode);
1108 }
1109 }
1110 if hdr.has_left_block(self.xpos, self.ypos, 0, (sub & 2) * 2, size) {
1111 let pu = &self.pu_info[self.pu_pos - 1];
1112 if pu.is_intra() {
1113 ipm_cand.add(self.blk_info[blk_pos - 1 - (sub & 1)].imode);
1114 }
1115 }
1116 let tl_x = if (sub & 2) == 0 { self.xpos + (sub & 1) * 4 } else { self.xpos };
1117 let tl_y = self.ypos + (sub & 2) * 4;
1118 if (tl_x > 0) && (tl_y > 0) {
1119 let pu = match sub {
1120 0 => &self.pu_info[self.pu_pos - self.pu_stride - 1],
1121 1 => &self.pu_info[self.pu_pos - self.pu_stride],
1122 2 => &self.pu_info[self.pu_pos - 1],
1123 _ => &self.pu_info[self.pu_pos - 1],
1124 };
1125 if pu.is_intra() {
1126 if sub != 3 {
1127 ipm_cand.add(self.blk_info[blk_pos - self.blk_stride - 1].imode);
1128 } else {
1129 ipm_cand.add(self.blk_info[blk_pos - self.blk_stride - 2].imode);
1130 }
1131 }
1132 }
1133 for el in RV60_CANDIDATE_INTRA_ANGLES.iter() {
1134 ipm_cand.add(*el);
1135 }
1136 // actually decode prediction mode
1137 match cbh.imode[sub] {
1138 IntraMode::Index(idx) => {
1139 ipm_cand.list[idx as usize]
1140 },
1141 IntraMode::Mode(mode) => {
1142 let mut imode = mode;
1143 let mut ipm_cs: [u8; 3] = [ipm_cand.list[0], ipm_cand.list[1], ipm_cand.list[2]];
1144 ipm_cs.sort();
1145 for ic in ipm_cs.iter() {
1146 if imode >= *ic {
1147 imode += 1;
1148 }
1149 }
1150 imode
1151 },
1152 _ => unreachable!(),
1153 }
1154 }
1155 #[allow(clippy::too_many_arguments)]
1156 fn populate_ipred(&mut self, hdr: &FrameHeader, src: &[u8], soff: usize, stride: usize, xoff: usize, yoff: usize, size: usize, is_luma: bool) {
1157 let src_off = if is_luma {
1158 soff + self.xpos + xoff + (self.ypos + yoff) * stride
1159 } else {
1160 soff + (self.xpos >> 1) + (self.ypos >> 1) * stride
1161 };
1162 self.ipred = IntraPredContext::new();
1163 if (self.ypos + yoff) > 0 {
1164 self.ipred.has_t = true;
1165 for x in 0..size {
1166 self.ipred.t[x + 1] = src[src_off - stride + x];
1167 }
1168 if (is_luma && hdr.has_top_right_block(self.xpos, self.ypos, xoff, yoff, size)) ||
1169 (!is_luma && hdr.has_top_right_block(self.xpos, self.ypos, 0, 0, size << 1)) {
1170 self.ipred.has_tr = true;
1171 for x in size..size*2 {
1172 self.ipred.t[x + 1] = src[src_off - stride + x];
1173 }
1174 } else {
1175 for i in 0..size {
1176 self.ipred.t[size + i + 1] = self.ipred.t[size];
1177 }
1178 }
1179 if (self.xpos + xoff) > 0 {
1180 self.ipred.t[0] = src[src_off - stride - 1];
1181 }
1182 }
1183 if (self.xpos + xoff) > 0 {
1184 self.ipred.has_l = true;
1185 for y in 0..size {
1186 self.ipred.l[y + 1] = src[src_off - 1 + y * stride];
1187 }
1188 if (is_luma && hdr.has_left_down_block(self.xpos, self.ypos, xoff, yoff, size)) ||
1189 (!is_luma && hdr.has_left_down_block(self.xpos, self.ypos, 0, 0, size << 1)) {
1190 self.ipred.has_ld = true;
1191 for y in size..size*2 {
1192 self.ipred.l[y + 1] = src[src_off - 1 + y * stride];
1193 }
1194 } else {
1195 for i in 0..size {
1196 self.ipred.l[size + i + 1] = self.ipred.l[size];
1197 }
1198 }
1199 if (self.ypos + yoff) > 0 {
1200 self.ipred.l[0] = src[src_off - stride - 1];
1201 }
1202 }
1203 }
1204 fn predict_mv(&self, hdr: &FrameHeader, mv_x: usize, mv_y: usize, mv_w: usize, mvi: &MVInfo) -> MVInfo {
1205 let mv_pos = mv_x + mv_y * self.blk_stride;
1206 let f_mv: MV;
1207 let b_mv: MV;
1208 if mvi.mvref.is_fwd() {
1209 let mut mv_cand: [MV; 3] = [ZERO_MV; 3];
1210 let mut mv_cand_size: usize = 0;
1211 if mv_x > 0 {
1212 let ref_mv = &self.blk_info[mv_pos - 1].mv;
1213 if ref_mv.matches_fwd(mvi.mvref) {
1214 mv_cand[mv_cand_size] = ref_mv.f_mv;
1215 mv_cand_size += 1;
1216 }
1217 }
1218 if mv_y > 0 {
1219 let ref_mv = &self.blk_info[mv_pos - self.blk_stride].mv;
1220 if ref_mv.matches_fwd(mvi.mvref) {
1221 mv_cand[mv_cand_size] = ref_mv.f_mv;
1222 mv_cand_size += 1;
1223 }
1224 }
1225 if hdr.has_top_block(mv_x << 2, mv_y << 2, mv_w << 2, 0, 4) {
1226 let ref_mv = &self.blk_info[mv_pos - self.blk_stride + mv_w].mv;
1227 if ref_mv.matches_fwd(mvi.mvref) {
1228 mv_cand[mv_cand_size] = ref_mv.f_mv;
1229 mv_cand_size += 1;
1230 }
1231 }
1232 f_mv = match mv_cand_size {
1233 1 | 2 => {
1234 let x = mv_cand[0].x + mv_cand[1].x + mv_cand[2].x;
1235 let y = mv_cand[0].y + mv_cand[1].y + mv_cand[2].y;
1236 if mv_cand_size == 1 {
1237 MV { x, y }
1238 } else {
1239 MV { x: x >> 1, y: y >> 1 }
1240 }
1241 },
1242 3 => MV::pred(mv_cand[0], mv_cand[1], mv_cand[2]),
1243 _ => ZERO_MV,
1244 };
1245 } else {
1246 f_mv = ZERO_MV;
1247 }
1248 if mvi.mvref.is_bwd() {
1249 let mut mv_cand: [MV; 3] = [ZERO_MV; 3];
1250 let mut mv_cand_size: usize = 0;
1251 if mv_x > 0 {
1252 let ref_mv = &self.blk_info[mv_pos - 1].mv;
1253 if ref_mv.matches_bwd(mvi.mvref) {
1254 mv_cand[mv_cand_size] = ref_mv.b_mv;
1255 mv_cand_size += 1;
1256 }
1257 }
1258 if mv_y > 0 {
1259 let ref_mv = &self.blk_info[mv_pos - self.blk_stride].mv;
1260 if ref_mv.matches_bwd(mvi.mvref) {
1261 mv_cand[mv_cand_size] = ref_mv.b_mv;
1262 mv_cand_size += 1;
1263 }
1264 }
1265 if hdr.has_top_block(mv_x << 2, mv_y << 2, mv_w << 2, 0, 4) {
1266 let ref_mv = &self.blk_info[mv_pos - self.blk_stride + mv_w].mv;
1267 if ref_mv.matches_bwd(mvi.mvref) {
1268 mv_cand[mv_cand_size] = ref_mv.b_mv;
1269 mv_cand_size += 1;
1270 }
1271 }
1272 b_mv = match mv_cand_size {
1273 1 | 2 => {
1274 let x = mv_cand[0].x + mv_cand[1].x + mv_cand[2].x;
1275 let y = mv_cand[0].y + mv_cand[1].y + mv_cand[2].y;
1276 if mv_cand_size == 1 {
1277 MV { x, y }
1278 } else {
1279 MV { x: x >> 1, y: y >> 1 }
1280 }
1281 },
1282 3 => MV::pred(mv_cand[0], mv_cand[1], mv_cand[2]),
1283 _ => ZERO_MV,
1284 };
1285 } else {
1286 b_mv = ZERO_MV;
1287 }
1288
1289 MVInfo { f_mv: mvi.f_mv + f_mv, b_mv: mvi.b_mv + b_mv, mvref: mvi.mvref }
1290 }
1291 fn fill_skip_cand(&mut self, hdr: &FrameHeader, skip_cand: &mut UniqueList<MVInfo>, size: usize) {
1292 let mv_size = size >> 2;
1293
1294 if self.xpos > 0 {
1295 let mv = &self.blk_info[self.blk_pos - 1].mv;
1296 if mv.is_some() {
1297 skip_cand.add(*mv);
1298 }
1299 }
1300 if self.ypos > 0 {
1301 let mv = &self.blk_info[self.blk_pos - self.blk_stride].mv;
1302 if mv.is_some() {
1303 skip_cand.add(*mv);
1304 }
1305 }
1306 if hdr.has_top_right_block_mv(self.xpos, self.ypos, 0, 0, size) {
1307 let mv = &self.blk_info[self.blk_pos - self.blk_stride + mv_size].mv;
1308 if mv.is_some() {
1309 skip_cand.add(*mv);
1310 }
1311 }
1312 if hdr.has_left_down_block_mv(self.xpos, self.ypos, 0, 0, size) {
1313 let mv = &self.blk_info[self.blk_pos + self.blk_stride * mv_size - 1].mv;
1314 if mv.is_some() {
1315 skip_cand.add(*mv);
1316 }
1317 }
1318 if hdr.has_left_block(self.xpos, self.ypos, 0, 0, size) {
1319 let mv = &self.blk_info[self.blk_pos + self.blk_stride * (mv_size - 1) - 1].mv;
1320 if mv.is_some() {
1321 skip_cand.add(*mv);
1322 }
1323 }
1324 if hdr.has_top_block(self.xpos, self.ypos, 0, 0, size) {
1325 let mv = &self.blk_info[self.blk_pos - self.blk_stride + mv_size - 1].mv;
1326 if mv.is_some() {
1327 skip_cand.add(*mv);
1328 }
1329 }
1330 if (self.xpos > 0) && (self.ypos > 0) {
1331 let mv = &self.blk_info[self.blk_pos - self.blk_stride - 1].mv;
1332 if mv.is_some() {
1333 skip_cand.add(*mv);
1334 }
1335 }
1336 for i in skip_cand.fill..4 {
1337 skip_cand.list[i] = MVInfo { f_mv: ZERO_MV, b_mv: ZERO_MV, mvref: MVRef::Ref0 };
1338 }
1339 }
1340 fn calc_tile_size(&self, pu_pos: usize, cu_type: CUType, log_size: u8) -> u8 {
1341 match log_size {
1342 3 => 3,
1343 4 if (cu_type != CUType::Intra) && (self.pu_info[pu_pos].pu_type != PUType::Full) => 3,
1344 4 | 5 | 6 => 4,
1345 _ => unreachable!(),
1346 }
1347 }
1348 fn deblock_cb_tree(&mut self, buf: &mut NASimpleVideoFrame<u8>, hdr: &FrameHeader, xpos: usize, ypos: usize, log_size: u8) {
1349 if (xpos >= hdr.awidth) || (ypos >= hdr.aheight) { return; }
1350 let split = self.cu_splits.pop().unwrap();
1351 if split {
1352 let hsize = 1 << (log_size - 1);
1353 self.deblock_cb_tree(buf, hdr, xpos, ypos, log_size - 1);
1354 self.deblock_cb_tree(buf, hdr, xpos + hsize, ypos, log_size - 1);
1355 self.deblock_cb_tree(buf, hdr, xpos, ypos + hsize, log_size - 1);
1356 self.deblock_cb_tree(buf, hdr, xpos + hsize, ypos + hsize, log_size - 1);
1357 } else {
1358 let pu_pos = (xpos >> 3) + (ypos >> 3) * self.pu_stride;
1359 let cu_type = self.pu_info[pu_pos].cu_type;
1360 let tsize = self.calc_tile_size(pu_pos, cu_type, log_size);
1361 let ntiles = 1 << (log_size - tsize);
1362 let dparams = RV60DeblockParams {
1363 deblock_chroma: hdr.deblock_chroma,
1364 width: hdr.awidth,
1365 height: hdr.aheight,
1366 dblkstride: self.dblk.stride,
1367 };
1368 for ty in 0..ntiles {
1369 for tx in 0..ntiles {
1370 let x = xpos + (tx << tsize);
1371 let y = ypos + (ty << tsize);
1372 let cb_pos = ((x & 63) >> 3) + ((y & 63) >> 3) * 8;
1373 if cu_type == CUType::Intra {
1374 self.dblk.set_strength(x, y, 1 << tsize, self.qp, 2);
1375 } else if (cu_type != CUType::Skip) && self.coded_blk[cb_pos] {
1376 self.dblk.set_strength(x, y, 1 << tsize, self.qp, 1);
1377 } else {
1378 self.dblk.set_strength(x, y, 1 << tsize, self.qp, 0);
1379 self.derive_deblock_strength(x, y, 1 << (tsize - 2));
1380 }
1381 self.dsp.do_deblock(&dparams, buf, x, y, 1 << tsize,
1382 self.dblk.top_str.as_slice(),
1383 self.dblk.left_str.as_slice(),
1384 self.dblk.get_pos(x, y));
1385 }
1386 }
1387 }
1388 }
1389 #[allow(clippy::collapsible_if)]
1390 fn derive_deblock_strength(&mut self, xpos: usize, ypos: usize, size4: usize) {
1391 let blk_pos = (xpos >> 2) + (ypos >> 2) * self.blk_stride;
1392 let mut dblk_pos = self.dblk.get_pos(xpos, ypos);
1393 if ypos > 0 {
1394 let top_blk_pos = blk_pos - self.blk_stride;
1395 for i in 0..size4 {
1396 if self.dblk.get_top_strength(dblk_pos - self.dblk.stride + i) == 0 {
1397 if self.blk_info[blk_pos + i].mv.is_deblock_cand(&self.blk_info[top_blk_pos + i].mv) {
1398 self.dblk.set_top_strength(dblk_pos + i, 1);
1399 }
1400 }
1401 }
1402 }
1403 if xpos > 0 {
1404 for i in 0..size4 {
1405 if self.dblk.get_left_strength(dblk_pos - 1) == 0 {
1406 if self.blk_info[blk_pos + i * self.blk_stride].mv.is_deblock_cand(&self.blk_info[blk_pos + i * self.blk_stride - 1].mv) {
1407 self.dblk.set_left_strength(dblk_pos, 1);
1408 }
1409 }
1410 dblk_pos += self.dblk.stride;
1411 }
1412 }
1413 }
1414 }
1415
1416 impl NADecoder for RealVideo60Decoder {
1417 fn init(&mut self, supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
1418 if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
1419 let fmt = YUV420_FORMAT;
1420 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, fmt));
1421 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
1422
1423 let edata = info.get_extradata().unwrap();
1424 let src: &[u8] = &edata;
1425
1426 if src.len() < 8 { return Err(DecoderError::InvalidData); }
1427 let mut mr = MemoryReader::new_read(src);
1428 let mut br = ByteReader::new(&mut mr);
1429 let _flags = br.read_u32be()?;
1430 let version = br.read_u32be()?;
1431 let _unk = br.read_u16be()?;
1432 validate!((version >> 28) == 4);
1433 // then width and height again as 16be
1434
1435 //self.bd.width = vinfo.get_width();
1436 //self.bd.height = vinfo.get_height();
1437 //self.frmmgr.clear();
1438
1439 supp.pool_u8.set_dec_bufs(3);
1440 supp.pool_u8.prealloc_video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, fmt), 6)?;
1441
1442 Ok(())
1443 } else {
1444 Err(DecoderError::InvalidData)
1445 }
1446 }
1447 fn decode(&mut self, supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
1448 let src = pkt.get_buffer();
1449
1450 validate!(src.len() > 9);
1451 let hsize = (src[0] as usize) * 8 + 9;
1452 let mut br = BitReader::new(&src[hsize..], BitReaderMode::BE);
1453 let hdr = FrameHeader::read(&mut br)?;
1454 match self.skip_mode {
1455 FrameSkipMode::None => {},
1456 FrameSkipMode::KeyframesOnly => {
1457 if hdr.ftype == FrameType::B {
1458 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::None);
1459 frm.set_frame_type(FrameType::Skip);
1460 return Ok(frm.into_ref());
1461 }
1462 },
1463 FrameSkipMode::IntraOnly => {
1464 if hdr.ftype != FrameType::I {
1465 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::None);
1466 frm.set_frame_type(FrameType::Skip);
1467 return Ok(frm.into_ref());
1468 }
1469 },
1470 };
1471
1472 let mut slices: Vec<usize> = Vec::new();
1473 hdr.parse_slice_sizes(&mut br, &mut slices)?;
1474 match hdr.ftype {
1475 FrameType::P => {
1476 if self.ipbs.get_lastref().is_none() {
1477 return Err(DecoderError::MissingReference);
1478 }
1479 },
1480 FrameType::B => {
1481 if self.ipbs.get_lastref().is_none() {
1482 return Err(DecoderError::MissingReference);
1483 }
1484 if self.ipbs.get_nextref().is_none() {
1485 return Err(DecoderError::MissingReference);
1486 }
1487 },
1488 _ => {},
1489 };
1490
1491 let tmp_vinfo = NAVideoInfo::new(hdr.width, hdr.height, false, YUV420_FORMAT);
1492 let ret = supp.pool_u8.get_free();
1493 if ret.is_none() {
1494 return Err(DecoderError::AllocError);
1495 }
1496 let mut buf = ret.unwrap();
1497 if buf.get_info() != tmp_vinfo {
1498 self.ipbs.clear();
1499 supp.pool_u8.reset();
1500 supp.pool_u8.prealloc_video(tmp_vinfo, 6)?;
1501 let ret = supp.pool_u8.get_free();
1502 if ret.is_none() {
1503 return Err(DecoderError::AllocError);
1504 }
1505 buf = ret.unwrap();
1506 }
1507
1508 let cu_w = hdr.get_width_cu();
1509 let cu_h = hdr.get_height_cu();
1510 self.pu_stride = cu_w << 3;
1511 self.pu_info.resize(self.pu_stride * (cu_h << 3), PUInfo::default());
1512 self.blk_stride = cu_w << 4;
1513 self.blk_info.clear();
1514 self.blk_info.resize(self.blk_stride * (cu_h << 4), BlockInfo::default());
1515 if hdr.deblock {
1516 self.dblk.reinit(hdr.awidth, hdr.aheight);
1517 }
1518 let mut off = hsize + (br.tell() >> 3);
1519 let mut dframe = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap();
1520 for (cu_y, size) in slices.into_iter().enumerate() {
1521 self.decode_cu_line(&mut dframe, &hdr, &src[off..][..size], cu_y)?;
1522 off += size;
1523 }
1524 if (hdr.ftype == FrameType::I) || (hdr.ftype == FrameType::P) {
1525 self.ipbs.add_frame(buf.clone());
1526 }
1527
1528 if hdr.ftype != FrameType::B {
1529 self.ref0_pts = self.ref1_pts;
1530 self.ref1_pts = pkt.get_pts().unwrap_or(0);
1531 self.ref0_ts = self.ref1_ts;
1532 self.ref1_ts = hdr.ts as u64;
1533 if (self.ref1_pts > self.ref0_pts) && (self.ref1_ts > self.ref0_ts) {
1534 self.ts_scale = (self.ref1_pts - self.ref0_pts) / (self.ref1_ts - self.ref0_ts);
1535 }
1536 }
1537 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::Video(buf));
1538 frm.set_keyframe(hdr.ftype == FrameType::I);
1539 if hdr.ftype == FrameType::B {
1540 let pts = self.ref0_pts + ((hdr.ts as u64) - self.ref0_ts) * self.ts_scale;
1541 frm.set_pts(Some(pts));
1542 }
1543 frm.set_frame_type(hdr.ftype);
1544 Ok(frm.into_ref())
1545 }
1546 fn flush(&mut self) {
1547 self.ipbs.clear();
1548 }
1549 }
1550
1551 const DECODER_OPTIONS: &[NAOptionDefinition] = &[
1552 NAOptionDefinition {
1553 name: FRAME_SKIP_OPTION, description: FRAME_SKIP_OPTION_DESC,
1554 opt_type: NAOptionDefinitionType::String(Some(&[
1555 FRAME_SKIP_OPTION_VAL_NONE,
1556 FRAME_SKIP_OPTION_VAL_KEYFRAME,
1557 FRAME_SKIP_OPTION_VAL_INTRA
1558 ])) },
1559 ];
1560
1561 impl NAOptionHandler for RealVideo60Decoder {
1562 fn get_supported_options(&self) -> &[NAOptionDefinition] { DECODER_OPTIONS }
1563 fn set_options(&mut self, options: &[NAOption]) {
1564 for option in options.iter() {
1565 for opt_def in DECODER_OPTIONS.iter() {
1566 if opt_def.check(option).is_ok() {
1567 match (option.name, &option.value) {
1568 (FRAME_SKIP_OPTION, NAValue::String(ref strval)) => {
1569 if let Ok(smode) = FrameSkipMode::from_str(strval) {
1570 self.skip_mode = smode;
1571 }
1572 },
1573 _ => {},
1574 }
1575 }
1576 }
1577 }
1578 }
1579 fn query_option_value(&self, name: &str) -> Option<NAValue> {
1580 match name {
1581 FRAME_SKIP_OPTION => Some(NAValue::String(self.skip_mode.to_string())),
1582 _ => None,
1583 }
1584 }
1585 }
1586
1587 pub fn get_decoder() -> Box<dyn NADecoder + Send> {
1588 Box::new(RealVideo60Decoder::new())
1589 }
1590
1591 #[cfg(test)]
1592 mod test {
1593 use nihav_core::codecs::RegisteredDecoders;
1594 use nihav_core::demuxers::RegisteredDemuxers;
1595 use nihav_codec_support::test::dec_video::*;
1596 use crate::realmedia_register_all_decoders;
1597 use crate::realmedia_register_all_demuxers;
1598 #[test]
1599 fn test_rv60() {
1600 let mut dmx_reg = RegisteredDemuxers::new();
1601 realmedia_register_all_demuxers(&mut dmx_reg);
1602 let mut dec_reg = RegisteredDecoders::new();
1603 realmedia_register_all_decoders(&mut dec_reg);
1604
1605 // sample from a private collection
1606 test_decoding("realmedia", "realvideo6", "assets/RV/RV60.rmhd", Some(1000), &dmx_reg, &dec_reg,
1607 ExpectedTestResult::MD5Frames(vec![
1608 [0x2b1f1807, 0x09edef33, 0x0e6c78c1, 0x3b3c8179],
1609 [0x76743a3b, 0x7dd4f196, 0x0193fe5a, 0x4f78c7cb],
1610 [0x2b1f1807, 0x09edef33, 0x0e6c78c1, 0x3b3c8179],
1611 [0xfee70206, 0x626f3bea, 0x7677ad4b, 0x1228f3b6],
1612 [0x7156cbc2, 0xf381bcb6, 0xe86531f2, 0xb311c3ea],
1613 [0x1742b5a1, 0x66252580, 0x242753de, 0x5215d732],
1614 [0xd357ebda, 0x6460dba6, 0xa93eb616, 0x63ee6d60],
1615 [0x4cd72275, 0x28e1e439, 0xad17dfca, 0x3fd7253f],
1616 [0xe389ce4f, 0x8f0891b3, 0x88639b23, 0x21ed114f],
1617 [0x5b2b2f1b, 0x17a7518b, 0x53806e6a, 0x4538bb00],
1618 [0xdca03c9a, 0x1a45d80c, 0x86141211, 0x79912ed4],
1619 [0x0bf66bf4, 0x46385620, 0xc6fa4796, 0xd8e16d56],
1620 [0x4671a7f0, 0x46f50649, 0x268df27b, 0x70b71ab3]]));
1621 }
1622 #[test]
1623 fn test_rv60_dqp() {
1624 let mut dmx_reg = RegisteredDemuxers::new();
1625 realmedia_register_all_demuxers(&mut dmx_reg);
1626 let mut dec_reg = RegisteredDecoders::new();
1627 realmedia_register_all_decoders(&mut dec_reg);
1628
1629 // sample provided by Peter Ross
1630 test_decoding("realmedia", "realvideo6", "assets/RV/qp-offset-type-2.rmhd", Some(500), &dmx_reg, &dec_reg,
1631 ExpectedTestResult::MD5Frames(vec![
1632 [0x3dc2f19e, 0x0f8c66bd, 0x8e81ceda, 0xa1bf8f58],
1633 [0xbd9c0f89, 0x67b780b0, 0xa4afe443, 0x9f17221a],
1634 [0xf3e0a7ba, 0xe620ace9, 0x03857219, 0x8c3bd1fb],
1635 [0xc4eedc8c, 0x81d2dd0f, 0xa6443847, 0x09c8cec9],
1636 [0x565fc952, 0x4d5dc166, 0xf64b7b0d, 0x1570de50],
1637 [0x0e50786a, 0xaf058ff3, 0xa3f71eba, 0x370c197a],
1638 [0x1b92667b, 0x9cab9e24, 0x1bf48cb2, 0x368db124],
1639 [0xefcc0ab4, 0x6efceb20, 0xb2501ee8, 0xb449b7b6],
1640 [0xbbc2ca23, 0x6a7a8da2, 0xeadc1ff7, 0x2ff0a7f3],
1641 [0x6d14a2b4, 0x0d2642fb, 0x78fcad10, 0xba571ec1],
1642 [0xbdf889fd, 0x5f15838a, 0x8fedd13f, 0xc26a2e50],
1643 [0x886f03b6, 0xc46ba7c3, 0xae6aa971, 0x90cf94b6],
1644 [0x951693e7, 0xa77f68f3, 0x765990c9, 0x4a4d57fa],
1645 [0x3c25f4eb, 0x5c113c41, 0x4d73f498, 0xd7e210b0]]));
1646 }
1647 }