diff --git a/examples/gstreamer-player/Cargo.toml b/examples/gstreamer-player/Cargo.toml index e6ee440c27f..935df0439b2 100644 --- a/examples/gstreamer-player/Cargo.toml +++ b/examples/gstreamer-player/Cargo.toml @@ -16,13 +16,13 @@ name = "gstreamer-player" [dependencies] slint = { path = "../../api/rs/slint" } -anyhow = { version = "1.0" } futures = { version = "0.3.28" } gst = {package = "gstreamer", version = "0.21.3"} gst-audio = {package = "gstreamer-audio", version = "0.21.3" } gst-video = {package = "gstreamer-video", version = "0.21.2" } gst-app = {package = "gstreamer-app", version = "0.21.2" } +thiserror = "1.0.57" [build-dependencies] diff --git a/examples/gstreamer-player/main.rs b/examples/gstreamer-player/main.rs index a0eccaec93b..498925996e2 100644 --- a/examples/gstreamer-player/main.rs +++ b/examples/gstreamer-player/main.rs @@ -3,33 +3,59 @@ slint::include_modules!(); -use anyhow::{bail, Result}; - use gst::prelude::*; +use gst_app::AppSink; -fn try_gstreamer_video_frame_to_pixel_buffer( - frame: &gst_video::VideoFrame, -) -> Result> { - match frame.format() { - gst_video::VideoFormat::Rgb => { - let mut slint_pixel_buffer = - slint::SharedPixelBuffer::::new(frame.width(), frame.height()); - frame - .buffer() - .copy_to_slice(0, slint_pixel_buffer.make_mut_bytes()) - .expect("Unable to copy to slice!"); // Copies! - Ok(slint_pixel_buffer) - } - _ => { - bail!( - "Cannot convert frame to a slint RGB frame because it is format {}", - frame.format().to_str() - ) - } - } +#[derive(thiserror::Error, Debug)] +enum GStreamerSlintError { + #[error("wrong video type")] + WrongVideoFormatError(String) } -fn main() { +// This code would be moved to a rust crate that +// TODO make width and height work dynamically without passing them +fn set_callback_that_updates_a_slint_image(app_sink: &mut AppSink, callback: F, width:u32, height:u32) -> Result<(), GStreamerSlintError> +where F: FnOnce(&slint::Image) + Send +{ + app_sink.set_callbacks( + gst_app::AppSinkCallbacks::builder() + .new_sample(move |appsink| { + let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?; + let buffer = sample.buffer_owned().unwrap(); // Probably copies! + let video_info = + gst_video::VideoInfo::builder(gst_video::VideoFormat::Rgb, width, height) + .build() + .expect("couldn't build video info!"); + let video_frame = + gst_video::VideoFrame::from_buffer_readable(buffer, &video_info).unwrap(); + + let slint_frame = match video_frame.format() { + gst_video::VideoFormat::Rgb => { + let mut slint_pixel_buffer = + slint::SharedPixelBuffer::::new(video_frame.width(), video_frame.height()); + video_frame + .buffer() + .copy_to_slice(0, slint_pixel_buffer.make_mut_bytes()) + .expect("Unable to copy to slice!"); // Copies! + Ok(slint_pixel_buffer) + } + _ => { + Err(format!("Cannot convert frame to a slint RGB8 images because it is format {}", video_frame.format().to_str())) + } + }.expect("Unable to convert the video frame to a slint video frame!"); + let slint_image = slint::Image::from_rgb8(slint_frame); + callback(&slint_image); + + Ok(gst::FlowSuccess::Ok) + }) + .build() + ); + Ok(()) +} + + + +fn main() -> Result<(), GStreamerSlintError> { let app = App::new().unwrap(); let app_weak = app.as_weak(); @@ -43,7 +69,7 @@ fn main() { let width: u32 = 1024; let height: u32 = 1024; - let appsink = gst_app::AppSink::builder() + let mut app_sink = AppSink::builder() .caps( &gst_video::VideoCapsBuilder::new() .format(gst_video::VideoFormat::Rgb) @@ -55,37 +81,26 @@ fn main() { let pipeline = gst::Pipeline::with_name("test-pipeline"); - pipeline.add_many([&source, &appsink.upcast_ref()]).unwrap(); - source.link(&appsink).expect("Elements could not be linked."); - - appsink.set_callbacks( - gst_app::AppSinkCallbacks::builder() - .new_sample(move |appsink| { - let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?; - let buffer = sample.buffer_owned().unwrap(); // Probably copies! - let video_info = - gst_video::VideoInfo::builder(gst_video::VideoFormat::Rgb, width, height) - .build() - .expect("couldn't build video info!"); - let video_frame = - gst_video::VideoFrame::from_buffer_readable(buffer, &video_info).unwrap(); - let slint_frame = try_gstreamer_video_frame_to_pixel_buffer(&video_frame) - .expect("Unable to convert the video frame to a slint video frame!"); + pipeline.add_many([&source, &app_sink.upcast_ref()]).unwrap(); + source.link(&app_sink).expect("Elements could not be linked."); - app_weak - .upgrade_in_event_loop(|app| { - app.set_video_frame(slint::Image::from_rgb8(slint_frame)) - }) - .unwrap(); - Ok(gst::FlowSuccess::Ok) + // Library user has to write this closure because app and set_video_frame are both codegenned. + // Captures app_weak + let set_video_frame = |image: & slint::Image| { + // app.set_video_frame(image); + app_weak + .upgrade_in_event_loop(|app| { + app.set_video_frame(*image) }) - .build(), - ); + .unwrap(); + }; + set_callback_that_updates_a_slint_image(&mut app_sink, set_video_frame, 256, 256)?; pipeline .set_state(gst::State::Playing) .expect("Unable to set the pipeline to the `Playing` state"); app.run().unwrap(); + Ok(()) }