]>
Commit | Line | Data |
---|---|---|
8bde0eae KS |
1 | use nihav_core::codecs::*; |
2 | use nihav_core::io::byteio::*; | |
3 | use nihav_core::io::bitreader::*; | |
4 | use nihav_core::io::codebook::*; | |
5 | ||
6 | #[repr(u8)] | |
7 | enum TM2StreamType { | |
8 | CHigh = 0, | |
9 | CLow, | |
10 | LHigh, | |
11 | LLow, | |
12 | Update, | |
13 | Motion, | |
14 | BlockType, | |
15 | Num | |
16 | } | |
17 | ||
18 | #[repr(u8)] | |
19 | #[derive(Debug,Clone,Copy)] | |
20 | enum TM2BlockType { | |
21 | HiRes, | |
22 | MedRes, | |
23 | LowRes, | |
24 | NullRes, | |
25 | Update, | |
26 | Still, | |
27 | Motion | |
28 | } | |
29 | ||
30 | const TM2_BLOCK_TYPES: [TM2BlockType; 7] = [ | |
31 | TM2BlockType::HiRes, TM2BlockType::MedRes, TM2BlockType::LowRes, TM2BlockType::NullRes, | |
32 | TM2BlockType::Update, TM2BlockType::Still, TM2BlockType::Motion | |
33 | ]; | |
34 | ||
35 | trait ReadLenEsc { | |
36 | fn read_len_esc(&mut self) -> DecoderResult<usize>; | |
37 | } | |
38 | ||
39 | const TM2_ESCAPE: usize = 0x80000000; | |
40 | ||
41 | impl<'a> ReadLenEsc for ByteReader<'a> { | |
42 | fn read_len_esc(&mut self) -> DecoderResult<usize> { | |
43 | let len = self.read_u32le()? as usize; | |
44 | if len == TM2_ESCAPE { | |
45 | let len2 = self.read_u32le()? as usize; | |
46 | Ok(len2) | |
47 | } else { | |
48 | Ok(len) | |
49 | } | |
50 | } | |
51 | } | |
52 | ||
53 | struct HuffDef { | |
54 | val_bits: u8, | |
55 | max_bits: u8, | |
56 | nelems: usize, | |
57 | } | |
58 | ||
59 | impl HuffDef { | |
20b5a55f | 60 | #[allow(clippy::identity_op)] |
8bde0eae KS |
61 | fn read(&mut self, br: &mut BitReader, codes: &mut Vec<FullCodebookDesc<u8>>, prefix: u32, len: u8) -> DecoderResult<()> { |
62 | validate!(len <= self.max_bits); | |
63 | if !br.read_bool()? { | |
64 | validate!(codes.len() < self.nelems); | |
65 | let sym = br.read(self.val_bits)? as u8; | |
66 | codes.push(FullCodebookDesc { code: prefix, bits: len, sym }); | |
67 | } else { | |
68 | self.read(br, codes, (prefix << 1) | 0, len + 1)?; | |
69 | self.read(br, codes, (prefix << 1) | 1, len + 1)?; | |
70 | } | |
71 | Ok(()) | |
72 | } | |
73 | } | |
74 | ||
75 | struct HuffTree { | |
76 | cb: Option<Codebook<u8>>, | |
77 | sym0: u8, | |
78 | } | |
79 | ||
80 | impl HuffTree { | |
81 | fn new() -> Self { | |
82 | Self { cb: None, sym0: 0 } | |
83 | } | |
84 | } | |
85 | ||
86 | const TM2_MAX_DELTAS: usize = 64; | |
87 | ||
88 | struct TM2Stream { | |
89 | tokens: Vec<u8>, | |
90 | deltas: [i32; TM2_MAX_DELTAS], | |
91 | pos: usize, | |
92 | } | |
93 | ||
94 | impl Default for TM2Stream { | |
95 | fn default() -> Self { | |
96 | Self { | |
97 | tokens: Vec::new(), | |
98 | deltas: [0; TM2_MAX_DELTAS], | |
99 | pos: 0, | |
100 | } | |
101 | } | |
102 | } | |
103 | ||
104 | impl TM2Stream { | |
105 | fn read_header(&mut self, src: &[u8], br: &mut ByteReader) -> DecoderResult<()> { | |
37952415 | 106 | self.tokens.clear(); |
8bde0eae KS |
107 | self.pos = 0; |
108 | ||
109 | let len = br.read_u32le()? as usize; | |
110 | let endpos = br.tell() + (len as u64) * 4; | |
111 | if len == 0 { | |
112 | return Ok(()); | |
113 | } | |
114 | let ntoks = br.read_u32le()? as usize; | |
115 | validate!(ntoks < (1 << 24)); | |
116 | if (ntoks & 1) != 0 { | |
117 | let dlen = br.read_len_esc()?; | |
118 | if (dlen as i32) > 0 { | |
119 | let rest_size = (endpos - br.tell()) as usize; | |
120 | let skip_size = self.read_deltas(&src[br.tell() as usize..][..rest_size])?; | |
121 | validate!(skip_size == dlen * 4); | |
122 | br.read_skip(skip_size)?; | |
123 | } | |
124 | } | |
125 | let _len = br.read_len_esc()?; | |
126 | let _algo = br.read_u32le()?; | |
127 | ||
128 | let mut htree = HuffTree::new(); | |
129 | let rest_size = (endpos - br.tell()) as usize; | |
130 | let skip_size = self.read_huff_tree(&src[br.tell() as usize..][..rest_size], &mut htree)?; | |
131 | br.read_skip(skip_size)?; | |
132 | ||
133 | let len = br.read_u32le()? as usize; | |
134 | validate!(br.tell() + (len as u64) * 4 <= endpos); | |
135 | if len > 0 { | |
136 | self.tokens.reserve(ntoks >> 1); | |
137 | let rest_size = (endpos - br.tell()) as usize; | |
138 | let skip_size = self.read_tokens(&src[br.tell() as usize..][..rest_size], &htree, ntoks >> 1)?; | |
139 | br.read_skip(skip_size)?; | |
140 | } else { | |
141 | self.tokens.resize(ntoks >> 1, htree.sym0); | |
142 | } | |
143 | ||
144 | ||
145 | let pos = br.tell(); | |
146 | validate!(pos <= endpos); | |
147 | let toskip = endpos - pos; | |
148 | br.read_skip(toskip as usize)?; | |
d24468d9 | 149 | |
8bde0eae KS |
150 | Ok(()) |
151 | } | |
152 | fn read_deltas(&mut self, src: &[u8]) -> DecoderResult<usize> { | |
fa90ccfb | 153 | let mut br = BitReader::new(src, BitReaderMode::LE32MSB); |
8bde0eae KS |
154 | let coded_deltas = br.read(9)? as usize; |
155 | let bits = br.read(5)? as u8; | |
156 | validate!((coded_deltas <= TM2_MAX_DELTAS) && (bits > 0)); | |
157 | let mask = 1 << (bits - 1); | |
158 | let bias = 1 << bits; | |
159 | self.deltas = [0; TM2_MAX_DELTAS]; | |
160 | for i in 0..coded_deltas { | |
161 | let val = br.read(bits)?; | |
162 | if (val & mask) != 0 { | |
163 | self.deltas[i] = (val as i32) - bias; | |
164 | } else { | |
165 | self.deltas[i] = val as i32; | |
166 | } | |
167 | } | |
d24468d9 | 168 | |
8bde0eae KS |
169 | Ok(((br.tell() + 31) >> 5) << 2) |
170 | } | |
171 | fn read_huff_tree(&mut self, src: &[u8], htree: &mut HuffTree) -> DecoderResult<usize> { | |
fa90ccfb | 172 | let mut br = BitReader::new(src, BitReaderMode::LE32MSB); |
8bde0eae KS |
173 | |
174 | let val_bits = br.read(5)? as u8; | |
175 | let max_bits = br.read(5)? as u8; | |
176 | let min_bits = br.read(5)? as u8; | |
177 | let nelems = br.read(17)? as usize; | |
178 | validate!(val_bits > 0 && val_bits <= 6); | |
179 | validate!(nelems > 0); | |
180 | validate!((max_bits < 25) && (min_bits <= max_bits)); | |
181 | ||
182 | let mut codes: Vec<FullCodebookDesc<u8>> = Vec::with_capacity(nelems); | |
183 | let mut hdef = HuffDef { val_bits, max_bits, nelems }; | |
184 | hdef.read(&mut br, &mut codes, 0, 0)?; | |
185 | htree.sym0 = codes[0].sym; | |
186 | if nelems > 1 { | |
78da9b7f | 187 | let mut cr = FullCodebookDescReader::new(&codes); |
8bde0eae KS |
188 | htree.cb = Some(Codebook::new(&mut cr, CodebookMode::MSB)?); |
189 | } | |
d24468d9 | 190 | |
8bde0eae KS |
191 | Ok(((br.tell() + 31) >> 5) << 2) |
192 | } | |
193 | fn read_tokens(&mut self, src: &[u8], htree: &HuffTree, ntoks: usize) -> DecoderResult<usize> { | |
fa90ccfb | 194 | let mut br = BitReader::new(src, BitReaderMode::LE32MSB); |
8bde0eae KS |
195 | |
196 | if let Some(ref cb) = htree.cb { | |
197 | for _ in 0..ntoks { | |
198 | let tok = br.read_cb(cb)?; | |
199 | self.tokens.push(tok); | |
200 | } | |
201 | } | |
d24468d9 | 202 | |
8bde0eae KS |
203 | Ok(((br.tell() + 31) >> 5) << 2) |
204 | } | |
205 | ||
206 | fn get_block_type(&mut self) -> DecoderResult<u8> { | |
207 | validate!(self.pos < self.tokens.len()); | |
208 | let res = self.tokens[self.pos]; | |
209 | self.pos += 1; | |
210 | Ok(res) | |
211 | } | |
212 | fn get_token(&mut self) -> DecoderResult<i32> { | |
213 | validate!(self.pos < self.tokens.len()); | |
214 | let idx = self.tokens[self.pos] as usize; | |
215 | validate!(idx < TM2_MAX_DELTAS); | |
216 | self.pos += 1; | |
217 | Ok(self.deltas[idx]) | |
218 | } | |
219 | } | |
220 | ||
221 | #[derive(Default)] | |
222 | struct DeltaState { | |
223 | dy: [i32; 4], | |
224 | dc: [[i32; 2]; 2], | |
225 | } | |
226 | ||
c2a4fa57 | 227 | #[allow(clippy::erasing_op)] |
20b5a55f | 228 | #[allow(clippy::identity_op)] |
8bde0eae KS |
229 | impl DeltaState { |
230 | fn apply_y(&mut self, dst: &mut [u8], mut yoff: usize, ystride: usize, ydeltas: &[i32; 16], last: &mut [i32]) { | |
231 | for y in 0..4 { | |
232 | let mut d = self.dy[y]; | |
233 | for x in 0..4 { | |
234 | d += ydeltas[x + y * 4]; | |
235 | last[x] += d; | |
236 | dst[yoff + x] = last[x].max(0).min(255) as u8; | |
237 | } | |
238 | self.dy[y] = d; | |
239 | yoff += ystride; | |
240 | } | |
241 | } | |
242 | fn apply_c(&mut self, dst: &mut [i16], mut coff: usize, cstride: usize, cdeltas: &[i32; 4], idx: usize, last: &mut [i32]) { | |
243 | for y in 0..2 { | |
244 | let mut d = self.dc[idx][y]; | |
245 | for x in 0..2 { | |
246 | d += cdeltas[x + y * 2]; | |
247 | last[x] += d; | |
248 | dst[coff + x] = last[x] as i16; | |
249 | } | |
250 | self.dc[idx][y] = d; | |
251 | coff += cstride; | |
252 | } | |
253 | } | |
254 | fn interpolate_y_low(&mut self, last: &mut [i32]) { | |
255 | let dsum = self.dy[0] + self.dy[1] + self.dy[2] + self.dy[3]; | |
256 | last[1] = (last[0] - dsum + last[2]) >> 1; | |
257 | last[3] = (last[2] + last[4]) >> 1; | |
258 | ||
259 | let t0 = self.dy[0] + self.dy[1]; | |
260 | let t1 = self.dy[2] + self.dy[3]; | |
261 | self.dy[0] = t0 >> 1; | |
262 | self.dy[1] = t0 - (t0 >> 1); | |
263 | self.dy[2] = t1 >> 1; | |
264 | self.dy[3] = t1 - (t1 >> 1); | |
265 | } | |
266 | fn interpolate_y_null(&mut self, last: &mut [i32]) { | |
267 | let dsum = self.dy[0] + self.dy[1] + self.dy[2] + self.dy[3]; | |
268 | let left = last[0] - dsum; | |
269 | let right = last[4]; | |
270 | let diff = right - left; | |
271 | last[1] = left + (diff >> 2); | |
272 | last[2] = left + (diff >> 1); | |
273 | last[3] = right - (diff >> 2); | |
274 | ||
275 | let mut sum = left; | |
276 | self.dy[0] = (left + (dsum >> 2)) - sum; | |
277 | sum += self.dy[0]; | |
278 | self.dy[1] = (left + (dsum >> 1)) - sum; | |
279 | sum += self.dy[1]; | |
280 | self.dy[2] = (left + dsum - (dsum >> 2)) - sum; | |
281 | sum += self.dy[2]; | |
282 | self.dy[3] = (left + dsum) - sum; | |
283 | } | |
284 | fn interpolate_c(&mut self, idx: usize, last: &mut [i32]) { | |
285 | let dsum = self.dc[idx][0] + self.dc[idx][1]; | |
286 | let l = (last[0] + last[2] - dsum) >> 1; | |
287 | self.dc[idx][0] = dsum >> 1; | |
288 | self.dc[idx][1] = dsum - (dsum >> 1); | |
289 | last[1] = l; | |
290 | } | |
291 | fn recalc_y(&mut self, dst: &[u8], yoff: usize, ystride: usize, last: &mut [i32]) { | |
292 | let src = &dst[yoff+3..]; | |
8a7352c0 KS |
293 | self.dy[0] = i32::from(src[ystride * 0]) - last[3]; |
294 | self.dy[1] = i32::from(src[ystride * 1]) - i32::from(src[ystride * 0]); | |
295 | self.dy[2] = i32::from(src[ystride * 2]) - i32::from(src[ystride * 1]); | |
296 | self.dy[3] = i32::from(src[ystride * 3]) - i32::from(src[ystride * 2]); | |
8bde0eae KS |
297 | let src = &dst[yoff + 3 * ystride..]; |
298 | for x in 0..4 { | |
8a7352c0 | 299 | last[x] = i32::from(src[x]); |
8bde0eae KS |
300 | } |
301 | } | |
302 | fn recalc_c(&mut self, dst: &[i16], coff: usize, cstride: usize, idx: usize, last: &mut [i32]) { | |
8a7352c0 KS |
303 | self.dc[idx][0] = i32::from(dst[coff + 1]) - last[1]; |
304 | self.dc[idx][1] = i32::from(dst[coff + 1 + cstride]) - i32::from(dst[coff + 1]); | |
305 | last[0] = i32::from(dst[coff + cstride + 0]); | |
306 | last[1] = i32::from(dst[coff + cstride + 1]); | |
8bde0eae KS |
307 | } |
308 | } | |
309 | ||
310 | #[derive(Default)] | |
311 | struct TM2Frame { | |
312 | ydata: Vec<u8>, | |
313 | udata: Vec<i16>, | |
314 | vdata: Vec<i16>, | |
315 | ystride: usize, | |
316 | cstride: usize, | |
317 | } | |
318 | ||
319 | impl TM2Frame { | |
320 | fn alloc(width: usize, height: usize) -> Self { | |
321 | let ystride = (width + 3) & !3; | |
322 | let ysize = ystride * ((height + 3) & !3); | |
8bde0eae KS |
323 | let cstride = ystride >> 1; |
324 | let csize = cstride * (((height + 3) & !3) >> 1); | |
8a7352c0 | 325 | Self { ydata: vec![0; ysize], udata: vec![0; csize], vdata: vec![0; csize], ystride, cstride } |
8bde0eae KS |
326 | } |
327 | } | |
328 | ||
329 | #[derive(Default)] | |
330 | struct TM2Decoder { | |
2422d969 | 331 | info: NACodecInfoRef, |
8bde0eae KS |
332 | streams: [TM2Stream; TM2StreamType::Num as usize], |
333 | width: usize, | |
334 | height: usize, | |
335 | cur_frame: TM2Frame, | |
336 | prev_frame: TM2Frame, | |
337 | } | |
338 | ||
339 | impl TM2Decoder { | |
340 | fn new() -> Self { Self::default() } | |
341 | fn decode_blocks(&mut self) -> DecoderResult<bool> { | |
342 | let ydst = &mut self.cur_frame.ydata; | |
343 | let udst = &mut self.cur_frame.udata; | |
344 | let vdst = &mut self.cur_frame.vdata; | |
345 | let ystride = self.cur_frame.ystride; | |
346 | let cstride = self.cur_frame.cstride; | |
347 | let mut offs: [usize; 2] = [0; 2]; | |
348 | let mut is_intra = true; | |
349 | ||
350 | let bw = self.width >> 2; | |
351 | let bh = self.height >> 2; | |
352 | validate!(self.streams[TM2StreamType::BlockType as usize].tokens.len() == bw * bh); | |
353 | ||
354 | let mut ydeltas: [i32; 16] = [0; 16]; | |
355 | let mut cdeltas: [[i32; 4]; 2] = [[0; 4]; 2]; | |
8a7352c0 KS |
356 | let mut lasty: Vec<i32> = vec![0; self.width + 1]; |
357 | let mut lastu: Vec<i32> = vec![0; self.width/2 + 1]; | |
358 | let mut lastv: Vec<i32> = vec![0; self.width/2 + 1]; | |
8bde0eae KS |
359 | for by in 0..bh { |
360 | let mut dstate = DeltaState::default(); | |
361 | for bx in 0..bw { | |
362 | let bidx = self.streams[TM2StreamType::BlockType as usize].get_block_type()? as usize; | |
363 | validate!(bidx < TM2_BLOCK_TYPES.len()); | |
364 | let btype = TM2_BLOCK_TYPES[bidx]; | |
365 | match btype { | |
366 | TM2BlockType::HiRes => { | |
367 | for i in 0..4 { | |
368 | cdeltas[0][i] = self.streams[TM2StreamType::CHigh as usize].get_token()?; | |
369 | cdeltas[1][i] = self.streams[TM2StreamType::CHigh as usize].get_token()?; | |
370 | } | |
371 | dstate.apply_c(udst, offs[1] + bx * 2, cstride, &cdeltas[0], 0, &mut lastu[bx*2+1..]); | |
372 | dstate.apply_c(vdst, offs[1] + bx * 2, cstride, &cdeltas[1], 1, &mut lastv[bx*2+1..]); | |
20b5a55f KS |
373 | for yd in ydeltas.iter_mut() { |
374 | *yd = self.streams[TM2StreamType::LHigh as usize].get_token()?; | |
8bde0eae KS |
375 | } |
376 | dstate.apply_y(ydst, offs[0] + bx * 4, ystride, &ydeltas, &mut lasty[bx*4+1..]); | |
377 | }, | |
378 | TM2BlockType::MedRes => { | |
379 | cdeltas = [[0; 4]; 2]; | |
380 | cdeltas[0][0] = self.streams[TM2StreamType::CLow as usize].get_token()?; | |
381 | cdeltas[1][0] = self.streams[TM2StreamType::CLow as usize].get_token()?; | |
382 | dstate.interpolate_c(0, &mut lastu[bx*2..]); | |
383 | dstate.apply_c(udst, offs[1] + bx * 2, cstride, &cdeltas[0], 0, &mut lastu[bx*2+1..]); | |
384 | dstate.interpolate_c(1, &mut lastv[bx*2..]); | |
385 | dstate.apply_c(vdst, offs[1] + bx * 2, cstride, &cdeltas[1], 1, &mut lastv[bx*2+1..]); | |
20b5a55f KS |
386 | for yd in ydeltas.iter_mut() { |
387 | *yd = self.streams[TM2StreamType::LHigh as usize].get_token()?; | |
8bde0eae KS |
388 | } |
389 | dstate.apply_y(ydst, offs[0] + bx * 4, ystride, &ydeltas, &mut lasty[bx*4+1..]); | |
390 | }, | |
391 | TM2BlockType::LowRes => { | |
392 | cdeltas = [[0; 4]; 2]; | |
393 | cdeltas[0][0] = self.streams[TM2StreamType::CLow as usize].get_token()?; | |
394 | cdeltas[1][0] = self.streams[TM2StreamType::CLow as usize].get_token()?; | |
395 | dstate.interpolate_c(0, &mut lastu[bx*2..]); | |
396 | dstate.apply_c(udst, offs[1] + bx * 2, cstride, &cdeltas[0], 0, &mut lastu[bx*2+1..]); | |
397 | dstate.interpolate_c(1, &mut lastv[bx*2..]); | |
398 | dstate.apply_c(vdst, offs[1] + bx * 2, cstride, &cdeltas[1], 1, &mut lastv[bx*2+1..]); | |
399 | ydeltas = [0; 16]; | |
400 | ydeltas[ 0] = self.streams[TM2StreamType::LLow as usize].get_token()?; | |
401 | ydeltas[ 2] = self.streams[TM2StreamType::LLow as usize].get_token()?; | |
402 | ydeltas[ 8] = self.streams[TM2StreamType::LLow as usize].get_token()?; | |
403 | ydeltas[10] = self.streams[TM2StreamType::LLow as usize].get_token()?; | |
404 | dstate.interpolate_y_low(&mut lasty[bx*4..]); | |
405 | dstate.apply_y(ydst, offs[0] + bx * 4, ystride, &ydeltas, &mut lasty[bx*4+1..]); | |
406 | }, | |
407 | TM2BlockType::NullRes => { | |
408 | cdeltas = [[0; 4]; 2]; | |
409 | dstate.interpolate_c(0, &mut lastu[bx*2..]); | |
410 | dstate.apply_c(udst, offs[1] + bx * 2, cstride, &cdeltas[0], 0, &mut lastu[bx*2+1..]); | |
411 | dstate.interpolate_c(1, &mut lastv[bx*2..]); | |
412 | dstate.apply_c(vdst, offs[1] + bx * 2, cstride, &cdeltas[1], 1, &mut lastv[bx*2+1..]); | |
413 | ydeltas = [0; 16]; | |
414 | dstate.interpolate_y_null(&mut lasty[bx*4..]); | |
415 | dstate.apply_y(ydst, offs[0] + bx * 4, ystride, &ydeltas, &mut lasty[bx*4+1..]); | |
416 | }, | |
417 | TM2BlockType::Update => { | |
418 | is_intra = false; | |
419 | ||
420 | let mut coff = offs[1] + bx * 2; | |
421 | let usrc = &self.prev_frame.udata; | |
422 | let vsrc = &self.prev_frame.vdata; | |
423 | for _ in 0..2 { | |
424 | for x in 0..2 { | |
425 | let du = self.streams[TM2StreamType::Update as usize].get_token()?; | |
426 | let dv = self.streams[TM2StreamType::Update as usize].get_token()?; | |
427 | udst[coff + x] = usrc[coff + x] + (du as i16); | |
428 | vdst[coff + x] = vsrc[coff + x] + (dv as i16); | |
429 | } | |
430 | coff += cstride; | |
431 | } | |
432 | dstate.recalc_c(udst, offs[1] + bx * 2, cstride, 0, &mut lastu[bx*2+1..]); | |
433 | dstate.recalc_c(vdst, offs[1] + bx * 2, cstride, 1, &mut lastv[bx*2+1..]); | |
434 | let mut yoff = offs[0] + bx * 4; | |
435 | let ysrc = &self.prev_frame.ydata; | |
436 | for _ in 0..4 { | |
437 | for x in 0..4 { | |
438 | let dy = self.streams[TM2StreamType::Update as usize].get_token()?; | |
47933c6d | 439 | ydst[yoff + x] = (i32::from(ysrc[yoff + x]) + dy) as u8; |
8bde0eae KS |
440 | } |
441 | yoff += ystride; | |
442 | } | |
443 | dstate.recalc_y(ydst, offs[0] + bx * 4, ystride, &mut lasty[bx*4+1..]); | |
444 | }, | |
445 | TM2BlockType::Still => { | |
446 | is_intra = false; | |
447 | ||
20b5a55f KS |
448 | let coff = offs[1] + bx * 2; |
449 | for (dline, sline) in udst[coff..].chunks_mut(cstride) | |
450 | .zip(self.prev_frame.udata[coff..].chunks(cstride)).take(2) { | |
451 | dline[..2].copy_from_slice(&sline[..2]); | |
8bde0eae | 452 | } |
20b5a55f KS |
453 | for (dline, sline) in vdst[coff..].chunks_mut(cstride) |
454 | .zip(self.prev_frame.vdata[coff..].chunks(cstride)).take(2) { | |
455 | dline[..2].copy_from_slice(&sline[..2]); | |
8bde0eae | 456 | } |
20b5a55f KS |
457 | dstate.recalc_c(udst, coff, cstride, 0, &mut lastu[bx*2+1..]); |
458 | dstate.recalc_c(vdst, coff, cstride, 1, &mut lastv[bx*2+1..]); | |
459 | let yoff = offs[0] + bx * 4; | |
460 | for (dline, sline) in ydst[yoff..].chunks_mut(ystride) | |
461 | .zip(self.prev_frame.ydata[yoff..].chunks(ystride)).take(4) { | |
462 | dline[..4].copy_from_slice(&sline[..4]); | |
463 | } | |
464 | dstate.recalc_y(ydst, yoff, ystride, &mut lasty[bx*4+1..]); | |
8bde0eae KS |
465 | }, |
466 | TM2BlockType::Motion => { | |
467 | is_intra = false; | |
468 | ||
469 | let mx = self.streams[TM2StreamType::Motion as usize].get_token()?; | |
470 | let my = self.streams[TM2StreamType::Motion as usize].get_token()?; | |
471 | let xpos = (((bx as i32) * 4) + mx).max(0).min((self.width - 4) as i32) as usize; | |
472 | let ypos = (((by as i32) * 4) + my).max(0).min((self.height - 4) as i32) as usize; | |
20b5a55f KS |
473 | let coff = offs[1] + bx * 2; |
474 | let csoff = (xpos >> 1) + (ypos >> 1) * cstride; | |
475 | for (dline, sline) in udst[coff..].chunks_mut(cstride) | |
476 | .zip(self.prev_frame.udata[csoff..].chunks(cstride)).take(2) { | |
477 | dline[..2].copy_from_slice(&sline[..2]); | |
8bde0eae | 478 | } |
20b5a55f KS |
479 | for (dline, sline) in vdst[coff..].chunks_mut(cstride) |
480 | .zip(self.prev_frame.vdata[csoff..].chunks(cstride)).take(2) { | |
481 | dline[..2].copy_from_slice(&sline[..2]); | |
8bde0eae | 482 | } |
20b5a55f KS |
483 | dstate.recalc_c(udst, coff, cstride, 0, &mut lastu[bx*2+1..]); |
484 | dstate.recalc_c(vdst, coff, cstride, 1, &mut lastv[bx*2+1..]); | |
485 | let yoff = offs[0] + bx * 4; | |
486 | let ysoff = xpos + ypos * ystride; | |
487 | for (dline, sline) in ydst[yoff..].chunks_mut(ystride) | |
488 | .zip(self.prev_frame.ydata[ysoff..].chunks(ystride)).take(4) { | |
489 | dline[..4].copy_from_slice(&sline[..4]); | |
490 | } | |
491 | dstate.recalc_y(ydst, yoff, ystride, &mut lasty[bx*4+1..]); | |
8bde0eae KS |
492 | }, |
493 | }; | |
494 | } | |
495 | offs[0] += ystride * 4; | |
496 | offs[1] += cstride * 2; | |
497 | } | |
498 | ||
499 | Ok(is_intra) | |
500 | } | |
501 | fn output_frame(&mut self, buf: &mut NAVideoBuffer<u8>) { | |
502 | let fmt = buf.get_info().get_format(); | |
503 | let offs = [fmt.get_chromaton(0).unwrap().get_offset() as usize, | |
504 | fmt.get_chromaton(1).unwrap().get_offset() as usize, | |
505 | fmt.get_chromaton(2).unwrap().get_offset() as usize]; | |
506 | let stride = buf.get_stride(0); | |
1a967e6b | 507 | let data = buf.get_data_mut().unwrap(); |
8bde0eae KS |
508 | let dst = data.as_mut_slice(); |
509 | ||
510 | let mut off = 0; | |
511 | let mut ysrc = 0; | |
512 | let mut csrc = 0; | |
513 | for y in 0..self.height { | |
514 | let out = &mut dst[off..]; | |
515 | for (x, pic) in out.chunks_exact_mut(3).take(self.width).enumerate() { | |
8a7352c0 | 516 | let y = i16::from(self.cur_frame.ydata[ysrc + x]); |
8bde0eae KS |
517 | let u = self.cur_frame.udata[csrc + (x >> 1)]; |
518 | let v = self.cur_frame.vdata[csrc + (x >> 1)]; | |
519 | pic[offs[0]] = (y + u).max(0).min(255) as u8; | |
520 | pic[offs[1]] = y.max(0).min(255) as u8; | |
521 | pic[offs[2]] = (y + v).max(0).min(255) as u8; | |
522 | } | |
523 | off += stride; | |
524 | ysrc += self.cur_frame.ystride; | |
525 | if (y & 1) != 0 { | |
526 | csrc += self.cur_frame.cstride; | |
527 | } | |
528 | } | |
529 | } | |
530 | } | |
531 | ||
532 | impl NADecoder for TM2Decoder { | |
01613464 | 533 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { |
8bde0eae KS |
534 | if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { |
535 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, YUV410_FORMAT)); | |
536 | self.width = vinfo.get_width(); | |
537 | self.height = vinfo.get_height(); | |
538 | self.cur_frame = TM2Frame::alloc(self.width, self.height); | |
539 | self.prev_frame = TM2Frame::alloc(self.width, self.height); | |
2422d969 | 540 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); |
8bde0eae KS |
541 | Ok(()) |
542 | } else { | |
543 | Err(DecoderError::InvalidData) | |
544 | } | |
545 | } | |
01613464 | 546 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { |
8bde0eae KS |
547 | let src = pkt.get_buffer(); |
548 | validate!(src.len() >= 40 + (TM2StreamType::Num as usize) * 4 + 4); | |
549 | let mut mr = MemoryReader::new_read(&src); | |
550 | let mut br = ByteReader::new(&mut mr); | |
551 | ||
552 | let magic = br.read_u32be()?; | |
553 | validate!(magic == 0x100 || magic == 0x101); | |
554 | br.read_skip(36)?; | |
817e4872 KS |
555 | for stream in self.streams.iter_mut() { |
556 | stream.read_header(&src, &mut br)?; | |
8bde0eae KS |
557 | } |
558 | ||
559 | let myinfo = NAVideoInfo::new(self.width, self.height, false, RGB24_FORMAT); | |
8a7352c0 | 560 | let bufinfo = alloc_video_buffer(myinfo, 2)?; |
8bde0eae KS |
561 | let mut buf = bufinfo.get_vbuf().unwrap(); |
562 | ||
563 | let is_intra = self.decode_blocks()?; | |
564 | self.output_frame(&mut buf); | |
565 | std::mem::swap(&mut self.cur_frame, &mut self.prev_frame); | |
566 | ||
567 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); | |
568 | frm.set_keyframe(is_intra); | |
569 | frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); | |
171860fc | 570 | Ok(frm.into_ref()) |
8bde0eae | 571 | } |
f9be4e75 KS |
572 | fn flush(&mut self) { |
573 | } | |
8bde0eae KS |
574 | } |
575 | ||
7d57ae2f KS |
576 | impl NAOptionHandler for TM2Decoder { |
577 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
578 | fn set_options(&mut self, _options: &[NAOption]) { } | |
579 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
580 | } | |
581 | ||
08a1fab7 | 582 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { |
8bde0eae KS |
583 | Box::new(TM2Decoder::new()) |
584 | } | |
585 | ||
586 | #[cfg(test)] | |
587 | mod test { | |
588 | use nihav_core::codecs::RegisteredDecoders; | |
589 | use nihav_core::demuxers::RegisteredDemuxers; | |
ce742854 | 590 | use nihav_codec_support::test::dec_video::*; |
78fb6560 | 591 | use crate::duck_register_all_decoders; |
e64739f8 | 592 | use nihav_commonfmt::generic_register_all_demuxers; |
8bde0eae KS |
593 | #[test] |
594 | fn test_tm2() { | |
595 | let mut dmx_reg = RegisteredDemuxers::new(); | |
596 | generic_register_all_demuxers(&mut dmx_reg); | |
597 | let mut dec_reg = RegisteredDecoders::new(); | |
78fb6560 | 598 | duck_register_all_decoders(&mut dec_reg); |
8bde0eae | 599 | |
886cde48 | 600 | // sample: https://samples.mplayerhq.hu/V-codecs/TM20/tm20.avi |
9d861281 KS |
601 | test_decoding("avi", "truemotion2", "assets/Duck/tm20.avi", Some(16), |
602 | &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![ | |
603 | [0x8c336eb4, 0x10d0d934, 0x52392306, 0xc0bc6dd3], | |
604 | [0xf168ddc2, 0x502fef17, 0xf7a5d0a2, 0xc0bf2d26], | |
605 | [0xf33e02fa, 0x3931b691, 0xb29a0754, 0x07c0f8fa], | |
606 | [0x2dd81034, 0x1c9f7616, 0x64eed48a, 0x3aa09cf0], | |
607 | [0x55d18cd9, 0x0a3fd971, 0xf28fd5af, 0x9d9c3e3d], | |
608 | [0xbf9cbbd8, 0x7b44c122, 0x1c7b1904, 0x77cc87aa], | |
609 | [0xa6f6e79d, 0xc463a5bc, 0x5df9460c, 0xfce2e352], | |
610 | [0x2ad22b2d, 0xd4ceaff8, 0xa4adb974, 0x37888a8d], | |
611 | [0x4d45575f, 0x7e5b7670, 0x40cb7438, 0x9872d422], | |
612 | [0xb35bc12b, 0x026c77c6, 0x93163784, 0xb37630b7], | |
613 | [0xb1ec5059, 0x1fe26596, 0x4ac8d214, 0xdaf1b895], | |
614 | [0x69dd5a5f, 0xe14a16fa, 0xa0653092, 0xb04e0739], | |
615 | [0x979d8fe1, 0xbef29f89, 0xefae5f86, 0xa1ceb7d2], | |
616 | [0xc6dc80d7, 0x80153c6b, 0x76d770c0, 0x8fd7cce7], | |
617 | [0x8da96394, 0x1bd68024, 0x5feddfba, 0xd2b00660], | |
618 | [0x53ff97c3, 0xc021a9b3, 0xabdddc10, 0xc99ab86f], | |
619 | [0x66c877c4, 0xd8358048, 0xbca593db, 0xc6ecc4d1]])); | |
8bde0eae KS |
620 | } |
621 | } |