From 0250a2619bed3bf45dc5f4b72f2fb1a43555663e Mon Sep 17 00:00:00 2001 From: jazzfool Date: Sun, 3 Nov 2024 01:17:02 +1100 Subject: [PATCH] video: make thread safe --- src/video.rs | 66 ++++++++++++++++++++++++++------------------- src/video_player.rs | 4 +-- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/video.rs b/src/video.rs index 6b6b8a9..c7de2b0 100644 --- a/src/video.rs +++ b/src/video.rs @@ -3,10 +3,10 @@ use gstreamer as gst; use gstreamer_app as gst_app; use gstreamer_app::prelude::*; use iced::widget::image as img; -use std::cell::RefCell; use std::num::NonZeroU8; +use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; /// Position in the media. @@ -176,11 +176,11 @@ impl Internal { /// A multimedia video loaded from a URI (e.g., a local file path or HTTP stream). #[derive(Debug)] -pub struct Video(pub(crate) RefCell); +pub struct Video(pub(crate) RwLock); impl Drop for Video { fn drop(&mut self) { - let inner = self.0.get_mut(); + let inner = self.0.get_mut().expect("failed to lock"); inner .source @@ -368,7 +368,7 @@ impl Video { } }); - Ok(Video(RefCell::new(Internal { + Ok(Video(RwLock::new(Internal { id, bus: pipeline.bus().unwrap(), @@ -397,14 +397,26 @@ impl Video { }))) } + pub(crate) fn read(&self) -> impl Deref + '_ { + self.0.read().expect("lock") + } + + pub(crate) fn write(&self) -> impl DerefMut + '_ { + self.0.write().expect("lock") + } + + pub(crate) fn get_mut(&mut self) -> impl DerefMut + '_ { + self.0.get_mut().expect("lock") + } + /// Get the size/resolution of the video as `(width, height)`. pub fn size(&self) -> (i32, i32) { - (self.0.borrow().width, self.0.borrow().height) + (self.read().width, self.read().height) } /// Get the framerate of the video as frames per second. pub fn framerate(&self) -> f64 { - self.0.borrow().framerate + self.read().framerate } /// Set the volume multiplier of the audio. @@ -412,74 +424,72 @@ impl Video { /// /// This uses a linear scale, for example `0.5` is perceived as half as loud. pub fn set_volume(&mut self, volume: f64) { - self.0.get_mut().source.set_property("volume", volume); + self.get_mut().source.set_property("volume", volume); self.set_muted(self.muted()); // for some reason gstreamer unmutes when changing volume? } /// Get the volume multiplier of the audio. pub fn volume(&self) -> f64 { - self.0.borrow().source.property("volume") + self.read().source.property("volume") } /// Set if the audio is muted or not, without changing the volume. pub fn set_muted(&mut self, muted: bool) { - self.0.get_mut().source.set_property("mute", muted); + self.get_mut().source.set_property("mute", muted); } /// Get if the audio is muted or not. pub fn muted(&self) -> bool { - self.0.borrow().source.property("mute") + self.read().source.property("mute") } /// Get if the stream ended or not. pub fn eos(&self) -> bool { - self.0.borrow().is_eos + self.read().is_eos } /// Get if the media will loop or not. pub fn looping(&self) -> bool { - self.0.borrow().looping + self.read().looping } /// Set if the media will loop or not. pub fn set_looping(&mut self, looping: bool) { - self.0.get_mut().looping = looping; + self.get_mut().looping = looping; } /// Set if the media is paused or not. pub fn set_paused(&mut self, paused: bool) { - let inner = self.0.get_mut(); - inner.set_paused(paused); + self.get_mut().set_paused(paused) } /// Get if the media is paused or not. pub fn paused(&self) -> bool { - self.0.borrow().paused() + self.read().paused() } /// Jumps to a specific position in the media. /// Passing `true` to the `accurate` parameter will result in more accurate seeking, /// however, it is also slower. For most seeks (e.g., scrubbing) this is not needed. pub fn seek(&mut self, position: impl Into, accurate: bool) -> Result<(), Error> { - self.0.get_mut().seek(position, accurate) + self.get_mut().seek(position, accurate) } /// Set the playback speed of the media. /// The default speed is `1.0`. pub fn set_speed(&mut self, speed: f64) -> Result<(), Error> { - self.0.get_mut().set_speed(speed) + self.get_mut().set_speed(speed) } /// Get the current playback speed. pub fn speed(&self) -> f64 { - self.0.borrow().speed + self.read().speed } /// Get the current playback position in time. pub fn position(&self) -> Duration { Duration::from_nanos( - self.0 - .borrow() + self.read() .source .query_position::() .map_or(0, |pos| pos.nseconds()), @@ -488,18 +498,18 @@ impl Video { /// Get the media duration. pub fn duration(&self) -> Duration { - self.0.borrow().duration + self.read().duration } /// Restarts a stream; seeks to the first frame and unpauses, sets the `eos` flag to false. pub fn restart_stream(&mut self) -> Result<(), Error> { - self.0.get_mut().restart_stream() + self.get_mut().restart_stream() } /// Set the subtitle URL to display. pub fn set_subtitle_url(&mut self, url: &url::Url) -> Result<(), Error> { let paused = self.paused(); - let inner = self.0.get_mut(); + let mut inner = self.get_mut(); inner.source.set_state(gst::State::Ready)?; inner.source.set_property("suburi", url.as_str()); inner.set_paused(paused); @@ -508,12 +518,12 @@ impl Video { /// Get the current subtitle URL. pub fn subtitle_url(&self) -> Option { - url::Url::parse(&self.0.borrow().source.property::("suburi")).ok() + url::Url::parse(&self.read().source.property::("suburi")).ok() } /// Get the underlying GStreamer pipeline. pub fn pipeline(&self) -> gst::Pipeline { - self.0.borrow().source.clone() + self.read().source.clone() } /// Generates a list of thumbnails based on a set of positions in the media, downscaled by a given factor. @@ -538,7 +548,7 @@ impl Video { self.set_muted(true); let out = { - let inner = self.0.borrow(); + let inner = self.read(); let width = inner.width; let height = inner.height; positions diff --git a/src/video_player.rs b/src/video_player.rs index c5b760c..2d8e44f 100644 --- a/src/video_player.rs +++ b/src/video_player.rs @@ -156,7 +156,7 @@ where _cursor: advanced::mouse::Cursor, _viewport: &iced::Rectangle, ) { - let mut inner = self.video.0.borrow_mut(); + let mut inner = self.video.write(); // bounds based on `Image::draw` let image_size = iced::Size::new(inner.width as f32, inner.height as f32); @@ -215,7 +215,7 @@ where shell: &mut advanced::Shell<'_, Message>, _viewport: &iced::Rectangle, ) -> Status { - let mut inner = self.video.0.borrow_mut(); + let mut inner = self.video.write(); if let iced::Event::Window(iced::window::Event::RedrawRequested(_)) = event { if inner.restart_stream || (!inner.is_eos && !inner.paused()) {