use crate::utils::common::{gst_from_otc_format, MissingElement};
use crate::audio_device::AudioSample;
use crate::video_frame::FrameFormat;
use anyhow::Error;
use byte_slice_cast::*;
use gst::prelude::*;
pub struct Renderer {
pipeline: gst::Pipeline,
video_src: gst::Element,
audio_src: gst::Element,
}
impl Renderer {
pub fn new() -> Result<Self, Error> {
gst::init()?;
let pipeline = gst::Pipeline::new(None);
let video_src =
gst::ElementFactory::make("appsrc", None).map_err(|_| MissingElement("appsrc"))?;
let videoconvert = gst::ElementFactory::make("videoconvert", None)
.map_err(|_| MissingElement("videoconvert"))?;
let sink = gst::ElementFactory::make("autovideosink", None)
.map_err(|_| MissingElement("autovideosink"))?;
pipeline.add_many(&[&video_src, &videoconvert, &sink])?;
gst::Element::link_many(&[&video_src, &videoconvert, &sink])?;
let audio_src =
gst::ElementFactory::make("appsrc", None).map_err(|_| MissingElement("appsrc"))?;
let audioresample = gst::ElementFactory::make("audioresample", None)
.map_err(|_| MissingElement("audioresample"))?;
let audioconvert = gst::ElementFactory::make("audioconvert", None)
.map_err(|_| MissingElement("audioconvert"))?;
let sink = gst::ElementFactory::make("autoaudiosink", None)
.map_err(|_| MissingElement("autoaudiosink"))?;
pipeline.add_many(&[&audio_src, &audioresample, &audioconvert, &sink])?;
gst::Element::link_many(&[&audio_src, &audioresample, &audioconvert, &sink])?;
pipeline.set_state(gst::State::Playing)?;
if cfg!(debug_assertions) {
let bin = pipeline.clone().upcast::<gst::Bin>();
gst::debug_bin_to_dot_file_with_ts(
&bin,
gst::DebugGraphDetails::all(),
"RendererPipeline",
);
}
Ok(Self {
pipeline,
video_src,
audio_src,
})
}
pub fn push_video_buffer(
&self,
data: &[u8],
format: FrameFormat,
width: u32,
height: u32,
offset: &[usize],
stride: &[i32],
) {
let mut buffer = gst::Buffer::with_size(data.len()).unwrap();
let gst_format = gst_from_otc_format(format);
{
let buffer = buffer.get_mut().unwrap();
buffer.copy_from_slice(0, data).expect("copying failed");
let flags = gst_video::VideoFrameFlags::empty();
gst_video::VideoMeta::add_full(
buffer, flags, gst_format, width, height, offset, stride,
)
.unwrap();
}
let appsrc = self
.video_src
.clone()
.dynamic_cast::<gst_app::AppSrc>()
.expect("Source element is expected to be an appsrc!");
let caps = gst::Caps::builder("video/x-raw")
.field("width", width as i32)
.field("height", height as i32)
.field("framerate", gst::Fraction::new(1, 1))
.field("format", format!("{}", gst_format))
.build();
let sample = gst::Sample::builder().caps(&caps).buffer(&buffer).build();
let _ = appsrc.push_sample(&sample);
}
pub fn render_audio_sample(&self, sample: AudioSample) {
struct CastVec(Vec<i16>);
impl AsRef<[u8]> for CastVec {
fn as_ref(&self) -> &[u8] {
self.0.as_byte_slice()
}
}
let buffer = gst::Buffer::from_slice(CastVec(sample.data.0));
let gst_format = "S16LE";
let appsrc = self
.audio_src
.clone()
.dynamic_cast::<gst_app::AppSrc>()
.expect("Source element is expected to be an appsrc!");
let caps = gst::Caps::builder("audio/x-raw")
.field("format", gst_format.to_string())
.field("layout", "interleaved")
.field("rate", sample.sampling_rate)
.field("channels", sample.number_of_channels)
.build();
let sample = gst::Sample::builder().caps(&caps).buffer(&buffer).build();
let _ = appsrc.push_sample(&sample);
}
}
impl Drop for Renderer {
fn drop(&mut self) {
let _ = self.pipeline.set_state(gst::State::Null);
}
}