pub struct Call {
pub dialog: Dialog,
/* private fields */
}Expand description
A call in progress.
Fields§
§dialog: DialogThe dialog it runs in.
Implementations§
Source§impl Call
impl Call
Sourcepub fn dialog_snapshot(
&self,
now: Instant,
) -> Result<DialogSnapshot, DialogPersistenceError>
pub fn dialog_snapshot( &self, now: Instant, ) -> Result<DialogSnapshot, DialogPersistenceError>
Capture the bounded protocol state needed to continue this confirmed dialog.
now is explicit and only the session timer’s remaining duration is retained. Sockets,
endpoint handles, media sessions, tasks, transactions, credentials, keys, entropy and
process-local clock instants never enter DialogSnapshot. Capture refuses any call with
active work whose safe continuation would require one of those runtime values.
Sourcepub fn restore_dialog(
snapshot: &DialogSnapshot,
context: &DialogRestoreContext,
) -> Result<Self, DialogPersistenceError>
pub fn restore_dialog( snapshot: &DialogSnapshot, context: &DialogRestoreContext, ) -> Result<Self, DialogPersistenceError>
Attach validated durable dialog state to fresh endpoint and media drivers.
Restoration is synchronous and performs no I/O. Every snapshot and context invariant is checked before handles are cloned or events are published, so a refusal creates no task or transaction and leaves the borrowed context running exactly as supplied. Snapshot storage, authorization, encryption at rest, distribution and single-owner election belong to the host; a successful decode proves format validity, not permission to resume a call.
Sourcepub fn initial_status(&self) -> u16
pub fn initial_status(&self) -> u16
The successful final response that established this call.
Sourcepub fn media(&self) -> &MediaSession
pub fn media(&self) -> &MediaSession
The audio.
Sourcepub fn media_handle(&self) -> Arc<MediaSession>
pub fn media_handle(&self) -> Arc<MediaSession>
A shared media handle for an owning actor that must move one operation into a bounded task.
Most applications should use Self::media. This form exists for interactive owners that
must keep accepting control commands while a recording receives frames; sharing the session
does not share or clone the Call’s signalling state.
Sourcepub fn set_rtcp_quality_hook(&self, hook: Option<RtcpQualityHook>)
pub fn set_rtcp_quality_hook(&self, hook: Option<RtcpQualityHook>)
Install or clear the application callback for peer RTCP quality reports.
This is call-owned policy: it remains installed across an ordinary re-INVITE, a media
session replacement, and an ICE restart. The callback itself must return promptly; see
sipx_media::RtcpQualityHook.
Sourcepub fn rtcp_quality_hook(&self) -> Option<RtcpQualityHook>
pub fn rtcp_quality_hook(&self) -> Option<RtcpQualityHook>
The peer RTCP quality callback currently attached to this call.
Sourcepub async fn send_digit(&self, digit: Digit, duration: Duration) -> bool
pub async fn send_digit(&self, digit: Digit, duration: Duration) -> bool
Send a DTMF digit.
Sourcepub async fn send_digits(&self, digits: &str, duration: Duration) -> bool
pub async fn send_digits(&self, digits: &str, duration: Duration) -> bool
Send a string of digits, each held for duration.
Characters that are not DTMF digits are skipped rather than rejected: a caller passing a formatted number should not have to strip the spaces and dashes itself.
Sourcepub async fn recv_digit(&self) -> Option<Digit>
pub async fn recv_digit(&self) -> Option<Digit>
Take the next digit the far end pressed.
The digit only; a caller that wants how long it was held can read Self::media’s own
MediaSession::recv_digit, which this delegates to.
Sourcepub async fn play(&self, samples: &[i16]) -> bool
pub async fn play(&self, samples: &[i16]) -> bool
Play a clip and wait for it, reporting on the event stream when it stops.
Paced by the send loop, so this resolves when the audio has actually gone out rather than
when it was queued. Emits CallEvent::PlaybackFinished either way, with completed
saying which happened: the clip ran to the end, or something cut it short. A host driving
the call from its events needs that distinction — “the announcement finished” and “the
caller hung up during the announcement” lead to different next steps.
The packet size is the session’s own, so a clip plays correctly under a codec whose clock is not 8 kHz without the caller knowing the rate.
This is Self::start_playback awaited, with Interrupt::Never — the clip runs to its
end whatever the far end presses. A caller that wants to stop it, or wants a keypress to,
needs the handle. Cancel-on-drop, like MediaSession::play: abandoning this future — a
timeout that fires, a lost select! — stops the clip rather than leaving it playing.
Sourcepub async fn play_pcm(&self, pcm: &Pcm) -> Result<bool, PcmError>
pub async fn play_pcm(&self, pcm: &Pcm) -> Result<bool, PcmError>
Convert and play explicit linear PCM, reporting completion on the call event stream.
§Errors
Returns sipx_audio::PcmError before queuing audio when the format cannot be converted.
Sourcepub fn start_playback(
&self,
samples: Vec<i16>,
interrupt: Interrupt,
) -> Playback
pub fn start_playback( &self, samples: Vec<i16>, interrupt: Interrupt, ) -> Playback
Start a clip and hand back a handle to it, without waiting (M-17).
The primitive under “play a prompt and collect digits”: the caller goes on to read digits
while the audio plays, and can reach back through the handle to stop the prompt — or ask
for Interrupt::OnDigit and have the far end’s first keypress stop it. That keypress is
not consumed by interrupting; it arrives at Self::recv_digit like any other, which
is what makes the application contract’s gather{prompt, interruptible}
(docs/specs/app-contract.md §6.2) buildable rather than a menu that eats the first digit
of every PIN.
Clips queue: a second playback started while one is running begins when that one ends.
See MediaSession::start_playback for why, and for what a clip queued while another is
stopping does. The bound on stopping is Playback::STOP_BOUND_PACKETS packets.
Reports CallEvent::PlaybackFinished for this playback however it ends, without the
caller having to await the handle — a watcher task does it, so a fire-and-forget
announcement is still observable to a host driving the call from its events.
Sourcepub fn start_pcm_playback(
&self,
pcm: &Pcm,
interrupt: Interrupt,
) -> Result<Playback, PcmError>
pub fn start_pcm_playback( &self, pcm: &Pcm, interrupt: Interrupt, ) -> Result<Playback, PcmError>
Convert explicit linear PCM and start a controllable playback.
§Errors
Returns sipx_audio::PcmError before creating a playback when conversion is refused.
Sourcepub async fn record_until_idle(&self, idle: Duration) -> Vec<i16>
pub async fn record_until_idle(&self, idle: Duration) -> Vec<i16>
Record until the far end goes quiet for idle, and report the result on the event stream.
Emits CallEvent::RecordingFinished carrying how much audio was captured — measured
from the samples themselves and the session’s clock rate, not by timing the call, so the
number describes the recording rather than how long this side waited for it. The trailing
idle silence is not part of it: it is how the end was detected, not something the far
end said.
Sourcepub async fn record_at_least(
&self,
samples: usize,
within: Duration,
) -> Vec<i16>
pub async fn record_at_least( &self, samples: usize, within: Duration, ) -> Vec<i16>
Record until samples samples have arrived or within elapses, and report the result on
the event stream.
The counted wait, for a caller that knows how much audio the far end was given;
MediaSession::record_at_least has the reasoning, and why within is a bound on
failure rather than a window to measure in. Emits the same
CallEvent::RecordingFinished as Self::record_until_idle, measured the same way —
from the samples, not from how long this side waited for them.
Sourcepub fn mute(&self)
pub fn mute(&self)
Stop contributing audio to the far end, without telling it anything (story M-18).
§Mute is not hold
This is the distinction the whole verb exists for, and getting it wrong is how a call ends up renegotiated when all that was wanted was a quiet microphone:
mute | reinvite(Direction::SendOnly) | |
|---|---|---|
| Signalling | none — no re-INVITE, nothing on the wire | a re-INVITE the far end must answer |
| The SDP direction | unchanged; the session is the one that was negotiated | changed, and that is the mechanism |
| What the far end knows | nothing; is_on_hold there is unaffected | that this call is on hold, and it may play its own hold music |
| The RTP stream | keeps flowing, carrying silence | governed by the new direction |
| Can fail | no | yes — the far end can refuse the renegotiation |
Hold is a state two parties agree on. Mute is a decision one party makes about its own microphone, and a far end that could tell the difference between a muted caller and a silent one would be reading something it was never sent.
§What it does and does not gate
Outbound audio only, and it is a gate rather than a suppressor: Self::play still runs
and still resolves the same way, the packets still go out at the same pacing, and what the
far end decodes out of them is silence. Reception is untouched — Self::recv_digit,
Self::record_until_idle and MediaSession::quality all keep working while muted —
and so is DTMF in the sending direction: Self::send_digits is an explicit act by this
endpoint, like a keypad tone on a handset, not something the microphone picked up.
Emits CallEvent::Muted on the transition, and nothing when the call was already muted.
Sourcepub fn unmute(&self)
pub fn unmute(&self)
Contribute audio to the far end again, undoing Self::mute.
Emits CallEvent::Unmuted on the transition, and nothing when the call was not muted.
Like Self::mute it sends nothing: there is no renegotiation to undo, because muting
never made one.
Sourcepub fn is_muted(&self) -> bool
pub fn is_muted(&self) -> bool
Whether this side’s outbound audio is muted.
Local state, and a different question from Self::is_on_hold, which reports what the
far end has signalled about the session.
Sourcepub fn is_encrypted(&self) -> bool
pub fn is_encrypted(&self) -> bool
Whether the media is encrypted (RFC 3711).
Worth asking, and worth being able to answer without a packet capture. A call whose
signalling is encrypted and whose audio is not looks identical from the outside to one
where both are — which is exactly the confusion that makes people believe sips: covers
the media. It does not.
Sourcepub fn negotiated_keying(&self) -> NegotiatedKeying
pub fn negotiated_keying(&self) -> NegotiatedKeying
The keying mechanism this established call actually negotiated.
Unlike the initial Keying policy, the result contains no Auto: by confirmation the
compatibility choice has resolved to either plain RTP or SDES-SRTP.
Sourcepub const fn media_profile(&self) -> MediaProfile
pub const fn media_profile(&self) -> MediaProfile
The named media profile this established call retained.
Sourcepub fn browser_component(&self) -> Option<BrowserComponentSnapshot>
pub fn browser_component(&self) -> Option<BrowserComponentSnapshot>
Nominated-pair, generation, state, and bounded ingress facts for browser audio.
Sourcepub fn negotiated_payload_type(&self) -> u8
pub fn negotiated_payload_type(&self) -> u8
RTP payload type selected for sending the established audio codec.
Sourcepub fn negotiated_receive_payload_type(&self) -> u8
pub fn negotiated_receive_payload_type(&self) -> u8
RTP payload type accepted when receiving the established audio codec.
Usually equal to Self::negotiated_payload_type, but each SDP description may assign a
different dynamic number to the same format (RFC 3264 §6.1).
Sourcepub fn negotiated_clock_rate(&self) -> u32
pub fn negotiated_clock_rate(&self) -> u32
RTP clock rate selected for the established audio codec.
Sourcepub fn events(&mut self) -> Option<CallEvents>
pub fn events(&mut self) -> Option<CallEvents>
This call’s event stream (story C-3).
Some the first time this is called, None every time after — there is exactly one
consumer, per the vision’s “own it, don’t share it” (principle 3), so the receiver is
handed out rather than cloned.
Sourcepub fn set_dialog_credentials(&mut self, credentials: Credentials)
pub fn set_dialog_credentials(&mut self, credentials: Credentials)
Retain credentials for authenticated requests originated inside this dialog.
Outbound calls inherit DialOptions::credentials. This setter supplies the equivalent
policy for answered calls or rotates the credentials on an existing call.
Sourcepub fn admit_dialog_method(&mut self, method: &Method) -> Result<()>
pub fn admit_dialog_method(&mut self, method: &Method) -> Result<()>
Admit one private, case-sensitive method token to the application-owned dialog path.
Known SIP methods are refused: their ownership is decided by the stack, not converted into a private extension by application policy.
Sourcepub async fn send_dialog_request(
&mut self,
method: Method,
headers: &[Header],
body: Bytes,
) -> Result<Response>
pub async fn send_dialog_request( &mut self, method: Method, headers: &[Header], body: Bytes, ) -> Result<Response>
Send an application-owned request inside this dialog.
The dialog supplies the Request-URI, route set, identifiers and next CSeq. headers may
contain application fields such as Content-Type, but never routing, dialog, framing, or
authorization fields. A supported 401/407 challenge is retried once when dialog credentials
are available.
Sourcepub async fn handle(&mut self, incoming: &Incoming) -> Result<bool>
pub async fn handle(&mut self, incoming: &Incoming) -> Result<bool>
Feed an in-dialog request to the call.
Returns whether it belonged here. Without this an incoming BYE reaches nothing and the local media session goes on sending RTP into a call the far end has torn down — worse than a call that never connects, because it does not stop.
Sourcepub fn session_interval(&self) -> Option<(Duration, bool)>
pub fn session_interval(&self) -> Option<(Duration, bool)>
The negotiated session interval, and whether this side is the one refreshing it.
None means no timer was agreed, so nothing will ever notice a far end that stops
answering — worth being able to check, because that is a property of the peer, not of
what this side asked for.
Sourcepub fn history(&self) -> Option<&HistoryInfo>
pub fn history(&self) -> Option<&HistoryInfo>
The diversion history received while this call was established.
Sourcepub fn session_deadline(&self) -> Option<Instant>
pub fn session_deadline(&self) -> Option<Instant>
When Self::on_session_deadline next needs to be called, if a timer was negotiated.
Returned as an instant rather than as a future on purpose. A future would borrow the
call for as long as it was being awaited, which is exactly the borrow
Self::handle needs in the other arm of the select! this is written for.
Sourcepub async fn on_session_deadline(&mut self) -> Result<()>
pub async fn on_session_deadline(&mut self) -> Result<()>
Do whatever the session timer’s deadline asked for (RFC 4028 §10).
For the refresher that is an UPDATE or a re-INVITE — whichever the peer’s Allow says
it can take (§7.4); for the other side it is a BYE,
because nothing arrived and the far end is presumed gone. Calling this early is harmless
— it re-reads the deadline and does nothing if it has not passed.
Sourcepub fn is_on_hold(&self) -> bool
pub fn is_on_hold(&self) -> bool
Whether the far end has put the call on hold.
Sourcepub async fn update(&mut self, direction: Direction) -> Result<()>
pub async fn update(&mut self, direction: Direction) -> Result<()>
Renegotiate this call with an UPDATE (RFC 3311).
Self::reinvite remains the right way to renegotiate a confirmed dialog — §5.1
recommends it, because an UPDATE must be answered promptly and leaves the far end no
window in which to ask a user whether the change is acceptable. This is here for the
cases where that does not apply: a peer that asked for UPDATE, or a change that nobody
would be asked about.
Refuses locally rather than putting an illegal request on the wire when an offer of ours is unanswered or one of theirs is unanswered by us (§5.1, RFC 3264): the far end would answer 491 or 500 and the round trip would have told us only what we already knew.
Sourcepub async fn restart_ice(&mut self) -> Result<()>
pub async fn restart_ice(&mut self) -> Result<()>
Restart ICE on this call (RFC 8445 §9, RFC 8839 §4.4.1.1.1; ice.md §13.5).
Sends a re-INVITE whose offer carries new ice-ufrag and ice-pwd for this stream,
which is the entire signal — the peer reads both having changed and begins a new ICE
session. Everything else about the call is unchanged, including its direction, so a
restart does not resume a call that was on hold.
Media keeps flowing on the pair the finished session selected until the new one selects its own. That is what makes a restart usable in the situation it exists for: the path has become doubtful, not yet unusable, and going silent while checks converge would turn a recoverable call into a dropped one.
A call not running ICE is left alone and reports success. There is nothing to restart, and making the caller distinguish “no ICE” from “restart failed” would push the check to every call site.
§Errors
Returns Error when the re-INVITE cannot be built or sent, or when the far end refuses
it — the same failures as any other renegotiation, and like them it leaves the call running.
Sourcepub async fn reinvite(&mut self, direction: Direction) -> Result<()>
pub async fn reinvite(&mut self, direction: Direction) -> Result<()>
Send a re-INVITE renegotiating this call.
direction puts the call on hold (SendOnly or Inactive) or takes it off
(SendRecv).
Note what hold is not: RFC 8839 §4.4.1.1.1 makes c=0.0.0.0 imply an ICE restart, so a
hold spelled with a null connection address would restart ICE on every mute. Hold here is a
direction and nothing else (RFC 3264), which is what it has always been, and this is the
story that makes that a decision rather than an accident.
Sourcepub async fn refer(&mut self, target: &Uri) -> Result<()>
pub async fn refer(&mut self, target: &Uri) -> Result<()>
Ask the far end to transfer this call to target (RFC 3515).
Returns once the transferee has accepted the request, which is not the same as the
transfer having worked: a 202 Accepted means “I will try”. What became of it arrives
afterwards, as NOTIFY, and shows up in Self::transfer. Reporting success here would
tell a user their call was handed over when it may have been refused or rung out.
Sourcepub async fn refer_attended(&mut self, other: &Call) -> Result<()>
pub async fn refer_attended(&mut self, other: &Call) -> Result<()>
Ask the far end to replace other with a call to this one’s peer (RFC 3891 + 3515).
The attended half of a transfer. Where a blind transfer says “call this number”, this says “call this number, and when you get through, take the place of the call I already have with them” — which is what makes the handover seamless rather than a second ring.
Sourcepub fn referral(&self) -> Option<&Referral>
pub fn referral(&self) -> Option<&Referral>
The transfer the far end has asked for, if it has asked and we have not answered.
Sourcepub fn transfer(&self) -> Option<&Transfer>
pub fn transfer(&self) -> Option<&Transfer>
A transfer we asked for, and what has become of it. None if we asked for none.
Sourcepub async fn accept_referral(
&mut self,
target: Target,
options: &DialOptions,
) -> Result<Call>
pub async fn accept_referral( &mut self, target: Target, options: &DialOptions, ) -> Result<Call>
Accept the transfer, place the call, and report the outcome (RFC 3515 §2.4.5).
target is where to send the new INVITE; the Refer-To URI is what goes in it. The
two are separate for the same reason they are separate in dial: resolving a URI to
an address is RFC 3263’s job and lives in the transport, not here.
The original call is left running. Whether to hang up on the transferor is a policy decision — a blind transfer usually ends it, an attended one does not — and it belongs to whoever is making that decision, not to this function.
Sourcepub async fn refuse_referral(
&mut self,
status: u16,
reason: &'static str,
) -> Result<()>
pub async fn refuse_referral( &mut self, status: u16, reason: &'static str, ) -> Result<()>
Refuse the transfer (RFC 3515 §2.4.2).
No subscription is created by a REFER that was not accepted, so nothing further is owed and no NOTIFY is sent. The transferor learns the outcome from the status, which is why it should be one they can act on — 603 for “no”, 488 for “not that target”.
Sourcepub async fn hang_up_observed(&mut self, within: Duration) -> Result<u16>
pub async fn hang_up_observed(&mut self, within: Duration) -> Result<u16>
End the call and return the valid final response to the originated BYE.
Unlike Self::hang_up, this is an evidence-producing teardown: within bounds failure,
and success requires the final response to name this exact dialog and the BYE’s exact
CSeq.
A valid non-2xx is returned as Error::Rejected, while a mismatched response is
Error::InvalidDialogResponse.
Sourcepub async fn hang_up_with_reason(&mut self, reason: ReasonValue) -> Result<()>
pub async fn hang_up_with_reason(&mut self, reason: ReasonValue) -> Result<()>
End the call with an explicit protocol cause.
This is the coupled-leg shape from RFC 3326 §3.1: a controller which knows the winning response can tell the other dialog why it is being ended instead of reducing every teardown to a local hangup.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Call
impl !RefUnwindSafe for Call
impl Send for Call
impl Sync for Call
impl Unpin for Call
impl UnsafeUnpin for Call
impl !UnwindSafe for Call
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more