feat: Modify gst crate to add lot of more granularity

This commit is contained in:
uttarayan21
2025-12-22 13:27:30 +05:30
parent d42ef3b550
commit 043d1e99f0
23 changed files with 947 additions and 392 deletions

View File

@@ -1,19 +1,40 @@
pub mod bin;
pub mod bus;
pub mod caps;
pub mod element;
pub mod errors;
pub mod isa;
pub mod pad;
pub mod pipeline;
pub mod plugins;
// pub mod playbin3;
// pub mod videoconvert;
pub use bin::*;
pub use bus::*;
pub use caps::*;
pub use element::*;
pub use pad::*;
pub use pipeline::*;
pub use plugins::*;
// pub use playbin3::*;
// pub use videoconvert::*;
pub(crate) mod priv_prelude {
pub use crate::errors::*;
pub use crate::*;
pub use gstreamer::prelude::*;
#[track_caller]
pub fn duration_to_clocktime(
timeout: impl Into<Option<core::time::Duration>>,
) -> Result<Option<gstreamer::ClockTime>> {
match timeout.into() {
Some(dur) => {
let clocktime = gstreamer::ClockTime::try_from(dur)
.change_context(Error)
.attach("Failed to convert duration to ClockTime")?;
Ok(Some(clocktime))
}
None => Ok(None),
}
}
}
use errors::*;
use gstreamer::prelude::*;
@@ -36,282 +57,13 @@ impl Gst {
Arc::clone(&GST)
}
pub fn pipeline_from_str(&self, s: &str) -> Result<Pipeline> {
let pipeline = gstreamer::parse::launch(s).change_context(Error)?;
let pipeline = pipeline.downcast::<gstreamer::Pipeline>();
let pipeline = match pipeline {
Err(_e) => return Err(Error).attach("Failed to downcast to Pipeline"),
Ok(p) => p,
};
Ok(Pipeline { inner: pipeline })
}
}
pub struct Pipeline {
inner: gstreamer::Pipeline,
}
impl core::fmt::Debug for Pipeline {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Pipeline")
.field("pipeline", &self.inner)
// .field("state", &self.pipeline.state(gstreamer::ClockTime::NONE))
.finish()
}
}
impl Drop for Pipeline {
fn drop(&mut self) {
let _ = self.inner.set_state(gstreamer::State::Null);
}
}
impl Pipeline {
pub fn bus(&self) -> Result<Bus> {
let bus = self
.inner
.bus()
.ok_or(Error)
.attach("Failed to get bus from pipeline")?;
Ok(Bus { bus })
}
pub fn play(&self) -> Result<()> {
self.inner
.set_state(gstreamer::State::Playing)
.change_context(Error)
.attach("Failed to set pipeline to Playing state")?;
Ok(())
}
pub fn pause(&self) -> Result<()> {
self.inner
.set_state(gstreamer::State::Paused)
.change_context(Error)
.attach("Failed to set pipeline to Paused state")?;
Ok(())
}
pub fn ready(&self) -> Result<()> {
self.inner
.set_state(gstreamer::State::Ready)
.change_context(Error)
.attach("Failed to set pipeline to Paused state")?;
Ok(())
}
pub unsafe fn set_state(
&self,
state: gstreamer::State,
) -> Result<gstreamer::StateChangeSuccess> {
let result = self
.inner
.set_state(state)
.change_context(Error)
.attach("Failed to set pipeline state")?;
Ok(result)
}
}
pub struct Bus {
bus: gstreamer::Bus,
}
impl Bus {
pub fn iter_timed(
&self,
timeout: impl Into<Option<core::time::Duration>>,
) -> gstreamer::bus::Iter<'_> {
let clocktime = match timeout.into() {
Some(dur) => gstreamer::ClockTime::try_from(dur).ok(),
None => gstreamer::ClockTime::NONE,
};
self.bus.iter_timed(clocktime)
}
pub fn stream(&self) -> gstreamer::bus::BusStream {
self.bus.stream()
}
}
pub struct Playbin3Builder {
uri: Option<String>,
video_sink: Option<Element>,
audio_sink: Option<Element>,
text_sink: Option<Element>,
}
#[test]
fn gst_parse_pipeline() {
let gst = Gst::new();
let pipeline = gst
.pipeline_from_str("videotestsrc ! autovideosink")
.expect("Failed to create pipeline");
println!("{:?}", pipeline);
}
#[test]
fn gst_parse_invalid_pipeline() {
let gst = Gst::new();
let result = gst.pipeline_from_str("invalidpipeline");
assert!(result.is_err());
}
#[test]
fn gst_play_pipeline() {
let gst = Gst::new();
let pipeline = gst
.pipeline_from_str("videotestsrc ! autovideosink")
.expect("Failed to create pipeline");
let bus = pipeline.bus().expect("Failed to get bus from pipeline");
pipeline
.play()
.expect("Unable to set the pipeline to the `Playing` state");
for msg in bus.iter_timed(None) {
use gstreamer::MessageView;
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
eprintln!(
"Error from {:?}: {} ({:?})",
err.src().map(|s| s.path_string()),
err.error(),
err.debug()
);
break;
}
_ => (),
}
}
}
#[test]
#[ignore]
fn gstreamer_unwrapped() {
gstreamer::init();
let uri = "https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm";
let pipeline = gstreamer::parse::launch(&format!("playbin uri={}", uri)).unwrap();
use gstreamer::prelude::*;
pipeline.set_state(gstreamer::State::Playing).unwrap();
let bus = pipeline.bus().unwrap();
for msg in bus.iter_timed(gstreamer::ClockTime::NONE) {
use gstreamer::MessageView;
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
eprintln!(
"Error from {:?}: {} ({:?})",
err.src().map(|s| s.path_string()),
err.error(),
err.debug()
);
break;
}
_ => (),
}
}
pipeline.set_state(gstreamer::State::Null).unwrap();
}
#[test]
fn test_appsink() {
let gst = Gst::new();
let pipeline = gst
.pipeline_from_str(
"videotestsrc ! videoconvert | capsfilter name=video-filter ! appsink name=video-sink",
)
.expect("Failed to create pipeline");
// let video_sink = pipeline.
let bus = pipeline.bus().expect("Failed to get bus from pipeline");
let sink = pipeline
.inner
.by_name("video-sink")
.expect("Sink not found")
.downcast::<gstreamer_app::AppSink>()
.expect("Failed to downcast to AppSink");
let capsfilter = pipeline
.inner
.by_name("video-filter")
.expect("Capsfilter not found");
let caps = gstreamer::Caps::builder("video/x-raw")
.field("format", "RGBA")
.build();
capsfilter.set_property("caps", &caps);
sink.set_callbacks(
gstreamer_app::AppSinkCallbacks::builder()
.new_sample(|sink| {
// foo
Ok(gstreamer::FlowSuccess::Ok)
})
.build(),
);
pipeline
.play()
.expect("Unable to set the pipeline to the `Playing` state");
for msg in bus.iter_timed(None) {
use gstreamer::MessageView;
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
eprintln!(
"Error from {:?}: {} ({:?})",
err.src().map(|s| s.path_string()),
err.error(),
err.debug()
);
break;
}
_ => (),
}
}
}
#[test]
fn gst_test_manual_pipeline() {
use gstreamer as gst;
use gstreamer::prelude::*;
// Initialize GStreamer
gst::init().unwrap();
// Create a new pipeline
let pipeline = gst::Pipeline::new();
// Create elements for the pipeline
let src = gst::ElementFactory::make("videotestsrc").build().unwrap();
let sink = gst::ElementFactory::make("autovideosink").build().unwrap();
// Add elements to the pipeline
pipeline.add_many(&[&src, &sink]).unwrap();
// Link elements together
src.link(&sink).unwrap();
// Set the pipeline to the playing state
pipeline.set_state(gst::State::Playing).unwrap();
// Start the main event loop
// let main_loop = glib::MainLoop::new(None, false);
// main_loop.run();
// Shut down the pipeline and GStreamer
let bus = pipeline.bus().unwrap();
let messages = bus.iter_timed(gst::ClockTime::NONE);
for msg in messages {
dbg!(msg);
}
pipeline.set_state(gst::State::Null).unwrap();
// pub fn pipeline_from_str(&self, s: &str) -> Result<Pipeline> {
// let pipeline = gstreamer::parse::launch(s).change_context(Error)?;
// let pipeline = pipeline.downcast::<gstreamer::Pipeline>();
// let pipeline = match pipeline {
// Err(_e) => return Err(Error).attach("Failed to downcast to Pipeline"),
// Ok(p) => p,
// };
// Ok(Pipeline { inner: pipeline })
// }
}