video: make thread safe

This commit is contained in:
jazzfool 2024-11-03 01:17:02 +11:00
parent 68973748fd
commit 0250a2619b
2 changed files with 40 additions and 30 deletions

View file

@ -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<Internal>);
pub struct Video(pub(crate) RwLock<Internal>);
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<Target = Internal> + '_ {
self.0.read().expect("lock")
}
pub(crate) fn write(&self) -> impl DerefMut<Target = Internal> + '_ {
self.0.write().expect("lock")
}
pub(crate) fn get_mut(&mut self) -> impl DerefMut<Target = Internal> + '_ {
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<Position>, 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::<gst::ClockTime>()
.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> {
url::Url::parse(&self.0.borrow().source.property::<String>("suburi")).ok()
url::Url::parse(&self.read().source.property::<String>("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

View file

@ -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()) {