Skip to content

Instantly share code, notes, and snippets.

@jakobrs
Last active July 12, 2022 10:33
Show Gist options
  • Save jakobrs/78687f41e365da9ea3a0e23a0b4db693 to your computer and use it in GitHub Desktop.
Save jakobrs/78687f41e365da9ea3a0e23a0b4db693 to your computer and use it in GitHub Desktop.
use gstreamer as gst;
use gstreamer_app as gst_app;
use gstreamer_video as gst_video;
use anyhow::Result;
use byte_slice_cast::*;
use gst::prelude::*;
use portal_screencast::ScreenCast;
fn main() -> Result<()> {
env_logger::init();
let screencast = ScreenCast::new()?.start(None)?;
let fd = screencast.pipewire_fd();
let stream = screencast.streams().next().expect("No stream");
let node = stream.pipewire_node();
log::info!("Pipewire screencast session set up with fd={fd} node={node}");
gst::init()?;
let pipeline = gst::Pipeline::new(None);
let pipewiresrc =
gst::ElementFactory::make_with_properties("pipewiresrc", &[("fd", &fd), ("path", &node)])?;
let appsink = gst::ElementFactory::make("appsink", None)?;
pipeline.add_many(&[&pipewiresrc, &appsink])?;
pipewiresrc.link(&appsink)?;
let appsink = appsink
.dynamic_cast::<gst_app::AppSink>()
.map_err(|_| anyhow::anyhow!("Failed to perform dynamic cast"))?;
appsink.set_caps(Some(
&gst::Caps::builder("video/x-raw")
.field("format", "RGBx") // this seems to be the default and is the only format that works
.build(),
));
appsink.set_callbacks(
gst_app::AppSinkCallbacks::builder()
.new_sample(|appsink| {
let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?;
let buffer = sample.buffer().expect("Getting buffer"); // TODO: errors
let map = buffer.map_readable().expect("Mapping");
let samples = map.as_slice_of::<u32>().expect("Reinterpreting samples");
log::info!("First 10 pixels: {:x?}", &samples[..10]);
Ok(gst::FlowSuccess::Ok)
})
.build(),
);
// don't really know how this works
// pieced together from random bits of documentation and this
// StackOveflow answer: https://stackoverflow.com/a/55516750
let pad = pipewiresrc
.static_pad("src")
.ok_or_else(|| anyhow::anyhow!("Unable to get pad thingy"))?;
pad.add_probe(gst::PadProbeType::EVENT_BOTH, |_, info| {
if let Some(gst::PadProbeData::Event(ev)) = &info.data {
match ev.view() {
gst::EventView::Caps(caps) => {
log::info!("caps: {:?}", caps.caps());
}
_ => (),
}
}
gst::PadProbeReturn::Ok
})
.ok_or_else(|| anyhow::anyhow!("Unable to add probe"))?;
log::info!("Setting state to PLAYING");
pipeline.set_state(gst::State::Playing)?;
log::info!("Set state to PLAYING");
let bus = pipeline.bus().unwrap();
for msg in bus.iter_timed(gst::ClockTime::NONE) {
use gst::MessageView;
log::debug!("{msg:?}");
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
println!(
"Error from {:?}: {} ({:?})",
err.src().map(|s| s.path_string()),
err.error(),
err.debug()
);
break;
}
_ => (),
}
}
pipeline.set_state(gst::State::Null)?;
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment