libmpv integration

This commit is contained in:
Martin Kavík 2022-04-01 15:19:07 +02:00
parent 4cc69e42b7
commit 81e088800b
4 changed files with 244 additions and 307 deletions

177
Cargo.lock generated
View file

@ -118,15 +118,6 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "enum_primitive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180"
dependencies = [
"num-traits 0.1.43",
]
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.10.9" version = "0.10.9"
@ -140,12 +131,6 @@ dependencies = [
"spin", "spin",
] ]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.17" version = "0.3.17"
@ -222,6 +207,21 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
[[package]]
name = "libmpv"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a58e2d19b34775e81e0fdca194b3b8ee8de973b092e7582b416343979e22e7"
dependencies = [
"libmpv-sys",
]
[[package]]
name = "libmpv-sys"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df938d3145cd8f134572721a27afa3a51f9bc1c26ae30a1d5077162f96d074b"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.5" version = "0.4.5"
@ -231,15 +231,6 @@ dependencies = [
"scopeguard", "scopeguard",
] ]
[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
dependencies = [
"log 0.4.14",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.14" version = "0.4.14"
@ -255,16 +246,6 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "mpv"
version = "0.2.3"
source = "git+https://github.com/Stremio/mpv-rs.git#3ef09f637803711f474edfe2c73a9f7dd35067e2"
dependencies = [
"enum_primitive",
"log 0.3.9",
"num",
]
[[package]] [[package]]
name = "muldiv" name = "muldiv"
version = "0.2.1" version = "0.2.1"
@ -308,84 +289,6 @@ dependencies = [
"winapi-build", "winapi-build",
] ]
[[package]]
name = "num"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits 0.2.14",
]
[[package]]
name = "num-bigint"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1"
dependencies = [
"num-integer",
"num-traits 0.2.14",
"rand",
"rustc-serialize",
]
[[package]]
name = "num-complex"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656"
dependencies = [
"num-traits 0.2.14",
"rustc-serialize",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits 0.2.14",
]
[[package]]
name = "num-iter"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
dependencies = [
"autocfg",
"num-integer",
"num-traits 0.2.14",
]
[[package]]
name = "num-rational"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e"
dependencies = [
"num-bigint",
"num-integer",
"num-traits 0.2.14",
"rustc-serialize",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.14",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.14" version = "0.2.14"
@ -469,7 +372,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a"
dependencies = [ dependencies = [
"num-traits 0.2.14", "num-traits",
"plotters-backend", "plotters-backend",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
@ -532,43 +435,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.5.4" version = "1.5.4"
@ -586,12 +452,6 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"
@ -660,7 +520,8 @@ dependencies = [
"bitflags", "bitflags",
"embed-resource", "embed-resource",
"flume", "flume",
"mpv", "libmpv",
"libmpv-sys",
"native-windows-derive", "native-windows-derive",
"native-windows-gui", "native-windows-gui",
"once_cell", "once_cell",
@ -869,7 +730,7 @@ checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
"log 0.4.14", "log",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",

View file

@ -12,7 +12,8 @@ winapi = { version = "0.3.9", features = [
] } ] }
webview2 = "0.1.0" webview2 = "0.1.0"
webview2-sys = "0.1.0-beta.1" webview2-sys = "0.1.0-beta.1"
mpv = { git = "https://github.com/Stremio/mpv-rs.git" } libmpv = "2.0.1"
libmpv-sys = "3.1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
structopt = "0.3" structopt = "0.3"

View file

@ -2,6 +2,7 @@ use core::convert::TryFrom;
use parse_display::{Display, FromStr}; use parse_display::{Display, FromStr};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use libmpv::{events::PropertyData, EndFileReason, mpv_end_file_reason};
// Responses // Responses
const JSON_RESPONSES: [&str; 3] = ["track-list", "video-params", "metadata"]; const JSON_RESPONSES: [&str; 3] = ["track-list", "video-params", "metadata"];
@ -12,26 +13,27 @@ pub struct PlayerProprChange {
data: serde_json::Value, data: serde_json::Value,
} }
impl PlayerProprChange { impl PlayerProprChange {
fn value_from_format(data: mpv::Format, as_json: bool) -> serde_json::Value { fn value_from_format(data: PropertyData, as_json: bool) -> serde_json::Value {
match data { match data {
mpv::Format::Flag(d) => serde_json::Value::Bool(d), PropertyData::Flag(d) => serde_json::Value::Bool(d),
mpv::Format::Int(d) => serde_json::Value::Number( PropertyData::Int64(d) => serde_json::Value::Number(
serde_json::Number::from_f64(d as f64).expect("MPV returned invalid number"), serde_json::Number::from_f64(d as f64).expect("MPV returned invalid number"),
), ),
mpv::Format::Double(d) => serde_json::Value::Number( PropertyData::Double(d) => serde_json::Value::Number(
serde_json::Number::from_f64(d).expect("MPV returned invalid number"), serde_json::Number::from_f64(d).expect("MPV returned invalid number"),
), ),
mpv::Format::OsdStr(s) => serde_json::Value::String(s.to_string()), PropertyData::OsdStr(s) => serde_json::Value::String(s.to_string()),
mpv::Format::Str(s) => { PropertyData::Str(s) => {
if as_json { if as_json {
serde_json::from_str(s).expect("MPV returned invalid JSON data") serde_json::from_str(s).expect("MPV returned invalid JSON data")
} else { } else {
serde_json::Value::String(s.to_string()) serde_json::Value::String(s.to_string())
} }
} }
PropertyData::Node(_) => unimplemented!("`PropertyData::Node` is not supported"),
} }
} }
pub fn from_name_value(name: String, value: mpv::Format) -> Self { pub fn from_name_value(name: String, value: PropertyData) -> Self {
let is_json = JSON_RESPONSES.contains(&name.as_str()); let is_json = JSON_RESPONSES.contains(&name.as_str());
Self { Self {
name, name,
@ -44,14 +46,14 @@ pub struct PlayerEnded {
reason: String, reason: String,
} }
impl PlayerEnded { impl PlayerEnded {
fn string_from_end_reason(data: mpv::EndFileReason) -> String { fn string_from_end_reason(data: EndFileReason) -> String {
match data { match data {
mpv::EndFileReason::MPV_END_FILE_REASON_ERROR => "error".to_string(), mpv_end_file_reason::Error => "error".to_string(),
mpv::EndFileReason::MPV_END_FILE_REASON_QUIT => "quit".to_string(), mpv_end_file_reason::Quit => "quit".to_string(),
_ => "other".to_string(), _ => "other".to_string(),
} }
} }
pub fn from_end_reason(data: mpv::EndFileReason) -> Self { pub fn from_end_reason(data: EndFileReason) -> Self {
Self { Self {
reason: Self::string_from_end_reason(data), reason: Self::string_from_end_reason(data),
} }

View file

@ -1,14 +1,21 @@
use crate::stremio_app::ipc; use crate::stremio_app::ipc;
use crate::stremio_app::RPCResponse; use crate::stremio_app::RPCResponse;
use flume::{Receiver, Sender};
use libmpv::{Mpv, events::Event, Format, SetData};
use native_windows_gui::{self as nwg, PartialUi}; use native_windows_gui::{self as nwg, PartialUi};
use std::cell::RefCell; use winapi::shared::windef::HWND;
use std::thread; use std::{thread::{self, JoinHandle}, sync::Arc};
use crate::stremio_app::stremio_player::{ use crate::stremio_app::stremio_player::{
InMsg, InMsgArgs, InMsgFn, PlayerEnded, PlayerEvent, PlayerProprChange, PlayerResponse, InMsg, InMsgArgs, InMsgFn, PlayerEnded, PlayerEvent, PlayerProprChange, PlayerResponse,
PropKey, PropVal, PropKey, PropVal, CmdVal,
}; };
struct ObserveProperty {
name: String,
format: Format,
}
#[derive(Default)] #[derive(Default)]
pub struct Player { pub struct Player {
pub channel: ipc::Channel, pub channel: ipc::Channel,
@ -16,147 +23,213 @@ pub struct Player {
impl PartialUi for Player { impl PartialUi for Player {
fn build_partial<W: Into<nwg::ControlHandle>>( fn build_partial<W: Into<nwg::ControlHandle>>(
// @TODO replace with `&mut self`?
data: &mut Self, data: &mut Self,
parent: Option<W>, parent: Option<W>,
) -> Result<(), nwg::NwgError> { ) -> Result<(), nwg::NwgError> {
let (tx, rx) = flume::unbounded(); let (in_msg_sender, in_msg_receiver) = flume::unbounded();
let (tx1, rx1) = flume::unbounded(); let (rpc_response_sender, rpc_response_receiver) = flume::unbounded();
data.channel = RefCell::new(Some((tx, rx1)));
let hwnd = parent data.channel = ipc::Channel::new(Some((in_msg_sender, rpc_response_receiver)));
.expect("No parent window")
let window_handle = parent
.expect("no parent window")
.into() .into()
.hwnd() .hwnd()
.expect("Cannot obtain window handle") as i64; .expect("cannot obtain window handle");
// @TODO replace all `expect`s with proper error handling?
thread::spawn(move || { let mpv = create_shareable_mpv(window_handle);
let mut mpv_builder = let (observe_property_sender, observe_property_receiver) = flume::unbounded();
mpv::MpvHandlerBuilder::new().expect("Error while creating MPV builder");
mpv_builder
.set_option("wid", hwnd)
.expect("failed setting wid");
// mpv_builder.set_option("vo", "gpu").expect("unable to set vo");
// win, opengl: works but least performancy, 10-15% CPU
// winvk, vulkan: works as good as d3d11
// d3d11, d1d11: works great
// dxinterop, auto: works, slightly more cpu use than d3d11
// default (auto) seems to be d3d11 (vo/gpu/d3d11)
mpv_builder
.set_option("gpu-context", "angle")
.and_then(|_| mpv_builder.set_option("gpu-api", "auto"))
.expect("setting gpu options failed");
mpv_builder
.try_hardware_decoding()
.expect("failed setting hwdec");
mpv_builder
.set_option("title", "Stremio")
.expect("failed setting title");
mpv_builder
.set_option("terminal", "yes")
.expect("failed setting terminal");
mpv_builder
.set_option("msg-level", "all=no,cplayer=debug")
.expect("failed setting msg-level");
mpv_builder
.set_option("quiet", "yes")
.expect("failed setting msg-level");
let mut mpv_built = mpv_builder.build().expect("Cannot build MPV");
// FIXME: very often the audio track isn't selected when using "aid" = "auto" let _event_thread = create_event_thread(Arc::clone(&mpv), observe_property_receiver, rpc_response_sender);
mpv_built.set_property("aid", 1).ok(); let _message_thread = create_message_thread(mpv, observe_property_sender, in_msg_receiver);
// @TODO implement a mechanism to stop threads on `Player` drop if needed
let mut mpv = mpv_built.clone();
let event_thread = thread::spawn(move || {
// -1.0 means to block and wait for an event.
while let Some(event) = mpv.wait_event(-1.0) {
if mpv.raw().is_null() {
return;
}
// even if you don't do anything with the events, it is still necessary to empty
// the event loop
let resp_event = match event {
mpv::Event::PropertyChange {
name,
change,
reply_userdata: _,
} => PlayerResponse(
"mpv-prop-change",
PlayerEvent::PropChange(PlayerProprChange::from_name_value(
name.to_string(),
change,
)),
)
.to_value(),
mpv::Event::EndFile(Ok(reason)) => PlayerResponse(
"mpv-event-ended",
PlayerEvent::End(PlayerEnded::from_end_reason(reason)),
)
.to_value(),
mpv::Event::Shutdown => {
break;
}
_ => None,
};
if resp_event.is_some() {
tx1.send(RPCResponse::response_message(resp_event)).ok();
}
} // event drain loop
}); // event thread
let mut mpv = mpv_built.clone();
let message_thread = thread::spawn(move || {
for msg in rx.iter() {
if mpv.raw().is_null() {
return;
}
match serde_json::from_str::<InMsg>(msg.as_str()) {
Ok(InMsg(
InMsgFn::MpvObserveProp,
InMsgArgs::ObProp(PropKey::Bool(prop)),
)) => mpv.observe_property::<bool>(prop.to_string().as_str(), 0),
Ok(InMsg(
InMsgFn::MpvObserveProp,
InMsgArgs::ObProp(PropKey::Int(prop)),
)) => mpv.observe_property::<i64>(prop.to_string().as_str(), 0),
Ok(InMsg(
InMsgFn::MpvObserveProp,
InMsgArgs::ObProp(PropKey::Fp(prop)),
)) => mpv.observe_property::<f64>(prop.to_string().as_str(), 0),
Ok(InMsg(
InMsgFn::MpvObserveProp,
InMsgArgs::ObProp(PropKey::Str(prop)),
)) => mpv.observe_property::<&str>(prop.to_string().as_str(), 0),
Ok(InMsg(
InMsgFn::MpvSetProp,
InMsgArgs::StProp(prop, PropVal::Bool(val)),
)) => mpv.set_property(prop.to_string().as_str(), val),
Ok(InMsg(
InMsgFn::MpvSetProp,
InMsgArgs::StProp(prop, PropVal::Num(val)),
)) => mpv.set_property(prop.to_string().as_str(), val),
Ok(InMsg(
InMsgFn::MpvSetProp,
InMsgArgs::StProp(prop, PropVal::Str(val)),
)) => mpv.set_property(prop.to_string().as_str(), val.as_str()),
Ok(InMsg(InMsgFn::MpvCommand, InMsgArgs::Cmd(cmd))) => {
let cmd: Vec<String> = cmd.into();
mpv.command(&cmd.iter().map(|s| s.as_str()).collect::<Vec<_>>())
}
_ => {
eprintln!("MPV unsupported message {}", msg);
Ok(())
}
}
.ok();
} // incoming message drain loop
}); // message thread
// If we don't join our communication threads
// the `mpv_built` gets dropped and we have
// "use after free" errors which is very bad
event_thread.join().ok();
message_thread.join().ok();
}); // builder thread
Ok(()) Ok(())
} }
} }
fn create_shareable_mpv(window_handle: HWND) -> Arc<Mpv> {
let mpv = Mpv::with_initializer(|initializer| {
initializer.set_property("wid", window_handle as i64).expect("failed setting wid");
// initializer.set_property("vo", "gpu").expect("unable to set vo");
// win, opengl: works but least performancy, 10-15% CPU
// winvk, vulkan: works as good as d3d11
// d3d11, d1d11: works great
// dxinterop, auto: works, slightly more cpu use than d3d11
// default (auto) seems to be d3d11 (vo/gpu/d3d11)
initializer.set_property("gpu-context", "angle").expect("failed setting gpu-contex");
initializer.set_property("gpu-api", "auto").expect("failed setting gpu-api");
initializer.set_property("title", "Stremio").expect("failed setting title");
initializer.set_property("terminal", "yes").expect("failed setting terminal");
initializer.set_property("msg-level", "all=no,cplayer=debug").expect("failed setting msg-level");
initializer.set_property("quiet", "yes").expect("failed setting quiet");
initializer.set_property("hwdec", "auto").expect("failed setting hwdec");
// FIXME: very often the audio track isn't selected when using "aid" = "auto"
initializer.set_property("aid", 1).expect("failed setting aid");
Ok(())
}).expect("cannot build MPV");
Arc::new(mpv)
}
fn create_event_thread(
mpv: Arc<Mpv>,
observe_property_receiver: Receiver<ObserveProperty>,
rpc_response_sender: Sender<String>
) -> JoinHandle<()> {
thread::spawn(move || {
let mut event_context = mpv.create_event_context();
event_context.disable_deprecated_events().expect("failed to disable deprecated MPV events");
loop {
for ObserveProperty { name, format } in observe_property_receiver.drain() {
event_context.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.) {
Some(Ok(event)) => event,
Some(Err(error)) => {
eprintln!("Event errored: {error:?}");
continue;
}
// dummy event received (may be created on a wake up call or on timeout)
None => continue,
};
// even if you don't do anything with the events, it is still necessary to empty the event loop
let resp_event = match event {
Event::PropertyChange {
name,
change,
..
} => PlayerResponse(
"mpv-prop-change",
PlayerEvent::PropChange(PlayerProprChange::from_name_value(
name.to_string(),
change,
)),
)
.to_value(),
Event::EndFile(reason) => PlayerResponse(
"mpv-event-ended",
PlayerEvent::End(PlayerEnded::from_end_reason(reason)),
)
.to_value(),
Event::Shutdown => {
break;
}
_ => None,
};
if resp_event.is_some() {
rpc_response_sender.send(RPCResponse::response_message(resp_event)).ok();
}
}
})
}
fn create_message_thread(
mpv: Arc<Mpv>,
observe_property_sender: Sender<ObserveProperty>,
in_msg_receiver: Receiver<String>
) -> JoinHandle<()> {
thread::spawn(move || {
// -- Helpers --
let observe_property = |name: String, format: Format| {
observe_property_sender.send(ObserveProperty { name, format }).expect("cannot send ObserveProperty");
mpv.wake_up();
};
let send_command = |cmd: CmdVal| {
let (name, arg) = match cmd {
CmdVal::Double(name, arg) => (name, format!(r#""{arg}""#)),
CmdVal::Single((name,)) => (name, String::new())
};
mpv.command(&name.to_string(), &[&arg]).expect("failed to execute MPV command");
};
fn set_property(name: impl ToString, value: impl SetData, mpv: &Mpv) {
if let Err(error) = mpv.set_property(&name.to_string(), value) {
eprintln!("cannot set MPV property: '{error:#}'")
};
}
// -- InMsg handler loop --
for msg in in_msg_receiver.iter() {
let in_msg: InMsg = match serde_json::from_str(&msg) {
Ok(in_msg) => in_msg,
Err(error) => {
eprintln!("cannot parse InMsg: {error:#}");
continue;
}
};
match in_msg {
InMsg(
InMsgFn::MpvObserveProp,
InMsgArgs::ObProp(PropKey::Bool(prop)),
) => {
observe_property(prop.to_string(), Format::Flag);
},
InMsg(
InMsgFn::MpvObserveProp,
InMsgArgs::ObProp(PropKey::Int(prop)),
) => {
observe_property(prop.to_string(), Format::Int64);
},
InMsg(
InMsgFn::MpvObserveProp,
InMsgArgs::ObProp(PropKey::Fp(prop)),
) => {
observe_property(prop.to_string(), Format::Double);
},
InMsg(
InMsgFn::MpvObserveProp,
InMsgArgs::ObProp(PropKey::Str(prop)),
) => {
observe_property(prop.to_string(), Format::String);
},
InMsg(
InMsgFn::MpvSetProp,
InMsgArgs::StProp(prop, PropVal::Bool(value)),
) => {
set_property(prop, value, &mpv);
}
InMsg(
InMsgFn::MpvSetProp,
InMsgArgs::StProp(prop, PropVal::Num(value)),
) => {
set_property(prop, value, &mpv);
}
InMsg(
InMsgFn::MpvSetProp,
InMsgArgs::StProp(prop, PropVal::Str(value)),
) => {
set_property(prop, value, &mpv);
}
InMsg(InMsgFn::MpvCommand, InMsgArgs::Cmd(cmd)) => {
send_command(cmd);
}
msg => {
eprintln!("MPV unsupported message: '{msg:?}'");
}
}
}
})
}
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 { libmpv_sys::mpv_wakeup(self.ctx.as_ptr()) }
}
}