}
}
+#[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 {
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>,
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)) {
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;
}
}
- 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
}