X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=src%2Fmain.rs;h=a2889a86b6ab7df77251d3f9bf2f8aa5ba9d54c7;hb=30611a63a7e339e63f05acb263a41307255040f1;hp=fd1fdaefbc5cb5dabcb3eaa9edb76700b8d7d460;hpb=91a15e39ebb3b9fef4e8806b191a3c10ddb8a27c;p=nihav-encoder.git diff --git a/src/main.rs b/src/main.rs index fd1fdae..a2889a8 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::*; @@ -17,11 +17,28 @@ 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; +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."); @@ -33,6 +50,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, @@ -82,6 +131,7 @@ struct Transcoder { no_audio: bool, start: NATimePoint, end: NATimePoint, + verbose: u8, } macro_rules! parse_and_apply_options { @@ -340,7 +390,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 { @@ -524,9 +574,72 @@ println!("encoder {} is not supported by output (expected {})", istr.id, istr.ge out_sm.add_stream_ref(istr); self.encoders.push(OutputMode::Copy(out_id)); } else { -println!("stream {} ({}) can't be handled", istr.id, istr.get_info().get_name()); -// todo autoselect encoder? - return false; + 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 + OutputConvert::Audio(*dainfo, dchmap) + } + }, + _ => 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 } @@ -625,7 +738,14 @@ println!("stream {} ({}) can't be handled", istr.id, istr.get_info().get_name()) 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 { + println!("encoder does not support skip frames, skipping"); + return true; + } + buf + } else { + match cvt { OutputConvert::None => buf, OutputConvert::Video(ref mut scaler, ref mut dbuf) => { let cur_ifmt = get_scale_fmt_from_pic(&buf); @@ -654,7 +774,8 @@ fn encode_frame(dst_id: u32, encoder: &mut Box, cvt: &mut OutputC } ret.unwrap() }, - }; + } + }; let cfrm = NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cbuf); encoder.encode(&cfrm).unwrap(); true @@ -684,6 +805,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"); @@ -868,6 +990,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]; @@ -935,6 +1060,9 @@ fn main() { 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(); @@ -1015,6 +1143,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; } @@ -1025,12 +1157,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; @@ -1054,6 +1209,12 @@ 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(); } } @@ -1099,6 +1260,9 @@ println!("stream {} - {} {}", i, s, info.get_name()); _ => {}, }; } + if transcoder.verbose > 0 { + println!(); + } let ret = mux.end(); if ret.is_err() {