// Copyright (c) 2015 T. Okubo // This file is part of vlc-rs. // Licensed under the MIT license, see the LICENSE file. use std::ptr; use std::borrow::Cow; use std::marker::PhantomData; use std::ffi::CString; use std::i32; use std::convert::TryInto; use libc::{c_void, c_char, c_int}; use vlc_sys as sys; use crate::tools::{to_cstr, from_cstr, from_cstr_ref}; use crate::enums::*; /// Retrieve libvlc version. pub fn version() -> String { unsafe{ from_cstr_ref(sys::libvlc_get_version()).unwrap().into_owned() } } /// Retrieve libvlc compiler version. pub fn compiler() -> String { unsafe{ from_cstr_ref(sys::libvlc_get_compiler()).unwrap().into_owned() } } pub struct Instance { pub(crate) ptr: *mut sys::libvlc_instance_t, } unsafe impl Send for Instance {} impl Instance { /// Create and initialize a libvlc instance with specified args. /// Note: args.len() has to be less or equal to i32::MAX /// Note: libvlc discourages using arguments as these are not guaranteed to be stable between different versions of libvlc pub fn with_args(args: Option>) -> Option { let args_c_ptr: Vec<*const c_char> ; let args_c: Vec; if let Some(argv) = args { args_c = argv.into_iter() .map(|x| CString::new(x).expect("Error: Unexpected null byte")).collect(); args_c_ptr = args_c.iter().map(|x| x.as_ptr()).collect(); } else { args_c_ptr = Vec::new(); } unsafe{ let p = if args_c_ptr.is_empty() { sys::libvlc_new(0, ptr::null()) } else { sys::libvlc_new(args_c_ptr.len() as i32, args_c_ptr.as_ptr()) }; if p.is_null() { return None; } Some(Instance{ptr: p}) } } /// Create and initialize a libvlc instance. pub fn new() -> Option { Instance::with_args(None) } /// Try to start a user interface for the libvlc instance. pub fn add_intf(&self, name: &str) -> Result<(), ()> { let cstr = to_cstr(name); let result = unsafe{ sys::libvlc_add_intf(self.ptr, cstr.as_ptr()) }; if result == 0 { Ok(()) } else { Err(()) } } /// Sets the application name. /// LibVLC passes this as the user agent string when a protocol requires it. pub fn set_user_agent(&self, name: &str, http: &str) { unsafe{ sys::libvlc_set_user_agent( self.ptr, to_cstr(name).as_ptr(), to_cstr(http).as_ptr()); } } /// Waits until an interface causes the instance to exit. pub fn wait(&self) { unsafe{ sys::libvlc_wait(self.ptr) }; } /// Sets some meta-information about the application. pub fn set_app_id(&self, id: &str, version: &str, icon: &str) { unsafe{ sys::libvlc_set_app_id( self.ptr, to_cstr(id).as_ptr(), to_cstr(version).as_ptr(), to_cstr(icon).as_ptr()); } } /// Returns a list of audio filters that are available. pub fn audio_filter_list_get(&self) -> Option { unsafe{ let p = sys::libvlc_audio_filter_list_get(self.ptr); if p.is_null() { None } else { Some(ModuleDescriptionList{ptr: p}) } } } /// Returns a list of video filters that are available. pub fn video_filter_list_get(&self) -> Option { unsafe{ let p = sys::libvlc_video_filter_list_get(self.ptr); if p.is_null() { None } else { Some(ModuleDescriptionList{ptr: p}) } } } /// Returns the VLM event manager pub fn vlm_event_manager<'a>(&'a self) -> EventManager<'a> { unsafe{ let p = sys::libvlc_vlm_get_event_manager(self.ptr); assert!(!p.is_null()); EventManager{ptr: p, _phantomdata: ::std::marker::PhantomData} } } /// Set logging callback pub fn set_log) + Send + 'static>(&self, f: F) { let cb: Box) + Send + 'static>> = Box::new(Box::new(f)); unsafe{ sys::libvlc_log_set(self.ptr, Some(logging_cb), Box::into_raw(cb) as *mut _); } } /// Returns raw pointer pub fn raw(&self) -> *mut sys::libvlc_instance_t { self.ptr } } impl Drop for Instance { fn drop(&mut self) { unsafe{ sys::libvlc_release(self.ptr); } } } const BUF_SIZE: usize = 1024; // Write log message to the buffer by vsnprintf. unsafe extern "C" fn logging_cb( data: *mut c_void, level: c_int, ctx: *const sys::libvlc_log_t, fmt: *const c_char, args: sys::VaList) { let f: &Box) + Send + 'static> = ::std::mem::transmute(data); let mut buf: [c_char; BUF_SIZE] = [0; BUF_SIZE]; sys::vsnprintf(buf.as_mut_ptr(), BUF_SIZE, fmt, args); f((level as u32).into(), Log{ptr: ctx}, from_cstr_ref(buf.as_ptr()).unwrap()); } /// List of module description. pub struct ModuleDescriptionList { ptr: *mut sys::libvlc_module_description_t, } impl ModuleDescriptionList { /// Returns raw pointer pub fn raw(&self) -> *mut sys::libvlc_module_description_t { self.ptr } } impl Drop for ModuleDescriptionList { fn drop(&mut self) { unsafe{ sys::libvlc_module_description_list_release(self.ptr) }; } } impl<'a> IntoIterator for &'a ModuleDescriptionList { type Item = ModuleDescriptionRef<'a>; type IntoIter = ModuleDescriptionListIter<'a>; fn into_iter(self) -> Self::IntoIter { ModuleDescriptionListIter{ptr: self.ptr, _phantomdata: PhantomData} } } pub struct ModuleDescriptionListIter<'a> { ptr: *mut sys::libvlc_module_description_t, _phantomdata: PhantomData<&'a sys::libvlc_module_description_t>, } /// Description of a module. /// The strings are owned. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct ModuleDescription { pub name: Option, pub shortname: Option, pub longname: Option, pub help: Option, } /// Description of a module. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct ModuleDescriptionRef<'a> { pub name: Option>, pub shortname: Option>, pub longname: Option>, pub help: Option>, } impl<'a> Iterator for ModuleDescriptionListIter<'a> { type Item = ModuleDescriptionRef<'a>; fn next(&mut self) -> Option { unsafe{ if self.ptr.is_null() { return None; } let p = self.ptr; self.ptr = (*p).p_next; Some(ModuleDescriptionRef{ name: from_cstr_ref((*p).psz_name), shortname: from_cstr_ref((*p).psz_shortname), longname: from_cstr_ref((*p).psz_longname), help: from_cstr_ref((*p).psz_help), }) } } } impl<'a> ModuleDescriptionRef<'a> { /// Convert to owned strings. pub fn into_owned(&'a self) -> ModuleDescription { ModuleDescription { name: self.name .as_ref().map(|s| s.clone().into_owned()), shortname: self.shortname.as_ref().map(|s| s.clone().into_owned()), longname: self.name .as_ref().map(|s| s.clone().into_owned()), help: self.shortname.as_ref().map(|s| s.clone().into_owned()), } } } pub fn errmsg() -> Option { unsafe{ from_cstr(sys::libvlc_errmsg()) } } pub fn clearerr() { unsafe{ sys::libvlc_clearerr() }; } #[derive(Clone, Debug)] pub enum Event { MediaMetaChanged(Meta), MediaSubItemAdded, MediaDurationChanged(i64), MediaParsedChanged(i32), MediaFreed, MediaStateChanged(State), MediaSubItemTreeAdded, MediaPlayerMediaChanged, MediaPlayerNothingSpecial, MediaPlayerOpening, MediaPlayerBuffering(f32), MediaPlayerPlaying, MediaPlayerPaused, MediaPlayerStopped, MediaPlayerForward, MediaPlayerBackward, MediaPlayerEndReached, MediaPlayerEncounteredError, MediaPlayerTimeChanged, MediaPlayerPositionChanged(f32), MediaPlayerSeekableChanged, MediaPlayerPausableChanged, MediaPlayerTitleChanged, MediaPlayerSnapshotTaken, MediaPlayerLengthChanged, MediaPlayerVout, MediaPlayerScrambledChanged, MediaListItemAdded, MediaListWillAddItem, MediaListItemDeleted, MediaListWillDeleteItem, MediaListViewItemAdded, MediaListViewWillAddItem, MediaListViewItemDeleted, MediaListViewWillDeleteItem, MediaListPlayerPlayed, MediaListPlayerNextItemSet, MediaListPlayerStopped, MediaDiscovererStarted, MediaDiscovererEnded, VlmMediaAdded(Option, Option), VlmMediaRemoved(Option, Option), VlmMediaChanged(Option, Option), VlmMediaInstanceStarted(Option, Option), VlmMediaInstanceStopped(Option, Option), VlmMediaInstanceStatusInit(Option, Option), VlmMediaInstanceStatusOpening(Option, Option), VlmMediaInstanceStatusPlaying(Option, Option), VlmMediaInstanceStatusPause(Option, Option), VlmMediaInstanceStatusEnd(Option, Option), VlmMediaInstanceStatusError(Option, Option) } pub struct EventManager<'a> { pub(crate) ptr: *mut sys::libvlc_event_manager_t, pub(crate) _phantomdata: ::std::marker::PhantomData<&'a sys::libvlc_event_manager_t>, } impl<'a> EventManager<'a> { pub fn detach(&self, event_type: EventType, registered_callback: *mut c_void) { unsafe { sys::libvlc_event_detach(self.ptr, event_type as i32, Some(event_manager_callback), registered_callback) } } pub fn attach(&self, event_type: EventType, callback: F) -> Result<*mut c_void, ()> where F: Fn(Event, VLCObject) + Send + 'static { // Explicit type annotation is needed let callback: Box> = Box::new(Box::new(callback)); let raw = Box::into_raw(callback) as *mut c_void; let result = unsafe{ sys::libvlc_event_attach( self.ptr, event_type as i32, Some(event_manager_callback), raw) }; if result == 0 { Ok(raw) }else{ Err(()) } } /// Returns raw pointer pub fn raw(&self) -> *mut sys::libvlc_event_manager_t { self.ptr } } unsafe extern "C" fn event_manager_callback(pe: *const sys::libvlc_event_t, data: *mut c_void) { let f: &Box = ::std::mem::transmute(data); f(conv_event(pe), VLCObject{ ptr: (*pe).p_obj }); } // Convert c-style libvlc_event_t to Event fn conv_event(pe: *const sys::libvlc_event_t) -> Event { let event_type: EventType = (unsafe{ (*pe).type_ } as u32).into(); match event_type { EventType::MediaMetaChanged => { unsafe{ Event::MediaMetaChanged((*pe).u.media_meta_changed.meta_type.into()) } }, EventType::MediaSubItemAdded => { Event::MediaSubItemAdded }, EventType::MediaDurationChanged => { unsafe{ Event::MediaDurationChanged((*pe).u.media_duration_changed.new_duration) } }, EventType::MediaParsedChanged => { unsafe{ Event::MediaParsedChanged((*pe).u.media_parsed_changed.new_status) } }, EventType::MediaFreed => { Event::MediaFreed }, EventType::MediaStateChanged => { unsafe{ let new_state: sys::libvlc_state_t = (*pe).u.media_state_changed.new_state.try_into().unwrap(); Event::MediaStateChanged(new_state.into()) } }, EventType::MediaSubItemTreeAdded => { Event::MediaSubItemTreeAdded }, EventType::MediaPlayerMediaChanged => { Event::MediaPlayerMediaChanged }, EventType::MediaPlayerNothingSpecial => { Event::MediaPlayerNothingSpecial }, EventType::MediaPlayerOpening => { Event::MediaPlayerOpening }, EventType::MediaPlayerBuffering => { unsafe{ Event::MediaPlayerBuffering((*pe).u.media_player_buffering.new_cache) } }, EventType::MediaPlayerPlaying => { Event::MediaPlayerPlaying }, EventType::MediaPlayerPaused => { Event::MediaPlayerPaused }, EventType::MediaPlayerStopped => { Event::MediaPlayerStopped }, EventType::MediaPlayerForward => { Event::MediaPlayerForward }, EventType::MediaPlayerBackward => { Event::MediaPlayerBackward }, EventType::MediaPlayerEndReached => { Event::MediaPlayerEndReached }, EventType::MediaPlayerEncounteredError => { Event::MediaPlayerEncounteredError }, EventType::MediaPlayerTimeChanged => { Event::MediaPlayerTimeChanged }, EventType::MediaPlayerPositionChanged => { unsafe{ Event::MediaPlayerPositionChanged((*pe).u.media_player_position_changed.new_position) } }, EventType::MediaPlayerSeekableChanged => { Event::MediaPlayerSeekableChanged }, EventType::MediaPlayerPausableChanged => { Event::MediaPlayerPausableChanged }, EventType::MediaPlayerTitleChanged => { Event::MediaPlayerTitleChanged }, EventType::MediaPlayerSnapshotTaken => { Event::MediaPlayerSnapshotTaken }, EventType::MediaPlayerLengthChanged => { Event::MediaPlayerLengthChanged }, EventType::MediaPlayerVout => { Event::MediaPlayerVout }, EventType::MediaPlayerScrambledChanged => { Event::MediaPlayerScrambledChanged }, EventType::MediaListItemAdded => { Event::MediaListItemAdded }, EventType::MediaListWillAddItem => { Event::MediaListWillAddItem }, EventType::MediaListItemDeleted => { Event::MediaListItemDeleted }, EventType::MediaListWillDeleteItem => { Event::MediaListWillDeleteItem }, EventType::MediaListViewItemAdded => { Event::MediaListViewItemAdded }, EventType::MediaListViewWillAddItem => { Event::MediaListViewWillAddItem }, EventType::MediaListViewItemDeleted => { Event::MediaListViewItemDeleted }, EventType::MediaListViewWillDeleteItem => { Event::MediaListViewWillDeleteItem }, EventType::MediaListPlayerPlayed => { Event::MediaListPlayerPlayed }, EventType::MediaListPlayerNextItemSet => { Event::MediaListPlayerNextItemSet }, EventType::MediaListPlayerStopped => { Event::MediaListPlayerStopped }, EventType::MediaDiscovererStarted => { Event::MediaDiscovererStarted }, EventType::MediaDiscovererEnded => { Event::MediaDiscovererEnded }, EventType::VlmMediaAdded => { unsafe { Event::VlmMediaAdded(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaRemoved => { unsafe { Event::VlmMediaRemoved(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaChanged => { unsafe { Event::VlmMediaChanged(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaInstanceStarted => { unsafe { Event::VlmMediaInstanceStarted(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaInstanceStopped => { unsafe { Event::VlmMediaInstanceStopped(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaInstanceStatusInit => { unsafe { Event::VlmMediaInstanceStatusInit(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaInstanceStatusOpening => { unsafe { Event::VlmMediaInstanceStatusOpening(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaInstanceStatusPlaying => { unsafe { Event::VlmMediaInstanceStatusPlaying(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaInstanceStatusPause => { unsafe { Event::VlmMediaInstanceStatusPause(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaInstanceStatusEnd => { unsafe { Event::VlmMediaInstanceStatusEnd(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, EventType::VlmMediaInstanceStatusError => { unsafe { Event::VlmMediaInstanceStatusError(from_cstr((*pe).u.vlm_media_event.psz_instance_name), from_cstr((*pe).u.vlm_media_event.psz_media_name)) } }, } } pub struct VLCObject { ptr: *mut c_void, } impl VLCObject { /// Returns raw pointer pub fn raw(&self) -> *mut c_void { self.ptr } } pub struct Log { pub(crate) ptr: *const sys::libvlc_log_t } impl Log { /// Returns raw pointer pub fn raw(&self) -> *const sys::libvlc_log_t { self.ptr } }