| 1 | use nihav_core::codecs::*; |
| 2 | use nihav_core::io::byteio::*; |
| 3 | |
| 4 | mod blocks; |
| 5 | mod coder; |
| 6 | use coder::*; |
| 7 | mod frame_coder; |
| 8 | use frame_coder::*; |
| 9 | mod mb_coding; |
| 10 | mod models; |
| 11 | use models::*; |
| 12 | mod motion_est; |
| 13 | use motion_est::MVSearchMode; |
| 14 | mod rdo; |
| 15 | use rdo::*; |
| 16 | |
| 17 | #[derive(PartialEq,Debug)] |
| 18 | enum EncodingState { |
| 19 | Intra, |
| 20 | Refinement, |
| 21 | JustEncode, |
| 22 | } |
| 23 | |
| 24 | #[allow(dead_code)] |
| 25 | struct VP7Encoder { |
| 26 | stream: Option<NAStreamRef>, |
| 27 | pkt: Option<NAPacket>, |
| 28 | version: u8, |
| 29 | key_int: u8, |
| 30 | frmcount: u8, |
| 31 | width: usize, |
| 32 | height: usize, |
| 33 | mb_w: usize, |
| 34 | mb_h: usize, |
| 35 | |
| 36 | metric: RateDistMetric, |
| 37 | fenc: FrameEncoder, |
| 38 | pmodels: VP7Models, |
| 39 | br_ctl: BitRateControl, |
| 40 | |
| 41 | last_frame: NABufferType, |
| 42 | gold_frame: NABufferType, |
| 43 | last_gold: bool, |
| 44 | me_mode: MVSearchMode, |
| 45 | me_range: i16, |
| 46 | lf_level: Option<u8>, |
| 47 | |
| 48 | mbt_depth: usize, |
| 49 | mb_weight: Vec<usize>, |
| 50 | mb_map: Vec<usize>, |
| 51 | mb_map2: Vec<usize>, |
| 52 | qframes: Vec<NAFrame>, |
| 53 | frm_pool: NAVideoBufferPool<u8>, |
| 54 | i_frame: NAVideoBufferRef<u8>, |
| 55 | enc_state: EncodingState, |
| 56 | } |
| 57 | |
| 58 | impl VP7Encoder { |
| 59 | fn new() -> Self { |
| 60 | let vt = alloc_video_buffer(NAVideoInfo::new(24, 24, false, YUV420_FORMAT), 4).unwrap(); |
| 61 | let mc_buf1 = vt.get_vbuf().unwrap(); |
| 62 | let vt = alloc_video_buffer(NAVideoInfo::new(24, 24, false, YUV420_FORMAT), 4).unwrap(); |
| 63 | let mc_buf2 = vt.get_vbuf().unwrap(); |
| 64 | let vt = alloc_video_buffer(NAVideoInfo::new(24, 24, false, YUV420_FORMAT), 4).unwrap(); |
| 65 | let i_frame = vt.get_vbuf().unwrap(); |
| 66 | Self { |
| 67 | stream: None, |
| 68 | pkt: None, |
| 69 | version: 0, |
| 70 | key_int: 10, |
| 71 | frmcount: 0, |
| 72 | width: 0, |
| 73 | height: 0, |
| 74 | mb_w: 0, |
| 75 | mb_h: 0, |
| 76 | |
| 77 | metric: RateDistMetric::new(), |
| 78 | fenc: FrameEncoder::new(mc_buf1, mc_buf2), |
| 79 | pmodels: VP7Models::new(), |
| 80 | br_ctl: BitRateControl::new(), |
| 81 | |
| 82 | last_frame: NABufferType::None, |
| 83 | gold_frame: NABufferType::None, |
| 84 | last_gold: false, |
| 85 | me_mode: MVSearchMode::default(), |
| 86 | me_range: 16, |
| 87 | lf_level: None, |
| 88 | |
| 89 | mbt_depth: 0, |
| 90 | mb_weight: Vec::new(), |
| 91 | mb_map: Vec::new(), |
| 92 | mb_map2: Vec::new(), |
| 93 | qframes: Vec::new(), |
| 94 | frm_pool: NAVideoBufferPool::new(0), |
| 95 | i_frame, |
| 96 | enc_state: EncodingState::JustEncode, |
| 97 | } |
| 98 | } |
| 99 | fn encode_frame(&mut self, frm: &NAFrame) -> EncoderResult<()> { |
| 100 | let buf = frm.get_buffer(); |
| 101 | if let Some(ref vbuf) = buf.get_vbuf() { |
| 102 | self.fenc.set_me_params(self.me_mode, self.me_range, self.version); |
| 103 | self.fenc.load_frame(vbuf); |
| 104 | |
| 105 | let mut dbuf = Vec::with_capacity(4); |
| 106 | let mut gw = GrowableMemoryWriter::new_write(&mut dbuf); |
| 107 | let mut bw = ByteWriter::new(&mut gw); |
| 108 | |
| 109 | let is_intra = self.frmcount == 0; |
| 110 | let golden_frame = is_intra; |
| 111 | |
| 112 | self.br_ctl.set_key_interval(self.key_int); |
| 113 | let cur_quant = self.br_ctl.get_frame_quant(is_intra); |
| 114 | |
| 115 | if let Some(level) = self.lf_level { |
| 116 | self.fenc.loop_params.loop_filter_level = level; |
| 117 | } else { |
| 118 | self.fenc.loop_params.loop_filter_level = if cur_quant <= 16 { 0 } else { (cur_quant / 4) as u8 }; |
| 119 | } |
| 120 | |
| 121 | if is_intra { |
| 122 | self.pmodels.reset(); |
| 123 | let mbt_frames = self.mbt_depth.min(self.key_int as usize); |
| 124 | self.fenc.intra_blocks(cur_quant, &self.metric, &self.pmodels, if mbt_frames > 0 { Some(&self.mb_weight) } else { None }); |
| 125 | } else { |
| 126 | let gold_ref = if !self.last_gold { &self.gold_frame } else { &NABufferType::None }; |
| 127 | self.fenc.inter_blocks(cur_quant, &self.metric, &self.pmodels, &self.last_frame, gold_ref); |
| 128 | } |
| 129 | |
| 130 | let mut stats = VP7ModelsStat::new(); |
| 131 | let mut models = self.pmodels; |
| 132 | self.fenc.generate_models(is_intra, &mut stats); |
| 133 | stats.generate(&mut models, is_intra); |
| 134 | |
| 135 | bw.write_u24le(0)?; // frame tag |
| 136 | if self.version == 0 { |
| 137 | bw.write_byte(0)?; // unused |
| 138 | } |
| 139 | |
| 140 | let start = bw.tell(); |
| 141 | |
| 142 | let mut bc = BoolEncoder::new(&mut bw); |
| 143 | if is_intra { |
| 144 | bc.put_bits(self.width as u32, 12)?; |
| 145 | bc.put_bits(self.height as u32, 12)?; |
| 146 | bc.put_bits(0, 2)?; // scale vertical |
| 147 | bc.put_bits(0, 2)?; // scale horizontal |
| 148 | } |
| 149 | |
| 150 | self.fenc.encode_features(&mut bc, cur_quant, &models)?; |
| 151 | |
| 152 | bc.put_bits(cur_quant as u32, 7)?; // y_ac_q |
| 153 | bc.put_bool(false, 128)?; // y_dc_q |
| 154 | bc.put_bool(false, 128)?; // y2_ac_q |
| 155 | bc.put_bool(false, 128)?; // y2_dc_q |
| 156 | bc.put_bool(false, 128)?; // uv_ac_q |
| 157 | bc.put_bool(false, 128)?; // uv_dc_q |
| 158 | |
| 159 | if !is_intra { |
| 160 | bc.put_bool(false, 128)?; // update golden frame |
| 161 | } |
| 162 | |
| 163 | let has_fading = self.version == 0 || is_intra; |
| 164 | if self.version != 0 { |
| 165 | bc.put_bool(true, 128)?; // keep probabilities |
| 166 | if !is_intra { |
| 167 | bc.put_bool(false, 128)?; // has fading feature |
| 168 | } |
| 169 | } |
| 170 | if has_fading { |
| 171 | bc.put_bool(false, 128)?; // fading |
| 172 | } |
| 173 | |
| 174 | if self.version == 0 { |
| 175 | bc.put_bool(self.fenc.loop_params.lf_simple, 128)?; |
| 176 | } |
| 177 | |
| 178 | // scan |
| 179 | bc.put_bool(false, 128)?; |
| 180 | |
| 181 | if self.version != 0 { |
| 182 | bc.put_bool(self.fenc.loop_params.lf_simple, 128)?; |
| 183 | } |
| 184 | |
| 185 | bc.put_bits(u32::from(self.fenc.loop_params.loop_filter_level), 6)?; |
| 186 | bc.put_bits(u32::from(self.fenc.loop_params.loop_sharpness), 3)?; |
| 187 | |
| 188 | encode_dct_coef_prob_upd(&mut bc, &models.coef_probs, &self.pmodels.coef_probs)?; |
| 189 | |
| 190 | if !is_intra { |
| 191 | bc.put_byte(models.prob_intra_pred)?; |
| 192 | bc.put_byte(models.prob_last_pred)?; |
| 193 | |
| 194 | let ymode_differs = models.kf_ymode_prob != self.pmodels.kf_ymode_prob; |
| 195 | bc.put_bool(ymode_differs, 128)?; |
| 196 | if ymode_differs { |
| 197 | for &el in models.kf_ymode_prob.iter() { |
| 198 | bc.put_byte(el)?; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | let uvmode_differs = models.kf_uvmode_prob != self.pmodels.kf_uvmode_prob; |
| 203 | bc.put_bool(uvmode_differs, 128)?; |
| 204 | if uvmode_differs { |
| 205 | for &el in models.kf_uvmode_prob.iter() { |
| 206 | bc.put_byte(el)?; |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | encode_mv_prob_upd(&mut bc, &models.mv_probs, &self.pmodels.mv_probs)?; |
| 211 | } |
| 212 | |
| 213 | self.fenc.encode_mb_types(&mut bc, is_intra, &models)?; |
| 214 | |
| 215 | bc.flush()?; |
| 216 | let end = bw.tell(); |
| 217 | |
| 218 | let mut bc = BoolEncoder::new(&mut bw); |
| 219 | self.fenc.encode_residues(&mut bc, &models)?; |
| 220 | bc.flush()?; |
| 221 | |
| 222 | bw.seek(SeekFrom::Start(0))?; |
| 223 | bw.write_u24le((((end - start) as u32) << 4) | |
| 224 | (u32::from(self.version) << 1) | |
| 225 | if is_intra { 0 } else { 1 })?; |
| 226 | |
| 227 | let cur_size = dbuf.len(); |
| 228 | |
| 229 | self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf)); |
| 230 | |
| 231 | self.pmodels = models; |
| 232 | |
| 233 | if self.key_int > 0 { |
| 234 | self.frmcount += 1; |
| 235 | } |
| 236 | if self.frmcount == self.key_int { |
| 237 | self.frmcount = 0; |
| 238 | } |
| 239 | |
| 240 | if let Some(ref mut vbuf) = self.last_frame.get_vbuf() { |
| 241 | let mut frm = NASimpleVideoFrame::from_video_buf(vbuf).unwrap(); |
| 242 | self.fenc.reconstruct_frame(&mut frm, is_intra); |
| 243 | } |
| 244 | self.last_gold = golden_frame; |
| 245 | if golden_frame { |
| 246 | let mut dfrm = self.gold_frame.get_vbuf().unwrap(); |
| 247 | let src = self.last_frame.get_vbuf().unwrap(); |
| 248 | |
| 249 | let dst = dfrm.get_data_mut().unwrap(); |
| 250 | dst.copy_from_slice(src.get_data()); |
| 251 | } |
| 252 | |
| 253 | self.br_ctl.update(cur_size); |
| 254 | if self.br_ctl.has_bitrate() { |
| 255 | let tgt_size = (self.br_ctl.get_target_size(is_intra) / 8) as usize; |
| 256 | self.metric.adjust_br(cur_size, tgt_size); |
| 257 | } |
| 258 | |
| 259 | Ok(()) |
| 260 | } else { |
| 261 | Err(EncoderError::InvalidParameters) |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | impl NAEncoder for VP7Encoder { |
| 267 | fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> { |
| 268 | match encinfo.format { |
| 269 | NACodecTypeInfo::None => { |
| 270 | let mut ofmt = EncodeParameters::default(); |
| 271 | ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); |
| 272 | Ok(ofmt) |
| 273 | }, |
| 274 | NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), |
| 275 | NACodecTypeInfo::Video(vinfo) => { |
| 276 | let outinfo = NAVideoInfo::new((vinfo.width + 15) & !15, (vinfo.height + 15) & !15, false, YUV420_FORMAT); |
| 277 | let mut ofmt = *encinfo; |
| 278 | ofmt.format = NACodecTypeInfo::Video(outinfo); |
| 279 | Ok(ofmt) |
| 280 | } |
| 281 | } |
| 282 | } |
| 283 | fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> { |
| 284 | match encinfo.format { |
| 285 | NACodecTypeInfo::None => Err(EncoderError::FormatError), |
| 286 | NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), |
| 287 | NACodecTypeInfo::Video(vinfo) => { |
| 288 | if vinfo.format != YUV420_FORMAT { |
| 289 | return Err(EncoderError::FormatError); |
| 290 | } |
| 291 | if ((vinfo.width | vinfo.height) & 15) != 0 { |
| 292 | return Err(EncoderError::FormatError); |
| 293 | } |
| 294 | if (vinfo.width | vinfo.height) >= (1 << 12) { |
| 295 | return Err(EncoderError::FormatError); |
| 296 | } |
| 297 | |
| 298 | let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format); |
| 299 | let info = NACodecInfo::new("vp7", NACodecTypeInfo::Video(out_info), None); |
| 300 | let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); |
| 301 | stream.set_num(stream_id as usize); |
| 302 | let stream = stream.into_ref(); |
| 303 | |
| 304 | self.last_frame = alloc_video_buffer(out_info, 4)?; |
| 305 | self.gold_frame = alloc_video_buffer(out_info, 4)?; |
| 306 | |
| 307 | self.stream = Some(stream.clone()); |
| 308 | |
| 309 | self.width = vinfo.width; |
| 310 | self.height = vinfo.height; |
| 311 | self.mb_w = (vinfo.width + 15) >> 4; |
| 312 | self.mb_h = (vinfo.height + 15) >> 4; |
| 313 | self.fenc.resize(self.mb_w, self.mb_h); |
| 314 | |
| 315 | self.br_ctl.set_params(encinfo.tb_num, encinfo.tb_den, encinfo.bitrate, self.key_int, self.mb_w * self.mb_h); |
| 316 | |
| 317 | self.frm_pool.reset(); |
| 318 | self.frm_pool.set_dec_bufs(self.mbt_depth + 1); |
| 319 | self.frm_pool.prealloc_video(out_info, 4)?; |
| 320 | self.i_frame = self.frm_pool.get_free().unwrap(); |
| 321 | self.mb_weight.resize(self.mb_w * self.mb_h, 0); |
| 322 | self.mb_map.resize(self.mb_w * self.mb_h, 0); |
| 323 | self.mb_map2.resize(self.mb_w * self.mb_h, 0); |
| 324 | self.qframes.clear(); |
| 325 | self.enc_state = if self.mbt_depth.min(self.key_int as usize) > 0 { |
| 326 | EncodingState::Intra |
| 327 | } else { |
| 328 | EncodingState::JustEncode |
| 329 | }; |
| 330 | |
| 331 | Ok(stream) |
| 332 | }, |
| 333 | } |
| 334 | } |
| 335 | fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { |
| 336 | if let Some(ref vbuf) = frm.get_buffer().get_vbuf() { |
| 337 | let mbt_frames = self.mbt_depth.min(self.key_int as usize); |
| 338 | if !self.qframes.is_empty() || (mbt_frames > 0 && self.enc_state != EncodingState::JustEncode) { |
| 339 | if let Some(dbuf) = self.frm_pool.get_copy(vbuf) { |
| 340 | let newfrm = NAFrame::new(frm.ts, frm.frame_type, frm.key, frm.get_info(), NABufferType::Video(dbuf)); |
| 341 | if self.enc_state == EncodingState::Intra { |
| 342 | for (i, el) in self.mb_map.iter_mut().enumerate() { |
| 343 | *el = i; |
| 344 | } |
| 345 | for el in self.mb_weight.iter_mut() { |
| 346 | *el = 1; |
| 347 | } |
| 348 | let frm = NASimpleVideoFrame::from_video_buf(&mut self.i_frame).unwrap(); |
| 349 | let src = vbuf.get_data(); |
| 350 | for plane in 0..3 { |
| 351 | let soff = vbuf.get_offset(plane); |
| 352 | let sstride = vbuf.get_stride(plane); |
| 353 | let copy_len = sstride.min(frm.stride[plane]); |
| 354 | for (dst, src) in frm.data[frm.offset[plane]..].chunks_mut(frm.stride[plane]).zip(src[soff..].chunks(sstride)).take(frm.height[plane]) { |
| 355 | dst[..copy_len].copy_from_slice(&src[..copy_len]); |
| 356 | } |
| 357 | } |
| 358 | self.enc_state = EncodingState::Refinement; |
| 359 | } else { |
| 360 | self.fenc.set_me_params(self.me_mode, self.me_range, self.version); |
| 361 | self.fenc.load_frame(vbuf); |
| 362 | self.fenc.mb_tree_search(self.i_frame.clone(), &self.mb_map, &mut self.mb_map2, &mut self.mb_weight); |
| 363 | std::mem::swap(&mut self.mb_map, &mut self.mb_map2); |
| 364 | } |
| 365 | self.qframes.push(newfrm); |
| 366 | Ok(()) |
| 367 | } else { |
| 368 | self.enc_state = EncodingState::JustEncode; |
| 369 | self.encode_frame(frm) |
| 370 | } |
| 371 | } else { |
| 372 | self.encode_frame(frm) |
| 373 | } |
| 374 | } else { |
| 375 | Err(EncoderError::FormatError) |
| 376 | } |
| 377 | } |
| 378 | fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> { |
| 379 | let mbt_frames = self.mbt_depth.min(self.key_int as usize); |
| 380 | if self.qframes.len() >= mbt_frames { |
| 381 | self.enc_state = EncodingState::JustEncode; |
| 382 | } |
| 383 | if self.pkt.is_none() && !self.qframes.is_empty() && self.enc_state == EncodingState::JustEncode { |
| 384 | let frm = self.qframes.remove(0); |
| 385 | self.encode_frame(&frm)?; |
| 386 | if self.qframes.is_empty() && self.mbt_depth > 0 && self.frmcount == 0 { |
| 387 | self.enc_state = EncodingState::Intra; |
| 388 | } |
| 389 | } |
| 390 | let mut npkt = None; |
| 391 | std::mem::swap(&mut self.pkt, &mut npkt); |
| 392 | Ok(npkt) |
| 393 | } |
| 394 | fn flush(&mut self) -> EncoderResult<()> { |
| 395 | self.frmcount = 0; |
| 396 | self.enc_state = EncodingState::JustEncode; |
| 397 | Ok(()) |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | const VERSION_OPTION: &str = "version"; |
| 402 | const LF_LEVEL_OPTION: &str = "lf_level"; |
| 403 | const LF_SHARP_OPTION: &str = "lf_sharpness"; |
| 404 | const LF_SIMPLE_OPTION: &str = "lf_simple"; |
| 405 | const QUANT_OPTION: &str = "quant"; |
| 406 | const MV_SEARCH_OPTION: &str = "mv_mode"; |
| 407 | const MV_RANGE_OPTION: &str = "mv_range"; |
| 408 | const MBTREE_DEPTH: &str = "mbtree_depth"; |
| 409 | |
| 410 | const ENCODER_OPTS: &[NAOptionDefinition] = &[ |
| 411 | NAOptionDefinition { |
| 412 | name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC, |
| 413 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, |
| 414 | NAOptionDefinition { |
| 415 | name: VERSION_OPTION, description: "internal codec version", |
| 416 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(1)) }, |
| 417 | NAOptionDefinition { |
| 418 | name: LF_LEVEL_OPTION, description: "loop filter level (-1 = automatic)", |
| 419 | opt_type: NAOptionDefinitionType::Int(Some(-1), Some(63)) }, |
| 420 | NAOptionDefinition { |
| 421 | name: LF_SHARP_OPTION, description: "loop filter sharpness", |
| 422 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(7)) }, |
| 423 | NAOptionDefinition { |
| 424 | name: LF_SIMPLE_OPTION, description: "use simple loop filter", |
| 425 | opt_type: NAOptionDefinitionType::Bool }, |
| 426 | NAOptionDefinition { |
| 427 | name: QUANT_OPTION, description: "force fixed quantiser for encoding", |
| 428 | opt_type: NAOptionDefinitionType::Int(Some(-1), Some(127)) }, |
| 429 | NAOptionDefinition { |
| 430 | name: MV_SEARCH_OPTION, description: "motion search mode", |
| 431 | opt_type: NAOptionDefinitionType::String(Some(&["sea", "dia", "hex", "epzs"])) }, |
| 432 | NAOptionDefinition { |
| 433 | name: MV_RANGE_OPTION, description: "motion search range (in pixels)", |
| 434 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(30)) }, |
| 435 | NAOptionDefinition { |
| 436 | name: MBTREE_DEPTH, description: "number of frames in MB tree analysis buffer", |
| 437 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, |
| 438 | ]; |
| 439 | |
| 440 | impl NAOptionHandler for VP7Encoder { |
| 441 | fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS } |
| 442 | fn set_options(&mut self, options: &[NAOption]) { |
| 443 | for option in options.iter() { |
| 444 | for opt_def in ENCODER_OPTS.iter() { |
| 445 | if opt_def.check(option).is_ok() { |
| 446 | match option.name { |
| 447 | KEYFRAME_OPTION => { |
| 448 | if let NAValue::Int(intval) = option.value { |
| 449 | self.key_int = intval as u8; |
| 450 | } |
| 451 | }, |
| 452 | VERSION_OPTION => { |
| 453 | if let NAValue::Int(intval) = option.value { |
| 454 | self.version = intval as u8; |
| 455 | } |
| 456 | }, |
| 457 | LF_LEVEL_OPTION => { |
| 458 | if let NAValue::Int(intval) = option.value { |
| 459 | self.lf_level = if intval < 0 { None } else { Some(intval as u8) }; |
| 460 | } |
| 461 | }, |
| 462 | LF_SHARP_OPTION => { |
| 463 | if let NAValue::Int(intval) = option.value { |
| 464 | self.fenc.loop_params.loop_sharpness = intval as u8; |
| 465 | } |
| 466 | }, |
| 467 | LF_SIMPLE_OPTION => { |
| 468 | if let NAValue::Bool(flag) = option.value { |
| 469 | self.fenc.loop_params.lf_simple = flag; |
| 470 | } |
| 471 | }, |
| 472 | QUANT_OPTION => { |
| 473 | if let NAValue::Int(intval) = option.value { |
| 474 | self.br_ctl.set_quant(if intval < 0 { None } else { Some(intval as usize) }); |
| 475 | } |
| 476 | }, |
| 477 | MV_SEARCH_OPTION => { |
| 478 | if let NAValue::String(ref string) = option.value { |
| 479 | if let Ok(mv_mode) = string.parse::<MVSearchMode>() { |
| 480 | self.me_mode = mv_mode; |
| 481 | } |
| 482 | } |
| 483 | }, |
| 484 | MV_RANGE_OPTION => { |
| 485 | if let NAValue::Int(intval) = option.value { |
| 486 | self.me_range = intval as i16; |
| 487 | } |
| 488 | }, |
| 489 | MBTREE_DEPTH => { |
| 490 | if let NAValue::Int(intval) = option.value { |
| 491 | self.mbt_depth = intval as usize; |
| 492 | } |
| 493 | }, |
| 494 | _ => {}, |
| 495 | }; |
| 496 | } |
| 497 | } |
| 498 | } |
| 499 | } |
| 500 | fn query_option_value(&self, name: &str) -> Option<NAValue> { |
| 501 | match name { |
| 502 | KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))), |
| 503 | VERSION_OPTION => Some(NAValue::Int(i64::from(self.version))), |
| 504 | QUANT_OPTION => if let Some(q) = self.br_ctl.get_quant() { |
| 505 | Some(NAValue::Int(q as i64)) |
| 506 | } else { |
| 507 | Some(NAValue::Int(-1)) |
| 508 | }, |
| 509 | LF_LEVEL_OPTION => if let Some(lev) = self.lf_level { |
| 510 | Some(NAValue::Int(i64::from(lev))) |
| 511 | } else { |
| 512 | Some(NAValue::Int(-1)) |
| 513 | }, |
| 514 | LF_SHARP_OPTION => Some(NAValue::Int(i64::from(self.fenc.loop_params.loop_sharpness))), |
| 515 | LF_SIMPLE_OPTION => Some(NAValue::Bool(self.fenc.loop_params.lf_simple)), |
| 516 | MV_SEARCH_OPTION => Some(NAValue::String(self.me_mode.to_string())), |
| 517 | MV_RANGE_OPTION => Some(NAValue::Int(i64::from(self.me_range))), |
| 518 | MBTREE_DEPTH => Some(NAValue::Int(self.mbt_depth as i64)), |
| 519 | _ => None, |
| 520 | } |
| 521 | } |
| 522 | } |
| 523 | |
| 524 | pub fn get_encoder() -> Box<dyn NAEncoder + Send> { |
| 525 | Box::new(VP7Encoder::new()) |
| 526 | } |
| 527 | |
| 528 | #[cfg(test)] |
| 529 | mod test { |
| 530 | use nihav_core::codecs::*; |
| 531 | use nihav_core::demuxers::*; |
| 532 | use nihav_core::muxers::*; |
| 533 | use crate::*; |
| 534 | use nihav_commonfmt::*; |
| 535 | use nihav_codec_support::test::enc_video::*; |
| 536 | |
| 537 | fn encode_test(out_name: &'static str, enc_options: &[NAOption], hash: &[u32; 4]) { |
| 538 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 539 | generic_register_all_demuxers(&mut dmx_reg); |
| 540 | let mut dec_reg = RegisteredDecoders::new(); |
| 541 | duck_register_all_decoders(&mut dec_reg); |
| 542 | let mut mux_reg = RegisteredMuxers::new(); |
| 543 | generic_register_all_muxers(&mut mux_reg); |
| 544 | let mut enc_reg = RegisteredEncoders::new(); |
| 545 | duck_register_all_encoders(&mut enc_reg); |
| 546 | |
| 547 | // sample: https://samples.mplayerhq.hu/V-codecs/VP4/ot171_vp40.avi |
| 548 | let dec_config = DecoderTestParams { |
| 549 | demuxer: "avi", |
| 550 | in_name: "assets/Duck/ot171_vp40.avi", |
| 551 | stream_type: StreamType::Video, |
| 552 | limit: Some(9), |
| 553 | dmx_reg, dec_reg, |
| 554 | }; |
| 555 | let enc_config = EncoderTestParams { |
| 556 | muxer: "avi", |
| 557 | enc_name: "vp7", |
| 558 | out_name, |
| 559 | mux_reg, enc_reg, |
| 560 | }; |
| 561 | let dst_vinfo = NAVideoInfo { |
| 562 | width: 0, |
| 563 | height: 0, |
| 564 | format: YUV420_FORMAT, |
| 565 | flipped: false, |
| 566 | bits: 12, |
| 567 | }; |
| 568 | let enc_params = EncodeParameters { |
| 569 | format: NACodecTypeInfo::Video(dst_vinfo), |
| 570 | quality: 0, |
| 571 | bitrate: 50000, |
| 572 | tb_num: 0, |
| 573 | tb_den: 0, |
| 574 | flags: 0, |
| 575 | }; |
| 576 | //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); |
| 577 | test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, |
| 578 | hash); |
| 579 | } |
| 580 | #[test] |
| 581 | fn test_vp7_encoder() { |
| 582 | let enc_options = &[ |
| 583 | NAOption { name: super::QUANT_OPTION, value: NAValue::Int(42) }, |
| 584 | ]; |
| 585 | encode_test("vp7-q42.avi", enc_options, &[0xa5079e5b, 0x33dd8a63, 0xfc189e21, 0xee08332b]); |
| 586 | } |
| 587 | #[test] |
| 588 | fn test_vp7_encoder_noloop() { |
| 589 | let enc_options = &[ |
| 590 | NAOption { name: super::QUANT_OPTION, value: NAValue::Int(42) }, |
| 591 | NAOption { name: super::LF_LEVEL_OPTION, value: NAValue::Int(0) }, |
| 592 | ]; |
| 593 | encode_test("vp7-noloop.avi", enc_options, &[0xc7d41732, 0x09b03059, 0x8550921c, 0xa99d4c29]); |
| 594 | } |
| 595 | #[test] |
| 596 | fn test_vp7_encoder_mbtree() { |
| 597 | let enc_options = &[ |
| 598 | NAOption { name: super::QUANT_OPTION, value: NAValue::Int(24) }, |
| 599 | NAOption { name: super::MBTREE_DEPTH, value: NAValue::Int(10) }, |
| 600 | ]; |
| 601 | encode_test("vp7-mbt.avi", enc_options, &[0xd0d90d31, 0x0253275d, 0xbe502d3c, 0xacf2b6e7]); |
| 602 | } |
| 603 | #[test] |
| 604 | fn test_vp7_encoder_ratectl() { |
| 605 | let enc_options = &[ |
| 606 | NAOption { name: super::QUANT_OPTION, value: NAValue::Int(-1) }, |
| 607 | ]; |
| 608 | encode_test("vp7-br.avi", enc_options, &[0x47dcd4da, 0x04b06feb, 0x386163c1, 0x54899da3]); |
| 609 | } |
| 610 | } |