Bump libmpv2 crate from 4.1.0 to 5.0.3

The 4.x -> 5.x release folded `EventContext` into `Mpv` and made
`wait_event` take `&mut self`, so the previous design (a single
`Arc<Mpv>` shared between the message and event threads, with the event
thread constructing its own `EventContext` from `mpv.ctx`) no longer
compiles.

Migration:
- Build the main `Mpv` (with `wid`, `title`, etc.) and use it from the
  message thread for commands and `set_property`.
- `create_client(None)` a sub-client `Mpv`, owned by the event thread,
  which calls `disable_deprecated_events`/`observe_property`/`wait_event`
  on itself. Global events (`EndFile`, `Shutdown`) and property changes
  observed by this client land in its event queue.
- Replace the old `MpvExt::wake_up` (which woke the main `Mpv`) with an
  `EventClientWakeup` holding the sub-client's `mpv_handle` pointer, so
  the message thread can interrupt the event thread's blocking
  `wait_event` after pushing a new observe request onto the channel.

Also removed the `PropertyData::Node(_)` match arm in
`communication.rs`: 5.0.0 dropped `mpv_node` support, so the variant no
longer exists and the match is exhaustive without it.

https://claude.ai/code/session_01CuGHFREBZFkf8tfaiRV4K6
This commit is contained in:
Claude 2026-05-05 07:59:26 +00:00
parent e199d7f38c
commit 9b057a0030
No known key found for this signature in database
4 changed files with 34 additions and 27 deletions

6
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

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

View file

@ -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<libmpv2_sys::mpv_handle>);
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<Mpv> {
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<Mpv> {
// set_property!("vo", "gpu-next,");
Ok(())
});
Arc::new(mpv.expect("cannot build MPV"))
mpv.expect("cannot build MPV")
}
fn create_event_thread(
mpv: Arc<Mpv>,
mut mpv_event_client: Mpv,
observe_property_receiver: Receiver<ObserveProperty>,
rpc_response_sender: Sender<String>,
) -> 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<Mpv>,
event_wakeup: EventClientWakeup,
observe_property_sender: Sender<ObserveProperty>,
in_msg_receiver: Receiver<String>,
) -> 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()) }
}
}