]>
Commit | Line | Data |
---|---|---|
1 | extern crate nihav_core; | |
2 | extern crate nihav_codec_support; | |
3 | extern crate nihav_registry; | |
4 | extern crate nihav_allstuff; | |
5 | ||
6 | use std::fs::File; | |
7 | use std::io::Write; | |
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; | |
16 | use std::env; | |
17 | use std::time::{Duration, Instant}; | |
18 | ||
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; | |
30 | } | |
31 | if matches { | |
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 { | |
35 | opts.push(val); | |
36 | } else { | |
37 | println!("invalid option {} for {}", opt.name, $name); | |
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 | ||
50 | mod demux; | |
51 | use crate::demux::FullRegister; | |
52 | mod null; | |
53 | mod acvt; | |
54 | mod imgseq; | |
55 | mod transcoder; | |
56 | use crate::transcoder::*; | |
57 | ||
58 | ||
59 | fn 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) | |
67 | } else { | |
68 | format!("{}:{:02}.{}", min, s, ds) | |
69 | } | |
70 | } else { | |
71 | format!("{}:{:02}:{:02}.{}", h, min, s, ds) | |
72 | } | |
73 | } | |
74 | ||
75 | fn 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); | |
82 | } | |
83 | } | |
84 | } | |
85 | ||
86 | macro_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 | ||
95 | macro_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 | ||
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) { | |
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 | } | |
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 | } | |
146 | true | |
147 | } | |
148 | ||
149 | #[allow(clippy::single_match)] | |
150 | fn main() { | |
151 | let args: Vec<_> = env::args().collect(); | |
152 | ||
153 | if args.len() == 1 { | |
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:"); | |
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"); | |
178 | println!(); | |
179 | println!(" (de)muxer and stream options are passed as comma-separated list e.g. --ostream0 width=320,height=240,flip"); | |
180 | return; | |
181 | } | |
182 | ||
183 | let full_reg = FullRegister::new(); | |
184 | ||
185 | let mut transcoder = Transcoder::new(); | |
186 | ||
187 | let mut arg_idx = 1; | |
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); | |
201 | } | |
202 | } else { | |
203 | println!("No registered decoders."); | |
204 | } | |
205 | printed_info = true; | |
206 | }, | |
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); | |
214 | } | |
215 | } else { | |
216 | println!("No registered encoders."); | |
217 | } | |
218 | printed_info = true; | |
219 | }, | |
220 | "--list-demuxers" => { | |
221 | print!("Registered demuxers:"); | |
222 | for dmx in full_reg.dmx_reg.iter() { | |
223 | print!(" {}", dmx.get_name()); | |
224 | } | |
225 | println!(); | |
226 | printed_info = true; | |
227 | }, | |
228 | "--list-muxers" => { | |
229 | print!("Registered muxers:"); | |
230 | for mux in full_reg.mux_reg.iter() { | |
231 | print!(" {}", mux.get_name()); | |
232 | } | |
233 | println!(); | |
234 | printed_info = true; | |
235 | }, | |
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); | |
243 | } else { | |
244 | println!("codec {} is not found", cname); | |
245 | } | |
246 | printed_info = true; | |
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); | |
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); | |
257 | } else { | |
258 | println!("demuxer {} is not found", dname); | |
259 | } | |
260 | printed_info = true; | |
261 | }, | |
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); | |
269 | } else { | |
270 | println!("codec {} is not found", cname); | |
271 | } | |
272 | printed_info = true; | |
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); | |
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); | |
284 | } else { | |
285 | println!("muxer {} is not found", name); | |
286 | } | |
287 | printed_info = true; | |
288 | }, | |
289 | "--output" | "-o" => { | |
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 | }, | |
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 | }, | |
304 | "--no-video" | "-vn" => { | |
305 | transcoder.no_video = true; | |
306 | }, | |
307 | "--no-audio" | "-an" => { | |
308 | transcoder.no_audio = true; | |
309 | }, | |
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 { | |
326 | println!("invalid end time"); | |
327 | return; | |
328 | } | |
329 | }, | |
330 | "--sync" => { | |
331 | force_sync = true; | |
332 | }, | |
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"); | |
337 | return; | |
338 | } | |
339 | }, | |
340 | "--calc-len" => { | |
341 | transcoder.calc_len = true; | |
342 | }, | |
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 | }, | |
358 | "--verbose" | "-v" => transcoder.verbose = 1, | |
359 | "-vv" => transcoder.verbose = 2, | |
360 | "-v-" => transcoder.verbose = 0, | |
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); | |
372 | if !transcoder.parse_ostream_options(opt0, &args[arg_idx], &full_reg.enc_reg) { | |
373 | println!("invalid output stream option syntax"); | |
374 | return; | |
375 | } | |
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"); | |
398 | return; | |
399 | } | |
400 | } else if args[arg_idx].starts_with("--") { | |
401 | println!("unknown option '{}'", args[arg_idx]); | |
402 | } else { | |
403 | println!("unrecognized argument '{}'", args[arg_idx]); | |
404 | } | |
405 | }, | |
406 | }; | |
407 | arg_idx += 1; | |
408 | } | |
409 | ||
410 | if printed_info { | |
411 | return; | |
412 | } | |
413 | ||
414 | if transcoder.input_name.iter().flatten().count() == 0 { | |
415 | println!("no input name(s) provided"); | |
416 | return; | |
417 | } | |
418 | if transcoder.output_name.is_empty() { | |
419 | println!("no output name provided"); | |
420 | return; | |
421 | } | |
422 | ||
423 | let mut demuxers = Vec::with_capacity(1); | |
424 | if !transcoder.create_demuxers(&mut demuxers, &full_reg, true) { | |
425 | return; | |
426 | } | |
427 | ||
428 | let duration = demuxers.iter().fold(0u64, |mindur, (dmx, _)| { | |
429 | let dur = dmx.get_duration(); | |
430 | if mindur == 0 { | |
431 | dur | |
432 | } else if dur > 0 { | |
433 | mindur.min(dur) | |
434 | } else { | |
435 | mindur | |
436 | } | |
437 | }); | |
438 | let duration_string = if duration != 0 { format_time(duration) } else { String::new() }; | |
439 | ||
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(); | |
450 | if transcoder.global_tb == (0, 0) && newstream.get_media_type() == StreamType::Video { | |
451 | transcoder.global_tb = newstream.get_timebase(); | |
452 | } | |
453 | newstream.id += start; | |
454 | ism.add_stream(newstream); | |
455 | } | |
456 | start += max_id + 1; | |
457 | nstreams += sm.get_num_streams(); | |
458 | } | |
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 { | |
467 | let mut decoder = (create_dec)(); | |
468 | let mut dsupp = Box::new(NADecoderSupport::new()); | |
469 | let ret = decoder.init(&mut dsupp, info.clone()); | |
470 | if ret.is_err() { | |
471 | println!("Error initialising decoder '{}' for stream {}", info.get_name(), str_id); | |
472 | return; | |
473 | } | |
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 { | |
477 | desc.has_reorder() | |
478 | } else { | |
479 | println!("No codec description found, using B-frame reorderer."); | |
480 | true | |
481 | }; | |
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 })); | |
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 | } | |
494 | } | |
495 | } | |
496 | ||
497 | let output_fmt = if let Some(ref fmtname) = transcoder.output_fmt { | |
498 | fmtname | |
499 | } else if transcoder.output_name.as_str() == "/dev/null" { | |
500 | "null" | |
501 | } else if let Some(fmtname) = detect::detect_format_by_name(transcoder.output_name.as_str()) { | |
502 | fmtname | |
503 | } else { | |
504 | println!("Cannot guess muxer for output"); | |
505 | return; | |
506 | }; | |
507 | let ret = full_reg.mux_reg.find_muxer(output_fmt); | |
508 | let ofmt = output_fmt.to_string(); | |
509 | ||
510 | if ret.is_none() { | |
511 | println!("cannot find muxer '{}'", output_fmt); | |
512 | return; | |
513 | } | |
514 | let mux_creator = ret.unwrap(); | |
515 | ||
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 | ||
532 | let mux_quirks = mux_creator.get_quirks(); | |
533 | if mux_quirks.is_fixed_duration() { | |
534 | transcoder.calc_len = true; | |
535 | } | |
536 | transcoder.fixed_rate = mux_quirks.is_fixed_rate(); | |
537 | transcoder.queue.set_sync(force_sync || !mux_quirks.is_unsync()); | |
538 | ||
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 | ||
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"); | |
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() { | |
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 { | |
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 { "" }); | |
600 | } | |
601 | ||
602 | transcoder.queue.reserve_streams(mux.get_num_streams()); | |
603 | ||
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; | |
608 | let mut cur_dmx = 0; | |
609 | let mut last_known_time = None; | |
610 | 'main_loop: loop { | |
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 | ||
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(); | |
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]; | |
640 | let ts = pkt.ts; | |
641 | let newstream = ism.get_stream(src_id).unwrap(); | |
642 | pkt.reassign(newstream, ts); | |
643 | ||
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 { | |
650 | print!(" {time}"); | |
651 | } else { | |
652 | print!(" ???"); | |
653 | } | |
654 | if !duration_string.is_empty() { | |
655 | print!(" / {}", duration_string); | |
656 | } | |
657 | if transcoder.verbose > 1 { | |
658 | print!(" data sizes V: {} A: {}", vdata_size, adata_size); | |
659 | } | |
660 | print!("\r"); | |
661 | std::io::stdout().flush().unwrap(); | |
662 | time = Instant::now(); | |
663 | } | |
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; }, | |
674 | _ => {}, | |
675 | }; | |
676 | transcoder.queue.queue_packet(pkt); | |
677 | }, | |
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) { | |
682 | continue; | |
683 | } | |
684 | if ret.is_err() { | |
685 | println!("error decoding stream {}", src_id); | |
686 | break; | |
687 | } | |
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) { | |
692 | Ok(true) => {}, | |
693 | Ok(false) => break, | |
694 | Err(err) => { | |
695 | println!("encoder error {err:?}"); | |
696 | break 'main_loop; | |
697 | } | |
698 | } | |
699 | } | |
700 | } else { | |
701 | println!("no decoder for stream {}", src_id); | |
702 | break; | |
703 | } | |
704 | }, | |
705 | }; | |
706 | ||
707 | if !retrieve_packets(&mut transcoder, &mut mux, &mut vdata_size, &mut adata_size, false) { | |
708 | break; | |
709 | } | |
710 | } | |
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) { | |
717 | Ok(true) => {}, | |
718 | Ok(false) => break, | |
719 | Err(err) => { | |
720 | println!("encoder error {err:?}"); | |
721 | break 'reord_flush_loop; | |
722 | } | |
723 | } | |
724 | } | |
725 | } | |
726 | } | |
727 | } | |
728 | /*'flush_loop:*/ for enc in transcoder.encoders.iter_mut() { | |
729 | match enc { | |
730 | OutputMode::Encode(str_id, ref mut encoder) => { | |
731 | let ret = encoder.flush(&mut transcoder.queue); | |
732 | if ret.is_err() { | |
733 | println!("error flushing encoder for stream {}", str_id); | |
734 | break; | |
735 | } | |
736 | }, | |
737 | _ => {}, | |
738 | }; | |
739 | } | |
740 | ||
741 | retrieve_packets(&mut transcoder, &mut mux, &mut vdata_size, &mut adata_size, true); | |
742 | ||
743 | if transcoder.verbose > 0 { | |
744 | println!(); | |
745 | } | |
746 | ||
747 | let ret = mux.end(); | |
748 | if ret.is_err() { | |
749 | println!("error at finalising muxing"); | |
750 | } | |
751 | } | |
752 | ||
753 | struct ProfileDef { | |
754 | name: &'static str, | |
755 | profile: EncodingProfile, | |
756 | } | |
757 | ||
758 | const 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 | }, | |
788 | ProfileDef { | |
789 | name: "raw", | |
790 | profile: EncodingProfile { | |
791 | vname: "rawvideo-ms", | |
792 | voptions: &[], | |
793 | aname: "pcm", | |
794 | aoptions: &[], | |
795 | } | |
796 | }, | |
797 | ]), | |
798 | ]; |