]>
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; | |
5ec8115f | 7 | use std::io::{BufReader, Write}; |
6dc69f51 KS |
8 | use nihav_core::io::byteio::{FileReader, ByteReader}; |
9 | use nihav_core::frame::*; | |
10 | use nihav_core::options::*; | |
11 | use nihav_core::codecs::*; | |
12 | use nihav_core::demuxers::*; | |
13 | use nihav_core::muxers::*; | |
d9fe2b71 | 14 | use nihav_core::reorder::*; |
6dc69f51 KS |
15 | use nihav_core::scale::*; |
16 | use nihav_core::soundcvt::*; | |
17 | use nihav_registry::detect; | |
e6cb09af | 18 | use nihav_registry::register; |
6dc69f51 | 19 | use std::env; |
5ec8115f | 20 | use std::time::{Duration, Instant}; |
6dc69f51 | 21 | |
91a15e39 KS |
22 | mod demux; |
23 | use crate::demux::*; | |
93521506 | 24 | mod null; |
93521506 | 25 | |
5ec8115f KS |
26 | fn format_time(ms: u64) -> String { |
27 | let s = ms / 1000; | |
28 | let ds = (ms % 1000) / 100; | |
29 | let (min, s) = (s / 60, s % 60); | |
30 | let (h, min) = (min / 60, min % 60); | |
31 | if h == 0 { | |
32 | if min == 0 { | |
33 | format!("{}.{}", s, ds) | |
34 | } else { | |
35 | format!("{}:{:02}.{}", min, s, ds) | |
36 | } | |
37 | } else { | |
38 | format!("{}:{:02}:{:02}.{}", h, min, s, ds) | |
39 | } | |
40 | } | |
41 | ||
6dc69f51 KS |
42 | fn print_options(name: &str, options: &[NAOptionDefinition]) { |
43 | if options.is_empty() { | |
44 | println!("No custom options."); | |
45 | } else { | |
46 | println!("Options for '{}'", name); | |
47 | for opt in options.iter() { | |
48 | println!(" {}", opt); | |
49 | } | |
50 | } | |
51 | } | |
52 | ||
53 | struct OptionArgs { | |
54 | name: String, | |
55 | value: Option<String>, | |
56 | } | |
57 | ||
58 | struct InputStreamOptions { | |
59 | id: u32, | |
60 | drop: bool, | |
61 | dec_opts: Vec<OptionArgs>, | |
62 | } | |
63 | ||
64 | struct OutputStreamOptions { | |
65 | id: u32, | |
66 | enc_params: EncodeParameters, | |
67 | enc_name: String, | |
68 | enc_opts: Vec<OptionArgs>, | |
69 | } | |
70 | ||
71 | enum OutputConvert { | |
72 | Video(NAScale, NABufferType), | |
73 | Audio(NAAudioInfo, NAChannelMap), | |
74 | None, | |
75 | } | |
76 | ||
86c54d88 | 77 | #[allow(clippy::large_enum_variant)] |
6dc69f51 KS |
78 | enum OutputMode { |
79 | Drop, | |
80 | Copy(u32), | |
81 | Encode(u32, Box<dyn NAEncoder>, OutputConvert), | |
82 | } | |
83 | ||
84 | #[derive(Default)] | |
86c54d88 | 85 | #[allow(clippy::type_complexity)] |
6dc69f51 KS |
86 | struct Transcoder { |
87 | input_name: String, | |
88 | input_fmt: Option<String>, | |
89 | output_name: String, | |
90 | output_fmt: Option<String>, | |
91 | demux_opts: Vec<OptionArgs>, | |
92 | mux_opts: Vec<OptionArgs>, | |
93 | istr_opts: Vec<InputStreamOptions>, | |
94 | ostr_opts: Vec<OutputStreamOptions>, | |
df37d3b1 | 95 | scale_opts: Vec<(String, String)>, |
d9fe2b71 | 96 | decoders: Vec<Option<(Box<NADecoderSupport>, Box<dyn NADecoder>, Box<dyn FrameReorderer>)>>, |
6dc69f51 KS |
97 | encoders: Vec<OutputMode>, |
98 | no_video: bool, | |
99 | no_audio: bool, | |
951916e8 KS |
100 | start: NATimePoint, |
101 | end: NATimePoint, | |
5ec8115f | 102 | verbose: bool, |
6dc69f51 KS |
103 | } |
104 | ||
105 | macro_rules! parse_and_apply_options { | |
106 | ($obj: expr, $in_opts: expr, $name: expr) => { | |
107 | let mut opts = Vec::with_capacity($in_opts.len()); | |
108 | let opt_def = $obj.get_supported_options(); | |
109 | for opt in $in_opts.iter() { | |
110 | let mut found = false; | |
111 | for opt_def in opt_def.iter() { | |
3e2a4e88 KS |
112 | let mut matches = opt.name == opt_def.name; |
113 | if !matches && opt.name.starts_with("no") { | |
114 | let (_, name) = opt.name.split_at(2); | |
115 | matches = name == opt_def.name; | |
116 | } | |
117 | if matches { | |
6dc69f51 KS |
118 | let arg = if let Some(ref str) = opt.value { Some(str) } else { None }; |
119 | let ret = opt_def.parse(&opt.name, arg); | |
86c54d88 | 120 | if let Ok((val, _)) = ret { |
6dc69f51 | 121 | opts.push(val); |
86c54d88 KS |
122 | } else { |
123 | println!("invalid option {} for {}", opt.name, $name); | |
6dc69f51 KS |
124 | } |
125 | found = true; | |
126 | } | |
127 | } | |
128 | if !found { | |
129 | println!(" ignoring option '{}' for {}", opt.name, $name); | |
130 | } | |
131 | } | |
132 | $obj.set_options(opts.as_slice()); | |
133 | } | |
134 | } | |
135 | ||
136 | impl Transcoder { | |
137 | fn new() -> Self { Self::default() } | |
138 | fn parse_istream_options(&mut self, opt0: &str, opt1: &str) -> bool { | |
139 | let (_, strno) = opt0.split_at(9); | |
140 | let ret = strno.parse::<u32>(); | |
141 | if ret.is_err() { return false; } | |
142 | let streamno = ret.unwrap(); | |
143 | ||
144 | let sidx = if let Some(idx) = self.istr_opts.iter().position(|str| str.id == streamno) { | |
145 | idx | |
146 | } else { | |
147 | self.istr_opts.push(InputStreamOptions {id: streamno, drop: false, dec_opts: Vec::new() }); | |
148 | self.istr_opts.len() - 1 | |
149 | }; | |
150 | let istr = &mut self.istr_opts[sidx]; | |
830c03a1 | 151 | |
6dc69f51 KS |
152 | for opt in opt1.split(',') { |
153 | let oval: Vec<_> = opt.split('=').collect(); | |
154 | if oval.len() == 1 { | |
155 | match oval[0] { | |
156 | "drop" => { istr.drop = true; }, | |
157 | _ => { | |
158 | istr.dec_opts.push(OptionArgs{ name: oval[0].to_string(), value: None }); | |
159 | }, | |
160 | }; | |
161 | } else if oval.len() == 2 { | |
162 | istr.dec_opts.push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); | |
163 | } else { | |
164 | println!("unrecognized option '{}'", opt); | |
165 | } | |
166 | } | |
167 | true | |
168 | } | |
169 | fn parse_ostream_options(&mut self, opt0: &str, opt1: &str, enc_reg: &RegisteredEncoders) -> bool { | |
170 | let (_, strno) = opt0.split_at(9); | |
171 | let ret = strno.parse::<u32>(); | |
172 | if ret.is_err() { return false; } | |
173 | let streamno = ret.unwrap(); | |
174 | ||
175 | let sidx = if let Some(idx) = self.ostr_opts.iter().position(|str| str.id == streamno) { | |
176 | idx | |
177 | } else { | |
178 | self.ostr_opts.push(OutputStreamOptions {id: streamno, enc_name: String::new(), enc_params: EncodeParameters::default(), enc_opts: Vec::new() }); | |
179 | self.ostr_opts.len() - 1 | |
180 | }; | |
181 | let ostr = &mut self.ostr_opts[sidx]; | |
830c03a1 | 182 | |
6dc69f51 KS |
183 | for opt in opt1.split(',') { |
184 | let oval: Vec<_> = opt.split('=').collect(); | |
185 | if oval.len() == 1 { | |
186 | match oval[0] { | |
187 | "flip" => { | |
188 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
189 | ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); | |
190 | } | |
191 | if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { | |
192 | vinfo.flipped = true; | |
193 | } else { | |
194 | println!("video option for audio stream"); | |
195 | } | |
196 | }, | |
197 | "noflip" => { | |
198 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
199 | ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); | |
200 | } | |
201 | if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { | |
202 | vinfo.flipped = false; | |
203 | } else { | |
204 | println!("video option for audio stream"); | |
205 | } | |
206 | }, | |
207 | _ => { | |
208 | ostr.enc_opts.push(OptionArgs{ name: oval[0].to_string(), value: None }); | |
209 | }, | |
210 | }; | |
211 | } else if oval.len() == 2 { | |
212 | //todo parse encoder options, store, init later | |
213 | match oval[0] { | |
ca3b31d7 KS |
214 | "timebase" => { |
215 | let mut parts = oval[1].split('/'); | |
216 | let num = parts.next().unwrap(); | |
217 | let den = parts.next(); | |
218 | if let Some(den) = den { | |
219 | let rnum = num.parse::<u32>(); | |
220 | let rden = den.parse::<u32>(); | |
221 | if let (Ok(num), Ok(den)) = (rnum, rden) { | |
222 | ostr.enc_params.tb_num = num; | |
223 | ostr.enc_params.tb_den = den; | |
224 | } else { | |
225 | println!("invalid timebase value"); | |
226 | } | |
227 | } else { | |
228 | println!("invalid timebase format (should be num/den)"); | |
229 | } | |
230 | }, | |
6dc69f51 KS |
231 | "encoder" => { |
232 | if enc_reg.find_encoder(oval[1]).is_some() { | |
233 | ostr.enc_name = oval[1].to_string(); | |
234 | } else { | |
235 | println!("unknown encoder '{}'", oval[1]); | |
c0052668 | 236 | ostr.enc_name = oval[1].to_string(); |
6dc69f51 KS |
237 | } |
238 | }, | |
239 | "width" => { | |
240 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
241 | ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); | |
242 | } | |
243 | if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { | |
244 | let ret = oval[1].parse::<usize>(); | |
245 | if let Ok(val) = ret { | |
246 | vinfo.width = val; | |
247 | } else { | |
248 | println!("invalid width"); | |
249 | } | |
250 | } else { | |
251 | println!("video option for audio stream"); | |
252 | } | |
253 | }, | |
254 | "height" => { | |
255 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
256 | ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); | |
257 | } | |
258 | if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { | |
259 | let ret = oval[1].parse::<usize>(); | |
260 | if let Ok(val) = ret { | |
261 | vinfo.height = val; | |
262 | } else { | |
263 | println!("invalid height"); | |
264 | } | |
265 | } else { | |
266 | println!("video option for audio stream"); | |
267 | } | |
268 | }, | |
e176bfee KS |
269 | "pixfmt" => { |
270 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
271 | ostr.enc_params.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)); | |
272 | } | |
273 | if let NACodecTypeInfo::Video(ref mut vinfo) = ostr.enc_params.format { | |
274 | let ret = oval[1].parse::<NAPixelFormaton>(); | |
275 | if let Ok(val) = ret { | |
276 | vinfo.format = val; | |
277 | } else { | |
278 | println!("invalid pixel format"); | |
279 | } | |
280 | } else { | |
281 | println!("video option for audio stream"); | |
282 | } | |
283 | }, | |
6dc69f51 KS |
284 | "srate" => { |
285 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
286 | ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0)); | |
287 | } | |
288 | if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { | |
289 | let ret = oval[1].parse::<u32>(); | |
290 | if let Ok(val) = ret { | |
291 | ainfo.sample_rate = val; | |
292 | } else { | |
293 | println!("invalid sampling rate"); | |
294 | } | |
295 | } else { | |
296 | println!("audio option for video stream"); | |
297 | } | |
298 | }, | |
299 | "channels" => { | |
300 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
301 | ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0)); | |
302 | } | |
303 | if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { | |
304 | let ret = oval[1].parse::<u8>(); | |
305 | if let Ok(val) = ret { | |
306 | ainfo.channels = val; | |
307 | } else { | |
308 | println!("invalid number of channels"); | |
309 | } | |
310 | } else { | |
311 | println!("audio option for video stream"); | |
312 | } | |
313 | }, | |
314 | "block_len" => { | |
315 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
316 | ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0)); | |
317 | } | |
318 | if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { | |
319 | let ret = oval[1].parse::<usize>(); | |
320 | if let Ok(val) = ret { | |
321 | ainfo.block_len = val; | |
322 | } else { | |
323 | println!("invalid block_length"); | |
324 | } | |
325 | } else { | |
326 | println!("audio option for video stream"); | |
327 | } | |
328 | }, | |
e176bfee KS |
329 | "sfmt" => { |
330 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
331 | ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0)); | |
332 | } | |
333 | if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { | |
334 | let ret = oval[1].parse::<NASoniton>(); | |
335 | if let Ok(val) = ret { | |
336 | ainfo.format = val; | |
337 | } else { | |
338 | println!("invalid audio format"); | |
339 | } | |
340 | } else { | |
341 | println!("audio option for video stream"); | |
342 | } | |
343 | }, | |
344 | // todo channel map negotiation | |
345 | /*"chmap" => { | |
346 | if ostr.enc_params.format == NACodecTypeInfo::None { | |
347 | ostr.enc_params.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 0, SND_S16_FORMAT, 0)); | |
348 | } | |
349 | if let NACodecTypeInfo::Audio(ref mut ainfo) = ostr.enc_params.format { | |
350 | let ret = oval[1].parse::<NAChannelMap>(); | |
351 | if let Ok(val) = ret { | |
352 | ainfo.chmap = val; | |
353 | } else { | |
354 | println!("invalid channel map"); | |
355 | } | |
356 | } else { | |
357 | println!("audio option for video stream"); | |
358 | } | |
359 | },*/ | |
6dc69f51 KS |
360 | "bitrate" => { |
361 | let ret = oval[1].parse::<u32>(); | |
362 | if let Ok(val) = ret { | |
363 | ostr.enc_params.bitrate = val; | |
364 | } else { | |
365 | println!("invalid bitrate value"); | |
366 | } | |
367 | }, | |
368 | "quality" => { | |
369 | let ret = oval[1].parse::<u8>(); | |
370 | if let Ok(val) = ret { | |
371 | ostr.enc_params.quality = val; | |
372 | } else { | |
373 | println!("invalid quality value"); | |
374 | } | |
375 | }, | |
376 | _ => { | |
377 | ostr.enc_opts.push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); | |
378 | }, | |
379 | } | |
380 | } else { | |
381 | println!("unrecognized option '{}'", opt); | |
382 | } | |
383 | } | |
384 | true | |
385 | } | |
386 | fn parse_demuxer_options(&mut self, opts: &str, dmx_reg: &RegisteredDemuxers) -> bool { | |
387 | for opt in opts.split(',') { | |
388 | let oval: Vec<_> = opt.split('=').collect(); | |
389 | if oval.len() == 1 { | |
390 | self.demux_opts.push(OptionArgs{ name: oval[0].to_string(), value: None }); | |
391 | } else if oval.len() == 2 { | |
392 | if oval[0] == "format" { | |
393 | if dmx_reg.find_demuxer(oval[1]).is_some() { | |
394 | self.input_fmt = Some(oval[1].to_string()); | |
395 | } else { | |
396 | println!("unknown demuxer format '{}'", oval[1]); | |
397 | } | |
398 | } else { | |
399 | self.demux_opts.push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); | |
400 | } | |
401 | } else { | |
402 | println!("unrecognized option '{}'", opt); | |
403 | } | |
404 | } | |
405 | true | |
406 | } | |
407 | fn parse_muxer_options(&mut self, opts: &str, mux_reg: &RegisteredMuxers) -> bool { | |
408 | for opt in opts.split(',') { | |
409 | let oval: Vec<_> = opt.split('=').collect(); | |
410 | if oval.len() == 1 { | |
411 | self.mux_opts.push(OptionArgs{ name: oval[0].to_string(), value: None }); | |
412 | } else if oval.len() == 2 { | |
413 | if oval[0] == "format" { | |
414 | if mux_reg.find_muxer(oval[1]).is_some() { | |
415 | self.output_fmt = Some(oval[1].to_string()); | |
416 | } else { | |
417 | println!("unknown muxer format '{}'", oval[1]); | |
418 | } | |
419 | } else { | |
420 | self.mux_opts.push(OptionArgs{ name: oval[0].to_string(), value: Some(oval[1].to_string()) }); | |
421 | } | |
422 | } else { | |
423 | println!("unrecognized option '{}'", opt); | |
424 | } | |
425 | } | |
426 | true | |
427 | } | |
df37d3b1 KS |
428 | fn parse_scale_options(&mut self, opts: &str) -> bool { |
429 | for opt in opts.split(',') { | |
430 | let oval: Vec<_> = opt.split('=').collect(); | |
431 | if oval.len() == 1 { | |
432 | self.scale_opts.push((oval[0].to_string(), "".to_string())); | |
433 | } else if oval.len() == 2 { | |
434 | self.scale_opts.push((oval[0].to_string(), oval[1].to_string())); | |
435 | } else { | |
436 | println!("unrecognized option '{}'", opt); | |
437 | return false; | |
438 | } | |
439 | } | |
440 | true | |
441 | } | |
73f889bf | 442 | fn apply_decoder_options(&self, dec: &mut dyn NADecoder, str_id: u32) { |
6dc69f51 KS |
443 | if let Some(str_idx) = self.istr_opts.iter().position(|str| str.id == str_id) { |
444 | let dec_opts = dec.get_supported_options(); | |
445 | if dec_opts.is_empty() { return; } | |
446 | let name = format!("input stream {}", str_id); | |
447 | parse_and_apply_options!(dec, &self.istr_opts[str_idx].dec_opts, name); | |
448 | } | |
449 | } | |
450 | fn register_output_stream(&mut self, cname: &str, istr: NAStreamRef, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { | |
451 | let out_id = out_sm.get_num_streams() as u32; | |
452 | if let Some(str_idx) = self.istr_opts.iter().position(|str| str.id == (istr.get_num() as u32)) { | |
453 | if self.istr_opts[str_idx].drop { | |
454 | self.encoders.push(OutputMode::Drop); | |
455 | return true; | |
456 | } | |
457 | } | |
458 | ||
459 | if let Some(str_idx) = self.ostr_opts.iter().position(|str| str.id == out_id) { | |
460 | let oopts = &mut self.ostr_opts[str_idx]; | |
a7b5f008 | 461 | if oopts.enc_name.as_str() == "copy" && (cname == "any" || istr.get_info().get_name() == cname) { |
86c54d88 | 462 | out_sm.add_stream_ref(istr); |
6dc69f51 KS |
463 | self.encoders.push(OutputMode::Copy(out_id)); |
464 | } else if cname == "any" || oopts.enc_name.as_str() == cname { | |
465 | let enc_create = enc_reg.find_encoder(oopts.enc_name.as_str()); | |
466 | if enc_create.is_none() { | |
467 | println!("encoder '{}' not found", oopts.enc_name.as_str()); | |
468 | return false; | |
469 | } | |
470 | let mut encoder = (enc_create.unwrap())(); | |
eb563e3d | 471 | let forced_out = oopts.enc_params.format != NACodecTypeInfo::None; |
6dc69f51 KS |
472 | if oopts.enc_params.format == NACodecTypeInfo::None { |
473 | oopts.enc_params.format = istr.get_info().get_properties(); | |
474 | } | |
475 | if oopts.enc_params.tb_num == 0 { | |
476 | oopts.enc_params.tb_num = istr.tb_num; | |
477 | oopts.enc_params.tb_den = istr.tb_den; | |
478 | } | |
479 | let ret_eparams = encoder.negotiate_format(&oopts.enc_params); | |
480 | if ret_eparams.is_err() { | |
481 | println!("cannot negotiate encoding parameters"); | |
482 | return false; | |
483 | } | |
484 | let ret_eparams = ret_eparams.unwrap(); | |
485 | ||
486 | //todo check for params mismatch | |
487 | let cvt = match (&oopts.enc_params.format, &ret_eparams.format) { | |
488 | (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => { | |
eb563e3d | 489 | if svinfo == dvinfo && !forced_out { |
6dc69f51 KS |
490 | OutputConvert::None |
491 | } else { | |
492 | let ofmt = ScaleInfo { fmt: dvinfo.format, width: dvinfo.width, height: dvinfo.height }; | |
df37d3b1 | 493 | let ret = NAScale::new_with_options(ofmt, ofmt, &self.scale_opts); |
6dc69f51 KS |
494 | if ret.is_err() { |
495 | println!("cannot create scaler"); | |
496 | return false; | |
497 | } | |
498 | let scaler = ret.unwrap(); | |
499 | let ret = alloc_video_buffer(*dvinfo, 4); | |
500 | if ret.is_err() { | |
501 | println!("cannot create scaler buffer"); | |
502 | return false; | |
503 | } | |
504 | let cvt_buf = ret.unwrap(); | |
505 | OutputConvert::Video(scaler, cvt_buf) | |
506 | } | |
507 | }, | |
508 | (NACodecTypeInfo::Audio(sainfo), NACodecTypeInfo::Audio(dainfo)) => { | |
509 | if sainfo == dainfo { | |
510 | OutputConvert::None | |
511 | } else { | |
512 | let dchmap = match dainfo.channels { | |
513 | 1 => NAChannelMap::from_ms_mapping(0x4), | |
514 | 2 => NAChannelMap::from_ms_mapping(0x3), | |
515 | _ => { | |
516 | println!("can't generate default channel map for {} channels", dainfo.channels); | |
517 | return false; | |
518 | }, | |
519 | }; | |
520 | //todo channelmap | |
521 | OutputConvert::Audio(*dainfo, dchmap) | |
522 | } | |
523 | }, | |
524 | _ => OutputConvert::None, | |
525 | }; | |
526 | let ret = encoder.init(out_id, ret_eparams); | |
527 | if ret.is_err() { | |
528 | println!("error initialising encoder"); | |
529 | return false; | |
530 | } | |
531 | out_sm.add_stream_ref(ret.unwrap()); | |
532 | ||
533 | let name = format!("output stream {}", out_id); | |
534 | parse_and_apply_options!(encoder, &oopts.enc_opts, name); | |
535 | ||
536 | self.encoders.push(OutputMode::Encode(out_id, encoder, cvt)); | |
537 | } else { | |
538 | println!("encoder {} is not supported by output (expected {})", istr.id, istr.get_info().get_name()); | |
539 | return false; | |
540 | } | |
86c54d88 KS |
541 | } else if cname == "any" || istr.get_info().get_name() == cname { |
542 | out_sm.add_stream_ref(istr); | |
543 | self.encoders.push(OutputMode::Copy(out_id)); | |
6dc69f51 | 544 | } else { |
00b4c46f KS |
545 | let mut oopts = OutputStreamOptions {id: out_id, enc_name: cname.to_owned(), enc_params: EncodeParameters::default(), enc_opts: Vec::new() }; |
546 | ||
547 | let enc_create = enc_reg.find_encoder(cname); | |
548 | if enc_create.is_none() { | |
549 | println!("encoder '{}' not found", oopts.enc_name.as_str()); | |
550 | return false; | |
551 | } | |
552 | let mut encoder = (enc_create.unwrap())(); | |
553 | oopts.enc_params.format = istr.get_info().get_properties(); | |
554 | oopts.enc_params.tb_num = istr.tb_num; | |
555 | oopts.enc_params.tb_den = istr.tb_den; | |
556 | let ret_eparams = encoder.negotiate_format(&oopts.enc_params); | |
557 | if ret_eparams.is_err() { | |
558 | println!("cannot negotiate encoding parameters"); | |
559 | return false; | |
560 | } | |
561 | let ret_eparams = ret_eparams.unwrap(); | |
562 | ||
563 | //todo check for params mismatch | |
564 | let cvt = match (&oopts.enc_params.format, &ret_eparams.format) { | |
565 | (NACodecTypeInfo::Video(svinfo), NACodecTypeInfo::Video(dvinfo)) => { | |
566 | if svinfo == dvinfo { | |
567 | OutputConvert::None | |
568 | } else { | |
569 | let ofmt = ScaleInfo { fmt: dvinfo.format, width: dvinfo.width, height: dvinfo.height }; | |
570 | let ret = NAScale::new_with_options(ofmt, ofmt, &self.scale_opts); | |
571 | if ret.is_err() { | |
572 | println!("cannot create scaler"); | |
573 | return false; | |
574 | } | |
575 | let scaler = ret.unwrap(); | |
576 | let ret = alloc_video_buffer(*dvinfo, 4); | |
577 | if ret.is_err() { | |
578 | println!("cannot create scaler buffer"); | |
579 | return false; | |
580 | } | |
581 | let cvt_buf = ret.unwrap(); | |
582 | OutputConvert::Video(scaler, cvt_buf) | |
583 | } | |
584 | }, | |
585 | (NACodecTypeInfo::Audio(sainfo), NACodecTypeInfo::Audio(dainfo)) => { | |
586 | if sainfo == dainfo { | |
587 | OutputConvert::None | |
588 | } else { | |
589 | let dchmap = match dainfo.channels { | |
590 | 1 => NAChannelMap::from_ms_mapping(0x4), | |
591 | 2 => NAChannelMap::from_ms_mapping(0x3), | |
592 | _ => { | |
593 | println!("can't generate default channel map for {} channels", dainfo.channels); | |
594 | return false; | |
595 | }, | |
596 | }; | |
597 | //todo channelmap | |
598 | OutputConvert::Audio(*dainfo, dchmap) | |
599 | } | |
600 | }, | |
601 | _ => OutputConvert::None, | |
602 | }; | |
603 | let ret = encoder.init(out_id, ret_eparams); | |
604 | if ret.is_err() { | |
605 | println!("error initialising encoder"); | |
606 | return false; | |
607 | } | |
608 | out_sm.add_stream_ref(ret.unwrap()); | |
609 | self.encoders.push(OutputMode::Encode(out_id, encoder, cvt)); | |
610 | self.ostr_opts.push(oopts); | |
6dc69f51 KS |
611 | } |
612 | true | |
613 | } | |
614 | fn map_single(&mut self, cname: &str, ctype: StreamType, src_sm: &StreamManager, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { | |
615 | let mut found_stream = false; | |
616 | for istr in src_sm.iter() { | |
617 | if istr.get_media_type() != ctype || found_stream { | |
618 | self.encoders.push(OutputMode::Drop); | |
619 | } else { | |
620 | if !self.register_output_stream(cname, istr, out_sm, enc_reg) { | |
621 | return false; | |
622 | } | |
623 | found_stream = true; | |
624 | } | |
625 | } | |
626 | found_stream | |
627 | } | |
628 | fn negotiate_stream_map(&mut self, src_sm: &StreamManager, mux_caps: MuxerCapabilities, out_sm: &mut StreamManager, enc_reg: &RegisteredEncoders) -> bool { | |
629 | match mux_caps { | |
630 | MuxerCapabilities::SingleVideo(cname) => { | |
631 | if self.no_video { return false; } | |
632 | self.map_single(cname, StreamType::Video, src_sm, out_sm, enc_reg) | |
633 | }, | |
634 | MuxerCapabilities::SingleAudio(cname) => { | |
635 | if self.no_audio { return false; } | |
636 | self.map_single(cname, StreamType::Audio, src_sm, out_sm, enc_reg) | |
637 | }, | |
638 | MuxerCapabilities::SingleVideoAndAudio(vname, aname) => { | |
639 | let mut found_vid = false; | |
640 | let mut found_aud = false; | |
641 | for istr in src_sm.iter() { | |
642 | if istr.get_media_type() == StreamType::Video && !found_vid && !self.no_video { | |
643 | if !self.register_output_stream(vname, istr, out_sm, enc_reg) { | |
644 | return false; | |
645 | } | |
646 | found_vid = true; | |
647 | } else if istr.get_media_type() == StreamType::Audio && !found_aud && !self.no_audio { | |
648 | if !self.register_output_stream(aname, istr, out_sm, enc_reg) { | |
649 | return false; | |
650 | } | |
651 | found_aud = true; | |
652 | } else { | |
653 | self.encoders.push(OutputMode::Drop); | |
654 | } | |
655 | } | |
656 | found_vid | found_aud | |
657 | }, | |
658 | MuxerCapabilities::OnlyVideo => { | |
659 | if self.no_video { return false; } | |
660 | ||
661 | let mut found_vid = false; | |
662 | for istr in src_sm.iter() { | |
663 | if istr.get_media_type() == StreamType::Video && !found_vid { | |
664 | if !self.register_output_stream("any", istr, out_sm, enc_reg) { | |
665 | return false; | |
666 | } | |
667 | found_vid = true; | |
668 | } else { | |
669 | self.encoders.push(OutputMode::Drop); | |
670 | } | |
671 | } | |
672 | found_vid | |
673 | }, | |
674 | MuxerCapabilities::OnlyAudio => { | |
675 | if self.no_audio { return false; } | |
676 | ||
677 | let mut found_aud = false; | |
678 | for istr in src_sm.iter() { | |
679 | if istr.get_media_type() == StreamType::Audio && !found_aud { | |
680 | if !self.register_output_stream("any", istr, out_sm, enc_reg) { | |
681 | return false; | |
682 | } | |
683 | found_aud = true; | |
684 | } else { | |
685 | self.encoders.push(OutputMode::Drop); | |
686 | } | |
687 | } | |
688 | found_aud | |
689 | }, | |
690 | MuxerCapabilities::Universal => { | |
691 | for istr in src_sm.iter() { | |
692 | if (istr.get_media_type() == StreamType::Video && self.no_video) || | |
693 | (istr.get_media_type() == StreamType::Audio && self.no_audio) { | |
694 | self.encoders.push(OutputMode::Drop); | |
695 | continue; | |
696 | } | |
697 | if !self.register_output_stream("any", istr, out_sm, enc_reg) { | |
698 | return false; | |
699 | } | |
700 | } | |
701 | true | |
702 | }, | |
703 | } | |
704 | } | |
705 | } | |
706 | ||
df37d3b1 | 707 | fn encode_frame(dst_id: u32, encoder: &mut Box<dyn NAEncoder>, cvt: &mut OutputConvert, frm: NAFrameRef, scale_opts: &[(String, String)]) -> bool { |
d9fe2b71 KS |
708 | let buf = frm.get_buffer(); |
709 | let cbuf = match cvt { | |
710 | OutputConvert::None => buf, | |
711 | OutputConvert::Video(ref mut scaler, ref mut dbuf) => { | |
712 | let cur_ifmt = get_scale_fmt_from_pic(&buf); | |
713 | let last_ifmt = scaler.get_in_fmt(); | |
714 | if cur_ifmt != last_ifmt { | |
715 | let ofmt = scaler.get_out_fmt(); | |
df37d3b1 | 716 | let ret = NAScale::new_with_options(cur_ifmt, ofmt, scale_opts); |
d9fe2b71 KS |
717 | if ret.is_err() { |
718 | println!("error re-initialising scaler for {} -> {}", cur_ifmt, ofmt); | |
719 | return false; | |
720 | } | |
721 | *scaler = ret.unwrap(); | |
722 | } | |
723 | let ret = scaler.convert(&buf, dbuf); | |
724 | if ret.is_err() { | |
725 | println!("error converting frame for encoding"); | |
726 | return false; | |
727 | } | |
728 | dbuf.clone() | |
729 | }, | |
730 | OutputConvert::Audio(ref dinfo, ref dchmap) => { | |
731 | let ret = convert_audio_frame(&buf, dinfo, dchmap); | |
732 | if ret.is_err() { | |
733 | println!("error converting audio for stream {}", dst_id); | |
734 | return false; | |
735 | } | |
736 | ret.unwrap() | |
737 | }, | |
738 | }; | |
739 | let cfrm = NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cbuf); | |
740 | encoder.encode(&cfrm).unwrap(); | |
741 | true | |
742 | } | |
743 | ||
6dc69f51 KS |
744 | macro_rules! next_arg { |
745 | ($args: expr, $arg_idx: expr) => { | |
746 | if $arg_idx + 1 >= $args.len() { | |
747 | println!("codec name is required"); | |
748 | } | |
749 | $arg_idx += 1; | |
750 | } | |
751 | } | |
752 | ||
86c54d88 | 753 | #[allow(clippy::single_match)] |
6dc69f51 KS |
754 | fn main() { |
755 | let args: Vec<_> = env::args().collect(); | |
756 | ||
757 | if args.len() == 1 { | |
071d353e KS |
758 | println!("usage: nihav-encoder [options] --input inputfile --output outputfile"); |
759 | println!(" use nihav-encoder --help to list all available options"); | |
760 | return; | |
761 | } | |
762 | if args.len() == 2 && (args[1] == "--help" || args[1] == "-h") { | |
763 | println!("usage: nihav-encoder [options] --input inputfile --output outputfile"); | |
764 | println!(" query options:"); | |
765 | println!(" --list-{{decoders,encoders,demuxers,muxers}} - lists all available decoders/encoders/demuxers/muxers"); | |
766 | println!(" --query-{{decoder,encoder,demuxer,muxer}}-options name - lists all options recognized by that decoder/encoder/demuxer/muxer"); | |
767 | println!(" processing options:"); | |
3e82df4a | 768 | println!(" --verbose - show time for the currently processed input"); |
071d353e KS |
769 | println!(" --input inputfile - set input file"); |
770 | println!(" --input-format fmt - force input format"); | |
771 | println!(" --demuxer-options options - set input demuxer options"); | |
df37d3b1 | 772 | println!(" --scale-options options - set scaler options"); |
071d353e KS |
773 | println!(" --output outputfile - set output file"); |
774 | println!(" --output-format fmt - force output format"); | |
775 | println!(" --muxer-options options - set output muxer options"); | |
776 | println!(" --no-audio - do not decode audio streams"); | |
777 | println!(" --no-video - do not decode video streams"); | |
778 | println!(" --start starttime - start decoding from given position"); | |
779 | println!(" --end endtime - end decoding at given position"); | |
780 | println!(" --istreamX options - set options for input stream X"); | |
781 | println!(" --ostreamX options - set options for output stream X"); | |
782 | println!(); | |
783 | println!(" (de)muxer and stream options are passed as comma-separated list e.g. --ostream0 width=320,height=240,flip"); | |
6dc69f51 KS |
784 | return; |
785 | } | |
786 | ||
91a15e39 | 787 | let full_reg = FullRegister::new(); |
6dc69f51 KS |
788 | |
789 | let mut transcoder = Transcoder::new(); | |
790 | ||
791 | let mut arg_idx = 1; | |
5a8a7cdb | 792 | let mut printed_info = false; |
6dc69f51 KS |
793 | while arg_idx < args.len() { |
794 | match args[arg_idx].as_str() { | |
e6cb09af | 795 | "--list-decoders" => { |
91a15e39 | 796 | if full_reg.dec_reg.iter().len() > 0 { |
e6cb09af | 797 | println!("Registered decoders:"); |
91a15e39 | 798 | for dec in full_reg.dec_reg.iter() { |
e6cb09af KS |
799 | let cdesc = register::get_codec_description(dec.name); |
800 | let full_name = if let Some(cd) = cdesc { cd.get_full_name() } else { "???" }; | |
801 | println!(" {} ({})", dec.name, full_name); | |
802 | } | |
803 | } else { | |
804 | println!("No registered decoders."); | |
805 | } | |
5a8a7cdb | 806 | printed_info = true; |
e6cb09af KS |
807 | }, |
808 | "--list-encoders" => { | |
91a15e39 | 809 | if full_reg.enc_reg.iter().len() > 0 { |
e6cb09af | 810 | println!("Registered encoders:"); |
91a15e39 | 811 | for enc in full_reg.enc_reg.iter() { |
e6cb09af KS |
812 | let cdesc = register::get_codec_description(enc.name); |
813 | let full_name = if let Some(cd) = cdesc { cd.get_full_name() } else { "???" }; | |
814 | println!(" {} ({})", enc.name, full_name); | |
815 | } | |
816 | } else { | |
817 | println!("No registered encoders."); | |
818 | } | |
5a8a7cdb | 819 | printed_info = true; |
e6cb09af KS |
820 | }, |
821 | "--list-demuxers" => { | |
822 | print!("Registered demuxers:"); | |
91a15e39 | 823 | for dmx in full_reg.dmx_reg.iter() { |
e6cb09af KS |
824 | print!(" {}", dmx.get_name()); |
825 | } | |
826 | println!(); | |
5a8a7cdb | 827 | printed_info = true; |
e6cb09af KS |
828 | }, |
829 | "--list-muxers" => { | |
830 | print!("Registered muxers:"); | |
91a15e39 | 831 | for mux in full_reg.mux_reg.iter() { |
e6cb09af KS |
832 | print!(" {}", mux.get_name()); |
833 | } | |
834 | println!(); | |
5a8a7cdb | 835 | printed_info = true; |
e6cb09af | 836 | }, |
6dc69f51 KS |
837 | "--query-decoder-options" => { |
838 | next_arg!(args, arg_idx); | |
839 | let cname = args[arg_idx].as_str(); | |
91a15e39 | 840 | if let Some(decfunc) = full_reg.dec_reg.find_decoder(cname) { |
6dc69f51 KS |
841 | let dec = (decfunc)(); |
842 | let opts = dec.get_supported_options(); | |
843 | print_options(cname, opts); | |
844 | } else { | |
845 | println!("codec {} is not found", cname); | |
846 | } | |
5a8a7cdb | 847 | printed_info = true; |
6dc69f51 KS |
848 | }, |
849 | "--query-demuxer-options" => { | |
850 | next_arg!(args, arg_idx); | |
851 | let dname = args[arg_idx].as_str(); | |
852 | let mut mr = MemoryReader::new_read(&[]); | |
853 | let mut br = ByteReader::new(&mut mr); | |
91a15e39 | 854 | if let Some(dmx_creator) = full_reg.dmx_reg.find_demuxer(dname) { |
6dc69f51 KS |
855 | let dmx = dmx_creator.new_demuxer(&mut br); |
856 | let opts = dmx.get_supported_options(); | |
857 | print_options(dname, opts); | |
858 | } else { | |
859 | println!("demuxer {} is not found", dname); | |
860 | } | |
5a8a7cdb | 861 | printed_info = true; |
6dc69f51 KS |
862 | }, |
863 | "--query-encoder-options" => { | |
864 | next_arg!(args, arg_idx); | |
865 | let cname = args[arg_idx].as_str(); | |
91a15e39 | 866 | if let Some(encfunc) = full_reg.enc_reg.find_encoder(cname) { |
6dc69f51 KS |
867 | let enc = (encfunc)(); |
868 | let opts = enc.get_supported_options(); | |
869 | print_options(cname, opts); | |
870 | } else { | |
871 | println!("codec {} is not found", cname); | |
872 | } | |
5a8a7cdb | 873 | printed_info = true; |
6dc69f51 KS |
874 | }, |
875 | "--query-muxer-options" => { | |
876 | next_arg!(args, arg_idx); | |
877 | let name = args[arg_idx].as_str(); | |
878 | let mut data = []; | |
879 | let mut mw = MemoryWriter::new_write(&mut data); | |
880 | let mut bw = ByteWriter::new(&mut mw); | |
91a15e39 | 881 | if let Some(mux_creator) = full_reg.mux_reg.find_muxer(name) { |
6dc69f51 KS |
882 | let mux = mux_creator.new_muxer(&mut bw); |
883 | let opts = mux.get_supported_options(); | |
884 | print_options(name, opts); | |
885 | } else { | |
886 | println!("muxer {} is not found", name); | |
887 | } | |
5a8a7cdb | 888 | printed_info = true; |
6dc69f51 | 889 | }, |
4b5c61e2 | 890 | "--input" | "-i" => { |
6dc69f51 KS |
891 | next_arg!(args, arg_idx); |
892 | transcoder.input_name = args[arg_idx].clone(); | |
893 | }, | |
894 | "--input-format" => { | |
895 | next_arg!(args, arg_idx); | |
896 | transcoder.input_fmt = Some(args[arg_idx].clone()); | |
897 | }, | |
4b5c61e2 | 898 | "--output" | "-o" => { |
6dc69f51 KS |
899 | next_arg!(args, arg_idx); |
900 | transcoder.output_name = args[arg_idx].clone(); | |
901 | }, | |
902 | "--output-format" => { | |
903 | next_arg!(args, arg_idx); | |
904 | transcoder.output_fmt = Some(args[arg_idx].clone()); | |
905 | }, | |
906 | "--demuxer-options" => { | |
907 | next_arg!(args, arg_idx); | |
91a15e39 | 908 | if !transcoder.parse_demuxer_options(&args[arg_idx], &full_reg.dmx_reg) { |
6dc69f51 KS |
909 | println!("invalid demuxer option syntax"); |
910 | return; | |
911 | } | |
912 | }, | |
df37d3b1 KS |
913 | "--scale-options" => { |
914 | next_arg!(args, arg_idx); | |
915 | if !transcoder.parse_scale_options(&args[arg_idx]) { | |
916 | println!("invalid scale option syntax"); | |
917 | return; | |
918 | } | |
919 | }, | |
4b5c61e2 | 920 | "--no-video" | "-vn" => { |
6dc69f51 KS |
921 | transcoder.no_video = true; |
922 | }, | |
4b5c61e2 | 923 | "--no-audio" | "-an" => { |
6dc69f51 KS |
924 | transcoder.no_audio = true; |
925 | }, | |
951916e8 KS |
926 | "--start" => { |
927 | next_arg!(args, arg_idx); | |
928 | let ret = args[arg_idx].parse::<NATimePoint>(); | |
929 | if let Ok(val) = ret { | |
930 | transcoder.start = val; | |
931 | } else { | |
932 | println!("invalid start time"); | |
933 | return; | |
934 | } | |
935 | }, | |
936 | "--end" => { | |
937 | next_arg!(args, arg_idx); | |
938 | let ret = args[arg_idx].parse::<NATimePoint>(); | |
939 | if let Ok(val) = ret { | |
940 | transcoder.end = val; | |
941 | } else { | |
b0c51548 | 942 | println!("invalid end time"); |
951916e8 KS |
943 | return; |
944 | } | |
945 | }, | |
6dc69f51 KS |
946 | "--muxer-options" => { |
947 | next_arg!(args, arg_idx); | |
91a15e39 | 948 | if !transcoder.parse_muxer_options(&args[arg_idx], &full_reg.mux_reg) { |
6dc69f51 KS |
949 | println!("invalid muxer option syntax"); |
950 | return; | |
951 | } | |
952 | }, | |
5ec8115f | 953 | "--verbose" | "-v" => transcoder.verbose = true, |
6dc69f51 KS |
954 | _ => { |
955 | if args[arg_idx].starts_with("--istream") { | |
956 | let opt0 = &args[arg_idx]; | |
957 | next_arg!(args, arg_idx); | |
958 | if !transcoder.parse_istream_options(opt0, &args[arg_idx]) { | |
959 | println!("invalid input stream option syntax"); | |
960 | return; | |
961 | } | |
962 | } else if args[arg_idx].starts_with("--ostream") { | |
963 | let opt0 = &args[arg_idx]; | |
964 | next_arg!(args, arg_idx); | |
91a15e39 | 965 | if !transcoder.parse_ostream_options(opt0, &args[arg_idx], &full_reg.enc_reg) { |
6dc69f51 KS |
966 | println!("invalid output stream option syntax"); |
967 | return; | |
968 | } | |
969 | } else if args[arg_idx].starts_with("--") { | |
970 | println!("unknown option"); | |
971 | } else { | |
972 | println!("unrecognized argument"); | |
973 | } | |
974 | }, | |
975 | }; | |
976 | arg_idx += 1; | |
977 | } | |
978 | ||
5a8a7cdb KS |
979 | if printed_info { |
980 | return; | |
981 | } | |
982 | ||
86c54d88 | 983 | if transcoder.input_name.is_empty() { |
6dc69f51 KS |
984 | println!("no input name provided"); |
985 | return; | |
986 | } | |
86c54d88 | 987 | if transcoder.output_name.is_empty() { |
6dc69f51 KS |
988 | println!("no output name provided"); |
989 | return; | |
990 | } | |
991 | ||
992 | let res = File::open(transcoder.input_name.as_str()); | |
993 | if res.is_err() { | |
994 | println!("error opening input"); | |
995 | return; | |
996 | } | |
91a15e39 KS |
997 | let file = res.unwrap(); |
998 | let file = BufReader::new(file); | |
999 | let mut fr = FileReader::new_read(file); | |
6dc69f51 | 1000 | let mut br = ByteReader::new(&mut fr); |
91a15e39 KS |
1001 | let (is_raw, start, end) = if transcoder.input_fmt.is_none() { |
1002 | detect_tags(&mut br) | |
6dc69f51 | 1003 | } else { |
91a15e39 | 1004 | (false, 0, None) |
6dc69f51 | 1005 | }; |
91a15e39 KS |
1006 | |
1007 | let mut nfr: Box<dyn ByteIO>; | |
1008 | if start != 0 || end.is_some() { | |
1009 | let file = fr.finish(); | |
1010 | nfr = Box::new(BoundedFileReader::new_read(file, start, end).unwrap()); | |
1011 | } else { | |
1012 | nfr = Box::new(fr); | |
1013 | } | |
1014 | let mut br = ByteReader::new(nfr.as_mut()); | |
1015 | ||
1016 | let mut dmx = DemuxerObject::create(&mut br, &full_reg, transcoder.input_name.as_str(), &transcoder.input_fmt, is_raw); | |
1017 | if dmx.is_none() { | |
1018 | println!("cannot find demuxer for '{}'", transcoder.input_name.as_str()); | |
6dc69f51 KS |
1019 | return; |
1020 | } | |
5ec8115f KS |
1021 | let duration = dmx.get_duration(); |
1022 | let duration_string = if duration != 0 { format_time(duration) } else { String::new() }; | |
1023 | ||
6dc69f51 KS |
1024 | parse_and_apply_options!(dmx, &transcoder.demux_opts, "input"); |
1025 | for i in 0..dmx.get_num_streams() { | |
1026 | let s = dmx.get_stream(i).unwrap(); | |
1027 | let info = s.get_info(); | |
91a15e39 | 1028 | let decfunc = full_reg.dec_reg.find_decoder(info.get_name()); |
6dc69f51 KS |
1029 | println!("stream {} - {} {}", i, s, info.get_name()); |
1030 | let str_id = s.get_num() as u32; | |
1031 | if let Some(create_dec) = decfunc { | |
1032 | let mut dec = (create_dec)(); | |
1033 | let mut dsupp = Box::new(NADecoderSupport::new()); | |
1034 | let ret = dec.init(&mut dsupp, info.clone()); | |
1035 | if ret.is_err() { | |
1036 | println!("Error initialising decoder '{}' for stream {}", info.get_name(), str_id); | |
1037 | return; | |
1038 | } | |
1039 | transcoder.apply_decoder_options(dec.as_mut(), str_id); | |
d9fe2b71 KS |
1040 | let desc = register::get_codec_description(info.get_name()); |
1041 | let has_b = if let Some(desc) = desc { | |
1042 | desc.has_reorder() | |
1043 | } else { | |
1044 | println!("No codec description found, using B-frame reorderer."); | |
1045 | true | |
1046 | }; | |
1047 | let reord: Box<dyn FrameReorderer> = if has_b { Box::new(IPBReorderer::new()) } else { Box::new(NoReorderer::new()) }; | |
1048 | transcoder.decoders.push(Some((dsupp, dec, reord))); | |
6dc69f51 KS |
1049 | } else { |
1050 | println!("No decoder for stream {} ({}) is found", str_id, info.get_name()); | |
1051 | transcoder.decoders.push(None); | |
1052 | } | |
1053 | } | |
951916e8 KS |
1054 | if transcoder.start != NATimePoint::None { |
1055 | let ret = dmx.seek(transcoder.start); | |
1056 | if ret.is_err() { | |
1057 | println!(" failed to seek to {} error {:?}", transcoder.start, ret.err().unwrap()); | |
1058 | } | |
1059 | } | |
6dc69f51 KS |
1060 | |
1061 | let output_fmt = if let Some(ref str) = transcoder.output_fmt { | |
1062 | str | |
86c54d88 KS |
1063 | } else if let Some(str) = detect::detect_format_by_name(transcoder.output_name.as_str()) { |
1064 | str | |
6dc69f51 | 1065 | } else { |
86c54d88 KS |
1066 | println!("Cannot guess muxer for output"); |
1067 | return; | |
6dc69f51 | 1068 | }; |
91a15e39 | 1069 | let ret = full_reg.mux_reg.find_muxer(output_fmt); |
6dc69f51 KS |
1070 | let ofmt = output_fmt.to_string(); |
1071 | ||
1072 | if ret.is_none() { | |
1073 | println!("cannot find muxer '{}'", output_fmt); | |
1074 | } | |
1075 | let mux_creator = ret.unwrap(); | |
1076 | ||
1077 | let mux_caps = mux_creator.get_capabilities(); | |
1078 | let mut out_sm = StreamManager::new(); | |
91a15e39 | 1079 | if !transcoder.negotiate_stream_map(dmx.get_stream_manager(), mux_caps, &mut out_sm, &full_reg.enc_reg) { |
6dc69f51 KS |
1080 | println!("cannot determine stream map"); |
1081 | return; | |
1082 | } | |
1083 | ||
1084 | let ret = File::create(transcoder.output_name.as_str()); | |
1085 | if ret.is_err() { | |
1086 | println!("cannot open output file"); | |
1087 | return; | |
1088 | } | |
1089 | let mut fw = FileWriter::new_write(ret.unwrap()); | |
1090 | let mut bw = ByteWriter::new(&mut fw); | |
1091 | let ret = create_muxer(mux_creator, out_sm, &mut bw); | |
1092 | if let Err(err) = ret { | |
1093 | println!("cannot create muxer instance {:?}", err); | |
1094 | return; | |
1095 | } | |
1096 | let mut mux = ret.unwrap(); | |
1097 | parse_and_apply_options!(mux, &transcoder.mux_opts, "output"); | |
1098 | ||
1099 | println!("Output {} muxer {}", transcoder.output_name, ofmt); | |
1100 | for ostr in mux.get_streams() { | |
1101 | println!(" #{}: {} {}", ostr.get_num(), ostr, ostr.get_info().get_name()); | |
1102 | } | |
1103 | ||
5ec8115f KS |
1104 | let mut time = Instant::now(); |
1105 | let show_interval = Duration::from_millis(100); | |
951916e8 | 1106 | 'main_loop: loop { |
6dc69f51 KS |
1107 | let pktres = dmx.get_frame(); |
1108 | if let Err(DemuxerError::EOF) = pktres { break; } | |
1109 | if pktres.is_err() { | |
1110 | println!("demuxing error"); | |
1111 | break; | |
1112 | } | |
1113 | let mut pkt = pktres.unwrap(); | |
f88b8fb4 | 1114 | if transcoder.start != NATimePoint::None && pkt.ts.less_than(transcoder.start) { continue; } |
6dc69f51 | 1115 | let src_id = pkt.get_stream().get_num(); |
5ec8115f KS |
1116 | if transcoder.verbose && time.elapsed() >= show_interval { |
1117 | if let Some(pts) = pkt.get_pts() { | |
1118 | let cur_time = format_time(NATimeInfo::ts_to_time(pts, 1000, pkt.ts.tb_num, pkt.ts.tb_den)); | |
1119 | print!(" {}", cur_time); | |
1120 | } else { | |
1121 | print!(" ???"); | |
1122 | } | |
1123 | if !duration_string.is_empty() { | |
1124 | print!(" / {}", duration_string); | |
1125 | } | |
1126 | print!("\r"); | |
1127 | std::io::stdout().flush().unwrap(); | |
1128 | time = Instant::now(); | |
1129 | } | |
6dc69f51 KS |
1130 | match transcoder.encoders[src_id] { |
1131 | OutputMode::Drop => {}, | |
1132 | OutputMode::Copy(dst_id) => { | |
1133 | let dstr = mux.get_stream(dst_id as usize).unwrap(); | |
1134 | pkt.reassign(dstr, pkt.get_time_information()); | |
951916e8 | 1135 | if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'main_loop; } |
6dc69f51 KS |
1136 | if mux.mux_frame(pkt).is_err() { |
1137 | println!("error muxing packet"); | |
1138 | break; | |
1139 | } | |
1140 | }, | |
1141 | OutputMode::Encode(dst_id, ref mut encoder, ref mut cvt) => { | |
d9fe2b71 | 1142 | if let Some((ref mut dsupp, ref mut decoder, ref mut reorderer)) = transcoder.decoders[src_id] { |
6dc69f51 | 1143 | let ret = decoder.decode(dsupp, &pkt); |
f88b8fb4 KS |
1144 | if let (true, Err(DecoderError::MissingReference)) = (transcoder.start != NATimePoint::None, &ret) { |
1145 | continue; | |
1146 | } | |
6dc69f51 KS |
1147 | if ret.is_err() { |
1148 | println!("error decoding stream {}", src_id); | |
1149 | break; | |
1150 | } | |
1151 | let frm = ret.unwrap(); | |
d9fe2b71 KS |
1152 | reorderer.add_frame(frm); |
1153 | while let Some(frm) = reorderer.get_frame() { | |
df37d3b1 | 1154 | if !encode_frame(dst_id, encoder, cvt, frm, &transcoder.scale_opts) { |
d9fe2b71 KS |
1155 | break; |
1156 | } | |
1157 | while let Ok(Some(pkt)) = encoder.get_packet() { | |
1158 | if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'main_loop; } | |
1159 | mux.mux_frame(pkt).unwrap(); | |
1160 | } | |
6dc69f51 KS |
1161 | } |
1162 | } else { | |
1163 | println!("no decoder for stream {}", src_id); | |
1164 | break; | |
1165 | } | |
1166 | }, | |
1167 | }; | |
1168 | } | |
d9fe2b71 KS |
1169 | 'reord_flush_loop: for str in dmx.get_streams() { |
1170 | let src_id = str.get_num(); | |
1171 | if let OutputMode::Encode(dst_id, ref mut encoder, ref mut cvt) = transcoder.encoders[src_id] { | |
1172 | if let Some((_, _, ref mut reorderer)) = transcoder.decoders[src_id] { | |
1173 | while let Some(frm) = reorderer.get_last_frames() { | |
df37d3b1 | 1174 | if !encode_frame(dst_id, encoder, cvt, frm, &transcoder.scale_opts) { |
d9fe2b71 KS |
1175 | break; |
1176 | } | |
1177 | while let Ok(Some(pkt)) = encoder.get_packet() { | |
1178 | if transcoder.end != NATimePoint::None && !pkt.ts.less_than(transcoder.end) { break 'reord_flush_loop; } | |
1179 | mux.mux_frame(pkt).unwrap(); | |
1180 | } | |
1181 | } | |
1182 | } | |
1183 | } | |
1184 | } | |
6dc69f51 KS |
1185 | 'flush_loop: for enc in transcoder.encoders.iter_mut() { |
1186 | match enc { | |
1187 | OutputMode::Encode(str_id, ref mut encoder, _) => { | |
1188 | let ret = encoder.flush(); | |
1189 | if ret.is_err() { | |
1190 | println!("error flushing encoder for stream {}", str_id); | |
1191 | break; | |
1192 | } else { | |
1193 | while let Ok(Some(pkt)) = encoder.get_packet() { | |
1194 | if mux.mux_frame(pkt).is_err() { | |
1195 | println!("error muxing packet"); | |
1196 | break 'flush_loop; | |
1197 | } | |
1198 | } | |
1199 | } | |
1200 | }, | |
1201 | _ => {}, | |
1202 | }; | |
1203 | } | |
5ec8115f KS |
1204 | if transcoder.verbose { |
1205 | println!(); | |
1206 | } | |
6dc69f51 KS |
1207 | |
1208 | let ret = mux.end(); | |
1209 | if ret.is_err() { | |
1210 | println!("error at finalising muxing"); | |
1211 | } | |
1212 | } |