use std::ffi::c_void;
use std::sync::Arc;
use rsmpv::Mpv;
use rsmpv::render::{OpenGlFbo, OwnedRenderContext, SwPixelFormat};
use crate::error::Result;
pub type ProcAddressFn = Box<dyn FnMut(&str) -> *mut c_void + Send + 'static>;
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct GlRenderOptions {
pub block_for_target_time: bool,
pub advanced_control: bool,
}
impl Default for GlRenderOptions {
fn default() -> Self {
Self {
block_for_target_time: true,
advanced_control: false,
}
}
}
impl GlRenderOptions {
#[must_use]
pub fn block_for_target_time(mut self, block: bool) -> Self {
self.block_for_target_time = block;
self
}
#[must_use]
pub fn advanced_control(mut self, advanced: bool) -> Self {
self.advanced_control = advanced;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum RenderKind {
OpenGl,
Software,
}
pub(crate) enum RenderBackend {
Gl(GlRender),
Sw(SwRender),
}
impl RenderBackend {
pub(crate) fn update(&mut self) -> bool {
match self {
RenderBackend::Gl(r) => r.ctx.update(),
RenderBackend::Sw(r) => r.0.update(),
}
}
pub(crate) fn kind(&self) -> RenderKind {
match self {
RenderBackend::Gl(_) => RenderKind::OpenGl,
RenderBackend::Sw(_) => RenderKind::Software,
}
}
}
pub(crate) struct GlRender {
ctx: OwnedRenderContext,
block_for_target_time: bool,
}
impl GlRender {
pub(crate) unsafe fn create(
core: Arc<Mpv>,
get_proc_address: ProcAddressFn,
options: GlRenderOptions,
on_update: impl Fn() + Send + Sync + 'static,
) -> Result<Self> {
let mut ctx = unsafe {
OwnedRenderContext::new_opengl(core, options.advanced_control, get_proc_address)?
};
ctx.set_update_callback(on_update);
Ok(Self {
ctx,
block_for_target_time: options.block_for_target_time,
})
}
pub(crate) fn render(&mut self, fbo: i32, w: i32, h: i32, flip_y: bool) -> Result<()> {
let fbo = OpenGlFbo {
fbo,
width: w,
height: h,
internal_format: 0,
};
self.ctx
.render_opengl(fbo, flip_y, self.block_for_target_time)?;
Ok(())
}
}
pub(crate) struct SwRender(OwnedRenderContext);
impl SwRender {
pub(crate) fn create(
core: Arc<Mpv>,
on_update: impl Fn() + Send + Sync + 'static,
) -> Result<Self> {
let mut ctx = OwnedRenderContext::new_software(core)?;
ctx.set_update_callback(on_update);
Ok(Self(ctx))
}
pub(crate) fn render(&mut self, w: i32, h: i32, buf: &mut Vec<u8>) -> Result<()> {
let (Ok(uw), Ok(uh)) = (usize::try_from(w), usize::try_from(h)) else {
return Ok(());
};
let Some(len) = uw.checked_mul(uh).and_then(|p| p.checked_mul(4)) else {
return Ok(());
};
if len == 0 {
return Ok(());
}
buf.resize(len, 0);
self.0
.render_software(w, h, SwPixelFormat::Rgb0, uw * 4, buf)?;
for px in buf.chunks_exact_mut(4) {
px[3] = 0xFF;
}
Ok(())
}
}