use gstreamer::{ self as gst, prelude::{ Cast, ElementExt, ElementExtManual, GObjectExtManualGst, GstBinExt, GstBinExtManual, PadExt, }, ElementFactory, }; use gstreamer_app as gst_app; use crate::config::AppConfig; pub struct Pipeline { pub pipeline: gst::Pipeline, pub sink: gst_app::AppSink, } const HEIGHT: usize = 480; const SOCKET_PATH: &str = "/tmp/VCS-streamer-shm"; pub fn new_pipeline(config: &AppConfig) -> Pipeline { let pipeline = gst::Pipeline::builder() .name("camera_to_rtp_pipeine") .build(); let source = { #[cfg(target_os = "windows")] { ElementFactory::make("mfvideosrc") .build() .expect("Could not make mfvideosrc element!") } #[cfg(not(target_os = "windows"))] { ElementFactory::make("v4l2src") .build() .expect("Could not make mfvideosrc element!") } }; let video_convert = ElementFactory::make("videoconvert") .build() .expect("Could not make videoconvert gst element!"); let video_rate = ElementFactory::make("videorate") .build() .expect("Could not make videoscale gst element!"); let tee = ElementFactory::make("tee") .build() .expect("Could not make Tee gst element!"); let video_scale = ElementFactory::make("videoscale") .property("add-borders", true) .build() .unwrap(); let video_scale_caps = gstreamer::Caps::builder("video/x-raw") .field("height", HEIGHT as i32) .field("width", (HEIGHT as f64 * config.aspect_ratio) as i32) // .field("framerate", gstreamer::Fraction::new(30, 1)) .build(); // We are using VP8 because VP9 resulted in much worse video quality // when testing -NY 8/25/2024 let vp8enc = ElementFactory::make("vp8enc") .build() .expect("Could not make vp8enc gst element!"); let rtp = ElementFactory::make("rtpvp8pay") .build() .expect("Could not make rtpvp8pay gst element!"); let app_sink = gst_app::AppSink::builder().build(); pipeline .add_many([ &source, &video_convert, &video_rate, &tee, &video_scale, &vp8enc, &rtp, app_sink.upcast_ref(), ]) .expect("Could not add all the stuff to the pipeline"); source .link(&video_convert) .expect("Could not link source to video convert"); let tee_caps = gstreamer::Caps::builder("video/x-raw").build(); video_convert .link_filtered(&tee, &tee_caps) .expect("Could not link video convert and tee!"); let tee_src_1 = tee .request_pad_simple("src_%u") .expect("Could not get src pad 1 from tee"); tee_src_1 .link( &video_rate .static_pad("sink") .expect("Could not get static sink pad from video_rate"), ) .expect("Could not link tee pad and video rate"); video_rate .link(&video_scale) .expect("Could not link video rate to video scale"); video_scale .link_filtered(&vp8enc, &video_scale_caps) .expect("Could not link videoscale to vp8enc with caps!"); gst::Element::link_many(&[&vp8enc, &rtp, app_sink.upcast_ref()]) .expect("Could not gst link vp8enc through appsink!"); #[cfg(not(target_os = "windows"))] { let hailo_sink = ElementFactory::make("shmsink") .build() .expect("Could not create the shared memory sink!"); hailo_sink.set_property_from_str("socket-path", SOCKET_PATH); hailo_sink.set_property_from_str("sync", "true"); hailo_sink.set_property_from_str("wait-for-connection", "false"); pipeline .add(&hailo_sink) .expect("Could not add hailo shm to pipeline bin!"); let tee_src_2 = tee .request_pad_simple("src_%u") .expect("Could not create hailo tee src pad"); tee_src_2 .link( &hailo_sink .static_pad("sink") .expect("Could not get hailo static sink pad"), ) .expect("Could not link tee src pad to hailo sink pad"); } Pipeline { pipeline, sink: app_sink, } }