use super::{FrameData, GVideoInner, IcedGStreamerError};
use gst::prelude::*;
use gstreamer as gst;
use gstreamer_app as gst_app;
use std::{
os::fd::RawFd,
sync::{atomic::AtomicBool, Arc, Mutex, RwLock},
};
pub type GVideoPipewire = GVideoInner<1>;
impl GVideoPipewire {
pub(crate) fn new_pipewire(path: u32, fd: RawFd) -> Result<Self, IcedGStreamerError> {
gst::init()?;
let source = gst::Pipeline::new();
let pipewiresrc = gst::ElementFactory::make("pipewiresrc")
.property("fd", fd)
.property("path", path.to_string())
.build()?;
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
let app_sink_caps = gst::Caps::builder("video/x-raw")
.field("format", "NV12")
.field("pixel-aspect-ratio", gst::Fraction::new(1, 1))
.build();
let app_sink: gst_app::AppSink = gst_app::AppSink::builder()
.name("app_sink")
.caps(&app_sink_caps)
.build();
let state = Arc::new(RwLock::new(crate::State::new()));
let upload_frame = Arc::new(AtomicBool::new(false));
let upload_frame_i = upload_frame.clone();
let frame = Arc::new(Mutex::new(None));
let frame_i = frame.clone();
app_sink.set_callbacks(
gst_app::AppSinkCallbacks::builder()
.new_sample(move |sink| {
let sample = sink.pull_sample().map_err(|_| gst::FlowError::Eos)?;
let buffer = sample.buffer().ok_or(gst::FlowError::Error)?;
let map = buffer.map_readable().map_err(|_| gst::FlowError::Error)?;
let caps = sample.caps().ok_or(gst::FlowError::Error)?;
let s = caps.structure(0).ok_or(gst::FlowError::Error)?;
let width = s.get::<i32>("width").map_err(|_| gst::FlowError::Error)?;
let height = s.get::<i32>("height").map_err(|_| gst::FlowError::Error)?;
upload_frame_i.store(true, std::sync::atomic::Ordering::SeqCst);
let data = FrameData {
width: width as _,
height: height as _,
pixels: map.as_slice().to_owned(),
};
*frame_i.lock().map_err(|_| gst::FlowError::Eos)? = Some(data);
Ok(gst::FlowSuccess::Ok)
})
.build(),
);
let app_sink: gst::Element = app_sink.clone().into();
source.add_many([&pipewiresrc, &videoconvert, &app_sink])?;
gst::Element::link_many([&pipewiresrc, &videoconvert, &app_sink])?;
source.set_state(gst::State::Playing)?;
Ok(Self {
bus: source.bus().unwrap(),
source: source.into(),
state,
upload_frame,
frame,
alive: Arc::new(AtomicBool::new(true)),
id: crate::id::Id::unique(),
})
}
}