mirror of
https://github.com/v0l/zap-stream-core.git
synced 2025-06-20 13:40:33 +00:00
feat: clean shutdown RTMP stream
All checks were successful
continuous-integration/drone Build is passing
All checks were successful
continuous-integration/drone Build is passing
This commit is contained in:
@ -17,6 +17,7 @@ pub async fn listen(out_dir: String, path: PathBuf, overseer: Arc<dyn Overseer>)
|
||||
app_name: "".to_string(),
|
||||
key: "test".to_string(),
|
||||
};
|
||||
let url = path.to_str().unwrap().to_string();
|
||||
let file = std::fs::File::open(path)?;
|
||||
spawn_pipeline(
|
||||
Handle::current(),
|
||||
@ -24,6 +25,8 @@ pub async fn listen(out_dir: String, path: PathBuf, overseer: Arc<dyn Overseer>)
|
||||
out_dir.clone(),
|
||||
overseer.clone(),
|
||||
Box::new(file),
|
||||
Some(url),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::overseer::Overseer;
|
||||
use crate::pipeline::runner::PipelineRunner;
|
||||
use crate::pipeline::runner::{PipelineCommand, PipelineRunner};
|
||||
use log::{error, info, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::Read;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use tokio::runtime::Handle;
|
||||
@ -40,8 +41,10 @@ pub fn spawn_pipeline(
|
||||
out_dir: String,
|
||||
seer: Arc<dyn Overseer>,
|
||||
reader: Box<dyn Read + Send>,
|
||||
url: Option<String>,
|
||||
rx: Option<Receiver<PipelineCommand>>,
|
||||
) {
|
||||
match PipelineRunner::new(handle, out_dir, seer, info, reader, None) {
|
||||
match PipelineRunner::new(handle, out_dir, seer, info, reader, url, rx) {
|
||||
Ok(pl) => match run_pipeline(pl) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::ingress::{BufferedReader, ConnectionInfo};
|
||||
use crate::overseer::Overseer;
|
||||
use crate::pipeline::runner::PipelineRunner;
|
||||
use crate::pipeline::runner::{PipelineCommand, PipelineRunner};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use log::{error, info};
|
||||
@ -11,6 +11,7 @@ use rml_rtmp::sessions::{
|
||||
use std::collections::VecDeque;
|
||||
use std::io::{ErrorKind, Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::net::TcpListener;
|
||||
@ -32,10 +33,11 @@ struct RtmpClient {
|
||||
msg_queue: VecDeque<ServerSessionResult>,
|
||||
pub published_stream: Option<RtmpPublishedStream>,
|
||||
muxer: FlvMuxer,
|
||||
tx: Sender<PipelineCommand>,
|
||||
}
|
||||
|
||||
impl RtmpClient {
|
||||
pub fn new(socket: TcpStream) -> Result<Self> {
|
||||
pub fn new(socket: TcpStream, tx: Sender<PipelineCommand>) -> Result<Self> {
|
||||
socket.set_nonblocking(false)?;
|
||||
let cfg = ServerSessionConfig::new();
|
||||
let (ses, res) = ServerSession::new(cfg)?;
|
||||
@ -46,6 +48,7 @@ impl RtmpClient {
|
||||
msg_queue: VecDeque::from(res),
|
||||
published_stream: None,
|
||||
muxer: FlvMuxer::new(),
|
||||
tx,
|
||||
})
|
||||
}
|
||||
|
||||
@ -201,8 +204,12 @@ impl RtmpClient {
|
||||
self.published_stream = Some(RtmpPublishedStream(app_name, stream_key));
|
||||
}
|
||||
}
|
||||
ServerSessionEvent::PublishStreamFinished { .. } => {
|
||||
// TODO: shutdown pipeline
|
||||
ServerSessionEvent::PublishStreamFinished {
|
||||
app_name,
|
||||
stream_key,
|
||||
} => {
|
||||
self.tx.send(PipelineCommand::Shutdown)?;
|
||||
info!("Stream ending: {app_name}/{stream_key}");
|
||||
}
|
||||
ServerSessionEvent::StreamMetadataChanged {
|
||||
app_name,
|
||||
@ -271,7 +278,6 @@ pub async fn listen(out_dir: String, addr: String, overseer: Arc<dyn Overseer>)
|
||||
|
||||
info!("RTMP listening on: {}", &addr);
|
||||
while let Ok((socket, ip)) = listener.accept().await {
|
||||
let mut cc = RtmpClient::new(socket.into_std()?)?;
|
||||
let overseer = overseer.clone();
|
||||
let out_dir = out_dir.clone();
|
||||
let handle = Handle::current();
|
||||
@ -279,6 +285,8 @@ pub async fn listen(out_dir: String, addr: String, overseer: Arc<dyn Overseer>)
|
||||
std::thread::Builder::new()
|
||||
.name(format!("client:rtmp:{}", new_id))
|
||||
.spawn(move || {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let mut cc = RtmpClient::new(socket.into_std()?, tx)?;
|
||||
if let Err(e) = cc.handshake() {
|
||||
bail!("Error during handshake: {}", e)
|
||||
}
|
||||
@ -301,6 +309,7 @@ pub async fn listen(out_dir: String, addr: String, overseer: Arc<dyn Overseer>)
|
||||
info,
|
||||
Box::new(cc),
|
||||
None,
|
||||
Some(rx),
|
||||
) {
|
||||
Ok(pl) => pl,
|
||||
Err(e) => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::ingress::{spawn_pipeline, BufferedReader, ConnectionInfo};
|
||||
use crate::overseer::Overseer;
|
||||
use crate::pipeline::runner::PipelineCommand;
|
||||
use anyhow::Result;
|
||||
use futures_util::stream::FusedStream;
|
||||
use futures_util::StreamExt;
|
||||
@ -7,6 +8,7 @@ use log::info;
|
||||
use srt_tokio::{SrtListener, SrtSocket};
|
||||
use std::io::Read;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use std::sync::Arc;
|
||||
use tokio::runtime::Handle;
|
||||
use uuid::Uuid;
|
||||
@ -31,6 +33,7 @@ pub async fn listen(out_dir: String, addr: String, overseer: Arc<dyn Overseer>)
|
||||
.as_ref()
|
||||
.map_or(String::new(), |s| s.to_string()),
|
||||
};
|
||||
let (tx, rx) = channel();
|
||||
spawn_pipeline(
|
||||
Handle::current(),
|
||||
info,
|
||||
@ -40,7 +43,10 @@ pub async fn listen(out_dir: String, addr: String, overseer: Arc<dyn Overseer>)
|
||||
handle: Handle::current(),
|
||||
socket,
|
||||
buffer: BufferedReader::new(4096, MAX_SRT_BUFFER_SIZE, "SRT"),
|
||||
tx,
|
||||
}),
|
||||
None,
|
||||
Some(rx),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
@ -50,6 +56,7 @@ struct SrtReader {
|
||||
pub handle: Handle,
|
||||
pub socket: SrtSocket,
|
||||
pub buffer: BufferedReader,
|
||||
pub tx: Sender<PipelineCommand>, // TODO: implement clean shutdown
|
||||
}
|
||||
|
||||
impl Read for SrtReader {
|
||||
|
@ -27,6 +27,8 @@ pub async fn listen(out_dir: String, addr: String, overseer: Arc<dyn Overseer>)
|
||||
out_dir.clone(),
|
||||
overseer.clone(),
|
||||
Box::new(socket),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
|
@ -13,7 +13,6 @@ use ringbuf::traits::{Observer, Split};
|
||||
use ringbuf::{HeapCons, HeapRb};
|
||||
use std::io::Read;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tiny_skia::Pixmap;
|
||||
use tokio::runtime::Handle;
|
||||
use uuid::Uuid;
|
||||
@ -21,10 +20,6 @@ use uuid::Uuid;
|
||||
pub async fn listen(out_dir: String, overseer: Arc<dyn Overseer>) -> Result<()> {
|
||||
info!("Test pattern enabled");
|
||||
|
||||
// add a delay, there is a race condition somewhere, the test pattern doesnt always
|
||||
// get added to active_streams
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
|
||||
let info = ConnectionInfo {
|
||||
id: Uuid::new_v4(),
|
||||
endpoint: "test-pattern",
|
||||
@ -36,9 +31,11 @@ pub async fn listen(out_dir: String, overseer: Arc<dyn Overseer>) -> Result<()>
|
||||
spawn_pipeline(
|
||||
Handle::current(),
|
||||
info,
|
||||
out_dir.clone(),
|
||||
overseer.clone(),
|
||||
out_dir,
|
||||
overseer,
|
||||
Box::new(src),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user