diff --git a/Cargo.lock b/Cargo.lock index d322459..1ba1a01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -880,9 +880,9 @@ checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" [[package]] name = "libmpv2" -version = "4.1.0" +version = "5.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0c3802b4bb1a18adbf5659b078ce24cb8e16c79ff695557f4e10af2a44722a" +checksum = "88dc8432d3de09ed6e343488d6b208467db6801fb41971a1f726fa2248302fe5" dependencies = [ "libmpv2-sys", ] @@ -1598,7 +1598,7 @@ dependencies = [ [[package]] name = "stremio-shell-ng" -version = "5.0.20" +version = "5.0.21" dependencies = [ "anyhow", "bitflags 2.4.2", diff --git a/Cargo.toml b/Cargo.toml index 2321a28..26a46ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ winapi = { version = "0.3.9", features = [ ] } webview2 = "0.1.4" webview2-sys = "0.1.1" -libmpv2 = "4.1.0" +libmpv2 = "5.0.3" libmpv2-sys = "4.0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/src/stremio_app/stremio_player/communication.rs b/src/stremio_app/stremio_player/communication.rs index 9c5d90a..02204d7 100644 --- a/src/stremio_app/stremio_player/communication.rs +++ b/src/stremio_app/stremio_player/communication.rs @@ -30,7 +30,6 @@ impl PlayerProprChange { serde_json::Value::String(s.to_string()) } } - PropertyData::Node(_) => unimplemented!("`PropertyData::Node` is not supported"), } } pub fn from_name_value(name: String, value: PropertyData) -> Self { diff --git a/src/stremio_app/stremio_player/player.rs b/src/stremio_app/stremio_player/player.rs index 06501b5..ee234cd 100644 --- a/src/stremio_app/stremio_player/player.rs +++ b/src/stremio_app/stremio_player/player.rs @@ -1,9 +1,10 @@ use crate::stremio_app::ipc; use crate::stremio_app::RPCResponse; use flume::{Receiver, Sender}; -use libmpv2::{events::Event, events::EventContext, Format, Mpv, SetData}; +use libmpv2::{events::Event, Format, Mpv, SetData}; use native_windows_gui::{self as nwg, PartialUi}; use std::{ + ptr::NonNull, sync::Arc, thread::{self, JoinHandle}, }; @@ -19,6 +20,18 @@ struct ObserveProperty { format: Format, } +// Wakes up the event thread blocked in `mpv_wait_event` on the sub-client. +// `mpv_wakeup` is documented as safe to call from any thread. +#[derive(Clone, Copy)] +struct EventClientWakeup(NonNull); +unsafe impl Send for EventClientWakeup {} +unsafe impl Sync for EventClientWakeup {} +impl EventClientWakeup { + fn wake_up(&self) { + unsafe { libmpv2_sys::mpv_wakeup(self.0.as_ptr()) } + } +} + #[derive(Default)] pub struct Player { pub channel: ipc::Channel, @@ -43,21 +56,26 @@ impl PartialUi for Player { let (observe_property_sender, observe_property_receiver) = flume::unbounded(); data.channel = ipc::Channel::new(Some((in_msg_sender, rpc_response_receiver))); - let mpv = create_shareable_mpv(window_handle); + let mpv = Arc::new(create_mpv(window_handle)); + let mpv_event_client = mpv + .create_client(None) + .expect("cannot create MPV event client"); + let event_wakeup = EventClientWakeup(mpv_event_client.ctx); let _event_thread = create_event_thread( - Arc::clone(&mpv), + mpv_event_client, observe_property_receiver, rpc_response_sender, ); - let _message_thread = create_message_thread(mpv, observe_property_sender, in_msg_receiver); + let _message_thread = + create_message_thread(mpv, event_wakeup, observe_property_sender, in_msg_receiver); // @TODO implement a mechanism to stop threads on `Player` drop if needed Ok(()) } } -fn create_shareable_mpv(window_handle: HWND) -> Arc { +fn create_mpv(window_handle: HWND) -> Mpv { let mpv = Mpv::with_initializer(|initializer| { macro_rules! set_property { ($name:literal, $value:expr) => { @@ -79,17 +97,16 @@ fn create_shareable_mpv(window_handle: HWND) -> Arc { // set_property!("vo", "gpu-next,"); Ok(()) }); - Arc::new(mpv.expect("cannot build MPV")) + mpv.expect("cannot build MPV") } fn create_event_thread( - mpv: Arc, + mut mpv_event_client: Mpv, observe_property_receiver: Receiver, rpc_response_sender: Sender, ) -> JoinHandle<()> { thread::spawn(move || { - let mut event_context = EventContext::new(mpv.ctx); - event_context + mpv_event_client .disable_deprecated_events() .expect("failed to disable deprecated MPV events"); @@ -97,13 +114,13 @@ fn create_event_thread( loop { for ObserveProperty { name, format } in observe_property_receiver.drain() { - event_context + mpv_event_client .observe_property(&name, format, 0) .expect("failed to observer MPV property"); } // -1.0 means to block and wait for an event. - let event = match event_context.wait_event(-1.) { + let event = match mpv_event_client.wait_event(-1.) { Some(Ok(event)) => event, Some(Err(error)) => { eprintln!("Event errored: {error:?}"); @@ -141,6 +158,7 @@ fn create_event_thread( fn create_message_thread( mpv: Arc, + event_wakeup: EventClientWakeup, observe_property_sender: Sender, in_msg_receiver: Receiver, ) -> JoinHandle<()> { @@ -151,7 +169,7 @@ fn create_message_thread( observe_property_sender .send(ObserveProperty { name, format }) .expect("cannot send ObserveProperty"); - mpv.wake_up(); + event_wakeup.wake_up(); }; let send_command = |cmd: CmdVal| { @@ -252,13 +270,3 @@ fn create_message_thread( }) } -trait MpvExt { - fn wake_up(&self); -} - -impl MpvExt for Mpv { - // @TODO create a PR to the `libmpv` crate and then remove `libmpv-sys` from Cargo.toml? - fn wake_up(&self) { - unsafe { libmpv2_sys::mpv_wakeup(self.ctx.as_ptr()) } - } -}