//! Voicemail session — answer → play greeting → beep → record → done. use crate::audio_player::{play_beep, play_wav_file}; use crate::ipc::{emit_event, OutTx}; use crate::recorder::Recorder; use std::net::SocketAddr; use std::sync::Arc; use tokio::net::UdpSocket; /// Run a voicemail session on an RTP port. /// /// 1. Plays the greeting WAV file to the caller /// 2. Plays a beep tone /// 3. Records the caller's message until BYE or max duration /// /// The RTP receive loop is separate — it feeds packets to the recorder /// via the returned channel. pub async fn run_voicemail_session( rtp_socket: Arc, provider_media: SocketAddr, codec_pt: u8, greeting_wav: Option, recording_path: String, max_recording_ms: u64, call_id: String, caller_number: String, out_tx: OutTx, ) { let ssrc: u32 = rand::random(); emit_event( &out_tx, "voicemail_started", serde_json::json!({ "call_id": call_id, "caller_number": caller_number, }), ); // Step 1: Play greeting. let mut next_seq: u16 = 0; let mut next_ts: u32 = 0; if let Some(wav_path) = &greeting_wav { match play_wav_file(wav_path, rtp_socket.clone(), provider_media, codec_pt, ssrc).await { Ok(frames) => { next_seq = frames as u16; next_ts = frames * crate::rtp::rtp_clock_increment(codec_pt); } Err(e) => { emit_event( &out_tx, "voicemail_error", serde_json::json!({ "call_id": call_id, "error": format!("greeting: {e}") }), ); } } } // Step 2: Play beep (1kHz, 500ms). match play_beep( rtp_socket.clone(), provider_media, codec_pt, ssrc, next_seq, next_ts, 1000, 500, ) .await { Ok((_seq, _ts)) => {} Err(e) => { emit_event( &out_tx, "voicemail_error", serde_json::json!({ "call_id": call_id, "error": format!("beep: {e}") }), ); } } // Step 3: Record incoming audio. let recorder = match Recorder::new(&recording_path, codec_pt, Some(max_recording_ms)) { Ok(r) => r, Err(e) => { emit_event( &out_tx, "voicemail_error", serde_json::json!({ "call_id": call_id, "error": format!("recorder: {e}") }), ); return; } }; // Receive RTP and feed to recorder. let result = record_from_socket(rtp_socket, recorder, max_recording_ms).await; // Step 4: Done — emit recording result. emit_event( &out_tx, "recording_done", serde_json::json!({ "call_id": call_id, "file_path": result.file_path, "duration_ms": result.duration_ms, "caller_number": caller_number, }), ); } /// Read RTP packets from the socket and feed them to the recorder. /// Returns when the socket errors out (BYE closes the call/socket) /// or max duration is reached. async fn record_from_socket( socket: Arc, mut recorder: Recorder, max_ms: u64, ) -> crate::recorder::RecordingResult { let mut buf = vec![0u8; 65535]; let deadline = tokio::time::Instant::now() + tokio::time::Duration::from_millis(max_ms + 2000); loop { let timeout = tokio::time::timeout_at(deadline, socket.recv_from(&mut buf)); match timeout.await { Ok(Ok((n, _addr))) => { if !recorder.process_rtp(&buf[..n]) { break; // Max duration reached. } } Ok(Err(_)) => break, // Socket error (closed). Err(_) => break, // Timeout (max duration + grace). } } recorder.stop() }