1use crate::{Error, Result, mpv::mpv_err};
2use std::collections::HashMap;
3use std::ffi::{CStr, c_char, c_void};
4use std::os::raw::c_int;
5use std::ptr;
6
7type DeleterFn = unsafe fn(*mut c_void);
8
9pub struct RenderContext {
10 ctx: *mut libmpv2_sys::mpv_render_context,
11 update_callback_cleanup: Option<Box<dyn FnOnce()>>,
12}
13
14pub struct OpenGLInitParams<GLContext> {
16 pub get_proc_address: fn(ctx: &GLContext, name: &str) -> *mut c_void,
26
27 pub ctx: GLContext,
29}
30
31pub struct FBO {
33 pub fbo: i32,
34 pub width: i32,
35 pub height: i32,
36}
37
38#[repr(u32)]
39#[derive(Clone)]
40pub enum RenderFrameInfoFlag {
41 Present = libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_PRESENT,
42 Redraw = libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_REDRAW,
43 Repeat = libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_REPEAT,
44 BlockVSync = libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_BLOCK_VSYNC,
45}
46
47impl From<u64> for RenderFrameInfoFlag {
48 fn from(val: u64) -> Self {
50 let val = val as u32;
51 match val {
52 libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_PRESENT => {
53 RenderFrameInfoFlag::Present
54 }
55 libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_REDRAW => {
56 RenderFrameInfoFlag::Redraw
57 }
58 libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_REPEAT => {
59 RenderFrameInfoFlag::Repeat
60 }
61 libmpv2_sys::mpv_render_frame_info_flag_MPV_RENDER_FRAME_INFO_BLOCK_VSYNC => {
62 RenderFrameInfoFlag::BlockVSync
63 }
64 _ => panic!("Tried converting invalid value to RenderFrameInfoFlag"),
65 }
66 }
67}
68
69#[derive(Clone)]
70pub struct RenderFrameInfo {
71 pub flags: RenderFrameInfoFlag,
72 pub target_time: i64,
73}
74
75pub use libmpv2_sys::mpv_render_update_flag as MpvRenderUpdate;
76pub mod mpv_render_update {
77 pub use libmpv2_sys::mpv_render_update_flag_MPV_RENDER_UPDATE_FRAME as Frame;
78}
79
80pub enum RenderParamApiType {
81 OpenGl,
82}
83
84pub enum RenderParam<GLContext> {
85 Invalid,
86 ApiType(RenderParamApiType),
87 InitParams(OpenGLInitParams<GLContext>),
88 FBO(FBO),
89 FlipY(bool),
90 Depth(i32),
91 ICCProfile(Vec<u8>),
92 AmbientLight(i32),
93 X11Display(*const c_void),
94 WaylandDisplay(*const c_void),
95 AdvancedControl(bool),
96 NextFrameInfo(RenderFrameInfo),
97 BlockForTargetTime(bool),
98 SkipRendering(bool),
99}
100
101impl<C> From<&RenderParam<C>> for u32 {
102 fn from(val: &RenderParam<C>) -> Self {
103 match val {
104 RenderParam::Invalid => 0,
105 RenderParam::ApiType(_) => 1,
106 RenderParam::InitParams(_) => 2,
107 RenderParam::FBO(_) => 3,
108 RenderParam::FlipY(_) => 4,
109 RenderParam::Depth(_) => 5,
110 RenderParam::ICCProfile(_) => 6,
111 RenderParam::AmbientLight(_) => 7,
112 RenderParam::X11Display(_) => 8,
113 RenderParam::WaylandDisplay(_) => 9,
114 RenderParam::AdvancedControl(_) => 10,
115 RenderParam::NextFrameInfo(_) => 11,
116 RenderParam::BlockForTargetTime(_) => 12,
117 RenderParam::SkipRendering(_) => 13,
118 }
119 }
120}
121
122unsafe extern "C" fn gpa_wrapper<GLContext>(ctx: *mut c_void, name: *const c_char) -> *mut c_void {
123 if ctx.is_null() {
124 panic!("ctx for get_proc_address wrapper is NULL");
125 }
126
127 let params: *mut OpenGLInitParams<GLContext> = ctx as _;
128 let params = unsafe { &*params };
129 (params.get_proc_address)(
130 ¶ms.ctx,
131 unsafe { CStr::from_ptr(name) }
132 .to_str()
133 .expect("Could not convert function name to str"),
134 )
135}
136
137unsafe extern "C" fn ru_wrapper<F: Fn() + Send + 'static>(ctx: *mut c_void) {
138 if ctx.is_null() {
139 panic!("ctx for render_update wrapper is NULL");
140 }
141
142 unsafe { (*(ctx as *mut F))() };
143}
144
145impl<C> From<OpenGLInitParams<C>> for libmpv2_sys::mpv_opengl_init_params {
146 fn from(val: OpenGLInitParams<C>) -> Self {
147 Self {
148 get_proc_address: Some(gpa_wrapper::<OpenGLInitParams<C>>),
149 get_proc_address_ctx: Box::into_raw(Box::new(val)) as *mut c_void,
150 }
151 }
152}
153
154impl<C> From<RenderParam<C>> for libmpv2_sys::mpv_render_param {
155 fn from(val: RenderParam<C>) -> Self {
156 let type_ = u32::from(&val);
157 let data = match val {
158 RenderParam::Invalid => ptr::null_mut(),
159 RenderParam::ApiType(api_type) => match api_type {
160 RenderParamApiType::OpenGl => {
161 libmpv2_sys::MPV_RENDER_API_TYPE_OPENGL.as_ptr() as *mut c_void
162 }
163 },
164 RenderParam::InitParams(params) => {
165 Box::into_raw(Box::new(libmpv2_sys::mpv_opengl_init_params::from(params)))
166 as *mut c_void
167 }
168 RenderParam::FBO(fbo) => Box::into_raw(Box::new(fbo)) as *mut c_void,
169 RenderParam::FlipY(flip) => Box::into_raw(Box::new(flip as c_int)) as *mut c_void,
170 RenderParam::Depth(depth) => Box::into_raw(Box::new(depth)) as *mut c_void,
171 RenderParam::ICCProfile(bytes) => {
172 Box::into_raw(bytes.into_boxed_slice()) as *mut c_void
173 }
174 RenderParam::AmbientLight(lux) => Box::into_raw(Box::new(lux)) as *mut c_void,
175 RenderParam::X11Display(ptr) => ptr as *mut _,
176 RenderParam::WaylandDisplay(ptr) => ptr as *mut _,
177 RenderParam::AdvancedControl(adv_ctrl) => {
178 Box::into_raw(Box::new(adv_ctrl as c_int)) as *mut c_void
179 }
180 RenderParam::NextFrameInfo(frame_info) => {
181 Box::into_raw(Box::new(frame_info)) as *mut c_void
182 }
183 RenderParam::BlockForTargetTime(block) => {
184 Box::into_raw(Box::new(block as c_int)) as *mut c_void
185 }
186 RenderParam::SkipRendering(skip_rendering) => {
187 Box::into_raw(Box::new(skip_rendering as c_int)) as *mut c_void
188 }
189 };
190 Self { type_, data }
191 }
192}
193
194unsafe fn free_void_data<T>(ptr: *mut c_void) {
195 drop(unsafe { Box::<T>::from_raw(ptr as *mut T) });
196}
197
198unsafe fn free_init_params<C>(ptr: *mut c_void) {
199 let params = unsafe { Box::from_raw(ptr as *mut libmpv2_sys::mpv_opengl_init_params) };
200 drop(unsafe { Box::from_raw(params.get_proc_address_ctx as *mut OpenGLInitParams<C>) });
201}
202
203impl RenderContext {
204 pub fn new<C>(
205 mpv: &mut libmpv2_sys::mpv_handle,
206 params: impl IntoIterator<Item = RenderParam<C>>,
207 ) -> Result<Self> {
208 let params: Vec<_> = params.into_iter().collect();
209 let mut raw_params: Vec<libmpv2_sys::mpv_render_param> = Vec::new();
210 raw_params.reserve(params.len() + 1);
211 let mut raw_ptrs: HashMap<*const c_void, DeleterFn> = HashMap::new();
212
213 for p in params {
214 let deleter: Option<DeleterFn> = match p {
217 RenderParam::InitParams(_) => Some(free_init_params::<C>),
218 RenderParam::FBO(_) => Some(free_void_data::<FBO>),
219 RenderParam::FlipY(_) => Some(free_void_data::<i32>),
220 RenderParam::Depth(_) => Some(free_void_data::<i32>),
221 RenderParam::ICCProfile(_) => Some(free_void_data::<Box<[u8]>>),
222 RenderParam::AmbientLight(_) => Some(free_void_data::<i32>),
223 RenderParam::NextFrameInfo(_) => Some(free_void_data::<RenderFrameInfo>),
224 _ => None,
225 };
226 let raw_param: libmpv2_sys::mpv_render_param = p.into();
227 if let Some(deleter) = deleter {
228 raw_ptrs.insert(raw_param.data, deleter);
229 }
230
231 raw_params.push(raw_param);
232 }
233 raw_params.push(libmpv2_sys::mpv_render_param {
235 type_: 0,
236 data: ptr::null_mut(),
237 });
238
239 unsafe {
240 let raw_array =
241 Box::into_raw(raw_params.into_boxed_slice()) as *mut libmpv2_sys::mpv_render_param;
242 let ctx = Box::into_raw(Box::new(std::ptr::null_mut() as _));
243 let err = libmpv2_sys::mpv_render_context_create(ctx, &mut *mpv, raw_array);
244 drop(Box::from_raw(raw_array));
245 for (ptr, deleter) in raw_ptrs.iter() {
246 (deleter)(*ptr as _);
247 }
248
249 mpv_err(
250 Self {
251 ctx: *Box::from_raw(ctx),
252 update_callback_cleanup: None,
253 },
254 err,
255 )
256 }
257 }
258
259 pub fn set_parameter<C>(&self, param: RenderParam<C>) -> Result<()> {
260 unsafe {
261 mpv_err(
262 (),
263 libmpv2_sys::mpv_render_context_set_parameter(
264 self.ctx,
265 libmpv2_sys::mpv_render_param::from(param),
266 ),
267 )
268 }
269 }
270
271 pub fn get_info<C>(&self, param: RenderParam<C>) -> Result<RenderParam<C>> {
272 let is_next_frame_info = matches!(param, RenderParam::NextFrameInfo(_));
273 let raw_param = libmpv2_sys::mpv_render_param::from(param);
274 let res = unsafe { libmpv2_sys::mpv_render_context_get_info(self.ctx, raw_param) };
275 if res == 0 {
276 if !is_next_frame_info {
277 panic!("I don't know how to handle this info type.");
278 }
279 let raw_frame_info = raw_param.data as *mut libmpv2_sys::mpv_render_frame_info;
280 unsafe {
281 let raw_frame_info = *raw_frame_info;
282 return Ok(RenderParam::NextFrameInfo(RenderFrameInfo {
283 flags: raw_frame_info.flags.into(),
284 target_time: raw_frame_info.target_time,
285 }));
286 }
287 }
288 Err(Error::Raw(res))
289 }
290
291 pub fn render<GLContext>(&self, fbo: i32, width: i32, height: i32, flip: bool) -> Result<()> {
325 let mut raw_params: Vec<libmpv2_sys::mpv_render_param> = Vec::with_capacity(3);
326 let mut raw_ptrs: HashMap<*const c_void, DeleterFn> = HashMap::new();
327
328 let raw_param: libmpv2_sys::mpv_render_param =
329 RenderParam::<GLContext>::FBO(FBO { fbo, width, height }).into();
330 raw_ptrs.insert(raw_param.data, free_void_data::<FBO>);
331 raw_params.push(raw_param);
332 let raw_param: libmpv2_sys::mpv_render_param = RenderParam::<GLContext>::FlipY(flip).into();
333 raw_ptrs.insert(raw_param.data, free_void_data::<i32>);
334 raw_params.push(raw_param);
335 raw_params.push(libmpv2_sys::mpv_render_param {
337 type_: 0,
338 data: ptr::null_mut(),
339 });
340
341 let raw_array =
342 Box::into_raw(raw_params.into_boxed_slice()) as *mut libmpv2_sys::mpv_render_param;
343
344 let ret = unsafe {
345 mpv_err(
346 (),
347 libmpv2_sys::mpv_render_context_render(self.ctx, raw_array),
348 )
349 };
350 unsafe {
351 drop(Box::from_raw(raw_array));
352 }
353
354 unsafe {
355 for (ptr, deleter) in raw_ptrs.iter() {
356 (deleter)(*ptr as _);
357 }
358 }
359
360 ret
361 }
362
363 pub fn report_swap(&self) {
371 unsafe { libmpv2_sys::mpv_render_context_report_swap(self.ctx) }
372 }
373
374 pub fn set_update_callback<F: Fn() + Send + 'static>(&mut self, callback: F) {
384 if let Some(update_callback_cleanup) = self.update_callback_cleanup.take() {
385 update_callback_cleanup();
386 }
387 let raw_callback = Box::into_raw(Box::new(callback));
388 self.update_callback_cleanup = Some(Box::new(move || unsafe {
389 drop(Box::from_raw(raw_callback));
390 }) as Box<dyn FnOnce()>);
391 unsafe {
392 libmpv2_sys::mpv_render_context_set_update_callback(
393 self.ctx,
394 Some(ru_wrapper::<F>),
395 raw_callback as *mut c_void,
396 );
397 }
398 }
399
400 pub fn update(&self) -> Result<MpvRenderUpdate> {
424 let res = unsafe { libmpv2_sys::mpv_render_context_update(self.ctx) };
425 match res.try_into() {
426 Ok(res) => Ok(res),
427 Err(_) => Err(Error::Raw(libmpv2_sys::mpv_error_MPV_ERROR_GENERIC)),
428 }
429 }
430}
431
432impl Drop for RenderContext {
433 fn drop(&mut self) {
434 if let Some(update_callback_cleanup) = self.update_callback_cleanup.take() {
435 update_callback_cleanup();
436 }
437 unsafe {
438 libmpv2_sys::mpv_render_context_free(self.ctx);
439 }
440 }
441}