From ff7c03667b4f16bb215c4518c3d545eb51e70157 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Tue, 14 Apr 2026 18:25:23 +0200 Subject: [PATCH] factor out stream encoder creation logic 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 | 22 +-- src/transcoder.rs | 405 ++++++++++++++++++---------------------------- 2 files changed, 169 insertions(+), 258 deletions(-) diff --git a/src/main.rs b/src/main.rs index feb3441..ac55eab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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)")); } diff --git a/src/transcoder.rs b/src/transcoder.rs index 4a164b2..dadf4bb 100644 --- a/src/transcoder.rs +++ b/src/transcoder.rs @@ -502,6 +502,14 @@ impl OutputQueue { } } +#[derive(Default)] +pub struct QuirkSupport { + pub calc_len: bool, + pub nframes: Vec, + 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, - pub global_tb: (u32, u32), + pub qsupport: QuirkSupport, pub queue: OutputQueue, - pub fixed_rate: bool, pub profile: Option, @@ -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 { + 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 = 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 = 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 = 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 } -- 2.39.5