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