2024-04-24 21:29:59 -07:00
|
|
|
use gstreamer::prelude::{ElementExt, ElementExtManual, GstBinExtManual, PadExt};
|
|
|
|
use gstreamer::{Element, ElementFactory, Pipeline, State};
|
|
|
|
|
2024-04-24 21:30:32 -07:00
|
|
|
#[derive(Clone)]
|
2024-04-24 21:29:59 -07:00
|
|
|
pub struct WebcamPipeline {
|
|
|
|
pub pipeline: Pipeline,
|
|
|
|
|
|
|
|
pub src: Element,
|
|
|
|
pub converter: Element,
|
|
|
|
pub tee: Element,
|
|
|
|
pub sink_paintable: Element,
|
|
|
|
|
|
|
|
pub resize: Element,
|
|
|
|
pub decode: Element,
|
|
|
|
pub sink_frame: Element,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WebcamPipeline {
|
|
|
|
pub fn new() -> WebcamPipeline {
|
|
|
|
let pipeline = Pipeline::with_name("webcam_pipeline");
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
let sink_paintable = ElementFactory::make("gtk4paintablesink")
|
|
|
|
.build()
|
|
|
|
.expect("Could not build gtk sink for GStreamer");
|
|
|
|
|
|
|
|
let resize = ElementFactory::make("videoscale")
|
|
|
|
.build()
|
|
|
|
.expect("Could not build videoscale for GStreamer");
|
|
|
|
|
|
|
|
todo!("Video XRaw is not actually working yet");
|
|
|
|
let decode = ElementFactory::make("video/x-raw,width=650,height=480")
|
|
|
|
.build()
|
|
|
|
.expect("Could not create decoder");
|
|
|
|
|
|
|
|
let sink_frame = ElementFactory::make("appsink name=frame_sink")
|
|
|
|
.build()
|
|
|
|
.expect("Could not build appsrc for GStreamer");
|
|
|
|
|
|
|
|
pipeline.add_many(&[
|
|
|
|
&source,
|
|
|
|
&convert,
|
|
|
|
&tee,
|
|
|
|
&sink_paintable,
|
|
|
|
&resize,
|
|
|
|
&sink_frame,
|
|
|
|
]).expect("Could not link the elements to the pipeline");
|
|
|
|
|
|
|
|
source
|
|
|
|
.link(&convert)
|
|
|
|
.expect("Could not link video source to converter");
|
|
|
|
convert.link(&tee).expect("Could not link converter to tee");
|
|
|
|
|
|
|
|
let tee_pad_template = tee
|
|
|
|
.pad_template("src_%u")
|
|
|
|
.expect("Could not get pad templates");
|
|
|
|
|
|
|
|
let tee_src_1 = tee
|
|
|
|
.request_pad(&tee_pad_template, Some("paintable_src_pad"), None)
|
|
|
|
.expect("Could not create src pad 1");
|
|
|
|
let sink_paintable_sinkpad = sink_paintable
|
|
|
|
.static_pad("sink")
|
|
|
|
.expect("Could not get sink pad for paintablesink");
|
|
|
|
tee_src_1
|
|
|
|
.link(&sink_paintable_sinkpad)
|
|
|
|
.expect("Could not link tee srcpad 1 to paintablesink pad");
|
|
|
|
|
|
|
|
let tee_src_2 = tee
|
|
|
|
.request_pad(&tee_pad_template, Some("output_src_pad"), None)
|
|
|
|
.expect("Could not create src pad 2");
|
|
|
|
let sink_frameoutput_sinkpad = sink_frame
|
|
|
|
.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");
|
|
|
|
|
|
|
|
resize
|
|
|
|
.link(&decode)
|
|
|
|
.expect("Could not bind resize to decoder");
|
|
|
|
decode
|
|
|
|
.link(&sink_frame)
|
|
|
|
.expect("Could not bind decoder to webcam frame output");
|
|
|
|
|
|
|
|
WebcamPipeline {
|
|
|
|
pipeline,
|
|
|
|
src: source,
|
|
|
|
converter: convert,
|
|
|
|
tee,
|
|
|
|
sink_paintable,
|
|
|
|
resize,
|
|
|
|
decode,
|
|
|
|
sink_frame,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for WebcamPipeline {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.pipeline
|
|
|
|
.set_state(State::Null)
|
|
|
|
.expect("Could not close pipeline during window deconstruction");
|
|
|
|
}
|
|
|
|
}
|