pub struct ClientCallHandler {
pub client_event_handler: Arc<RwLock<Option<Arc<dyn ClientEventHandler>>>>,
pub call_mapping: Arc<DashMap<SessionId, CallId>>,
pub session_mapping: Arc<DashMap<CallId, SessionId>>,
pub call_info: Arc<DashMap<CallId, CallInfo>>,
pub incoming_calls: Arc<DashMap<CallId, IncomingCall>>,
pub event_tx: Option<Sender<ClientEvent>>,
/* private fields */
}Expand description
Internal call handler that bridges session-core events to client-core events
This handler receives events from the session-core layer and translates them into client-core events that applications can consume. It manages mappings between session IDs and call IDs, tracks call state, and forwards events to registered event handlers.
§Architecture
The handler maintains several mappings:
- Session ID ↔ Call ID mapping for event translation
- Call information storage with extended metadata
- Incoming call storage for deferred acceptance/rejection
- Event broadcasting through multiple channels
§Examples
use rvoip_client_core::client::events::ClientCallHandler;
use rvoip_session_core::CallHandler;
use std::sync::Arc;
use dashmap::DashMap;
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()), // call_mapping
Arc::new(DashMap::new()), // session_mapping
Arc::new(DashMap::new()), // call_info
Arc::new(DashMap::new()), // incoming_calls
);Fields§
§client_event_handler: Arc<RwLock<Option<Arc<dyn ClientEventHandler>>>>Client event handler for forwarding processed events to applications
This optional handler receives high-level client events after they have been processed and enriched by this bridge. Applications can register handlers to receive notifications about incoming calls, state changes, etc.
call_mapping: Arc<DashMap<SessionId, CallId>>Mapping from session-core SessionId to client-core CallId
This bidirectional mapping allows the handler to translate between session-core’s internal session identifiers and the client-facing call IDs that applications use to reference calls.
session_mapping: Arc<DashMap<CallId, SessionId>>Reverse mapping from client-core CallId to session-core SessionId
Provides efficient lookup in the opposite direction from call_mapping, allowing quick translation from client call IDs to session IDs when making session-core API calls.
call_info: Arc<DashMap<CallId, CallInfo>>Enhanced call information storage with extended metadata
Stores comprehensive call information including state, timing data, participant details, and custom metadata. This information persists throughout the call lifecycle and can be used for history and reporting.
incoming_calls: Arc<DashMap<CallId, IncomingCall>>Storage for incoming calls awaiting acceptance or rejection
When incoming calls arrive, they are stored here until the application decides whether to accept or reject them. This allows for deferred call handling and provides access to full call details for decision making.
event_tx: Option<Sender<ClientEvent>>Optional broadcast channel for real-time event streaming
If configured, events are broadcast through this channel in addition to being sent to the registered event handler. This allows multiple consumers to receive events independently.
Implementations§
Source§impl ClientCallHandler
impl ClientCallHandler
Sourcepub fn new(
call_mapping: Arc<DashMap<SessionId, CallId>>,
session_mapping: Arc<DashMap<CallId, SessionId>>,
call_info: Arc<DashMap<CallId, CallInfo>>,
incoming_calls: Arc<DashMap<CallId, IncomingCall>>,
) -> Self
pub fn new( call_mapping: Arc<DashMap<SessionId, CallId>>, session_mapping: Arc<DashMap<CallId, SessionId>>, call_info: Arc<DashMap<CallId, CallInfo>>, incoming_calls: Arc<DashMap<CallId, IncomingCall>>, ) -> Self
Create a new ClientCallHandler with required mappings and storage
This constructor initializes the handler with the necessary data structures for managing call state and event translation between session-core and client-core.
§Arguments
call_mapping- Bidirectional mapping between session IDs and call IDssession_mapping- Reverse mapping for efficient lookupscall_info- Storage for comprehensive call information and metadataincoming_calls- Storage for pending incoming calls
§Examples
use rvoip_client_core::client::events::ClientCallHandler;
use std::sync::Arc;
use dashmap::DashMap;
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);Sourcepub fn with_event_tx(self, event_tx: Sender<ClientEvent>) -> Self
pub fn with_event_tx(self, event_tx: Sender<ClientEvent>) -> Self
Configure the handler with an event broadcast channel
This method adds broadcast capability to the handler, allowing events to be sent to multiple consumers through a tokio broadcast channel. Events will be sent to both the registered event handler and the broadcast channel.
§Arguments
event_tx- Broadcast sender for streaming events to multiple consumers
§Examples
use rvoip_client_core::client::events::ClientCallHandler;
use std::sync::Arc;
use dashmap::DashMap;
let (tx, _rx) = tokio::sync::broadcast::channel(100);
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
).with_event_tx(tx);Sourcepub async fn set_event_handler(&self, handler: Arc<dyn ClientEventHandler>)
pub async fn set_event_handler(&self, handler: Arc<dyn ClientEventHandler>)
Register an event handler to receive processed client events
This method sets the application-level event handler that will receive high-level client events after they have been processed and enriched by this bridge. The handler will be called for incoming calls, state changes, media events, etc.
§Arguments
handler- The event handler implementation to register
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
let event_handler = Arc::new(MyEventHandler);
handler.set_event_handler(event_handler).await;Sourcepub async fn store_incoming_call(
&self,
call_id: CallId,
incoming_call: IncomingCall,
)
pub async fn store_incoming_call( &self, call_id: CallId, incoming_call: IncomingCall, )
Store an IncomingCall object for later use
This method stores an incoming call in the handler’s storage, allowing it to be retrieved later when the application decides to accept or reject the call. This enables deferred call handling where the application can examine call details before making a decision.
§Arguments
call_id- The client-core call ID for this incoming callincoming_call- The session-core IncomingCall object with full details
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
let call_id = CallId::new_v4();
handler.store_incoming_call(call_id, incoming_call).await;Sourcepub async fn get_incoming_call(&self, call_id: &CallId) -> Option<IncomingCall>
pub async fn get_incoming_call(&self, call_id: &CallId) -> Option<IncomingCall>
Retrieve a stored IncomingCall object
This method retrieves a previously stored incoming call by its call ID. This is useful when the application needs to access the full incoming call details (SDP, headers, etc.) when making accept/reject decisions or during call processing.
§Arguments
call_id- The client-core call ID to retrieve the incoming call for
§Returns
Some(IncomingCall) if a stored incoming call exists for the given call ID,
None if no incoming call is found or if the call has already been processed.
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
let call_id = CallId::new_v4();
// Retrieve incoming call details
if let Some(incoming_call) = handler.get_incoming_call(&call_id).await {
println!("Found incoming call from: {}", incoming_call.from);
println!("SDP offer present: {}", incoming_call.sdp.is_some());
} else {
println!("No incoming call found for ID: {}", call_id);
}Sourcepub fn extract_display_name(
&self,
uri: &str,
headers: &HashMap<String, String>,
) -> Option<String>
pub fn extract_display_name( &self, uri: &str, headers: &HashMap<String, String>, ) -> Option<String>
Extract display name from SIP URI or headers
This method attempts to extract a human-readable display name from a SIP URI or associated headers. It implements multiple extraction strategies to handle various SIP message formats and header configurations.
§Arguments
uri- The SIP URI to extract display name from (e.g., “Alice Smith” sip:alice@example.com)headers- SIP message headers that may contain display name information
§Returns
Some(String) containing the extracted display name if found, None if no
display name could be extracted from the URI or headers.
§Extraction Strategy
- Check for quoted display name in URI:
"Display Name" <sip:user@domain> - Check for unquoted display name before angle brackets:
Display Name <sip:user@domain> - Check From header for display name using the same strategies
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
let mut headers = HashMap::new();
headers.insert("From".to_string(), "\"Alice Smith\" <sip:alice@example.com>".to_string());
// Extract from quoted URI
let display_name = handler.extract_display_name(
"\"Alice Smith\" <sip:alice@example.com>",
&headers
);
assert_eq!(display_name, Some("Alice Smith".to_string()));
// Extract from unquoted URI
let display_name = handler.extract_display_name(
"Bob Jones <sip:bob@example.com>",
&HashMap::new()
);
assert_eq!(display_name, Some("Bob Jones".to_string()));
// No display name available
let display_name = handler.extract_display_name(
"sip:carol@example.com",
&HashMap::new()
);
assert_eq!(display_name, None);Sourcepub fn extract_display_name_from_header(&self, header: &str) -> Option<String>
pub fn extract_display_name_from_header(&self, header: &str) -> Option<String>
Extract display name from a SIP header string
This method extracts a human-readable display name from a SIP header value, typically the From or To header. It handles both quoted and unquoted display name formats commonly found in SIP messages.
§Arguments
header- The SIP header value to parse for display name information
§Returns
Some(String) containing the extracted display name if found, None if no
display name could be extracted from the header.
§Supported Formats
- Quoted display name:
"Alice Smith" <sip:alice@example.com> - Unquoted display name:
Alice Smith <sip:alice@example.com> - Plain SIP URI:
sip:alice@example.com(returns None)
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
// Extract from quoted header
let name = handler.extract_display_name_from_header(
"\"Alice Smith\" <sip:alice@example.com>"
);
assert_eq!(name, Some("Alice Smith".to_string()));
// Extract from unquoted header
let name = handler.extract_display_name_from_header(
"Bob Jones <sip:bob@example.com>"
);
assert_eq!(name, Some("Bob Jones".to_string()));
// No display name in plain URI
let name = handler.extract_display_name_from_header(
"sip:carol@example.com"
);
assert_eq!(name, None);Sourcepub fn extract_subject(
&self,
headers: &HashMap<String, String>,
) -> Option<String>
pub fn extract_subject( &self, headers: &HashMap<String, String>, ) -> Option<String>
Extract call subject from SIP message headers
This method extracts the subject/purpose of a call from SIP message headers. The subject provides contextual information about the call and is typically used for displaying call purpose in user interfaces or for call routing decisions.
§Arguments
headers- HashMap containing SIP message headers to search for subject information
§Returns
Some(String) containing the subject text if found and non-empty,
None if no subject header exists or if the subject is empty.
§Header Priority
The method searches for subject information in the following order:
- “Subject” header (standard case)
- “subject” header (lowercase variant)
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
let mut headers = HashMap::new();
headers.insert("Subject".to_string(), "Conference Call".to_string());
// Extract subject from headers
let subject = handler.extract_subject(&headers);
assert_eq!(subject, Some("Conference Call".to_string()));
// Empty subject returns None
let mut empty_headers = HashMap::new();
empty_headers.insert("Subject".to_string(), "".to_string());
let subject = handler.extract_subject(&empty_headers);
assert_eq!(subject, None);
// No subject header returns None
let subject = handler.extract_subject(&HashMap::new());
assert_eq!(subject, None);Sourcepub fn extract_call_id(
&self,
headers: &HashMap<String, String>,
) -> Option<String>
pub fn extract_call_id( &self, headers: &HashMap<String, String>, ) -> Option<String>
Extract SIP Call-ID from message headers
This method extracts the unique Call-ID identifier from SIP message headers. The Call-ID is a mandatory header in SIP messages that uniquely identifies a call dialog and remains constant throughout the entire call session.
§Arguments
headers- HashMap containing SIP message headers to search for Call-ID
§Returns
Some(String) containing the Call-ID value if found,
None if no Call-ID header exists in the message.
§Header Priority
The method searches for Call-ID information in the following order:
- “Call-ID” header (standard case)
- “call-id” header (lowercase variant)
§SIP Specification
According to RFC 3261, the Call-ID header is mandatory in all SIP requests and responses. It consists of a locally unique identifier followed by an “@” sign and a globally unique identifier (usually the host domain).
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
let mut headers = HashMap::new();
headers.insert("Call-ID".to_string(), "1234567890@example.com".to_string());
// Extract Call-ID from headers
let call_id = handler.extract_call_id(&headers);
assert_eq!(call_id, Some("1234567890@example.com".to_string()));
// Case-insensitive header lookup
let mut headers_lower = HashMap::new();
headers_lower.insert("call-id".to_string(), "abcdef@sip.example.org".to_string());
let call_id = handler.extract_call_id(&headers_lower);
assert_eq!(call_id, Some("abcdef@sip.example.org".to_string()));
// No Call-ID header returns None
let call_id = handler.extract_call_id(&HashMap::new());
assert_eq!(call_id, None);Sourcepub async fn update_call_info_from_session(
&self,
call_id: CallId,
session: &CallSession,
)
pub async fn update_call_info_from_session( &self, call_id: CallId, session: &CallSession, )
Update stored call information with enhanced session data
This method synchronizes call information stored in the client-core layer with the current state from the session-core layer. It handles state transitions, timestamp updates, and event emission when significant changes occur.
§Arguments
call_id- The client-core call ID to update information forsession- The session-core CallSession object containing current state and metadata
§Behavior
The method performs the following operations:
- Maps session-core state to client-core state representation
- Updates timestamps for significant state transitions (connected, ended)
- Emits state change events to registered handlers when state changes
- Preserves historical information and call metadata
§State Transitions
Special handling is applied for specific state transitions:
- Connected: Sets
connected_attimestamp if not already set - Terminated/Failed/Cancelled: Sets
ended_attimestamp if not already set - Other states: Updates state without timestamp modifications
§Event Emission
When a state change is detected, the method automatically emits a CallStatusInfo
event to the registered client event handler, providing:
- Current and previous states
- Transition timestamp
- Call identification information
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
let call_id = CallId::new_v4();
// Update call info when session state changes
handler.update_call_info_from_session(call_id, &session).await;
// The method will:
// 1. Map CallState::Active to client-core Connected state
// 2. Set connected_at timestamp if transitioning to Connected
// 3. Emit state change event to registered handlers§Thread Safety
This method is async and thread-safe. It uses atomic operations on the underlying DashMap storage and properly handles concurrent access to call information.
Sourcepub fn map_session_state_to_client_state(
&self,
session_state: &CallState,
) -> CallState
pub fn map_session_state_to_client_state( &self, session_state: &CallState, ) -> CallState
Map session-core CallState to client-core CallState with enhanced logic
This method translates between the internal session-core call state representation and the client-facing call state representation. It provides a clean abstraction layer and applies enhanced logic for complex state mappings.
§Arguments
session_state- The session-core CallState to map to client-core representation
§Returns
The corresponding client-core CallState that represents the same logical state but with client-appropriate semantics and naming.
§State Mapping Logic
The mapping applies the following transformations:
| Session-Core State | Client-Core State | Notes |
|---|---|---|
Initiating | Initiating | Direct mapping |
Ringing | Ringing | Direct mapping |
Active | Connected | Semantic clarity for client |
OnHold | Connected | Still connected, just on hold |
Transferring | Proceeding | Transfer in progress |
Terminating | Terminating | Direct mapping |
Terminated | Terminated | Direct mapping |
Cancelled | Cancelled | Direct mapping |
Failed(reason) | Failed | Logs reason, maps to simple Failed |
§Enhanced Logic
- OnHold Handling: Calls on hold are still considered “Connected” from the client perspective, as the media session is established and can be resumed.
- Transfer Handling: Calls being transferred are mapped to “Proceeding” to indicate ongoing call setup activities.
- Failure Handling: Failed states with reasons are logged for debugging but simplified to a single Failed state for client consumption.
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
// Map active session to connected client state
let session_state = CallState::Active;
let client_state = handler.map_session_state_to_client_state(&session_state);
assert_eq!(client_state, rvoip_client_core::call::CallState::Connected);
// Map on-hold session to still connected client state
let session_state = CallState::OnHold;
let client_state = handler.map_session_state_to_client_state(&session_state);
assert_eq!(client_state, rvoip_client_core::call::CallState::Connected);
// Map failed session to failed client state (reason is logged)
let session_state = CallState::Failed("Network timeout".to_string());
let client_state = handler.map_session_state_to_client_state(&session_state);
assert_eq!(client_state, rvoip_client_core::call::CallState::Failed);§Logging
The method logs debug information for failed states to assist with troubleshooting while providing a clean interface to client applications.
Trait Implementations§
Source§impl CallHandler for ClientCallHandler
Implementation of session-core CallHandler trait for ClientCallHandler
impl CallHandler for ClientCallHandler
Implementation of session-core CallHandler trait for ClientCallHandler
This trait implementation bridges session-core events to client-core events, providing the core event translation and handling logic. The implementation receives low-level session events and transforms them into high-level client events that applications can easily consume.
§Event Flow
- Session-core events arrive through this trait implementation
- Event translation maps session concepts to client concepts
- State management updates call information and mappings
- Client events are emitted to registered handlers and broadcast channels
- Cleanup removes temporary state when calls complete
§Thread Safety
All methods in this implementation are async and thread-safe, using atomic operations and concurrent data structures for state management.
Source§fn on_incoming_call<'life0, 'async_trait>(
&'life0 self,
call: IncomingCall,
) -> Pin<Box<dyn Future<Output = CallDecision> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn on_incoming_call<'life0, 'async_trait>(
&'life0 self,
call: IncomingCall,
) -> Pin<Box<dyn Future<Output = CallDecision> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Handle incoming call from session-core layer
This method is called by session-core when a new incoming call arrives. It performs comprehensive call processing including ID mapping, metadata extraction, event emission, and decision routing to the application layer.
§Arguments
call- The IncomingCall object from session-core containing all call details
§Returns
CallDecision indicating how session-core should handle the call:
Accept(sdp)- Accept the call with optional SDP answerReject(reason)- Reject the call with a reason stringDefer- Defer the decision (used for ignore action)
§Processing Flow
- ID Mapping: Creates new client call ID and establishes bidirectional mapping
- Metadata Extraction: Extracts display names, subject, and SIP headers
- Call Info Creation: Creates comprehensive CallInfo with all available data
- Event Broadcasting: Emits incoming call event to broadcast channel if configured
- Handler Consultation: Forwards to application event handler for decision
- Decision Translation: Maps client decision back to session-core format
§SDP Handling
- If the incoming call contains an SDP offer and the application accepts, the method allows session-core to generate the SDP answer automatically
- If no SDP offer is present, the call is accepted without SDP negotiation
- Complex SDP scenarios are handled transparently by session-core
§State Management
- Call starts in
IncomingPendingstate - Full call information is stored for history and reporting
- Incoming call object is stored for later access during accept/reject operations
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
// This method is called automatically by session-core
let decision = handler.on_incoming_call(incoming_call).await;
// The method will:
// 1. Extract caller display name "Alice Smith"
// 2. Create call info with all metadata
// 3. Emit incoming call event to application
// 4. Return application's accept/reject decisionSource§fn on_call_ended<'life0, 'life1, 'async_trait>(
&'life0 self,
session: CallSession,
reason: &'life1 str,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn on_call_ended<'life0, 'life1, 'async_trait>(
&'life0 self,
session: CallSession,
reason: &'life1 str,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Handle call termination from session-core layer
This method is called by session-core when a call ends, regardless of the cause (user hangup, network failure, timeout, etc.). It performs cleanup operations, updates call statistics, and emits final state change events.
§Arguments
session- The CallSession object containing final call state and metadatareason- Human-readable string describing why the call ended
§Processing Flow
- Call Lookup: Maps session ID to client call ID
- Statistics Update: Handles connected call counter management
- State Finalization: Updates call info with final state and timestamp
- Metadata Preservation: Stores termination reason and final state
- Event Emission: Broadcasts call ended event to handlers
- Cleanup: Removes active mappings while preserving call history
§Critical Bug Fix
This method includes a critical fix for integer overflow in call statistics. Previously, calls ending through session-core (network timeouts, remote hangup) weren’t decrementing the connected_calls counter, leading to overflow issues.
The fix:
- Tracks whether the call was in Connected state before termination
- Adds metadata flag for the manager to decrement counters appropriately
- Prevents statistics corruption from external call termination
§State Management
- Final call state is mapped from session-core to client-core representation
ended_attimestamp is set to current time- Termination reason is stored in call metadata for history
- Call information is preserved for reporting and analytics
§Cleanup Operations
- Removes active session-to-call and call-to-session mappings
- Preserves call_info for historical access and reporting
- Cleans up any temporary state related to the call
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
// This method is called automatically by session-core
handler.on_call_ended(session, "User hangup").await;
// The method will:
// 1. Update call info to Terminated state
// 2. Set ended_at timestamp
// 3. Store "User hangup" in metadata
// 4. Emit final state change event
// 5. Clean up active mappingsSource§fn on_call_established<'life0, 'async_trait>(
&'life0 self,
session: CallSession,
local_sdp: Option<String>,
remote_sdp: Option<String>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn on_call_established<'life0, 'async_trait>(
&'life0 self,
session: CallSession,
local_sdp: Option<String>,
remote_sdp: Option<String>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Handle successful call establishment from session-core layer
This method is called by session-core when a call is successfully established and media can flow between participants. It represents the transition from call setup to active communication phase.
§Arguments
session- The CallSession object containing established call statelocal_sdp- Optional SDP offer/answer generated locallyremote_sdp- Optional SDP offer/answer received from remote party
§Processing Flow
- Call Lookup: Maps session ID to client call ID
- State Update: Transitions call to Connected state
- Timestamp Recording: Sets connected_at timestamp for analytics
- SDP Storage: Preserves SDP information in call metadata
- Event Emission: Broadcasts call established event with high priority
- Logging: Records successful establishment for debugging
§SDP Information Management
Both local and remote SDP information is stored in call metadata:
local_sdp: The SDP offer or answer generated by the local endpointremote_sdp: The SDP offer or answer received from the remote endpoint
This information is crucial for:
- Media session management and troubleshooting
- Codec negotiation analysis
- Network path and capability verification
- Call quality investigation
§State Transitions
- Updates call state to
Connected - Sets
connected_attimestamp if not already set - Preserves call establishment timing for billing and analytics
§Event Priority
Call establishment events are emitted with High priority because:
- They represent successful completion of call setup
- Applications often need immediate notification for UI updates
- Statistics and billing systems require prompt notification
§Examples
let handler = ClientCallHandler::new(
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
Arc::new(DashMap::new()),
);
let local_sdp = Some("v=0\r\no=alice 123456 654321 IN IP4 192.168.1.100\r\n...".to_string());
let remote_sdp = Some("v=0\r\no=bob 789012 210987 IN IP4 192.168.1.200\r\n...".to_string());
// This method is called automatically by session-core
handler.on_call_established(session, local_sdp, remote_sdp).await;
// The method will:
// 1. Update call state to Connected
// 2. Set connected_at timestamp
// 3. Store both local and remote SDP in metadata
// 4. Emit high-priority call established event
// 5. Log successful establishment