try to improve state handling in decoding threads
[nihav-player.git] / videoplayer / src / videodec.rs
index ac60466be8fa4827f78b964bcf40f14cb5d1a814..1a34aad80d221bf691d0ef3b7730453fbb386f51 100644 (file)
@@ -1,5 +1,4 @@
 use std::thread::JoinHandle;
-use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::{Receiver, SyncSender, TrySendError};
 use std::thread;
 
@@ -10,11 +9,9 @@ use nihav_core::formats::*;
 use nihav_core::codecs::*;
 use nihav_core::scale::*;
 
-use super::{DecoderStuff, DecoderType, DispQueue, FrameRecord, PktSendEvent, FRAME_QUEUE_LEN};
+use super::{DecoderStuff, DecoderType, DecoderState, DecodingState, DispQueue, FrameRecord, PktSendEvent, FRAME_QUEUE_LEN};
 
-static SKIP_VDECODING: AtomicBool = AtomicBool::new(false);
-static VIDEO_END: AtomicBool = AtomicBool::new(false);
-static GET_FRAMES_END: AtomicBool = AtomicBool::new(false);
+static VDEC_STATE: DecoderState = DecoderState::new();
 
 pub const FRAME_QUEUE_SIZE: usize = 25;
 
@@ -69,7 +66,7 @@ impl VideoDecoder {
         let mut opic = if let ColorModel::YUV(_) = self.ifmt.get_format().get_model() {
                 self.yuv_pool.prealloc_video(self.oinfo_yuv, 2).unwrap();
                 while self.yuv_pool.get_free().is_none() {
-                    if SKIP_VDECODING.load(Ordering::Relaxed) {
+                    if VDEC_STATE.is_flushing() {
                         return None;
                     }
                     std::thread::yield_now();
@@ -78,7 +75,7 @@ impl VideoDecoder {
             } else {
                 self.rgb_pool.prealloc_video(self.oinfo_rgb, 0).unwrap();
                 while self.rgb_pool.get_free().is_none() {
-                    if SKIP_VDECODING.load(Ordering::Relaxed) {
+                    if VDEC_STATE.is_flushing() {
                         return None;
                     }
                     std::thread::yield_now();
@@ -225,13 +222,13 @@ impl VideoDecoder {
 
 fn start_video_decoding(width: usize, height: usize, tb_num: u32, tb_den: u32, video_dec: DecoderStuff, vprecv: Receiver<PktSendEvent>, vfsend: SyncSender<(NABufferType, u64)>) -> JoinHandle<()> {
     std::thread::Builder::new().name("vdecoder".to_string()).spawn(move ||{
-            SKIP_VDECODING.store(false, Ordering::Relaxed);
+            VDEC_STATE.set_state(DecodingState::Waiting);
             let mut vdec = VideoDecoder::new(width, height, tb_num, tb_den, video_dec);
             let mut skip_mode = FrameSkipMode::None;
             loop {
                 match vprecv.recv() {
                     Ok(PktSendEvent::Packet(pkt)) => {
-                        if !SKIP_VDECODING.load(Ordering::Relaxed) {
+                        if !VDEC_STATE.is_flushing() {
                             if let Some((buf, time)) = vdec.next_frame(&pkt) {
                                 vfsend.send((buf, time)).unwrap();
                             }
@@ -244,11 +241,11 @@ fn start_video_decoding(width: usize, height: usize, tb_num: u32, tb_den: u32, v
                         while let Some((buf, time)) = vdec.more_frames(false) {
                             vfsend.send((buf, time)).unwrap();
                         }
-                        GET_FRAMES_END.store(true, Ordering::Relaxed);
+                        VDEC_STATE.set_state(DecodingState::Waiting);
                     },
                     Ok(PktSendEvent::Flush) => {
                         vdec.flush();
-                        SKIP_VDECODING.store(false, Ordering::Relaxed);
+                        VDEC_STATE.set_state(DecodingState::Waiting);
                     },
                     Ok(PktSendEvent::End) => {
                         while vdec.yuv_pool.get_free().is_some() && vdec.rgb_pool.get_free().is_some() {
@@ -258,11 +255,11 @@ fn start_video_decoding(width: usize, height: usize, tb_num: u32, tb_den: u32, v
                             }
                             vfsend.send(ret.unwrap()).unwrap();
                         }
-                        VIDEO_END.store(true, Ordering::Relaxed);
+                        VDEC_STATE.set_state(DecodingState::End);
                         break;
                     },
                     Ok(PktSendEvent::ImmediateEnd) => {
-                        VIDEO_END.store(true, Ordering::Relaxed);
+                        VDEC_STATE.set_state(DecodingState::End);
                         break;
                     },
                     Ok(PktSendEvent::HurryUp) => {
@@ -335,7 +332,7 @@ impl VideoControl {
         let (vpsend, vprecv) = std::sync::mpsc::sync_channel::<PktSendEvent>(0);
         let (vfsend, vfrecv) = std::sync::mpsc::sync_channel::<FrameRecord>(FRAME_QUEUE_SIZE - 1);
 
-        VIDEO_END.store(false, Ordering::Relaxed);
+        VDEC_STATE.set_state(DecodingState::Normal);
 
         let vthread = if let Some(video_dec) = video_dec {
                 start_video_decoding(width, height, tb_num, tb_den, video_dec, vprecv, vfsend)
@@ -351,6 +348,7 @@ impl VideoControl {
                                 _ => {},
                             };
                         }
+                        VDEC_STATE.set_state(DecodingState::End);
                     }).unwrap()
             };
 
@@ -364,7 +362,7 @@ impl VideoControl {
     }
     pub fn flush(&mut self) {
         self.vqueue.clear();
-        SKIP_VDECODING.store(true, Ordering::Release);
+        VDEC_STATE.set_state(DecodingState::Flush);
         for _ in 0..8 {
             let _ = self.vfrecv.try_recv();
         }
@@ -401,15 +399,22 @@ impl VideoControl {
         true
     }
     pub fn is_video_end(&self) -> bool {
-        VIDEO_END.load(Ordering::Relaxed)
+        matches!(VDEC_STATE.get_state(), DecodingState::End | DecodingState::Error)
     }
-    pub fn wait_for_frames(&mut self) {
-        GET_FRAMES_END.store(false, Ordering::Relaxed);
+    pub fn wait_for_frames(&mut self) -> Result<(), ()> {
+        VDEC_STATE.set_state(DecodingState::Prefetch);
         self.try_send_event(PktSendEvent::GetFrames);
         while !self.try_send_queued() {
         }
-        while !GET_FRAMES_END.load(Ordering::Relaxed) {
-            thread::yield_now();
+        loop {
+            match VDEC_STATE.get_state() {
+                DecodingState::Waiting => {
+                    VDEC_STATE.set_state(DecodingState::Normal);
+                    return Ok(());
+                },
+                DecodingState::Prefetch => thread::yield_now(),
+                _ => return Err(()),
+            };
         }
     }
 
@@ -451,7 +456,7 @@ impl VideoControl {
     }
 
     pub fn finish(self) {
-        SKIP_VDECODING.store(true, Ordering::Release);
+        VDEC_STATE.set_state(DecodingState::Flush);
         for _ in 0..8 {
             let _ = self.vfrecv.try_recv();
         }