use gstreamer::{BufferCopyFlags, BufferFlags, Caps, ClockTime};
use gstreamer_app::{AppSink, AppSrc};
use wayclip_core::models::error::WayclipError;
use crate::common::video::ring::data::EncodedFrame;
const DEFAULT_APPSRC_FORMAT: gstreamer::Format = gstreamer::Format::Time;
const DEFAULT_APPSRC_IS_LIVE: bool = false;
pub const DEFAULT_APPSRC_DO_TIMESTAMP: bool = false;
const DEFAULT_APPSINK_MAX_BUFFERS: u32 = 100;
const DEFAULT_APPSINK_DROP: bool = false;
const DEFAULT_APPSINK_SYNC: bool = false;
pub struct GStreamerApp;
impl GStreamerApp {
pub fn build_app_sink() -> AppSink {
gstreamer_app::AppSink::builder()
.property("drop", DEFAULT_APPSINK_DROP)
.property("max-buffers", DEFAULT_APPSINK_MAX_BUFFERS)
.property("sync", DEFAULT_APPSINK_SYNC)
.build()
}
pub fn build_app_src(caps: &Caps) -> AppSrc {
gstreamer_app::AppSrc::builder()
.caps(caps)
.format(DEFAULT_APPSRC_FORMAT)
.is_live(DEFAULT_APPSRC_IS_LIVE)
.do_timestamp(DEFAULT_APPSRC_DO_TIMESTAMP)
.build()
}
fn sync_timestamp(ts: ClockTime, base: ClockTime, offset_ms: i64) -> ClockTime {
let normalised = ts.saturating_sub(base);
let offset_dur = ClockTime::from_mseconds(offset_ms.unsigned_abs());
if offset_ms >= 0 {
normalised.saturating_add(offset_dur)
} else {
normalised.saturating_sub(offset_dur)
}
}
pub fn push_frames(
appsrc: AppSrc,
frame_info: EncodedFrameInfo,
) -> std::thread::JoinHandle<Result<(), WayclipError>> {
std::thread::spawn(move || {
for frame in frame_info.frames {
let mut payload = frame
.payload
.copy_region(BufferCopyFlags::MEMORY | BufferCopyFlags::FLAGS, ..)
.map_err(|e| {
WayclipError::Remux(format!("Failed to copy buffer: {e:?}").into())
})?;
let pts = GStreamerApp::sync_timestamp(
frame.pts,
frame_info.base_pts,
frame_info.offset_ms,
);
let dts = frame.dts.map(|dts| {
GStreamerApp::sync_timestamp(dts, frame_info.base_pts, frame_info.offset_ms)
});
if let Some(buffer_ref) = payload.get_mut() {
buffer_ref.set_pts(pts);
if let Some(dts) = dts {
buffer_ref.set_dts(dts);
}
if let Some(duration) = frame.duration {
buffer_ref.set_duration(duration);
}
if !frame.is_keyframe {
buffer_ref.set_flags(BufferFlags::DELTA_UNIT);
}
}
appsrc.push_buffer(payload).map_err(|e| {
WayclipError::Remux(format!("Failed to push buffer: {:?}", e).into())
})?;
}
appsrc
.end_of_stream()
.map_err(|e| WayclipError::Remux(format!("Failed to send EOS: {:?}", e).into()))?;
Ok(())
})
}
}
pub struct EncodedFrameInfo {
pub frames: Vec<EncodedFrame>,
pub base_pts: ClockTime,
pub offset_ms: i64,
}