From: Kostya Shishkov Date: Sat, 6 Jun 2020 15:50:40 +0000 (+0200) Subject: Initial work on transcoder X-Git-Url: https://git.nihav.org/?p=nihav-encoder.git;a=commitdiff_plain;h=6dc69f517742892c72d02f84ce74b074f9b4d6e5 Initial work on transcoder --- 6dc69f517742892c72d02f84ce74b074f9b4d6e5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0e3bca --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f73d291 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "nihav-encoder" +version = "0.1.0" +authors = ["Kostya Shishkov "] +edition = "2018" + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..fb4d80f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,875 @@ +extern crate nihav_core; +extern crate nihav_codec_support; +extern crate nihav_registry; +extern crate nihav_allstuff; + +use std::fs::File; +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::scale::*; +use nihav_core::soundcvt::*; +use nihav_registry::detect; +use nihav_allstuff::*; +use std::env; + +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); + } + } +} + +struct OptionArgs { + name: String, + value: Option, +} + +struct InputStreamOptions { + id: u32, + drop: bool, + dec_opts: Vec, +} + +struct OutputStreamOptions { + id: u32, + enc_params: EncodeParameters, + enc_name: String, + enc_opts: Vec, +} + +enum OutputConvert { + Video(NAScale, NABufferType), + Audio(NAAudioInfo, NAChannelMap), + None, +} + +enum OutputMode { + Drop, + Copy(u32), + Encode(u32, Box, OutputConvert), +} + +#[derive(Default)] +struct Transcoder { + input_name: String, + input_fmt: Option, + output_name: String, + output_fmt: Option, + demux_opts: Vec, + mux_opts: Vec, + istr_opts: Vec, + ostr_opts: Vec, + decoders: Vec, Box)>>, + encoders: Vec, + no_video: bool, + no_audio: bool, +} + +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() { + if opt.name == opt_def.name { + let arg = if let Some(ref str) = opt.value { Some(str) } else { None }; + let ret = opt_def.parse(&opt.name, arg); + if ret.is_err() { + println!("invalid option {} for {}", opt.name, $name); + } else { + let (val, _) = ret.unwrap(); + opts.push(val); + } + 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::(); + 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::(); + 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] { + "encoder" => { + if enc_reg.find_encoder(oval[1]).is_some() { + ostr.enc_name = oval[1].to_string(); + } else { + println!("unknown encoder '{}'", oval[1]); + } + }, + "width" => { + if ostr.enc_params.format == NACodecTypeInfo::None { + ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); + } + if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { + let ret = oval[1].parse::(); + if let Ok(val) = ret { + vinfo.width = val; + } else { + println!("invalid width"); + } + } else { + println!("video option for audio stream"); + } + }, + "height" => { + if ostr.enc_params.format == NACodecTypeInfo::None { + ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); + } + if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { + let ret = oval[1].parse::(); + if let Ok(val) = ret { + vinfo.height = val; + } else { + println!("invalid height"); + } + } else { + println!("video option for audio stream"); + } + }, +//todo formaton + "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::(); + 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::(); + 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::(); + if let Ok(val) = ret { + ainfo.block_len = val; + } else { + println!("invalid block_length"); + } + } else { + println!("audio option for video stream"); + } + }, +//todo soniton, channel map + "bitrate" => { + let ret = oval[1].parse::(); + if let Ok(val) = ret { + ostr.enc_params.bitrate = val; + } else { + println!("invalid bitrate value"); + } + }, + "quality" => { + let ret = oval[1].parse::(); + if let Ok(val) = ret { + ostr.enc_params.quality = val; + } else { + println!("invalid quality value"); + } + }, + _ => { + ostr.enc_opts.push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); + }, + } + } else { + println!("unrecognized option '{}'", opt); + } + } + true + } + fn parse_demuxer_options(&mut self, opts: &str, dmx_reg: &RegisteredDemuxers) -> 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 apply_decoder_options(&self, dec: &mut 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 cname == "copy" && istr.get_info().get_name() == cname { + 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())(); + 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 { + OutputConvert::None + } else { + let ofmt = ScaleInfo { fmt: dvinfo.format, width: dvinfo.width, height: dvinfo.height }; + let ret = NAScale::new(ofmt, ofmt); + 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()); + + 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 { + 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; + } + } + 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 + }, + } + } +} + +macro_rules! next_arg { + ($args: expr, $arg_idx: expr) => { + if $arg_idx + 1 >= $args.len() { + println!("codec name is required"); + } + $arg_idx += 1; + } +} + +fn main() { + let args: Vec<_> = env::args().collect(); + + if args.len() == 1 { + println!("usage: nihav-encoder [-noout] [-vn] [-an] input [lastpts]"); + return; + } + + let mut dmx_reg = RegisteredDemuxers::new(); + nihav_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + nihav_register_all_codecs(&mut dec_reg); + + let mut mux_reg = RegisteredMuxers::new(); + nihav_register_all_muxers(&mut mux_reg); + let mut enc_reg = RegisteredEncoders::new(); + nihav_register_all_encoders(&mut enc_reg); + + let mut transcoder = Transcoder::new(); + + let mut arg_idx = 1; + while arg_idx < args.len() { + match args[arg_idx].as_str() { + "--query-decoder-options" => { + next_arg!(args, arg_idx); + let cname = args[arg_idx].as_str(); + if let Some(decfunc) = 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); + } + }, + "--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) = 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); + } + }, + "--query-encoder-options" => { + next_arg!(args, arg_idx); + let cname = args[arg_idx].as_str(); + if let Some(encfunc) = 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); + } + }, + "--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) = 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); + } + }, + "--input" => { + 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" => { + 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], &dmx_reg) { + println!("invalid demuxer option syntax"); + return; + } + }, + "--no-video" => { + transcoder.no_video = true; + }, + "--no-audio" => { + transcoder.no_audio = true; + }, + "--muxer-options" => { + next_arg!(args, arg_idx); + if !transcoder.parse_muxer_options(&args[arg_idx], &mux_reg) { + println!("invalid muxer option syntax"); + return; + } + }, + _ => { + 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], &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 transcoder.input_name.len() == 0 { + println!("no input name provided"); + return; + } + if transcoder.output_name.len() == 0 { + 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 mut file = res.unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + + let dmx_name = if let Some(ref str) = transcoder.input_fmt { + str.as_str() + } else { + if let Some((dmx_name, score)) = detect::detect_format(transcoder.input_name.as_str(), &mut br) { + println!("detected {} with score {:?}", dmx_name, score); + dmx_name + } else { + println!("cannot detect input format"); + return; + } + }; + let ret = dmx_reg.find_demuxer(dmx_name); + if ret.is_none() { + println!("cannot find demuxer for '{}'", dmx_name); + return; + } + let dmx_fact = ret.unwrap(); + br.seek(SeekFrom::Start(0)).unwrap(); + let mut dmx = create_demuxer(dmx_fact, &mut br).unwrap(); + 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 = 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); + transcoder.decoders.push(Some((dsupp, dec))); + } else { + println!("No decoder for stream {} ({}) is found", str_id, info.get_name()); + transcoder.decoders.push(None); + } + } + + 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 = 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, &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()); + } + + 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(); + let src_id = pkt.get_stream().get_num(); + 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 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)) = transcoder.decoders[src_id] { + let ret = decoder.decode(dsupp, &pkt); + if ret.is_err() { + println!("error decoding stream {}", src_id); + break; + } + let frm = ret.unwrap(); + let buf = frm.get_buffer(); + let cbuf = 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(cur_ifmt, ofmt); + if ret.is_err() { + println!("error re-initialising scaler for {} -> {}", cur_ifmt, ofmt); + break; + } + *scaler = ret.unwrap(); + } + let ret = scaler.convert(&buf, dbuf); + if ret.is_err() { + println!("error converting frame for encoding"); + break; + } + dbuf.clone() + }, + OutputConvert::Audio(ref dinfo, ref dchmap) => { + let ret = convert_audio_frame(&buf, dinfo, dchmap); + if ret.is_err() { + println!("error converting audio for stream {}", dst_id); + break; + } + ret.unwrap() + }, + }; + let cfrm = NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cbuf); + encoder.encode(&cfrm).unwrap(); + while let Ok(Some(pkt)) = encoder.get_packet() { + mux.mux_frame(pkt).unwrap(); + } + } else { + println!("no decoder for stream {}", src_id); + break; + } + }, + }; + } + '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; + } + } + } + }, + _ => {}, + }; + } + + let ret = mux.end(); + if ret.is_err() { + println!("error at finalising muxing"); + } +}