From f5811896a9573ba0a35d804f5fb72f9829c91a36 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Wed, 19 Mar 2025 18:30:11 +0100 Subject: [PATCH] add framerate conversion ability --- src/transcoder.rs | 102 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 10 deletions(-) diff --git a/src/transcoder.rs b/src/transcoder.rs index f6fb29f..78e6296 100644 --- a/src/transcoder.rs +++ b/src/transcoder.rs @@ -178,6 +178,10 @@ pub struct VideoEncodeContext { pub encoder: Box, pub scaler: Option, pub scaler_buf: NABufferType, + pub tb_num: u32, + pub tb_den: u32, + pub cfr: bool, + pub last_ts: Option, } impl EncoderInterface for VideoEncodeContext { @@ -215,11 +219,53 @@ impl EncoderInterface for VideoEncodeContext { } else { buf }; - let cfrm = NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cbuf); - self.encoder.encode(&cfrm).unwrap(); - while let Ok(Some(pkt)) = self.encoder.get_packet() { - queue.queue_packet(pkt); + if self.scaler.is_none() && !matches!(cbuf, NABufferType::None) { + self.scaler_buf = cbuf.clone(); } + + let ref_ts = frm.get_time_information(); + let new_pts = if let Some(ts) = ref_ts.pts { + Some(NATimeInfo::ts_to_time(ts, u64::from(self.tb_den), ref_ts.tb_num, ref_ts.tb_den)) + } else { None }; + let new_duration = if let Some(dur) = ref_ts.duration { + Some(NATimeInfo::ts_to_time(dur, u64::from(self.tb_den), ref_ts.tb_num, ref_ts.tb_den)) + } else { None }; + let ts = NATimeInfo::new(new_pts, None, new_duration, self.tb_num, self.tb_den); + let mut cfrm = NAFrame::new(ts, frm.frame_type, frm.key, frm.get_info(), cbuf); + if self.last_ts.is_none() { + self.last_ts = new_pts; + } + + let mut converted = false; + if let Some(ref mut last_ts) = self.last_ts { + if self.cfr && new_pts.is_some() { + let cur_ts = new_pts.unwrap_or_default(); + let mut cur_lts = *last_ts; + let mut next_lts = *last_ts + u64::from(self.tb_num); + let mut had_frame = false; + while cur_ts >= cur_lts { + cfrm.ts.pts = Some(cur_lts); + self.encoder.encode(&cfrm).unwrap(); + while let Ok(Some(pkt)) = self.encoder.get_packet() { + queue.queue_packet(pkt); + } + cur_lts = next_lts; + next_lts += u64::from(self.tb_num); + had_frame = true; + } + converted = true; + if had_frame { + *last_ts = cur_lts; + } + } + } + if !converted { + self.encoder.encode(&cfrm).unwrap(); + while let Ok(Some(pkt)) = self.encoder.get_packet() { + queue.queue_packet(pkt); + } + } + true } fn flush(&mut self, queue: &mut OutputQueue) -> EncoderResult<()> { @@ -749,8 +795,17 @@ impl Transcoder { //todo check for params mismatch let enc_ctx: Box = match (&iformat, &real_fmt) { (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => { + let force_cfr = self.fixed_rate || (ret_eparams.flags & ENC_MODE_CFR) != 0; if svinfo == dvinfo && !forced_out { - Box::new(VideoEncodeContext { encoder, scaler: None, scaler_buf: NABufferType::None }) + Box::new(VideoEncodeContext { + encoder, + scaler: None, + scaler_buf: NABufferType::None, + cfr: force_cfr, + last_ts: None, + tb_num: enc_stream.tb_num, + tb_den: enc_stream.tb_den, + }) } else { let ofmt = ScaleInfo { fmt: dvinfo.format, width: dvinfo.width, height: dvinfo.height }; let ret = NAScale::new_with_options(ofmt, ofmt, &self.scale_opts); @@ -765,7 +820,15 @@ impl Transcoder { return RegisterResult::Failed; } let scaler_buf = ret.unwrap(); - Box::new(VideoEncodeContext { encoder, scaler: Some(scaler), scaler_buf }) + Box::new(VideoEncodeContext { + encoder, + scaler: Some(scaler), + scaler_buf, + cfr: force_cfr, + last_ts: None, + tb_num: enc_stream.tb_num, + tb_den: enc_stream.tb_den, + }) } }, (NACodecTypeInfo::Audio(sainfo), NACodecTypeInfo::Audio(dainfo)) => { @@ -826,11 +889,22 @@ println!("encoder {} is not supported by output (expected {})", istr.id, istr.ge println!("error initialising encoder"); return RegisterResult::Failed; } + let enc_stream = ret.unwrap(); //todo check for params mismatch - let enc_ctx: Box = match (&oopts.enc_params.format, &ret_eparams.format) { + let real_fmt = enc_stream.get_info().get_properties(); + let enc_ctx: Box = match (&oopts.enc_params.format, &real_fmt) { (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => { + let force_cfr = self.fixed_rate || (ret_eparams.flags & ENC_MODE_CFR) != 0; if svinfo == dvinfo { - Box::new(VideoEncodeContext { encoder, scaler: None, scaler_buf: NABufferType::None }) + Box::new(VideoEncodeContext { + encoder, + scaler: None, + scaler_buf: NABufferType::None, + cfr: force_cfr, + last_ts: None, + tb_num: enc_stream.tb_num, + tb_den: enc_stream.tb_den, + }) } else { let ofmt = ScaleInfo { fmt: dvinfo.format, width: dvinfo.width, height: dvinfo.height }; let ret = NAScale::new_with_options(ofmt, ofmt, &self.scale_opts); @@ -845,7 +919,15 @@ println!("encoder {} is not supported by output (expected {})", istr.id, istr.ge return RegisterResult::Failed; } let scaler_buf = ret.unwrap(); - Box::new(VideoEncodeContext { encoder, scaler: Some(scaler), scaler_buf }) + Box::new(VideoEncodeContext { + encoder, + scaler: Some(scaler), + scaler_buf, + cfr: force_cfr, + last_ts: None, + tb_num: enc_stream.tb_num, + tb_den: enc_stream.tb_den, + }) } }, (NACodecTypeInfo::Audio(sainfo), NACodecTypeInfo::Audio(dainfo)) => { @@ -867,7 +949,7 @@ println!("can't generate default channel map for {} channels", dainfo.channels); }, _ => unreachable!(), }; - out_sm.add_stream_ref(ret.unwrap()); + out_sm.add_stream_ref(enc_stream); self.encoders.push(OutputMode::Encode(out_id, enc_ctx)); self.ostr_opts.push(oopts); } -- 2.39.5