2 extern crate nihav_core;
3 extern crate nihav_registry;
4 extern crate nihav_allstuff;
10 use std::time::{Duration, Instant};
13 use sdl2::event::{Event, WindowEvent};
14 use sdl2::keyboard::Keycode;
15 use sdl2::mouse::MouseButton;
16 use sdl2::render::{Canvas, Texture, TextureCreator};
17 use sdl2::pixels::PixelFormatEnum;
18 use sdl2::video::{Window, WindowContext};
20 use nihav_registry::detect;
21 use nihav_core::frame::*;
22 use nihav_core::io::byteio::{FileReader, ByteReader};
23 use nihav_core::reorder::*;
24 use nihav_core::codecs::*;
25 use nihav_core::demuxers::*;
26 use nihav_registry::register::*;
27 use nihav_allstuff::*;
36 #[cfg(feature="debug")]
37 macro_rules! debug_log {
38 ($log: expr; $blk: block) => {
39 $log.logfile.write($blk.as_bytes()).unwrap();
40 $log.logfile.write(b"\n").unwrap();
43 #[cfg(not(feature="debug"))]
44 macro_rules! debug_log {
45 ($log: expr; $blk: block) => {};
48 pub enum PktSendEvent {
57 pub enum DecoderType {
58 Audio(Box<dyn NADecoder + Send>),
59 Video(Box<dyn NADecoder + Send>, Box<dyn FrameReorderer + Send>),
60 VideoMT(Box<dyn NADecoderMT + Send>, MTFrameReorderer),
63 pub struct DecoderStuff {
64 pub dsupp: Box<NADecoderSupport>,
68 fn format_time(ms: u64) -> String {
70 let ds = (ms % 1000) / 100;
71 let (min, s) = (s / 60, s % 60);
72 let (h, min) = (min / 60, min % 60);
75 format!("{}.{}", s, ds)
77 format!("{}:{:02}.{}", min, s, ds)
80 format!("{}:{:02}:{:02}.{}", h, min, s, ds)
84 const FRAME_QUEUE_LEN: usize = 25;
85 const MAX_VOLUME: usize = 200;
87 pub type FrameRecord = (NABufferType, u64);
97 ref_time: Instant::now(),
101 pub fn get_cur_time(&self) -> u64 {
102 let add = self.ref_time.elapsed().as_millis() as u64;
105 fn reset_ts(&mut self) {
108 fn reset_all(&mut self, ts: u64) {
109 self.ref_time = Instant::now();
112 fn set_ts(&mut self) {
113 self.ref_ts = self.get_cur_time();
115 fn set_time(&mut self) {
116 self.ref_time = Instant::now();
120 pub struct DispFrame<'a> {
124 pub rgb_tex: Texture<'a>,
125 pub yuv_tex: Texture<'a>,
128 pub struct DispQueue<'a> {
129 pub pool: Vec<DispFrame<'a>>,
139 impl<'a> DispQueue<'a> {
140 fn new(texture_creator: &'a TextureCreator<WindowContext>, width: usize, height: usize, len: usize) -> Self {
141 let mut pool = Vec::with_capacity(len);
142 for _ in 0..len + 1 {
143 let rgb_tex = texture_creator.create_texture_streaming(PixelFormatEnum::RGB24, width as u32, height as u32).unwrap();
144 let yuv_tex = texture_creator.create_texture_streaming(PixelFormatEnum::IYUV, ((width + 1) & !1) as u32, ((height + 1) & !1) as u32).unwrap();
145 pool.push(DispFrame{ ts: 0, is_yuv: false, valid: false, rgb_tex, yuv_tex });
147 pool[len].is_yuv = false;
148 pool[len].rgb_tex.with_lock(None, |buffer: &mut [u8], _pitch: usize| {
149 for el in buffer.iter_mut() { *el = 0; }
152 Self { pool, first_ts: 0, last_ts: 0, start: 0, end: 0, len, width, height }
155 fn flush(&mut self) {
160 for frm in self.pool.iter_mut() {
165 fn get_last_texture(&mut self, osd: &OSD) -> &Texture<'a> {
166 if self.pool[self.len].is_yuv {
168 self.pool[self.len].yuv_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
169 osd.draw_yuv(buffer, pitch);
172 &self.pool[self.len].yuv_tex
175 self.pool[self.len].rgb_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
176 osd.draw_rgb(buffer, pitch);
179 &self.pool[self.len].rgb_tex
182 pub fn is_empty(&self) -> bool { self.start == self.end }
183 pub fn is_full(&self) -> bool { self.len == 0 || self.start == (self.end + 1) % self.len }
184 pub fn move_end(&mut self) {
186 if self.end >= self.len {
187 self.end -= self.len;
190 pub fn move_start(&mut self) {
191 self.pool.swap(self.start, self.len);
193 if self.start >= self.len {
194 self.start -= self.len;
196 if !self.is_empty() {
197 self.first_ts = self.pool[self.start].ts;
202 fn try_display(disp_queue: &mut DispQueue, canvas: &mut Canvas<Window>, osd: &mut OSD, ctime: &TimeKeep) -> Option<u64> {
203 while !disp_queue.is_empty() {
204 let disp_time = disp_queue.first_ts;
205 let ctime = ctime.get_cur_time();
206 if disp_time > ctime + 10 {
207 return Some(disp_time - ctime);
208 } else if disp_time + 10 < ctime {
209 disp_queue.move_start();
214 let frm = &mut disp_queue.pool[disp_queue.start];
215 let texture = if frm.is_yuv {
217 frm.yuv_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
218 osd.draw_yuv(buffer, pitch);
224 frm.rgb_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
225 osd.draw_rgb(buffer, pitch);
231 canvas.copy(texture, None, None).unwrap();
234 disp_queue.move_start();
235 if !disp_queue.is_empty() {
236 return Some((disp_queue.first_ts - ctime).saturating_sub(2));
246 sdl_context: sdl2::Sdl,
247 vsystem: sdl2::VideoSubsystem,
248 asystem: sdl2::AudioSubsystem,
250 acontrol: AudioControl,
251 vcontrol: VideoControl,
273 #[cfg(feature="debug")]
279 let sdl_context = sdl2::init().unwrap();
280 let vsystem = sdl_context.video().unwrap();
281 let asystem = sdl_context.audio().unwrap();
282 vsystem.disable_screen_saver();
283 let acontrol = AudioControl::new(None, None, &asystem);
284 let vcontrol = VideoControl::new(None, 0, 0, 0, 0);
286 sdl_context, asystem, vsystem,
305 tkeep: TimeKeep::new(),
310 #[cfg(feature="debug")]
311 logfile: File::create("debug.log").unwrap(),
314 fn seek(&mut self, off: u64, fwd: bool, dmx: &mut Demuxer, disp_queue: &mut DispQueue) {
315 let cur_time = self.tkeep.get_cur_time();
316 let seektime = if fwd { cur_time + off * 1000 } else {
317 cur_time.saturating_sub(off * 1000) };
318 debug_log!(self; {format!(" seek to {}", seektime)});
320 let ret = dmx.seek(NATimePoint::Milliseconds(seektime));
322 println!(" seek error");
326 self.acontrol.flush();
327 self.vcontrol.flush();
330 self.tkeep.reset_ts();
331 self.prefill(dmx, disp_queue);
332 if !disp_queue.is_empty() {
333 self.tkeep.reset_all(disp_queue.first_ts);
335 let mut iterations = 0;
336 let mut time = self.acontrol.get_time();
337 while time.is_none() {
339 std::thread::yield_now();
340 if iterations > 1000000 { println!(" still no time set?!"); break; }
341 time = self.acontrol.get_time();
343 if let Some(time) = time {
344 self.tkeep.reset_all(time);
348 self.acontrol.resume();
351 fn prefill(&mut self, dmx: &mut Demuxer, disp_queue: &mut DispQueue) {
352 debug_log!(self; {" prefilling"});
353 while self.vcontrol.get_queue_size() < FRAME_QUEUE_LEN {
354 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()));
356 if !self.vcontrol.try_send_queued() && self.vcontrol.get_queue_size() > FRAME_QUEUE_LEN / 2 {
359 if !self.acontrol.try_send_queued() && self.acontrol.get_queue_size() > FRAME_QUEUE_LEN / 2 {
363 match dmx.get_frame() {
364 Err(DemuxerError::EOF) => break,
367 let streamno = pkt.get_stream().get_id();
368 if self.has_video && streamno == self.video_str {
369 self.vcontrol.try_send_video(PktSendEvent::Packet(pkt));
370 } else if self.has_audio && streamno == self.audio_str {
371 self.acontrol.try_send_audio(PktSendEvent::Packet(pkt));
376 self.vcontrol.fill(disp_queue);
383 while self.vcontrol.get_queue_size() > 0 && !disp_queue.is_full() {
384 self.vcontrol.try_send_queued();
385 self.vcontrol.fill(disp_queue);
386 std::thread::sleep(Duration::from_millis(10));
388 self.vcontrol.wait_for_frames();
389 self.vcontrol.fill(disp_queue);
391 debug_log!(self; {format!(" prefilling done, frames {}-{} audio {}", disp_queue.start, disp_queue.end, self.acontrol.get_fill())});
393 fn toggle_pause(&mut self) {
394 self.paused = !self.paused;
396 self.vsystem.enable_screen_saver();
399 self.vsystem.disable_screen_saver();
400 self.tkeep.set_time();
403 self.acontrol.pause();
405 self.acontrol.resume();
408 fn handle_events(&mut self, event_pump: &mut sdl2::EventPump, canvas: &mut Canvas<Window>, dmx: &mut Demuxer, disp_queue: &mut DispQueue) -> bool {
409 for event in event_pump.poll_iter() {
410 if let Event::Quit {..} = event {
415 if let Event::Window {win_event: WindowEvent::Exposed, ..} = event {
417 canvas.copy(disp_queue.get_last_texture(&self.osd), None, None).unwrap();
420 if let Event::MouseButtonDown {mouse_btn: MouseButton::Right, ..} = event {
423 if let Event::KeyDown {keycode: Some(keycode), ..} = event {
425 Keycode::Escape | Keycode::Q => {
430 Keycode::Return => return true,
431 Keycode::Right => { self.seek(10, true, dmx, disp_queue); },
432 Keycode::Left => { self.seek(10, false, dmx, disp_queue); },
433 Keycode::Up => { self.seek(60, true, dmx, disp_queue); },
434 Keycode::Down => { self.seek(60, false, dmx, disp_queue); },
435 Keycode::PageUp => { self.seek(600, true, dmx, disp_queue); },
436 Keycode::PageDown => { self.seek(600, false, dmx, disp_queue); },
437 Keycode::Space => { self.toggle_pause(); },
438 Keycode::Plus | Keycode::KpPlus => {
439 self.volume = (self.volume + 10).min(MAX_VOLUME);
441 self.acontrol.set_volume(self.volume);
444 Keycode::Minus | Keycode::KpMinus => {
445 self.volume = self.volume.saturating_sub(10);
447 self.acontrol.set_volume(self.volume);
451 self.debug = !self.debug;
454 self.mute = !self.mute;
456 self.acontrol.set_volume(0);
458 self.acontrol.set_volume(self.volume);
462 self.vcontrol.try_send_video(PktSendEvent::HurryUp);
470 print!("{:60}\r", ' ');
471 std::io::stdout().flush().unwrap();
477 fn play(&mut self, name: &str, start_time: NATimePoint) {
478 debug_log!(self; {format!("Playing {}", name)});
480 // prepare data source
481 let path = Path::new(name);
482 let mut file = File::open(path).unwrap();
484 let mut fr = FileReader::new_read(&mut file);
485 let mut br = ByteReader::new(&mut fr);
486 let res = detect::detect_format(name, &mut br);
488 println!("cannot detect format for {}", name);
491 let (dmx_name, _score) = res.unwrap();
492 debug_log!(self; {format!(" found demuxer {} with score {:?}", dmx_name, _score)});
493 println!("trying demuxer {} on {}", dmx_name, name);
495 let mut dmx_reg = RegisteredDemuxers::new();
496 nihav_register_all_demuxers(&mut dmx_reg);
497 let mut dec_reg = RegisteredDecoders::new();
498 nihav_register_all_decoders(&mut dec_reg);
499 let mut mtdec_reg = RegisteredMTDecoders::new();
501 nihav_register_all_mt_decoders(&mut mtdec_reg);
504 let ret = dmx_reg.find_demuxer(dmx_name);
506 println!("error finding {} demuxer", dmx_name);
509 dmx_fact = ret.unwrap();
510 br.seek(SeekFrom::Start(0)).unwrap();
511 let ret = create_demuxer(dmx_fact, &mut br);
513 println!("error creating demuxer");
516 let mut dmx = ret.unwrap();
517 if start_time != NATimePoint::None {
518 debug_log!(self; {format!(" start seek to {}", start_time)});
519 if dmx.seek(start_time).is_err() {
520 println!("initial seek failed");
525 let mut height = 480;
528 let mut ainfo: Option<NAAudioInfo> = None;
530 let mut video_dec: Option<DecoderStuff> = None;
531 let mut audio_dec: Option<DecoderStuff> = None;
533 let duration = dmx.get_duration();
535 println!(" total duration {}", format_time(duration));
537 self.has_video = false;
538 self.has_audio = false;
539 for i in 0..dmx.get_num_streams() {
540 let s = dmx.get_stream(i).unwrap();
541 let info = s.get_info();
542 let decfunc = dec_reg.find_decoder(info.get_name());
543 let decfunc_mt = mtdec_reg.find_decoder(info.get_name());
544 println!("stream {} - {} {}", i, s, info.get_name());
545 debug_log!(self; {format!(" stream {} - {} {}", i, s, info.get_name())});
546 let str_id = s.get_id();
548 if video_dec.is_none() && self.play_video {
549 if let Some(decfunc) = decfunc_mt {
550 let mut dec = (decfunc)();
551 let mut dsupp = Box::new(NADecoderSupport::new());
552 let props = info.get_properties().get_video_info().unwrap();
553 if props.get_width() != 0 {
554 width = props.get_width();
555 height = props.get_height();
557 if dec.init(&mut dsupp, info.clone(), self.vthreads).is_ok() {
558 video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::VideoMT(dec, MTFrameReorderer::new()) });
559 self.video_str = str_id;
560 let (tbn, tbd) = s.get_timebase();
563 self.has_video = true;
566 println!("failed to create multi-threaded decoder, falling back");
569 if let Some(decfunc) = decfunc {
570 let mut dec = (decfunc)();
571 let mut dsupp = Box::new(NADecoderSupport::new());
572 let props = info.get_properties().get_video_info().unwrap();
573 if props.get_width() != 0 {
574 width = props.get_width();
575 height = props.get_height();
577 let desc = get_codec_description(info.get_name());
578 let (reorder_depth, reord) = if desc.is_none() || (desc.unwrap().caps & CODEC_CAP_COMPLEX_REORDER) == 0 {
579 let reord: Box<dyn FrameReorderer + Send> = Box::new(IPBReorderer::new());
582 let reord: Box<dyn FrameReorderer + Send> = Box::new(ComplexReorderer::new());
585 dsupp.pool_u8 = NAVideoBufferPool::new(reorder_depth);
586 dsupp.pool_u16 = NAVideoBufferPool::new(reorder_depth);
587 dsupp.pool_u32 = NAVideoBufferPool::new(reorder_depth);
588 dec.init(&mut dsupp, info).unwrap();
589 video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Video(dec, reord) });
590 self.video_str = str_id;
591 let (tbn, tbd) = s.get_timebase();
594 self.has_video = true;
596 println!("no video decoder for {} found!", info.get_name());
599 } else if info.is_audio() {
600 if audio_dec.is_none() && self.play_audio {
601 if let Some(decfunc) = decfunc {
602 let mut dec = (decfunc)();
603 let mut dsupp = Box::new(NADecoderSupport::new());
604 ainfo = info.get_properties().get_audio_info();
605 dec.init(&mut dsupp, info).unwrap();
606 audio_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Audio(dec) });
607 self.audio_str = str_id;
608 self.has_audio = true;
610 println!("no audio decoder for {} found!", info.get_name());
614 println!("decoder {} not found", info.get_name());
617 if !self.has_video && !self.has_audio {
618 println!("No playable streams found.");
622 while (width <= 384) && (height <= 288) {
627 // prepare playback structure
628 let mut new_vcontrol = VideoControl::new(video_dec, width, height, tb_num, tb_den);
629 std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
631 let mut new_acontrol = AudioControl::new(audio_dec, ainfo, &self.asystem);
632 std::mem::swap(&mut self.acontrol, &mut new_acontrol);
635 self.acontrol.set_volume(0);
637 self.acontrol.set_volume(self.volume);
640 let fname = path.file_name();
641 let wname = if let Some(fname) = fname {
642 "NihAV player - ".to_owned() + fname.to_str().unwrap()
644 "NihAV player".to_owned()
646 let window = self.vsystem.window(&wname, width as u32, height as u32)
647 .position_centered().build().unwrap();
648 let mut canvas = window.into_canvas().build().unwrap();
649 let texture_creator = canvas.texture_creator();
650 let mut disp_q = DispQueue::new(&texture_creator, width, height, if self.has_video { FRAME_QUEUE_LEN } else { 0 });
653 canvas.copy(disp_q.get_last_texture(&self.osd), None, None).unwrap();
657 self.has_audio = self.acontrol.has_audio();
658 if !self.has_video && !self.has_audio {
659 println!("No playable streams.");
664 self.prefill(&mut dmx, &mut disp_q);
665 self.tkeep.reset_all(0);
667 self.acontrol.resume();
669 let mut event_pump = self.sdl_context.event_pump().unwrap();
670 let mut last_disp = Instant::now();
671 let mut has_data = true;
673 if self.handle_events(&mut event_pump, &mut canvas, &mut dmx, &mut disp_q) {
678 let mut try_send = self.acontrol.get_queue_size() < FRAME_QUEUE_LEN && self.vcontrol.get_queue_size() < FRAME_QUEUE_LEN;
679 if !self.vcontrol.try_send_queued() && self.vcontrol.is_filled(FRAME_QUEUE_LEN) {
682 if !self.acontrol.try_send_queued() {
685 while has_data && try_send {
686 match dmx.get_frame() {
687 Err(DemuxerError::EOF) => {
688 self.vcontrol.try_send_video(PktSendEvent::End);
689 self.acontrol.try_send_audio(PktSendEvent::End);
692 Err(err) => { println!("demuxer error {:?}", err); },
694 let streamno = pkt.get_stream().get_id();
695 if self.has_video && streamno == self.video_str {
696 debug_log!(self; {" sending video packet"});
697 self.vcontrol.try_send_video(PktSendEvent::Packet(pkt));
698 if self.vcontrol.is_filled(FRAME_QUEUE_LEN) {
701 } else if self.has_audio && streamno == self.audio_str {
702 debug_log!(self; {" sending audio packet"});
703 self.acontrol.try_send_audio(PktSendEvent::Packet(pkt));
704 if self.acontrol.get_queue_size() >= FRAME_QUEUE_LEN {
711 self.vcontrol.fill(&mut disp_q);
712 let mut sleep_time = 25;
713 debug_log!(self; {format!(" time {}", self.tkeep.get_cur_time())});
715 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())});
716 let ret = try_display(&mut disp_q, &mut canvas, &mut self.osd, &self.tkeep);
717 if let Some(next_time) = ret {
718 sleep_time = sleep_time.min(next_time);
722 let time_left = self.acontrol.get_time_left();
723 debug_log!(self; {format!(" audio left {}", time_left)});
724 sleep_time = sleep_time.min(time_left);
726 debug_log!(self; {format!(" sleep {}ms", sleep_time)});
727 if last_disp.elapsed().as_millis() >= 10 {
728 let c_time = self.tkeep.get_cur_time();
731 print!(" {} {}% \r", format_time(c_time), self.acontrol.get_volume());
733 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());
735 std::io::stdout().flush().unwrap();
736 last_disp = Instant::now();
739 if self.has_video && !self.vcontrol.is_video_end() {
742 if self.has_audio && !self.acontrol.is_audio_end() {
748 thread::sleep(Duration::from_millis(sleep_time));
750 thread::sleep(Duration::from_millis(20));
754 std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
755 new_vcontrol.finish();
756 std::mem::swap(&mut self.acontrol, &mut new_acontrol);
757 new_acontrol.finish();
762 let args: Vec<String> = env::args().collect();
765 println!("usage: nihav-player file1 file2 ...");
769 let mut player = Player::new();
771 let mut aiter = args.iter().skip(1);
772 let mut seek_time = NATimePoint::None;
773 while let Some(arg) = aiter.next() {
775 "-an" => { player.play_audio = false; },
776 "-ae" => { player.play_audio = true; },
777 "-vn" => { player.play_video = false; },
778 "-ve" => { player.play_video = true; },
780 if let Some(arg) = aiter.next() {
781 if let Ok(time) = arg.parse::<NATimePoint>() {
784 println!("wrong seek time");
785 seek_time = NATimePoint::None;
790 if let Some(arg) = aiter.next() {
791 if let Ok(vol) = arg.parse::<usize>() {
792 player.volume = vol.min(MAX_VOLUME);
794 println!("wrong volume");
802 player.debug = false;
805 player.use_mt = true;
808 player.use_mt = false;
811 if let Some(arg) = aiter.next() {
812 if let Ok(val) = arg.parse::<usize>() {
813 player.vthreads = val.max(1);
815 println!("wrong number of threads");
820 player.play(arg, seek_time);
821 if player.end { break; }
822 seek_time = NATimePoint::None;