refactored video stream handler to handle RTSP feed on linux with multiple OS data, avoinding DRY

This commit is contained in:
Valmo Trindade
2025-06-19 23:44:19 -03:00
parent b1fac90e78
commit e713c2f35f

View File

@@ -16,6 +16,56 @@ lazy_static! {
#[cfg(target_os = "windows")]
const CREATE_NO_WINDOW: u32 = 0x08000000;
fn build_rtsp_url(address: &str, port: &str, stream_path: &str, username: &str, password: &str) -> String {
if username.is_empty() || password.is_empty() {
format!("rtsp://{}:{}/{}", address, port, stream_path)
} else {
format!(
"rtsp://{}:{}@{}:{}/{}",
username, password, address, port, stream_path
)
}
}
#[cfg(any(target_os = "windows", target_os = "linux"))]
fn spawn_ffmpeg(
rtsp_url: String,
stop_rx: Receiver<()>,
status_tx: Sender<Result<(), String>>,
) {
thread::spawn(move || {
let mut cmd = Command::new("ffmpeg");
cmd.args(&[
"-f",
"gdigrab",
"-i",
"desktop",
"-f",
"rtsp",
"-rtsp_transport",
"tcp",
&rtsp_url,
]);
#[cfg(target_os = "windows")]
let child_result = cmd.creation_flags(CREATE_NO_WINDOW).spawn();
#[cfg(target_os = "linux")]
let child_result = cmd.spawn();
match child_result {
Ok(mut child) => {
let _ = status_tx.send(Ok(()));
let _ = stop_rx.recv();
let _ = child.kill();
}
Err(e) => {
let _ = status_tx.send(Err(format!("Failed to start FFmpeg: {}", e)));
}
}
});
}
pub fn start_stream(
ctx: Context,
address: String,
@@ -24,52 +74,15 @@ pub fn start_stream(
username: String,
password: String,
) -> &'static str {
#[cfg(target_os = "linux")]
#[cfg(any(target_os = "windows", target_os = "linux"))]
{
let (stop_tx, stop_rx): (Sender<()>, Receiver<()>) = mpsc::channel();
let (status_tx, status_rx): (Sender<Result<(), String>>, Receiver<Result<(), String>>) = mpsc::channel();
let (stop_tx, stop_rx) = mpsc::channel();
let (status_tx, status_rx) = mpsc::channel();
let rtsp_url = if username.is_empty() || password.is_empty() {
format!("rtsp://{}:{}/{}", address, port, stream_path)
} else {
format!(
"rtsp://{}:{}@{}:{}/{}",
username, password, address, port, stream_path
)
};
let rtsp_url = build_rtsp_url(&address, &port, &stream_path, &username, &password);
let rtsp_url_clone = rtsp_url.clone();
spawn_ffmpeg(rtsp_url, stop_rx, status_tx);
thread::spawn(move || {
let mut cmd = Command::new("ffmpeg");
cmd.args(&[
"-f",
"gdigrab",
"-i",
"desktop",
"-f",
"rtsp",
"-rtsp_transport",
"tcp",
&rtsp_url_clone,
]);
// Try to spawn ffmpeg process
let child_result = cmd.spawn();
match child_result {
Ok(mut child) => {
let _ = status_tx.send(Ok(()));
if stop_rx.recv().is_err() {}
let _ = child.kill();
}
Err(e) => {
let _ = status_tx.send(Err(format!("Failed to start FFmpeg: {}", e)));
}
}
});
// Save the stop channel
match STREAM_CTRL.lock() {
Ok(mut lock) => *lock = Some(stop_tx),
Err(e) => {
@@ -82,82 +95,6 @@ pub fn start_stream(
}
}
// Wait up to 2 seconds to see if ffmpeg started correctly
match status_rx.recv_timeout(Duration::from_secs(2)) {
Ok(Ok(())) => {
let _ = ctx.callback_null("VIDEO", "FFmpeg started successfully");
"starting video stream"
}
Ok(Err(e)) => {
let _ = ctx.callback_data("VIDEO ERROR", "FFmpeg failed to start", e);
"ffmpeg failed to start"
}
Err(_) => {
let _ = ctx.callback_null("VIDEO ERROR", "FFmpeg did not respond in time");
"ffmpeg did not respond"
}
}
}
#[cfg(target_os = "windows")]
{
let (stop_tx, stop_rx): (Sender<()>, Receiver<()>) = mpsc::channel();
let (status_tx, status_rx): (Sender<Result<(), String>>, Receiver<Result<(), String>>) = mpsc::channel();
let rtsp_url = if username.is_empty() || password.is_empty() {
format!("rtsp://{}:{}/{}", address, port, stream_path)
} else {
format!(
"rtsp://{}:{}@{}:{}/{}",
username, password, address, port, stream_path
)
};
let rtsp_url_clone = rtsp_url.clone();
thread::spawn(move || {
let mut cmd = Command::new("ffmpeg");
cmd.args(&[
"-f",
"gdigrab",
"-i",
"desktop",
"-f",
"rtsp",
"-rtsp_transport",
"tcp",
&rtsp_url_clone,
]);
// Try to spawn ffmpeg process
let child_result = cmd.creation_flags(CREATE_NO_WINDOW).spawn();
match child_result {
Ok(mut child) => {
let _ = status_tx.send(Ok(()));
if stop_rx.recv().is_err() {}
let _ = child.kill();
}
Err(e) => {
let _ = status_tx.send(Err(format!("Failed to start FFmpeg: {}", e)));
}
}
});
// Save the stop channel
match STREAM_CTRL.lock() {
Ok(mut lock) => *lock = Some(stop_tx),
Err(e) => {
let _ = ctx.callback_data(
"VIDEO ERROR",
"Failed to acquire lock for stream control",
e.to_string(),
);
return "stream control lock error";
}
}
// Wait up to 2 seconds to see if ffmpeg started correctly
match status_rx.recv_timeout(Duration::from_secs(2)) {
Ok(Ok(())) => {
let _ = ctx.callback_null("VIDEO", "FFmpeg started successfully");