2024-10-30 07:41:17 +00:00
|
|
|
mod ffi;
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
use std::ffi::{c_int, c_void, CStr, CString};
|
|
|
|
|
2024-10-30 07:44:25 +00:00
|
|
|
#[link(name = "mpv")]
|
|
|
|
extern "C" {}
|
2024-10-30 07:41:17 +00:00
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
pub struct Error(c_int);
|
2024-10-30 07:41:17 +00:00
|
|
|
|
|
|
|
impl Error {
|
2024-11-02 14:43:43 +00:00
|
|
|
fn from_return_code(rc: c_int) -> Result<(), Self> {
|
2024-10-30 07:41:17 +00:00
|
|
|
if rc == 0 {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(Self(rc))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-02 15:21:13 +00:00
|
|
|
use std::fmt;
|
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
|
|
fmt::Display::fmt(
|
|
|
|
unsafe { &CStr::from_ptr(ffi::mpv_error_string(self.0 as c_int)) }
|
|
|
|
.to_str()
|
|
|
|
.unwrap(),
|
|
|
|
f,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
2024-10-30 07:41:17 +00:00
|
|
|
f.debug_tuple("Error")
|
2024-11-02 15:21:13 +00:00
|
|
|
.field(
|
|
|
|
&unsafe { &CStr::from_ptr(ffi::mpv_error_string(self.0 as c_int)) }
|
|
|
|
.to_str()
|
|
|
|
.unwrap(),
|
|
|
|
)
|
2024-10-30 07:41:17 +00:00
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-02 15:21:13 +00:00
|
|
|
impl std::error::Error for Error {}
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
use std::pin::Pin;
|
2024-10-30 07:41:17 +00:00
|
|
|
use std::ptr::NonNull;
|
2024-11-02 15:50:10 +00:00
|
|
|
use event_listener::{Event, IntoNotification};
|
|
|
|
use std::cell::RefCell;
|
2024-10-30 07:41:17 +00:00
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
pub struct Handle {
|
|
|
|
inner: NonNull<ffi::mpv_handle>,
|
2024-11-02 15:50:10 +00:00
|
|
|
wakeup: Pin<Box<Event>>, // the wakeup callback holds a pointer to this
|
|
|
|
tick_cell: RefCell<()>,
|
2024-11-02 14:43:43 +00:00
|
|
|
|
|
|
|
observed_property_callbacks:
|
|
|
|
Vec<Pin<Box<Box<dyn FnMut(ffi::mpv_format, *mut c_void) + 'static>>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// The client API is generally fully thread-safe, unless otherwise noted.
|
2024-11-02 15:22:57 +00:00
|
|
|
//unsafe impl Send for Handle {}
|
|
|
|
//unsafe impl Sync for Handle {}
|
|
|
|
// ........but we'd rather keep this in a single thread to play better with gtk
|
2024-11-02 14:43:43 +00:00
|
|
|
|
|
|
|
impl Default for Handle {
|
|
|
|
fn default() -> Self {
|
2024-11-02 15:50:10 +00:00
|
|
|
Self::new()
|
2024-11-02 14:43:43 +00:00
|
|
|
}
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
|
|
|
|
impl Handle {
|
2024-11-02 15:50:10 +00:00
|
|
|
pub fn new() -> Self {
|
2024-11-02 14:43:43 +00:00
|
|
|
let inner =
|
|
|
|
NonNull::new(unsafe { ffi::mpv_create() }).expect("could not create mpv handle");
|
|
|
|
|
2024-11-02 15:50:10 +00:00
|
|
|
let wakeup = Box::pin(Event::new());
|
2024-11-02 14:43:43 +00:00
|
|
|
|
|
|
|
extern "C" fn wakeup_callback(d: *mut c_void) {
|
2024-11-02 15:50:10 +00:00
|
|
|
let d = d as *const Event;
|
|
|
|
let wakeup = unsafe { &*d };
|
|
|
|
wakeup.notify(1_u32.relaxed()); // mpv has its own synchronization, trust it
|
2024-11-02 14:43:43 +00:00
|
|
|
}
|
|
|
|
unsafe {
|
|
|
|
ffi::mpv_set_wakeup_callback(
|
|
|
|
inner.as_ptr(),
|
|
|
|
Some(wakeup_callback),
|
2024-11-02 15:50:10 +00:00
|
|
|
std::ptr::from_ref::<Event>(&wakeup) as *mut c_void,
|
2024-11-02 14:43:43 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-11-02 15:50:10 +00:00
|
|
|
// TODO: maybe we need to set something before initialization, but idk
|
|
|
|
// also wed need a builder, since "Note that you should avoid doing concurrent accesses on the uninitialized client handle."
|
|
|
|
Error::from_return_code(unsafe { ffi::mpv_initialize(inner.as_ptr()) }).expect("could not initialize mpv handle");
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
Self {
|
|
|
|
inner: inner,
|
2024-11-02 15:50:10 +00:00
|
|
|
wakeup: wakeup,
|
|
|
|
tick_cell: RefCell::new(()),
|
2024-11-02 14:43:43 +00:00
|
|
|
|
|
|
|
observed_property_callbacks: vec![],
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
pub fn set_property<'a>(&self, name: &str, value: impl FormatValue) -> Result<(), Error> {
|
|
|
|
// need to add zero terminator
|
|
|
|
let name = CString::new(name).expect("null bytes in property name");
|
|
|
|
value.into_data(|format, data| {
|
|
|
|
Error::from_return_code(unsafe {
|
|
|
|
ffi::mpv_set_property(self.inner.as_ptr(), name.as_ptr(), format, data)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: some way to deregister these maybe??
|
|
|
|
pub fn observe_property<T>(
|
|
|
|
&mut self,
|
|
|
|
name: &str,
|
|
|
|
mut cb: impl FnMut(Option<T>) + 'static,
|
|
|
|
) -> Result<(), Error>
|
|
|
|
where
|
|
|
|
T: FormatValue,
|
|
|
|
{
|
|
|
|
let name = CString::new(name).unwrap();
|
|
|
|
let cb = Box::new(move |format, data| T::from_data(format, data, |x| cb(x)));
|
|
|
|
let cb = Box::<dyn FnMut(ffi::mpv_format, *mut c_void)>::from(cb);
|
|
|
|
let cb = Box::pin(cb);
|
|
|
|
let userdata =
|
|
|
|
std::ptr::from_ref::<Box<dyn FnMut(ffi::mpv_format, *mut c_void)>>(&cb) as usize as u64;
|
|
|
|
|
|
|
|
Error::from_return_code(unsafe {
|
|
|
|
ffi::mpv_observe_property(self.inner.as_ptr(), userdata, name.as_ptr(), T::FORMAT)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
self.observed_property_callbacks.push(cb);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-11-02 15:50:10 +00:00
|
|
|
pub async fn tick(&self) {
|
|
|
|
// mpv_wait_event is not reentrant!! use a refcell to ensure that
|
|
|
|
self.tick_cell.borrow_mut();
|
|
|
|
|
|
|
|
// take listener before we drain the event loop, so we don't miss any notifications
|
|
|
|
let listener = self.wakeup.listen();
|
2024-10-30 07:41:17 +00:00
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
loop {
|
|
|
|
let event = unsafe { &*ffi::mpv_wait_event(self.inner.as_ptr(), 0.0) };
|
|
|
|
match event.event_id {
|
|
|
|
ffi::mpv_event_id_MPV_EVENT_NONE => break,
|
|
|
|
|
|
|
|
ffi::mpv_event_id_MPV_EVENT_PROPERTY_CHANGE => {
|
|
|
|
let cb = unsafe {
|
|
|
|
&mut *(event.reply_userdata
|
|
|
|
as *mut Box<dyn FnMut(ffi::mpv_format, *mut c_void)>)
|
|
|
|
};
|
|
|
|
let data = unsafe { &*(event.data as *mut ffi::mpv_event_property) };
|
|
|
|
cb(data.format, data.data);
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
11 => { /* deprecated, ignore */ }
|
|
|
|
|
|
|
|
_ => todo!("event {}", event.event_id),
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
}
|
2024-11-02 15:50:10 +00:00
|
|
|
|
|
|
|
listener.await;
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
impl Drop for Handle {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
// destroy? terminate_destroy??
|
|
|
|
todo!()
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
pub trait FormatValue: Sized {
|
|
|
|
const FORMAT: ffi::mpv_format;
|
|
|
|
fn into_data<T>(self, f: impl FnOnce(ffi::mpv_format, *mut c_void) -> T) -> T;
|
|
|
|
fn from_data<T>(
|
|
|
|
format: ffi::mpv_format,
|
|
|
|
data: *mut c_void,
|
|
|
|
f: impl FnOnce(Option<Self>) -> T,
|
|
|
|
) -> T;
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
impl<'a> FormatValue for &'a str {
|
|
|
|
const FORMAT: ffi::mpv_format = ffi::mpv_format_MPV_FORMAT_STRING;
|
2024-10-30 07:41:17 +00:00
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
fn into_data<T>(self, f: impl FnOnce(ffi::mpv_format, *mut c_void) -> T) -> T {
|
|
|
|
let str = CString::new(self).expect("null bytes in string");
|
|
|
|
let str_ptr = str.as_ptr();
|
|
|
|
f(Self::FORMAT, (&str_ptr) as *const _ as *mut c_void)
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
fn from_data<T>(
|
|
|
|
_format: ffi::mpv_format,
|
|
|
|
_data: *mut c_void,
|
|
|
|
_f: impl FnOnce(Option<Self>) -> T,
|
|
|
|
) -> T {
|
|
|
|
todo!()
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
impl FormatValue for bool {
|
|
|
|
const FORMAT: ffi::mpv_format = ffi::mpv_format_MPV_FORMAT_FLAG;
|
2024-10-30 07:41:17 +00:00
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
fn into_data<T>(self, f: impl FnOnce(ffi::mpv_format, *mut c_void) -> T) -> T {
|
|
|
|
let flag: c_int = if self { 1 } else { 0 };
|
|
|
|
f(Self::FORMAT, (&flag) as *const c_int as *mut c_void)
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
fn from_data<T>(
|
|
|
|
_format: ffi::mpv_format,
|
|
|
|
_data: *mut c_void,
|
|
|
|
_f: impl FnOnce(Option<Self>) -> T,
|
|
|
|
) -> T {
|
|
|
|
todo!()
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
|
2024-11-02 14:43:43 +00:00
|
|
|
impl FormatValue for f64 {
|
|
|
|
const FORMAT: ffi::mpv_format = ffi::mpv_format_MPV_FORMAT_DOUBLE;
|
|
|
|
|
|
|
|
fn into_data<T>(self, f: impl FnOnce(ffi::mpv_format, *mut c_void) -> T) -> T {
|
|
|
|
f(Self::FORMAT, (&self) as *const f64 as *mut c_void)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_data<T>(
|
|
|
|
format: ffi::mpv_format,
|
|
|
|
data: *mut c_void,
|
|
|
|
f: impl FnOnce(Option<Self>) -> T,
|
|
|
|
) -> T {
|
|
|
|
match format {
|
|
|
|
ffi::mpv_format_MPV_FORMAT_NONE => f(None),
|
|
|
|
ffi::mpv_format_MPV_FORMAT_DOUBLE => f(Some(unsafe { *(data as *mut f64) })),
|
|
|
|
_ => panic!(),
|
|
|
|
}
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
}
|