]> git.nihav.org Git - nihav-encoder.git/blame - src/main.rs
use u32::MAX instead of deprecated std::u32::MAX
[nihav-encoder.git] / src / main.rs
CommitLineData
6dc69f51
KS
1extern crate nihav_core;
2extern crate nihav_codec_support;
3extern crate nihav_registry;
4extern crate nihav_allstuff;
5
6use std::fs::File;
28a83637 7use std::io::Write;
6dc69f51
KS
8use nihav_core::frame::*;
9use nihav_core::options::*;
10use nihav_core::codecs::*;
11use nihav_core::demuxers::*;
12use nihav_core::muxers::*;
d9fe2b71 13use nihav_core::reorder::*;
6dc69f51 14use nihav_registry::detect;
e6cb09af 15use nihav_registry::register;
6dc69f51 16use std::env;
5ec8115f 17use std::time::{Duration, Instant};
6dc69f51 18
6dc69f51
KS
19macro_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() {
3e2a4e88
KS
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;
30 }
31 if matches {
fd03a232 32 let arg = if let Some(ref strval) = opt.value { Some(strval) } else { None };
6dc69f51 33 let ret = opt_def.parse(&opt.name, arg);
86c54d88 34 if let Ok((val, _)) = ret {
6dc69f51 35 opts.push(val);
86c54d88
KS
36 } else {
37 println!("invalid option {} for {}", opt.name, $name);
6dc69f51
KS
38 }
39 found = true;
40 }
41 }
42 if !found {
43 println!(" ignoring option '{}' for {}", opt.name, $name);
44 }
45 }
46 $obj.set_options(opts.as_slice());
47 }
48}
49
28a83637
KS
50mod demux;
51use crate::demux::FullRegister;
52mod null;
53mod acvt;
54mod imgseq;
55mod transcoder;
56use crate::transcoder::*;
6dc69f51 57
6dc69f51 58
28a83637
KS
59fn format_time(ms: u64) -> String {
60 let s = ms / 1000;
61 let ds = (ms % 1000) / 100;
62 let (min, s) = (s / 60, s % 60);
63 let (h, min) = (min / 60, min % 60);
64 if h == 0 {
65 if min == 0 {
66 format!("{}.{}", s, ds)
6dc69f51 67 } else {
28a83637 68 format!("{}:{:02}.{}", min, s, ds)
6dc69f51 69 }
28a83637
KS
70 } else {
71 format!("{}:{:02}:{:02}.{}", h, min, s, ds)
6dc69f51 72 }
28a83637 73}
79eb70eb 74
28a83637
KS
75fn print_options(name: &str, options: &[NAOptionDefinition]) {
76 if options.is_empty() {
77 println!("No custom options.");
78 } else {
79 println!("Options for '{}'", name);
80 for opt in options.iter() {
81 println!(" {}", opt);
79eb70eb 82 }
79eb70eb 83 }
6dc69f51
KS
84}
85
86macro_rules! next_arg {
87 ($args: expr, $arg_idx: expr) => {
88 if $arg_idx + 1 >= $args.len() {
89 println!("codec name is required");
90 }
91 $arg_idx += 1;
92 }
93}
94
4a835a2a
KS
95macro_rules! parse_id {
96 ($val: expr, $stype: expr, $maxval: expr) => {
97 if $val.is_empty() {
98 0
99 } else if let Ok(val) = $val.parse::<usize>() {
100 if val < $maxval {
101 val
102 } else {
103 println!("{} number should be below {}", $stype, $maxval);
104 return;
105 }
106 } else {
107 println!("invalid {} number '{}'", $stype, $val);
108 return;
109 }
110 }
111}
112
a5c44ebf 113fn retrieve_packets(transcoder: &mut Transcoder, mux: &mut Muxer, vdata_size: &mut usize, adata_size: &mut usize, end: bool) -> bool {
69f7e674
KS
114 while let Some(pkt) = transcoder.queue.get_packet() {
115 if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) {
116 return false;
117 }
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; },
122 _ => {},
123 };
124 if mux.mux_frame(pkt).is_err() {
125 println!("error muxing packet");
126 return false;
127 }
128 }
a5c44ebf
KS
129 if end {
130 while let Some(pkt) = transcoder.queue.get_last_packet() {
131 if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) {
132 return false;
133 }
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; },
138 _ => {},
139 };
140 if mux.mux_frame(pkt).is_err() {
141 println!("error muxing packet");
142 return false;
143 }
144 }
145 }
69f7e674
KS
146 true
147}
148
86c54d88 149#[allow(clippy::single_match)]
6dc69f51
KS
150fn main() {
151 let args: Vec<_> = env::args().collect();
152
153 if args.len() == 1 {
071d353e
KS
154 println!("usage: nihav-encoder [options] --input inputfile --output outputfile");
155 println!(" use nihav-encoder --help to list all available options");
156 return;
157 }
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:");
3e82df4a 164 println!(" --verbose - show time for the currently processed input");
071d353e
KS
165 println!(" --input inputfile - set input file");
166 println!(" --input-format fmt - force input format");
167 println!(" --demuxer-options options - set input demuxer options");
df37d3b1 168 println!(" --scale-options options - set scaler options");
071d353e
KS
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");
178 println!();
179 println!(" (de)muxer and stream options are passed as comma-separated list e.g. --ostream0 width=320,height=240,flip");
6dc69f51
KS
180 return;
181 }
182
91a15e39 183 let full_reg = FullRegister::new();
6dc69f51
KS
184
185 let mut transcoder = Transcoder::new();
186
187 let mut arg_idx = 1;
5a8a7cdb 188 let mut printed_info = false;
df5974a1 189 let mut force_sync = false;
4e931658
KS
190 let mut profile_name = "".to_string();
191 let mut custom_profile = false;
6dc69f51
KS
192 while arg_idx < args.len() {
193 match args[arg_idx].as_str() {
e6cb09af 194 "--list-decoders" => {
91a15e39 195 if full_reg.dec_reg.iter().len() > 0 {
e6cb09af 196 println!("Registered decoders:");
91a15e39 197 for dec in full_reg.dec_reg.iter() {
e6cb09af
KS
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);
201 }
202 } else {
203 println!("No registered decoders.");
204 }
5a8a7cdb 205 printed_info = true;
e6cb09af
KS
206 },
207 "--list-encoders" => {
91a15e39 208 if full_reg.enc_reg.iter().len() > 0 {
e6cb09af 209 println!("Registered encoders:");
91a15e39 210 for enc in full_reg.enc_reg.iter() {
e6cb09af
KS
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);
214 }
215 } else {
216 println!("No registered encoders.");
217 }
5a8a7cdb 218 printed_info = true;
e6cb09af
KS
219 },
220 "--list-demuxers" => {
221 print!("Registered demuxers:");
91a15e39 222 for dmx in full_reg.dmx_reg.iter() {
e6cb09af
KS
223 print!(" {}", dmx.get_name());
224 }
225 println!();
5a8a7cdb 226 printed_info = true;
e6cb09af
KS
227 },
228 "--list-muxers" => {
229 print!("Registered muxers:");
91a15e39 230 for mux in full_reg.mux_reg.iter() {
e6cb09af
KS
231 print!(" {}", mux.get_name());
232 }
233 println!();
5a8a7cdb 234 printed_info = true;
e6cb09af 235 },
6dc69f51
KS
236 "--query-decoder-options" => {
237 next_arg!(args, arg_idx);
238 let cname = args[arg_idx].as_str();
91a15e39 239 if let Some(decfunc) = full_reg.dec_reg.find_decoder(cname) {
6dc69f51
KS
240 let dec = (decfunc)();
241 let opts = dec.get_supported_options();
242 print_options(cname, opts);
243 } else {
244 println!("codec {} is not found", cname);
245 }
5a8a7cdb 246 printed_info = true;
6dc69f51
KS
247 },
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);
91a15e39 253 if let Some(dmx_creator) = full_reg.dmx_reg.find_demuxer(dname) {
6dc69f51
KS
254 let dmx = dmx_creator.new_demuxer(&mut br);
255 let opts = dmx.get_supported_options();
256 print_options(dname, opts);
257 } else {
258 println!("demuxer {} is not found", dname);
259 }
5a8a7cdb 260 printed_info = true;
6dc69f51
KS
261 },
262 "--query-encoder-options" => {
263 next_arg!(args, arg_idx);
264 let cname = args[arg_idx].as_str();
91a15e39 265 if let Some(encfunc) = full_reg.enc_reg.find_encoder(cname) {
6dc69f51
KS
266 let enc = (encfunc)();
267 let opts = enc.get_supported_options();
268 print_options(cname, opts);
269 } else {
270 println!("codec {} is not found", cname);
271 }
5a8a7cdb 272 printed_info = true;
6dc69f51
KS
273 },
274 "--query-muxer-options" => {
275 next_arg!(args, arg_idx);
276 let name = args[arg_idx].as_str();
277 let mut data = [];
278 let mut mw = MemoryWriter::new_write(&mut data);
279 let mut bw = ByteWriter::new(&mut mw);
91a15e39 280 if let Some(mux_creator) = full_reg.mux_reg.find_muxer(name) {
6dc69f51
KS
281 let mux = mux_creator.new_muxer(&mut bw);
282 let opts = mux.get_supported_options();
283 print_options(name, opts);
284 } else {
285 println!("muxer {} is not found", name);
286 }
5a8a7cdb 287 printed_info = true;
6dc69f51 288 },
4b5c61e2 289 "--output" | "-o" => {
6dc69f51
KS
290 next_arg!(args, arg_idx);
291 transcoder.output_name = args[arg_idx].clone();
292 },
293 "--output-format" => {
294 next_arg!(args, arg_idx);
295 transcoder.output_fmt = Some(args[arg_idx].clone());
296 },
df37d3b1
KS
297 "--scale-options" => {
298 next_arg!(args, arg_idx);
299 if !transcoder.parse_scale_options(&args[arg_idx]) {
300 println!("invalid scale option syntax");
301 return;
302 }
303 },
4b5c61e2 304 "--no-video" | "-vn" => {
6dc69f51
KS
305 transcoder.no_video = true;
306 },
4b5c61e2 307 "--no-audio" | "-an" => {
6dc69f51
KS
308 transcoder.no_audio = true;
309 },
951916e8
KS
310 "--start" => {
311 next_arg!(args, arg_idx);
312 let ret = args[arg_idx].parse::<NATimePoint>();
313 if let Ok(val) = ret {
314 transcoder.start = val;
315 } else {
316 println!("invalid start time");
317 return;
318 }
319 },
320 "--end" => {
321 next_arg!(args, arg_idx);
322 let ret = args[arg_idx].parse::<NATimePoint>();
323 if let Ok(val) = ret {
324 transcoder.end = val;
325 } else {
b0c51548 326 println!("invalid end time");
951916e8
KS
327 return;
328 }
329 },
df5974a1
KS
330 "--sync" => {
331 force_sync = true;
332 },
6dc69f51
KS
333 "--muxer-options" => {
334 next_arg!(args, arg_idx);
91a15e39 335 if !transcoder.parse_muxer_options(&args[arg_idx], &full_reg.mux_reg) {
6dc69f51
KS
336 println!("invalid muxer option syntax");
337 return;
338 }
339 },
79eb70eb
KS
340 "--calc-len" => {
341 transcoder.calc_len = true;
342 },
4e931658
KS
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);
349 }
350 println!();
351 }
352 printed_info = true;
353 },
354 "--profile" => {
355 next_arg!(args, arg_idx);
356 profile_name = args[arg_idx].to_string();
357 },
908ba48c
KS
358 "--verbose" | "-v" => transcoder.verbose = 1,
359 "-vv" => transcoder.verbose = 2,
360 "-v-" => transcoder.verbose = 0,
6dc69f51
KS
361 _ => {
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");
367 return;
368 }
369 } else if args[arg_idx].starts_with("--ostream") {
370 let opt0 = &args[arg_idx];
371 next_arg!(args, arg_idx);
91a15e39 372 if !transcoder.parse_ostream_options(opt0, &args[arg_idx], &full_reg.enc_reg) {
6dc69f51
KS
373 println!("invalid output stream option syntax");
374 return;
375 }
4e931658 376 custom_profile = true;
5d3b4c0a
KS
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());
4a835a2a
KS
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");
398 return;
399 }
6dc69f51 400 } else if args[arg_idx].starts_with("--") {
bafd0754 401 println!("unknown option '{}'", args[arg_idx]);
6dc69f51 402 } else {
bafd0754 403 println!("unrecognized argument '{}'", args[arg_idx]);
6dc69f51
KS
404 }
405 },
406 };
407 arg_idx += 1;
408 }
409
5a8a7cdb
KS
410 if printed_info {
411 return;
412 }
413
4a835a2a
KS
414 if transcoder.input_name.iter().flatten().count() == 0 {
415 println!("no input name(s) provided");
6dc69f51
KS
416 return;
417 }
86c54d88 418 if transcoder.output_name.is_empty() {
6dc69f51
KS
419 println!("no output name provided");
420 return;
421 }
422
4a835a2a 423 let mut demuxers = Vec::with_capacity(1);
79eb70eb
KS
424 if !transcoder.create_demuxers(&mut demuxers, &full_reg, true) {
425 return;
6dc69f51 426 }
90683bc3 427
4a835a2a
KS
428 let duration = demuxers.iter().fold(0u64, |mindur, (dmx, _)| {
429 let dur = dmx.get_duration();
fe5bdfb1
KS
430 if mindur == 0 {
431 dur
432 } else if dur > 0 {
4a835a2a
KS
433 mindur.min(dur)
434 } else {
435 mindur
436 }
437 });
5ec8115f
KS
438 let duration_string = if duration != 0 { format_time(duration) } else { String::new() };
439
4a835a2a
KS
440 let mut ism = StreamManager::new();
441 let mut is_offset = Vec::with_capacity(demuxers.len());
442 let mut start = 0;
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();
c121b242 450 if transcoder.global_tb == (0, 0) && newstream.get_media_type() == StreamType::Video {
e47b2114
KS
451 transcoder.global_tb = newstream.get_timebase();
452 }
4a835a2a
KS
453 newstream.id += start;
454 ism.add_stream(newstream);
6dc69f51 455 }
4a835a2a
KS
456 start += max_id + 1;
457 nstreams += sm.get_num_streams();
6dc69f51 458 }
4a835a2a
KS
459
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 {
15fd30c7 467 let mut decoder = (create_dec)();
4a835a2a 468 let mut dsupp = Box::new(NADecoderSupport::new());
15fd30c7 469 let ret = decoder.init(&mut dsupp, info.clone());
4a835a2a
KS
470 if ret.is_err() {
471 println!("Error initialising decoder '{}' for stream {}", info.get_name(), str_id);
472 return;
473 }
15fd30c7 474 transcoder.apply_decoder_options(decoder.as_mut(), str_id);
4a835a2a
KS
475 let desc = register::get_codec_description(info.get_name());
476 let has_b = if let Some(desc) = desc {
477 desc.has_reorder()
478 } else {
479 println!("No codec description found, using B-frame reorderer.");
480 true
481 };
15fd30c7
KS
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 }));
4a835a2a
KS
484 } else {
485 println!("No decoder for stream {} ({}) is found", str_id, info.get_name());
486 transcoder.decoders.push(None);
487 }
488 }
489 if transcoder.start != NATimePoint::None {
490 let ret = dmx.seek(transcoder.start);
491 if ret.is_err() {
492 println!(" failed to seek to {} error {:?}", transcoder.start, ret.err().unwrap());
493 }
951916e8
KS
494 }
495 }
6dc69f51 496
fd03a232
KS
497 let output_fmt = if let Some(ref fmtname) = transcoder.output_fmt {
498 fmtname
c23d0c3a
KS
499 } else if transcoder.output_name.as_str() == "/dev/null" {
500 "null"
fd03a232
KS
501 } else if let Some(fmtname) = detect::detect_format_by_name(transcoder.output_name.as_str()) {
502 fmtname
6dc69f51 503 } else {
86c54d88
KS
504 println!("Cannot guess muxer for output");
505 return;
6dc69f51 506 };
91a15e39 507 let ret = full_reg.mux_reg.find_muxer(output_fmt);
6dc69f51
KS
508 let ofmt = output_fmt.to_string();
509
510 if ret.is_none() {
511 println!("cannot find muxer '{}'", output_fmt);
becae00f 512 return;
6dc69f51
KS
513 }
514 let mux_creator = ret.unwrap();
515
4e931658
KS
516 if custom_profile && !profile_name.is_empty() {
517 println!("profile setting is incompatible with custom --ostream options");
518 return;
519 }
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);
524 } else {
525 println!("profile '{profile_name}' is not defined for output format '{output_fmt}'");
526 }
527 } else {
528 println!("no profiles for output format '{output_fmt}'");
529 }
530 }
531
4692c27f
KS
532 let mux_quirks = mux_creator.get_quirks();
533 if mux_quirks.is_fixed_duration() {
534 transcoder.calc_len = true;
535 }
69f7e674 536 transcoder.fixed_rate = mux_quirks.is_fixed_rate();
df5974a1 537 transcoder.queue.set_sync(force_sync || !mux_quirks.is_unsync());
4692c27f 538
79eb70eb
KS
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();
545 sids.clear();
546 for stream in sm.iter() {
547 transcoder.nframes.push(0);
548 sids.push(stream.get_id());
549 }
550
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;
555 }
556 }
557 // this is necessary since not all demuxers allow to seek even back to the start
558 demuxers.clear();
559 if !transcoder.create_demuxers(&mut demuxers, &full_reg, false) {
560 println!("failed to re-create demuxer(s)");
561 return;
562 }
563 }
564
6dc69f51
KS
565 let mux_caps = mux_creator.get_capabilities();
566 let mut out_sm = StreamManager::new();
4a835a2a 567 if !transcoder.negotiate_stream_map(&ism, mux_caps, &mut out_sm, &full_reg.enc_reg) {
6dc69f51
KS
568 println!("cannot determine stream map");
569 return;
570 }
571
572 let ret = File::create(transcoder.output_name.as_str());
573 if ret.is_err() {
574 println!("cannot open output file");
575 return;
576 }
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);
582 return;
583 }
584 let mut mux = ret.unwrap();
585 parse_and_apply_options!(mux, &transcoder.mux_opts, "output");
586
587 println!("Output {} muxer {}", transcoder.output_name, ofmt);
588 for ostr in mux.get_streams() {
c8dc1d8a 589 let mut is_copy = false;
81c145fc
KS
590 let stream_id = ostr.get_num() as u32;
591 for omode in transcoder.encoders.iter() {
592 if let OutputMode::Copy(sno) = omode {
c8dc1d8a
KS
593 if sno == &stream_id {
594 is_copy = true;
595 break;
596 }
597 }
598 }
599 println!(" #{}: {} {}{}", ostr.get_num(), ostr, ostr.get_info().get_name(), if is_copy { " (copy)" } else { "" });
6dc69f51
KS
600 }
601
df5974a1
KS
602 transcoder.queue.reserve_streams(mux.get_num_streams());
603
5ec8115f
KS
604 let mut time = Instant::now();
605 let show_interval = Duration::from_millis(100);
908ba48c
KS
606 let mut adata_size = 0;
607 let mut vdata_size = 0;
4a835a2a 608 let mut cur_dmx = 0;
a8a11935 609 let mut last_known_time = None;
951916e8 610 'main_loop: loop {
4a835a2a
KS
611 let mut pktres = Err(DemuxerError::EOF);
612 let mut src_dmx = 0;
613 loop {
614 if !demuxers.iter().any(|(_, eof)| !eof) {
615 break;
616 }
617 let mut got_res = false;
618 if !demuxers[cur_dmx].1 {
619 pktres = demuxers[cur_dmx].0.get_frame();
620 got_res = true;
621 src_dmx = cur_dmx;
622 }
623 cur_dmx += 1;
624 if cur_dmx >= demuxers.len() {
625 cur_dmx = 0;
626 }
627 if got_res {
628 break;
629 }
630 }
631
6dc69f51
KS
632 if let Err(DemuxerError::EOF) = pktres { break; }
633 if pktres.is_err() {
634 println!("demuxing error");
635 break;
636 }
637 let mut pkt = pktres.unwrap();
f88b8fb4 638 if transcoder.start != NATimePoint::None && pkt.ts.less_than(transcoder.start) { continue; }
4a835a2a
KS
639 let src_id = pkt.get_stream().get_num() + is_offset[src_dmx];
640 let ts = pkt.ts;
641 let newstream = ism.get_stream(src_id).unwrap();
642 pkt.reassign(newstream, ts);
643
908ba48c 644 if transcoder.verbose > 0 && time.elapsed() >= show_interval {
5ec8115f
KS
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);
a8a11935
KS
648 last_known_time = Some(cur_time);
649 } else if let Some(ref time) = last_known_time {
650 print!(" {time}");
5ec8115f
KS
651 } else {
652 print!(" ???");
653 }
654 if !duration_string.is_empty() {
655 print!(" / {}", duration_string);
656 }
908ba48c
KS
657 if transcoder.verbose > 1 {
658 print!(" data sizes V: {} A: {}", vdata_size, adata_size);
659 }
5ec8115f
KS
660 print!("\r");
661 std::io::stdout().flush().unwrap();
662 time = Instant::now();
663 }
6dc69f51
KS
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());
951916e8 669 if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'main_loop; }
908ba48c
KS
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; },
674 _ => {},
675 };
df5974a1 676 transcoder.queue.queue_packet(pkt);
6dc69f51 677 },
15fd30c7
KS
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);
f88b8fb4
KS
681 if let (true, Err(DecoderError::MissingReference)) = (transcoder.start != NATimePoint::None, &ret) {
682 continue;
683 }
6dc69f51
KS
684 if ret.is_err() {
685 println!("error decoding stream {}", src_id);
686 break;
687 }
688 let frm = ret.unwrap();
15fd30c7
KS
689 dec_ctx.reorderer.add_frame(frm);
690 while let Some(frm) = dec_ctx.reorderer.get_frame() {
ca3a553a
KS
691 match encoder.encode_frame(dst_id, frm, &transcoder.scale_opts, &mut transcoder.queue) {
692 Ok(true) => {},
693 Ok(false) => break,
694 Err(err) => {
695 println!("encoder error {err:?}");
696 break 'main_loop;
697 }
d9fe2b71 698 }
6dc69f51
KS
699 }
700 } else {
701 println!("no decoder for stream {}", src_id);
702 break;
703 }
704 },
705 };
df5974a1 706
a5c44ebf 707 if !retrieve_packets(&mut transcoder, &mut mux, &mut vdata_size, &mut adata_size, false) {
69f7e674 708 break;
df5974a1 709 }
6dc69f51 710 }
ca3a553a 711 'reord_flush_loop: for stream in ism.iter() {
fd03a232 712 let src_id = stream.get_num();
15fd30c7
KS
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() {
ca3a553a
KS
716 match encoder.encode_frame(dst_id, frm, &transcoder.scale_opts, &mut transcoder.queue) {
717 Ok(true) => {},
718 Ok(false) => break,
719 Err(err) => {
720 println!("encoder error {err:?}");
721 break 'reord_flush_loop;
722 }
d9fe2b71 723 }
d9fe2b71
KS
724 }
725 }
726 }
727 }
df5974a1 728 /*'flush_loop:*/ for enc in transcoder.encoders.iter_mut() {
6dc69f51 729 match enc {
15fd30c7
KS
730 OutputMode::Encode(str_id, ref mut encoder) => {
731 let ret = encoder.flush(&mut transcoder.queue);
6dc69f51
KS
732 if ret.is_err() {
733 println!("error flushing encoder for stream {}", str_id);
734 break;
6dc69f51
KS
735 }
736 },
737 _ => {},
738 };
739 }
df5974a1 740
a5c44ebf 741 retrieve_packets(&mut transcoder, &mut mux, &mut vdata_size, &mut adata_size, true);
df5974a1 742
908ba48c 743 if transcoder.verbose > 0 {
5ec8115f
KS
744 println!();
745 }
6dc69f51
KS
746
747 let ret = mux.end();
748 if ret.is_err() {
749 println!("error at finalising muxing");
750 }
751}
4e931658
KS
752
753struct ProfileDef {
754 name: &'static str,
755 profile: EncodingProfile,
756}
757
758const PROFILES: &[(&str, &[ProfileDef])] = &[
759 ("avi",
760 &[
761 ProfileDef {
762 name: "cinepak",
763 profile: EncodingProfile {
764 vname: "cinepak",
765 voptions: &[],
766 aname: "pcm",
767 aoptions: &[],
768 }
769 },
770 ProfileDef {
771 name: "lossless",
772 profile: EncodingProfile {
773 vname: "zmbv",
774 voptions: &[("range", Some("4")), ("compr_level", Some("fast"))],
775 aname: "pcm",
776 aoptions: &[],
777 }
778 },
779 ProfileDef {
780 name: "ms-lossy",
781 profile: EncodingProfile {
782 vname: "msvideo1",
783 voptions: &[],
784 aname: "ms-adpcm",
785 aoptions: &[],
786 }
787 },
f6c8492c
KS
788 ProfileDef {
789 name: "raw",
790 profile: EncodingProfile {
791 vname: "rawvideo-ms",
792 voptions: &[],
793 aname: "pcm",
794 aoptions: &[],
795 }
796 },
4e931658
KS
797 ]),
798];