rvoip-rtp-core 0.2.3

RTP/RTCP protocol implementation for the rvoip stack
Documentation
//! Media mixing functionality
//!
//! This module handles media mixing for conference scenarios.

use std::sync::Arc;
use bytes::Bytes;
use tokio::sync::RwLock;

use crate::api::common::frame::MediaFrame;
use crate::api::common::error::MediaTransportError;
use crate::api::common::frame::MediaFrameType;
use crate::packet::RtpPacket;

/// Mix multiple audio frames into a single output frame
///
/// This is a simple implementation that performs basic mixing by:
/// 1. Converting all input frames to the same format (if needed)
/// 2. Normalizing audio levels
/// 3. Mixing the samples
/// 4. Creating a new output frame with mixed data
///
/// For more advanced mixing with proper level adjustment, silence detection,
/// and format conversion, a more sophisticated mixer should be used.
pub async fn mix_audio_frames(
    frames: Vec<MediaFrame>,
    output_payload_type: u8,
    output_timestamp: u32,
    output_ssrc: u32,
) -> Result<MediaFrame, MediaTransportError> {
    // Check if we have any frames to mix
    if frames.is_empty() {
        return Err(MediaTransportError::InvalidInput("No frames to mix".to_string()));
    }

    // For simplicity, just use the first frame's timestamp, sequence, and data
    // In a real implementation, this would properly mix audio samples
    let first_frame = &frames[0];

    // Create a new frame with mixed data (in this simplified implementation, just use the first frame)
    let mixed_frame = MediaFrame {
        frame_type: MediaFrameType::Audio,
        data: first_frame.data.clone(),
        timestamp: output_timestamp,
        sequence: first_frame.sequence, // In a real implementation, this would be generated
        marker: first_frame.marker,
        payload_type: output_payload_type,
        ssrc: output_ssrc,
        csrcs: frames.iter().map(|f| f.ssrc).collect(), // Use source SSRCs as CSRCs
    };

    Ok(mixed_frame)
}

/// Mix audio frames from multiple sources based on active speakers
///
/// This is useful for conference scenarios where we want to mix
/// the N loudest speakers into a single output stream.
pub async fn mix_active_speakers(
    frames: Vec<(String, MediaFrame)>,
    max_speakers: usize,
    output_payload_type: u8,
    output_timestamp: u32,
    output_ssrc: u32,
) -> Result<MediaFrame, MediaTransportError> {
    // Check if we have any frames to mix
    if frames.is_empty() {
        return Err(MediaTransportError::InvalidInput("No frames to mix".to_string()));
    }

    // Filter for audio frames only
    let audio_frames = frames
        .into_iter()
        .filter(|(_, frame)| frame.frame_type == MediaFrameType::Audio)
        .collect::<Vec<_>>();

    if audio_frames.is_empty() {
        return Err(MediaTransportError::InvalidInput("No audio frames to mix".to_string()));
    }

    // In a real implementation, we would:
    // 1. Calculate audio levels for each frame
    // 2. Sort by level (loudness)
    // 3. Take the top N frames
    // 4. Mix those frames

    // For simplicity, just take up to max_speakers frames
    let frames_to_mix = audio_frames
        .into_iter()
        .take(max_speakers)
        .map(|(_, frame)| frame)
        .collect::<Vec<_>>();

    // Mix the selected frames
    mix_audio_frames(frames_to_mix, output_payload_type, output_timestamp, output_ssrc).await
}

/// Calculate audio level for a frame (simple implementation)
///
/// Returns a value between 0 (silence) and 127 (loudest)
pub fn calculate_audio_level(frame: &MediaFrame) -> u8 {
    // In a real implementation, we would:
    // 1. Decode the audio samples
    // 2. Calculate RMS power
    // 3. Convert to dB
    // 4. Map to 0-127 range

    // For this simple implementation, we just use the average byte value
    // which is not accurate but serves as a placeholder
    if frame.data.is_empty() {
        return 0;
    }

    let sum: u32 = frame.data.iter().map(|&b| b as u32).sum();
    let avg = sum / frame.data.len() as u32;

    // Map average to 0-127 range
    let level = (avg * 127 / 255) as u8;

    level
}

/// Create a voice activity detection result
///
/// Returns (voice_active, level) where:
/// - voice_active is true if voice is detected, false otherwise
/// - level is the audio level (0-127, where 0 is quietest, 127 is loudest)
pub fn detect_voice_activity(frame: &MediaFrame, threshold: u8) -> (bool, u8) {
    // Calculate audio level
    let level = calculate_audio_level(frame);

    // Determine if voice is active (level above threshold)
    let voice_active = level > threshold;

    (voice_active, level)
}