]>
Commit | Line | Data |
---|---|---|
6dc69f51 KS |
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; | |
28a83637 | 7 | use std::io::Write; |
6dc69f51 KS |
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::*; | |
d9fe2b71 | 13 | use nihav_core::reorder::*; |
6dc69f51 | 14 | use nihav_registry::detect; |
e6cb09af | 15 | use nihav_registry::register; |
6dc69f51 | 16 | use std::env; |
5ec8115f | 17 | use std::time::{Duration, Instant}; |
6dc69f51 | 18 | |
6dc69f51 KS |
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() { | |
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 |
50 | mod demux; |
51 | use crate::demux::FullRegister; | |
52 | mod null; | |
53 | mod acvt; | |
54 | mod imgseq; | |
55 | mod transcoder; | |
56 | use crate::transcoder::*; | |
6dc69f51 | 57 | |
6dc69f51 | 58 | |
28a83637 KS |
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) | |
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 |
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); | |
79eb70eb | 82 | } |
79eb70eb | 83 | } |
6dc69f51 KS |
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 | ||
4a835a2a KS |
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 | ||
a5c44ebf | 113 | fn 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 |
150 | fn 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 | |
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 | }, | |
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 | ]; |