diff options
Diffstat (limited to 'libmpv2/src/mpv/render.rs')
-rw-r--r-- | libmpv2/src/mpv/render.rs | 406 |
1 files changed, 0 insertions, 406 deletions
diff --git a/libmpv2/src/mpv/render.rs b/libmpv2/src/mpv/render.rs deleted file mode 100644 index 91db34e..0000000 --- a/libmpv2/src/mpv/render.rs +++ /dev/null @@ -1,406 +0,0 @@ -// 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 crate::{mpv::mpv_err, Error, Result}; -use std::collections::HashMap; -use std::ffi::{c_void, CStr}; -use std::os::raw::c_int; -use std::ptr; - -type DeleterFn = unsafe fn(*mut c_void); - -pub struct RenderContext { - ctx: *mut libmpv2_sys::mpv_render_context, - update_callback_cleanup: Option<Box<dyn FnOnce()>>, -} - -/// For initializing the mpv OpenGL state via RenderParam::OpenGLInitParams -pub struct OpenGLInitParams<GLContext> { - /// This retrieves OpenGL function pointers, and will use them in subsequent - /// operation. - /// Usually, you can simply call the GL context APIs from this callback (e.g. - /// glXGetProcAddressARB or wglGetProcAddress), but some APIs do not always - /// return pointers for all standard functions (even if present); in this - /// case you have to compensate by looking up these functions yourself when - /// libmpv wants to resolve them through this callback. - /// libmpv will not normally attempt to resolve GL functions on its own, nor - /// does it link to GL libraries directly. - pub get_proc_address: fn(ctx: &GLContext, name: &str) -> *mut c_void, - - /// Value passed as ctx parameter to get_proc_address(). - pub ctx: GLContext, -} - -/// For RenderParam::FBO -pub struct FBO { - pub fbo: i32, - pub width: i32, - pub height: i32, -} - -#[repr(u32)] -#[derive(Clone)] -pub enum RenderFrameInfoFlag { - Present = libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_PRESENT, - Redraw = libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_REDRAW, - Repeat = libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_REPEAT, - BlockVSync = libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_BLOCK_VSYNC, -} - -impl From<u64> for RenderFrameInfoFlag { - // mpv_render_frame_info_flag is u32, but mpv_render_frame_info.flags is u64 o\ - fn from(val: u64) -> Self { - let val = val as u32; - match val { - libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_PRESENT => { - RenderFrameInfoFlag::Present - } - libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_REDRAW => { - RenderFrameInfoFlag::Redraw - } - libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_REPEAT => { - RenderFrameInfoFlag::Repeat - } - libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_BLOCK_VSYNC => { - RenderFrameInfoFlag::BlockVSync - } - _ => panic!("Tried converting invalid value to RenderFrameInfoFlag"), - } - } -} - -#[derive(Clone)] -pub struct RenderFrameInfo { - pub flags: RenderFrameInfoFlag, - pub target_time: i64, -} - -pub enum RenderParamApiType { - OpenGl, -} - -pub enum RenderParam<GLContext> { - Invalid, - ApiType(RenderParamApiType), - InitParams(OpenGLInitParams<GLContext>), - FBO(FBO), - FlipY(bool), - Depth(i32), - ICCProfile(Vec<u8>), - AmbientLight(i32), - X11Display(*const c_void), - WaylandDisplay(*const c_void), - AdvancedControl(bool), - NextFrameInfo(RenderFrameInfo), - BlockForTargetTime(bool), - SkipRendering(bool), -} - -impl<C> From<&RenderParam<C>> for u32 { - fn from(val: &RenderParam<C>) -> Self { - match val { - RenderParam::Invalid => 0, - RenderParam::ApiType(_) => 1, - RenderParam::InitParams(_) => 2, - RenderParam::FBO(_) => 3, - RenderParam::FlipY(_) => 4, - RenderParam::Depth(_) => 5, - RenderParam::ICCProfile(_) => 6, - RenderParam::AmbientLight(_) => 7, - RenderParam::X11Display(_) => 8, - RenderParam::WaylandDisplay(_) => 9, - RenderParam::AdvancedControl(_) => 10, - RenderParam::NextFrameInfo(_) => 11, - RenderParam::BlockForTargetTime(_) => 12, - RenderParam::SkipRendering(_) => 13, - } - } -} - -unsafe extern "C" fn gpa_wrapper<GLContext>(ctx: *mut c_void, name: *const i8) -> *mut c_void { - if ctx.is_null() { - panic!("ctx for get_proc_address wrapper is NULL"); - } - - let params: *mut OpenGLInitParams<GLContext> = ctx as _; - let params = &*params; - (params.get_proc_address)( - ¶ms.ctx, - CStr::from_ptr(name) - .to_str() - .expect("Could not convert function name to str"), - ) -} - -unsafe extern "C" fn ru_wrapper<F: Fn() + Send + 'static>(ctx: *mut c_void) { - if ctx.is_null() { - panic!("ctx for render_update wrapper is NULL"); - } - - (*(ctx as *mut F))(); -} - -impl<C> From<OpenGLInitParams<C>> for libmpv2_sys::mpv_opengl_init_params { - fn from(val: OpenGLInitParams<C>) -> Self { - Self { - get_proc_address: Some(gpa_wrapper::<OpenGLInitParams<C>>), - get_proc_address_ctx: Box::into_raw(Box::new(val)) as *mut c_void, - } - } -} - -impl<C> From<RenderParam<C>> for libmpv2_sys::mpv_render_param { - fn from(val: RenderParam<C>) -> Self { - let type_ = u32::from(&val); - let data = match val { - RenderParam::Invalid => ptr::null_mut(), - RenderParam::ApiType(api_type) => match api_type { - RenderParamApiType::OpenGl => { - libmpv2_sys::MPV_RENDER_API_TYPE_OPENGL.as_ptr() as *mut c_void - } - }, - RenderParam::InitParams(params) => { - Box::into_raw(Box::new(libmpv2_sys::mpv_opengl_init_params::from(params))) - as *mut c_void - } - RenderParam::FBO(fbo) => Box::into_raw(Box::new(fbo)) as *mut c_void, - RenderParam::FlipY(flip) => Box::into_raw(Box::new(flip as c_int)) as *mut c_void, - RenderParam::Depth(depth) => Box::into_raw(Box::new(depth)) as *mut c_void, - RenderParam::ICCProfile(bytes) => { - Box::into_raw(bytes.into_boxed_slice()) as *mut c_void - } - RenderParam::AmbientLight(lux) => Box::into_raw(Box::new(lux)) as *mut c_void, - RenderParam::X11Display(ptr) => ptr as *mut _, - RenderParam::WaylandDisplay(ptr) => ptr as *mut _, - RenderParam::AdvancedControl(adv_ctrl) => { - Box::into_raw(Box::new(adv_ctrl as c_int)) as *mut c_void - } - RenderParam::NextFrameInfo(frame_info) => { - Box::into_raw(Box::new(frame_info)) as *mut c_void - } - RenderParam::BlockForTargetTime(block) => { - Box::into_raw(Box::new(block as c_int)) as *mut c_void - } - RenderParam::SkipRendering(skip_rendering) => { - Box::into_raw(Box::new(skip_rendering as c_int)) as *mut c_void - } - }; - Self { type_, data } - } -} - -unsafe fn free_void_data<T>(ptr: *mut c_void) { - drop(Box::<T>::from_raw(ptr as *mut T)); -} - -unsafe fn free_init_params<C>(ptr: *mut c_void) { - let params = Box::from_raw(ptr as *mut libmpv2_sys::mpv_opengl_init_params); - drop(Box::from_raw( - params.get_proc_address_ctx as *mut OpenGLInitParams<C>, - )); -} - -impl RenderContext { - pub fn new<C>( - mpv: &mut libmpv2_sys::mpv_handle, - params: impl IntoIterator<Item = RenderParam<C>>, - ) -> Result<Self> { - let params: Vec<_> = params.into_iter().collect(); - let mut raw_params: Vec<libmpv2_sys::mpv_render_param> = Vec::new(); - raw_params.reserve(params.len() + 1); - let mut raw_ptrs: HashMap<*const c_void, DeleterFn> = HashMap::new(); - - for p in params { - // The render params are type-erased after they are passed to mpv. This is where we last - // know their real types, so we keep a deleter here. - let deleter: Option<DeleterFn> = match p { - RenderParam::InitParams(_) => Some(free_init_params::<C>), - RenderParam::FBO(_) => Some(free_void_data::<FBO>), - RenderParam::FlipY(_) => Some(free_void_data::<i32>), - RenderParam::Depth(_) => Some(free_void_data::<i32>), - RenderParam::ICCProfile(_) => Some(free_void_data::<Box<[u8]>>), - RenderParam::AmbientLight(_) => Some(free_void_data::<i32>), - RenderParam::NextFrameInfo(_) => Some(free_void_data::<RenderFrameInfo>), - _ => None, - }; - let raw_param: libmpv2_sys::mpv_render_param = p.into(); - if let Some(deleter) = deleter { - raw_ptrs.insert(raw_param.data, deleter); - } - - raw_params.push(raw_param); - } - // the raw array must end with type = 0 - raw_params.push(libmpv2_sys::mpv_render_param { - type_: 0, - data: ptr::null_mut(), - }); - - unsafe { - let raw_array = - Box::into_raw(raw_params.into_boxed_slice()) as *mut libmpv2_sys::mpv_render_param; - let ctx = Box::into_raw(Box::new(std::ptr::null_mut() as _)); - let err = libmpv2_sys::mpv_render_context_create(ctx, &mut *mpv, raw_array); - drop(Box::from_raw(raw_array)); - for (ptr, deleter) in raw_ptrs.iter() { - (deleter)(*ptr as _); - } - - mpv_err( - Self { - ctx: *Box::from_raw(ctx), - update_callback_cleanup: None, - }, - err, - ) - } - } - - pub fn set_parameter<C>(&self, param: RenderParam<C>) -> Result<()> { - unsafe { - mpv_err( - (), - libmpv2_sys::mpv_render_context_set_parameter( - self.ctx, - libmpv2_sys::mpv_render_param::from(param), - ), - ) - } - } - - pub fn get_info<C>(&self, param: RenderParam<C>) -> Result<RenderParam<C>> { - let is_next_frame_info = matches!(param, RenderParam::NextFrameInfo(_)); - let raw_param = libmpv2_sys::mpv_render_param::from(param); - let res = unsafe { libmpv2_sys::mpv_render_context_get_info(self.ctx, raw_param) }; - if res == 0 { - if !is_next_frame_info { - panic!("I don't know how to handle this info type."); - } - let raw_frame_info = raw_param.data as *mut libmpv2_sys::mpv_render_frame_info; - unsafe { - let raw_frame_info = *raw_frame_info; - return Ok(RenderParam::NextFrameInfo(RenderFrameInfo { - flags: raw_frame_info.flags.into(), - target_time: raw_frame_info.target_time, - })); - } - } - Err(Error::Raw(res)) - } - - /// Render video. - /// - /// Typically renders the video to a target surface provided via `fbo` - /// (the details depend on the backend in use). Options like "panscan" are - /// applied to determine which part of the video should be visible and how the - /// video should be scaled. You can change these options at runtime by using the - /// mpv property API. - /// - /// The renderer will reconfigure itself every time the target surface - /// configuration (such as size) is changed. - /// - /// This function implicitly pulls a video frame from the internal queue and - /// renders it. If no new frame is available, the previous frame is redrawn. - /// The update callback set with [set_update_callback](Self::set_update_callback) - /// notifies you when a new frame was added. The details potentially depend on - /// the backends and the provided parameters. - /// - /// Generally, libmpv will invoke your update callback some time before the video - /// frame should be shown, and then lets this function block until the supposed - /// display time. This will limit your rendering to video FPS. You can prevent - /// this by setting the "video-timing-offset" global option to 0. (This applies - /// only to "audio" video sync mode.) - /// - /// # Arguments - /// - /// * `fbo` - A framebuffer object to render to. In OpenGL, 0 is the current backbuffer - /// * `width` - The width of the framebuffer in pixels. This is used for scaling the - /// video properly. - /// * `height` - The height of the framebuffer in pixels. This is used for scaling the - /// video properly. - /// * `flip` - Whether to draw the image upside down. This is needed for OpenGL because - /// it uses a coordinate system with positive Y up, but videos use positive - /// Y down. - pub fn render<GLContext>(&self, fbo: i32, width: i32, height: i32, flip: bool) -> Result<()> { - let mut raw_params: Vec<libmpv2_sys::mpv_render_param> = Vec::with_capacity(3); - let mut raw_ptrs: HashMap<*const c_void, DeleterFn> = HashMap::new(); - - let raw_param: libmpv2_sys::mpv_render_param = - RenderParam::<GLContext>::FBO(FBO { fbo, width, height }).into(); - raw_ptrs.insert(raw_param.data, free_void_data::<FBO>); - raw_params.push(raw_param); - let raw_param: libmpv2_sys::mpv_render_param = RenderParam::<GLContext>::FlipY(flip).into(); - raw_ptrs.insert(raw_param.data, free_void_data::<i32>); - raw_params.push(raw_param); - // the raw array must end with type = 0 - raw_params.push(libmpv2_sys::mpv_render_param { - type_: 0, - data: ptr::null_mut(), - }); - - let raw_array = - Box::into_raw(raw_params.into_boxed_slice()) as *mut libmpv2_sys::mpv_render_param; - - let ret = unsafe { - mpv_err( - (), - libmpv2_sys::mpv_render_context_render(self.ctx, raw_array), - ) - }; - unsafe { - drop(Box::from_raw(raw_array)); - } - - unsafe { - for (ptr, deleter) in raw_ptrs.iter() { - (deleter)(*ptr as _); - } - } - - ret - } - - /// Set the callback that notifies you when a new video frame is available, or if the video display - /// configuration somehow changed and requires a redraw. Similar to [EventContext::set_wakeup_callback](crate::events::EventContext::set_wakeup_callback), you - /// must not call any mpv API from the callback, and all the other listed restrictions apply (such - /// as not exiting the callback by throwing exceptions). - /// - /// This can be called from any thread, except from an update callback. In case of the OpenGL backend, - /// no OpenGL state or API is accessed. - /// - /// Calling this will raise an update callback immediately. - pub fn set_update_callback<F: Fn() + Send + 'static>(&mut self, callback: F) { - if let Some(update_callback_cleanup) = self.update_callback_cleanup.take() { - update_callback_cleanup(); - } - let raw_callback = Box::into_raw(Box::new(callback)); - self.update_callback_cleanup = Some(Box::new(move || unsafe { - drop(Box::from_raw(raw_callback)); - }) as Box<dyn FnOnce()>); - unsafe { - libmpv2_sys::mpv_render_context_set_update_callback( - self.ctx, - Some(ru_wrapper::<F>), - raw_callback as *mut c_void, - ); - } - } -} - -impl Drop for RenderContext { - fn drop(&mut self) { - if let Some(update_callback_cleanup) = self.update_callback_cleanup.take() { - update_callback_cleanup(); - } - unsafe { - libmpv2_sys::mpv_render_context_free(self.ctx); - } - } -} |