| 1 | use nihav_core::codecs::*; |
| 2 | use nihav_core::io::byteio::*; |
| 3 | |
| 4 | mod cell; |
| 5 | use cell::*; |
| 6 | mod mv; |
| 7 | use mv::*; |
| 8 | mod ratectl; |
| 9 | use ratectl::*; |
| 10 | mod tree; |
| 11 | pub use tree::{Indeo3Cell, Plane}; |
| 12 | use tree::Indeo3PrimaryTree; |
| 13 | |
| 14 | const OS_HEADER_SIZE: usize = 16; |
| 15 | const BITSTREAM_HEADER_SIZE: usize = 48; |
| 16 | const HDR_FIELD_2: u32 = 0; |
| 17 | const FRMH_TAG: u32 = ((b'F' as u32) << 24) | ((b'R' as u32) << 16) |
| 18 | | ((b'M' as u32) << 8) | (b'H' as u32); |
| 19 | const PLANE_OFFSETS: usize = 32; |
| 20 | |
| 21 | const CB_SELECTORS: [u8; 16] = [ |
| 22 | 0x02, 0x14, 0x26, 0x38, 0x4A, 0x5C, 0x6E, 0x7F, |
| 23 | 0x82, 0x94, 0xA6, 0xB8, 0xCA, 0xDC, 0xEE, 0xFF |
| 24 | ]; |
| 25 | |
| 26 | const PLANE_ORDER: [usize; 3] = [1, 2, 0]; |
| 27 | |
| 28 | pub struct Indeo3Writer<'a> { |
| 29 | dst: &'a mut Vec<u8>, |
| 30 | bitbuf: u8, |
| 31 | bits: u8, |
| 32 | bitpos: Option<usize>, |
| 33 | } |
| 34 | |
| 35 | impl<'a> Indeo3Writer<'a> { |
| 36 | fn new(dst: &'a mut Vec<u8>) -> Self { |
| 37 | Self { |
| 38 | dst, |
| 39 | bitbuf: 0, |
| 40 | bits: 0, |
| 41 | bitpos: None, |
| 42 | } |
| 43 | } |
| 44 | pub fn put_byte(&mut self, b: u8) { |
| 45 | self.dst.push(b); |
| 46 | } |
| 47 | pub fn put_2bits(&mut self, val: u8) { |
| 48 | if self.bits == 0 { |
| 49 | self.bitpos = Some(self.dst.len()); |
| 50 | self.dst.push(0); |
| 51 | } |
| 52 | self.bitbuf |= val << (6 - self.bits); |
| 53 | self.bits += 2; |
| 54 | if self.bits == 8 { |
| 55 | let bpos = self.bitpos.unwrap_or(0); |
| 56 | self.dst[bpos] = self.bitbuf; |
| 57 | self.bitbuf = 0; |
| 58 | self.bits = 0; |
| 59 | self.bitpos = None; |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | impl<'a> Drop for Indeo3Writer<'a> { |
| 65 | fn drop(&mut self) { |
| 66 | if self.bits != 0 { |
| 67 | let bpos = self.bitpos.unwrap_or(0); |
| 68 | self.dst[bpos] = self.bitbuf; |
| 69 | } |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | #[derive(Default)] |
| 74 | struct Indeo3Frame { |
| 75 | plane: [Plane; 3], |
| 76 | } |
| 77 | |
| 78 | impl Indeo3Frame { |
| 79 | fn new() -> Self { Self::default() } |
| 80 | fn alloc(&mut self, width: usize, height: usize) { |
| 81 | self.plane[0].alloc(width, height, 40); |
| 82 | self.plane[1].alloc(width / 4, height / 4, 10); |
| 83 | self.plane[2].alloc(width / 4, height / 4, 10); |
| 84 | } |
| 85 | fn fill(&mut self, vbuf: &NAVideoBufferRef<u8>) { |
| 86 | let data = vbuf.get_data(); |
| 87 | for (plane_no, plane) in self.plane.iter_mut().enumerate() { |
| 88 | plane.fill(&data[vbuf.get_offset(plane_no)..], vbuf.get_stride(plane_no)); |
| 89 | } |
| 90 | } |
| 91 | fn clear_mvs(&mut self) { |
| 92 | for plane in self.plane.iter_mut() { |
| 93 | plane.clear_mvs(); |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | struct Indeo3Encoder { |
| 99 | stream: Option<NAStreamRef>, |
| 100 | pkt: Option<NAPacket>, |
| 101 | cframe: Indeo3Frame, |
| 102 | pframe: Indeo3Frame, |
| 103 | cenc: CellEncoder, |
| 104 | mv_est: MotionEstimator, |
| 105 | rc: RateControl, |
| 106 | frameno: u32, |
| 107 | buf_sel: bool, |
| 108 | width: usize, |
| 109 | height: usize, |
| 110 | |
| 111 | debug_tree: bool, |
| 112 | debug_frm: bool, |
| 113 | try_again: bool, |
| 114 | } |
| 115 | |
| 116 | impl Indeo3Encoder { |
| 117 | fn new() -> Self { |
| 118 | Self { |
| 119 | stream: None, |
| 120 | pkt: None, |
| 121 | cframe: Indeo3Frame::new(), |
| 122 | pframe: Indeo3Frame::new(), |
| 123 | cenc: CellEncoder::new(), |
| 124 | mv_est: MotionEstimator::new(), |
| 125 | rc: RateControl::new(), |
| 126 | frameno: 0, |
| 127 | buf_sel: false, |
| 128 | width: 0, |
| 129 | height: 0, |
| 130 | |
| 131 | debug_tree: false, |
| 132 | debug_frm: false, |
| 133 | try_again: false, |
| 134 | } |
| 135 | } |
| 136 | fn encode_planes(&mut self, dbuf: &mut Vec<u8>, trees: &[Box<Indeo3PrimaryTree>], is_intra: bool) -> EncoderResult<()> { |
| 137 | for (&planeno, tree) in PLANE_ORDER.iter().zip(trees.iter()) { |
| 138 | let offset = dbuf.len(); |
| 139 | let ref_plane = &self.pframe.plane[planeno]; |
| 140 | |
| 141 | let mut mc_count = [0; 4]; |
| 142 | let mvs = &self.cframe.plane[planeno].mvs; |
| 143 | write_u32le(&mut mc_count, mvs.len() as u32)?; |
| 144 | dbuf.extend_from_slice(&mc_count); |
| 145 | for &(mv, _) in mvs.iter() { |
| 146 | dbuf.push(mv.y as u8); |
| 147 | dbuf.push(mv.x as u8); |
| 148 | } |
| 149 | |
| 150 | let mut iw = Indeo3Writer::new(dbuf); |
| 151 | self.cframe.plane[planeno].encode_tree(&mut iw, &tree, &mut self.cenc, ref_plane); |
| 152 | drop(iw); |
| 153 | while (dbuf.len() & 3) != 0 { |
| 154 | dbuf.push(0); |
| 155 | } |
| 156 | |
| 157 | let plane_off = PLANE_OFFSETS + 4 * if planeno > 0 { planeno ^ 3 } else { 0 }; |
| 158 | write_u32le(&mut dbuf[plane_off..], (offset - OS_HEADER_SIZE) as u32)?; |
| 159 | } |
| 160 | |
| 161 | let mut checksum = 0; |
| 162 | for plane in self.cframe.plane.iter() { |
| 163 | checksum ^= plane.checksum(); |
| 164 | } |
| 165 | write_u16le(&mut dbuf[26..], checksum * 2)?; |
| 166 | |
| 167 | let size = (dbuf.len() - OS_HEADER_SIZE) as u32; |
| 168 | write_u32le(&mut dbuf[8..], self.frameno ^ HDR_FIELD_2 ^ FRMH_TAG ^ size)?; |
| 169 | write_u32le(&mut dbuf[12..], size)?; |
| 170 | write_u32le(&mut dbuf[20..], size * 8)?; |
| 171 | |
| 172 | if is_intra { |
| 173 | dbuf.extend_from_slice(b"\x0d\x0aVer 3.99.00.00\x0d\x0a\x00"); |
| 174 | while (dbuf.len() & 3) != 0 { |
| 175 | dbuf.push(0); |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | Ok(()) |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | impl NAEncoder for Indeo3Encoder { |
| 184 | fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> { |
| 185 | match encinfo.format { |
| 186 | NACodecTypeInfo::None => { |
| 187 | Ok(EncodeParameters { |
| 188 | format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV410_FORMAT)), |
| 189 | ..Default::default() |
| 190 | }) |
| 191 | }, |
| 192 | NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), |
| 193 | NACodecTypeInfo::Video(vinfo) => { |
| 194 | let pix_fmt = YUV410_FORMAT; |
| 195 | let outinfo = NAVideoInfo::new((vinfo.width + 15) & !15, (vinfo.height + 15) & !15, false, pix_fmt); |
| 196 | let mut ofmt = *encinfo; |
| 197 | ofmt.format = NACodecTypeInfo::Video(outinfo); |
| 198 | Ok(ofmt) |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> { |
| 203 | match encinfo.format { |
| 204 | NACodecTypeInfo::None => Err(EncoderError::FormatError), |
| 205 | NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), |
| 206 | NACodecTypeInfo::Video(vinfo) => { |
| 207 | if vinfo.format != YUV410_FORMAT { |
| 208 | return Err(EncoderError::FormatError); |
| 209 | } |
| 210 | if ((vinfo.width | vinfo.height) & 15) != 0 { |
| 211 | return Err(EncoderError::FormatError); |
| 212 | } |
| 213 | if (vinfo.width > 640) || (vinfo.height > 480) { |
| 214 | return Err(EncoderError::FormatError); |
| 215 | } |
| 216 | |
| 217 | self.width = vinfo.width; |
| 218 | self.height = vinfo.height; |
| 219 | |
| 220 | let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format); |
| 221 | let info = NACodecInfo::new("indeo3", NACodecTypeInfo::Video(out_info), None); |
| 222 | let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); |
| 223 | stream.set_num(stream_id as usize); |
| 224 | let stream = stream.into_ref(); |
| 225 | |
| 226 | self.stream = Some(stream.clone()); |
| 227 | |
| 228 | self.cframe.alloc(vinfo.width, vinfo.height); |
| 229 | self.pframe.alloc(vinfo.width, vinfo.height); |
| 230 | |
| 231 | self.rc.set_bitrate(encinfo.bitrate, encinfo.tb_num, encinfo.tb_den); |
| 232 | self.rc.set_quality(encinfo.quality); |
| 233 | |
| 234 | Ok(stream) |
| 235 | }, |
| 236 | } |
| 237 | } |
| 238 | fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { |
| 239 | let buf = frm.get_buffer(); |
| 240 | if self.debug_tree || self.debug_frm { |
| 241 | println!("frame {}:", self.frameno); |
| 242 | } |
| 243 | |
| 244 | let mut skip_frame = frm.get_frame_type() == FrameType::Skip; |
| 245 | if let NABufferType::None = buf { |
| 246 | skip_frame = true; |
| 247 | } |
| 248 | if skip_frame { |
| 249 | let mut dbuf = Vec::with_capacity(16); |
| 250 | let mut gw = GrowableMemoryWriter::new_write(&mut dbuf); |
| 251 | let mut bw = ByteWriter::new(&mut gw); |
| 252 | |
| 253 | // OS header |
| 254 | bw.write_u32le(self.frameno)?; |
| 255 | bw.write_u32le(HDR_FIELD_2)?; |
| 256 | bw.write_u32le(0)?; // check |
| 257 | bw.write_u32le(0)?; // size |
| 258 | |
| 259 | // bitstream header |
| 260 | bw.write_u16le(32)?; // version |
| 261 | bw.write_u16le(0)?; |
| 262 | bw.write_u32le(0)?; // data size in bits |
| 263 | bw.write_byte(0)?; // cb offset |
| 264 | bw.write_byte(14)?; // reserved |
| 265 | bw.write_u16le(0)?; // checksum |
| 266 | bw.write_u16le(self.height as u16)?; |
| 267 | bw.write_u16le(self.width as u16)?; |
| 268 | |
| 269 | let size = (dbuf.len() - OS_HEADER_SIZE) as u32; |
| 270 | write_u32le(&mut dbuf[8..], self.frameno ^ HDR_FIELD_2 ^ FRMH_TAG ^ size)?; |
| 271 | write_u32le(&mut dbuf[12..], size)?; |
| 272 | write_u32le(&mut dbuf[20..], size * 8)?; |
| 273 | |
| 274 | let fsize = dbuf.len() as u32; |
| 275 | self.rc.advance(fsize); |
| 276 | |
| 277 | self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, false, dbuf)); |
| 278 | return Ok(()); |
| 279 | } |
| 280 | |
| 281 | if let Some(ref vbuf) = buf.get_vbuf() { |
| 282 | let mut dbuf = Vec::with_capacity(16); |
| 283 | let mut gw = GrowableMemoryWriter::new_write(&mut dbuf); |
| 284 | let mut bw = ByteWriter::new(&mut gw); |
| 285 | |
| 286 | let (width, height) = vbuf.get_dimensions(0); |
| 287 | if width != self.width || height != self.height { |
| 288 | self.width = width; |
| 289 | self.height = height; |
| 290 | self.cframe.alloc(width, height); |
| 291 | self.pframe.alloc(width, height); |
| 292 | self.rc.reset(); |
| 293 | } |
| 294 | |
| 295 | let (is_intra, quant) = self.rc.get_quant(self.frameno); |
| 296 | self.cenc.quant = quant; |
| 297 | |
| 298 | if is_intra { |
| 299 | self.buf_sel = false; |
| 300 | } else { |
| 301 | self.buf_sel = !self.buf_sel; |
| 302 | } |
| 303 | |
| 304 | self.cframe.fill(vbuf); |
| 305 | self.cframe.clear_mvs(); |
| 306 | |
| 307 | // OS header |
| 308 | bw.write_u32le(self.frameno)?; |
| 309 | bw.write_u32le(HDR_FIELD_2)?; |
| 310 | bw.write_u32le(0)?; // check |
| 311 | bw.write_u32le(0)?; // size |
| 312 | |
| 313 | // bitstream header |
| 314 | bw.write_u16le(32)?; // version |
| 315 | let mut flags = 0; |
| 316 | if is_intra { |
| 317 | flags |= 0x5; |
| 318 | } else { |
| 319 | flags |= 1; |
| 320 | if self.buf_sel { |
| 321 | flags |= 1 << 9; |
| 322 | } |
| 323 | } |
| 324 | bw.write_u16le(flags)?; |
| 325 | bw.write_u32le(0)?; // data size in bits |
| 326 | bw.write_byte(0)?; // cb offset |
| 327 | bw.write_byte(14)?; // reserved |
| 328 | bw.write_u16le(0)?; // checksum |
| 329 | bw.write_u16le(height as u16)?; |
| 330 | bw.write_u16le(width as u16)?; |
| 331 | for _ in 0..3 { |
| 332 | bw.write_u32le(0)?; // plane data offset |
| 333 | } |
| 334 | bw.write_u32le(0)?; // reserved |
| 335 | bw.write_buf(&CB_SELECTORS)?; |
| 336 | |
| 337 | let mut trees = Vec::with_capacity(PLANE_ORDER.len()); |
| 338 | |
| 339 | // prepare plane data structure |
| 340 | for &planeno in PLANE_ORDER.iter() { |
| 341 | let ref_plane = &self.pframe.plane[planeno]; |
| 342 | let mut tree = self.cframe.plane[planeno].find_cells(is_intra, ref_plane, &self.mv_est); |
| 343 | if self.debug_tree { |
| 344 | println!(" tree for plane {}:", planeno); |
| 345 | tree.print(); |
| 346 | } |
| 347 | let mvs = &mut self.cframe.plane[planeno].mvs; |
| 348 | if mvs.len() > 256 { |
| 349 | compact_mvs(mvs); |
| 350 | self.cframe.plane[planeno].prune_extra_mvs(&mut tree); |
| 351 | } |
| 352 | trees.push(tree); |
| 353 | } |
| 354 | |
| 355 | self.encode_planes(&mut dbuf, &trees, is_intra)?; |
| 356 | |
| 357 | let cur_quant = self.cenc.quant.unwrap_or(42); |
| 358 | if self.try_again && !is_intra && cur_quant < 8 { |
| 359 | let expected_size = self.rc.get_expected_size(); |
| 360 | if expected_size > 0 { |
| 361 | let cur_size = dbuf.len() as u32; |
| 362 | // try re-encoding frame if possible |
| 363 | if cur_size > expected_size * 3 / 2 { |
| 364 | self.cframe.fill(vbuf); |
| 365 | let new_quant = if cur_quant < 7 { |
| 366 | cur_quant + 1 |
| 367 | } else { |
| 368 | cur_quant - 1 |
| 369 | }; |
| 370 | self.cenc.quant = Some(new_quant); |
| 371 | dbuf.truncate(OS_HEADER_SIZE + BITSTREAM_HEADER_SIZE); |
| 372 | self.encode_planes(&mut dbuf, &trees, is_intra)?; |
| 373 | } |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | if self.debug_frm { |
| 378 | for plane in self.cframe.plane.iter() { |
| 379 | for (y, line) in plane.data.chunks(plane.width).enumerate() { |
| 380 | print!(" {:3}:", y); |
| 381 | for &el in line.iter() { print!(" {:02X}", el); } |
| 382 | println!(); |
| 383 | } |
| 384 | println!(); |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | std::mem::swap(&mut self.cframe, &mut self.pframe); |
| 389 | self.frameno += 1; |
| 390 | |
| 391 | let fsize = dbuf.len() as u32; |
| 392 | self.rc.advance(fsize); |
| 393 | |
| 394 | self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf)); |
| 395 | Ok(()) |
| 396 | } else { |
| 397 | Err(EncoderError::InvalidParameters) |
| 398 | } |
| 399 | } |
| 400 | fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> { |
| 401 | let mut npkt = None; |
| 402 | std::mem::swap(&mut self.pkt, &mut npkt); |
| 403 | Ok(npkt) |
| 404 | } |
| 405 | fn flush(&mut self) -> EncoderResult<()> { |
| 406 | Ok(()) |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | const DEBUG_TREE_OPTION: &str = "debug_tree"; |
| 411 | const DEBUG_FRAME_OPTION: &str = "debug_frame"; |
| 412 | const MV_RANGE_OPTION: &str = "mv_range"; |
| 413 | const MV_FLAT_OPTION: &str = "mv_flat_threshold"; |
| 414 | const MV_THRESHOLD_OPTION: &str = "mv_threshold"; |
| 415 | const CELL_I_THRESHOLD_OPTION: &str = "cell_i_threshold"; |
| 416 | const CELL_P_THRESHOLD_OPTION: &str = "cell_p_threshold"; |
| 417 | const DO_RLE_OPTION: &str = "rle"; |
| 418 | const TRY_AGAIN_OPTION: &str = "try_recompress"; |
| 419 | |
| 420 | const ENCODER_OPTS: &[NAOptionDefinition] = &[ |
| 421 | NAOptionDefinition { |
| 422 | name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC, |
| 423 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, |
| 424 | NAOptionDefinition { |
| 425 | name: DEBUG_TREE_OPTION, description: "Print frame trees", |
| 426 | opt_type: NAOptionDefinitionType::Bool }, |
| 427 | NAOptionDefinition { |
| 428 | name: DEBUG_FRAME_OPTION, description: "Print encoder-reconstructed frames", |
| 429 | opt_type: NAOptionDefinitionType::Bool }, |
| 430 | NAOptionDefinition { |
| 431 | name: MV_RANGE_OPTION, description: "Motion search range", |
| 432 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(120)) }, |
| 433 | NAOptionDefinition { |
| 434 | name: MV_FLAT_OPTION, description: "Threshold for coding cell as skipped one", |
| 435 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(1000)) }, |
| 436 | NAOptionDefinition { |
| 437 | name: MV_THRESHOLD_OPTION, description: "Threshold for coding cell as inter", |
| 438 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(1000)) }, |
| 439 | NAOptionDefinition { |
| 440 | name: CELL_I_THRESHOLD_OPTION, description: "Threshold for coding intra block as flat", |
| 441 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, |
| 442 | NAOptionDefinition { |
| 443 | name: CELL_P_THRESHOLD_OPTION, description: "Threshold for coding inter cell in coarser mode", |
| 444 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, |
| 445 | NAOptionDefinition { |
| 446 | name: DO_RLE_OPTION, description: "Perform zero run length compation", |
| 447 | opt_type: NAOptionDefinitionType::Bool }, |
| 448 | NAOptionDefinition { |
| 449 | name: TRY_AGAIN_OPTION, description: "Try compressing the frame again for the better bitrate fit", |
| 450 | opt_type: NAOptionDefinitionType::Bool }, |
| 451 | ]; |
| 452 | |
| 453 | impl NAOptionHandler for Indeo3Encoder { |
| 454 | fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS } |
| 455 | fn set_options(&mut self, options: &[NAOption]) { |
| 456 | for option in options.iter() { |
| 457 | for opt_def in ENCODER_OPTS.iter() { |
| 458 | if opt_def.check(option).is_ok() { |
| 459 | match option.name { |
| 460 | KEYFRAME_OPTION => { |
| 461 | if let NAValue::Int(val) = option.value { |
| 462 | self.rc.set_key_int(val as u32); |
| 463 | } |
| 464 | }, |
| 465 | DEBUG_TREE_OPTION => { |
| 466 | if let NAValue::Bool(val) = option.value { |
| 467 | self.debug_tree = val; |
| 468 | } |
| 469 | }, |
| 470 | DEBUG_FRAME_OPTION => { |
| 471 | if let NAValue::Bool(val) = option.value { |
| 472 | self.debug_frm = val; |
| 473 | } |
| 474 | }, |
| 475 | MV_RANGE_OPTION => { |
| 476 | if let NAValue::Int(val) = option.value { |
| 477 | self.mv_est.mv_range = val as i8; |
| 478 | } |
| 479 | }, |
| 480 | MV_FLAT_OPTION => { |
| 481 | if let NAValue::Int(val) = option.value { |
| 482 | self.mv_est.flat_thr = val as u16; |
| 483 | } |
| 484 | }, |
| 485 | MV_THRESHOLD_OPTION => { |
| 486 | if let NAValue::Int(val) = option.value { |
| 487 | self.mv_est.mv_thr = val as u16; |
| 488 | } |
| 489 | }, |
| 490 | CELL_I_THRESHOLD_OPTION => { |
| 491 | if let NAValue::Int(val) = option.value { |
| 492 | self.cenc.flat_thr_i = val as u32; |
| 493 | } |
| 494 | }, |
| 495 | CELL_P_THRESHOLD_OPTION => { |
| 496 | if let NAValue::Int(val) = option.value { |
| 497 | self.cenc.flat_thr_p = val as u32; |
| 498 | } |
| 499 | }, |
| 500 | DO_RLE_OPTION => { |
| 501 | if let NAValue::Bool(val) = option.value { |
| 502 | self.cenc.do_rle = val; |
| 503 | } |
| 504 | }, |
| 505 | TRY_AGAIN_OPTION => { |
| 506 | if let NAValue::Bool(val) = option.value { |
| 507 | self.try_again = val; |
| 508 | } |
| 509 | }, |
| 510 | _ => {}, |
| 511 | }; |
| 512 | } |
| 513 | } |
| 514 | } |
| 515 | } |
| 516 | fn query_option_value(&self, name: &str) -> Option<NAValue> { |
| 517 | match name { |
| 518 | KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.rc.get_key_int()))), |
| 519 | DEBUG_TREE_OPTION => Some(NAValue::Bool(self.debug_tree)), |
| 520 | DEBUG_FRAME_OPTION => Some(NAValue::Bool(self.debug_frm)), |
| 521 | MV_RANGE_OPTION => Some(NAValue::Int(i64::from(self.mv_est.mv_range))), |
| 522 | MV_FLAT_OPTION => Some(NAValue::Int(i64::from(self.mv_est.flat_thr))), |
| 523 | MV_THRESHOLD_OPTION => Some(NAValue::Int(i64::from(self.mv_est.mv_thr))), |
| 524 | CELL_I_THRESHOLD_OPTION => Some(NAValue::Int(i64::from(self.cenc.flat_thr_i))), |
| 525 | CELL_P_THRESHOLD_OPTION => Some(NAValue::Int(i64::from(self.cenc.flat_thr_p))), |
| 526 | DO_RLE_OPTION => Some(NAValue::Bool(self.cenc.do_rle)), |
| 527 | TRY_AGAIN_OPTION => Some(NAValue::Bool(self.try_again)), |
| 528 | _ => None, |
| 529 | } |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | pub fn get_encoder() -> Box<dyn NAEncoder + Send> { |
| 534 | Box::new(Indeo3Encoder::new()) |
| 535 | } |
| 536 | |
| 537 | #[cfg(test)] |
| 538 | mod test { |
| 539 | use crate::*; |
| 540 | use nihav_core::codecs::*; |
| 541 | use nihav_core::demuxers::*; |
| 542 | use nihav_core::muxers::*; |
| 543 | use nihav_commonfmt::*; |
| 544 | use nihav_codec_support::test::enc_video::*; |
| 545 | |
| 546 | #[allow(unused_variables)] |
| 547 | fn encode_test(name: &'static str, enc_options: &[NAOption], limit: Option<u64>, hash: &[u32; 4]) { |
| 548 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 549 | generic_register_all_demuxers(&mut dmx_reg); |
| 550 | let mut dec_reg = RegisteredDecoders::new(); |
| 551 | indeo_register_all_decoders(&mut dec_reg); |
| 552 | let mut mux_reg = RegisteredMuxers::new(); |
| 553 | generic_register_all_muxers(&mut mux_reg); |
| 554 | let mut enc_reg = RegisteredEncoders::new(); |
| 555 | indeo_register_all_encoders(&mut enc_reg); |
| 556 | |
| 557 | let dec_config = DecoderTestParams { |
| 558 | demuxer: "avi", |
| 559 | in_name: "assets/Indeo/laser05.avi", |
| 560 | stream_type: StreamType::Video, |
| 561 | limit, |
| 562 | dmx_reg, dec_reg, |
| 563 | }; |
| 564 | let enc_config = EncoderTestParams { |
| 565 | muxer: "avi", |
| 566 | enc_name: "indeo3", |
| 567 | out_name: name, |
| 568 | mux_reg, enc_reg, |
| 569 | }; |
| 570 | let dst_vinfo = NAVideoInfo { |
| 571 | width: 0, |
| 572 | height: 0, |
| 573 | format: YUV410_FORMAT, |
| 574 | flipped: false, |
| 575 | bits: 9, |
| 576 | }; |
| 577 | let enc_params = EncodeParameters { |
| 578 | format: NACodecTypeInfo::Video(dst_vinfo), |
| 579 | quality: 0, |
| 580 | bitrate: 25000 * 8, |
| 581 | tb_num: 0, |
| 582 | tb_den: 0, |
| 583 | flags: 0, |
| 584 | }; |
| 585 | //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); |
| 586 | test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, hash); |
| 587 | } |
| 588 | #[test] |
| 589 | fn test_indeo3_encoder1() { |
| 590 | let enc_options = &[ |
| 591 | NAOption { name: super::TRY_AGAIN_OPTION, value: NAValue::Bool(true) }, |
| 592 | ]; |
| 593 | encode_test("indeo3.avi", enc_options, Some(4), &[0x17d742bc, 0x6f4c1200, 0x79422bac, 0xc46b5dd0]); |
| 594 | } |
| 595 | /*#[test] |
| 596 | fn test_indeo3_roundtrip() { |
| 597 | const YPATTERN: [u8; 16] = [32, 72, 40, 106, 80, 20, 33, 58, 77, 140, 121, 100, 83, 57, 30, 11]; |
| 598 | const CPATTERN: [u8; 4] = [0x80; 4]; |
| 599 | |
| 600 | let dst_vinfo = NAVideoInfo { |
| 601 | width: 16, |
| 602 | height: 16, |
| 603 | format: YUV410_FORMAT, |
| 604 | flipped: false, |
| 605 | bits: 9, |
| 606 | }; |
| 607 | let enc_params = EncodeParameters { |
| 608 | format: NACodecTypeInfo::Video(dst_vinfo), |
| 609 | quality: 0, |
| 610 | bitrate: 0, |
| 611 | tb_num: 0, |
| 612 | tb_den: 0, |
| 613 | flags: 0, |
| 614 | }; |
| 615 | |
| 616 | let mut ienc = super::get_encoder(); |
| 617 | ienc.init(0, enc_params).unwrap(); |
| 618 | let mut buffer = alloc_video_buffer(dst_vinfo, 2).unwrap(); |
| 619 | if let NABufferType::Video(ref mut buf) = buffer { |
| 620 | let vbuf = NASimpleVideoFrame::from_video_buf(buf).unwrap(); |
| 621 | for i in 0..16 { |
| 622 | vbuf.data[vbuf.offset[0] + i * vbuf.stride[0]..][..16].copy_from_slice(&YPATTERN); |
| 623 | } |
| 624 | for plane in 1..3 { |
| 625 | for i in 0..4 { |
| 626 | vbuf.data[vbuf.offset[plane] + i * vbuf.stride[plane]..][..4].copy_from_slice(&CPATTERN); |
| 627 | } |
| 628 | } |
| 629 | } |
| 630 | let info = NACodecInfo::new("indeo3", NACodecTypeInfo::Video(dst_vinfo), None).into_ref(); |
| 631 | let frm = NAFrame::new(NATimeInfo::new(Some(0), None, None, 1, 12), FrameType::I, true, info.clone(), buffer); |
| 632 | //ienc.set_options(&[NAOption{ name: super::DEBUG_FRAME_OPTION, value: NAValue::Bool(true) }]); |
| 633 | ienc.encode(&frm).unwrap(); |
| 634 | let pkt = ienc.get_packet().unwrap().unwrap(); |
| 635 | println!(" pkt size {}", pkt.get_buffer().len()); |
| 636 | |
| 637 | let mut dec_reg = RegisteredDecoders::new(); |
| 638 | indeo_register_all_decoders(&mut dec_reg); |
| 639 | let decfunc = dec_reg.find_decoder("indeo3").unwrap(); |
| 640 | let mut dec = (decfunc)(); |
| 641 | let mut dsupp = Box::new(NADecoderSupport::new()); |
| 642 | dec.init(&mut dsupp, info).unwrap(); |
| 643 | dec.set_options(&[NAOption{ name: "checksum", value: NAValue::Bool(true) }]); |
| 644 | let dst = dec.decode(&mut dsupp, &pkt).unwrap(); |
| 645 | if let NABufferType::Video(ref vbuf) = dst.get_buffer() { |
| 646 | for plane in 0..3 { |
| 647 | let size = if plane == 0 { 16 } else { 4 }; |
| 648 | let start = vbuf.get_offset(plane); |
| 649 | for line in vbuf.get_data()[start..].chunks(vbuf.get_stride(plane)).take(size) { |
| 650 | print!(" "); |
| 651 | for &el in line[..size].iter() { |
| 652 | print!(" {:02X}", el >> 1); |
| 653 | } |
| 654 | println!(); |
| 655 | } |
| 656 | if plane == 0 { |
| 657 | print!("ref"); |
| 658 | for &el in YPATTERN.iter() { print!(" {:02X}", el >> 1); } println!(); |
| 659 | } |
| 660 | println!(); |
| 661 | } |
| 662 | } |
| 663 | panic!("end"); |
| 664 | }*/ |
| 665 | } |