From 28a8363768bc91ee3db5fab00cb9a512c9e56d5f Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Fri, 14 Mar 2025 18:30:39 +0100 Subject: [PATCH] split transcoder code into separate module --- src/main.rs | 889 +-------------------------------- src/{main.rs => transcoder.rs} | 822 ++---------------------------- 2 files changed, 80 insertions(+), 1631 deletions(-) copy src/{main.rs => transcoder.rs} (52%) diff --git a/src/main.rs b/src/main.rs index bcd3213..e1eba18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,7 @@ extern crate nihav_registry; extern crate nihav_allstuff; use std::fs::File; -use std::io::{BufReader, Write}; -use std::pin::Pin; -use nihav_core::io::byteio::{FileReader, ByteReader}; +use std::io::Write; use nihav_core::frame::*; use nihav_core::options::*; use nihav_core::codecs::*; @@ -19,179 +17,6 @@ use nihav_registry::register; use std::env; use std::time::{Duration, Instant}; -mod demux; -use crate::demux::*; -mod null; -mod acvt; -use crate::acvt::*; -mod imgseq; -use crate::imgseq::*; - -#[derive(Clone,Copy,Default,PartialEq)] -enum RegisterResult { - #[default] - Ok, - Ignored, - Failed, -} - -pub struct SelfBorrow { - bval: T, - dst: Option, -} - -impl SelfBorrow { - pub fn new(src: T, create: F) -> Pin> - where F: Fn(*mut T) -> U { - let mut obj = Box::pin(Self{ - bval: src, - dst: None, - }); - let ptr = &mut obj.bval as *mut T; - obj.dst = Some(create(ptr)); - obj - } - fn get_object(&self) -> &U { - if let Some(ref dst) = self.dst { - dst - } else { - unreachable!() - } - } - fn get_object_mut(&mut self) -> &mut U { - if let Some(ref mut dst) = self.dst { - dst - } else { - unreachable!() - } - } -} - -impl Drop for SelfBorrow { - fn drop(&mut self) { - self.dst = None; - } -} - -pub type SBBox = Pin>>; - -const SND_NO_FORMAT: NASoniton = NASoniton { bits: 0, be: false, packed: false, planar: false, float: false, signed: false }; - -fn format_time(ms: u64) -> String { - let s = ms / 1000; - let ds = (ms % 1000) / 100; - let (min, s) = (s / 60, s % 60); - let (h, min) = (min / 60, min % 60); - if h == 0 { - if min == 0 { - format!("{}.{}", s, ds) - } else { - format!("{}:{:02}.{}", min, s, ds) - } - } else { - format!("{}:{:02}:{:02}.{}", h, min, s, ds) - } -} - -fn print_options(name: &str, options: &[NAOptionDefinition]) { - if options.is_empty() { - println!("No custom options."); - } else { - println!("Options for '{}'", name); - for opt in options.iter() { - println!(" {}", opt); - } - } -} - -fn parse_bitrate(strval: &str) -> Result { - let mut val = 0; - let mut has_suffix = false; - for ch in strval.chars() { - match ch { - _ if has_suffix => return Err(()), - '0'..='9' => { - if val >= std::u32::MAX / 100 { - return Err(()); - } - val = val * 10 + ch.to_digit(10).unwrap_or(0); - }, - 'k' | 'K' => { - if val >= std::u32::MAX / 1000 { - return Err(()); - } - val *= 1000; - has_suffix = true; - }, - 'm' | 'M' => { - if val >= std::u32::MAX / 1000000 { - return Err(()); - } - val *= 1000000; - has_suffix = true; - }, - _ => return Err(()), - }; - } - Ok(val) -} - -struct OptionArgs { - name: String, - value: Option, -} - -struct InputStreamOptions { - id: u32, - drop: bool, - dec_opts: Vec, -} - -struct OutputStreamOptions { - id: u32, - enc_params: EncodeParameters, - enc_name: String, - enc_opts: Vec, -} - -enum OutputConvert { - Video(NAScale, NABufferType), - Audio(AudioConverter), - None, -} - -#[allow(clippy::large_enum_variant)] -enum OutputMode { - Drop, - Copy(u32), - Encode(u32, Box, OutputConvert), -} - -#[derive(Default)] -#[allow(clippy::type_complexity)] -struct Transcoder { - input_name: [Option; 16], - input_fmt: [Option; 16], - output_name: String, - output_fmt: Option, - demux_opts: [Vec; 16], - mux_opts: Vec, - istr_opts: Vec, - ostr_opts: Vec, - scale_opts: Vec<(String, String)>, - decoders: Vec, Box, Box)>>, - encoders: Vec, - no_video: bool, - no_audio: bool, - start: NATimePoint, - end: NATimePoint, - verbose: u8, - - calc_len: bool, - nframes: Vec, - global_tb: (u32, u32), -} - macro_rules! parse_and_apply_options { ($obj: expr, $in_opts: expr, $name: expr) => { let mut opts = Vec::with_capacity($in_opts.len()); @@ -223,699 +48,39 @@ macro_rules! parse_and_apply_options { } } -impl Transcoder { - fn new() -> Self { Self::default() } - fn parse_istream_options(&mut self, opt0: &str, opt1: &str) -> bool { - let (_, strno) = opt0.split_at(9); - let ret = strno.parse::(); - if ret.is_err() { return false; } - let streamno = ret.unwrap(); - - let sidx = if let Some(idx) = self.istr_opts.iter().position(|el| el.id == streamno) { - idx - } else { - self.istr_opts.push(InputStreamOptions {id: streamno, drop: false, dec_opts: Vec::new() }); - self.istr_opts.len() - 1 - }; - let istr = &mut self.istr_opts[sidx]; - - for opt in opt1.split(',') { - let oval: Vec<_> = opt.split('=').collect(); - if oval.len() == 1 { - match oval[0] { - "drop" => { istr.drop = true; }, - _ => { - istr.dec_opts.push(OptionArgs{ name: oval[0].to_string(), value: None }); - }, - }; - } else if oval.len() == 2 { - istr.dec_opts.push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); - } else { - println!("unrecognized option '{}'", opt); - } - } - true - } - fn parse_ostream_options(&mut self, opt0: &str, opt1: &str, enc_reg: &RegisteredEncoders) -> bool { - let (_, strno) = opt0.split_at(9); - let ret = strno.parse::(); - if ret.is_err() { return false; } - let streamno = ret.unwrap(); - - let sidx = if let Some(idx) = self.ostr_opts.iter().position(|el| el.id == streamno) { - idx - } else { - self.ostr_opts.push(OutputStreamOptions {id: streamno, enc_name: String::new(), enc_params: EncodeParameters::default(), enc_opts: Vec::new() }); - self.ostr_opts.len() - 1 - }; - let ostr = &mut self.ostr_opts[sidx]; - - for opt in opt1.split(',') { - let oval: Vec<_> = opt.split('=').collect(); - if oval.len() == 1 { - match oval[0] { - "flip" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); - } - if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { - vinfo.flipped = true; - } else { - println!("video option for audio stream"); - } - }, - "noflip" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); - } - if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { - vinfo.flipped = false; - } else { - println!("video option for audio stream"); - } - }, - _ => { - ostr.enc_opts.push(OptionArgs{ name: oval[0].to_string(), value: None }); - }, - }; - } else if oval.len() == 2 { -//todo parse encoder options, store, init later - match oval[0] { - "timebase" => { - let mut parts = oval[1].split('/'); - let num = parts.next().unwrap(); - let den = parts.next(); - if let Some(den) = den { - let rnum = num.parse::(); - let rden = den.parse::(); - if let (Ok(num), Ok(den)) = (rnum, rden) { - ostr.enc_params.tb_num = num; - ostr.enc_params.tb_den = den; - } else { - println!("invalid timebase value"); - } - } else { - println!("invalid timebase format (should be num/den)"); - } - }, - "encoder" => { - if enc_reg.find_encoder(oval[1]).is_some() { - ostr.enc_name = oval[1].to_string(); - } else { - println!("unknown encoder '{}'", oval[1]); - ostr.enc_name = oval[1].to_string(); - } - }, - "width" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); - } - if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - vinfo.width = val; - } else { - println!("invalid width"); - } - } else { - println!("video option for audio stream"); - } - }, - "height" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); - } - if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - vinfo.height = val; - } else { - println!("invalid height"); - } - } else { - println!("video option for audio stream"); - } - }, - "pixfmt" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); - } - if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - vinfo.format = val; - } else { - println!("invalid pixel format"); - } - } else { - println!("video option for audio stream"); - } - }, - "srate" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_NO_FORMAT, 0)); - } - if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - ainfo.sample_rate = val; - } else { - println!("invalid sampling rate"); - } - } else { - println!("audio option for video stream"); - } - }, - "channels" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_NO_FORMAT, 0)); - } - if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - ainfo.channels = val; - } else { - println!("invalid number of channels"); - } - } else { - println!("audio option for video stream"); - } - }, - "block_len" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_NO_FORMAT, 0)); - } - if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - ainfo.block_len = val; - } else { - println!("invalid block_length"); - } - } else { - println!("audio option for video stream"); - } - }, - "sfmt" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_NO_FORMAT, 0)); - } - if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - ainfo.format = val; - } else { - println!("invalid audio format"); - } - } else { - println!("audio option for video stream"); - } - }, -// todo channel map negotiation - /*"chmap" => { - if ostr.enc_params.format == NACodecTypeInfo::None { - ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_NO_FORMAT, 0)); - } - if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - ainfo.chmap = val; - } else { - println!("invalid channel map"); - } - } else { - println!("audio option for video stream"); - } - },*/ - "bitrate" => { - let ret = parse_bitrate(oval[1]); - if let Ok(val) = ret { - ostr.enc_params.bitrate = val; - } else { - println!("invalid bitrate value"); - } - }, - "quality" => { - let ret = oval[1].parse::(); - if let Ok(val) = ret { - ostr.enc_params.quality = val; - } else { - println!("invalid quality value"); - } - }, - _ => { - ostr.enc_opts.push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); - }, - } - } else { - println!("unrecognized option '{}'", opt); - } - } - true - } - fn parse_demuxer_options(&mut self, opts: &str, dmx_reg: &RegisteredDemuxers, input_no: usize) -> bool { - for opt in opts.split(',') { - let oval: Vec<_> = opt.split('=').collect(); - if oval.len() == 1 { - self.demux_opts[input_no].push(OptionArgs{ name: oval[0].to_string(), value: None }); - } else if oval.len() == 2 { - if oval[0] == "format" { - if dmx_reg.find_demuxer(oval[1]).is_some() { - self.input_fmt[input_no] = Some(oval[1].to_string()); - } else { - println!("unknown demuxer format '{}'", oval[1]); - } - } else { - self.demux_opts[input_no].push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); - } - } else { - println!("unrecognized option '{}'", opt); - } - } - true - } - fn parse_muxer_options(&mut self, opts: &str, mux_reg: &RegisteredMuxers) -> bool { - for opt in opts.split(',') { - let oval: Vec<_> = opt.split('=').collect(); - if oval.len() == 1 { - self.mux_opts.push(OptionArgs{ name: oval[0].to_string(), value: None }); - } else if oval.len() == 2 { - if oval[0] == "format" { - if mux_reg.find_muxer(oval[1]).is_some() { - self.output_fmt = Some(oval[1].to_string()); - } else { - println!("unknown muxer format '{}'", oval[1]); - } - } else { - self.mux_opts.push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); - } - } else { - println!("unrecognized option '{}'", opt); - } - } - true - } - fn parse_scale_options(&mut self, opts: &str) -> bool { - for opt in opts.split(',') { - let oval: Vec<_> = opt.split('=').collect(); - if oval.len() == 1 { - self.scale_opts.push((oval[0].to_string(), "".to_string())); - } else if oval.len() == 2 { - self.scale_opts.push((oval[0].to_string(), oval[1].to_string())); - } else { - println!("unrecognized option '{}'", opt); - return false; - } - } - true - } - fn apply_decoder_options(&self, dec: &mut dyn NADecoder, str_id: u32) { - if let Some(str_idx) = self.istr_opts.iter().position(|el| el.id == str_id) { - let dec_opts = dec.get_supported_options(); - if dec_opts.is_empty() { return; } - let name = format!("input stream {}", str_id); - parse_and_apply_options!(dec, &self.istr_opts[str_idx].dec_opts, name); - } - } - 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)) { - if self.istr_opts[str_idx].drop { - self.encoders.push(OutputMode::Drop); - return RegisterResult::Ignored; - } - } - - if let Some(str_idx) = self.ostr_opts.iter().position(|el| el.id == out_id) { - let oopts = &mut self.ostr_opts[str_idx]; - if oopts.enc_name.as_str() == "copy" && (cname == "any" || istr.get_info().get_name() == cname) { - 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(); - if oopts.enc_params.format == NACodecTypeInfo::None { - oopts.enc_params.format = istr.get_info().get_properties(); - } 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; - } - 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; - } - 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(); - -//todo check for params mismatch - let cvt = match (&iformat, &ret_eparams.format) { - (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => { - if svinfo == dvinfo && !forced_out { - OutputConvert::None - } 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 cvt_buf = ret.unwrap(); - OutputConvert::Video(scaler, cvt_buf) - } - }, - (NACodecTypeInfo::Audio(sainfo), NACodecTypeInfo::Audio(dainfo)) => { - let icodec = istr.get_info().get_name(); - if (sainfo == dainfo) && (icodec != "pcm" || oopts.enc_name.as_str() == "pcm") { - OutputConvert::None - } else { - let dchmap = match dainfo.channels { - 1 => NAChannelMap::from_ms_mapping(0x4), - 2 => NAChannelMap::from_ms_mapping(0x3), - _ => { -println!("can't generate default channel map for {} channels", dainfo.channels); - return RegisterResult::Failed; - }, - }; - let acvt = AudioConverter::new(sainfo, dainfo, dchmap); -//todo channelmap - OutputConvert::Audio(acvt) - } - }, - _ => OutputConvert::None, - }; - 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; - } - out_sm.add_stream_ref(ret.unwrap()); +mod demux; +use crate::demux::FullRegister; +mod null; +mod acvt; +mod imgseq; +mod transcoder; +use crate::transcoder::*; - parse_and_apply_options!(encoder, &oopts.enc_opts, name); - self.encoders.push(OutputMode::Encode(out_id, encoder, cvt)); - } else { -println!("encoder {} is not supported by output (expected {})", istr.id, istr.get_info().get_name()); - return RegisterResult::Failed; - } - } else if cname == "any" || istr.get_info().get_name() == cname { - out_sm.add_stream((*istr).clone()); - self.encoders.push(OutputMode::Copy(out_id)); +fn format_time(ms: u64) -> String { + let s = ms / 1000; + let ds = (ms % 1000) / 100; + let (min, s) = (s / 60, s % 60); + let (h, min) = (min / 60, min % 60); + if h == 0 { + if min == 0 { + format!("{}.{}", s, ds) } else { - let mut oopts = OutputStreamOptions {id: out_id, enc_name: cname.to_owned(), enc_params: EncodeParameters::default(), enc_opts: Vec::new() }; - - let enc_create = enc_reg.find_encoder(cname); - if enc_create.is_none() { - println!("encoder '{}' not found", oopts.enc_name.as_str()); - return RegisterResult::Failed; - } - let mut encoder = (enc_create.unwrap())(); - 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; - } else { - oopts.enc_params.tb_num = istr.tb_num; - oopts.enc_params.tb_den = istr.tb_den; - } - 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(); - -//todo check for params mismatch - let cvt = match (&oopts.enc_params.format, &ret_eparams.format) { - (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => { - if svinfo == dvinfo { - OutputConvert::None - } 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 cvt_buf = ret.unwrap(); - OutputConvert::Video(scaler, cvt_buf) - } - }, - (NACodecTypeInfo::Audio(sainfo), NACodecTypeInfo::Audio(dainfo)) => { - if sainfo == dainfo { - OutputConvert::None - } else { - let dchmap = match dainfo.channels { - 1 => NAChannelMap::from_ms_mapping(0x4), - 2 => NAChannelMap::from_ms_mapping(0x3), - _ => { -println!("can't generate default channel map for {} channels", dainfo.channels); - return RegisterResult::Failed; - }, - }; -//todo channelmap - let acvt = AudioConverter::new(sainfo, dainfo, dchmap); - OutputConvert::Audio(acvt) - } - }, - _ => OutputConvert::None, - }; - let ret = encoder.init(out_id, ret_eparams); - if ret.is_err() { - println!("error initialising encoder"); - return RegisterResult::Failed; - } - out_sm.add_stream_ref(ret.unwrap()); - self.encoders.push(OutputMode::Encode(out_id, encoder, cvt)); - self.ostr_opts.push(oopts); - } - RegisterResult::Ok - } - fn map_single(&mut self, cname: &str, ctype: StreamType, src_sm: &StreamManager, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { - let mut found_stream = false; - for (iidx, istr) in src_sm.iter().enumerate() { - if istr.get_media_type() != ctype || found_stream { - self.encoders.push(OutputMode::Drop); - } else { - match self.register_output_stream(cname, istr, iidx, out_sm, enc_reg) { - RegisterResult::Ok => found_stream = true, - RegisterResult::Failed => return false, - RegisterResult::Ignored => {}, - }; - } - } - found_stream - } - fn negotiate_stream_map(&mut self, src_sm: &StreamManager, mux_caps: MuxerCapabilities, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { - match mux_caps { - MuxerCapabilities::SingleVideo(cname) => { - if self.no_video { return false; } - self.map_single(cname, StreamType::Video, src_sm, out_sm, enc_reg) - }, - MuxerCapabilities::SingleAudio(cname) => { - if self.no_audio { return false; } - self.map_single(cname, StreamType::Audio, src_sm, out_sm, enc_reg) - }, - MuxerCapabilities::SingleVideoAndAudio(vname, aname) => { - let mut found_vid = false; - let mut found_aud = false; - for (iidx, istr) in src_sm.iter().enumerate() { - if istr.get_media_type() == StreamType::Video && !found_vid && !self.no_video { - match self.register_output_stream(vname, istr, iidx, out_sm, enc_reg) { - RegisterResult::Ok => found_vid = true, - RegisterResult::Failed => return false, - RegisterResult::Ignored => {}, - }; - } else if istr.get_media_type() == StreamType::Audio && !found_aud && !self.no_audio { - match self.register_output_stream(aname, istr, iidx, out_sm, enc_reg) { - RegisterResult::Ok => found_aud = true, - RegisterResult::Failed => return false, - RegisterResult::Ignored => {}, - }; - } else { - self.encoders.push(OutputMode::Drop); - } - } - found_vid | found_aud - }, - MuxerCapabilities::OnlyVideo => { - if self.no_video { return false; } - - let mut found_vid = false; - for (iidx, istr) in src_sm.iter().enumerate() { - if istr.get_media_type() == StreamType::Video { - match self.register_output_stream("any", istr, iidx, out_sm, enc_reg) { - RegisterResult::Ok => found_vid = true, - RegisterResult::Failed => return false, - RegisterResult::Ignored => {}, - }; - } else { - self.encoders.push(OutputMode::Drop); - } - } - found_vid - }, - MuxerCapabilities::OnlyAudio => { - if self.no_audio { return false; } - - let mut found_aud = false; - for (iidx, istr) in src_sm.iter().enumerate() { - if istr.get_media_type() == StreamType::Audio { - match self.register_output_stream("any", istr, iidx, out_sm, enc_reg) { - RegisterResult::Ok => found_aud = true, - RegisterResult::Failed => return false, - RegisterResult::Ignored => {}, - }; - } else { - self.encoders.push(OutputMode::Drop); - } - } - found_aud - }, - MuxerCapabilities::Universal => { - for (iidx, istr) in src_sm.iter().enumerate() { - if (istr.get_media_type() == StreamType::Video && self.no_video) || - (istr.get_media_type() == StreamType::Audio && self.no_audio) { - self.encoders.push(OutputMode::Drop); - continue; - } - if self.register_output_stream("any", istr, iidx, out_sm, enc_reg) == RegisterResult::Failed { - return false; - } - } - true - }, + format!("{}:{:02}.{}", min, s, ds) } + } else { + format!("{}:{:02}:{:02}.{}", h, min, s, ds) } - fn create_demuxers(&mut self, demuxers: &mut Vec<(DemuxerObject, bool)>, full_reg: &FullRegister, print_info: bool) -> bool { - let mut isn_start = 0; - for (i, (iname, ifmt)) in self.input_name.iter().zip( - self.input_fmt.iter()).enumerate() { - match (iname, ifmt.as_ref().map(|s| s.as_str())) { - (Some(name), Some("imgseq")) => { - println!("trying image sequence {}", name); - let mut isdc = ImgSeqDemuxerCreator::new(name.as_str()); - parse_and_apply_options!(isdc, &self.demux_opts[i], "input"); - let isd = if let Ok(ctx) = isdc.open() { - ctx - } else { - println!("failed to create image sequence demuxer!"); - return false; - }; - let dmx = DemuxerObject::create_imgseq(isd); - if print_info { - for i in 0..dmx.get_num_streams() { - let s = dmx.get_stream(i).unwrap(); - let info = s.get_info(); - println!(" stream {}({}) - {} {}", i, i + isn_start, s, info.get_name()); - } - } - isn_start += dmx.get_num_streams(); - demuxers.push((dmx, false)) - }, - (Some(name), _) => { - let res = File::open(name); - if res.is_err() { - println!("error opening input"); - return false; - } - let file = res.unwrap(); - let file = BufReader::new(file); - let mut fr = FileReader::new_read(file); - let mut br = ByteReader::new(&mut fr); - let (is_raw, start, end) = if ifmt.is_none() { - detect_tags(&mut br) - } else { - (false, 0, None) - }; - - let nfr: Box = if start != 0 || end.is_some() { - let file = fr.finish(); - Box::new(BoundedFileReader::new_read(file, start, end).unwrap()) - } else { - Box::new(fr) - }; - let sb = SelfBorrow::new(nfr, |rd| { - unsafe { - ByteReader::new(rd.as_mut().unwrap().as_mut()) - } - }); +} - let mut dmx = DemuxerObject::create(sb, full_reg, name, ifmt, is_raw, print_info); - if dmx.is_none() { - println!("cannot find demuxer for '{}'", name); - return false; - } - parse_and_apply_options!(dmx, &self.demux_opts[i], "input"); - if print_info { - for i in 0..dmx.get_num_streams() { - let s = dmx.get_stream(i).unwrap(); - let info = s.get_info(); - println!(" stream {}({}) - {} {}", i, i + isn_start, s, info.get_name()); - } - } - isn_start += dmx.get_num_streams(); - demuxers.push((dmx, false)); - }, - _ => {}, - }; +fn print_options(name: &str, options: &[NAOptionDefinition]) { + if options.is_empty() { + println!("No custom options."); + } else { + println!("Options for '{}'", name); + for opt in options.iter() { + println!(" {}", opt); } - true } } diff --git a/src/main.rs b/src/transcoder.rs similarity index 52% copy from src/main.rs copy to src/transcoder.rs index bcd3213..b6ba295 100644 --- a/src/main.rs +++ b/src/transcoder.rs @@ -1,12 +1,6 @@ -extern crate nihav_core; -extern crate nihav_codec_support; -extern crate nihav_registry; -extern crate nihav_allstuff; - use std::fs::File; -use std::io::{BufReader, Write}; +use std::io::BufReader; use std::pin::Pin; -use nihav_core::io::byteio::{FileReader, ByteReader}; use nihav_core::frame::*; use nihav_core::options::*; use nihav_core::codecs::*; @@ -14,21 +8,13 @@ use nihav_core::demuxers::*; use nihav_core::muxers::*; use nihav_core::reorder::*; use nihav_core::scale::*; -use nihav_registry::detect; -use nihav_registry::register; -use std::env; -use std::time::{Duration, Instant}; -mod demux; -use crate::demux::*; -mod null; -mod acvt; use crate::acvt::*; -mod imgseq; +use crate::demux::*; use crate::imgseq::*; #[derive(Clone,Copy,Default,PartialEq)] -enum RegisterResult { +pub enum RegisterResult { #[default] Ok, Ignored, @@ -51,14 +37,14 @@ impl SelfBorrow { obj.dst = Some(create(ptr)); obj } - fn get_object(&self) -> &U { + pub fn get_object(&self) -> &U { if let Some(ref dst) = self.dst { dst } else { unreachable!() } } - fn get_object_mut(&mut self) -> &mut U { + pub fn get_object_mut(&mut self) -> &mut U { if let Some(ref mut dst) = self.dst { dst } else { @@ -75,36 +61,9 @@ impl Drop for SelfBorrow { pub type SBBox = Pin>>; -const SND_NO_FORMAT: NASoniton = NASoniton { bits: 0, be: false, packed: false, planar: false, float: false, signed: false }; +pub const SND_NO_FORMAT: NASoniton = NASoniton { bits: 0, be: false, packed: false, planar: false, float: false, signed: false }; -fn format_time(ms: u64) -> String { - let s = ms / 1000; - let ds = (ms % 1000) / 100; - let (min, s) = (s / 60, s % 60); - let (h, min) = (min / 60, min % 60); - if h == 0 { - if min == 0 { - format!("{}.{}", s, ds) - } else { - format!("{}:{:02}.{}", min, s, ds) - } - } else { - format!("{}:{:02}:{:02}.{}", h, min, s, ds) - } -} - -fn print_options(name: &str, options: &[NAOptionDefinition]) { - if options.is_empty() { - println!("No custom options."); - } else { - println!("Options for '{}'", name); - for opt in options.iter() { - println!(" {}", opt); - } - } -} - -fn parse_bitrate(strval: &str) -> Result { +pub fn parse_bitrate(strval: &str) -> Result { let mut val = 0; let mut has_suffix = false; for ch in strval.chars() { @@ -136,32 +95,32 @@ fn parse_bitrate(strval: &str) -> Result { Ok(val) } -struct OptionArgs { - name: String, - value: Option, +pub struct OptionArgs { + pub name: String, + pub value: Option, } -struct InputStreamOptions { - id: u32, - drop: bool, - dec_opts: Vec, +pub struct InputStreamOptions { + pub id: u32, + pub drop: bool, + pub dec_opts: Vec, } -struct OutputStreamOptions { - id: u32, - enc_params: EncodeParameters, - enc_name: String, - enc_opts: Vec, +pub struct OutputStreamOptions { + pub id: u32, + pub enc_params: EncodeParameters, + pub enc_name: String, + pub enc_opts: Vec, } -enum OutputConvert { +pub enum OutputConvert { Video(NAScale, NABufferType), Audio(AudioConverter), None, } #[allow(clippy::large_enum_variant)] -enum OutputMode { +pub enum OutputMode { Drop, Copy(u32), Encode(u32, Box, OutputConvert), @@ -169,63 +128,32 @@ enum OutputMode { #[derive(Default)] #[allow(clippy::type_complexity)] -struct Transcoder { - input_name: [Option; 16], - input_fmt: [Option; 16], - output_name: String, - output_fmt: Option, - demux_opts: [Vec; 16], - mux_opts: Vec, - istr_opts: Vec, - ostr_opts: Vec, - scale_opts: Vec<(String, String)>, - decoders: Vec, Box, Box)>>, - encoders: Vec, - no_video: bool, - no_audio: bool, - start: NATimePoint, - end: NATimePoint, - verbose: u8, - - calc_len: bool, - nframes: Vec, - global_tb: (u32, u32), -} - -macro_rules! parse_and_apply_options { - ($obj: expr, $in_opts: expr, $name: expr) => { - let mut opts = Vec::with_capacity($in_opts.len()); - let opt_def = $obj.get_supported_options(); - for opt in $in_opts.iter() { - let mut found = false; - for opt_def in opt_def.iter() { - let mut matches = opt.name == opt_def.name; - if !matches && opt.name.starts_with("no") { - let (_, name) = opt.name.split_at(2); - matches = name == opt_def.name; - } - if matches { - let arg = if let Some(ref strval) = opt.value { Some(strval) } else { None }; - let ret = opt_def.parse(&opt.name, arg); - if let Ok((val, _)) = ret { - opts.push(val); - } else { - println!("invalid option {} for {}", opt.name, $name); - } - found = true; - } - } - if !found { - println!(" ignoring option '{}' for {}", opt.name, $name); - } - } - $obj.set_options(opts.as_slice()); - } +pub struct Transcoder { + pub input_name: [Option; 16], + pub input_fmt: [Option; 16], + pub output_name: String, + pub output_fmt: Option, + pub demux_opts: [Vec; 16], + pub mux_opts: Vec, + pub istr_opts: Vec, + pub ostr_opts: Vec, + pub scale_opts: Vec<(String, String)>, + pub decoders: Vec, Box, Box)>>, + pub encoders: Vec, + pub no_video: bool, + pub no_audio: bool, + pub start: NATimePoint, + pub end: NATimePoint, + pub verbose: u8, + + pub calc_len: bool, + pub nframes: Vec, + pub global_tb: (u32, u32), } impl Transcoder { - fn new() -> Self { Self::default() } - fn parse_istream_options(&mut self, opt0: &str, opt1: &str) -> bool { + pub fn new() -> Self { Self::default() } + pub fn parse_istream_options(&mut self, opt0: &str, opt1: &str) -> bool { let (_, strno) = opt0.split_at(9); let ret = strno.parse::(); if ret.is_err() { return false; } @@ -256,7 +184,7 @@ impl Transcoder { } true } - fn parse_ostream_options(&mut self, opt0: &str, opt1: &str, enc_reg: &RegisteredEncoders) -> bool { + pub fn parse_ostream_options(&mut self, opt0: &str, opt1: &str, enc_reg: &RegisteredEncoders) -> bool { let (_, strno) = opt0.split_at(9); let ret = strno.parse::(); if ret.is_err() { return false; } @@ -473,7 +401,7 @@ impl Transcoder { } true } - fn parse_demuxer_options(&mut self, opts: &str, dmx_reg: &RegisteredDemuxers, input_no: usize) -> bool { + pub fn parse_demuxer_options(&mut self, opts: &str, dmx_reg: &RegisteredDemuxers, input_no: usize) -> bool { for opt in opts.split(',') { let oval: Vec<_> = opt.split('=').collect(); if oval.len() == 1 { @@ -494,7 +422,7 @@ impl Transcoder { } true } - fn parse_muxer_options(&mut self, opts: &str, mux_reg: &RegisteredMuxers) -> bool { + pub fn parse_muxer_options(&mut self, opts: &str, mux_reg: &RegisteredMuxers) -> bool { for opt in opts.split(',') { let oval: Vec<_> = opt.split('=').collect(); if oval.len() == 1 { @@ -515,7 +443,7 @@ impl Transcoder { } true } - fn parse_scale_options(&mut self, opts: &str) -> bool { + pub fn parse_scale_options(&mut self, opts: &str) -> bool { for opt in opts.split(',') { let oval: Vec<_> = opt.split('=').collect(); if oval.len() == 1 { @@ -529,7 +457,7 @@ impl Transcoder { } true } - fn apply_decoder_options(&self, dec: &mut dyn NADecoder, str_id: u32) { + pub fn apply_decoder_options(&self, dec: &mut dyn NADecoder, str_id: u32) { if let Some(str_idx) = self.istr_opts.iter().position(|el| el.id == str_id) { let dec_opts = dec.get_supported_options(); if dec_opts.is_empty() { return; } @@ -537,7 +465,7 @@ impl Transcoder { parse_and_apply_options!(dec, &self.istr_opts[str_idx].dec_opts, name); } } - fn register_output_stream(&mut self, cname: &str, istr: NAStreamRef, iidx: usize, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> RegisterResult { + 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)) { if self.istr_opts[str_idx].drop { @@ -746,7 +674,7 @@ println!("can't generate default channel map for {} channels", dainfo.channels); } RegisterResult::Ok } - fn map_single(&mut self, cname: &str, ctype: StreamType, src_sm: &StreamManager, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { + pub fn map_single(&mut self, cname: &str, ctype: StreamType, src_sm: &StreamManager, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { let mut found_stream = false; for (iidx, istr) in src_sm.iter().enumerate() { if istr.get_media_type() != ctype || found_stream { @@ -761,7 +689,7 @@ println!("can't generate default channel map for {} channels", dainfo.channels); } found_stream } - fn negotiate_stream_map(&mut self, src_sm: &StreamManager, mux_caps: MuxerCapabilities, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { + pub fn negotiate_stream_map(&mut self, src_sm: &StreamManager, mux_caps: MuxerCapabilities, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { match mux_caps { MuxerCapabilities::SingleVideo(cname) => { if self.no_video { return false; } @@ -842,7 +770,7 @@ println!("can't generate default channel map for {} channels", dainfo.channels); }, } } - fn create_demuxers(&mut self, demuxers: &mut Vec<(DemuxerObject, bool)>, full_reg: &FullRegister, print_info: bool) -> bool { + pub fn create_demuxers(&mut self, demuxers: &mut Vec<(DemuxerObject, bool)>, full_reg: &FullRegister, print_info: bool) -> bool { let mut isn_start = 0; for (i, (iname, ifmt)) in self.input_name.iter().zip( self.input_fmt.iter()).enumerate() { @@ -918,647 +846,3 @@ println!("can't generate default channel map for {} channels", dainfo.channels); true } } - -fn encode_frame(dst_id: u32, encoder: &mut Box, cvt: &mut OutputConvert, frm: NAFrameRef, scale_opts: &[(String, String)]) -> bool { - let buf = frm.get_buffer(); - let cbuf = if let NABufferType::None = buf { - if (encoder.get_capabilities() & ENC_CAPS_SKIPFRAME) == 0 { - match cvt { - OutputConvert::Video(_, ref mut dbuf) => dbuf.clone(), - _ => { - println!("encoder does not support skip frames, skipping"); - return true; - }, - } - } else { - buf - } - } else { - match cvt { - OutputConvert::None => buf, - OutputConvert::Video(ref mut scaler, ref mut dbuf) => { - let cur_ifmt = get_scale_fmt_from_pic(&buf); - let last_ifmt = scaler.get_in_fmt(); - if cur_ifmt != last_ifmt { - let ofmt = scaler.get_out_fmt(); - let ret = NAScale::new_with_options(cur_ifmt, ofmt, scale_opts); - if ret.is_err() { - println!("error re-initialising scaler for {} -> {}", cur_ifmt, ofmt); - return false; - } - *scaler = ret.unwrap(); - } - let ret = scaler.convert(&buf, dbuf); - if ret.is_err() { - println!("error converting frame for encoding"); - return false; - } - dbuf.clone() - }, - OutputConvert::Audio(ref mut acvt) => { - if !acvt.queue_frame(buf, frm.get_time_information()) { - println!("error converting audio for stream {}", dst_id); - return false; - } - return true; - }, - } - }; - let cfrm = NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cbuf); - encoder.encode(&cfrm).unwrap(); - true -} - -macro_rules! next_arg { - ($args: expr, $arg_idx: expr) => { - if $arg_idx + 1 >= $args.len() { - println!("codec name is required"); - } - $arg_idx += 1; - } -} - -macro_rules! parse_id { - ($val: expr, $stype: expr, $maxval: expr) => { - if $val.is_empty() { - 0 - } else if let Ok(val) = $val.parse::() { - if val < $maxval { - val - } else { - println!("{} number should be below {}", $stype, $maxval); - return; - } - } else { - println!("invalid {} number '{}'", $stype, $val); - return; - } - } -} - -#[allow(clippy::single_match)] -fn main() { - let args: Vec<_> = env::args().collect(); - - if args.len() == 1 { - println!("usage: nihav-encoder [options] --input inputfile --output outputfile"); - println!(" use nihav-encoder --help to list all available options"); - return; - } - if args.len() == 2 && (args[1] == "--help" || args[1] == "-h") { - println!("usage: nihav-encoder [options] --input inputfile --output outputfile"); - println!(" query options:"); - println!(" --list-{{decoders,encoders,demuxers,muxers}} - lists all available decoders/encoders/demuxers/muxers"); - println!(" --query-{{decoder,encoder,demuxer,muxer}}-options name - lists all options recognized by that decoder/encoder/demuxer/muxer"); - println!(" processing options:"); - println!(" --verbose - show time for the currently processed input"); - println!(" --input inputfile - set input file"); - println!(" --input-format fmt - force input format"); - println!(" --demuxer-options options - set input demuxer options"); - println!(" --scale-options options - set scaler options"); - println!(" --output outputfile - set output file"); - println!(" --output-format fmt - force output format"); - println!(" --muxer-options options - set output muxer options"); - println!(" --no-audio - do not decode audio streams"); - println!(" --no-video - do not decode video streams"); - println!(" --start starttime - start decoding from given position"); - println!(" --end endtime - end decoding at given position"); - println!(" --istreamX options - set options for input stream X"); - println!(" --ostreamX options - set options for output stream X"); - println!(); - println!(" (de)muxer and stream options are passed as comma-separated list e.g. --ostream0 width=320,height=240,flip"); - return; - } - - let full_reg = FullRegister::new(); - - let mut transcoder = Transcoder::new(); - - let mut arg_idx = 1; - let mut printed_info = false; - while arg_idx < args.len() { - match args[arg_idx].as_str() { - "--list-decoders" => { - if full_reg.dec_reg.iter().len() > 0 { - println!("Registered decoders:"); - for dec in full_reg.dec_reg.iter() { - let cdesc = register::get_codec_description(dec.name); - let full_name = if let Some(cd) = cdesc { cd.get_full_name() } else { "???" }; - println!(" {} ({})", dec.name, full_name); - } - } else { - println!("No registered decoders."); - } - printed_info = true; - }, - "--list-encoders" => { - if full_reg.enc_reg.iter().len() > 0 { - println!("Registered encoders:"); - for enc in full_reg.enc_reg.iter() { - let cdesc = register::get_codec_description(enc.name); - let full_name = if let Some(cd) = cdesc { cd.get_full_name() } else { "???" }; - println!(" {} ({})", enc.name, full_name); - } - } else { - println!("No registered encoders."); - } - printed_info = true; - }, - "--list-demuxers" => { - print!("Registered demuxers:"); - for dmx in full_reg.dmx_reg.iter() { - print!(" {}", dmx.get_name()); - } - println!(); - printed_info = true; - }, - "--list-muxers" => { - print!("Registered muxers:"); - for mux in full_reg.mux_reg.iter() { - print!(" {}", mux.get_name()); - } - println!(); - printed_info = true; - }, - "--query-decoder-options" => { - next_arg!(args, arg_idx); - let cname = args[arg_idx].as_str(); - if let Some(decfunc) = full_reg.dec_reg.find_decoder(cname) { - let dec = (decfunc)(); - let opts = dec.get_supported_options(); - print_options(cname, opts); - } else { - println!("codec {} is not found", cname); - } - printed_info = true; - }, - "--query-demuxer-options" => { - next_arg!(args, arg_idx); - let dname = args[arg_idx].as_str(); - let mut mr = MemoryReader::new_read(&[]); - let mut br = ByteReader::new(&mut mr); - if let Some(dmx_creator) = full_reg.dmx_reg.find_demuxer(dname) { - let dmx = dmx_creator.new_demuxer(&mut br); - let opts = dmx.get_supported_options(); - print_options(dname, opts); - } else { - println!("demuxer {} is not found", dname); - } - printed_info = true; - }, - "--query-encoder-options" => { - next_arg!(args, arg_idx); - let cname = args[arg_idx].as_str(); - if let Some(encfunc) = full_reg.enc_reg.find_encoder(cname) { - let enc = (encfunc)(); - let opts = enc.get_supported_options(); - print_options(cname, opts); - } else { - println!("codec {} is not found", cname); - } - printed_info = true; - }, - "--query-muxer-options" => { - next_arg!(args, arg_idx); - let name = args[arg_idx].as_str(); - let mut data = []; - let mut mw = MemoryWriter::new_write(&mut data); - let mut bw = ByteWriter::new(&mut mw); - if let Some(mux_creator) = full_reg.mux_reg.find_muxer(name) { - let mux = mux_creator.new_muxer(&mut bw); - let opts = mux.get_supported_options(); - print_options(name, opts); - } else { - println!("muxer {} is not found", name); - } - printed_info = true; - }, - "--output" | "-o" => { - next_arg!(args, arg_idx); - transcoder.output_name = args[arg_idx].clone(); - }, - "--output-format" => { - next_arg!(args, arg_idx); - transcoder.output_fmt = Some(args[arg_idx].clone()); - }, - "--scale-options" => { - next_arg!(args, arg_idx); - if !transcoder.parse_scale_options(&args[arg_idx]) { - println!("invalid scale option syntax"); - return; - } - }, - "--no-video" | "-vn" => { - transcoder.no_video = true; - }, - "--no-audio" | "-an" => { - transcoder.no_audio = true; - }, - "--start" => { - next_arg!(args, arg_idx); - let ret = args[arg_idx].parse::(); - if let Ok(val) = ret { - transcoder.start = val; - } else { - println!("invalid start time"); - return; - } - }, - "--end" => { - next_arg!(args, arg_idx); - let ret = args[arg_idx].parse::(); - if let Ok(val) = ret { - transcoder.end = val; - } else { - println!("invalid end time"); - return; - } - }, - "--muxer-options" => { - next_arg!(args, arg_idx); - if !transcoder.parse_muxer_options(&args[arg_idx], &full_reg.mux_reg) { - println!("invalid muxer option syntax"); - return; - } - }, - "--calc-len" => { - transcoder.calc_len = true; - }, - "--verbose" | "-v" => transcoder.verbose = 1, - "-vv" => transcoder.verbose = 2, - "-v-" => transcoder.verbose = 0, - _ => { - if args[arg_idx].starts_with("--istream") { - let opt0 = &args[arg_idx]; - next_arg!(args, arg_idx); - if !transcoder.parse_istream_options(opt0, &args[arg_idx]) { - println!("invalid input stream option syntax"); - return; - } - } else if args[arg_idx].starts_with("--ostream") { - let opt0 = &args[arg_idx]; - next_arg!(args, arg_idx); - if !transcoder.parse_ostream_options(opt0, &args[arg_idx], &full_reg.enc_reg) { - println!("invalid output stream option syntax"); - return; - } - } else if args[arg_idx].starts_with("--iformat") { - let id = parse_id!(&args[arg_idx][9..], "input format", transcoder.input_fmt.len()); - next_arg!(args, arg_idx); - transcoder.input_fmt[id] = Some(args[arg_idx].clone()); - } else if args[arg_idx].starts_with("--input-format") { - let id = parse_id!(&args[arg_idx][14..], "input format", transcoder.input_fmt.len()); - next_arg!(args, arg_idx); - transcoder.input_fmt[id] = Some(args[arg_idx].clone()); - } else if args[arg_idx].starts_with("--input") { // should be after --input-format - let id = parse_id!(&args[arg_idx][7..], "input", transcoder.input_name.len()); - next_arg!(args, arg_idx); - transcoder.input_name[id] = Some(args[arg_idx].clone()); - } else if args[arg_idx].starts_with("-i") { - let id = parse_id!(&args[arg_idx][2..], "input", transcoder.input_name.len()); - next_arg!(args, arg_idx); - transcoder.input_name[id] = Some(args[arg_idx].clone()); - } else if args[arg_idx].starts_with("--demuxer-options") { - let id = parse_id!(&args[arg_idx][17..], "input options", transcoder.demux_opts.len()); - next_arg!(args, arg_idx); - if !transcoder.parse_demuxer_options(&args[arg_idx], &full_reg.dmx_reg, id) { - println!("invalid demuxer option syntax"); - return; - } - } else if args[arg_idx].starts_with("--") { - println!("unknown option '{}'", args[arg_idx]); - } else { - println!("unrecognized argument '{}'", args[arg_idx]); - } - }, - }; - arg_idx += 1; - } - - if printed_info { - return; - } - - if transcoder.input_name.iter().flatten().count() == 0 { - println!("no input name(s) provided"); - return; - } - if transcoder.output_name.is_empty() { - println!("no output name provided"); - return; - } - - let mut demuxers = Vec::with_capacity(1); - if !transcoder.create_demuxers(&mut demuxers, &full_reg, true) { - return; - } - - let duration = demuxers.iter().fold(0u64, |mindur, (dmx, _)| { - let dur = dmx.get_duration(); - if dur > 0 { - mindur.min(dur) - } else { - mindur - } - }); - let duration_string = if duration != 0 { format_time(duration) } else { String::new() }; - - let mut ism = StreamManager::new(); - let mut is_offset = Vec::with_capacity(demuxers.len()); - let mut start = 0; - let mut nstreams = 0; - for (dmx, _) in demuxers.iter() { - is_offset.push(nstreams); - let sm = dmx.get_stream_manager(); - 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(); - } - newstream.id += start; - ism.add_stream(newstream); - } - start += max_id + 1; - nstreams += sm.get_num_streams(); - } - - for (&is_off, (dmx, _)) in is_offset.iter().zip(demuxers.iter_mut()) { - for i in 0..dmx.get_num_streams() { - let s = dmx.get_stream(i).unwrap(); - let info = s.get_info(); - let decfunc = full_reg.dec_reg.find_decoder(info.get_name()); - let str_id = (s.get_num() + is_off) as u32; - if let Some(create_dec) = decfunc { - let mut dec = (create_dec)(); - let mut dsupp = Box::new(NADecoderSupport::new()); - let ret = dec.init(&mut dsupp, info.clone()); - if ret.is_err() { - println!("Error initialising decoder '{}' for stream {}", info.get_name(), str_id); - return; - } - transcoder.apply_decoder_options(dec.as_mut(), str_id); - let desc = register::get_codec_description(info.get_name()); - let has_b = if let Some(desc) = desc { - desc.has_reorder() - } else { - println!("No codec description found, using B-frame reorderer."); - true - }; - let reord: Box = if has_b { Box::new(IPBReorderer::new()) } else { Box::new(NoReorderer::new()) }; - transcoder.decoders.push(Some((dsupp, dec, reord))); - } else { - println!("No decoder for stream {} ({}) is found", str_id, info.get_name()); - transcoder.decoders.push(None); - } - } - if transcoder.start != NATimePoint::None { - let ret = dmx.seek(transcoder.start); - if ret.is_err() { - println!(" failed to seek to {} error {:?}", transcoder.start, ret.err().unwrap()); - } - } - } - - let output_fmt = if let Some(ref fmtname) = transcoder.output_fmt { - fmtname - } else if transcoder.output_name.as_str() == "/dev/null" { - "null" - } else if let Some(fmtname) = detect::detect_format_by_name(transcoder.output_name.as_str()) { - fmtname - } else { - println!("Cannot guess muxer for output"); - return; - }; - let ret = full_reg.mux_reg.find_muxer(output_fmt); - let ofmt = output_fmt.to_string(); - - if ret.is_none() { - println!("cannot find muxer '{}'", output_fmt); - return; - } - let mux_creator = ret.unwrap(); - - let mux_quirks = mux_creator.get_quirks(); - if mux_quirks.is_fixed_duration() { - transcoder.calc_len = true; - } - - if transcoder.calc_len { - let mut sids = Vec::new(); - transcoder.nframes.clear(); - for (dmx, _) in demuxers.iter_mut() { - let sstart = transcoder.nframes.len(); - let sm = dmx.get_stream_manager(); - sids.clear(); - for stream in sm.iter() { - transcoder.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; - } - } - // this is necessary since not all demuxers allow to seek even back to the start - demuxers.clear(); - if !transcoder.create_demuxers(&mut demuxers, &full_reg, false) { - println!("failed to re-create demuxer(s)"); - return; - } - } - - let mux_caps = mux_creator.get_capabilities(); - let mut out_sm = StreamManager::new(); - if !transcoder.negotiate_stream_map(&ism, mux_caps, &mut out_sm, &full_reg.enc_reg) { - println!("cannot determine stream map"); - return; - } - - let ret = File::create(transcoder.output_name.as_str()); - if ret.is_err() { - println!("cannot open output file"); - return; - } - let mut fw = FileWriter::new_write(ret.unwrap()); - let mut bw = ByteWriter::new(&mut fw); - let ret = create_muxer(mux_creator, out_sm, &mut bw); - if let Err(err) = ret { - println!("cannot create muxer instance {:?}", err); - return; - } - let mut mux = ret.unwrap(); - parse_and_apply_options!(mux, &transcoder.mux_opts, "output"); - - println!("Output {} muxer {}", transcoder.output_name, ofmt); - for ostr in mux.get_streams() { - println!(" #{}: {} {}", ostr.get_num(), ostr, ostr.get_info().get_name()); - } - - let mut time = Instant::now(); - let show_interval = Duration::from_millis(100); - let mut adata_size = 0; - let mut vdata_size = 0; - let mut cur_dmx = 0; - 'main_loop: loop { - let mut pktres = Err(DemuxerError::EOF); - let mut src_dmx = 0; - loop { - if !demuxers.iter().any(|(_, eof)| !eof) { - break; - } - let mut got_res = false; - if !demuxers[cur_dmx].1 { - pktres = demuxers[cur_dmx].0.get_frame(); - got_res = true; - src_dmx = cur_dmx; - } - cur_dmx += 1; - if cur_dmx >= demuxers.len() { - cur_dmx = 0; - } - if got_res { - break; - } - } - - if let Err(DemuxerError::EOF) = pktres { break; } - if pktres.is_err() { - println!("demuxing error"); - break; - } - let mut pkt = pktres.unwrap(); - if transcoder.start != NATimePoint::None && pkt.ts.less_than(transcoder.start) { continue; } - let src_id = pkt.get_stream().get_num() + is_offset[src_dmx]; - let ts = pkt.ts; - let newstream = ism.get_stream(src_id).unwrap(); - pkt.reassign(newstream, ts); - - if transcoder.verbose > 0 && time.elapsed() >= show_interval { - if let Some(pts) = pkt.get_pts() { - let cur_time = format_time(NATimeInfo::ts_to_time(pts, 1000, pkt.ts.tb_num, pkt.ts.tb_den)); - print!(" {}", cur_time); - } else { - print!(" ???"); - } - if !duration_string.is_empty() { - print!(" / {}", duration_string); - } - if transcoder.verbose > 1 { - print!(" data sizes V: {} A: {}", vdata_size, adata_size); - } - print!("\r"); - std::io::stdout().flush().unwrap(); - time = Instant::now(); - } - match transcoder.encoders[src_id] { - OutputMode::Drop => {}, - OutputMode::Copy(dst_id) => { - let dstr = mux.get_stream(dst_id as usize).unwrap(); - pkt.reassign(dstr, pkt.get_time_information()); - if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'main_loop; } - let pkt_size = pkt.get_buffer().len(); - match pkt.get_stream().get_media_type() { - StreamType::Video => { vdata_size += pkt_size; }, - StreamType::Audio => { adata_size += pkt_size; }, - _ => {}, - }; - if mux.mux_frame(pkt).is_err() { - println!("error muxing packet"); - break; - } - }, - OutputMode::Encode(dst_id, ref mut encoder, ref mut cvt) => { - if let Some((ref mut dsupp, ref mut decoder, ref mut reorderer)) = transcoder.decoders[src_id] { - let ret = decoder.decode(dsupp, &pkt); - if let (true, Err(DecoderError::MissingReference)) = (transcoder.start != NATimePoint::None, &ret) { - continue; - } - if ret.is_err() { - println!("error decoding stream {}", src_id); - break; - } - let frm = ret.unwrap(); - let tinfo = frm.get_info(); - reorderer.add_frame(frm); - while let Some(frm) = reorderer.get_frame() { - if !encode_frame(dst_id, encoder, cvt, frm, &transcoder.scale_opts) { - break; - } - while let Ok(Some(pkt)) = encoder.get_packet() { - if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'main_loop; } - let pkt_size = pkt.get_buffer().len(); - match pkt.get_stream().get_media_type() { - StreamType::Video => { vdata_size += pkt_size; }, - StreamType::Audio => { adata_size += pkt_size; }, - _ => {}, - }; - mux.mux_frame(pkt).unwrap(); - } - } - if let OutputConvert::Audio(ref mut acvt) = cvt { - while let Some(ofrm) = acvt.get_frame(tinfo.clone()) { - if encoder.encode(&ofrm).is_err() { - break; - } - while let Ok(Some(pkt)) = encoder.get_packet() { - if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'main_loop; } - let pkt_size = pkt.get_buffer().len(); - adata_size += pkt_size; - mux.mux_frame(pkt).unwrap(); - } - } - } - } else { - println!("no decoder for stream {}", src_id); - break; - } - }, - }; - } - 'reord_flush_loop: for stream in ism.iter() { - let src_id = stream.get_num(); - if let OutputMode::Encode(dst_id, ref mut encoder, ref mut cvt) = transcoder.encoders[src_id] { - if let Some((_, _, ref mut reorderer)) = transcoder.decoders[src_id] { - while let Some(frm) = reorderer.get_last_frames() { - if !encode_frame(dst_id, encoder, cvt, frm, &transcoder.scale_opts) { - break; - } - while let Ok(Some(pkt)) = encoder.get_packet() { - if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'reord_flush_loop; } - mux.mux_frame(pkt).unwrap(); - } - } - } - } - } - 'flush_loop: for enc in transcoder.encoders.iter_mut() { - match enc { - OutputMode::Encode(str_id, ref mut encoder, _) => { - let ret = encoder.flush(); - if ret.is_err() { - println!("error flushing encoder for stream {}", str_id); - break; - } else { - while let Ok(Some(pkt)) = encoder.get_packet() { - if mux.mux_frame(pkt).is_err() { - println!("error muxing packet"); - break 'flush_loop; - } - } - } - }, - _ => {}, - }; - } - if transcoder.verbose > 0 { - println!(); - } - - let ret = mux.end(); - if ret.is_err() { - println!("error at finalising muxing"); - } -} -- 2.39.5