use ratectl::*;
mod rdo;
+const VERSION_VP61: u8 = VERSION_VP60 + 1;
+
enum VP6Writer<'a, 'b> {
BoolCoder(BoolEncoder<'a, 'b>),
Huffman(HuffEncoder<'a, 'b>),
fenc: FrameEncoder,
ratectl: RateControl,
+ flipped: bool,
huffman: bool,
version: u8,
me_range: i16,
force_q: Option<usize>,
+ 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 {
ratectl: RateControl::new(),
mc_buf,
+ flipped,
huffman: false,
version: VERSION_VP60,
me_range: 16,
force_q: None,
+ fast: false,
}
}
fn decide_encoding(&mut self) -> bool {
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);
fn encode_inter(&mut self, bw: &mut ByteWriter, quant: usize) -> EncoderResult<bool> {
self.stats.reset();
- let multistream = !self.huffman;
+ let multistream = self.huffman || self.version != VERSION_VP60;
let loop_filter = false;
self.fenc.prepare_intra_blocks();
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);
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<bool> {
+ 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 {
fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
match encinfo.format {
NACodecTypeInfo::None => {
- let mut ofmt = EncodeParameters::default();
- ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT));
- Ok(ofmt)
+ Ok(EncodeParameters {
+ format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, self.flipped, YUV420_FORMAT)),
+ ..Default::default()
+ })
},
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)
}
}
}
+ fn get_capabilities(&self) -> u64 { 0 }
fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
match encinfo.format {
NACodecTypeInfo::None => Err(EncoderError::FormatError),
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();
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;
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 {
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)) },
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 {
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) };
self.me_range = intval as i16;
}
},
+ FAST_OPTION => {
+ if let NAValue::Bool(bval) = option.value {
+ self.fast = bval;
+ }
+ },
_ => {},
};
}
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 {
},
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<dyn NAEncoder + Send> {
- Box::new(VP6Encoder::new())
+ Box::new(VP6Encoder::new(true))
+}
+
+pub fn get_encoder_flv() -> Box<dyn NAEncoder + Send> {
+ Box::new(VP6Encoder::new(false))
}
#[cfg(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",
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() {
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]);
}
}