use nihav_core::muxers::*;
use nihav_core::reorder::*;
use nihav_core::scale::*;
-use nihav_core::soundcvt::*;
use nihav_registry::detect;
use nihav_registry::register;
use std::env;
mod demux;
use crate::demux::*;
mod null;
+mod acvt;
+use crate::acvt::*;
+mod imgseq;
+use crate::imgseq::*;
+
+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;
}
}
+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>,
enum OutputConvert {
Video(NAScale, NABufferType),
- Audio(NAAudioInfo, NAChannelMap),
+ Audio(AudioConverter),
None,
}
no_audio: bool,
start: NATimePoint,
end: NATimePoint,
- verbose: bool,
+ verbose: u8,
}
macro_rules! parse_and_apply_options {
matches = name == opt_def.name;
}
if matches {
- let arg = if let Some(ref str) = opt.value { Some(str) } else { None };
+ 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);
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) {
+ 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() });
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) {
+ 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() });
},
"srate" => {
if ostr.enc_params.format == NACodecTypeInfo::None {
- ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0));
+ 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::<u32>();
},
"channels" => {
if ostr.enc_params.format == NACodecTypeInfo::None {
- ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0));
+ 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::<u8>();
},
"block_len" => {
if ostr.enc_params.format == NACodecTypeInfo::None {
- ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0));
+ 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::<usize>();
},
"sfmt" => {
if ostr.enc_params.format == NACodecTypeInfo::None {
- ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0));
+ 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::<NASoniton>();
// 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));
+ 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::<NAChannelMap>();
}
},*/
"bitrate" => {
- let ret = oval[1].parse::<u32>();
+ let ret = parse_bitrate(oval[1]);
if let Ok(val) = ret {
ostr.enc_params.bitrate = val;
} else {
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) {
+ 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);
}
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 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 true;
}
}
- if let Some(str_idx) = self.ostr_opts.iter().position(|str| str.id == out_id) {
+ 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_ref(istr);
}
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 oopts.enc_params.tb_num == 0 {
oopts.enc_params.tb_num = istr.tb_num;
let ret_eparams = ret_eparams.unwrap();
//todo check for params mismatch
- let cvt = match (&oopts.enc_params.format, &ret_eparams.format) {
+ let cvt = match (&iformat, &ret_eparams.format) {
(NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => {
if svinfo == dvinfo && !forced_out {
OutputConvert::None
return false;
},
};
+ let acvt = AudioConverter::new(sainfo, dainfo, dchmap);
//todo channelmap
- OutputConvert::Audio(*dainfo, dchmap)
+ OutputConvert::Audio(acvt)
}
},
_ => OutputConvert::None,
},
};
//todo channelmap
- OutputConvert::Audio(*dainfo, dchmap)
+ let acvt = AudioConverter::new(sainfo, dainfo, dchmap);
+ OutputConvert::Audio(acvt)
}
},
_ => OutputConvert::None,
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 = match cvt {
+ 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);
}
dbuf.clone()
},
- OutputConvert::Audio(ref dinfo, ref dchmap) => {
- let ret = convert_audio_frame(&buf, dinfo, dchmap);
- if ret.is_err() {
+ OutputConvert::Audio(ref mut acvt) => {
+ if !acvt.queue_frame(buf, frm.get_time_information()) {
println!("error converting audio for stream {}", dst_id);
return false;
}
- ret.unwrap()
+ return true;
},
- };
+ }
+ };
let cfrm = NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cbuf);
encoder.encode(&cfrm).unwrap();
true
return;
}
},
- "--verbose" | "-v" => transcoder.verbose = 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];
return;
}
+ if let Some("imgseq") = transcoder.input_fmt.as_deref() {
+ let mut isdc = ImgSeqDemuxerCreator::new(transcoder.input_name.as_str());
+ parse_and_apply_options!(isdc, &transcoder.demux_opts, "input");
+ let isd = if let Ok(ctx) = isdc.open() {
+ ctx
+ } else {
+ println!("failed to create image sequence demuxer!");
+ return;
+ };
+ let mut dmx = DemuxerObject::create_imgseq(isd);
+ transcode(transcoder, full_reg, &mut dmx);
+ return;
+ }
+
let res = File::open(transcoder.input_name.as_str());
if res.is_err() {
println!("error opening input");
println!("cannot find demuxer for '{}'", transcoder.input_name.as_str());
return;
}
+ parse_and_apply_options!(dmx, &transcoder.demux_opts, "input");
+ transcode(transcoder, full_reg, &mut dmx);
+}
+
+fn transcode(mut transcoder: Transcoder, full_reg: FullRegister, dmx: &mut DemuxerObject) {
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 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
+ 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 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; }
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 && time.elapsed() >= show_interval {
+ 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);
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();
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;
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) {
}
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();
+ 'reord_flush_loop: for stream in dmx.get_streams() {
+ 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 transcoder.verbose {
+ if transcoder.verbose > 0 {
println!();
}