// 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) }