-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 nihav_core::io::byteio::{FileReader, ByteReader};
-use nihav_core::frame::*;
-use nihav_core::options::*;
-use nihav_core::codecs::*;
-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::*;
-
-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<u32, ()> {
- 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<String>,
-}
-
-struct InputStreamOptions {
- id: u32,
- drop: bool,
- dec_opts: Vec<OptionArgs>,
-}
-
-struct OutputStreamOptions {
- id: u32,
- enc_params: EncodeParameters,
- enc_name: String,
- enc_opts: Vec<OptionArgs>,
-}
-
-enum OutputConvert {
- Video(NAScale, NABufferType),
- Audio(AudioConverter),
- None,
-}
-
-#[allow(clippy::large_enum_variant)]
-enum OutputMode {
- Drop,
- Copy(u32),
- Encode(u32, Box<dyn NAEncoder>, OutputConvert),
-}
-
-#[derive(Default)]
-#[allow(clippy::type_complexity)]
-struct Transcoder {
- input_name: String,
- input_fmt: Option<String>,
- output_name: String,
- output_fmt: Option<String>,
- demux_opts: Vec<OptionArgs>,
- mux_opts: Vec<OptionArgs>,
- istr_opts: Vec<InputStreamOptions>,
- ostr_opts: Vec<OutputStreamOptions>,
- scale_opts: Vec<(String, String)>,
- decoders: Vec<Option<(Box<NADecoderSupport>, Box<dyn NADecoder>, Box<dyn FrameReorderer>)>>,
- encoders: Vec<OutputMode>,
- no_video: bool,
- no_audio: bool,
- start: NATimePoint,
- end: NATimePoint,
- verbose: u8,
-}
-
-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 str) = opt.value { Some(str) } 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());
- }
-}
-
-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::<u32>();
- if ret.is_err() { return false; }
- let streamno = ret.unwrap();
-
- let sidx = if let Some(idx) = self.istr_opts.iter().position(|str| str.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::<u32>();
- if ret.is_err() { return false; }
- let streamno = ret.unwrap();
-
- let sidx = if let Some(idx) = self.ostr_opts.iter().position(|str| str.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::<u32>();
- let rden = den.parse::<u32>();
- 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::<usize>();
- 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::<usize>();
- 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::<NAPixelFormaton>();
- 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_S16_FORMAT, 0));
- }
- if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format {
- let ret = oval[1].parse::<u32>();
- 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_S16_FORMAT, 0));
- }
- if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format {
- let ret = oval[1].parse::<u8>();
- 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_S16_FORMAT, 0));
- }
- if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format {
- let ret = oval[1].parse::<usize>();
- 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_S16_FORMAT, 0));
- }
- if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format {
- let ret = oval[1].parse::<NASoniton>();
- 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_S16_FORMAT, 0));
- }
- if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format {
- let ret = oval[1].parse::<NAChannelMap>();
- 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::<u8>();
- 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) -> bool {
- for opt in opts.split(',') {
- let oval: Vec<_> = opt.split('=').collect();
- if oval.len() == 1 {
- self.demux_opts.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 = Some(oval[1].to_string());
- } else {
- println!("unknown demuxer format '{}'", oval[1]);
- }
- } else {
- self.demux_opts.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(|str| str.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, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool {
- let out_id = out_sm.get_num_streams() as u32;
- if let Some(str_idx) = self.istr_opts.iter().position(|str| str.id == (istr.get_num() as u32)) {
- if self.istr_opts[str_idx].drop {
- self.encoders.push(OutputMode::Drop);
- return true;
- }
- }
-
- if let Some(str_idx) = self.ostr_opts.iter().position(|str| str.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_ref(istr);
- 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 false;
- }
- let mut encoder = (enc_create.unwrap())();
- let forced_out = oopts.enc_params.format != NACodecTypeInfo::None;
- if oopts.enc_params.format == NACodecTypeInfo::None {
- oopts.enc_params.format = istr.get_info().get_properties();
- }
- 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 false;
- }
- 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 && !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 false;
- }
- let scaler = ret.unwrap();
- let ret = alloc_video_buffer(*dvinfo, 4);
- if ret.is_err() {
- println!("cannot create scaler buffer");
- return false;
- }
- 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 false;
- },
- };
- let acvt = AudioConverter::new(sainfo, dainfo, dchmap);
-//todo channelmap
- OutputConvert::Audio(acvt)
- }
- },
- _ => OutputConvert::None,
- };
- let ret = encoder.init(out_id, ret_eparams);
- if ret.is_err() {
- println!("error initialising encoder");
- return false;
- }
- out_sm.add_stream_ref(ret.unwrap());
-
- let name = format!("output stream {}", out_id);
- 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 false;
- }
- } else if cname == "any" || istr.get_info().get_name() == cname {
- out_sm.add_stream_ref(istr);
- self.encoders.push(OutputMode::Copy(out_id));
- } 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 false;
- }
- let mut encoder = (enc_create.unwrap())();
- oopts.enc_params.format = istr.get_info().get_properties();
- 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 false;
- }
- 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 false;
- }
- let scaler = ret.unwrap();
- let ret = alloc_video_buffer(*dvinfo, 4);
- if ret.is_err() {
- println!("cannot create scaler buffer");
- return false;
- }
- 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 false;
- },
- };
-//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 false;
- }
- out_sm.add_stream_ref(ret.unwrap());
- self.encoders.push(OutputMode::Encode(out_id, encoder, cvt));
- self.ostr_opts.push(oopts);
- }
- true
- }
- 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 istr in src_sm.iter() {
- if istr.get_media_type() != ctype || found_stream {
- self.encoders.push(OutputMode::Drop);
- } else {
- if !self.register_output_stream(cname, istr, out_sm, enc_reg) {
- return false;
- }
- found_stream = true;
- }
- }
- 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 istr in src_sm.iter() {
- if istr.get_media_type() == StreamType::Video && !found_vid && !self.no_video {
- if !self.register_output_stream(vname, istr, out_sm, enc_reg) {
- return false;
- }
- found_vid = true;
- } else if istr.get_media_type() == StreamType::Audio && !found_aud && !self.no_audio {
- if !self.register_output_stream(aname, istr, out_sm, enc_reg) {
- return false;
- }
- found_aud = true;
- } else {
- self.encoders.push(OutputMode::Drop);
- }
- }
- found_vid | found_aud
- },
- MuxerCapabilities::OnlyVideo => {
- if self.no_video { return false; }
-
- let mut found_vid = false;
- for istr in src_sm.iter() {
- if istr.get_media_type() == StreamType::Video && !found_vid {
- if !self.register_output_stream("any", istr, out_sm, enc_reg) {
- return false;
- }
- found_vid = true;
- } else {
- self.encoders.push(OutputMode::Drop);
- }
- }
- found_vid
- },
- MuxerCapabilities::OnlyAudio => {
- if self.no_audio { return false; }
-
- let mut found_aud = false;
- for istr in src_sm.iter() {
- if istr.get_media_type() == StreamType::Audio && !found_aud {
- if !self.register_output_stream("any", istr, out_sm, enc_reg) {
- return false;
- }
- found_aud = true;
- } else {
- self.encoders.push(OutputMode::Drop);
- }
- }
- found_aud
- },
- MuxerCapabilities::Universal => {
- for istr in src_sm.iter() {
- 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, out_sm, enc_reg) {
- return false;
- }
- }
- true
- },
- }
- }
-}
-
-fn encode_frame(dst_id: u32, encoder: &mut Box<dyn NAEncoder>, 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;
- }
-}
-
-#[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;
- },
- "--input" | "-i" => {
- next_arg!(args, arg_idx);
- transcoder.input_name = args[arg_idx].clone();
- },
- "--input-format" => {
- next_arg!(args, arg_idx);
- transcoder.input_fmt = Some(args[arg_idx].clone());
- },
- "--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());
- },
- "--demuxer-options" => {
- next_arg!(args, arg_idx);
- if !transcoder.parse_demuxer_options(&args[arg_idx], &full_reg.dmx_reg) {
- println!("invalid demuxer option syntax");
- return;
- }
- },
- "--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::<NATimePoint>();
- 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::<NATimePoint>();
- 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;
- }
- },
- "--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("--") {
- println!("unknown option");
- } else {
- println!("unrecognized argument");
- }
- },
- };
- arg_idx += 1;
- }
-
- if printed_info {
- return;
- }
-
- if transcoder.input_name.is_empty() {
- println!("no input name provided");
- return;
- }
- if transcoder.output_name.is_empty() {
- println!("no output name provided");
- return;
- }
-
- let res = File::open(transcoder.input_name.as_str());
- if res.is_err() {
- println!("error opening input");
- return;
- }
- 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 transcoder.input_fmt.is_none() {
- detect_tags(&mut br)
- } else {
- (false, 0, None)
- };
-
- let mut nfr: Box<dyn ByteIO>;
- if start != 0 || end.is_some() {
- let file = fr.finish();
- nfr = Box::new(BoundedFileReader::new_read(file, start, end).unwrap());
- } else {
- nfr = Box::new(fr);
- }
- let mut br = ByteReader::new(nfr.as_mut());
-
- let mut dmx = DemuxerObject::create(&mut br, &full_reg, transcoder.input_name.as_str(), &transcoder.input_fmt, is_raw);
- if dmx.is_none() {
- println!("cannot find demuxer for '{}'", transcoder.input_name.as_str());
- return;
- }
- let duration = dmx.get_duration();
- let duration_string = if duration != 0 { format_time(duration) } else { String::new() };
-
- parse_and_apply_options!(dmx, &transcoder.demux_opts, "input");
- 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());
-println!("stream {} - {} {}", i, s, info.get_name());
- let str_id = s.get_num() 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<dyn FrameReorderer> = 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 str) = transcoder.output_fmt {
- str
- } else if let Some(str) = detect::detect_format_by_name(transcoder.output_name.as_str()) {
- str
- } 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);
- }
- let mux_creator = ret.unwrap();
-
- let mux_caps = mux_creator.get_capabilities();
- let mut out_sm = StreamManager::new();
- if !transcoder.negotiate_stream_map(dmx.get_stream_manager(), 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;
- 'main_loop: loop {
- let pktres = dmx.get_frame();
- 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();
- 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 str in dmx.get_streams() {
- let src_id = str.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");
- }
-}
+extern crate nihav_core;
+extern crate nihav_codec_support;
+extern crate nihav_registry;
+extern crate nihav_allstuff;
+
+use std::fs::File;
+use std::io::Write;
+use nihav_core::frame::*;
+use nihav_core::options::*;
+use nihav_core::codecs::*;
+use nihav_core::demuxers::*;
+use nihav_core::muxers::*;
+use nihav_core::reorder::*;
+use nihav_registry::detect;
+use nihav_registry::register;
+use std::env;
+use std::time::{Duration, Instant};
+
+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());
+ }
+}
+
+mod demux;
+use crate::demux::FullRegister;
+mod null;
+mod acvt;
+mod imgseq;
+mod transcoder;
+use crate::transcoder::*;
+
+
+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);
+ }
+ }
+}
+
+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::<usize>() {
+ if val < $maxval {
+ val
+ } else {
+ println!("{} number should be below {}", $stype, $maxval);
+ return;
+ }
+ } else {
+ println!("invalid {} number '{}'", $stype, $val);
+ return;
+ }
+ }
+}
+
+fn retrieve_packets(transcoder: &mut Transcoder, mux: &mut Muxer, vdata_size: &mut usize, adata_size: &mut usize, end: bool) -> bool {
+ while let Some(pkt) = transcoder.queue.get_packet() {
+ if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) {
+ return false;
+ }
+ 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");
+ return false;
+ }
+ }
+ if end {
+ while let Some(pkt) = transcoder.queue.get_last_packet() {
+ if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) {
+ return false;
+ }
+ 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");
+ return false;
+ }
+ }
+ }
+ true
+}
+
+#[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;
+ let mut force_sync = false;
+ let mut profile_name = "".to_string();
+ let mut custom_profile = 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::<NATimePoint>();
+ 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::<NATimePoint>();
+ if let Ok(val) = ret {
+ transcoder.end = val;
+ } else {
+ println!("invalid end time");
+ return;
+ }
+ },
+ "--sync" => {
+ force_sync = true;
+ },
+ "--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;
+ },
+ "--print-profiles" => {
+ println!("Supported profiles:");
+ for (name, profiles) in PROFILES.iter() {
+ print!(" profiles for format '{name}': ");
+ for profile in profiles.iter() {
+ print!(" {}", profile.name);
+ }
+ println!();
+ }
+ printed_info = true;
+ },
+ "--profile" => {
+ next_arg!(args, arg_idx);
+ profile_name = args[arg_idx].to_string();
+ },
+ "--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;
+ }
+ custom_profile = true;
+ } 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 mindur == 0 {
+ dur
+ } else 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 decoder = (create_dec)();
+ let mut dsupp = Box::new(NADecoderSupport::new());
+ let ret = decoder.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(decoder.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 reorderer: Box<dyn FrameReorderer> = if has_b { Box::new(IPBReorderer::new()) } else { Box::new(NoReorderer::new()) };
+ transcoder.decoders.push(Some(DecodeContext{ dsupp, decoder, reorderer }));
+ } 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();
+
+ if custom_profile && !profile_name.is_empty() {
+ println!("profile setting is incompatible with custom --ostream options");
+ return;
+ }
+ if !profile_name.is_empty() {
+ if let Some(profiles) = PROFILES.iter().find(|(fmt, _)| fmt == &output_fmt) {
+ if let Some(ref_profile) = profiles.1.iter().find(|p| p.name == profile_name) {
+ transcoder.profile = Some(ref_profile.profile);
+ } else {
+ println!("profile '{profile_name}' is not defined for output format '{output_fmt}'");
+ }
+ } else {
+ println!("no profiles for output format '{output_fmt}'");
+ }
+ }
+
+ let mux_quirks = mux_creator.get_quirks();
+ if mux_quirks.is_fixed_duration() {
+ transcoder.calc_len = true;
+ }
+ transcoder.fixed_rate = mux_quirks.is_fixed_rate();
+ transcoder.queue.set_sync(force_sync || !mux_quirks.is_unsync());
+
+ 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() {
+ let mut is_copy = false;
+ let stream_id = ostr.get_num() as u32;
+ for omode in transcoder.encoders.iter() {
+ if let OutputMode::Copy(sno) = omode {
+ if sno == &stream_id {
+ is_copy = true;
+ break;
+ }
+ }
+ }
+ println!(" #{}: {} {}{}", ostr.get_num(), ostr, ostr.get_info().get_name(), if is_copy { " (copy)" } else { "" });
+ }
+
+ transcoder.queue.reserve_streams(mux.get_num_streams());
+
+ 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;
+ let mut last_known_time = None;
+ '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);
+ last_known_time = Some(cur_time);
+ } else if let Some(ref time) = last_known_time {
+ print!(" {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; },
+ _ => {},
+ };
+ transcoder.queue.queue_packet(pkt);
+ },
+ OutputMode::Encode(dst_id, ref mut encoder) => {
+ if let Some(ref mut dec_ctx) = transcoder.decoders[src_id] {
+ let ret = dec_ctx.decoder.decode(&mut dec_ctx.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();
+ dec_ctx.reorderer.add_frame(frm);
+ while let Some(frm) = dec_ctx.reorderer.get_frame() {
+ match encoder.encode_frame(dst_id, frm, &transcoder.scale_opts, &mut transcoder.queue) {
+ Ok(true) => {},
+ Ok(false) => break,
+ Err(err) => {
+ println!("encoder error {err:?}");
+ break 'main_loop;
+ }
+ }
+ }
+ } else {
+ println!("no decoder for stream {}", src_id);
+ break;
+ }
+ },
+ };
+
+ if !retrieve_packets(&mut transcoder, &mut mux, &mut vdata_size, &mut adata_size, false) {
+ break;
+ }
+ }
+ 'reord_flush_loop: for stream in ism.iter() {
+ let src_id = stream.get_num();
+ if let OutputMode::Encode(dst_id, ref mut encoder) = transcoder.encoders[src_id] {
+ if let Some(ref mut dec_ctx) = transcoder.decoders[src_id] {
+ while let Some(frm) = dec_ctx.reorderer.get_last_frames() {
+ match encoder.encode_frame(dst_id, frm, &transcoder.scale_opts, &mut transcoder.queue) {
+ Ok(true) => {},
+ Ok(false) => break,
+ Err(err) => {
+ println!("encoder error {err:?}");
+ break 'reord_flush_loop;
+ }
+ }
+ }
+ }
+ }
+ }
+ /*'flush_loop:*/ for enc in transcoder.encoders.iter_mut() {
+ match enc {
+ OutputMode::Encode(str_id, ref mut encoder) => {
+ let ret = encoder.flush(&mut transcoder.queue);
+ if ret.is_err() {
+ println!("error flushing encoder for stream {}", str_id);
+ break;
+ }
+ },
+ _ => {},
+ };
+ }
+
+ retrieve_packets(&mut transcoder, &mut mux, &mut vdata_size, &mut adata_size, true);
+
+ if transcoder.verbose > 0 {
+ println!();
+ }
+
+ let ret = mux.end();
+ if ret.is_err() {
+ println!("error at finalising muxing");
+ }
+}
+
+struct ProfileDef {
+ name: &'static str,
+ profile: EncodingProfile,
+}
+
+const PROFILES: &[(&str, &[ProfileDef])] = &[
+ ("avi",
+ &[
+ ProfileDef {
+ name: "cinepak",
+ profile: EncodingProfile {
+ vname: "cinepak",
+ voptions: &[],
+ aname: "pcm",
+ aoptions: &[],
+ }
+ },
+ ProfileDef {
+ name: "lossless",
+ profile: EncodingProfile {
+ vname: "zmbv",
+ voptions: &[("range", Some("4")), ("compr_level", Some("fast"))],
+ aname: "pcm",
+ aoptions: &[],
+ }
+ },
+ ProfileDef {
+ name: "ms-lossy",
+ profile: EncodingProfile {
+ vname: "msvideo1",
+ voptions: &[],
+ aname: "ms-adpcm",
+ aoptions: &[],
+ }
+ },
+ ProfileDef {
+ name: "raw",
+ profile: EncodingProfile {
+ vname: "rawvideo-ms",
+ voptions: &[],
+ aname: "pcm",
+ aoptions: &[],
+ }
+ },
+ ]),
+];