495 lines
16 KiB
Rust
495 lines
16 KiB
Rust
// Copyright (c) 2015 T. Okubo
|
|
// This file is part of vlc-rs.
|
|
// Licensed under the MIT license, see the LICENSE file.
|
|
|
|
use crate::enums::*;
|
|
use crate::tools::{from_cstr, from_cstr_ref, linked_list_iter, to_cstr};
|
|
use libc::{c_char, c_int, c_void};
|
|
use std::borrow::Cow;
|
|
use std::convert::TryInto;
|
|
use std::ffi::CString;
|
|
use std::fmt;
|
|
use std::i32;
|
|
use std::ptr;
|
|
use vlc_sys as sys;
|
|
|
|
#[derive(Debug)]
|
|
pub struct InternalError;
|
|
|
|
impl fmt::Display for InternalError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "libvlc internal error")
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for InternalError {}
|
|
|
|
|
|
linked_list_iter!(
|
|
libvlc_module_description, // C raw type
|
|
module_description, // Rust base struct name
|
|
{
|
|
name: (String, str),
|
|
shortname: (String, str),
|
|
longname: (String, str),
|
|
help: (String, str),
|
|
} // fields
|
|
);
|
|
|
|
linked_list_iter!(
|
|
libvlc_audio_output, // C raw type
|
|
audio_output, // Rust base struct name
|
|
{
|
|
name: (String, str),
|
|
description: (String, str),
|
|
} // fields
|
|
);
|
|
|
|
linked_list_iter!(
|
|
libvlc_audio_output_device, // C raw type
|
|
audio_output_device, // Rust base struct name
|
|
{
|
|
device: (String, str),
|
|
description: (String, str),
|
|
} // fields
|
|
);
|
|
|
|
/// 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<Vec<String>>) -> Option<Instance> {
|
|
let args_c_ptr: Vec<*const c_char>;
|
|
let args_c: Vec<CString>;
|
|
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> {
|
|
Instance::with_args(None)
|
|
}
|
|
|
|
/// Try to start a user interface for the libvlc instance.
|
|
pub fn add_intf(&self, name: &str) -> Result<(), InternalError> {
|
|
let cstr = to_cstr(name);
|
|
|
|
let result = unsafe { sys::libvlc_add_intf(self.ptr, cstr.as_ptr()) };
|
|
|
|
if result == 0 {
|
|
Ok(())
|
|
} else {
|
|
Err(InternalError)
|
|
}
|
|
}
|
|
|
|
/// 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<ModuleDescriptionList> {
|
|
unsafe {
|
|
let p = sys::libvlc_audio_filter_list_get(self.ptr);
|
|
if p.is_null() {
|
|
None
|
|
} else {
|
|
Some(ModuleDescriptionList::new(p))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns a list of video filters that are available.
|
|
pub fn video_filter_list_get(&self) -> Option<ModuleDescriptionList> {
|
|
unsafe {
|
|
let p = sys::libvlc_video_filter_list_get(self.ptr);
|
|
if p.is_null() {
|
|
None
|
|
} else {
|
|
Some(ModuleDescriptionList::new(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<F: Fn(LogLevel, Log, Cow<str>) + Send + 'static>(&self, f: F) {
|
|
let cb: Box<Box<dyn Fn(LogLevel, Log, Cow<str>) + 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: *mut sys::__va_list_tag,
|
|
) {
|
|
let f: &Box<dyn Fn(LogLevel, Log, Cow<str>) + Send + 'static> = ::std::mem::transmute(data);
|
|
let mut buf: [c_char; BUF_SIZE] = [0; BUF_SIZE];
|
|
|
|
sys::vsnprintf(buf.as_mut_ptr(), BUF_SIZE.try_into().unwrap(), fmt, args);
|
|
|
|
f(
|
|
(level as u32).into(),
|
|
Log { ptr: ctx },
|
|
from_cstr_ref(buf.as_ptr()).unwrap(),
|
|
);
|
|
}
|
|
|
|
pub fn errmsg() -> Option<String> {
|
|
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<String>, Option<String>),
|
|
VlmMediaRemoved(Option<String>, Option<String>),
|
|
VlmMediaChanged(Option<String>, Option<String>),
|
|
VlmMediaInstanceStarted(Option<String>, Option<String>),
|
|
VlmMediaInstanceStopped(Option<String>, Option<String>),
|
|
VlmMediaInstanceStatusInit(Option<String>, Option<String>),
|
|
VlmMediaInstanceStatusOpening(Option<String>, Option<String>),
|
|
VlmMediaInstanceStatusPlaying(Option<String>, Option<String>),
|
|
VlmMediaInstanceStatusPause(Option<String>, Option<String>),
|
|
VlmMediaInstanceStatusEnd(Option<String>, Option<String>),
|
|
VlmMediaInstanceStatusError(Option<String>, Option<String>),
|
|
}
|
|
|
|
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 attach<F>(&self, event_type: EventType, callback: F) -> Result<(), InternalError>
|
|
where
|
|
F: Fn(Event, VLCObject) + Send + 'static,
|
|
{
|
|
// Explicit type annotation is needed
|
|
let callback: Box<Box<dyn Fn(Event, VLCObject) + Send + 'static>> =
|
|
Box::new(Box::new(callback));
|
|
|
|
let result = unsafe {
|
|
sys::libvlc_event_attach(
|
|
self.ptr,
|
|
event_type as i32,
|
|
Some(event_manager_callback),
|
|
Box::into_raw(callback) as *mut c_void,
|
|
)
|
|
};
|
|
|
|
if result == 0 {
|
|
Ok(())
|
|
} else {
|
|
Err(InternalError)
|
|
}
|
|
}
|
|
|
|
/// 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<dyn Fn(Event, VLCObject) + Send + 'static> = ::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
|
|
}
|
|
}
|