use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::debug;
use crate::api::common::error::MediaTransportError;
use crate::api::common::extension::ExtensionFormat;
use crate::api::server::transport::HeaderExtension;
pub async fn is_header_extensions_enabled(
header_extensions_enabled: &Arc<RwLock<bool>>,
) -> Result<bool, MediaTransportError> {
Ok(*header_extensions_enabled.read().await)
}
pub async fn enable_header_extensions(
header_extensions_enabled: &Arc<RwLock<bool>>,
header_extension_format: &Arc<RwLock<ExtensionFormat>>,
format: ExtensionFormat,
) -> Result<bool, MediaTransportError> {
if *header_extensions_enabled.read().await {
return Ok(true);
}
*header_extension_format.write().await = format;
*header_extensions_enabled.write().await = true;
debug!("Enabled header extensions with format: {:?}", format);
Ok(true)
}
pub async fn configure_header_extension(
header_extensions_enabled: &Arc<RwLock<bool>>,
header_extension_format: &Arc<RwLock<ExtensionFormat>>,
header_extension_mappings: &Arc<RwLock<HashMap<u8, String>>>,
id: u8,
uri: String,
) -> Result<(), MediaTransportError> {
if !*header_extensions_enabled.read().await {
return Err(MediaTransportError::ConfigError("Header extensions are not enabled".to_string()));
}
let format = *header_extension_format.read().await;
match format {
ExtensionFormat::OneByte => {
if id < 1 || id > 14 {
return Err(MediaTransportError::ConfigError(
format!("Invalid extension ID for one-byte format: {} (must be 1-14)", id)
));
}
},
ExtensionFormat::TwoByte => {
if id < 1 || id > 255 {
return Err(MediaTransportError::ConfigError(
format!("Invalid extension ID for two-byte format: {} (must be 1-255)", id)
));
}
},
ExtensionFormat::Legacy => {
},
}
let mut mappings = header_extension_mappings.write().await;
mappings.insert(id, uri.clone());
debug!("Configured header extension ID {} -> URI: {}", id, uri);
Ok(())
}
pub async fn configure_header_extensions(
header_extensions_enabled: &Arc<RwLock<bool>>,
header_extension_format: &Arc<RwLock<ExtensionFormat>>,
header_extension_mappings: &Arc<RwLock<HashMap<u8, String>>>,
mappings: HashMap<u8, String>,
) -> Result<(), MediaTransportError> {
for (id, uri) in mappings {
configure_header_extension(
header_extensions_enabled,
header_extension_format,
header_extension_mappings,
id,
uri
).await?;
}
Ok(())
}
pub async fn add_header_extension_for_client(
header_extensions_enabled: &Arc<RwLock<bool>>,
clients: &Arc<RwLock<HashMap<String, crate::api::server::transport::core::connection::ClientConnection>>>,
pending_extensions: &Arc<RwLock<HashMap<String, Vec<HeaderExtension>>>>,
client_id: &str,
extension: HeaderExtension,
) -> Result<(), MediaTransportError> {
if !*header_extensions_enabled.read().await {
return Err(MediaTransportError::ConfigError("Header extensions are not enabled".to_string()));
}
let clients_guard = clients.read().await;
if !clients_guard.contains_key(client_id) {
return Err(MediaTransportError::ClientNotFound(client_id.to_string()));
}
drop(clients_guard);
let mut pending = pending_extensions.write().await;
let client_pending = pending.entry(client_id.to_string()).or_insert_with(Vec::new);
client_pending.push(extension);
Ok(())
}
pub async fn add_header_extension_for_all_clients(
header_extensions_enabled: &Arc<RwLock<bool>>,
clients: &Arc<RwLock<HashMap<String, crate::api::server::transport::core::connection::ClientConnection>>>,
pending_extensions: &Arc<RwLock<HashMap<String, Vec<HeaderExtension>>>>,
extension: HeaderExtension,
) -> Result<(), MediaTransportError> {
if !*header_extensions_enabled.read().await {
return Err(MediaTransportError::ConfigError("Header extensions are not enabled".to_string()));
}
let clients_guard = clients.read().await;
let client_ids: Vec<String> = clients_guard.keys().cloned().collect();
drop(clients_guard);
for client_id in client_ids {
add_header_extension_for_client(
header_extensions_enabled,
clients,
pending_extensions,
&client_id,
extension.clone()
).await?;
}
Ok(())
}
pub async fn add_audio_level_extension_for_client(
header_extensions_enabled: &Arc<RwLock<bool>>,
header_extension_mappings: &Arc<RwLock<HashMap<u8, String>>>,
clients: &Arc<RwLock<HashMap<String, crate::api::server::transport::core::connection::ClientConnection>>>,
pending_extensions: &Arc<RwLock<HashMap<String, Vec<HeaderExtension>>>>,
client_id: &str,
voice_activity: bool,
level: u8,
) -> Result<(), MediaTransportError> {
if !*header_extensions_enabled.read().await {
return Err(MediaTransportError::ConfigError("Header extensions are not enabled".to_string()));
}
let level = level.min(127);
let data = if voice_activity {
vec![level]
} else {
vec![level | 0x80]
};
let audio_level_id = {
let mappings = header_extension_mappings.read().await;
let mut id = None;
for (mapping_id, uri) in mappings.iter() {
if uri == "urn:ietf:params:rtp-hdrext:ssrc-audio-level" {
id = Some(*mapping_id);
break;
}
}
id.unwrap_or(1)
};
let extension = HeaderExtension {
id: audio_level_id,
uri: "urn:ietf:params:rtp-hdrext:ssrc-audio-level".to_string(),
data,
};
add_header_extension_for_client(
header_extensions_enabled,
clients,
pending_extensions,
client_id,
extension
).await
}
pub async fn add_audio_level_extension_for_all_clients(
header_extensions_enabled: &Arc<RwLock<bool>>,
header_extension_mappings: &Arc<RwLock<HashMap<u8, String>>>,
clients: &Arc<RwLock<HashMap<String, crate::api::server::transport::core::connection::ClientConnection>>>,
pending_extensions: &Arc<RwLock<HashMap<String, Vec<HeaderExtension>>>>,
voice_activity: bool,
level: u8,
) -> Result<(), MediaTransportError> {
if !*header_extensions_enabled.read().await {
return Err(MediaTransportError::ConfigError("Header extensions are not enabled".to_string()));
}
let clients_guard = clients.read().await;
let client_ids: Vec<String> = clients_guard.keys().cloned().collect();
drop(clients_guard);
for client_id in client_ids {
add_audio_level_extension_for_client(
header_extensions_enabled,
header_extension_mappings,
clients,
pending_extensions,
&client_id,
voice_activity,
level
).await?;
}
Ok(())
}
pub async fn get_received_header_extensions(
header_extensions_enabled: &Arc<RwLock<bool>>,
received_extensions: &Arc<RwLock<HashMap<String, Vec<HeaderExtension>>>>,
client_id: &str,
) -> Result<Vec<HeaderExtension>, MediaTransportError> {
if !*header_extensions_enabled.read().await {
return Err(MediaTransportError::ConfigError("Header extensions are not enabled".to_string()));
}
let extensions = received_extensions.read().await;
let client_extensions = extensions.get(client_id).cloned().unwrap_or_default();
Ok(client_extensions)
}
pub async fn get_received_audio_level(
header_extensions_enabled: &Arc<RwLock<bool>>,
header_extension_mappings: &Arc<RwLock<HashMap<u8, String>>>,
received_extensions: &Arc<RwLock<HashMap<String, Vec<HeaderExtension>>>>,
client_id: &str,
) -> Result<Option<(bool, u8)>, MediaTransportError> {
if !*header_extensions_enabled.read().await {
return Err(MediaTransportError::ConfigError("Header extensions are not enabled".to_string()));
}
let client_extensions = get_received_header_extensions(
header_extensions_enabled,
received_extensions,
client_id
).await?;
for ext in client_extensions {
let is_audio_level = {
let mappings = header_extension_mappings.read().await;
mappings.get(&ext.id).map(|uri| uri == "urn:ietf:params:rtp-hdrext:ssrc-audio-level").unwrap_or(ext.id == 1)
};
if is_audio_level && !ext.data.is_empty() {
let byte = ext.data[0];
let voice_activity = (byte & 0x80) == 0;
let level = byte & 0x7F;
return Ok(Some((voice_activity, level)));
}
}
Ok(None)
}
pub async fn clear_pending_header_extensions(
pending_extensions: &Arc<RwLock<HashMap<String, Vec<HeaderExtension>>>>,
) -> Result<(), MediaTransportError> {
let mut pending = pending_extensions.write().await;
pending.clear();
debug!("Cleared all pending header extensions");
Ok(())
}