mirror of
https://github.com/v0l/zap-stream-core.git
synced 2025-06-20 05:30:29 +00:00
fix: dont upscale for variants
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@ -357,7 +357,8 @@ 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 cfg = get_variants_from_endpoint(&stream_info, &endpoint)?;
|
let caps = parse_capabilities(&endpoint.capabilities.unwrap_or("".to_string()));
|
||||||
|
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() {
|
||||||
bail!("No video src found");
|
bail!("No video src found");
|
||||||
@ -368,6 +369,27 @@ impl Overseer for ZapStreamOverseer {
|
|||||||
name: "hls".to_string(),
|
name: "hls".to_string(),
|
||||||
variants: cfg.variants.iter().map(|v| v.id()).collect(),
|
variants: cfg.variants.iter().map(|v| v.id()).collect(),
|
||||||
}));
|
}));
|
||||||
|
if let Some(EndpointCapability::DVR { height }) = caps
|
||||||
|
.iter()
|
||||||
|
.find(|c| matches!(c, EndpointCapability::DVR { .. }))
|
||||||
|
{
|
||||||
|
let var = cfg.variants.iter().find(|v| match v {
|
||||||
|
VariantStream::Video(v) => v.height == *height,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
match var {
|
||||||
|
Some(var) => egress.push(EgressType::Recorder(EgressConfig {
|
||||||
|
name: "dvr".to_string(),
|
||||||
|
variants: [var.id()].into(),
|
||||||
|
})),
|
||||||
|
None => {
|
||||||
|
warn!(
|
||||||
|
"Invalid DVR config, no variant found with height {}",
|
||||||
|
height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let stream_id = connection.id.clone();
|
let stream_id = connection.id.clone();
|
||||||
// insert new stream record
|
// insert new stream record
|
||||||
@ -551,13 +573,48 @@ struct EndpointConfig<'a> {
|
|||||||
variants: Vec<VariantStream>,
|
variants: Vec<VariantStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum EndpointCapability {
|
||||||
|
SourceVariant,
|
||||||
|
Variant { height: u16, bitrate: u64 },
|
||||||
|
DVR { height: u16 },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_capabilities(cap: &str) -> Vec<EndpointCapability> {
|
||||||
|
cap.to_ascii_lowercase()
|
||||||
|
.split(',')
|
||||||
|
.map_while(|c| {
|
||||||
|
let cs = c.split(':').collect::<Vec<&str>>();
|
||||||
|
match cs[0] {
|
||||||
|
"variant" if cs[1] == "source" => Some(EndpointCapability::SourceVariant),
|
||||||
|
"variant" if cs.len() == 3 => {
|
||||||
|
if let (Ok(h), Ok(br)) = (cs[1].parse(), cs[2].parse()) {
|
||||||
|
Some(EndpointCapability::Variant {
|
||||||
|
height: h,
|
||||||
|
bitrate: br,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
warn!("Invalid variant: {}", c);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"dvr" if cs.len() == 2 => {
|
||||||
|
if let Ok(h) = cs[1].parse() {
|
||||||
|
Some(EndpointCapability::DVR { height: h })
|
||||||
|
} else {
|
||||||
|
warn!("Invalid dvr: {}", c);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_variants_from_endpoint<'a>(
|
fn get_variants_from_endpoint<'a>(
|
||||||
info: &'a IngressInfo,
|
info: &'a IngressInfo,
|
||||||
endpoint: &IngestEndpoint,
|
capabilities: &Vec<EndpointCapability>,
|
||||||
) -> Result<EndpointConfig<'a>> {
|
) -> Result<EndpointConfig<'a>> {
|
||||||
let capabilities_str = endpoint.capabilities.as_deref().unwrap_or("");
|
|
||||||
let capabilities: Vec<&str> = capabilities_str.split(',').collect();
|
|
||||||
|
|
||||||
let mut vars: Vec<VariantStream> = vec![];
|
let mut vars: Vec<VariantStream> = vec![];
|
||||||
|
|
||||||
let video_src = info
|
let video_src = info
|
||||||
@ -574,43 +631,48 @@ fn get_variants_from_endpoint<'a>(
|
|||||||
let mut dst_index = 0;
|
let mut dst_index = 0;
|
||||||
|
|
||||||
for capability in capabilities {
|
for capability in capabilities {
|
||||||
let parts: Vec<&str> = capability.split(':').collect();
|
match capability {
|
||||||
|
EndpointCapability::SourceVariant => {
|
||||||
|
// Add copy variant (group for source)
|
||||||
|
if let Some(video_src) = video_src {
|
||||||
|
vars.push(VariantStream::CopyVideo(VariantMapping {
|
||||||
|
id: Uuid::new_v4(),
|
||||||
|
src_index: video_src.index,
|
||||||
|
dst_index,
|
||||||
|
group_id,
|
||||||
|
}));
|
||||||
|
dst_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
if parts.len() >= 2 && parts[0] == "variant" && parts[1] == "source" {
|
if let Some(audio_src) = audio_src {
|
||||||
// Add copy variant (group for source)
|
vars.push(VariantStream::CopyAudio(VariantMapping {
|
||||||
if let Some(video_src) = video_src {
|
id: Uuid::new_v4(),
|
||||||
vars.push(VariantStream::CopyVideo(VariantMapping {
|
src_index: audio_src.index,
|
||||||
id: Uuid::new_v4(),
|
dst_index,
|
||||||
src_index: video_src.index,
|
group_id,
|
||||||
dst_index,
|
}));
|
||||||
group_id,
|
dst_index += 1;
|
||||||
}));
|
}
|
||||||
dst_index += 1;
|
|
||||||
|
group_id += 1;
|
||||||
}
|
}
|
||||||
|
EndpointCapability::Variant { height, bitrate } => {
|
||||||
if let Some(audio_src) = audio_src {
|
|
||||||
vars.push(VariantStream::CopyAudio(VariantMapping {
|
|
||||||
id: Uuid::new_v4(),
|
|
||||||
src_index: audio_src.index,
|
|
||||||
dst_index,
|
|
||||||
group_id,
|
|
||||||
}));
|
|
||||||
dst_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
group_id += 1;
|
|
||||||
} else if parts.len() >= 3 && parts[0] == "variant" {
|
|
||||||
if let (Ok(target_height), Ok(bitrate)) =
|
|
||||||
(parts[1].parse::<u32>(), parts[2].parse::<u32>())
|
|
||||||
{
|
|
||||||
// Add video variant for this group
|
// Add video variant for this group
|
||||||
if let Some(video_src) = video_src {
|
if let Some(video_src) = video_src {
|
||||||
|
let output_height = *height;
|
||||||
|
if video_src.height < output_height as _ {
|
||||||
|
info!(
|
||||||
|
"Skipping variant {}p, source would be upscaled from {}p",
|
||||||
|
height, video_src.height
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate dimensions maintaining aspect ratio
|
// Calculate dimensions maintaining aspect ratio
|
||||||
let input_width = video_src.width as f32;
|
let input_width = video_src.width as f32;
|
||||||
let input_height = video_src.height as f32;
|
let input_height = video_src.height as f32;
|
||||||
let aspect_ratio = input_width / input_height;
|
let aspect_ratio = input_width / input_height;
|
||||||
|
|
||||||
let output_height = target_height;
|
|
||||||
let output_width = (output_height as f32 * aspect_ratio).round() as u16;
|
let output_width = (output_height as f32 * aspect_ratio).round() as u16;
|
||||||
|
|
||||||
// Ensure even dimensions for H.264 compatibility
|
// Ensure even dimensions for H.264 compatibility
|
||||||
@ -623,7 +685,7 @@ fn get_variants_from_endpoint<'a>(
|
|||||||
output_height + 1
|
output_height + 1
|
||||||
} else {
|
} else {
|
||||||
output_height
|
output_height
|
||||||
} as u16;
|
};
|
||||||
|
|
||||||
vars.push(VariantStream::Video(VideoVariant {
|
vars.push(VariantStream::Video(VideoVariant {
|
||||||
mapping: VariantMapping {
|
mapping: VariantMapping {
|
||||||
@ -633,9 +695,9 @@ fn get_variants_from_endpoint<'a>(
|
|||||||
group_id,
|
group_id,
|
||||||
},
|
},
|
||||||
width: output_width,
|
width: output_width,
|
||||||
height: output_height,
|
height: output_height as _,
|
||||||
fps: video_src.fps,
|
fps: video_src.fps,
|
||||||
bitrate: bitrate as u64,
|
bitrate: *bitrate as _,
|
||||||
codec: "libx264".to_string(),
|
codec: "libx264".to_string(),
|
||||||
profile: 77, // AV_PROFILE_H264_MAIN
|
profile: 77, // AV_PROFILE_H264_MAIN
|
||||||
level: 51, // High 5.1 (4K)
|
level: 51, // High 5.1 (4K)
|
||||||
@ -643,30 +705,30 @@ fn get_variants_from_endpoint<'a>(
|
|||||||
pixel_format: AV_PIX_FMT_YUV420P as u32,
|
pixel_format: AV_PIX_FMT_YUV420P as u32,
|
||||||
}));
|
}));
|
||||||
dst_index += 1;
|
dst_index += 1;
|
||||||
}
|
|
||||||
|
|
||||||
// Add audio variant for the same group
|
// Add audio variant for the same group
|
||||||
if let Some(audio_src) = audio_src {
|
if let Some(audio_src) = audio_src {
|
||||||
vars.push(VariantStream::Audio(AudioVariant {
|
vars.push(VariantStream::Audio(AudioVariant {
|
||||||
mapping: VariantMapping {
|
mapping: VariantMapping {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
src_index: audio_src.index,
|
src_index: audio_src.index,
|
||||||
dst_index,
|
dst_index,
|
||||||
group_id,
|
group_id,
|
||||||
},
|
},
|
||||||
bitrate: 192_000,
|
bitrate: 192_000,
|
||||||
codec: "aac".to_string(),
|
codec: "aac".to_string(),
|
||||||
channels: 2,
|
channels: 2,
|
||||||
sample_rate: 48_000,
|
sample_rate: 48_000,
|
||||||
sample_fmt: "fltp".to_owned(),
|
sample_fmt: "fltp".to_owned(),
|
||||||
}));
|
}));
|
||||||
dst_index += 1;
|
dst_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
group_id += 1;
|
group_id += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
// Handle other capabilities like dvr:720h here if needed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EndpointConfig {
|
Ok(EndpointConfig {
|
||||||
|
Reference in New Issue
Block a user