]> git.nihav.org Git - nihav-encoder.git/commitdiff
factor out stream encoder creation logic
authorKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 14 Apr 2026 16:25:23 +0000 (18:25 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 14 Apr 2026 16:25:23 +0000 (18:25 +0200)
The original instances were slightly different so it may re-introduce
some bugs but it's better to fix or change code just in one place.

src/main.rs
src/transcoder.rs

index feb3441d2fb2db16994b80b09a5df98b7c2d637f..ac55eab98628c05a9f732d678f5a746db43769a3 100644 (file)
@@ -352,7 +352,7 @@ fn main() {
                 }
             },
             "--calc-len" => {
-                transcoder.calc_len = true;
+                transcoder.qsupport.calc_len = true;
             },
             "--print-profiles" => {
                 println!("Supported profiles:");
@@ -478,8 +478,8 @@ fn main() {
         let max_id = sm.iter().fold(0u32, |id, strm| id.max(strm.id));
         for stream in sm.iter() {
             let mut newstream = (*stream).clone();
-            if transcoder.global_tb == (0, 0) && newstream.get_media_type() == StreamType::Video {
-                transcoder.global_tb = newstream.get_timebase();
+            if transcoder.qsupport.global_tb == (0, 0) && newstream.get_media_type() == StreamType::Video {
+                transcoder.qsupport.global_tb = newstream.get_timebase();
             }
             newstream.id += start;
             ism.add_stream(newstream);
@@ -602,32 +602,32 @@ fn main() {
 
     let mux_quirks = mux_creator.get_quirks();
     if mux_quirks.is_fixed_duration() {
-        transcoder.calc_len = true;
+        transcoder.qsupport.calc_len = true;
     }
-    transcoder.fixed_rate = mux_quirks.is_fixed_rate();
+    transcoder.qsupport.fixed_rate = mux_quirks.is_fixed_rate();
     transcoder.queue.set_sync(force_sync || !mux_quirks.is_unsync());
 
-    if transcoder.calc_len {
+    if transcoder.qsupport.calc_len {
         transcoder.debug_log(DebugLog::GENERAL, "Calculating total length of input streams");
 
         let mut sids = Vec::new();
-        transcoder.nframes.clear();
+        transcoder.qsupport.nframes.clear();
         for (dmx, _) in demuxers.iter_mut() {
-            let sstart = transcoder.nframes.len();
+            let sstart = transcoder.qsupport.nframes.len();
             let sm = dmx.get_stream_manager();
             sids.clear();
             for stream in sm.iter() {
-                transcoder.nframes.push(0);
+                transcoder.qsupport.nframes.push(0);
                 sids.push(stream.get_id());
             }
 
             while let Ok(pkt) = dmx.get_frame() {
                 let stream = pkt.get_stream();
                 let pos = sstart + sids.iter().position(|&x| x == stream.get_id()).unwrap();
-                transcoder.nframes[pos] += 1;
+                transcoder.qsupport.nframes[pos] += 1;
             }
         }
-        for (istr, nf) in transcoder.nframes.iter().enumerate() {
+        for (istr, nf) in transcoder.qsupport.nframes.iter().enumerate() {
             if let Some(ref mut dbg) = transcoder.debug {
                 dbg.log(DebugLog::GENERAL, &format!(" Stream {istr} {nf} frame(s)"));
             }
index 4a164b2b04fa68c83eba526ee1b02c0152132db4..dadf4bb163a043ee813163f2a49569d8404ff218 100644 (file)
@@ -502,6 +502,14 @@ impl OutputQueue {
     }
 }
 
+#[derive(Default)]
+pub struct QuirkSupport {
+    pub calc_len:       bool,
+    pub nframes:        Vec<usize>,
+    pub global_tb:      (u32, u32),
+    pub fixed_rate:     bool,
+}
+
 #[derive(Default)]
 #[allow(clippy::type_complexity)]
 pub struct Transcoder {
@@ -522,12 +530,9 @@ pub struct Transcoder {
     pub end:            NATimePoint,
     pub verbose:        u8,
 
-    pub calc_len:       bool,
-    pub nframes:        Vec<usize>,
-    pub global_tb:      (u32, u32),
+    pub qsupport:       QuirkSupport,
 
     pub queue:          OutputQueue,
-    pub fixed_rate:     bool,
 
     pub profile:        Option<EncodingProfile>,
 
@@ -875,6 +880,150 @@ impl Transcoder {
             parse_and_apply_options!(dec, &self.istr_opts[str_idx].dec_opts, name);
         }
     }
+    fn create_encoder(enc_reg: &RegisteredEncoders, qsupp: &mut QuirkSupport, iidx: usize, istr: &NAStream, out_sm: &mut StreamManager, oopts: &mut OutputStreamOptions, scale_opts: &[(String, String)]) -> Option<OutputMode> {
+        let out_id = out_sm.get_num_streams() as u32;
+
+        let enc_create = enc_reg.find_encoder(oopts.enc_name.as_str());
+        if enc_create.is_none() {
+            println!("encoder '{}' not found", oopts.enc_name.as_str());
+            return None;
+        }
+        let mut encoder = (enc_create.unwrap())();
+        let forced_out = oopts.enc_params.format != NACodecTypeInfo::None;
+        let iformat = istr.get_info().get_properties();
+        let mut default_afmt = false;
+        if oopts.enc_params.format == NACodecTypeInfo::None {
+            oopts.enc_params.format = istr.get_info().get_properties();
+            default_afmt = true;
+        } else {
+            match (&iformat, &mut oopts.enc_params.format) {
+                (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(ref mut dvinfo)) => {
+                    if dvinfo.width == 0 {
+                        dvinfo.width = svinfo.width;
+                    }
+                    if dvinfo.height == 0 {
+                        dvinfo.height = svinfo.height;
+                    }
+                },
+                (NACodecTypeInfo::Audio(sainfo), NACodecTypeInfo::Audio(ref mut dainfo)) => {
+                    if dainfo.sample_rate == 0 {
+                        dainfo.sample_rate = sainfo.sample_rate;
+                    }
+                    if dainfo.format == SND_NO_FORMAT {
+                        dainfo.format = sainfo.format;
+                        default_afmt = true;
+                    }
+                    if dainfo.channels == 0 {
+                        dainfo.channels = sainfo.channels;
+                    }
+                    if dainfo.block_len == 0 {
+                        dainfo.block_len = sainfo.block_len;
+                    }
+                },
+                _ => {},
+            };
+        }
+        if qsupp.global_tb != (0, 0) && oopts.enc_params.format.is_audio() {
+            oopts.enc_params.tb_num = qsupp.global_tb.0;
+            oopts.enc_params.tb_den = qsupp.global_tb.1;
+        }
+        if oopts.enc_params.tb_num == 0 {
+            oopts.enc_params.tb_num = istr.tb_num;
+            oopts.enc_params.tb_den = istr.tb_den;
+        }
+        if oopts.enc_name.as_str() == "pcm" && default_afmt {
+            if let NACodecTypeInfo::Audio(ref mut dainfo) = oopts.enc_params.format {
+                dainfo.format = SND_S16_FORMAT;
+            }
+        }
+        let ret_eparams = encoder.negotiate_format(&oopts.enc_params);
+        if ret_eparams.is_err() {
+            println!("cannot negotiate encoding parameters");
+            return None;
+        }
+        let ret_eparams = ret_eparams.unwrap();
+
+        let name = format!("output stream {}", out_id);
+        parse_and_apply_options!(encoder, &oopts.enc_opts, name);
+
+        if qsupp.calc_len && qsupp.nframes.len() > iidx {
+            encoder.set_options(&[NAOption{name: "nframes", value: NAValue::Int(qsupp.nframes[iidx] as i64)}]);
+        }
+
+        let ret = encoder.init(out_id, ret_eparams);
+        if ret.is_err() {
+            println!("error initialising encoder");
+            return None;
+        }
+
+        parse_and_apply_options!(encoder, &oopts.enc_opts, name);
+
+        let enc_stream = ret.unwrap();
+        let real_fmt = enc_stream.get_info().get_properties();
+        //todo check for params mismatch
+        let enc_ctx: Box<dyn EncoderInterface> = match (&iformat, &real_fmt) {
+                (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => {
+                    let force_cfr = qsupp.fixed_rate || (ret_eparams.flags & ENC_MODE_CFR) != 0;
+                    if force_cfr && enc_stream.tb_den / enc_stream.tb_num > 120 {
+                        println!("Timebase {}/{} is too much for constant framerate!", enc_stream.tb_num, enc_stream.tb_den);
+                        return None;
+                    }
+                    if svinfo == dvinfo && !forced_out {
+                        Box::new(VideoEncodeContext {
+                            encoder,
+                            vinfo: *dvinfo,
+                            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, scale_opts);
+                        if ret.is_err() {
+                            println!("cannot create scaler");
+                            return None;
+                        }
+                        let scaler = ret.unwrap();
+                        let ret = alloc_video_buffer(*dvinfo, 4);
+                        if ret.is_err() {
+                            println!("cannot create scaler buffer");
+                            return None;
+                        }
+                        let scaler_buf = ret.unwrap();
+                        Box::new(VideoEncodeContext {
+                            encoder,
+                            vinfo: *dvinfo,
+                            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)) => {
+                    let icodec = istr.get_info().get_name();
+                    if (sainfo == dainfo) && (icodec != "pcm" || oopts.enc_name.as_str() == "pcm") {
+                        Box::new(AudioEncodeContext { encoder, cvt: None, sainfo: *sainfo, dainfo: *dainfo, vol: oopts.volume })
+                    } else {
+                        let dchmap = if let Some(ret) = generate_channel_map(dainfo) {
+                                ret
+                            } else {
+                                return None;
+                            };
+                        let acvt = AudioConverter::new(sainfo, dainfo, dchmap);
+                        Box::new(AudioEncodeContext { encoder, cvt: Some(acvt), sainfo: *sainfo, dainfo: *dainfo, vol: oopts.volume })
+                    }
+                },
+                _ => unreachable!(),
+            };
+        out_sm.add_stream_ref(enc_stream);
+        Some(OutputMode::Encode(out_id, enc_ctx))
+    }
     pub fn register_output_stream(&mut self, cname: &str, istr: NAStreamRef, iidx: usize, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> RegisterResult {
         let out_id = out_sm.get_num_streams() as u32;
         if let Some(str_idx) = self.istr_opts.iter().position(|el| el.id == (istr.get_num() as u32)) {
@@ -901,146 +1050,11 @@ impl Transcoder {
                 out_sm.add_stream((*istr).clone());
                 self.encoders.push(OutputMode::Copy(out_id));
             } else if cname == "any" || oopts.enc_name.as_str() == cname {
-                let enc_create = enc_reg.find_encoder(oopts.enc_name.as_str());
-                if enc_create.is_none() {
-                    println!("encoder '{}' not found", oopts.enc_name.as_str());
-                    return RegisterResult::Failed;
-                }
-                let mut encoder = (enc_create.unwrap())();
-                let forced_out = oopts.enc_params.format != NACodecTypeInfo::None;
-                let iformat = istr.get_info().get_properties();
-                let mut default_afmt = false;
-                if oopts.enc_params.format == NACodecTypeInfo::None {
-                    oopts.enc_params.format = istr.get_info().get_properties();
-                    default_afmt = true;
+                if let Some(enc_m) = Self::create_encoder(enc_reg, &mut self.qsupport, iidx, &istr, out_sm, oopts, &self.scale_opts) {
+                    self.encoders.push(enc_m);
                 } else {
-                    match (&iformat, &mut oopts.enc_params.format) {
-                        (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(ref mut dvinfo)) => {
-                            if dvinfo.width == 0 {
-                                dvinfo.width = svinfo.width;
-                            }
-                            if dvinfo.height == 0 {
-                                dvinfo.height = svinfo.height;
-                            }
-                        },
-                        (NACodecTypeInfo::Audio(sainfo), NACodecTypeInfo::Audio(ref mut dainfo)) => {
-                            if dainfo.sample_rate == 0 {
-                                dainfo.sample_rate = sainfo.sample_rate;
-                            }
-                            if dainfo.format == SND_NO_FORMAT {
-                                dainfo.format = sainfo.format;
-                                default_afmt = true;
-                            }
-                            if dainfo.channels == 0 {
-                                dainfo.channels = sainfo.channels;
-                            }
-                            if dainfo.block_len == 0 {
-                                dainfo.block_len = sainfo.block_len;
-                            }
-                        },
-                        _ => {},
-                    };
-                }
-                if self.global_tb != (0, 0) && oopts.enc_params.format.is_audio() {
-                    oopts.enc_params.tb_num = self.global_tb.0;
-                    oopts.enc_params.tb_den = self.global_tb.1;
-                }
-                if oopts.enc_params.tb_num == 0 {
-                    oopts.enc_params.tb_num = istr.tb_num;
-                    oopts.enc_params.tb_den = istr.tb_den;
-                }
-                if oopts.enc_name.as_str() == "pcm" && default_afmt {
-                    if let NACodecTypeInfo::Audio(ref mut dainfo) = oopts.enc_params.format {
-                        dainfo.format = SND_S16_FORMAT;
-                    }
-                }
-                let ret_eparams = encoder.negotiate_format(&oopts.enc_params);
-                if ret_eparams.is_err() {
-                    println!("cannot negotiate encoding parameters");
                     return RegisterResult::Failed;
                 }
-                let ret_eparams = ret_eparams.unwrap();
-
-                let name = format!("output stream {}", out_id);
-                parse_and_apply_options!(encoder, &oopts.enc_opts, name);
-
-                if self.calc_len && self.nframes.len() > iidx {
-                    encoder.set_options(&[NAOption{name: "nframes", value: NAValue::Int(self.nframes[iidx] as i64)}]);
-                }
-
-                let ret = encoder.init(out_id, ret_eparams);
-                if ret.is_err() {
-                    println!("error initialising encoder");
-                    return RegisterResult::Failed;
-                }
-
-                parse_and_apply_options!(encoder, &oopts.enc_opts, name);
-
-                let enc_stream = ret.unwrap();
-                let real_fmt = enc_stream.get_info().get_properties();
-//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 force_cfr && enc_stream.tb_den / enc_stream.tb_num > 120 {
-                                println!("Timebase {}/{} is too much for constant framerate!", enc_stream.tb_num, enc_stream.tb_den);
-                                return RegisterResult::Failed;
-                            }
-                            if svinfo == dvinfo && !forced_out {
-                                Box::new(VideoEncodeContext {
-                                    encoder,
-                                    vinfo: *dvinfo,
-                                    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);
-                                if ret.is_err() {
-                                    println!("cannot create scaler");
-                                    return RegisterResult::Failed;
-                                }
-                                let scaler = ret.unwrap();
-                                let ret = alloc_video_buffer(*dvinfo, 4);
-                                if ret.is_err() {
-                                    println!("cannot create scaler buffer");
-                                    return RegisterResult::Failed;
-                                }
-                                let scaler_buf = ret.unwrap();
-                                Box::new(VideoEncodeContext {
-                                    encoder,
-                                    vinfo: *dvinfo,
-                                    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)) => {
-                            let icodec = istr.get_info().get_name();
-                            if (sainfo == dainfo) && (icodec != "pcm" || oopts.enc_name.as_str() == "pcm") {
-                                Box::new(AudioEncodeContext { encoder, cvt: None, sainfo: *sainfo, dainfo: *dainfo, vol: oopts.volume })
-                            } else {
-                                let dchmap = if let Some(ret) = generate_channel_map(dainfo) {
-                                        ret
-                                    } else {
-                                        return RegisterResult::Failed;
-                                    };
-                                let acvt = AudioConverter::new(sainfo, dainfo, dchmap);
-                                Box::new(AudioEncodeContext { encoder, cvt: Some(acvt), sainfo: *sainfo, dainfo: *dainfo, vol: oopts.volume })
-                            }
-                        },
-                        _ => unreachable!(),
-                    };
-                out_sm.add_stream_ref(enc_stream);
-                self.encoders.push(OutputMode::Encode(out_id, enc_ctx));
             } else {
 println!("encoder {} is not supported by output (expected {})", istr.id, istr.get_info().get_name());
                 return RegisterResult::Failed;
@@ -1121,115 +1135,12 @@ println!("encoder {} is not supported by output (expected {})", istr.id, istr.ge
                 }
             }
 
-            let enc_create = enc_reg.find_encoder(&oopts.enc_name);
-            if enc_create.is_none() {
-                println!("encoder '{}' not found", oopts.enc_name.as_str());
-                return RegisterResult::Failed;
-            }
-            let mut encoder = (enc_create.unwrap())();
-            if oopts.enc_params.format == NACodecTypeInfo::None {
-                oopts.enc_params.format = istr.get_info().get_properties();
-            }
-            if self.global_tb != (0, 0) {
-                oopts.enc_params.tb_num = self.global_tb.0;
-                oopts.enc_params.tb_den = self.global_tb.1;
+            if let Some(enc_m) = Self::create_encoder(enc_reg, &mut self.qsupport, iidx, &istr, out_sm, &mut oopts, &self.scale_opts) {
+                self.encoders.push(enc_m);
+                self.ostr_opts.push(oopts);
             } else {
-                oopts.enc_params.tb_num = istr.tb_num;
-                oopts.enc_params.tb_den = istr.tb_den;
-            }
-            if oopts.enc_name.as_str() == "pcm" {
-                if let NACodecTypeInfo::Audio(ref mut afmt) = oopts.enc_params.format {
-                    if afmt.format.bits == 0 || (afmt.format.bits & 7) != 0 {
-                        afmt.format = SND_S16_FORMAT;
-                    }
-                }
-            }
-            let ret_eparams = encoder.negotiate_format(&oopts.enc_params);
-            if ret_eparams.is_err() {
-                println!("cannot negotiate encoding parameters");
                 return RegisterResult::Failed;
             }
-            let ret_eparams = ret_eparams.unwrap();
-
-            let ret = encoder.init(out_id, ret_eparams);
-            if ret.is_err() {
-                println!("error initialising encoder");
-                return RegisterResult::Failed;
-            }
-
-            parse_and_apply_options!(encoder, &oopts.enc_opts, oopts.enc_name);
-
-            let enc_stream = ret.unwrap();
-//todo check for params mismatch
-            let real_fmt = enc_stream.get_info().get_properties();
-            let enc_ctx: Box<dyn EncoderInterface> = match (&oopts.enc_params.format, &ret_eparams.format) {
-                    (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => {
-                        let force_cfr = self.fixed_rate || (ret_eparams.flags & ENC_MODE_CFR) != 0;
-                        if force_cfr && enc_stream.tb_den / enc_stream.tb_num > 120 {
-                            println!("Timebase {}/{} is too much for constant framerate!", enc_stream.tb_num, enc_stream.tb_den);
-                            return RegisterResult::Failed;
-                        }
-                        if svinfo == dvinfo {
-                            Box::new(VideoEncodeContext {
-                                encoder,
-                                vinfo: *dvinfo,
-                                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);
-                            if ret.is_err() {
-                                println!("cannot create scaler");
-                                return RegisterResult::Failed;
-                            }
-                            let scaler = ret.unwrap();
-                            let ret = alloc_video_buffer(*dvinfo, 4);
-                            if ret.is_err() {
-                                println!("cannot create scaler buffer");
-                                return RegisterResult::Failed;
-                            }
-                            let scaler_buf = ret.unwrap();
-                            Box::new(VideoEncodeContext {
-                                encoder,
-                                vinfo: *dvinfo,
-                                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(ref_dainfo)) => {
-                        let mut dainfo = *ref_dainfo;
-                        if &oopts.enc_name == "pcm" {
-                            if let NACodecTypeInfo::Audio(oformat) = real_fmt {
-                                dainfo.block_len = oformat.block_len;
-                            }
-                        }
-                        if sainfo == &dainfo {
-                            Box::new(AudioEncodeContext { encoder, cvt: None, sainfo: *sainfo, dainfo, vol: oopts.volume })
-                        } else {
-                            let dchmap = if let Some(ret) = generate_channel_map(&dainfo) {
-                                    ret
-                                } else {
-                                    return RegisterResult::Failed;
-                                };
-                            let acvt = AudioConverter::new(sainfo, &dainfo, dchmap);
-                            Box::new(AudioEncodeContext { encoder, cvt: Some(acvt), sainfo: *sainfo, dainfo, vol: oopts.volume })
-                        }
-                    },
-                    _ => unreachable!(),
-                };
-            out_sm.add_stream_ref(enc_stream);
-            self.encoders.push(OutputMode::Encode(out_id, enc_ctx));
-            self.ostr_opts.push(oopts);
         }
         RegisterResult::Ok
     }