mirror of
https://github.com/v0l/zap-stream-core.git
synced 2025-06-21 22:12:50 +00:00
fix: tag recording in ended stream event
This commit is contained in:
@ -18,11 +18,13 @@ pub struct RecorderEgress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RecorderEgress {
|
impl RecorderEgress {
|
||||||
|
pub const FILENAME: &'static str = "recording.mp4";
|
||||||
|
|
||||||
pub fn new<'a>(
|
pub fn new<'a>(
|
||||||
out_dir: PathBuf,
|
out_dir: PathBuf,
|
||||||
variants: impl Iterator<Item = (&'a VariantStream, &'a Encoder)>,
|
variants: impl Iterator<Item = (&'a VariantStream, &'a Encoder)>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let out_file = out_dir.join("recording.mp4");
|
let out_file = out_dir.join(Self::FILENAME);
|
||||||
let mut var_map = HashMap::new();
|
let mut var_map = HashMap::new();
|
||||||
let muxer = unsafe {
|
let muxer = unsafe {
|
||||||
let mut m = Muxer::builder()
|
let mut m = Muxer::builder()
|
||||||
|
@ -358,10 +358,10 @@ impl ZapStreamDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get ingest endpoint by id
|
/// Get ingest endpoint by id
|
||||||
pub async fn get_ingest_endpoint(&self, endpoint_id: u64) -> Result<Option<IngestEndpoint>> {
|
pub async fn get_ingest_endpoint(&self, endpoint_id: u64) -> Result<IngestEndpoint> {
|
||||||
Ok(sqlx::query_as("select * from ingest_endpoint where id = ?")
|
Ok(sqlx::query_as("select * from ingest_endpoint where id = ?")
|
||||||
.bind(endpoint_id)
|
.bind(endpoint_id)
|
||||||
.fetch_optional(&self.db)
|
.fetch_one(&self.db)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ use tokio::sync::RwLock;
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use zap_stream_core::egress::hls::HlsEgress;
|
use zap_stream_core::egress::hls::HlsEgress;
|
||||||
|
use zap_stream_core::egress::recorder::RecorderEgress;
|
||||||
use zap_stream_core::egress::{EgressConfig, EgressSegment};
|
use zap_stream_core::egress::{EgressConfig, EgressSegment};
|
||||||
use zap_stream_core::ingress::ConnectionInfo;
|
use zap_stream_core::ingress::ConnectionInfo;
|
||||||
use zap_stream_core::overseer::{IngressInfo, IngressStream, IngressStreamType, Overseer};
|
use zap_stream_core::overseer::{IngressInfo, IngressStream, IngressStreamType, Overseer};
|
||||||
@ -230,9 +231,18 @@ impl ZapStreamOverseer {
|
|||||||
) -> Result<Event> {
|
) -> Result<Event> {
|
||||||
// TODO: remove assumption that HLS is enabled
|
// TODO: remove assumption that HLS is enabled
|
||||||
let pipeline_dir = PathBuf::from(stream.id.to_string());
|
let pipeline_dir = PathBuf::from(stream.id.to_string());
|
||||||
let extra_tags = vec![
|
let mut extra_tags = vec![
|
||||||
Tag::parse(["p", hex::encode(pubkey).as_str(), "", "host"])?,
|
Tag::parse(["p", hex::encode(pubkey).as_str(), "", "host"])?,
|
||||||
Tag::parse([
|
Tag::parse([
|
||||||
|
"image",
|
||||||
|
self.map_to_public_url(pipeline_dir.join("thumb.webp").to_str().unwrap())?
|
||||||
|
.as_str(),
|
||||||
|
])?,
|
||||||
|
Tag::parse(["service", self.map_to_public_url("api/v1")?.as_str()])?,
|
||||||
|
];
|
||||||
|
match stream.state {
|
||||||
|
UserStreamState::Live => {
|
||||||
|
extra_tags.push(Tag::parse([
|
||||||
"streaming",
|
"streaming",
|
||||||
self.map_to_public_url(
|
self.map_to_public_url(
|
||||||
pipeline_dir
|
pipeline_dir
|
||||||
@ -242,14 +252,31 @@ impl ZapStreamOverseer {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
)?
|
)?
|
||||||
.as_str(),
|
.as_str(),
|
||||||
])?,
|
])?);
|
||||||
Tag::parse([
|
}
|
||||||
"image",
|
UserStreamState::Ended => {
|
||||||
self.map_to_public_url(pipeline_dir.join("thumb.webp").to_str().unwrap())?
|
if let Some(ep) = stream.endpoint_id {
|
||||||
|
let endpoint = self.db.get_ingest_endpoint(ep).await?;
|
||||||
|
let caps = parse_capabilities(&endpoint.capabilities);
|
||||||
|
let has_recording = caps
|
||||||
|
.iter()
|
||||||
|
.any(|c| matches!(c, EndpointCapability::DVR { .. }));
|
||||||
|
if has_recording {
|
||||||
|
extra_tags.push(Tag::parse([
|
||||||
|
"recording",
|
||||||
|
self.map_to_public_url(
|
||||||
|
pipeline_dir
|
||||||
|
.join(RecorderEgress::FILENAME)
|
||||||
|
.to_str()
|
||||||
|
.unwrap(),
|
||||||
|
)?
|
||||||
.as_str(),
|
.as_str(),
|
||||||
])?,
|
])?);
|
||||||
Tag::parse(["service", self.map_to_public_url("api/v1")?.as_str()])?,
|
}
|
||||||
];
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
let ev = self
|
let ev = self
|
||||||
.stream_to_event_builder(stream)?
|
.stream_to_event_builder(stream)?
|
||||||
.tags(extra_tags)
|
.tags(extra_tags)
|
||||||
@ -357,7 +384,7 @@ impl Overseer for ZapStreamOverseer {
|
|||||||
// Get ingest endpoint configuration based on connection type
|
// Get ingest endpoint configuration based on connection type
|
||||||
let endpoint = self.detect_endpoint(&connection).await?;
|
let endpoint = self.detect_endpoint(&connection).await?;
|
||||||
|
|
||||||
let caps = parse_capabilities(&endpoint.capabilities.unwrap_or("".to_string()));
|
let caps = parse_capabilities(&endpoint.capabilities);
|
||||||
let cfg = get_variants_from_endpoint(&stream_info, &caps)?;
|
let cfg = get_variants_from_endpoint(&stream_info, &caps)?;
|
||||||
|
|
||||||
if cfg.video_src.is_none() || cfg.variants.is_empty() {
|
if cfg.video_src.is_none() || cfg.variants.is_empty() {
|
||||||
@ -451,11 +478,8 @@ impl Overseer for ZapStreamOverseer {
|
|||||||
|
|
||||||
// Get the cost per minute from the ingest endpoint, or use default
|
// Get the cost per minute from the ingest endpoint, or use default
|
||||||
let cost_per_minute = if let Some(endpoint_id) = stream.endpoint_id {
|
let cost_per_minute = if let Some(endpoint_id) = stream.endpoint_id {
|
||||||
if let Some(endpoint) = self.db.get_ingest_endpoint(endpoint_id).await? {
|
let ep = self.db.get_ingest_endpoint(endpoint_id).await?;
|
||||||
endpoint.cost
|
ep.cost
|
||||||
} else {
|
|
||||||
bail!("Endpoint doesnt exist");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
bail!("Endpoint id not set on stream");
|
bail!("Endpoint id not set on stream");
|
||||||
};
|
};
|
||||||
@ -586,7 +610,8 @@ enum EndpointCapability {
|
|||||||
DVR { height: u16 },
|
DVR { height: u16 },
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_capabilities(cap: &str) -> Vec<EndpointCapability> {
|
fn parse_capabilities(cap: &Option<String>) -> Vec<EndpointCapability> {
|
||||||
|
if let Some(cap) = cap {
|
||||||
cap.to_ascii_lowercase()
|
cap.to_ascii_lowercase()
|
||||||
.split(',')
|
.split(',')
|
||||||
.map_while(|c| {
|
.map_while(|c| {
|
||||||
@ -616,6 +641,9 @@ fn parse_capabilities(cap: &str) -> Vec<EndpointCapability> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_variants_from_endpoint<'a>(
|
fn get_variants_from_endpoint<'a>(
|
||||||
|
Reference in New Issue
Block a user