use azul_core::animation::UpdateImageType;
use azul_core::callbacks::Update;
use azul_core::gl::gl::{RGBA, TEXTURE_2D, UNSIGNED_BYTE};
use azul_core::gl::{GlContextPtr, OptionU8VecRef, Texture, U8VecRef};
use azul_core::geom::PhysicalSizeU32;
use azul_core::refany::RefAny;
use azul_core::resources::ImageRef;
use azul_core::video::VideoFrame;
use azul_css::impl_option_inner; use azul_css::props::basic::ColorU;
use crate::callbacks::CallbackInfo;
pub type OnVideoFrameCallbackType = extern "C" fn(RefAny, CallbackInfo, VideoFrame) -> Update;
impl_widget_callback!(
OnVideoFrame,
OptionOnVideoFrame,
OnVideoFrameCallback,
OnVideoFrameCallbackType
);
azul_core::impl_managed_callback! {
wrapper: OnVideoFrameCallback,
info_ty: CallbackInfo,
return_ty: Update,
default_ret: Update::DoNothing,
invoker_static: ON_VIDEO_FRAME_INVOKER,
invoker_ty: AzOnVideoFrameCallbackInvoker,
thunk_fn: az_on_video_frame_callback_thunk,
setter_fn: AzApp_setOnVideoFrameCallbackInvoker,
from_handle_fn: AzOnVideoFrameCallback_createFromHostHandle,
extra_args: [ frame: VideoFrame ],
}
pub fn invoke_on_frame(
hook: &OptionOnVideoFrame,
info: &mut CallbackInfo,
frame: &VideoFrame,
) -> Update {
match hook {
OptionOnVideoFrame::Some(h) => {
(h.callback.cb)(h.refany.clone(), info.clone(), frame.clone())
}
OptionOnVideoFrame::None => Update::DoNothing,
}
}
pub fn present_frame(
info: &mut CallbackInfo,
dataset: RefAny,
current_id: Option<u32>,
frame: &VideoFrame,
) -> Option<u32> {
let gl = match info.get_gl_context().into_option() {
Some(g) => g,
None => return current_id,
};
match current_id {
Some(id) => {
upload_rgba(&gl, id, frame);
info.update_all_image_callbacks();
Some(id)
}
None => {
let tex = Texture::allocate_rgba8(
gl.clone(),
PhysicalSizeU32 {
width: frame.width,
height: frame.height,
},
ColorU {
r: 0,
g: 0,
b: 0,
a: 0,
},
);
let id = tex.texture_id;
upload_rgba(&gl, id, frame);
let image = ImageRef::new_gltexture(tex);
if let Some(node) = info.get_node_id_of_root_dataset(dataset) {
if let Some(nid) = node.node.into_crate_internal() {
info.change_node_image(node.dom, nid, image, UpdateImageType::Content);
}
}
Some(id)
}
}
}
pub fn upload_rgba(gl: &GlContextPtr, texture_id: u32, frame: &VideoFrame) {
gl.bind_texture(TEXTURE_2D, texture_id);
gl.tex_image_2d(
TEXTURE_2D,
0,
RGBA as i32,
frame.width as i32,
frame.height as i32,
0,
RGBA,
UNSIGNED_BYTE,
OptionU8VecRef::Some(U8VecRef::from(frame.bytes.as_ref())),
);
}
#[derive(Clone, Copy)]
pub struct CaptureVTable {
pub open: fn(index: u32, width: u32, height: u32) -> u64,
pub read: fn(handle: u64, out: &mut alloc::vec::Vec<u8>) -> (u32, u32),
pub close: fn(handle: u64),
}
static CAMERA_BACKEND: std::sync::OnceLock<CaptureVTable> = std::sync::OnceLock::new();
static SCREEN_BACKEND: std::sync::OnceLock<CaptureVTable> = std::sync::OnceLock::new();
pub fn register_camera_backend(vtable: CaptureVTable) {
let _ = CAMERA_BACKEND.set(vtable);
}
pub fn register_screen_backend(vtable: CaptureVTable) {
let _ = SCREEN_BACKEND.set(vtable);
}
pub fn camera_backend() -> Option<CaptureVTable> {
CAMERA_BACKEND.get().copied()
}
pub fn screen_backend() -> Option<CaptureVTable> {
SCREEN_BACKEND.get().copied()
}
#[derive(Clone, Copy)]
pub struct AudioCaptureVTable {
pub open: fn(sample_rate: u32, channels: u16) -> u64,
pub read: fn(handle: u64, out: &mut alloc::vec::Vec<f32>) -> u32,
pub close: fn(handle: u64),
}
static MIC_BACKEND: std::sync::OnceLock<AudioCaptureVTable> = std::sync::OnceLock::new();
pub fn register_mic_backend(vtable: AudioCaptureVTable) {
let _ = MIC_BACKEND.set(vtable);
}
pub fn mic_backend() -> Option<AudioCaptureVTable> {
MIC_BACKEND.get().copied()
}