Skip to content

Commit 756e599

Browse files
committed
handle ffmpeg thread shutdown
1 parent af6a133 commit 756e599

File tree

4 files changed

+52
-35
lines changed

4 files changed

+52
-35
lines changed

src-tauri/src/lib.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,12 @@ async fn on_start_check_epg(
405405
}
406406

407407
#[tauri::command]
408-
async fn start_restream(state: State<'_, Mutex<AppState>>, channel: Channel) -> Result<(), String> {
409-
crate::restream::start_restream(state, channel)
408+
async fn start_restream(
409+
state: State<'_, Mutex<AppState>>,
410+
app: AppHandle,
411+
channel: Channel,
412+
) -> Result<(), String> {
413+
crate::restream::start_restream(state, app, channel)
410414
.await
411415
.map_err(map_err_frontend)
412416
}

src-tauri/src/restream.rs

+29-21
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::{
22
path::{Path, PathBuf},
33
process::{Child, Command, Stdio},
4+
time::Duration,
45
};
56

67
use anyhow::{Context, Result};
7-
use tauri::State;
8+
use tauri::{AppHandle, Emitter, State};
89
use tokio::{
910
fs,
1011
sync::{
@@ -14,7 +15,6 @@ use tokio::{
1415
};
1516

1617
use crate::{
17-
log::log,
1818
mpv,
1919
settings::get_settings,
2020
sql,
@@ -83,31 +83,39 @@ async fn start_web_server(
8383
return Ok((tx, handle));
8484
}
8585

86-
pub async fn start_restream(state: State<'_, Mutex<AppState>>, channel: Channel) -> Result<()> {
87-
let mut state = state.lock().await;
86+
pub async fn start_restream(
87+
state: State<'_, Mutex<AppState>>,
88+
app: AppHandle,
89+
channel: Channel,
90+
) -> Result<()> {
91+
let stop = state.lock().await.restream_stop_signal.clone();
92+
stop.store(false, std::sync::atomic::Ordering::Relaxed);
8893
let restream_dir = get_restream_folder()?;
8994
delete_old_segments(&restream_dir).await?;
90-
state.ffmpeg_child = Some(start_ffmpeg_listening(channel, restream_dir.clone())?);
91-
(state.web_server_tx, state.web_server_handle) = start_web_server(restream_dir)
92-
.await
93-
.map(|(tx, handle)| (Some(tx), Some(handle)))?;
95+
let mut ffmpeg_child = start_ffmpeg_listening(channel, restream_dir.clone())?;
96+
let (web_server_tx, web_server_handle) = start_web_server(restream_dir).await?;
97+
let _ = app.emit("restream_started", true);
98+
while !stop.load(std::sync::atomic::Ordering::Relaxed)
99+
&& ffmpeg_child
100+
.try_wait()
101+
.map(|option| option.is_none())
102+
.unwrap_or(true)
103+
&& !web_server_handle.is_finished()
104+
{
105+
tokio::time::sleep(Duration::from_millis(500)).await
106+
}
107+
let _ = ffmpeg_child.kill();
108+
let _ = web_server_tx.send(true);
109+
let _ = ffmpeg_child.wait();
110+
let _ = web_server_handle.await;
94111
Ok(())
95112
}
96113

97114
pub async fn stop_restream(state: State<'_, Mutex<AppState>>) -> Result<()> {
98-
let mut state = state.lock().await;
99-
let mut ffmpeg_child = state.ffmpeg_child.take().context("no ffmpeg child")?;
100-
let web_server_tx = state.web_server_tx.take().context("no web server tx")?;
101-
let web_server_handle = state
102-
.web_server_handle
103-
.take()
104-
.context("no web server handle")?;
105-
let _ = ffmpeg_child.kill();
106-
let _ = web_server_tx.send(true);
107-
let _ = ffmpeg_child.wait();
108-
let _ = web_server_handle
109-
.await
110-
.unwrap_or_else(|e| log(format!("{:?}", e)));
115+
let state = state.lock().await;
116+
state
117+
.restream_stop_signal
118+
.store(true, std::sync::atomic::Ordering::Relaxed);
111119
Ok(())
112120
}
113121

src-tauri/src/types.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,7 @@ pub struct EPGNotify {
141141
pub struct AppState {
142142
pub notify_stop: Arc<AtomicBool>,
143143
pub thread_handle: Option<JoinHandle<Result<(), anyhow::Error>>>,
144-
pub ffmpeg_child: Option<std::process::Child>,
145-
pub web_server_tx: Option<tokio::sync::oneshot::Sender<bool>>,
146-
pub web_server_handle: Option<tokio::task::JoinHandle<()>>,
144+
pub restream_stop_signal: Arc<AtomicBool>,
147145
}
148146

149147
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]

src/app/restream-modal/restream-modal.component.ts

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,48 @@
1-
import { Component, OnInit } from "@angular/core";
1+
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
22
import { Channel } from "../models/channel";
33
import { invoke } from "@tauri-apps/api/core";
44
import { ErrorService } from "../error.service";
55
import { NetworkInfo } from "../models/networkInfo";
66
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
7+
import { UnlistenFn, listen } from "@tauri-apps/api/event";
78

89
@Component({
910
selector: "app-restream-modal",
1011
templateUrl: "./restream-modal.component.html",
1112
styleUrl: "./restream-modal.component.css",
1213
})
13-
export class RestreamModalComponent implements OnInit {
14+
export class RestreamModalComponent implements OnInit, OnDestroy {
1415
channel?: Channel;
1516
loading = false;
1617
watching = false;
1718
started = false;
18-
address = "http://192.168.2.10/stream.m3u8";
19-
wanAddress = "http://10.145.22.12/stream.m3u8";
2019
networkInfo?: NetworkInfo;
2120
selectedIP?: string;
22-
21+
toUnlisten: UnlistenFn[] = [];
2322
constructor(
2423
private error: ErrorService,
2524
public activeModal: NgbActiveModal,
25+
private ngZone: NgZone,
2626
) {}
2727

2828
ngOnInit(): void {
2929
invoke("get_network_info").then((network) => {
3030
this.networkInfo = network as NetworkInfo;
3131
this.selectedIP = this.networkInfo.local_ips[0];
3232
});
33+
listen<boolean>("restream_started", () => {
34+
this.ngZone.run(() => {
35+
this.started = true;
36+
this.loading = false;
37+
});
38+
}).then((unlisten) => this.toUnlisten.push(unlisten));
3339
}
3440

3541
async start() {
3642
this.loading = true;
3743
try {
3844
await invoke("start_restream", { channel: this.channel });
39-
this.error.success("Successfully started service");
40-
this.started = true;
45+
this.started = false;
4146
} catch (e) {
4247
this.error.handleError(e);
4348
}
@@ -48,8 +53,6 @@ export class RestreamModalComponent implements OnInit {
4853
this.loading = true;
4954
try {
5055
await invoke("stop_restream");
51-
this.error.success("Successfully stopped service");
52-
this.started = false;
5356
} catch (e) {
5457
this.error.handleError(e);
5558
}
@@ -76,4 +79,8 @@ export class RestreamModalComponent implements OnInit {
7679
this.error.handleError(e);
7780
}
7881
}
82+
83+
ngOnDestroy(): void {
84+
this.toUnlisten.forEach((x) => x());
85+
}
7986
}

0 commit comments

Comments
 (0)