many changes

This commit is contained in:
Erica Z 2024-11-03 14:59:54 +01:00
parent dbd209f904
commit 225292d08a
6 changed files with 290 additions and 47 deletions

View file

@ -4,7 +4,7 @@ mod error;
pub use error::Error;
mod format;
pub use format::SetProperty;
pub use format::{GetProperty, SetProperty};
mod handle;
pub use handle::Handle;

View file

@ -17,7 +17,7 @@ pub enum Event {
PlaybackRestart,
PropertyChange(PropertyEvent),
//QueueOverflow,
//Hook,
Hook(HookEvent),
Unknown(u32),
}
@ -55,15 +55,12 @@ pub struct LogMessageEvent {
pub struct PropertyEvent {
pub reply_userdata: u64,
pub name: String,
pub value: PropertyEventValue,
//pub value: PropertyEventValue,
}
#[derive(Clone, Debug)]
pub enum PropertyEventValue {
None,
String(String),
OsdString(String),
Flag(bool),
Int64(i64),
Double(f64),
pub struct HookEvent {
pub reply_userdata: u64,
pub name: String,
pub id: u64,
}

View file

@ -1,63 +1,123 @@
use super::ffi;
use super::{ffi, Error};
use std::ffi::{c_char, c_int, c_void, CString};
pub trait SetProperty {
/// # Safety
/// see mpv_set_property
unsafe fn set_property(self, ctx: *mut ffi::mpv_handle, name: *const c_char) -> c_int;
unsafe fn set_property(
self,
ctx: *mut ffi::mpv_handle,
name: *const c_char,
) -> Result<(), Error>;
}
impl SetProperty for String {
unsafe fn set_property(self, ctx: *mut ffi::mpv_handle, name: *const c_char) -> c_int {
unsafe fn set_property(
self,
ctx: *mut ffi::mpv_handle,
name: *const c_char,
) -> Result<(), Error> {
// should add zero terminator directly
let value = CString::new(self).expect("null bytes in string property value");
let value_ptr: *const c_char = value.as_ptr();
ffi::mpv_set_property(
Error::from_return_code(ffi::mpv_set_property(
ctx,
name,
ffi::mpv_format_MPV_FORMAT_STRING,
std::ptr::from_ref::<*const c_char>(&value_ptr) as *mut c_void,
)
))
}
}
impl<'a> SetProperty for &'a str {
unsafe fn set_property(self, ctx: *mut ffi::mpv_handle, name: *const c_char) -> c_int {
unsafe fn set_property(
self,
ctx: *mut ffi::mpv_handle,
name: *const c_char,
) -> Result<(), Error> {
// need to add zero terminator
String::set_property(self.into(), ctx, name)
}
}
impl SetProperty for bool {
unsafe fn set_property(self, ctx: *mut ffi::mpv_handle, name: *const c_char) -> c_int {
unsafe fn set_property(
self,
ctx: *mut ffi::mpv_handle,
name: *const c_char,
) -> Result<(), Error> {
let value: c_int = if self { 1 } else { 0 };
ffi::mpv_set_property(
Error::from_return_code(ffi::mpv_set_property(
ctx,
name,
ffi::mpv_format_MPV_FORMAT_FLAG,
std::ptr::from_ref::<c_int>(&value) as *mut c_void,
)
))
}
}
impl SetProperty for i64 {
unsafe fn set_property(self, ctx: *mut ffi::mpv_handle, name: *const c_char) -> c_int {
ffi::mpv_set_property(
unsafe fn set_property(
self,
ctx: *mut ffi::mpv_handle,
name: *const c_char,
) -> Result<(), Error> {
Error::from_return_code(ffi::mpv_set_property(
ctx,
name,
ffi::mpv_format_MPV_FORMAT_INT64,
std::ptr::from_ref::<i64>(&self) as *mut c_void,
)
))
}
}
impl SetProperty for f64 {
unsafe fn set_property(self, ctx: *mut ffi::mpv_handle, name: *const c_char) -> c_int {
ffi::mpv_set_property(
unsafe fn set_property(
self,
ctx: *mut ffi::mpv_handle,
name: *const c_char,
) -> Result<(), Error> {
Error::from_return_code(ffi::mpv_set_property(
ctx,
name,
ffi::mpv_format_MPV_FORMAT_DOUBLE,
std::ptr::from_ref::<f64>(&self) as *mut c_void,
)
))
}
}
pub trait GetProperty: Sized {
/// # Safety
/// see mpv_get_property
unsafe fn get_property(ctx: *mut ffi::mpv_handle, name: *const c_char) -> Result<Self, Error>;
}
impl GetProperty for bool {
unsafe fn get_property(ctx: *mut ffi::mpv_handle, name: *const c_char) -> Result<Self, Error> {
let mut value: c_int = -1;
Error::from_return_code(ffi::mpv_get_property(
ctx,
name,
ffi::mpv_format_MPV_FORMAT_FLAG,
std::ptr::from_mut::<c_int>(&mut value) as *mut c_void,
))?;
match value {
0 => Ok(false),
1 => Ok(true),
_ => unreachable!(),
}
}
}
impl GetProperty for i64 {
unsafe fn get_property(ctx: *mut ffi::mpv_handle, name: *const c_char) -> Result<Self, Error> {
let mut value: i64 = -1;
Error::from_return_code(ffi::mpv_get_property(
ctx,
name,
ffi::mpv_format_MPV_FORMAT_INT64,
std::ptr::from_mut::<i64>(&mut value) as *mut c_void,
))?;
Ok(value)
}
}

View file

@ -1,6 +1,6 @@
use super::{event, ffi, Error, Event as MpvEvent, SetProperty};
use super::{event, ffi, Error, Event as MpvEvent, GetProperty, SetProperty};
use event::{
EndFileEvent, EndFileReason, LogMessageEvent, PropertyEvent, PropertyEventValue, StartFileEvent,
EndFileEvent, EndFileReason, HookEvent, LogMessageEvent, PropertyEvent, StartFileEvent,
};
use event_listener::{Event, EventListener, IntoNotification};
use std::cell::{RefCell, RefMut};
@ -83,7 +83,12 @@ impl Handle {
pub fn set_property(&self, name: &str, value: impl SetProperty) -> Result<(), Error> {
// need to add zero terminator
let name = CString::new(name).expect("null bytes in property name");
Error::from_return_code(unsafe { value.set_property(self.inner.as_ptr(), name.as_ptr()) })
unsafe { value.set_property(self.inner.as_ptr(), name.as_ptr()) }
}
pub fn get_property<T: GetProperty>(&self, name: &str) -> Result<T, Error> {
let name = CString::new(name).expect("null bytes in property name");
unsafe { T::get_property(self.inner.as_ptr(), name.as_ptr()) }
}
pub fn command<'a>(&self, args: impl IntoIterator<Item = &'a str>) -> Result<(), Error> {
@ -105,14 +110,14 @@ impl Handle {
Error::from_return_code(unsafe { ffi::mpv_command(self.inner.as_ptr(), args) })
}
pub fn observe_property_i64(&self, reply_userdata: u64, name: &str) -> Result<(), Error> {
pub fn observe_property(&self, reply_userdata: u64, name: &str) -> Result<(), Error> {
let name = CString::new(name).expect("null bytes in property name");
Error::from_return_code(unsafe {
ffi::mpv_observe_property(
self.inner.as_ptr(),
reply_userdata,
name.as_ptr(),
ffi::mpv_format_MPV_FORMAT_INT64,
ffi::mpv_format_MPV_FORMAT_NONE,
)
})
}
@ -123,6 +128,19 @@ impl Handle {
Error::from_return_code(rc).map(|()| rc as u32)
}
pub fn add_hook(&self, reply_userdata: u64, name: &str, priority: i32) -> Result<(), Error> {
let name = CString::new(name).expect("null bytes in property name");
Error::from_return_code(unsafe {
ffi::mpv_hook_add(self.inner.as_ptr(), reply_userdata, name.as_ptr(), priority)
})
}
/// # Safety
/// > It is explicitly undefined behavior to call this more than once for each MPV_EVENT_HOOK, to pass an incorrect ID, or to call this on a mpv_handle different from the one that registered the handler and received the event.
pub unsafe fn continue_hook_unchecked(&self, id: u64) -> Result<(), Error> {
Error::from_return_code(unsafe { ffi::mpv_hook_continue(self.inner.as_ptr(), id) })
}
// should take listener before we drain the event queue, so we don't miss any notifications
pub fn wakeup_listener(&self) -> EventListener {
self.wakeup.listen()
@ -196,13 +214,17 @@ impl Handle {
name: unsafe { CStr::from_ptr(data.name) }
.to_string_lossy()
.into(),
value: match data.format {
ffi::mpv_format_MPV_FORMAT_NONE => PropertyEventValue::None,
ffi::mpv_format_MPV_FORMAT_INT64 => {
PropertyEventValue::Int64(*unsafe { &*(data.data as *mut i64) })
}))
}
_ => todo!("{:?}", data.format),
},
ffi::mpv_event_id_MPV_EVENT_HOOK => {
let data = unsafe { &*(event.data as *mut ffi::mpv_event_hook) };
Some(MpvEvent::Hook(HookEvent {
reply_userdata: event.reply_userdata,
name: unsafe { CStr::from_ptr(data.name) }
.to_string_lossy()
.into(),
id: data.id,
}))
}

View file

@ -1,11 +1,33 @@
use crate::mpv;
use crate::signal::SignalEmitter;
use event_listener::EventListener;
use std::cell::{Ref, RefCell};
use url::Url;
pub struct Playbin {
mpv: mpv::Handle,
pub trait PlaybinEntry {
fn url(&self) -> &Url;
}
impl Default for Playbin {
impl PlaybinEntry for Url {
fn url(&self) -> &Url {
self
}
}
// E: generic entry type
pub struct Playbin<E> {
mpv: mpv::Handle,
entries: RefCell<Vec<E>>,
paused_changed: SignalEmitter<()>,
current_entry_changed: SignalEmitter<()>,
entry_inserted: SignalEmitter<u32>,
stopped: SignalEmitter<()>,
entry_removed: SignalEmitter<u32>,
}
impl<E> Default for Playbin<E> {
fn default() -> Self {
let mpv = mpv::Handle::new();
mpv.set_property("audio-client-name", "audrey").unwrap();
@ -14,14 +36,118 @@ impl Default for Playbin {
mpv.set_property("prefetch-playlist", true).unwrap();
mpv.set_property("gapless-audio", true).unwrap();
mpv.command(["loadfile", "https://www.youtube.com/watch?v=19y8YTbvri8"])
.unwrap();
mpv.observe_property(0, "pause").unwrap();
mpv.observe_property(1, "playlist-pos").unwrap();
Self { mpv }
// "Useful to drain property changes before a new file is loaded."
mpv.add_hook(0, "on_before_start_file", 0).unwrap();
Self {
mpv,
entries: RefCell::new(vec![]),
paused_changed: Default::default(),
current_entry_changed: Default::default(),
entry_inserted: Default::default(),
stopped: Default::default(),
entry_removed: Default::default(),
}
}
}
impl Playbin {
impl<E> Playbin<E>
where
E: PlaybinEntry,
{
pub fn paused(&self) -> bool {
self.mpv.get_property("pause").unwrap()
}
pub fn set_paused(&self, paused: bool) {
self.mpv.set_property("pause", paused).unwrap();
}
pub fn position(&self) -> Option<f64> {
todo!()
}
pub fn current_entry(&self) -> Option<u32> {
self.mpv
.get_property::<i64>("playlist-pos")
.unwrap()
.try_into()
.ok()
}
pub fn seek(&self, _position: f64) {
todo!()
}
pub fn next_entry(&self) {
self.mpv.command(["playlist-next"]).unwrap();
}
pub fn prev_entry(&self) {
self.mpv.command(["playlist-prev"]).unwrap();
}
pub fn play_entry(&self, index: u32) {
self.mpv
.command(["playlist-play-index", &index.to_string()])
.unwrap();
}
pub fn entries(&self) -> Ref<'_, [E]> {
Ref::map(self.entries.borrow(), Vec::as_ref)
}
pub fn push_entry(&self, entry: E) {
let mut entries = self.entries.borrow_mut();
self.mpv
.command(["loadfile", entry.url().as_str(), "append-play"])
.unwrap();
let index = entries.len();
entries.push(entry);
drop(entries);
self.entry_inserted.emit(index as u32);
}
pub fn insert_entry(&self, index: u32, entry: E) {
let mut entries = self.entries.borrow_mut();
self.mpv
.command(["loadfile", entry.url().as_str(), "insert-at-play"])
.unwrap();
entries.insert(index as usize, entry);
drop(entries);
self.entry_inserted.emit(index);
}
// stop playback and clear playlist
pub fn stop(&self) {
let mut entries = self.entries.borrow_mut();
self.mpv.command(["stop"]).unwrap();
entries.clear();
drop(entries);
self.stopped.emit(());
}
pub fn remove_entry(&self, index: u32) {
let mut entries = self.entries.borrow_mut();
self.mpv.command(["remove", &index.to_string()]).unwrap();
entries.remove(index as usize);
drop(entries);
self.entry_removed.emit(index);
}
pub fn move_entry(&self, _from: u32, _to: u32) {
todo!()
}
pub fn tick(&self) -> EventListener {
let listener = self.mpv.wakeup_listener();
while let Some(event) = self.mpv.wait_event(0.0) {
@ -31,11 +157,42 @@ impl Playbin {
}
fn handle_event(&self, event: mpv::Event) {
println!("mpv event {:?}", event);
match event {
mpv::Event::PropertyChange(event) => match event.reply_userdata {
0 => {
assert_eq!(&event.name, "pause");
self.paused_changed.emit(());
println!("new paused! {:?}", self.paused());
}
1 => {
assert_eq!(&event.name, "playlist-pos");
self.current_entry_changed.emit(());
println!("new current_entry! {:?}", self.current_entry());
}
_ => unreachable!(),
},
mpv::Event::Hook(event) => match event.reply_userdata {
0 => {
assert_eq!(&event.name, "on_before_start_file");
// just use this as a barrier
println!("on_before_start_file triggered");
unsafe {
self.mpv.continue_hook_unchecked(event.id).unwrap();
}
}
_ => unreachable!(),
},
_ => println!("mpv event {:?}", event),
}
}
}
impl Drop for Playbin {
impl<E> Drop for Playbin<E> {
fn drop(&mut self) {
println!("dropping Playbin2");
self.mpv.command(["quit"]).unwrap();

View file

@ -28,7 +28,7 @@ mod imp {
pub(super) setup: crate::ui::Setup,
pub(super) api: RefCell<Option<crate::subsonic_vala::Client>>,
playbin2: Rc<crate::playbin2::Playbin>,
playbin2: Rc<crate::playbin2::Playbin<url::Url>>,
}
#[glib::object_subclass]
@ -52,6 +52,13 @@ mod imp {
fn constructed(&self) {
self.parent_constructed();
self.playbin2.tick();
self.playbin2.push_entry(
"https://www.youtube.com/watch?v=19y8YTbvri8"
.try_into()
.unwrap(),
);
let playbin = Rc::downgrade(&self.playbin2);
glib::spawn_future_local(glib::clone!(async move {
loop {