1 extern crate nihav_core;
2 extern crate nihav_codec_support;
3 extern crate nihav_registry;
4 extern crate nihav_allstuff;
8 use nihav_core::frame::*;
9 use nihav_core::options::*;
10 use nihav_core::codecs::*;
11 use nihav_core::demuxers::*;
12 use nihav_core::muxers::*;
13 use nihav_core::reorder::*;
14 use nihav_registry::detect;
15 use nihav_registry::register;
17 use std::time::{Duration, Instant};
19 macro_rules! parse_and_apply_options {
20 ($obj: expr, $in_opts: expr, $name: expr) => {
21 let mut opts = Vec::with_capacity($in_opts.len());
22 let opt_def = $obj.get_supported_options();
23 for opt in $in_opts.iter() {
24 let mut found = false;
25 for opt_def in opt_def.iter() {
26 let mut matches = opt.name == opt_def.name;
27 if !matches && opt.name.starts_with("no") {
28 let (_, name) = opt.name.split_at(2);
29 matches = name == opt_def.name;
32 let arg = if let Some(ref strval) = opt.value { Some(strval) } else { None };
33 let ret = opt_def.parse(&opt.name, arg);
34 if let Ok((val, _)) = ret {
37 println!("invalid option {} for {}", opt.name, $name);
43 println!(" ignoring option '{}' for {}", opt.name, $name);
46 $obj.set_options(opts.as_slice());
51 use crate::demux::FullRegister;
56 use crate::transcoder::*;
59 fn format_time(ms: u64) -> String {
61 let ds = (ms % 1000) / 100;
62 let (min, s) = (s / 60, s % 60);
63 let (h, min) = (min / 60, min % 60);
66 format!("{}.{}", s, ds)
68 format!("{}:{:02}.{}", min, s, ds)
71 format!("{}:{:02}:{:02}.{}", h, min, s, ds)
75 fn print_options(name: &str, options: &[NAOptionDefinition]) {
76 if options.is_empty() {
77 println!("No custom options.");
79 println!("Options for '{}'", name);
80 for opt in options.iter() {
86 macro_rules! next_arg {
87 ($args: expr, $arg_idx: expr) => {
88 if $arg_idx + 1 >= $args.len() {
89 println!("codec name is required");
95 macro_rules! parse_id {
96 ($val: expr, $stype: expr, $maxval: expr) => {
99 } else if let Ok(val) = $val.parse::<usize>() {
103 println!("{} number should be below {}", $stype, $maxval);
107 println!("invalid {} number '{}'", $stype, $val);
113 fn retrieve_packets(transcoder: &mut Transcoder, mux: &mut Muxer, vdata_size: &mut usize, adata_size: &mut usize, end: bool) -> bool {
114 while let Some(pkt) = transcoder.queue.get_packet() {
115 if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) {
118 let pkt_size = pkt.get_buffer().len();
119 match pkt.get_stream().get_media_type() {
120 StreamType::Video => { *vdata_size += pkt_size; },
121 StreamType::Audio => { *adata_size += pkt_size; },
124 if mux.mux_frame(pkt).is_err() {
125 println!("error muxing packet");
130 while let Some(pkt) = transcoder.queue.get_last_packet() {
131 if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) {
134 let pkt_size = pkt.get_buffer().len();
135 match pkt.get_stream().get_media_type() {
136 StreamType::Video => { *vdata_size += pkt_size; },
137 StreamType::Audio => { *adata_size += pkt_size; },
140 if mux.mux_frame(pkt).is_err() {
141 println!("error muxing packet");
149 #[allow(clippy::single_match)]
151 let args: Vec<_> = env::args().collect();
154 println!("usage: nihav-encoder [options] --input inputfile --output outputfile");
155 println!(" use nihav-encoder --help to list all available options");
158 if args.len() == 2 && (args[1] == "--help" || args[1] == "-h") {
159 println!("usage: nihav-encoder [options] --input inputfile --output outputfile");
160 println!(" query options:");
161 println!(" --list-{{decoders,encoders,demuxers,muxers}} - lists all available decoders/encoders/demuxers/muxers");
162 println!(" --query-{{decoder,encoder,demuxer,muxer}}-options name - lists all options recognized by that decoder/encoder/demuxer/muxer");
163 println!(" processing options:");
164 println!(" --verbose - show time for the currently processed input");
165 println!(" --input inputfile - set input file");
166 println!(" --input-format fmt - force input format");
167 println!(" --demuxer-options options - set input demuxer options");
168 println!(" --scale-options options - set scaler options");
169 println!(" --output outputfile - set output file");
170 println!(" --output-format fmt - force output format");
171 println!(" --muxer-options options - set output muxer options");
172 println!(" --no-audio - do not decode audio streams");
173 println!(" --no-video - do not decode video streams");
174 println!(" --start starttime - start decoding from given position");
175 println!(" --end endtime - end decoding at given position");
176 println!(" --istreamX options - set options for input stream X");
177 println!(" --ostreamX options - set options for output stream X");
179 println!(" (de)muxer and stream options are passed as comma-separated list e.g. --ostream0 width=320,height=240,flip");
183 let full_reg = FullRegister::new();
185 let mut transcoder = Transcoder::new();
188 let mut printed_info = false;
189 let mut force_sync = false;
190 let mut profile_name = "".to_string();
191 let mut custom_profile = false;
192 while arg_idx < args.len() {
193 match args[arg_idx].as_str() {
194 "--list-decoders" => {
195 if full_reg.dec_reg.iter().len() > 0 {
196 println!("Registered decoders:");
197 for dec in full_reg.dec_reg.iter() {
198 let cdesc = register::get_codec_description(dec.name);
199 let full_name = if let Some(cd) = cdesc { cd.get_full_name() } else { "???" };
200 println!(" {} ({})", dec.name, full_name);
203 println!("No registered decoders.");
207 "--list-encoders" => {
208 if full_reg.enc_reg.iter().len() > 0 {
209 println!("Registered encoders:");
210 for enc in full_reg.enc_reg.iter() {
211 let cdesc = register::get_codec_description(enc.name);
212 let full_name = if let Some(cd) = cdesc { cd.get_full_name() } else { "???" };
213 println!(" {} ({})", enc.name, full_name);
216 println!("No registered encoders.");
220 "--list-demuxers" => {
221 print!("Registered demuxers:");
222 for dmx in full_reg.dmx_reg.iter() {
223 print!(" {}", dmx.get_name());
229 print!("Registered muxers:");
230 for mux in full_reg.mux_reg.iter() {
231 print!(" {}", mux.get_name());
236 "--query-decoder-options" => {
237 next_arg!(args, arg_idx);
238 let cname = args[arg_idx].as_str();
239 if let Some(decfunc) = full_reg.dec_reg.find_decoder(cname) {
240 let dec = (decfunc)();
241 let opts = dec.get_supported_options();
242 print_options(cname, opts);
244 println!("codec {} is not found", cname);
248 "--query-demuxer-options" => {
249 next_arg!(args, arg_idx);
250 let dname = args[arg_idx].as_str();
251 let mut mr = MemoryReader::new_read(&[]);
252 let mut br = ByteReader::new(&mut mr);
253 if let Some(dmx_creator) = full_reg.dmx_reg.find_demuxer(dname) {
254 let dmx = dmx_creator.new_demuxer(&mut br);
255 let opts = dmx.get_supported_options();
256 print_options(dname, opts);
258 println!("demuxer {} is not found", dname);
262 "--query-encoder-options" => {
263 next_arg!(args, arg_idx);
264 let cname = args[arg_idx].as_str();
265 if let Some(encfunc) = full_reg.enc_reg.find_encoder(cname) {
266 let enc = (encfunc)();
267 let opts = enc.get_supported_options();
268 print_options(cname, opts);
270 println!("codec {} is not found", cname);
274 "--query-muxer-options" => {
275 next_arg!(args, arg_idx);
276 let name = args[arg_idx].as_str();
278 let mut mw = MemoryWriter::new_write(&mut data);
279 let mut bw = ByteWriter::new(&mut mw);
280 if let Some(mux_creator) = full_reg.mux_reg.find_muxer(name) {
281 let mux = mux_creator.new_muxer(&mut bw);
282 let opts = mux.get_supported_options();
283 print_options(name, opts);
285 println!("muxer {} is not found", name);
289 "--output" | "-o" => {
290 next_arg!(args, arg_idx);
291 transcoder.output_name = args[arg_idx].clone();
293 "--output-format" => {
294 next_arg!(args, arg_idx);
295 transcoder.output_fmt = Some(args[arg_idx].clone());
297 "--scale-options" => {
298 next_arg!(args, arg_idx);
299 if !transcoder.parse_scale_options(&args[arg_idx]) {
300 println!("invalid scale option syntax");
304 "--no-video" | "-vn" => {
305 transcoder.no_video = true;
307 "--no-audio" | "-an" => {
308 transcoder.no_audio = true;
311 next_arg!(args, arg_idx);
312 let ret = args[arg_idx].parse::<NATimePoint>();
313 if let Ok(val) = ret {
314 transcoder.start = val;
316 println!("invalid start time");
321 next_arg!(args, arg_idx);
322 let ret = args[arg_idx].parse::<NATimePoint>();
323 if let Ok(val) = ret {
324 transcoder.end = val;
326 println!("invalid end time");
333 "--muxer-options" => {
334 next_arg!(args, arg_idx);
335 if !transcoder.parse_muxer_options(&args[arg_idx], &full_reg.mux_reg) {
336 println!("invalid muxer option syntax");
341 transcoder.calc_len = true;
343 "--print-profiles" => {
344 println!("Supported profiles:");
345 for (name, profiles) in PROFILES.iter() {
346 print!(" profiles for format '{name}': ");
347 for profile in profiles.iter() {
348 print!(" {}", profile.name);
355 next_arg!(args, arg_idx);
356 profile_name = args[arg_idx].to_string();
358 "--verbose" | "-v" => transcoder.verbose = 1,
359 "-vv" => transcoder.verbose = 2,
360 "-v-" => transcoder.verbose = 0,
362 if args[arg_idx].starts_with("--istream") {
363 let opt0 = &args[arg_idx];
364 next_arg!(args, arg_idx);
365 if !transcoder.parse_istream_options(opt0, &args[arg_idx]) {
366 println!("invalid input stream option syntax");
369 } else if args[arg_idx].starts_with("--ostream") {
370 let opt0 = &args[arg_idx];
371 next_arg!(args, arg_idx);
372 if !transcoder.parse_ostream_options(opt0, &args[arg_idx], &full_reg.enc_reg) {
373 println!("invalid output stream option syntax");
376 custom_profile = true;
377 } else if args[arg_idx].starts_with("--iformat") {
378 let id = parse_id!(&args[arg_idx][9..], "input format", transcoder.input_fmt.len());
379 next_arg!(args, arg_idx);
380 transcoder.input_fmt[id] = Some(args[arg_idx].clone());
381 } else if args[arg_idx].starts_with("--input-format") {
382 let id = parse_id!(&args[arg_idx][14..], "input format", transcoder.input_fmt.len());
383 next_arg!(args, arg_idx);
384 transcoder.input_fmt[id] = Some(args[arg_idx].clone());
385 } else if args[arg_idx].starts_with("--input") { // should be after --input-format
386 let id = parse_id!(&args[arg_idx][7..], "input", transcoder.input_name.len());
387 next_arg!(args, arg_idx);
388 transcoder.input_name[id] = Some(args[arg_idx].clone());
389 } else if args[arg_idx].starts_with("-i") {
390 let id = parse_id!(&args[arg_idx][2..], "input", transcoder.input_name.len());
391 next_arg!(args, arg_idx);
392 transcoder.input_name[id] = Some(args[arg_idx].clone());
393 } else if args[arg_idx].starts_with("--demuxer-options") {
394 let id = parse_id!(&args[arg_idx][17..], "input options", transcoder.demux_opts.len());
395 next_arg!(args, arg_idx);
396 if !transcoder.parse_demuxer_options(&args[arg_idx], &full_reg.dmx_reg, id) {
397 println!("invalid demuxer option syntax");
400 } else if args[arg_idx].starts_with("--") {
401 println!("unknown option '{}'", args[arg_idx]);
403 println!("unrecognized argument '{}'", args[arg_idx]);
414 if transcoder.input_name.iter().flatten().count() == 0 {
415 println!("no input name(s) provided");
418 if transcoder.output_name.is_empty() {
419 println!("no output name provided");
423 let mut demuxers = Vec::with_capacity(1);
424 if !transcoder.create_demuxers(&mut demuxers, &full_reg, true) {
428 let duration = demuxers.iter().fold(0u64, |mindur, (dmx, _)| {
429 let dur = dmx.get_duration();
438 let duration_string = if duration != 0 { format_time(duration) } else { String::new() };
440 let mut ism = StreamManager::new();
441 let mut is_offset = Vec::with_capacity(demuxers.len());
443 let mut nstreams = 0;
444 for (dmx, _) in demuxers.iter() {
445 is_offset.push(nstreams);
446 let sm = dmx.get_stream_manager();
447 let max_id = sm.iter().fold(0u32, |id, strm| id.max(strm.id));
448 for stream in sm.iter() {
449 let mut newstream = (*stream).clone();
450 if transcoder.global_tb == (0, 0) && newstream.get_media_type() == StreamType::Video {
451 transcoder.global_tb = newstream.get_timebase();
453 newstream.id += start;
454 ism.add_stream(newstream);
457 nstreams += sm.get_num_streams();
460 for (&is_off, (dmx, _)) in is_offset.iter().zip(demuxers.iter_mut()) {
461 for i in 0..dmx.get_num_streams() {
462 let s = dmx.get_stream(i).unwrap();
463 let info = s.get_info();
464 let decfunc = full_reg.dec_reg.find_decoder(info.get_name());
465 let str_id = (s.get_num() + is_off) as u32;
466 if let Some(create_dec) = decfunc {
467 let mut decoder = (create_dec)();
468 let mut dsupp = Box::new(NADecoderSupport::new());
469 let ret = decoder.init(&mut dsupp, info.clone());
471 println!("Error initialising decoder '{}' for stream {}", info.get_name(), str_id);
474 transcoder.apply_decoder_options(decoder.as_mut(), str_id);
475 let desc = register::get_codec_description(info.get_name());
476 let has_b = if let Some(desc) = desc {
479 println!("No codec description found, using B-frame reorderer.");
482 let reorderer: Box<dyn FrameReorderer> = if has_b { Box::new(IPBReorderer::new()) } else { Box::new(NoReorderer::new()) };
483 transcoder.decoders.push(Some(DecodeContext{ dsupp, decoder, reorderer }));
485 println!("No decoder for stream {} ({}) is found", str_id, info.get_name());
486 transcoder.decoders.push(None);
489 if transcoder.start != NATimePoint::None {
490 let ret = dmx.seek(transcoder.start);
492 println!(" failed to seek to {} error {:?}", transcoder.start, ret.err().unwrap());
497 let output_fmt = if let Some(ref fmtname) = transcoder.output_fmt {
499 } else if transcoder.output_name.as_str() == "/dev/null" {
501 } else if let Some(fmtname) = detect::detect_format_by_name(transcoder.output_name.as_str()) {
504 println!("Cannot guess muxer for output");
507 let ret = full_reg.mux_reg.find_muxer(output_fmt);
508 let ofmt = output_fmt.to_string();
511 println!("cannot find muxer '{}'", output_fmt);
514 let mux_creator = ret.unwrap();
516 if custom_profile && !profile_name.is_empty() {
517 println!("profile setting is incompatible with custom --ostream options");
520 if !profile_name.is_empty() {
521 if let Some(profiles) = PROFILES.iter().find(|(fmt, _)| fmt == &output_fmt) {
522 if let Some(ref_profile) = profiles.1.iter().find(|p| p.name == profile_name) {
523 transcoder.profile = Some(ref_profile.profile);
525 println!("profile '{profile_name}' is not defined for output format '{output_fmt}'");
528 println!("no profiles for output format '{output_fmt}'");
532 let mux_quirks = mux_creator.get_quirks();
533 if mux_quirks.is_fixed_duration() {
534 transcoder.calc_len = true;
536 transcoder.fixed_rate = mux_quirks.is_fixed_rate();
537 transcoder.queue.set_sync(force_sync || !mux_quirks.is_unsync());
539 if transcoder.calc_len {
540 let mut sids = Vec::new();
541 transcoder.nframes.clear();
542 for (dmx, _) in demuxers.iter_mut() {
543 let sstart = transcoder.nframes.len();
544 let sm = dmx.get_stream_manager();
546 for stream in sm.iter() {
547 transcoder.nframes.push(0);
548 sids.push(stream.get_id());
551 while let Ok(pkt) = dmx.get_frame() {
552 let stream = pkt.get_stream();
553 let pos = sstart + sids.iter().position(|&x| x == stream.get_id()).unwrap();
554 transcoder.nframes[pos] += 1;
557 // this is necessary since not all demuxers allow to seek even back to the start
559 if !transcoder.create_demuxers(&mut demuxers, &full_reg, false) {
560 println!("failed to re-create demuxer(s)");
565 let mux_caps = mux_creator.get_capabilities();
566 let mut out_sm = StreamManager::new();
567 if !transcoder.negotiate_stream_map(&ism, mux_caps, &mut out_sm, &full_reg.enc_reg) {
568 println!("cannot determine stream map");
572 let ret = File::create(transcoder.output_name.as_str());
574 println!("cannot open output file");
577 let mut fw = FileWriter::new_write(ret.unwrap());
578 let mut bw = ByteWriter::new(&mut fw);
579 let ret = create_muxer(mux_creator, out_sm, &mut bw);
580 if let Err(err) = ret {
581 println!("cannot create muxer instance {:?}", err);
584 let mut mux = ret.unwrap();
585 parse_and_apply_options!(mux, &transcoder.mux_opts, "output");
587 println!("Output {} muxer {}", transcoder.output_name, ofmt);
588 for ostr in mux.get_streams() {
589 let mut is_copy = false;
590 let stream_id = ostr.get_num() as u32;
591 for omode in transcoder.encoders.iter() {
592 if let OutputMode::Copy(sno) = omode {
593 if sno == &stream_id {
599 println!(" #{}: {} {}{}", ostr.get_num(), ostr, ostr.get_info().get_name(), if is_copy { " (copy)" } else { "" });
602 transcoder.queue.reserve_streams(mux.get_num_streams());
604 let mut time = Instant::now();
605 let show_interval = Duration::from_millis(100);
606 let mut adata_size = 0;
607 let mut vdata_size = 0;
609 let mut last_known_time = None;
611 let mut pktres = Err(DemuxerError::EOF);
614 if !demuxers.iter().any(|(_, eof)| !eof) {
617 let mut got_res = false;
618 if !demuxers[cur_dmx].1 {
619 pktres = demuxers[cur_dmx].0.get_frame();
624 if cur_dmx >= demuxers.len() {
632 if let Err(DemuxerError::EOF) = pktres { break; }
634 println!("demuxing error");
637 let mut pkt = pktres.unwrap();
638 if transcoder.start != NATimePoint::None && pkt.ts.less_than(transcoder.start) { continue; }
639 let src_id = pkt.get_stream().get_num() + is_offset[src_dmx];
641 let newstream = ism.get_stream(src_id).unwrap();
642 pkt.reassign(newstream, ts);
644 if transcoder.verbose > 0 && time.elapsed() >= show_interval {
645 if let Some(pts) = pkt.get_pts() {
646 let cur_time = format_time(NATimeInfo::ts_to_time(pts, 1000, pkt.ts.tb_num, pkt.ts.tb_den));
647 print!(" {}", cur_time);
648 last_known_time = Some(cur_time);
649 } else if let Some(ref time) = last_known_time {
654 if !duration_string.is_empty() {
655 print!(" / {}", duration_string);
657 if transcoder.verbose > 1 {
658 print!(" data sizes V: {} A: {}", vdata_size, adata_size);
661 std::io::stdout().flush().unwrap();
662 time = Instant::now();
664 match transcoder.encoders[src_id] {
665 OutputMode::Drop => {},
666 OutputMode::Copy(dst_id) => {
667 let dstr = mux.get_stream(dst_id as usize).unwrap();
668 pkt.reassign(dstr, pkt.get_time_information());
669 if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'main_loop; }
670 let pkt_size = pkt.get_buffer().len();
671 match pkt.get_stream().get_media_type() {
672 StreamType::Video => { vdata_size += pkt_size; },
673 StreamType::Audio => { adata_size += pkt_size; },
676 transcoder.queue.queue_packet(pkt);
678 OutputMode::Encode(dst_id, ref mut encoder) => {
679 if let Some(ref mut dec_ctx) = transcoder.decoders[src_id] {
680 let ret = dec_ctx.decoder.decode(&mut dec_ctx.dsupp, &pkt);
681 if let (true, Err(DecoderError::MissingReference)) = (transcoder.start != NATimePoint::None, &ret) {
685 println!("error decoding stream {}", src_id);
688 let frm = ret.unwrap();
689 dec_ctx.reorderer.add_frame(frm);
690 while let Some(frm) = dec_ctx.reorderer.get_frame() {
691 match encoder.encode_frame(dst_id, frm, &transcoder.scale_opts, &mut transcoder.queue) {
695 println!("encoder error {err:?}");
701 println!("no decoder for stream {}", src_id);
707 if !retrieve_packets(&mut transcoder, &mut mux, &mut vdata_size, &mut adata_size, false) {
711 'reord_flush_loop: for stream in ism.iter() {
712 let src_id = stream.get_num();
713 if let OutputMode::Encode(dst_id, ref mut encoder) = transcoder.encoders[src_id] {
714 if let Some(ref mut dec_ctx) = transcoder.decoders[src_id] {
715 while let Some(frm) = dec_ctx.reorderer.get_last_frames() {
716 match encoder.encode_frame(dst_id, frm, &transcoder.scale_opts, &mut transcoder.queue) {
720 println!("encoder error {err:?}");
721 break 'reord_flush_loop;
728 /*'flush_loop:*/ for enc in transcoder.encoders.iter_mut() {
730 OutputMode::Encode(str_id, ref mut encoder) => {
731 let ret = encoder.flush(&mut transcoder.queue);
733 println!("error flushing encoder for stream {}", str_id);
741 retrieve_packets(&mut transcoder, &mut mux, &mut vdata_size, &mut adata_size, true);
743 if transcoder.verbose > 0 {
749 println!("error at finalising muxing");
755 profile: EncodingProfile,
758 const PROFILES: &[(&str, &[ProfileDef])] = &[
763 profile: EncodingProfile {
772 profile: EncodingProfile {
774 voptions: &[("range", Some("4")), ("compr_level", Some("fast"))],
781 profile: EncodingProfile {
790 profile: EncodingProfile {
791 vname: "rawvideo-ms",