Created
November 17, 2024 14:21
-
-
Save SyedAhkam/5735ebc0f88ae7e82f0c6f3a3bfbf9ad to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{ | |
fs::File, | |
io::Cursor, | |
os::fd::{FromRawFd, IntoRawFd, OwnedFd}, | |
sync::{Arc, Mutex}, | |
}; | |
use ashpd::desktop::{ | |
screencast::{CursorMode, Screencast, SourceType, Stream as AshStream}, | |
PersistMode, | |
}; | |
use pipewire::{ | |
context::Context, | |
keys::{MEDIA_CATEGORY, MEDIA_ROLE, MEDIA_TYPE}, | |
main_loop::MainLoop, | |
properties::properties, | |
spa::{ | |
param::{ | |
format::{FormatProperties, MediaSubtype, MediaType}, | |
video::{VideoFormat, VideoInfoRaw}, | |
ParamType, | |
}, | |
pod::{self, deserialize::FractionVisitor, serialize::PodSerializer, Pod}, | |
sys::{ | |
spa_buffer, spa_meta_header, SPA_META_Header, SPA_PARAM_META_size, SPA_PARAM_META_type, | |
}, | |
utils::{Direction, Fraction, Id, Rectangle, SpaTypes}, | |
}, | |
stream::{Stream, StreamFlags}, | |
}; | |
async fn open_portal() -> ashpd::Result<(AshStream, OwnedFd)> { | |
let proxy = Screencast::new().await?; | |
let session = proxy.create_session().await?; | |
proxy | |
.select_sources( | |
&session, | |
CursorMode::Embedded, | |
SourceType::Monitor.into(), | |
false, | |
None, | |
PersistMode::DoNot, | |
) | |
.await?; | |
let response = proxy.start(&session, None).await?.response()?; | |
let stream = response.streams().first().unwrap().to_owned(); | |
let fd = proxy.open_pipe_wire_remote(&session).await?; | |
Ok((stream, fd)) | |
} | |
async fn start_streaming(node_id: u32, fd: OwnedFd) { | |
pipewire::init(); | |
let main_loop = MainLoop::new(None).expect("Failed to create main loop"); | |
let context = Context::new(&main_loop).expect("Failed to create PipeWire context"); | |
let core = context | |
.connect_fd(fd, None) | |
.expect("Failed to connect to PipeWire"); | |
let stream = Stream::new( | |
&core, | |
"ScreenCast", | |
properties! { | |
*MEDIA_TYPE => "Video", | |
*MEDIA_CATEGORY => "Capture", | |
*MEDIA_ROLE => "Screen", | |
}, | |
) | |
.expect("Failed to create stream"); | |
stream | |
.add_local_listener::<Vec<String>>() | |
.state_changed(|_, _, _, _| { | |
println!("Stream state changed"); | |
}) | |
.process(|stream, _| { | |
println!("{:?}", stream.state()); | |
}) | |
.register() | |
.expect("Failed to connect local listener"); | |
let obj = pod::object!( | |
SpaTypes::ObjectParamFormat, | |
ParamType::EnumFormat, | |
pod::property!(FormatProperties::MediaType, Id, MediaType::Video), | |
pod::property!(FormatProperties::MediaSubtype, Id, MediaSubtype::Raw), | |
pod::property!( | |
FormatProperties::VideoFormat, | |
Choice, | |
Enum, | |
Id, | |
VideoFormat::RGB, | |
VideoFormat::RGBA, | |
// VideoFormat::RGBx, | |
// VideoFormat::BGRx, | |
), | |
pod::property!( | |
FormatProperties::VideoSize, | |
Choice, | |
Range, | |
Rectangle, | |
Rectangle { | |
// Default | |
width: 2560, | |
height: 1600, | |
}, | |
Rectangle { | |
// Min | |
width: 1, | |
height: 1, | |
}, | |
Rectangle { | |
// Max | |
width: 4096, | |
height: 4096, | |
} | |
), | |
pod::property!( | |
FormatProperties::VideoFramerate, | |
Choice, | |
Range, | |
Fraction, | |
Fraction { num: 30, denom: 1 }, | |
Fraction { num: 1, denom: 1 }, | |
Fraction { | |
num: 1000, | |
denom: 1 | |
} | |
), | |
); | |
let metas_obj = pod::object!( | |
SpaTypes::ObjectParamMeta, | |
ParamType::Meta, | |
pod::Property::new(SPA_PARAM_META_type, pod::Value::Id(Id(SPA_META_Header))), | |
pod::Property::new( | |
SPA_PARAM_META_size, | |
pod::Value::Int(size_of::<spa_meta_header>() as i32) | |
), | |
); | |
let values: Vec<u8> = | |
PodSerializer::serialize(Cursor::new(Vec::new()), &pod::Value::Object(obj)) | |
.unwrap() | |
.0 | |
.into_inner(); | |
let metas_values: Vec<u8> = PodSerializer::serialize( | |
std::io::Cursor::new(Vec::new()), | |
&pod::Value::Object(metas_obj), | |
) | |
.unwrap() | |
.0 | |
.into_inner(); | |
println!("Node ID: {:?}", node_id); | |
let params = &mut [ | |
Pod::from_bytes(&values).unwrap(), | |
Pod::from_bytes(&metas_values).unwrap(), | |
]; | |
stream | |
.connect( | |
Direction::Output, | |
Some(node_id), | |
StreamFlags::AUTOCONNECT | StreamFlags::MAP_BUFFERS, | |
params, | |
) | |
.expect("Failed to connect stream"); | |
main_loop.run(); | |
} | |
smol_macros::main! { | |
async fn main() { | |
let (stream, fd) = open_portal().await.expect("failed to open portal"); | |
start_streaming(stream.pipe_wire_node_id(), fd).await; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment