use gst::prelude::*;
use gst::GenericFormattedValue;
use gstreamer as gst;
use gstreamer_app as gst_app;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex, RwLock};
use super::{FrameData, GVideoInner, IcedGStreamerError, Position};
pub type GVideoUrl = GVideoInner<0>;
impl GVideoUrl {
pub fn seek<T>(&self, position: T) -> Result<(), IcedGStreamerError>
where
T: Into<Position>,
{
let pos: Position = position.into();
let position: GenericFormattedValue = pos.into();
self.source.seek_simple(gst::SeekFlags::FLUSH, position)?;
Ok(())
}
pub(crate) fn new_url(url: &url::Url, islive: bool) -> Result<Self, IcedGStreamerError> {
gst::init()?;
let video_sink = gst::Bin::new();
let videoscale = gst::ElementFactory::make("videoscale").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("my_sink")
.caps(&app_sink_caps)
.build();
let state = Arc::new(RwLock::new(
crate::State::new().with_try_get_duration(!islive),
));
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.into();
video_sink.add_many([&videoscale, &videoconvert, &app_sink])?;
gst::Element::link_many([&videoscale, &videoconvert, &app_sink])?;
let staticpad = videoscale.static_pad("sink").unwrap();
let sinkgost = gst::GhostPad::builder_with_target(&staticpad)?.build();
sinkgost.set_active(true)?;
video_sink.add_pad(&sinkgost)?;
let videosource = gst::ElementFactory::make("playbin")
.property("uri", url.as_str())
.property("video-sink", video_sink.to_value())
.build()?;
let source = videosource.downcast::<gst::Bin>().unwrap();
Ok(Self {
bus: source.bus().unwrap(),
source,
state,
upload_frame,
frame,
alive: Arc::new(AtomicBool::new(true)),
id: crate::id::Id::unique(),
})
}
pub fn volume(&self) -> f64 {
let state = self.state.read().unwrap();
state.volume
}
pub fn set_volume(&self, volume: f64) {
self.source.set_property("volume", volume);
}
pub fn duration(&self) -> std::time::Duration {
let state = self.state.read().unwrap();
state.duration
}
pub fn position(&self) -> std::time::Duration {
let state = self.state.read().unwrap();
state.position
}
pub fn duration_seconds(&self) -> f64 {
let state = self.state.read().unwrap();
state.duration.as_secs_f64()
}
pub fn position_seconds(&self) -> f64 {
let state = self.state.read().unwrap();
state.position.as_secs_f64()
}
pub fn duration_nanos(&self) -> f64 {
let state = self.state.read().unwrap();
state.duration.as_secs_f64()
}
pub fn position_nanos(&self) -> u128 {
let state = self.state.read().unwrap();
state.position.as_nanos()
}
}