1mod error;
2mod ffi;
3mod format;
4
5pub use error::{Error, Result};
6use ffi::*;
7pub use format::Format;
8
9use std::ffi::{c_char, c_void, CStr, CString};
10use std::fmt;
11use std::ops::{Deref, DerefMut};
12use std::ptr::slice_from_raw_parts_mut;
13
14pub use ffi::mpv_handle;
15
16pub struct Handle {
19 inner: [mpv_handle],
20}
21
22pub struct Client(*mut mpv_handle);
24
25pub enum Event {
28 None,
30 Shutdown,
33 LogMessage(LogMessage),
36 GetPropertyReply(Result<()>, u64, Property),
39 SetPropertyReply(Result<()>, u64),
42 CommandReply(Result<()>, u64), StartFile(StartFile),
48 EndFile(EndFile),
51 FileLoaded,
54 ClientMessage(ClientMessage),
60 VideoReconfig,
69 AudioReconfig,
72 Seek,
75 PlaybackRestart,
80 PropertyChange(u64, Property),
83 QueueOverflow,
91 Hook(u64, Hook),
96}
97
98pub struct Property(*const mpv_event_property);
100
101pub struct LogMessage(*const mpv_event_log_message);
103
104pub struct StartFile(*const mpv_event_start_file);
106
107pub struct EndFile(*const mpv_event_end_file);
109
110pub struct ClientMessage(*const mpv_event_client_message);
112
113pub struct Hook(*const mpv_event_hook);
115
116macro_rules! result {
117 ($f:expr) => {
118 match $f {
119 mpv_error::SUCCESS => Ok(()),
120 e => Err(Error::new(e)),
121 }
122 };
123}
124
125#[macro_export]
126macro_rules! osd {
127 ($client:expr, $duration:expr, $($arg:tt)*) => {
128 $client.command(&["show-text", &format!($($arg)*), &$duration.as_millis().to_string()])
129 }
130}
131
132#[macro_export]
133macro_rules! osd_async {
134 ($client:expr, $reply:expr, $duration:expr, $($arg:tt)*) => {
135 $client.command_async($reply, &["show-text", &format!($($arg)*), &$duration.as_millis().to_string()])
136 }
137}
138
139impl Handle {
140 #[inline]
152 pub fn from_ptr<'a>(ptr: *mut mpv_handle) -> &'a mut Self {
153 unsafe { &mut *(slice_from_raw_parts_mut(ptr, 1) as *mut Self) }
154 }
155
156 #[inline]
157 pub unsafe fn as_ptr(&self) -> *const mpv_handle {
158 self.inner.as_ptr()
159 }
160
161 #[inline]
162 pub unsafe fn as_mut_ptr(&mut self) -> *mut mpv_handle {
163 self.inner.as_mut_ptr()
164 }
165
166 pub fn create_client<S: AsRef<str>>(&mut self, name: S) -> Result<Client> {
167 let name = CString::new(name.as_ref())?;
168 let handle = unsafe { mpv_create_client(self.as_mut_ptr(), name.as_ptr()) };
169 if handle.is_null() {
170 Err(Error::new(mpv_error::NOMEM))
171 } else {
172 Ok(Client(handle))
173 }
174 }
175
176 pub fn create_weak_client<S: AsRef<str>>(&mut self, name: S) -> Result<Client> {
177 let name = CString::new(name.as_ref())?;
178 let handle = unsafe { mpv_create_weak_client(self.as_mut_ptr(), name.as_ptr()) };
179 if handle.is_null() {
180 Err(Error::new(mpv_error::NOMEM))
181 } else {
182 Ok(Client(handle))
183 }
184 }
185
186 pub fn initialize(&mut self) -> Result<()> {
187 unsafe { result!(mpv_initialize(self.as_mut_ptr())) }
188 }
189
190 pub fn wait_event(&mut self, timeout: f64) -> Event {
209 unsafe { Event::from_ptr(mpv_wait_event(self.as_mut_ptr(), timeout)) }
210 }
211
212 pub fn name<'a>(&mut self) -> &'a str {
215 unsafe {
216 CStr::from_ptr(mpv_client_name(self.as_mut_ptr()))
217 .to_str()
218 .unwrap_or("unknown")
219 }
220 }
221
222 #[inline]
234 pub fn id(&mut self) -> i64 {
235 unsafe { mpv_client_id(self.as_mut_ptr()) }
236 }
237
238 pub fn command<I, S>(&mut self, args: I) -> Result<()>
242 where
243 I: IntoIterator<Item = S>,
244 S: AsRef<str>,
245 {
246 let args: Vec<CString> = args.into_iter().map(|s| CString::new(s.as_ref()).unwrap()).collect();
247 let mut raw_args: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
248 raw_args.push(std::ptr::null()); unsafe { result!(mpv_command(self.as_mut_ptr(), raw_args.as_ptr())) }
250 }
251
252 pub fn command_async<I, S>(&mut self, reply: u64, args: I) -> Result<()>
266 where
267 I: IntoIterator<Item = S>,
268 S: AsRef<str>,
269 {
270 let args: Vec<CString> = args.into_iter().map(|s| CString::new(s.as_ref()).unwrap()).collect();
271 let mut raw_args: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
272 raw_args.push(std::ptr::null()); unsafe { result!(mpv_command_async(self.as_mut_ptr(), reply, raw_args.as_ptr())) }
274 }
275
276 pub fn set_property<T: Format, S: AsRef<str>>(&mut self, name: S, data: T) -> Result<()> {
277 let name = CString::new(name.as_ref())?;
278 let handle = unsafe { self.as_mut_ptr() };
279 data.to_mpv(|data| unsafe { result!(mpv_set_property(handle, name.as_ptr(), T::MPV_FORMAT, data)) })
280 }
281
282 pub fn get_property<T: Format, S: AsRef<str>>(&mut self, name: S) -> Result<T> {
289 let name = CString::new(name.as_ref())?;
290 let handle = unsafe { self.as_mut_ptr() };
291 T::from_mpv(|data| unsafe { result!(mpv_get_property(handle, name.as_ptr(), T::MPV_FORMAT, data)) })
292 }
293
294 pub fn observe_property<S: AsRef<str>>(&mut self, reply: u64, name: S, format: i32) -> Result<()> {
295 let name = CString::new(name.as_ref())?;
296 unsafe { result!(mpv_observe_property(self.as_mut_ptr(), reply, name.as_ptr(), format)) }
297 }
298
299 pub fn unobserve_property(&mut self, registered_reply: u64) -> Result<()> {
304 unsafe { result!(mpv_unobserve_property(self.as_mut_ptr(), registered_reply)) }
305 }
306
307 pub fn hook_add(&mut self, reply: u64, name: &str, priority: i32) -> Result<()> {
308 let name = CString::new(name)?;
309 unsafe { result!(mpv_hook_add(self.as_mut_ptr(), reply, name.as_ptr(), priority)) }
310 }
311
312 pub fn hook_continue(&mut self, id: u64) -> Result<()> {
313 unsafe { result!(mpv_hook_continue(self.as_mut_ptr(), id)) }
314 }
315}
316
317impl Client {
318 pub fn new() -> Result<Self> {
319 let handle = unsafe { mpv_create() };
320 if handle.is_null() {
321 Err(Error::new(mpv_error::NOMEM))
322 } else {
323 Ok(Self(handle))
324 }
325 }
326
327 pub fn initialize(self) -> Result<Self> {
328 unsafe { result!(mpv_initialize(self.0)).map(|()| self) }
329 }
330}
331
332impl Drop for Client {
333 fn drop(&mut self) {
334 unsafe { mpv_destroy(self.0) }
335 }
336}
337
338impl Deref for Client {
339 type Target = Handle;
340
341 #[inline]
342 fn deref(&self) -> &Self::Target {
343 Handle::from_ptr(self.0)
344 }
345}
346
347impl DerefMut for Client {
348 #[inline]
349 fn deref_mut(&mut self) -> &mut Self::Target {
350 Handle::from_ptr(self.0)
351 }
352}
353
354unsafe impl Send for Client {}
355
356impl Event {
357 unsafe fn from_ptr(event: *const mpv_event) -> Event {
358 match (*event).event_id {
359 mpv_event_id::SHUTDOWN => Event::Shutdown,
360 mpv_event_id::LOG_MESSAGE => Event::LogMessage(LogMessage::from_ptr((*event).data)),
361 mpv_event_id::GET_PROPERTY_REPLY => Event::GetPropertyReply(
362 result!((*event).error),
363 (*event).reply_userdata,
364 Property::from_ptr((*event).data),
365 ),
366 mpv_event_id::SET_PROPERTY_REPLY => {
367 Event::SetPropertyReply(result!((*event).error), (*event).reply_userdata)
368 }
369 mpv_event_id::COMMAND_REPLY => Event::CommandReply(result!((*event).error), (*event).reply_userdata),
370 mpv_event_id::START_FILE => Event::StartFile(StartFile::from_ptr((*event).data)),
371 mpv_event_id::END_FILE => Event::EndFile(EndFile::from_ptr((*event).data)),
372 mpv_event_id::FILE_LOADED => Event::FileLoaded,
373 mpv_event_id::CLIENT_MESSAGE => Event::ClientMessage(ClientMessage::from_ptr((*event).data)),
374 mpv_event_id::VIDEO_RECONFIG => Event::VideoReconfig,
375 mpv_event_id::AUDIO_RECONFIG => Event::AudioReconfig,
376 mpv_event_id::SEEK => Event::Seek,
377 mpv_event_id::PLAYBACK_RESTART => Event::PlaybackRestart,
378 mpv_event_id::PROPERTY_CHANGE => {
379 Event::PropertyChange((*event).reply_userdata, Property::from_ptr((*event).data))
380 }
381 mpv_event_id::QUEUE_OVERFLOW => Event::QueueOverflow,
382 mpv_event_id::HOOK => Event::Hook((*event).reply_userdata, Hook::from_ptr((*event).data)),
383 _ => Event::None,
384 }
385 }
386}
387
388impl fmt::Display for Event {
389 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
390 let event = match *self {
391 Self::Shutdown => mpv_event_id::SHUTDOWN,
392 Self::LogMessage(..) => mpv_event_id::LOG_MESSAGE,
393 Self::GetPropertyReply(..) => mpv_event_id::GET_PROPERTY_REPLY,
394 Self::SetPropertyReply(..) => mpv_event_id::SET_PROPERTY_REPLY,
395 Self::CommandReply(..) => mpv_event_id::COMMAND_REPLY,
396 Self::StartFile(..) => mpv_event_id::START_FILE,
397 Self::EndFile(..) => mpv_event_id::END_FILE,
398 Self::FileLoaded => mpv_event_id::FILE_LOADED,
399 Self::ClientMessage(..) => mpv_event_id::CLIENT_MESSAGE,
400 Self::VideoReconfig => mpv_event_id::VIDEO_RECONFIG,
401 Self::AudioReconfig => mpv_event_id::AUDIO_RECONFIG,
402 Self::Seek => mpv_event_id::SEEK,
403 Self::PlaybackRestart => mpv_event_id::PLAYBACK_RESTART,
404 Self::PropertyChange(..) => mpv_event_id::PROPERTY_CHANGE,
405 Self::QueueOverflow => mpv_event_id::QUEUE_OVERFLOW,
406 Self::Hook(..) => mpv_event_id::HOOK,
407 _ => mpv_event_id::NONE,
408 };
409
410 f.write_str(unsafe {
411 CStr::from_ptr(mpv_event_name(event))
412 .to_str()
413 .unwrap_or("unknown event")
414 })
415 }
416}
417
418impl Property {
419 fn from_ptr(ptr: *const c_void) -> Self {
422 assert!(!ptr.is_null());
423 Self(ptr as *const mpv_event_property)
424 }
425
426 pub fn name(&self) -> &str {
428 unsafe { CStr::from_ptr((*self.0).name) }.to_str().unwrap_or("unknown")
429 }
430
431 pub fn data<T: Format>(&self) -> Option<T> {
432 unsafe {
433 if (*self.0).format == T::MPV_FORMAT {
434 T::from_ptr((*self.0).data).ok()
435 } else {
436 None
437 }
438 }
439 }
440}
441
442impl fmt::Display for Property {
443 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444 f.write_str(self.name())
445 }
446}
447
448impl LogMessage {
449 fn from_ptr(ptr: *const c_void) -> Self {
452 assert!(!ptr.is_null());
453 Self(ptr as *const mpv_event_log_message)
454 }
455}
456
457impl fmt::Display for LogMessage {
458 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
459 f.write_str("log message")
460 }
461}
462
463impl StartFile {
464 fn from_ptr(ptr: *const c_void) -> Self {
467 assert!(!ptr.is_null());
468 Self(ptr as *const mpv_event_start_file)
469 }
470
471 pub fn playlist_entry_id(&self) -> u64 {
473 unsafe { (*self.0).playlist_entry_id }
474 }
475}
476
477impl fmt::Display for StartFile {
478 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
479 f.write_str("start file")
480 }
481}
482
483impl EndFile {
484 fn from_ptr(ptr: *const c_void) -> Self {
487 assert!(!ptr.is_null());
488 Self(ptr as *const mpv_event_end_file)
489 }
490}
491
492impl fmt::Display for EndFile {
493 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
494 f.write_str("end file")
495 }
496}
497
498impl ClientMessage {
499 fn from_ptr(ptr: *const c_void) -> Self {
502 assert!(!ptr.is_null());
503 Self(ptr as *const mpv_event_client_message)
504 }
505
506 pub fn args<'a>(&self) -> Vec<&'a str> {
507 unsafe {
508 let args = std::slice::from_raw_parts((*self.0).args, (*self.0).num_args as usize);
509 args.into_iter()
510 .map(|arg| CStr::from_ptr(*arg).to_str().unwrap())
511 .collect()
512 }
513 }
514}
515
516impl fmt::Display for ClientMessage {
517 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
518 f.write_str("client-message")
519 }
520}
521
522impl Hook {
523 fn from_ptr(ptr: *const c_void) -> Self {
526 assert!(!ptr.is_null());
527 Self(ptr as *const mpv_event_hook)
528 }
529
530 pub fn name(&self) -> &str {
532 unsafe { CStr::from_ptr((*self.0).name).to_str().unwrap_or("unknown") }
533 }
534
535 pub fn id(&self) -> u64 {
537 unsafe { (*self.0).id }
538 }
539}
540
541impl fmt::Display for Hook {
542 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
543 f.write_str(self.name())
544 }
545}