]>
Commit | Line | Data |
---|---|---|
c5d5793c KS |
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 => { | |
6f263099 KS |
270 | Ok(EncodeParameters { |
271 | format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)), | |
272 | ..Default::default() }) | |
c5d5793c KS |
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 | } | |
2757a028 | 283 | fn get_capabilities(&self) -> u64 { 0 } |
c5d5793c KS |
284 | fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> { |
285 | match encinfo.format { | |
286 | NACodecTypeInfo::None => Err(EncoderError::FormatError), | |
287 | NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), | |
288 | NACodecTypeInfo::Video(vinfo) => { | |
289 | if vinfo.format != YUV420_FORMAT { | |
290 | return Err(EncoderError::FormatError); | |
291 | } | |
292 | if ((vinfo.width | vinfo.height) & 15) != 0 { | |
293 | return Err(EncoderError::FormatError); | |
294 | } | |
295 | if (vinfo.width | vinfo.height) >= (1 << 12) { | |
296 | return Err(EncoderError::FormatError); | |
297 | } | |
298 | ||
299 | let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format); | |
300 | let info = NACodecInfo::new("vp7", NACodecTypeInfo::Video(out_info), None); | |
301 | let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); | |
302 | stream.set_num(stream_id as usize); | |
303 | let stream = stream.into_ref(); | |
304 | ||
305 | self.last_frame = alloc_video_buffer(out_info, 4)?; | |
306 | self.gold_frame = alloc_video_buffer(out_info, 4)?; | |
307 | ||
308 | self.stream = Some(stream.clone()); | |
309 | ||
310 | self.width = vinfo.width; | |
311 | self.height = vinfo.height; | |
312 | self.mb_w = (vinfo.width + 15) >> 4; | |
313 | self.mb_h = (vinfo.height + 15) >> 4; | |
314 | self.fenc.resize(self.mb_w, self.mb_h); | |
315 | ||
316 | self.br_ctl.set_params(encinfo.tb_num, encinfo.tb_den, encinfo.bitrate, self.key_int, self.mb_w * self.mb_h); | |
317 | ||
318 | self.frm_pool.reset(); | |
319 | self.frm_pool.set_dec_bufs(self.mbt_depth + 1); | |
320 | self.frm_pool.prealloc_video(out_info, 4)?; | |
321 | self.i_frame = self.frm_pool.get_free().unwrap(); | |
322 | self.mb_weight.resize(self.mb_w * self.mb_h, 0); | |
323 | self.mb_map.resize(self.mb_w * self.mb_h, 0); | |
324 | self.mb_map2.resize(self.mb_w * self.mb_h, 0); | |
325 | self.qframes.clear(); | |
326 | self.enc_state = if self.mbt_depth.min(self.key_int as usize) > 0 { | |
327 | EncodingState::Intra | |
328 | } else { | |
329 | EncodingState::JustEncode | |
330 | }; | |
331 | ||
332 | Ok(stream) | |
333 | }, | |
334 | } | |
335 | } | |
336 | fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { | |
337 | if let Some(ref vbuf) = frm.get_buffer().get_vbuf() { | |
338 | let mbt_frames = self.mbt_depth.min(self.key_int as usize); | |
339 | if !self.qframes.is_empty() || (mbt_frames > 0 && self.enc_state != EncodingState::JustEncode) { | |
340 | if let Some(dbuf) = self.frm_pool.get_copy(vbuf) { | |
341 | let newfrm = NAFrame::new(frm.ts, frm.frame_type, frm.key, frm.get_info(), NABufferType::Video(dbuf)); | |
342 | if self.enc_state == EncodingState::Intra { | |
343 | for (i, el) in self.mb_map.iter_mut().enumerate() { | |
344 | *el = i; | |
345 | } | |
346 | for el in self.mb_weight.iter_mut() { | |
347 | *el = 1; | |
348 | } | |
349 | let frm = NASimpleVideoFrame::from_video_buf(&mut self.i_frame).unwrap(); | |
350 | let src = vbuf.get_data(); | |
351 | for plane in 0..3 { | |
352 | let soff = vbuf.get_offset(plane); | |
353 | let sstride = vbuf.get_stride(plane); | |
354 | let copy_len = sstride.min(frm.stride[plane]); | |
355 | for (dst, src) in frm.data[frm.offset[plane]..].chunks_mut(frm.stride[plane]).zip(src[soff..].chunks(sstride)).take(frm.height[plane]) { | |
356 | dst[..copy_len].copy_from_slice(&src[..copy_len]); | |
357 | } | |
358 | } | |
359 | self.enc_state = EncodingState::Refinement; | |
360 | } else { | |
361 | self.fenc.set_me_params(self.me_mode, self.me_range, self.version); | |
362 | self.fenc.load_frame(vbuf); | |
363 | self.fenc.mb_tree_search(self.i_frame.clone(), &self.mb_map, &mut self.mb_map2, &mut self.mb_weight); | |
364 | std::mem::swap(&mut self.mb_map, &mut self.mb_map2); | |
365 | } | |
366 | self.qframes.push(newfrm); | |
367 | Ok(()) | |
368 | } else { | |
369 | self.enc_state = EncodingState::JustEncode; | |
370 | self.encode_frame(frm) | |
371 | } | |
372 | } else { | |
373 | self.encode_frame(frm) | |
374 | } | |
375 | } else { | |
376 | Err(EncoderError::FormatError) | |
377 | } | |
378 | } | |
379 | fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> { | |
380 | let mbt_frames = self.mbt_depth.min(self.key_int as usize); | |
381 | if self.qframes.len() >= mbt_frames { | |
382 | self.enc_state = EncodingState::JustEncode; | |
383 | } | |
384 | if self.pkt.is_none() && !self.qframes.is_empty() && self.enc_state == EncodingState::JustEncode { | |
385 | let frm = self.qframes.remove(0); | |
386 | self.encode_frame(&frm)?; | |
387 | if self.qframes.is_empty() && self.mbt_depth > 0 && self.frmcount == 0 { | |
388 | self.enc_state = EncodingState::Intra; | |
389 | } | |
390 | } | |
391 | let mut npkt = None; | |
392 | std::mem::swap(&mut self.pkt, &mut npkt); | |
393 | Ok(npkt) | |
394 | } | |
395 | fn flush(&mut self) -> EncoderResult<()> { | |
396 | self.frmcount = 0; | |
397 | self.enc_state = EncodingState::JustEncode; | |
398 | Ok(()) | |
399 | } | |
400 | } | |
401 | ||
402 | const VERSION_OPTION: &str = "version"; | |
403 | const LF_LEVEL_OPTION: &str = "lf_level"; | |
404 | const LF_SHARP_OPTION: &str = "lf_sharpness"; | |
405 | const LF_SIMPLE_OPTION: &str = "lf_simple"; | |
406 | const QUANT_OPTION: &str = "quant"; | |
407 | const MV_SEARCH_OPTION: &str = "mv_mode"; | |
408 | const MV_RANGE_OPTION: &str = "mv_range"; | |
409 | const MBTREE_DEPTH: &str = "mbtree_depth"; | |
410 | ||
411 | const ENCODER_OPTS: &[NAOptionDefinition] = &[ | |
412 | NAOptionDefinition { | |
413 | name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC, | |
414 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, | |
415 | NAOptionDefinition { | |
416 | name: VERSION_OPTION, description: "internal codec version", | |
417 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(1)) }, | |
418 | NAOptionDefinition { | |
419 | name: LF_LEVEL_OPTION, description: "loop filter level (-1 = automatic)", | |
420 | opt_type: NAOptionDefinitionType::Int(Some(-1), Some(63)) }, | |
421 | NAOptionDefinition { | |
422 | name: LF_SHARP_OPTION, description: "loop filter sharpness", | |
423 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(7)) }, | |
424 | NAOptionDefinition { | |
425 | name: LF_SIMPLE_OPTION, description: "use simple loop filter", | |
426 | opt_type: NAOptionDefinitionType::Bool }, | |
427 | NAOptionDefinition { | |
428 | name: QUANT_OPTION, description: "force fixed quantiser for encoding", | |
429 | opt_type: NAOptionDefinitionType::Int(Some(-1), Some(127)) }, | |
430 | NAOptionDefinition { | |
431 | name: MV_SEARCH_OPTION, description: "motion search mode", | |
432 | opt_type: NAOptionDefinitionType::String(Some(&["sea", "dia", "hex", "epzs"])) }, | |
433 | NAOptionDefinition { | |
434 | name: MV_RANGE_OPTION, description: "motion search range (in pixels)", | |
435 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(30)) }, | |
436 | NAOptionDefinition { | |
437 | name: MBTREE_DEPTH, description: "number of frames in MB tree analysis buffer", | |
438 | opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, | |
439 | ]; | |
440 | ||
441 | impl NAOptionHandler for VP7Encoder { | |
442 | fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS } | |
443 | fn set_options(&mut self, options: &[NAOption]) { | |
444 | for option in options.iter() { | |
445 | for opt_def in ENCODER_OPTS.iter() { | |
446 | if opt_def.check(option).is_ok() { | |
447 | match option.name { | |
448 | KEYFRAME_OPTION => { | |
449 | if let NAValue::Int(intval) = option.value { | |
450 | self.key_int = intval as u8; | |
451 | } | |
452 | }, | |
453 | VERSION_OPTION => { | |
454 | if let NAValue::Int(intval) = option.value { | |
455 | self.version = intval as u8; | |
456 | } | |
457 | }, | |
458 | LF_LEVEL_OPTION => { | |
459 | if let NAValue::Int(intval) = option.value { | |
460 | self.lf_level = if intval < 0 { None } else { Some(intval as u8) }; | |
461 | } | |
462 | }, | |
463 | LF_SHARP_OPTION => { | |
464 | if let NAValue::Int(intval) = option.value { | |
465 | self.fenc.loop_params.loop_sharpness = intval as u8; | |
466 | } | |
467 | }, | |
468 | LF_SIMPLE_OPTION => { | |
469 | if let NAValue::Bool(flag) = option.value { | |
470 | self.fenc.loop_params.lf_simple = flag; | |
471 | } | |
472 | }, | |
473 | QUANT_OPTION => { | |
474 | if let NAValue::Int(intval) = option.value { | |
475 | self.br_ctl.set_quant(if intval < 0 { None } else { Some(intval as usize) }); | |
476 | } | |
477 | }, | |
478 | MV_SEARCH_OPTION => { | |
479 | if let NAValue::String(ref string) = option.value { | |
480 | if let Ok(mv_mode) = string.parse::<MVSearchMode>() { | |
481 | self.me_mode = mv_mode; | |
482 | } | |
483 | } | |
484 | }, | |
485 | MV_RANGE_OPTION => { | |
486 | if let NAValue::Int(intval) = option.value { | |
487 | self.me_range = intval as i16; | |
488 | } | |
489 | }, | |
490 | MBTREE_DEPTH => { | |
491 | if let NAValue::Int(intval) = option.value { | |
492 | self.mbt_depth = intval as usize; | |
493 | } | |
494 | }, | |
495 | _ => {}, | |
496 | }; | |
497 | } | |
498 | } | |
499 | } | |
500 | } | |
501 | fn query_option_value(&self, name: &str) -> Option<NAValue> { | |
502 | match name { | |
503 | KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))), | |
504 | VERSION_OPTION => Some(NAValue::Int(i64::from(self.version))), | |
505 | QUANT_OPTION => if let Some(q) = self.br_ctl.get_quant() { | |
506 | Some(NAValue::Int(q as i64)) | |
507 | } else { | |
508 | Some(NAValue::Int(-1)) | |
509 | }, | |
510 | LF_LEVEL_OPTION => if let Some(lev) = self.lf_level { | |
511 | Some(NAValue::Int(i64::from(lev))) | |
512 | } else { | |
513 | Some(NAValue::Int(-1)) | |
514 | }, | |
515 | LF_SHARP_OPTION => Some(NAValue::Int(i64::from(self.fenc.loop_params.loop_sharpness))), | |
516 | LF_SIMPLE_OPTION => Some(NAValue::Bool(self.fenc.loop_params.lf_simple)), | |
517 | MV_SEARCH_OPTION => Some(NAValue::String(self.me_mode.to_string())), | |
518 | MV_RANGE_OPTION => Some(NAValue::Int(i64::from(self.me_range))), | |
519 | MBTREE_DEPTH => Some(NAValue::Int(self.mbt_depth as i64)), | |
520 | _ => None, | |
521 | } | |
522 | } | |
523 | } | |
524 | ||
525 | pub fn get_encoder() -> Box<dyn NAEncoder + Send> { | |
526 | Box::new(VP7Encoder::new()) | |
527 | } | |
528 | ||
529 | #[cfg(test)] | |
530 | mod test { | |
531 | use nihav_core::codecs::*; | |
532 | use nihav_core::demuxers::*; | |
533 | use nihav_core::muxers::*; | |
534 | use crate::*; | |
535 | use nihav_commonfmt::*; | |
536 | use nihav_codec_support::test::enc_video::*; | |
537 | ||
538 | fn encode_test(out_name: &'static str, enc_options: &[NAOption], hash: &[u32; 4]) { | |
539 | let mut dmx_reg = RegisteredDemuxers::new(); | |
540 | generic_register_all_demuxers(&mut dmx_reg); | |
541 | let mut dec_reg = RegisteredDecoders::new(); | |
542 | duck_register_all_decoders(&mut dec_reg); | |
543 | let mut mux_reg = RegisteredMuxers::new(); | |
544 | generic_register_all_muxers(&mut mux_reg); | |
545 | let mut enc_reg = RegisteredEncoders::new(); | |
546 | duck_register_all_encoders(&mut enc_reg); | |
547 | ||
548 | // sample: https://samples.mplayerhq.hu/V-codecs/VP4/ot171_vp40.avi | |
549 | let dec_config = DecoderTestParams { | |
550 | demuxer: "avi", | |
551 | in_name: "assets/Duck/ot171_vp40.avi", | |
552 | stream_type: StreamType::Video, | |
553 | limit: Some(9), | |
554 | dmx_reg, dec_reg, | |
555 | }; | |
556 | let enc_config = EncoderTestParams { | |
557 | muxer: "avi", | |
558 | enc_name: "vp7", | |
559 | out_name, | |
560 | mux_reg, enc_reg, | |
561 | }; | |
562 | let dst_vinfo = NAVideoInfo { | |
563 | width: 0, | |
564 | height: 0, | |
565 | format: YUV420_FORMAT, | |
566 | flipped: false, | |
567 | bits: 12, | |
568 | }; | |
569 | let enc_params = EncodeParameters { | |
570 | format: NACodecTypeInfo::Video(dst_vinfo), | |
571 | quality: 0, | |
572 | bitrate: 50000, | |
573 | tb_num: 0, | |
574 | tb_den: 0, | |
575 | flags: 0, | |
576 | }; | |
577 | //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); | |
578 | test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, | |
579 | hash); | |
580 | } | |
581 | #[test] | |
582 | fn test_vp7_encoder() { | |
583 | let enc_options = &[ | |
584 | NAOption { name: super::QUANT_OPTION, value: NAValue::Int(42) }, | |
585 | ]; | |
586 | encode_test("vp7-q42.avi", enc_options, &[0xa5079e5b, 0x33dd8a63, 0xfc189e21, 0xee08332b]); | |
587 | } | |
588 | #[test] | |
589 | fn test_vp7_encoder_noloop() { | |
590 | let enc_options = &[ | |
591 | NAOption { name: super::QUANT_OPTION, value: NAValue::Int(42) }, | |
592 | NAOption { name: super::LF_LEVEL_OPTION, value: NAValue::Int(0) }, | |
593 | ]; | |
594 | encode_test("vp7-noloop.avi", enc_options, &[0xc7d41732, 0x09b03059, 0x8550921c, 0xa99d4c29]); | |
595 | } | |
596 | #[test] | |
597 | fn test_vp7_encoder_mbtree() { | |
598 | let enc_options = &[ | |
599 | NAOption { name: super::QUANT_OPTION, value: NAValue::Int(24) }, | |
600 | NAOption { name: super::MBTREE_DEPTH, value: NAValue::Int(10) }, | |
601 | ]; | |
602 | encode_test("vp7-mbt.avi", enc_options, &[0xd0d90d31, 0x0253275d, 0xbe502d3c, 0xacf2b6e7]); | |
603 | } | |
604 | #[test] | |
605 | fn test_vp7_encoder_ratectl() { | |
606 | let enc_options = &[ | |
607 | NAOption { name: super::QUANT_OPTION, value: NAValue::Int(-1) }, | |
608 | ]; | |
609 | encode_test("vp7-br.avi", enc_options, &[0x47dcd4da, 0x04b06feb, 0x386163c1, 0x54899da3]); | |
610 | } | |
611 | } |