X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=src%2Fmain.rs;h=97be1e2ea36367b6b833a1b61f5134cc5a5619da;hb=60dc08af39f51c44bff95b106337c60191912197;hp=d0b8d4b117a9fe7eb26ee9632820a4fc81beb12e;hpb=00b4c46f1c906a978dfac94bc569a6d3f4a429f5;p=nihav-encoder.git diff --git a/src/main.rs b/src/main.rs index d0b8d4b..97be1e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ extern crate nihav_registry; extern crate nihav_allstuff; use std::fs::File; -use std::io::BufReader; +use std::io::{BufReader, Write}; use nihav_core::io::byteio::{FileReader, ByteReader}; use nihav_core::frame::*; use nihav_core::options::*; @@ -13,14 +13,36 @@ use nihav_core::demuxers::*; 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; +use std::time::{Duration, Instant}; 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; + 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() { @@ -33,6 +55,38 @@ fn print_options(name: &str, options: &[NAOptionDefinition]) { } } +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, @@ -53,7 +107,7 @@ struct OutputStreamOptions { enum OutputConvert { Video(NAScale, NABufferType), - Audio(NAAudioInfo, NAChannelMap), + Audio(AudioConverter), None, } @@ -82,6 +136,7 @@ struct Transcoder { no_audio: bool, start: NATimePoint, end: NATimePoint, + verbose: u8, } macro_rules! parse_and_apply_options { @@ -97,7 +152,7 @@ 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); @@ -123,7 +178,7 @@ impl Transcoder { 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() }); @@ -154,7 +209,7 @@ impl Transcoder { 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() }); @@ -265,7 +320,7 @@ impl Transcoder { }, "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::(); @@ -280,7 +335,7 @@ impl Transcoder { }, "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::(); @@ -295,7 +350,7 @@ impl Transcoder { }, "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::(); @@ -310,7 +365,7 @@ impl Transcoder { }, "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::(); @@ -326,7 +381,7 @@ impl Transcoder { // 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::(); @@ -340,7 +395,7 @@ impl Transcoder { } },*/ "bitrate" => { - let ret = oval[1].parse::(); + let ret = parse_bitrate(oval[1]); if let Ok(val) = ret { ostr.enc_params.bitrate = val; } else { @@ -422,7 +477,7 @@ impl Transcoder { 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); @@ -431,14 +486,14 @@ impl Transcoder { } 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); @@ -451,8 +506,35 @@ impl Transcoder { } 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; @@ -466,7 +548,7 @@ impl Transcoder { 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 @@ -499,8 +581,9 @@ println!("can't generate default channel map for {} channels", dainfo.channels); return false; }, }; + let acvt = AudioConverter::new(sainfo, dainfo, dchmap); //todo channelmap - OutputConvert::Audio(*dainfo, dchmap) + OutputConvert::Audio(acvt) } }, _ => OutputConvert::None, @@ -577,7 +660,8 @@ println!("can't generate default channel map for {} channels", dainfo.channels); }, }; //todo channelmap - OutputConvert::Audio(*dainfo, dchmap) + let acvt = AudioConverter::new(sainfo, dainfo, dchmap); + OutputConvert::Audio(acvt) } }, _ => OutputConvert::None, @@ -688,7 +772,20 @@ println!("can't generate default channel map for {} channels", dainfo.channels); 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 = 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); @@ -709,15 +806,15 @@ fn encode_frame(dst_id: u32, encoder: &mut Box, cvt: &mut OutputC } 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 @@ -747,6 +844,7 @@ fn main() { 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"); @@ -931,6 +1029,9 @@ fn main() { 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]; @@ -969,6 +1070,20 @@ fn main() { 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"); @@ -999,6 +1114,13 @@ fn main() { 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() }; + for i in 0..dmx.get_num_streams() { let s = dmx.get_stream(i).unwrap(); let info = s.get_info(); @@ -1035,10 +1157,12 @@ println!("stream {} - {} {}", i, s, info.get_name()); } } - 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; @@ -1078,6 +1202,10 @@ println!("stream {} - {} {}", i, s, info.get_name()); 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; } @@ -1088,12 +1216,35 @@ println!("stream {} - {} {}", i, s, info.get_name()); 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; @@ -1110,6 +1261,7 @@ println!("stream {} - {} {}", i, s, info.get_name()); 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) { @@ -1117,9 +1269,28 @@ println!("stream {} - {} {}", i, s, info.get_name()); } 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; @@ -1127,8 +1298,8 @@ println!("stream {} - {} {}", i, s, info.get_name()); }, }; } - '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() { @@ -1162,6 +1333,9 @@ println!("stream {} - {} {}", i, s, info.get_name()); _ => {}, }; } + if transcoder.verbose > 0 { + println!(); + } let ret = mux.end(); if ret.is_err() {