2024-04-26 22:11:37 -07:00
|
|
|
use gstreamer::prelude::*;
|
|
|
|
use gstreamer::{Element, ElementFactory, Pipeline};
|
|
|
|
use gstreamer_app::AppSink;
|
|
|
|
use std::str::FromStr;
|
|
|
|
use std::sync::{Arc, Mutex};
|
2024-04-24 21:29:59 -07:00
|
|
|
|
|
|
|
pub struct WebcamPipeline {
|
|
|
|
pub pipeline: Pipeline,
|
|
|
|
|
|
|
|
pub src: Element,
|
|
|
|
pub converter: Element,
|
|
|
|
pub tee: Element,
|
2024-04-26 22:11:37 -07:00
|
|
|
pub queue_app: Element,
|
2024-04-24 21:29:59 -07:00
|
|
|
pub sink_paintable: Element,
|
|
|
|
|
2024-04-26 22:11:37 -07:00
|
|
|
pub queue: Element,
|
2024-04-24 21:29:59 -07:00
|
|
|
pub resize: Element,
|
2024-04-26 22:11:37 -07:00
|
|
|
pub sink_frame: Arc<Mutex<AppSink>>,
|
2024-04-24 21:29:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl WebcamPipeline {
|
|
|
|
pub fn new() -> WebcamPipeline {
|
|
|
|
let pipeline = Pipeline::with_name("webcam_pipeline");
|
|
|
|
|
2024-04-27 19:33:18 -07:00
|
|
|
// All of the following errors are unrecoverable
|
|
|
|
|
2024-04-24 21:29:59 -07:00
|
|
|
let source = ElementFactory::make("mfvideosrc")
|
|
|
|
.build()
|
|
|
|
.expect("Could not build video source for GStreamer");
|
|
|
|
let convert = ElementFactory::make("videoconvert")
|
|
|
|
.build()
|
|
|
|
.expect("Could not build video convert for GStreamer");
|
|
|
|
|
|
|
|
let tee = ElementFactory::make("tee")
|
|
|
|
.build()
|
|
|
|
.expect("Could not create tee element");
|
|
|
|
|
2024-04-26 22:11:37 -07:00
|
|
|
let queue_app = ElementFactory::make("queue")
|
|
|
|
.build()
|
|
|
|
.expect("Could not create the queue buffer");
|
2024-04-24 21:29:59 -07:00
|
|
|
let sink_paintable = ElementFactory::make("gtk4paintablesink")
|
2024-04-26 22:11:37 -07:00
|
|
|
.name("gtk4_output")
|
2024-04-24 21:29:59 -07:00
|
|
|
.build()
|
|
|
|
.expect("Could not build gtk sink for GStreamer");
|
|
|
|
|
2024-04-26 22:11:37 -07:00
|
|
|
let queue = ElementFactory::make("queue")
|
2024-04-24 21:29:59 -07:00
|
|
|
.build()
|
2024-04-26 22:11:37 -07:00
|
|
|
.expect("Could not create the queue buffer");
|
2024-04-24 21:29:59 -07:00
|
|
|
|
2024-04-26 22:11:37 -07:00
|
|
|
let resize = ElementFactory::make("videoscale")
|
2024-04-24 21:29:59 -07:00
|
|
|
.build()
|
2024-04-26 22:11:37 -07:00
|
|
|
.expect("Could not build videoscale for GStreamer");
|
2024-04-24 21:29:59 -07:00
|
|
|
|
2024-05-04 15:09:31 -07:00
|
|
|
let caps_string =
|
|
|
|
String::from("video/x-raw,format=RGB,width=640,height=480,max-buffers=1,drop=true");
|
2024-04-26 22:11:37 -07:00
|
|
|
// let caps_string = String::from("video/x-raw,format=RGB,max-buffers=1,drop=true");
|
2024-05-04 15:09:31 -07:00
|
|
|
let appsrc_caps =
|
|
|
|
gstreamer::Caps::from_str(&caps_string).expect("Couldn't create appsrc caps");
|
2024-04-26 22:11:37 -07:00
|
|
|
|
2024-05-04 15:09:31 -07:00
|
|
|
/*
|
2024-04-26 22:11:37 -07:00
|
|
|
// let sink_frame = ElementFactory::make("appsink")
|
|
|
|
// .name("frame_output")
|
|
|
|
// .property("sync", &false)
|
|
|
|
// .build()
|
|
|
|
// .expect("Could not build appsrc for GStreamer");
|
|
|
|
let video_info = gstreamer_video::VideoInfo::builder(gstreamer_video::VideoFormat::Rgb, 650, 480)
|
|
|
|
.build()
|
|
|
|
.expect("Couldn't build video info!");
|
|
|
|
*/
|
|
|
|
|
|
|
|
let sink_frame = AppSink::builder()
|
|
|
|
.name("frame_output")
|
|
|
|
.sync(false)
|
|
|
|
.max_buffers(1u32)
|
|
|
|
.drop(true)
|
|
|
|
.caps(&appsrc_caps)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
sink_frame.set_property("caps", &appsrc_caps.to_value());
|
2024-04-24 21:29:59 -07:00
|
|
|
|
2024-05-04 15:09:31 -07:00
|
|
|
pipeline
|
|
|
|
.add_many(&[
|
|
|
|
&source,
|
|
|
|
&convert,
|
|
|
|
&tee,
|
|
|
|
&queue_app,
|
|
|
|
&sink_paintable,
|
|
|
|
&resize,
|
|
|
|
&queue,
|
|
|
|
&sink_frame.upcast_ref(),
|
|
|
|
])
|
|
|
|
.expect("Could not link the elements to the pipeline");
|
2024-04-24 21:29:59 -07:00
|
|
|
|
|
|
|
source
|
|
|
|
.link(&convert)
|
|
|
|
.expect("Could not link video source to converter");
|
|
|
|
convert.link(&tee).expect("Could not link converter to tee");
|
|
|
|
|
|
|
|
let tee_src_1 = tee
|
2024-04-26 22:11:37 -07:00
|
|
|
.request_pad_simple("src_%u")
|
2024-04-24 21:29:59 -07:00
|
|
|
.expect("Could not create src pad 1");
|
2024-04-26 22:11:37 -07:00
|
|
|
let sink_paintable_sinkpad = queue_app
|
2024-04-24 21:29:59 -07:00
|
|
|
.static_pad("sink")
|
2024-04-26 22:11:37 -07:00
|
|
|
.expect("Could not get sink pad for paintablesink queue");
|
2024-04-24 21:29:59 -07:00
|
|
|
tee_src_1
|
|
|
|
.link(&sink_paintable_sinkpad)
|
|
|
|
.expect("Could not link tee srcpad 1 to paintablesink pad");
|
2024-04-26 22:11:37 -07:00
|
|
|
queue_app
|
|
|
|
.link(&sink_paintable)
|
|
|
|
.expect("Could not link app queue to paintable sink");
|
2024-04-24 21:29:59 -07:00
|
|
|
|
|
|
|
let tee_src_2 = tee
|
2024-04-26 22:11:37 -07:00
|
|
|
.request_pad_simple("src_%u")
|
2024-04-24 21:29:59 -07:00
|
|
|
.expect("Could not create src pad 2");
|
2024-04-26 22:11:37 -07:00
|
|
|
let sink_frameoutput_sinkpad = queue
|
2024-04-24 21:29:59 -07:00
|
|
|
.static_pad("sink")
|
|
|
|
.expect("Could not get sink pad for frameoutput sink");
|
|
|
|
tee_src_2
|
|
|
|
.link(&sink_frameoutput_sinkpad)
|
|
|
|
.expect("Could not link tee srcpad 2 to frame output sink pad");
|
|
|
|
|
2024-05-04 15:09:31 -07:00
|
|
|
queue.link(&resize).expect("Could not link queue to resize");
|
2024-04-24 21:29:59 -07:00
|
|
|
resize
|
|
|
|
.link(&sink_frame)
|
2024-04-26 22:11:37 -07:00
|
|
|
.expect("Could not bind resize to appsrc");
|
2024-04-24 21:29:59 -07:00
|
|
|
|
|
|
|
WebcamPipeline {
|
|
|
|
pipeline,
|
|
|
|
src: source,
|
|
|
|
converter: convert,
|
|
|
|
tee,
|
2024-04-26 22:11:37 -07:00
|
|
|
queue_app,
|
2024-04-24 21:29:59 -07:00
|
|
|
sink_paintable,
|
|
|
|
resize,
|
2024-04-26 22:11:37 -07:00
|
|
|
queue,
|
|
|
|
sink_frame: Arc::new(Mutex::new(sink_frame)),
|
2024-04-24 21:29:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|