2024-10-30 07:41:17 +00:00
|
|
|
#[allow(dead_code)]
|
|
|
|
#[allow(non_camel_case_types)]
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
|
|
#[allow(unreachable_code)]
|
|
|
|
mod ffi;
|
|
|
|
|
2024-10-30 07:44:25 +00:00
|
|
|
#[link(name = "mpv")]
|
|
|
|
extern "C" {}
|
2024-10-30 07:41:17 +00:00
|
|
|
|
|
|
|
pub struct Error(std::ffi::c_int);
|
|
|
|
|
|
|
|
impl Error {
|
|
|
|
fn from_return_code(rc: std::ffi::c_int) -> Result<(), Self> {
|
|
|
|
if rc == 0 {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(Self(rc))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for Error {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
|
|
f.debug_tuple("Error")
|
|
|
|
.field(unsafe {
|
|
|
|
&std::ffi::CStr::from_ptr(ffi::mpv_error_string(self.0 as std::ffi::c_int))
|
|
|
|
})
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
use std::borrow::Cow;
|
|
|
|
use std::ptr::NonNull;
|
|
|
|
|
|
|
|
pub struct Handle(NonNull<ffi::mpv_handle>);
|
|
|
|
|
|
|
|
impl Handle {
|
|
|
|
pub fn create() -> Self {
|
|
|
|
Self(NonNull::new(unsafe { ffi::mpv_create() }).expect("could not create mpv handle"))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn initialize(&self) -> Result<(), Error> {
|
|
|
|
Error::from_return_code(unsafe { ffi::mpv_initialize(self.0.as_ptr()) })
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn wait_event<'a>(&'a mut self, timeout: f64) -> Option<Event<'a>> {
|
|
|
|
let event = unsafe { &*ffi::mpv_wait_event(self.0.as_ptr(), timeout) };
|
|
|
|
match event.event_id {
|
|
|
|
0 => None,
|
|
|
|
1 => Some(Event::Shutdown),
|
|
|
|
2 => todo!("Event::LogMessage"),
|
|
|
|
3 => Some(Event::GetPropertyReply(
|
|
|
|
event.reply_userdata,
|
|
|
|
Error::from_return_code(event.error).map(|()| {
|
|
|
|
let data = unsafe { &*(event.data as *mut ffi::mpv_event_property) };
|
|
|
|
EventProperty {
|
|
|
|
name: unsafe { std::ffi::CStr::from_ptr(data.name) }
|
|
|
|
.to_str()
|
|
|
|
.unwrap(),
|
2024-10-30 07:44:25 +00:00
|
|
|
value: unsafe { FormatData::new(data.format, data.data) },
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
)),
|
|
|
|
4 => todo!("Event::SetPropertyReply"),
|
|
|
|
5 => todo!("Event::CommandReply"),
|
|
|
|
6 => todo!("Event::StartFile"),
|
|
|
|
7 => todo!("Event::EndFile"),
|
|
|
|
8 => todo!("Event::FileLoaded"),
|
|
|
|
16 => todo!("Event::ClientMessage"),
|
|
|
|
17 => todo!("Event::VideoReconfig"),
|
|
|
|
18 => todo!("Event::AudioReconfig"),
|
|
|
|
20 => todo!("Event::Seek"),
|
|
|
|
21 => todo!("Event::PlaybackRestart"),
|
|
|
|
22 => todo!("Event::PropertyChange"),
|
|
|
|
24 => todo!("Event::QueueOverflow"),
|
|
|
|
25 => todo!("Event::Hook"),
|
2024-10-30 07:44:25 +00:00
|
|
|
_ => Some(Event::Ignore(event.event_id)),
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum FormatData<'a> {
|
|
|
|
None,
|
|
|
|
String(Cow<'a, str>),
|
|
|
|
OsdString(&'a str),
|
|
|
|
Flag(bool),
|
|
|
|
Int64(i64),
|
|
|
|
Double(f64),
|
|
|
|
//Node(Node<'a>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FormatData<'a> {
|
|
|
|
unsafe fn new(format: ffi::mpv_format, data: *mut std::ffi::c_void) -> Self {
|
|
|
|
match format {
|
|
|
|
0 => Self::None,
|
|
|
|
1 => {
|
|
|
|
let data = data as *mut *mut std::ffi::c_char;
|
|
|
|
Self::String(String::from_utf8_lossy(
|
|
|
|
std::ffi::CStr::from_ptr(*data).to_bytes(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
2 => {
|
|
|
|
let data = data as *mut *mut std::ffi::c_char;
|
|
|
|
Self::OsdString(
|
|
|
|
std::ffi::CStr::from_ptr(*data)
|
|
|
|
.to_str()
|
|
|
|
.expect("OSD string wasn't UTF-8"),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
3 => {
|
|
|
|
let data = data as *mut std::ffi::c_int;
|
|
|
|
Self::Flag(match *data {
|
|
|
|
0 => false,
|
|
|
|
1 => true,
|
|
|
|
_ => panic!("invalid mpv flag value"),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
4 => {
|
|
|
|
let data = data as *mut i64;
|
|
|
|
Self::Int64(*data)
|
|
|
|
}
|
|
|
|
5 => {
|
|
|
|
let data = data as *mut f64;
|
|
|
|
Self::Double(*data)
|
|
|
|
}
|
|
|
|
6 => todo!(),
|
|
|
|
7 => todo!(),
|
|
|
|
8 => todo!(),
|
|
|
|
9 => todo!(),
|
|
|
|
_ => panic!("invalid mpv format"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EventProperty<'a> {
|
2024-10-30 07:44:25 +00:00
|
|
|
pub name: &'a str,
|
|
|
|
pub value: FormatData<'a>,
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EventLogMessage<'a> {
|
2024-10-30 07:44:25 +00:00
|
|
|
pub prefix: &'a str,
|
|
|
|
pub level: &'a str,
|
|
|
|
pub text: &'a str,
|
|
|
|
pub log_level: i32,
|
2024-10-30 07:41:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum EndFileReason {
|
|
|
|
Eof,
|
|
|
|
Stop,
|
|
|
|
Quit,
|
|
|
|
Error(Error),
|
|
|
|
Redirect,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EventStartFile {
|
|
|
|
pub playlist_entry_id: i64,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EventEndFile {
|
|
|
|
pub reason: EndFileReason,
|
|
|
|
pub playlist_entry_id: i64,
|
|
|
|
pub playlist_insert_id: i64,
|
|
|
|
pub playlist_insert_num_entries: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EventClientMessage<'a> {
|
|
|
|
pub num_args: i32,
|
|
|
|
pub args: &'a [&'a str],
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EventHook<'a> {
|
|
|
|
pub name: &'a str,
|
|
|
|
pub id: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum Event<'a> {
|
|
|
|
Shutdown,
|
|
|
|
LogMessage(EventLogMessage<'a>),
|
|
|
|
GetPropertyReply(u64, Result<EventProperty<'a>, Error>),
|
|
|
|
SetPropertyReply(u64, Result<(), Error>),
|
|
|
|
//CommandReply(u64, Result<Node<'a>, Error>),
|
|
|
|
StartFile(EventStartFile),
|
|
|
|
EndFile(EventEndFile),
|
|
|
|
FileLoaded,
|
|
|
|
ClientMessage(EventClientMessage<'a>),
|
|
|
|
VideoReconfig,
|
|
|
|
AudioReconfig,
|
|
|
|
Seek,
|
|
|
|
PlaybackRestart,
|
|
|
|
PropertyChange(u64, EventProperty<'a>),
|
|
|
|
QueueOverflow,
|
|
|
|
Hook(u64, EventHook<'a>),
|
|
|
|
// "Keep in mind that later ABI compatible releases might add new event
|
|
|
|
// types. These should be ignored by the API user."
|
|
|
|
Ignore(u32),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Handle {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
// destroy? terminate_destroy??
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|