]> git.nihav.org Git - nihav-encoder.git/commitdiff
add framerate conversion ability
authorKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 19 Mar 2025 17:30:11 +0000 (18:30 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 19 Mar 2025 17:30:11 +0000 (18:30 +0100)
src/transcoder.rs

index f6fb29ff31ba0c83ce212d9dab8dfff1f57abe66..78e6296c93b00f787aff749335ab5bb1f1a84604 100644 (file)
@@ -178,6 +178,10 @@ pub struct VideoEncodeContext {
     pub encoder:    Box<dyn NAEncoder>,
     pub scaler:     Option<NAScale>,
     pub scaler_buf: NABufferType,
+    pub tb_num:     u32,
+    pub tb_den:     u32,
+    pub cfr:        bool,
+    pub last_ts:    Option<u64>,
 }
 
 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<dyn EncoderInterface> = 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<dyn EncoderInterface> = match (&oopts.enc_params.format, &ret_eparams.format) {
+            let real_fmt = enc_stream.get_info().get_properties();
+            let enc_ctx: Box<dyn EncoderInterface> = 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);
         }