2 extern crate nihav_core;
3 extern crate nihav_registry;
4 extern crate nihav_allstuff;
10 use std::time::{Duration, Instant};
12 use std::sync::atomic::{AtomicU8, Ordering};
14 use sdl2::event::{Event, WindowEvent};
15 use sdl2::keyboard::{Keycode, Mod};
16 use sdl2::mouse::{MouseButton, MouseWheelDirection};
17 use sdl2::render::{Canvas, Texture, TextureCreator};
18 use sdl2::pixels::PixelFormatEnum;
19 use sdl2::video::{Window, WindowContext};
21 use nihav_registry::detect;
22 use nihav_core::frame::*;
23 use nihav_core::io::byteio::{FileReader, ByteReader};
24 use nihav_core::reorder::*;
25 use nihav_core::codecs::*;
26 use nihav_core::demuxers::*;
27 use nihav_registry::register::*;
28 use nihav_allstuff::*;
38 #[derive(Clone,Copy,Debug,PartialEq,Default)]
49 impl From<u8> for DecodingState {
50 fn from(val: u8) -> Self {
52 0 => DecodingState::Normal,
53 1 => DecodingState::Waiting,
54 2 => DecodingState::Flush,
55 3 => DecodingState::Prefetch,
56 4 => DecodingState::End,
57 _ => DecodingState::Error,
67 const fn new() -> Self {
69 state: AtomicU8::new(DecodingState::Normal as u8)
72 fn set_state(&self, state: DecodingState) {
73 self.state.store(state as u8, Ordering::Release);
75 fn get_state(&self) -> DecodingState {
76 self.state.load(Ordering::Acquire).into()
78 fn is_flushing(&self) -> bool {
79 matches!(self.get_state(), DecodingState::Flush | DecodingState::Error)
83 #[cfg(feature="debug")]
84 macro_rules! debug_log {
85 ($log: expr; $blk: block) => {
86 $log.logfile.write($blk.as_bytes()).unwrap();
87 $log.logfile.write(b"\n").unwrap();
90 #[cfg(not(feature="debug"))]
91 macro_rules! debug_log {
92 ($log: expr; $blk: block) => {};
95 pub enum PktSendEvent {
104 pub enum DecoderType {
105 Audio(Box<dyn NADecoder + Send>),
106 Video(Box<dyn NADecoder + Send>, Box<dyn FrameReorderer + Send>),
107 VideoMT(Box<dyn NADecoderMT + Send>, MTFrameReorderer),
110 pub struct DecoderStuff {
111 pub dsupp: Box<NADecoderSupport>,
112 pub dec: DecoderType,
115 fn format_time(ms: u64) -> String {
117 let ds = (ms % 1000) / 100;
118 let (min, s) = (s / 60, s % 60);
119 let (h, min) = (min / 60, min % 60);
122 format!("{}.{}", s, ds)
124 format!("{}:{:02}.{}", min, s, ds)
127 format!("{}:{:02}:{:02}.{}", h, min, s, ds)
131 const FRAME_QUEUE_LEN: usize = 25;
132 const MAX_VOLUME: usize = 200;
134 pub type FrameRecord = (NABufferType, u64);
136 pub struct TimeKeep {
144 ref_time: Instant::now(),
148 pub fn get_cur_time(&self) -> u64 {
149 let add = self.ref_time.elapsed().as_millis() as u64;
152 fn reset_ts(&mut self) {
155 fn reset_all(&mut self, ts: u64) {
156 self.ref_time = Instant::now();
159 fn set_ts(&mut self) {
160 self.ref_ts = self.get_cur_time();
162 fn set_time(&mut self) {
163 self.ref_time = Instant::now();
167 pub struct DispFrame<'a> {
171 pub rgb_tex: Texture<'a>,
172 pub yuv_tex: Texture<'a>,
175 pub struct DispQueue<'a> {
176 pub pool: Vec<DispFrame<'a>>,
186 impl<'a> DispQueue<'a> {
187 fn new(texture_creator: &'a TextureCreator<WindowContext>, width: usize, height: usize, len: usize) -> Self {
188 let mut pool = Vec::with_capacity(len);
189 for _ in 0..len + 1 {
190 let rgb_tex = texture_creator.create_texture_streaming(PixelFormatEnum::RGB24, width as u32, height as u32).expect("failed to create RGB texture");
191 let yuv_tex = texture_creator.create_texture_streaming(PixelFormatEnum::IYUV, ((width + 1) & !1) as u32, ((height + 1) & !1) as u32).expect("failed to create YUV texture");
192 pool.push(DispFrame{ ts: 0, is_yuv: false, valid: false, rgb_tex, yuv_tex });
194 pool[len].is_yuv = false;
195 pool[len].rgb_tex.with_lock(None, |buffer: &mut [u8], _pitch: usize| {
196 for el in buffer.iter_mut() { *el = 0; }
197 }).expect("RGB texture could not be locked");
199 Self { pool, first_ts: 0, last_ts: 0, start: 0, end: 0, len, width, height }
202 fn flush(&mut self) {
207 for frm in self.pool.iter_mut() {
212 fn get_last_texture(&mut self, osd: &OSD) -> &Texture<'a> {
213 if self.pool[self.len].is_yuv {
215 self.pool[self.len].yuv_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
216 osd.draw_yuv(buffer, pitch);
217 }).expect("YUV texture locking failure");
219 &self.pool[self.len].yuv_tex
222 self.pool[self.len].rgb_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
223 osd.draw_rgb(buffer, pitch);
224 }).expect("RGB texture locking failure");
226 &self.pool[self.len].rgb_tex
229 pub fn is_empty(&self) -> bool { self.start == self.end }
230 pub fn is_full(&self) -> bool { self.len == 0 || self.start == (self.end + 1) % self.len }
231 pub fn move_end(&mut self) {
233 if self.end >= self.len {
234 self.end -= self.len;
237 pub fn move_start(&mut self) {
238 self.pool.swap(self.start, self.len);
240 if self.start >= self.len {
241 self.start -= self.len;
243 if !self.is_empty() {
244 self.first_ts = self.pool[self.start].ts;
249 fn try_display(disp_queue: &mut DispQueue, canvas: &mut Canvas<Window>, osd: &mut OSD, ctime: &TimeKeep) -> Option<u64> {
250 while !disp_queue.is_empty() {
251 let disp_time = disp_queue.first_ts;
252 let ctime = ctime.get_cur_time();
253 if disp_time > ctime + 10 {
254 return Some(disp_time - ctime);
255 } else if disp_time + 10 < ctime {
256 disp_queue.move_start();
261 let frm = &mut disp_queue.pool[disp_queue.start];
262 let texture = if frm.is_yuv {
264 frm.yuv_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
265 osd.draw_yuv(buffer, pitch);
266 }).expect("YUV texture locking failure");
271 frm.rgb_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
272 osd.draw_rgb(buffer, pitch);
273 }).expect("RGB texture locking failure");
278 canvas.copy(texture, None, None).expect("canvas blit failure");
281 disp_queue.move_start();
282 if !disp_queue.is_empty() {
283 return Some((disp_queue.first_ts - ctime).saturating_sub(2));
293 sdl_context: sdl2::Sdl,
294 vsystem: sdl2::VideoSubsystem,
295 asystem: sdl2::AudioSubsystem,
299 acontrol: AudioControl,
300 vcontrol: VideoControl,
322 #[cfg(feature="debug")]
328 let sdl_context = sdl2::init().expect("SDL2 init failure");
329 let vsystem = sdl_context.video().expect("video subsystem init failure");
330 let asystem = sdl_context.audio().expect("audio subsystem init failure");
331 vsystem.disable_screen_saver();
332 let acontrol = AudioControl::new(None, None, false, &asystem);
333 let vcontrol = VideoControl::new(None, 0, 0, 0, 0);
335 sdl_context, asystem, vsystem,
356 tkeep: TimeKeep::new(),
361 #[cfg(feature="debug")]
362 logfile: File::create("debug.log").expect("'debug.log' should be available for writing"),
365 fn seek(&mut self, off: u64, fwd: bool, dmx: &mut Demuxer, disp_queue: &mut DispQueue) -> Result<(), ()> {
366 let cur_time = self.tkeep.get_cur_time();
367 let seektime = if fwd { cur_time + off * 1000 } else {
368 cur_time.saturating_sub(off * 1000) };
369 debug_log!(self; {format!(" seek to {}", seektime)});
371 let ret = dmx.seek(NATimePoint::Milliseconds(seektime));
373 println!(" seek error");
374 return Ok(()); //TODO: not ignore some of seek errors?
377 self.acontrol.flush();
378 self.vcontrol.flush();
381 self.tkeep.reset_ts();
382 self.prefill(dmx, disp_queue)?;
383 if !disp_queue.is_empty() {
384 self.tkeep.reset_all(disp_queue.first_ts);
386 let mut iterations = 0;
387 let mut time = self.acontrol.get_time();
388 while time.is_none() {
390 std::thread::yield_now();
391 if iterations > 1000000 { println!(" still no time set?!"); break; }
392 time = self.acontrol.get_time();
394 if let Some(time) = time {
395 self.tkeep.reset_all(time);
399 self.acontrol.resume();
403 fn prefill(&mut self, dmx: &mut Demuxer, disp_queue: &mut DispQueue) -> Result<(), ()> {
404 debug_log!(self; {" prefilling"});
405 while self.vcontrol.get_queue_size() < FRAME_QUEUE_LEN {
406 let mut try_send = self.acontrol.get_queue_size() < FRAME_QUEUE_LEN && (!self.has_video || (!self.vcontrol.is_filled(FRAME_QUEUE_LEN) && !disp_queue.is_full()));
408 if !self.vcontrol.try_send_queued() && self.vcontrol.get_queue_size() > FRAME_QUEUE_LEN / 2 {
411 if !self.acontrol.try_send_queued() && self.acontrol.get_queue_size() > FRAME_QUEUE_LEN / 2 {
415 match dmx.get_frame() {
416 Err(DemuxerError::EOF) => break,
419 let streamno = pkt.get_stream().get_id();
420 if self.has_video && streamno == self.video_str {
421 self.vcontrol.try_send_video(PktSendEvent::Packet(pkt));
422 } else if self.has_audio && streamno == self.audio_str {
423 self.acontrol.try_send_audio(PktSendEvent::Packet(pkt));
428 self.vcontrol.fill(disp_queue);
435 while self.vcontrol.get_queue_size() > 0 && !disp_queue.is_full() {
436 self.vcontrol.try_send_queued();
437 self.vcontrol.fill(disp_queue);
438 std::thread::sleep(Duration::from_millis(10));
440 self.vcontrol.wait_for_frames()?;
441 self.vcontrol.fill(disp_queue);
443 debug_log!(self; {format!(" prefilling done, frames {}-{} audio {}", disp_queue.start, disp_queue.end, self.acontrol.get_fill())});
446 fn toggle_pause(&mut self) {
447 self.paused = !self.paused;
449 self.vsystem.enable_screen_saver();
452 self.vsystem.disable_screen_saver();
453 self.tkeep.set_time();
456 self.acontrol.pause();
458 self.acontrol.resume();
461 fn handle_events(&mut self, event_pump: &mut sdl2::EventPump, canvas: &mut Canvas<Window>, dmx: &mut Demuxer, disp_queue: &mut DispQueue) -> Result<bool, ()> {
462 for event in event_pump.poll_iter() {
463 if let Event::Quit {..} = event {
468 if let Event::Window {win_event: WindowEvent::Exposed, ..} = event {
470 canvas.copy(disp_queue.get_last_texture(&self.osd), None, None).expect("blitting failure");
473 if let Event::MouseButtonDown {mouse_btn, ..} = event {
475 MouseButton::Right => self.toggle_pause(),
476 MouseButton::Middle => self.osd.toggle(),
480 if let Event::MouseWheel {direction: MouseWheelDirection::Normal, x: 0, y, ..} = event {
481 self.seek(10, y > 0, dmx, disp_queue)?;
483 if let Event::KeyDown {keycode: Some(keycode), keymod, ..} = event {
490 Keycode::Q if keymod.contains(Mod::RSHIFTMOD) || keymod.contains(Mod::LSHIFTMOD) => {
495 Keycode::Return => return Ok(true),
496 Keycode::Right => { self.seek(10, true, dmx, disp_queue)?; },
497 Keycode::Left => { self.seek(10, false, dmx, disp_queue)?; },
498 Keycode::Up => { self.seek(60, true, dmx, disp_queue)?; },
499 Keycode::Down => { self.seek(60, false, dmx, disp_queue)?; },
500 Keycode::PageUp => { self.seek(600, true, dmx, disp_queue)?; },
501 Keycode::PageDown => { self.seek(600, false, dmx, disp_queue)?; },
502 Keycode::Space => { self.toggle_pause(); },
503 Keycode::Plus | Keycode::KpPlus => {
504 self.volume = (self.volume + 10).min(MAX_VOLUME);
506 self.acontrol.set_volume(self.volume);
509 Keycode::Minus | Keycode::KpMinus => {
510 self.volume = self.volume.saturating_sub(10);
512 self.acontrol.set_volume(self.volume);
516 self.debug = !self.debug;
519 self.mute = !self.mute;
521 self.acontrol.set_volume(0);
523 self.acontrol.set_volume(self.volume);
527 self.vcontrol.try_send_video(PktSendEvent::HurryUp);
535 print!("{:60}\r", ' ');
536 std::io::stdout().flush().unwrap();
542 fn play(&mut self, name: &str, start_time: NATimePoint) {
543 debug_log!(self; {format!("Playing {}", name)});
545 // prepare data source
546 let path = Path::new(name);
547 let mut file = if let Ok(handle) = File::open(path) {
550 println!("failed to open {}", name);
553 let mut fr = FileReader::new_read(&mut file);
554 let mut br = ByteReader::new(&mut fr);
555 let res = detect::detect_format(name, &mut br);
557 println!("cannot detect format for {}", name);
560 let (dmx_name, _score) = res.unwrap();
561 debug_log!(self; {format!(" found demuxer {} with score {:?}", dmx_name, _score)});
562 println!("trying demuxer {} on {}", dmx_name, name);
564 let mut dmx_reg = RegisteredDemuxers::new();
565 nihav_register_all_demuxers(&mut dmx_reg);
566 let mut dec_reg = RegisteredDecoders::new();
567 nihav_register_all_decoders(&mut dec_reg);
568 let mut mtdec_reg = RegisteredMTDecoders::new();
570 nihav_register_all_mt_decoders(&mut mtdec_reg);
573 let ret = dmx_reg.find_demuxer(dmx_name);
575 println!("error finding {} demuxer", dmx_name);
578 let dmx_fact = ret.unwrap();
579 br.seek(SeekFrom::Start(0)).expect("should be able to seek to the start");
580 let ret = create_demuxer(dmx_fact, &mut br);
582 println!("error creating demuxer");
585 let mut dmx = ret.unwrap();
586 if start_time != NATimePoint::None {
587 debug_log!(self; {format!(" start seek to {}", start_time)});
588 if dmx.seek(start_time).is_err() {
589 println!("initial seek failed");
594 let mut height = 480;
597 let mut ainfo: Option<NAAudioInfo> = None;
598 let mut sbr_hack = false;
600 let mut video_dec: Option<DecoderStuff> = None;
601 let mut audio_dec: Option<DecoderStuff> = None;
603 let duration = dmx.get_duration();
605 println!(" total duration {}", format_time(duration));
607 self.has_video = false;
608 self.has_audio = false;
610 self.osd.set_duration(duration);
611 for i in 0..dmx.get_num_streams() {
612 let s = dmx.get_stream(i).unwrap();
613 let info = s.get_info();
614 let decfunc = dec_reg.find_decoder(info.get_name());
615 let decfunc_mt = mtdec_reg.find_decoder(info.get_name());
616 println!("stream {} - {} {}", i, s, info.get_name());
617 debug_log!(self; {format!(" stream {} - {} {}", i, s, info.get_name())});
618 let str_id = s.get_id();
620 if video_dec.is_none() && self.play_video {
621 if let Some(decfunc) = decfunc_mt {
622 let mut dec = (decfunc)();
623 let mut dsupp = Box::new(NADecoderSupport::new());
624 let props = info.get_properties().get_video_info().unwrap();
625 if props.get_width() != 0 {
626 width = props.get_width();
627 height = props.get_height();
629 if dec.init(&mut dsupp, info.clone(), self.vthreads).is_ok() {
630 video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::VideoMT(dec, MTFrameReorderer::new()) });
631 self.video_str = str_id;
632 let (tbn, tbd) = s.get_timebase();
635 self.has_video = true;
638 println!("failed to create multi-threaded decoder, falling back");
641 if let Some(decfunc) = decfunc {
642 let mut dec = (decfunc)();
643 let mut dsupp = Box::new(NADecoderSupport::new());
644 let props = info.get_properties().get_video_info().unwrap();
645 if props.get_width() != 0 {
646 width = props.get_width();
647 height = props.get_height();
649 let desc = get_codec_description(info.get_name());
650 let (reorder_depth, reord) = if desc.is_none() || (desc.unwrap().caps & CODEC_CAP_COMPLEX_REORDER) == 0 {
651 let reord: Box<dyn FrameReorderer + Send> = Box::new(IPBReorderer::new());
654 let reord: Box<dyn FrameReorderer + Send> = Box::new(ComplexReorderer::new());
657 dsupp.pool_u8 = NAVideoBufferPool::new(reorder_depth);
658 dsupp.pool_u16 = NAVideoBufferPool::new(reorder_depth);
659 dsupp.pool_u32 = NAVideoBufferPool::new(reorder_depth);
660 if dec.init(&mut dsupp, info).is_err() {
661 println!("failed to initialise video decoder");
664 video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Video(dec, reord) });
665 self.video_str = str_id;
666 let (tbn, tbd) = s.get_timebase();
669 self.has_video = true;
671 println!("no video decoder for {} found!", info.get_name());
674 } else if info.is_audio() {
675 if audio_dec.is_none() && self.play_audio {
676 if let Some(decfunc) = decfunc {
677 let mut dec = (decfunc)();
678 let mut dsupp = Box::new(NADecoderSupport::new());
679 ainfo = info.get_properties().get_audio_info();
680 if let (true, Some(ref ai)) = (info.get_name() == "aac", ainfo) {
681 if ai.sample_rate < 32000 {
685 if dec.init(&mut dsupp, info).is_err() {
686 println!("failed to initialise audio decoder");
689 audio_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Audio(dec) });
690 self.audio_str = str_id;
691 self.has_audio = true;
693 println!("no audio decoder for {} found!", info.get_name());
697 println!("decoder {} not found", info.get_name());
700 if !self.has_video && !self.has_audio {
701 println!("No playable streams found.");
705 while (width <= 384) && (height <= 288) {
710 // prepare playback structure
711 let mut new_vcontrol = VideoControl::new(video_dec, width, height, tb_num, tb_den);
712 std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
714 let mut new_acontrol = AudioControl::new(audio_dec, ainfo, sbr_hack, &self.asystem);
715 std::mem::swap(&mut self.acontrol, &mut new_acontrol);
718 self.acontrol.set_volume(0);
720 self.acontrol.set_volume(self.volume);
723 let fname = path.file_name();
724 let wname = if let Some(fname) = fname {
725 "NihAV player - ".to_owned() + fname.to_str().expect("should be able to set window title")
727 "NihAV player".to_owned()
729 let mut builder = self.vsystem.window(&wname, width as u32, height as u32);
730 let window = if let (Some(xpos), Some(ypos)) = (self.xpos, self.ypos) {
731 builder.position(xpos, ypos).build().expect("should be able to set window position")
733 builder.position_centered().build().expect("should be able to centre window")
735 let mut canvas = window.into_canvas().build().expect("should be able to build canvas");
736 let texture_creator = canvas.texture_creator();
737 let mut disp_q = DispQueue::new(&texture_creator, width, height, if self.has_video { FRAME_QUEUE_LEN } else { 0 });
740 canvas.copy(disp_q.get_last_texture(&self.osd), None, None).expect("blit failure");
744 self.has_audio = self.acontrol.has_audio();
745 if !self.has_video && !self.has_audio {
746 println!("No playable streams.");
751 if self.prefill(&mut dmx, &mut disp_q).is_err() {
752 std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
753 new_vcontrol.finish();
754 std::mem::swap(&mut self.acontrol, &mut new_acontrol);
755 new_acontrol.finish();
758 self.tkeep.reset_all(if !disp_q.is_empty() { disp_q.first_ts } else { 0 });
760 self.acontrol.resume();
762 let mut event_pump = self.sdl_context.event_pump().expect("should be able to create event pump");
763 let mut last_disp = Instant::now();
764 let mut has_data = true;
766 let ret = self.handle_events(&mut event_pump, &mut canvas, &mut dmx, &mut disp_q);
767 if matches!(ret, Ok(true) | Err(_)) {
772 let mut try_send = self.acontrol.get_queue_size() < FRAME_QUEUE_LEN && self.vcontrol.get_queue_size() < FRAME_QUEUE_LEN;
773 if !self.vcontrol.try_send_queued() && self.vcontrol.is_filled(FRAME_QUEUE_LEN) {
776 if !self.acontrol.try_send_queued() {
779 while has_data && try_send {
780 match dmx.get_frame() {
781 Err(DemuxerError::EOF) => {
782 self.vcontrol.try_send_video(PktSendEvent::End);
783 self.acontrol.try_send_audio(PktSendEvent::End);
787 println!("demuxer error {:?}", err);
788 if err == DemuxerError::IOError {
789 self.vcontrol.try_send_video(PktSendEvent::End);
790 self.acontrol.try_send_audio(PktSendEvent::End);
795 let streamno = pkt.get_stream().get_id();
796 if self.has_video && streamno == self.video_str {
797 debug_log!(self; {" sending video packet"});
798 self.vcontrol.try_send_video(PktSendEvent::Packet(pkt));
799 if self.vcontrol.is_filled(FRAME_QUEUE_LEN) {
802 } else if self.has_audio && streamno == self.audio_str {
803 debug_log!(self; {" sending audio packet"});
804 self.acontrol.try_send_audio(PktSendEvent::Packet(pkt));
805 if self.acontrol.get_queue_size() >= FRAME_QUEUE_LEN {
812 self.vcontrol.fill(&mut disp_q);
813 let mut sleep_time = 25;
814 debug_log!(self; {format!(" time {}", self.tkeep.get_cur_time())});
816 debug_log!(self; {format!(" disp queue {}-{}, {}-{} vqueue fill {}", disp_q.first_ts, disp_q.last_ts, disp_q.start, disp_q.end, self.vcontrol.get_queue_size())});
817 let ret = try_display(&mut disp_q, &mut canvas, &mut self.osd, &self.tkeep);
818 if let Some(next_time) = ret {
819 sleep_time = sleep_time.min(next_time);
823 let time_left = self.acontrol.get_time_left();
824 debug_log!(self; {format!(" audio left {}", time_left)});
825 sleep_time = sleep_time.min(time_left);
827 debug_log!(self; {format!(" sleep {}ms", sleep_time)});
828 if last_disp.elapsed().as_millis() >= 10 {
829 let c_time = self.tkeep.get_cur_time();
832 print!(" {} {}% \r", format_time(c_time), self.acontrol.get_volume());
834 print!(" {} {} {}% {:3} {:6}\r", format_time(c_time), if self.vcontrol.is_yuv() { 'Y' } else { 'R' }, self.acontrol.get_volume(), (disp_q.end + disp_q.len - disp_q.start) % disp_q.len, self.acontrol.get_fill());
836 std::io::stdout().flush().unwrap();
837 last_disp = Instant::now();
840 if self.has_video && !self.vcontrol.is_video_end() {
843 if self.has_audio && !self.acontrol.is_audio_end() {
849 thread::sleep(Duration::from_millis(sleep_time));
851 thread::sleep(Duration::from_millis(20));
854 let (xpos, ypos) = canvas.into_window().position();
855 self.xpos = Some(xpos);
856 self.ypos = Some(ypos);
858 std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
859 new_vcontrol.finish();
860 std::mem::swap(&mut self.acontrol, &mut new_acontrol);
861 new_acontrol.finish();
866 let args: Vec<String> = env::args().collect();
869 println!("usage: nihav-player file1 file2 ...");
873 let mut player = Player::new();
875 let mut aiter = args.iter().skip(1);
876 let mut seek_time = NATimePoint::None;
877 while let Some(arg) = aiter.next() {
879 "-an" => { player.play_audio = false; },
880 "-ae" => { player.play_audio = true; },
881 "-vn" => { player.play_video = false; },
882 "-ve" => { player.play_video = true; },
883 "-seek" | "-start" => {
884 if let Some(arg) = aiter.next() {
885 if let Ok(time) = arg.parse::<NATimePoint>() {
888 println!("wrong seek time");
889 seek_time = NATimePoint::None;
894 if let Some(arg) = aiter.next() {
895 if let Ok(vol) = arg.parse::<usize>() {
896 player.volume = vol.min(MAX_VOLUME);
898 println!("wrong volume");
906 player.debug = false;
909 player.use_mt = true;
912 player.use_mt = false;
915 if let Some(arg) = aiter.next() {
916 if let Ok(val) = arg.parse::<usize>() {
917 player.vthreads = val.max(1);
919 println!("wrong number of threads");
924 player.play(arg, seek_time);
925 if player.end { break; }
926 seek_time = NATimePoint::None;