WIP node request

This commit is contained in:
Melody Becker 2025-03-23 08:23:51 +01:00
parent 64210b1983
commit 752715e55b
2 changed files with 115 additions and 9 deletions

View file

@ -3,9 +3,14 @@ use std::thread;
use godot::{classes::notify::NodeNotification, prelude::*}; use godot::{classes::notify::NodeNotification, prelude::*};
use pipewire::{ use pipewire::{
channel::{Receiver, Sender}, channel::{Receiver, Sender},
context::Context,
core::Core,
main_loop::MainLoop, main_loop::MainLoop,
stream::Stream,
}; };
mod stream_node;
#[derive(GodotClass)] #[derive(GodotClass)]
#[class(base=Node)] #[class(base=Node)]
/// Node for interacting with Pipewire /// Node for interacting with Pipewire
@ -15,10 +20,19 @@ pub(crate) struct Pipewire {
/// Handle for the thread running the pipewire main loop /// Handle for the thread running the pipewire main loop
thread_handle: Option<thread::JoinHandle<()>>, thread_handle: Option<thread::JoinHandle<()>>,
shutdown_sender: Sender<Terminate>, shutdown_sender: Sender<InboundPwSignal>,
} }
struct Terminate; #[derive(Debug, Clone, PartialEq)]
enum InboundPwSignal {
Terminate,
ConnectToStream(u32),
}
#[derive(Debug)]
enum ResultPwSignal {
StreamConnection(Result<Stream, String>),
}
#[godot_api] #[godot_api]
impl INode for Pipewire { impl INode for Pipewire {
@ -37,33 +51,37 @@ impl INode for Pipewire {
|| what == NodeNotification::EXIT_TREE || what == NodeNotification::EXIT_TREE
{ {
godot_print!("Sending shutdown signal to pipewire thread"); godot_print!("Sending shutdown signal to pipewire thread");
let _ = self.shutdown_sender.send(Terminate {}); let _ = self.shutdown_sender.send(InboundPwSignal::Terminate);
let handle = self.thread_handle.take().unwrap(); let handle = self.thread_handle.take().unwrap();
let _join_result = handle.join(); let _join_result = handle.join();
godot_print!("Pipewire thread completed"); godot_print!("Pipewire thread completed");
} }
} }
fn process(&mut self, _delta: f64) {}
} }
#[godot_api] #[godot_api]
impl Pipewire { impl Pipewire {
pub const SINGLETON: &'static str = "Pipewire"; pub const SINGLETON: &'static str = "Pipewire";
fn start_pipewire_thread() -> (thread::JoinHandle<()>, Sender<Terminate>) { fn start_pipewire_thread() -> (thread::JoinHandle<()>, Sender<InboundPwSignal>) {
godot_print!("Starting pipewire thread"); godot_print!("Starting pipewire thread");
let (shutdown_sender, shutdown_receiver) = pipewire::channel::channel::<Terminate>(); let (shutdown_sender, shutdown_receiver) = pipewire::channel::channel::<InboundPwSignal>();
let thread_handle = thread::spawn(|| Self::pipewire_main(shutdown_receiver)); let thread_handle = thread::spawn(|| Self::pipewire_main(shutdown_receiver));
(thread_handle, shutdown_sender) (thread_handle, shutdown_sender)
} }
/// Func that actually runs the main loop in the thread /// Func that actually runs the main loop in the thread
fn pipewire_main(shutdown_receiver: Receiver<Terminate>) { fn pipewire_main(shutdown_receiver: Receiver<InboundPwSignal>) {
let mainloop = MainLoop::new(None).expect("Failed to create pipewire main loop"); let mainloop = MainLoop::new(None).expect("Failed to create pipewire main loop");
let context =
Context::new(&mainloop).expect("Failed to get context from pipewire main loop");
let core = context
.connect(None)
.expect("Failed to get core from pipewire context");
let _receiver = shutdown_receiver.attach(mainloop.loop_(), { let _receiver = shutdown_receiver.attach(mainloop.loop_(), {
godot_print!("Stopping pipewire thread"); pipewire_loop_signal_handler(mainloop.clone(), core.clone())
let mainloop = mainloop.clone();
move |_| mainloop.quit()
}); });
godot_print!("Entering pipewire main loop"); godot_print!("Entering pipewire main loop");
@ -71,3 +89,11 @@ impl Pipewire {
println!("Pipewire main loop exited"); println!("Pipewire main loop exited");
} }
} }
fn pipewire_loop_signal_handler(mainloop: MainLoop, _core: Core) -> impl Fn(InboundPwSignal) {
godot_print!("Stopping pipewire thread");
move |signal| match signal {
InboundPwSignal::Terminate => mainloop.quit(),
InboundPwSignal::ConnectToStream(_stream_id) => {}
}
}

View file

@ -0,0 +1,80 @@
use godot::{
classes::{IVideoStream, IVideoStreamPlayback, Texture2D, VideoStream, VideoStreamPlayback},
prelude::*,
};
use pipewire::stream::Stream;
#[derive(GodotClass)]
#[class(base=VideoStream)]
pub(crate) struct PwVideoStreamNode {
base: Base<VideoStream>,
stream: Option<Stream>,
}
#[derive(GodotClass)]
#[class(base=VideoStreamPlayback)]
pub(crate) struct PwVideoStreamPlayback {
base: Base<VideoStreamPlayback>,
paused: bool,
}
#[godot_api]
impl IVideoStream for PwVideoStreamNode {
fn init(base: Base<VideoStream>) -> Self {
Self { base, stream: None }
}
}
#[godot_api]
impl IVideoStreamPlayback for PwVideoStreamPlayback {
fn init(base: Base<VideoStreamPlayback>) -> Self {
Self { base, paused: true }
}
fn update(&mut self, _delta: f64) {
if self.paused {
return;
}
// TODO: Get latest frame or keep last one if no new one
todo!()
}
fn play(&mut self) {
self.paused = false;
// TODO: Start copying frames again
todo!()
}
fn stop(&mut self) {
self.paused = true;
// TODO: Stop updating the last known frame
todo!()
}
fn is_paused(&self) -> bool {
self.paused
}
fn set_paused(&mut self, paused: bool) {
if paused {
self.stop();
} else {
self.play();
}
}
fn is_playing(&self) -> bool {
!self.paused
}
fn get_texture(&self) -> Option<Gd<Texture2D>> {
// TODO: Transform latest frame into Godot image
todo!()
}
}
impl PwVideoStreamPlayback {
pub fn setup(&mut self) {
// TODO: Store pipewire stream and fetch first frame
}
}