diff options
Diffstat (limited to '')
-rw-r--r-- | libmpv2/examples/events.rs | 93 | ||||
-rw-r--r-- | libmpv2/examples/opengl.rs | 139 | ||||
-rw-r--r-- | libmpv2/examples/protocol.rs | 87 |
3 files changed, 319 insertions, 0 deletions
diff --git a/libmpv2/examples/events.rs b/libmpv2/examples/events.rs new file mode 100644 index 0000000..8f7c79f --- /dev/null +++ b/libmpv2/examples/events.rs @@ -0,0 +1,93 @@ +// yt - A fully featured command line YouTube client +// +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Yt. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. + +use libmpv2::{events::*, mpv_node::MpvNode, *}; + +use std::{collections::HashMap, env, thread, time::Duration}; + +const VIDEO_URL: &str = "https://www.youtube.com/watch?v=VLnWf1sQkjY"; + +fn main() -> Result<()> { + let path = env::args() + .nth(1) + .unwrap_or_else(|| String::from(VIDEO_URL)); + + // Create an `Mpv` and set some properties. + let mpv = Mpv::with_initializer(|init| { + init.set_property("vo", "null")?; + Ok(()) + }) + .unwrap(); + mpv.set_property("volume", 15)?; + + let mut ev_ctx = EventContext::new(mpv.ctx); + ev_ctx.disable_deprecated_events()?; + ev_ctx.observe_property("volume", Format::Int64, 0)?; + ev_ctx.observe_property("demuxer-cache-state", Format::Node, 0)?; + + crossbeam::scope(|scope| { + scope.spawn(|_| { + mpv.command("loadfile", &[&path, "append-play"]).unwrap(); + + thread::sleep(Duration::from_secs(3)); + + mpv.set_property("volume", 25).unwrap(); + + thread::sleep(Duration::from_secs(5)); + + // Trigger `Event::EndFile`. + mpv.command("playlist-next", &["force"]).unwrap(); + }); + scope.spawn(move |_| loop { + let ev = ev_ctx.wait_event(600.).unwrap_or(Err(Error::Null)); + + match ev { + Ok(Event::EndFile(r)) => { + println!("Exiting! Reason: {:?}", r); + break; + } + + Ok(Event::PropertyChange { + name: "demuxer-cache-state", + change: PropertyData::Node(mpv_node), + .. + }) => { + let ranges = seekable_ranges(mpv_node); + println!("Seekable ranges updated: {:?}", ranges); + } + Ok(e) => println!("Event triggered: {:?}", e), + Err(e) => println!("Event errored: {:?}", e), + } + }); + }) + .unwrap(); + Ok(()) +} + +fn seekable_ranges(demuxer_cache_state: MpvNode) -> Vec<(f64, f64)> { + let mut res = Vec::new(); + let props = demuxer_cache_state + .map() + .unwrap() + .collect::<HashMap<_, _>>(); + let ranges = props + .get("seekable-ranges") + .unwrap() + .clone() + .array() + .unwrap(); + for node in ranges { + let range = node.map().unwrap().collect::<HashMap<_, _>>(); + let start = range.get("start").unwrap().f64().unwrap(); + let end = range.get("end").unwrap().f64().unwrap(); + res.push((start, end)); + } + res +} diff --git a/libmpv2/examples/opengl.rs b/libmpv2/examples/opengl.rs new file mode 100644 index 0000000..1de307f --- /dev/null +++ b/libmpv2/examples/opengl.rs @@ -0,0 +1,139 @@ +// yt - A fully featured command line YouTube client +// +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Yt. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. + +use libmpv2::{ + render::{OpenGLInitParams, RenderContext, RenderParam, RenderParamApiType}, + Mpv, +}; +use std::{env, ffi::c_void}; + +fn get_proc_address(display: &sdl2::VideoSubsystem, name: &str) -> *mut c_void { + display.gl_get_proc_address(name) as *mut c_void +} + +const VIDEO_URL: &str = "test-data/jellyfish.mp4"; + +#[derive(Debug)] +enum UserEvent { + MpvEventAvailable, + RedrawRequested, +} + +fn main() { + let (window, mut events_loop, event_subsystem, video, _context) = create_sdl2_context(); + + let path = env::args() + .nth(1) + .unwrap_or_else(|| String::from(VIDEO_URL)); + + let mut mpv = Mpv::with_initializer(|init| { + init.set_property("vo", "libmpv")?; + Ok(()) + }) + .unwrap(); + let mut render_context = RenderContext::new( + unsafe { mpv.ctx.as_mut() }, + vec![ + RenderParam::ApiType(RenderParamApiType::OpenGl), + RenderParam::InitParams(OpenGLInitParams { + get_proc_address, + ctx: video, + }), + ], + ) + .expect("Failed creating render context"); + + event_subsystem + .register_custom_event::<UserEvent>() + .unwrap(); + + mpv.event_context_mut().disable_deprecated_events().unwrap(); + + let event_sender = event_subsystem.event_sender(); + render_context.set_update_callback(move || { + event_sender + .push_custom_event(UserEvent::RedrawRequested) + .unwrap(); + }); + + let event_sender = event_subsystem.event_sender(); + mpv.event_context_mut().set_wakeup_callback(move || { + event_sender + .push_custom_event(UserEvent::MpvEventAvailable) + .unwrap(); + }); + mpv.command("loadfile", &[&path, "replace"]).unwrap(); + + 'render: loop { + for event in events_loop.poll_iter() { + use sdl2::event::Event; + + if event.is_user_event() { + match event.as_user_event_type::<UserEvent>().unwrap() { + UserEvent::RedrawRequested => { + let (width, height) = window.drawable_size(); + render_context + .render::<sdl2::VideoSubsystem>(0, width as _, height as _, true) + .expect("Failed to draw on sdl2 window"); + window.gl_swap_window(); + } + UserEvent::MpvEventAvailable => loop { + match mpv.event_context_mut().wait_event(0.0) { + Some(Ok(libmpv2::events::Event::EndFile(_))) => { + break 'render; + } + Some(Ok(mpv_event)) => { + eprintln!("MPV event: {:?}", mpv_event); + } + Some(Err(err)) => { + eprintln!("MPV Error: {}", err); + break 'render; + } + None => break, + } + }, + } + } + + match event { + Event::Quit { .. } => { + break 'render; + } + _ => (), + } + } + } +} + +fn create_sdl2_context() -> ( + sdl2::video::Window, + sdl2::EventPump, + sdl2::EventSubsystem, + sdl2::VideoSubsystem, + sdl2::video::GLContext, +) { + let sdl = sdl2::init().unwrap(); + let video = sdl.video().unwrap(); + let event_subsystem = sdl.event().unwrap(); + let gl_attr = video.gl_attr(); + gl_attr.set_context_profile(sdl2::video::GLProfile::Core); + gl_attr.set_context_version(3, 3); + gl_attr.set_context_flags().forward_compatible().set(); + let window = video + .window("OpenGL mpv", 960, 540) + .opengl() + .resizable() + .build() + .unwrap(); + let gl_context = window.gl_create_context().unwrap(); + let event_loop = sdl.event_pump().unwrap(); + + (window, event_loop, event_subsystem, video, gl_context) +} diff --git a/libmpv2/examples/protocol.rs b/libmpv2/examples/protocol.rs new file mode 100644 index 0000000..46702d6 --- /dev/null +++ b/libmpv2/examples/protocol.rs @@ -0,0 +1,87 @@ +// yt - A fully featured command line YouTube client +// +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Yt. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. + +use std::{ + env, + fs::File, + io::{Read, Seek, SeekFrom}, + mem, thread, + time::Duration, +}; + +#[cfg(all(not(test), not(feature = "protocols")))] +compile_error!("The feature `protocols` needs to be enabled for this example`"); + +#[cfg(feature = "protocols")] +fn main() { + use libmpv2::{protocol::*, *}; + + let path = format!( + "filereader://{}", + env::args() + .nth(1) + .expect("Expected path to local media as argument, found nil.") + ); + + let protocol = unsafe { + Protocol::new( + "filereader".into(), + (), + open, + close, + read, + Some(seek), + Some(size), + ) + }; + + let mpv = Mpv::new().unwrap(); + mpv.set_property("volume", 25).unwrap(); + + let proto_ctx = mpv.create_protocol_context(); + proto_ctx.register(protocol).unwrap(); + + mpv.command("loadfile", &[&path, "append-play"]).unwrap(); + + thread::sleep(Duration::from_secs(10)); + + mpv.command("seek", &["15"]).unwrap(); + + thread::sleep(Duration::from_secs(5)); +} + +fn open(_: &mut (), uri: &str) -> File { + // Open the file, and strip the `filereader://` part + let ret = File::open(&uri[13..]).unwrap(); + + println!("Opened file[{}], ready for orders o7", &uri[13..]); + ret +} + +fn close(_: Box<File>) { + println!("Closing file, bye bye~~"); +} + +fn read(cookie: &mut File, buf: &mut [i8]) -> i64 { + unsafe { + let forbidden_magic = mem::transmute::<&mut [i8], &mut [u8]>(buf); + + cookie.read(forbidden_magic).unwrap() as _ + } +} + +fn seek(cookie: &mut File, offset: i64) -> i64 { + println!("Seeking to byte {}", offset); + cookie.seek(SeekFrom::Start(offset as u64)).unwrap() as _ +} + +fn size(cookie: &mut File) -> i64 { + cookie.metadata().unwrap().len() as _ +} |