X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-duck%2Fsrc%2Fcodecs%2Fvp6enc%2Fmod.rs;h=b6ce632cd550aa9a9ae71efdb5b820f12b95bba3;hp=0ffceb2a3ab7ca281072499837d1f2f7538926c4;hb=7cb65894212b51b13b8ac2e30f9a520627938a3e;hpb=3952bfd9d2d5c2a64d50c2a89b02e93d9b97d541 diff --git a/nihav-duck/src/codecs/vp6enc/mod.rs b/nihav-duck/src/codecs/vp6enc/mod.rs index 0ffceb2..b6ce632 100644 --- a/nihav-duck/src/codecs/vp6enc/mod.rs +++ b/nihav-duck/src/codecs/vp6enc/mod.rs @@ -17,6 +17,8 @@ mod ratectl; use ratectl::*; mod rdo; +const VERSION_VP61: u8 = VERSION_VP60 + 1; + enum VP6Writer<'a, 'b> { BoolCoder(BoolEncoder<'a, 'b>), Huffman(HuffEncoder<'a, 'b>), @@ -170,6 +172,7 @@ struct VP6Encoder { fenc: FrameEncoder, ratectl: RateControl, + flipped: bool, huffman: bool, version: u8, @@ -186,10 +189,11 @@ struct VP6Encoder { me_range: i16, force_q: Option, + fast: bool, } impl VP6Encoder { - fn new() -> Self { + fn new(flipped: bool) -> Self { let vt = alloc_video_buffer(NAVideoInfo::new(24, 24, false, VP_YUVA420_FORMAT), 4).unwrap(); let mc_buf = vt.get_vbuf().unwrap(); Self { @@ -205,6 +209,7 @@ impl VP6Encoder { ratectl: RateControl::new(), mc_buf, + flipped, huffman: false, version: VERSION_VP60, @@ -221,6 +226,7 @@ impl VP6Encoder { me_range: 16, force_q: None, + fast: false, } } fn decide_encoding(&mut self) -> bool { @@ -414,7 +420,7 @@ impl VP6Encoder { self.pmodels.reset(false); self.pmodels.reset_mbtype_models(); - let multistream = self.huffman; + let multistream = self.huffman || self.version != VERSION_VP60; self.fenc.prepare_intra_blocks(); self.fenc.apply_dc_prediction(&mut self.dc_pred); @@ -464,7 +470,7 @@ impl VP6Encoder { fn encode_inter(&mut self, bw: &mut ByteWriter, quant: usize) -> EncoderResult { self.stats.reset(); - let multistream = !self.huffman; + let multistream = self.huffman || self.version != VERSION_VP60; let loop_filter = false; self.fenc.prepare_intra_blocks(); @@ -478,6 +484,12 @@ impl VP6Encoder { let (_force_intra, golden_frame) = self.fenc.decide_frame_type(); self.fenc.apply_dc_prediction(&mut self.dc_pred); self.fenc.predict_mvs(); + + self.write_inter_frame(bw, quant, multistream, loop_filter, golden_frame)?; + + Ok(golden_frame) + } + fn write_inter_frame(&mut self, bw: &mut ByteWriter, quant: usize, multistream: bool, loop_filter: bool, golden_frame: bool) -> EncoderResult<()> { self.estimate_blocks(false); self.stats.generate(&mut self.models, false); @@ -556,7 +568,31 @@ impl VP6Encoder { VP6Writer::Huffman(HuffEncoder::new(bw)) }; self.encode_coeffs(writer)?; - Ok(golden_frame) + + Ok(()) + } + fn encode_inter_fast(&mut self, bw: &mut ByteWriter, quant: usize) -> EncoderResult { + self.stats.reset(); + + let multistream = self.huffman || self.version != VERSION_VP60; + let loop_filter = false; + + let last_frm = self.last_frame.get_vbuf().unwrap(); + let gold_frm = if !self.last_gold { + Some(self.gold_frame.get_vbuf().unwrap()) + } else { + None + }; + let lambda = if self.force_q.is_some() { 1.0 } else { self.ratectl.lambda }; + self.fenc.select_inter_blocks_fast(last_frm, gold_frm, self.mc_buf.clone(), lambda); + let golden_frame = false; + self.fenc.apply_dc_prediction(&mut self.dc_pred); + self.fenc.predict_mvs(); + self.estimate_blocks(false); + + self.write_inter_frame(bw, quant, multistream, loop_filter, golden_frame)?; + + Ok(false) } fn encode_coeffs(&mut self, mut writer: VP6Writer) -> EncoderResult<()> { if self.huffman { @@ -631,12 +667,12 @@ impl NAEncoder for VP6Encoder { match encinfo.format { NACodecTypeInfo::None => { let mut ofmt = EncodeParameters::default(); - ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT)); + ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, self.flipped, YUV420_FORMAT)); Ok(ofmt) }, NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), NACodecTypeInfo::Video(vinfo) => { - let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, YUV420_FORMAT); + let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, self.flipped, YUV420_FORMAT); let mut ofmt = *encinfo; ofmt.format = NACodecTypeInfo::Video(outinfo); Ok(ofmt) @@ -658,8 +694,8 @@ impl NAEncoder for VP6Encoder { return Err(EncoderError::FormatError); } - let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, vinfo.format); - let info = NACodecInfo::new("vp6", NACodecTypeInfo::Video(out_info), None); + let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, self.flipped, vinfo.format); + let info = NACodecInfo::new(if self.flipped { "vp6" } else { "vp6f" }, NACodecTypeInfo::Video(out_info), None); let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); stream.set_num(stream_id as usize); let stream = stream.into_ref(); @@ -705,12 +741,14 @@ impl NAEncoder for VP6Encoder { self.fenc.me_range = self.me_range; let golden_frame = if is_intra { self.encode_intra(&mut bw, quant)? - } else { + } else if !self.fast { self.fenc.estimate_mvs(self.last_frame.get_vbuf().unwrap(), self.mc_buf.clone(), false); if !self.last_gold { self.fenc.estimate_mvs(self.gold_frame.get_vbuf().unwrap(), self.mc_buf.clone(), true); } self.encode_inter(&mut bw, quant)? + } else { + self.encode_inter_fast(&mut bw, quant)? }; self.fenc.reconstruct_frame(&mut self.dc_pred, self.last_frame.get_vbuf().unwrap()); self.last_gold = golden_frame; @@ -751,8 +789,10 @@ impl NAEncoder for VP6Encoder { const HUFFMAN_OPTION: &str = "huffman"; const QUANT_OPTION: &str = "quant"; +const VERSION_OPTION: &str = "version"; const MV_SEARCH_OPTION: &str = "mv_mode"; const MV_RANGE_OPTION: &str = "mv_range"; +const FAST_OPTION: &str = "fast"; const ENCODER_OPTS: &[NAOptionDefinition] = &[ NAOptionDefinition { @@ -761,6 +801,9 @@ const ENCODER_OPTS: &[NAOptionDefinition] = &[ NAOptionDefinition { name: HUFFMAN_OPTION, description: "use Huffman encoding", opt_type: NAOptionDefinitionType::Bool }, + NAOptionDefinition { + name: VERSION_OPTION, description: "codec minor version", + opt_type: NAOptionDefinitionType::String(Some(&["vp60", "vp61", "vp62"])) }, NAOptionDefinition { name: QUANT_OPTION, description: "force fixed quantiser for encoding", opt_type: NAOptionDefinitionType::Int(Some(-1), Some(63)) }, @@ -770,6 +813,9 @@ const ENCODER_OPTS: &[NAOptionDefinition] = &[ NAOptionDefinition { name: MV_RANGE_OPTION, description: "motion search range (in pixels)", opt_type: NAOptionDefinitionType::Int(Some(0), Some(30)) }, + NAOptionDefinition { + name: FAST_OPTION, description: "faster (but worse) encoding", + opt_type: NAOptionDefinitionType::Bool }, ]; impl NAOptionHandler for VP6Encoder { @@ -789,6 +835,16 @@ impl NAOptionHandler for VP6Encoder { self.huffman = bval; } }, + VERSION_OPTION => { + if let NAValue::String(ref string) = option.value { + self.version = match string.as_str() { + "vp60" => VERSION_VP60, + "vp61" => VERSION_VP61, + "vp62" => VERSION_VP62, + _ => unreachable!(), + }; + } + }, QUANT_OPTION => { if let NAValue::Int(intval) = option.value { self.force_q = if intval < 0 { None } else { Some(intval as usize) }; @@ -806,6 +862,11 @@ impl NAOptionHandler for VP6Encoder { self.me_range = intval as i16; } }, + FAST_OPTION => { + if let NAValue::Bool(bval) = option.value { + self.fast = bval; + } + }, _ => {}, }; } @@ -816,6 +877,15 @@ impl NAOptionHandler for VP6Encoder { match name { KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))), HUFFMAN_OPTION => Some(NAValue::Bool(self.huffman)), + VERSION_OPTION => { + let ver = match self.version { + VERSION_VP60 => "vp60", + VERSION_VP61 => "vp61", + VERSION_VP62 => "vp62", + _ => unreachable!(), + }; + Some(NAValue::String(ver.to_string())) + }, QUANT_OPTION => if let Some(q) = self.force_q { Some(NAValue::Int(q as i64)) } else { @@ -823,13 +893,18 @@ impl NAOptionHandler for VP6Encoder { }, MV_SEARCH_OPTION => Some(NAValue::String(self.me_mode.to_string())), MV_RANGE_OPTION => Some(NAValue::Int(i64::from(self.me_range))), + FAST_OPTION => Some(NAValue::Bool(self.fast)), _ => None, } } } pub fn get_encoder() -> Box { - Box::new(VP6Encoder::new()) + Box::new(VP6Encoder::new(true)) +} + +pub fn get_encoder_flv() -> Box { + Box::new(VP6Encoder::new(false)) } #[cfg(test)] @@ -851,6 +926,7 @@ mod test { let mut enc_reg = RegisteredEncoders::new(); duck_register_all_encoders(&mut enc_reg); + // sample: https://samples.mplayerhq.hu/V-codecs/VP4/ot171_vp40.avi let dec_config = DecoderTestParams { demuxer: "avi", in_name: "assets/Duck/ot171_vp40.avi", @@ -888,13 +964,21 @@ mod test { let enc_options = &[ NAOption { name: super::QUANT_OPTION, value: NAValue::Int(42) }, ]; - encode_test("vp6-bool.avi", enc_options, &[0x3649ebc5, 0x4ed1cd7d, 0x1ad40c7b, 0xadd30276]); + encode_test("vp6-bool.avi", enc_options, &[0xb57f49e5, 0x6b48accd, 0xc28fadb3, 0xc89a30d2]); + } + #[test] + fn test_vp6_encoder_fast() { + let enc_options = &[ + NAOption { name: super::QUANT_OPTION, value: NAValue::Int(42) }, + NAOption { name: super::FAST_OPTION, value: NAValue::Bool(true) }, + ]; + encode_test("vp6-fast.avi", enc_options, &[0xb8037ce1, 0xc00ade72, 0x3c0b73c2, 0xbfc4113d]); } #[test] fn test_vp6_encoder_rc() { let enc_options = &[ ]; - encode_test("vp6-rc.avi", enc_options, &[0x97f3ea9d, 0x5374d30f, 0xf900a594, 0xbfa34b0f]); + encode_test("vp6-rc.avi", enc_options, &[0x790baca9, 0x663eafcf, 0x36d1bed8, 0xddf882de]); } #[test] fn test_vp6_encoder_huff() { @@ -902,6 +986,6 @@ mod test { NAOption { name: super::HUFFMAN_OPTION, value: NAValue::Bool(true) }, NAOption { name: super::QUANT_OPTION, value: NAValue::Int(42) }, ]; - encode_test("vp6-huff.avi", enc_options, &[0x4558af0a, 0x4d260b6b, 0x16b7c501, 0x178f42c5]); + encode_test("vp6-huff.avi", enc_options, &[0x6e9bb23d, 0xde296d92, 0x4c225bae, 0x3651e31f]); } }