medea_client_api_proto/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![cfg_attr(any(doc, test), doc = include_str!("../README.md"))]
3#![cfg_attr(not(any(doc, test)), doc = env!("CARGO_PKG_NAME"))]
4#![deny(nonstandard_style, rustdoc::all, trivial_casts, trivial_numeric_casts)]
5#![forbid(non_ascii_idents, unsafe_code)]
6#![warn(
7 clippy::absolute_paths,
8 clippy::allow_attributes,
9 clippy::allow_attributes_without_reason,
10 clippy::as_conversions,
11 clippy::as_pointer_underscore,
12 clippy::as_ptr_cast_mut,
13 clippy::assertions_on_result_states,
14 clippy::branches_sharing_code,
15 clippy::cfg_not_test,
16 clippy::clear_with_drain,
17 clippy::clone_on_ref_ptr,
18 clippy::collection_is_never_read,
19 clippy::create_dir,
20 clippy::dbg_macro,
21 clippy::debug_assert_with_mut_call,
22 clippy::decimal_literal_representation,
23 clippy::default_union_representation,
24 clippy::derive_partial_eq_without_eq,
25 clippy::doc_include_without_cfg,
26 clippy::else_if_without_else,
27 clippy::empty_drop,
28 clippy::empty_structs_with_brackets,
29 clippy::equatable_if_let,
30 clippy::empty_enum_variants_with_brackets,
31 clippy::exit,
32 clippy::expect_used,
33 clippy::fallible_impl_from,
34 clippy::filetype_is_file,
35 clippy::float_cmp_const,
36 clippy::fn_to_numeric_cast_any,
37 clippy::format_push_string,
38 clippy::get_unwrap,
39 clippy::if_then_some_else_none,
40 clippy::imprecise_flops,
41 clippy::infinite_loop,
42 clippy::iter_on_empty_collections,
43 clippy::iter_on_single_items,
44 clippy::iter_over_hash_type,
45 clippy::iter_with_drain,
46 clippy::large_include_file,
47 clippy::large_stack_frames,
48 clippy::let_underscore_untyped,
49 clippy::literal_string_with_formatting_args,
50 clippy::lossy_float_literal,
51 clippy::map_err_ignore,
52 clippy::map_with_unused_argument_over_ranges,
53 clippy::mem_forget,
54 clippy::missing_assert_message,
55 clippy::missing_asserts_for_indexing,
56 clippy::missing_const_for_fn,
57 clippy::missing_docs_in_private_items,
58 clippy::module_name_repetitions,
59 clippy::multiple_inherent_impl,
60 clippy::multiple_unsafe_ops_per_block,
61 clippy::mutex_atomic,
62 clippy::mutex_integer,
63 clippy::needless_collect,
64 clippy::needless_pass_by_ref_mut,
65 clippy::needless_raw_strings,
66 clippy::non_zero_suggestions,
67 clippy::nonstandard_macro_braces,
68 clippy::option_if_let_else,
69 clippy::or_fun_call,
70 clippy::panic_in_result_fn,
71 clippy::partial_pub_fields,
72 clippy::pathbuf_init_then_push,
73 clippy::pedantic,
74 clippy::print_stderr,
75 clippy::print_stdout,
76 clippy::pub_without_shorthand,
77 clippy::rc_buffer,
78 clippy::rc_mutex,
79 clippy::read_zero_byte_vec,
80 clippy::redundant_clone,
81 clippy::redundant_type_annotations,
82 clippy::renamed_function_params,
83 clippy::ref_patterns,
84 clippy::rest_pat_in_fully_bound_structs,
85 clippy::same_name_method,
86 clippy::semicolon_inside_block,
87 clippy::set_contains_or_insert,
88 clippy::shadow_unrelated,
89 clippy::significant_drop_in_scrutinee,
90 clippy::significant_drop_tightening,
91 clippy::str_to_string,
92 clippy::string_add,
93 clippy::string_lit_as_bytes,
94 clippy::string_lit_chars_any,
95 clippy::string_slice,
96 clippy::string_to_string,
97 clippy::suboptimal_flops,
98 clippy::suspicious_operation_groupings,
99 clippy::suspicious_xor_used_as_pow,
100 clippy::tests_outside_test_module,
101 clippy::todo,
102 clippy::too_long_first_doc_paragraph,
103 clippy::trailing_empty_array,
104 clippy::transmute_undefined_repr,
105 clippy::trivial_regex,
106 clippy::try_err,
107 clippy::undocumented_unsafe_blocks,
108 clippy::unimplemented,
109 clippy::uninhabited_references,
110 clippy::unnecessary_safety_comment,
111 clippy::unnecessary_safety_doc,
112 clippy::unnecessary_self_imports,
113 clippy::unnecessary_struct_initialization,
114 clippy::unneeded_field_pattern,
115 clippy::unused_peekable,
116 clippy::unused_result_ok,
117 clippy::unused_trait_names,
118 clippy::unwrap_in_result,
119 clippy::unwrap_used,
120 clippy::use_debug,
121 clippy::use_self,
122 clippy::useless_let_if_seq,
123 clippy::verbose_file_reads,
124 clippy::while_float,
125 clippy::wildcard_enum_match_arm,
126 ambiguous_negative_literals,
127 closure_returning_async_block,
128 future_incompatible,
129 impl_trait_redundant_captures,
130 let_underscore_drop,
131 macro_use_extern_crate,
132 meta_variable_misuse,
133 missing_abi,
134 missing_copy_implementations,
135 missing_debug_implementations,
136 missing_docs,
137 redundant_lifetimes,
138 rust_2018_idioms,
139 single_use_lifetimes,
140 unit_bindings,
141 unnameable_types,
142 unreachable_pub,
143 unstable_features,
144 unused,
145 variant_size_differences
146)]
147
148pub mod state;
149pub mod stats;
150
151use std::{
152 collections::HashMap,
153 hash::{Hash, Hasher},
154};
155
156use derive_more::with_trait::{Constructor, Display, From, Into};
157use medea_macro::dispatchable;
158use rand::{Rng as _, distr::Alphanumeric};
159use secrecy::{ExposeSecret as _, SecretString};
160use serde::{Deserialize, Serialize, Serializer};
161
162use self::stats::RtcStat;
163
164/// ID of a `Room`.
165#[derive(
166 Clone, Debug, Display, Serialize, Deserialize, Eq, From, Hash, PartialEq,
167)]
168#[from(forward)]
169pub struct RoomId(pub String);
170
171/// ID of a `Member`.
172#[derive(
173 Clone, Debug, Display, Serialize, Deserialize, Eq, From, Hash, PartialEq,
174)]
175#[from(forward)]
176pub struct MemberId(pub String);
177
178/// ID of a `Peer`.
179#[cfg_attr(feature = "server", derive(Default))]
180#[derive(
181 Clone, Copy, Debug, Deserialize, Display, Eq, Hash, PartialEq, Serialize,
182)]
183pub struct PeerId(pub u32);
184
185/// ID of a `MediaTrack`.
186#[cfg_attr(feature = "server", derive(Default))]
187#[derive(
188 Clone, Copy, Debug, Deserialize, Display, Eq, Hash, PartialEq, Serialize,
189)]
190pub struct TrackId(pub u32);
191
192/// Secret used for a client authentication on an [`IceServer`].
193#[derive(Clone, Debug, Deserialize, From, Into)]
194pub struct IcePassword(SecretString);
195
196impl IcePassword {
197 /// Length of a randomly generated [`IcePassword`].
198 const RANDOM_LENGTH: usize = 16;
199
200 /// Provides access to the underlying secret [`str`].
201 #[must_use]
202 pub fn expose_str(&self) -> &str {
203 self.0.expose_secret()
204 }
205
206 /// Generates a new random [`IcePassword`].
207 #[must_use]
208 pub fn random() -> Self {
209 Self(
210 rand::rng()
211 .sample_iter(&Alphanumeric)
212 .take(Self::RANDOM_LENGTH)
213 .map(char::from)
214 .collect::<String>()
215 .into(),
216 )
217 }
218}
219impl Serialize for IcePassword {
220 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
221 where
222 S: Serializer,
223 {
224 self.0.expose_secret().serialize(serializer)
225 }
226}
227
228impl Eq for IcePassword {}
229
230impl PartialEq for IcePassword {
231 fn eq(&self, other: &Self) -> bool {
232 use subtle::ConstantTimeEq as _;
233
234 self.expose_str().as_bytes().ct_eq(other.expose_str().as_bytes()).into()
235 }
236}
237
238/// Credential used for a `Member` authentication.
239#[derive(Clone, Debug, Deserialize)]
240pub struct Credential(SecretString);
241
242impl Serialize for Credential {
243 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
244 where
245 S: Serializer,
246 {
247 self.0.expose_secret().serialize(serializer)
248 }
249}
250
251impl Credential {
252 /// Provides access to the underlying secret [`str`].
253 #[must_use]
254 pub fn expose_str(&self) -> &str {
255 self.0.expose_secret()
256 }
257}
258
259impl<T> From<T> for Credential
260where
261 T: Into<String>,
262{
263 fn from(value: T) -> Self {
264 Self(value.into().into())
265 }
266}
267
268impl Hash for Credential {
269 fn hash<H: Hasher>(&self, state: &mut H) {
270 self.expose_str().hash(state);
271 }
272}
273
274impl Eq for Credential {}
275
276impl PartialEq for Credential {
277 fn eq(&self, other: &Self) -> bool {
278 use subtle::ConstantTimeEq as _;
279
280 self.expose_str().as_bytes().ct_eq(other.expose_str().as_bytes()).into()
281 }
282}
283
284#[cfg(feature = "server")]
285/// Value being able to be increment by `1`.
286pub trait Incrementable {
287 /// Returns current value + 1.
288 #[must_use]
289 fn incr(&self) -> Self;
290}
291
292#[cfg(feature = "server")]
293/// Implements [`Incrementable`] trait for a newtype with any numeric type.
294macro_rules! impl_incrementable {
295 ($name:ty) => {
296 impl Incrementable for $name {
297 fn incr(&self) -> Self {
298 Self(self.0 + 1)
299 }
300 }
301 };
302}
303
304#[cfg(feature = "server")]
305impl_incrementable!(PeerId);
306#[cfg(feature = "server")]
307impl_incrementable!(TrackId);
308
309/// Message sent by Media Server to Web Client.
310#[cfg_attr(
311 any(
312 target_family = "wasm",
313 all(target_arch = "arm", target_os = "android")
314 ),
315 expect(variant_size_differences, reason = "`Event` is the most common")
316)]
317#[derive(Clone, Debug, Eq, PartialEq)]
318#[cfg_attr(feature = "client", derive(Deserialize))]
319#[cfg_attr(feature = "server", derive(Serialize))]
320#[serde(tag = "msg", content = "data")]
321pub enum ServerMsg {
322 /// `ping` message that Media Server is expected to send to Web Client
323 /// periodically for probing its aliveness.
324 Ping(u32),
325
326 /// Media Server notifies Web Client about happened facts and it reacts on
327 /// them to reach the proper state.
328 Event {
329 /// ID of the `Room` that this [`Event`] is associated with.
330 room_id: RoomId,
331
332 /// Actual [`Event`] sent to Web Client.
333 event: Event,
334 },
335
336 /// Media Server notifies Web Client about necessity to update its RPC
337 /// settings.
338 RpcSettings(RpcSettings),
339}
340
341/// Message by Web Client to Media Server.
342#[cfg_attr(
343 any(
344 target_family = "wasm",
345 all(target_arch = "arm", target_os = "android")
346 ),
347 expect(variant_size_differences, reason = "`Command` is the most common")
348)]
349#[derive(Clone, Debug, PartialEq)]
350#[cfg_attr(feature = "client", derive(Serialize))]
351#[cfg_attr(feature = "server", derive(Deserialize))]
352pub enum ClientMsg {
353 /// `pong` message that Web Client answers with to Media Server in response
354 /// to received [`ServerMsg::Ping`].
355 Pong(u32),
356
357 /// Request of Web Client to change its state on Media Server.
358 Command {
359 /// ID of the `Room` that this [`Command`] is associated with.
360 room_id: RoomId,
361
362 /// Actual [`Command`] sent to Media Server.
363 command: Command,
364 },
365}
366
367/// RPC settings of Web Client received from Media Server.
368#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
369pub struct RpcSettings {
370 /// Timeout of considering Web Client as lost by Media Server when it
371 /// doesn't receive any [`ClientMsg::Pong`]s.
372 ///
373 /// Unit: millisecond.
374 pub idle_timeout_ms: u32,
375
376 /// Interval that Media Server sends [`ServerMsg::Ping`]s with.
377 ///
378 /// Unit: millisecond.
379 pub ping_interval_ms: u32,
380}
381
382/// Possible commands sent by Web Client to Media Server.
383#[dispatchable]
384#[cfg_attr(feature = "client", derive(Serialize))]
385#[cfg_attr(feature = "server", derive(Deserialize))]
386#[derive(Clone, Debug, PartialEq)]
387#[serde(tag = "command", content = "data")]
388pub enum Command {
389 /// Request to join a `Room`.
390 JoinRoom {
391 /// ID of the `Member` who joins the `Room`.
392 member_id: MemberId,
393
394 /// [`Credential`] of the `Member` to authenticate with.
395 credential: Credential,
396
397 /// [`Capabilities`] reported by Web Client (e.g. available codecs,
398 /// platform).
399 capabilities: Capabilities,
400 },
401
402 /// Request to leave a `Room`.
403 LeaveRoom {
404 /// ID of the `Member` who leaves the `Room`.
405 member_id: MemberId,
406 },
407
408 /// Web Client sends SDP Offer.
409 MakeSdpOffer {
410 /// ID of the `Peer` SDP Offer is sent for.
411 peer_id: PeerId,
412
413 /// SDP Offer of the `Peer`.
414 sdp_offer: String,
415
416 /// Associations between [`Track`] and transceiver's
417 /// [media description][1].
418 ///
419 /// `mid` is basically an ID of [`m=<media>` section][1] in SDP.
420 ///
421 /// [1]: https://tools.ietf.org/html/rfc4566#section-5.14
422 mids: HashMap<TrackId, String>,
423
424 /// Statuses of the `Peer` transceivers.
425 transceivers_statuses: HashMap<TrackId, bool>,
426 },
427
428 /// Web Client sends SDP Answer.
429 MakeSdpAnswer {
430 /// ID of the `Peer` SDP Answer is sent for.
431 peer_id: PeerId,
432
433 /// SDP Answer of the `Peer`.
434 sdp_answer: String,
435
436 /// Statuses of the `Peer` transceivers.
437 transceivers_statuses: HashMap<TrackId, bool>,
438 },
439
440 /// Web Client sends an Ice Candidate.
441 SetIceCandidate {
442 /// ID of the `Peer` the Ice Candidate is sent for.
443 peer_id: PeerId,
444
445 /// [`IceCandidate`] sent by the `Peer`.
446 candidate: IceCandidate,
447 },
448
449 /// Web Client sends Peer Connection metrics.
450 AddPeerConnectionMetrics {
451 /// ID of the `Peer` metrics are sent for.
452 peer_id: PeerId,
453
454 /// Metrics of the `Peer`.
455 metrics: PeerMetrics,
456 },
457
458 /// Web Client asks permission to update [`Track`]s in the specified
459 /// `Peer`. Media Server gives permission by sending
460 /// [`Event::PeerUpdated`].
461 UpdateTracks {
462 /// ID of the `Peer` to update [`Track`]s in.
463 peer_id: PeerId,
464
465 /// Patches for updating the [`Track`]s.
466 tracks_patches: Vec<TrackPatchCommand>,
467 },
468
469 /// Web Client asks Media Server to synchronize Client State with a
470 /// Server State.
471 SynchronizeMe {
472 /// Whole Client State of the `Room`.
473 state: state::Room,
474 },
475}
476
477/// Web Client's `PeerConnection` metrics.
478#[cfg_attr(feature = "client", derive(Serialize))]
479#[cfg_attr(feature = "server", derive(Deserialize))]
480#[derive(Clone, Debug, PartialEq)]
481pub enum PeerMetrics {
482 /// `PeerConnection`'s ICE connection state.
483 IceConnectionState(IceConnectionState),
484
485 /// `PeerConnection`'s connection state.
486 PeerConnectionState(PeerConnectionState),
487
488 /// `PeerConnection` related error occurred.
489 PeerConnectionError(PeerConnectionError),
490
491 /// `PeerConnection`'s RTC stats.
492 RtcStats(Vec<RtcStat>),
493}
494
495/// Possible errors related to a `PeerConnection`.
496#[cfg_attr(feature = "client", derive(Serialize))]
497#[cfg_attr(feature = "server", derive(Deserialize))]
498#[derive(Clone, Debug, Eq, PartialEq)]
499pub enum PeerConnectionError {
500 /// Error occurred with ICE candidate from a `PeerConnection`.
501 IceCandidate(IceCandidateError),
502}
503
504/// Error occurred with an [ICE] candidate from a `PeerConnection`.
505///
506/// [ICE]: https://webrtcglossary.com/ice
507#[cfg_attr(feature = "client", derive(Serialize))]
508#[cfg_attr(feature = "server", derive(Deserialize))]
509#[derive(Clone, Debug, Eq, PartialEq)]
510pub struct IceCandidateError {
511 /// Local IP address used to communicate with a [STUN]/[TURN] server.
512 ///
513 /// [STUN]: https://webrtcglossary.com/stun
514 /// [TURN]: https://webrtcglossary.com/turn
515 pub address: Option<String>,
516
517 /// Port used to communicate with a [STUN]/[TURN] server.
518 ///
519 /// [STUN]: https://webrtcglossary.com/stun
520 /// [TURN]: https://webrtcglossary.com/turn
521 pub port: Option<u32>,
522
523 /// URL identifying the [STUN]/[TURN] server for which the failure
524 /// occurred.
525 ///
526 /// [STUN]: https://webrtcglossary.com/stun
527 /// [TURN]: https://webrtcglossary.com/turn
528 pub url: String,
529
530 /// Numeric [STUN] error code returned by the [STUN]/[TURN] server.
531 ///
532 /// If no host candidate can reach the server, this error code will be set
533 /// to the value `701`, which is outside the [STUN] error code range. This
534 /// error is only fired once per server URL while in the
535 /// `RTCIceGatheringState` of "gathering".
536 ///
537 /// [STUN]: https://webrtcglossary.com/stun
538 /// [TURN]: https://webrtcglossary.com/turn
539 pub error_code: i32,
540
541 /// [STUN] reason text returned by the [STUN]/[TURN] server.
542 ///
543 /// If the server could not be reached, this reason test will be set to an
544 /// implementation-specific value providing details about the error.
545 ///
546 /// [STUN]: https://webrtcglossary.com/stun
547 /// [TURN]: https://webrtcglossary.com/turn
548 pub error_text: String,
549}
550
551/// `PeerConnection`'s ICE connection state.
552#[cfg_attr(feature = "client", derive(Serialize))]
553#[cfg_attr(feature = "server", derive(Deserialize))]
554#[derive(Clone, Copy, Debug, Eq, PartialEq)]
555pub enum IceConnectionState {
556 /// ICE agent is gathering addresses or is waiting to be given remote
557 /// candidates.
558 New,
559
560 /// ICE agent has been given one or more remote candidates and is checking
561 /// pairs of local and remote candidates against one another to try to find
562 /// a compatible match, but hasn't yet found a pair which will allow the
563 /// `PeerConnection` to be made. It's possible that gathering of candidates
564 /// is also still underway.
565 Checking,
566
567 /// Usable pairing of local and remote candidates has been found for all
568 /// components of the connection, and the connection has been established.
569 /// It's possible that gathering is still underway, and it's also possible
570 /// that the ICE agent is still checking candidates against one another
571 /// looking for a better connection to use.
572 Connected,
573
574 /// ICE agent has finished gathering candidates, has checked all pairs
575 /// against one another, and has found a connection for all components.
576 Completed,
577
578 /// ICE candidate has checked all candidates pairs against one another and
579 /// has failed to find compatible matches for all components of the
580 /// connection. It is, however, possible that the ICE agent did find
581 /// compatible connections for some components.
582 Failed,
583
584 /// Checks to ensure that components are still connected failed for at
585 /// least one component of the `PeerConnection`. This is a less stringent
586 /// test than [`IceConnectionState::Failed`] and may trigger intermittently
587 /// and resolve just as spontaneously on less reliable networks, or during
588 /// temporary disconnections. When the problem resolves, the connection may
589 /// return to the [`IceConnectionState::Connected`] state.
590 Disconnected,
591
592 /// ICE agent for this `PeerConnection` has shut down and is no longer
593 /// handling requests.
594 Closed,
595}
596
597/// `PeerConnection`'s connection state.
598#[cfg_attr(feature = "client", derive(Serialize))]
599#[cfg_attr(feature = "server", derive(Deserialize))]
600#[derive(Clone, Copy, Debug, Eq, PartialEq)]
601pub enum PeerConnectionState {
602 /// At least one of the connection's ICE transports are in the
603 /// [`IceConnectionState::New`] state, and none of them are in one
604 /// of the following states: [`IceConnectionState::Checking`],
605 /// [`IceConnectionState::Failed`], or
606 /// [`IceConnectionState::Disconnected`], or all of the connection's
607 /// transports are in the [`IceConnectionState::Closed`] state.
608 New,
609
610 /// One or more of the ICE transports are currently in the process of
611 /// establishing a connection; that is, their [`IceConnectionState`] is
612 /// either [`IceConnectionState::Checking`] or
613 /// [`IceConnectionState::Connected`], and no transports are in the
614 /// [`IceConnectionState::Failed`] state.
615 Connecting,
616
617 /// Every ICE transport used by the connection is either in use (state
618 /// [`IceConnectionState::Connected`] or [`IceConnectionState::Completed`])
619 /// or is closed ([`IceConnectionState::Closed`]); in addition,
620 /// at least one transport is either [`IceConnectionState::Connected`] or
621 /// [`IceConnectionState::Completed`].
622 Connected,
623
624 /// At least one of the ICE transports for the connection is in the
625 /// [`IceConnectionState::Disconnected`] state and none of the other
626 /// transports are in the state [`IceConnectionState::Failed`] or
627 /// [`IceConnectionState::Checking`].
628 Disconnected,
629
630 /// One or more of the ICE transports on the connection is in the
631 /// [`IceConnectionState::Failed`] state.
632 Failed,
633
634 /// The `PeerConnection` is closed.
635 Closed,
636}
637
638impl From<IceConnectionState> for PeerConnectionState {
639 fn from(ice_con_state: IceConnectionState) -> Self {
640 use IceConnectionState as Ice;
641
642 match ice_con_state {
643 Ice::New => Self::New,
644 Ice::Checking => Self::Connecting,
645 Ice::Connected | Ice::Completed => Self::Connected,
646 Ice::Failed => Self::Failed,
647 Ice::Disconnected => Self::Disconnected,
648 Ice::Closed => Self::Closed,
649 }
650 }
651}
652
653/// Reason of disconnecting Web Client from Media Server.
654#[derive(
655 Copy, Clone, Debug, Deserialize, Display, Eq, PartialEq, Serialize,
656)]
657pub enum CloseReason {
658 /// Client session was finished on a server side.
659 Finished,
660
661 /// Old connection was closed due to a client reconnection.
662 Reconnected,
663
664 /// Connection has been inactive for a while and thus considered idle
665 /// by a server.
666 Idle,
667
668 /// Establishing of connection with a server was rejected on server side.
669 ///
670 /// Most likely because of incorrect `Member` credentials.
671 Rejected,
672
673 /// Server internal error has occurred while connecting.
674 ///
675 /// This close reason is similar to 500 HTTP status code.
676 InternalError,
677
678 /// Client was evicted on the server side.
679 Evicted,
680}
681
682/// Description which is sent in [Close] WebSocket frame from Media Server to
683/// Web Client.
684///
685/// [Close]: https://tools.ietf.org/html/rfc6455#section-5.5.1
686#[derive(
687 Clone, Constructor, Copy, Debug, Deserialize, Eq, PartialEq, Serialize,
688)]
689pub struct CloseDescription {
690 /// Reason of why WebSocket connection has been closed.
691 pub reason: CloseReason,
692}
693
694/// Possible WebSocket messages sent from Media Server to Web Client.
695#[dispatchable(self: &Self, async_trait(?Send))]
696#[cfg_attr(feature = "client", derive(Deserialize))]
697#[cfg_attr(feature = "server", derive(Serialize))]
698#[derive(Clone, Debug, Eq, PartialEq)]
699#[serde(tag = "event", content = "data")]
700pub enum Event {
701 /// Media Server notifies Web Client that a `Member` joined a `Room`.
702 RoomJoined {
703 /// ID of the `Member` who joined the `Room`.
704 member_id: MemberId,
705 },
706
707 /// Media Server notifies Web Client that a `Member` left a `Room`.
708 RoomLeft {
709 /// [`CloseReason`] with which the `Member` left the `Room`.
710 close_reason: CloseReason,
711 },
712
713 /// Media Server notifies Web Client about necessity of RTCPeerConnection
714 /// creation.
715 PeerCreated {
716 /// ID of the `Peer` to create RTCPeerConnection for.
717 peer_id: PeerId,
718
719 /// [`NegotiationRole`] of the `Peer`.
720 negotiation_role: NegotiationRole,
721
722 /// Indicator whether this `Peer` is working in a [P2P mesh] or [SFU]
723 /// mode.
724 ///
725 /// [P2P mesh]: https://webrtcglossary.com/mesh
726 /// [SFU]: https://webrtcglossary.com/sfu
727 connection_mode: ConnectionMode,
728
729 /// [`Track`]s to create RTCPeerConnection with.
730 tracks: Vec<Track>,
731
732 /// [`IceServer`]s to create RTCPeerConnection with.
733 ice_servers: Vec<IceServer>,
734
735 /// Indicator whether the created RTCPeerConnection should be forced to
736 /// use relay [`IceServer`]s only.
737 force_relay: bool,
738 },
739
740 /// Media Server notifies Web Client about necessity to apply the specified
741 /// SDP Answer to Web Client's RTCPeerConnection.
742 SdpAnswerMade {
743 /// ID of the `Peer` to apply SDP Answer to.
744 peer_id: PeerId,
745
746 /// SDP Answer to be applied.
747 sdp_answer: String,
748 },
749
750 /// Media Server notifies Web Client that his SDP offer was applied.
751 LocalDescriptionApplied {
752 /// ID of the `Peer` which SDP offer was applied.
753 peer_id: PeerId,
754
755 /// SDP offer that was applied.
756 sdp_offer: String,
757 },
758
759 /// Media Server notifies Web Client about necessity to apply the specified
760 /// ICE Candidate.
761 IceCandidateDiscovered {
762 /// ID of the `Peer` to apply ICE Candidate to.
763 peer_id: PeerId,
764
765 /// ICE Candidate to be applied.
766 candidate: IceCandidate,
767 },
768
769 /// Media Server notifies Web Client about necessity of RTCPeerConnection
770 /// close.
771 PeersRemoved {
772 /// IDs of `Peer`s to be removed.
773 peer_ids: Vec<PeerId>,
774 },
775
776 /// Media Server notifies about necessity to update [`Track`]s in a `Peer`.
777 PeerUpdated {
778 /// ID of the `Peer` to update [`Track`]s in.
779 peer_id: PeerId,
780
781 /// List of [`PeerUpdate`]s which should be applied.
782 updates: Vec<PeerUpdate>,
783
784 /// Negotiation role basing on which should be sent
785 /// [`Command::MakeSdpOffer`] or [`Command::MakeSdpAnswer`].
786 ///
787 /// If [`None`] then no (re)negotiation should be done.
788 negotiation_role: Option<NegotiationRole>,
789 },
790
791 /// Media Server notifies about connection quality score update.
792 ConnectionQualityUpdated {
793 /// Partner [`MemberId`] of the `Peer`.
794 partner_member_id: MemberId,
795
796 /// Estimated connection quality.
797 quality_score: ConnectionQualityScore,
798 },
799
800 /// Media Server synchronizes Web Client state and reports the proper one.
801 StateSynchronized {
802 /// Proper state that should be assumed by Web Client.
803 state: state::Room,
804 },
805}
806
807/// `Peer`'s negotiation role.
808///
809/// Some [`Event`]s can trigger SDP negotiation:
810/// - If [`Event`] contains [`NegotiationRole::Offerer`], then `Peer` is
811/// expected to create SDP Offer and send it via [`Command::MakeSdpOffer`].
812/// - If [`Event`] contains [`NegotiationRole::Answerer`], then `Peer` is
813/// expected to apply provided SDP Offer and provide its SDP Answer in a
814/// [`Command::MakeSdpAnswer`].
815#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
816pub enum NegotiationRole {
817 /// [`Command::MakeSdpOffer`] should be sent by client.
818 Offerer,
819
820 /// [`Command::MakeSdpAnswer`] should be sent by client.
821 Answerer(String),
822}
823
824/// Indication whether a `Peer` is working in a [P2P mesh] or [SFU] mode.
825///
826/// [P2P mesh]: https://webrtcglossary.com/mesh
827/// [SFU]: https://webrtcglossary.com/sfu
828#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
829pub enum ConnectionMode {
830 /// `Peer` is configured to work in a [P2P mesh] mode.
831 ///
832 /// [P2P mesh]: https://webrtcglossary.com/mesh
833 Mesh,
834
835 /// `Peer` is configured to work in an [SFU] mode.
836 ///
837 /// [SFU]: https://webrtcglossary.com/sfu
838 Sfu,
839}
840
841/// [`Track`] update which should be applied to the `Peer`.
842#[cfg_attr(feature = "client", derive(Deserialize))]
843#[cfg_attr(feature = "server", derive(Serialize))]
844#[derive(Clone, Debug, Eq, PartialEq)]
845pub enum PeerUpdate {
846 /// New [`Track`] should be added to the `Peer`.
847 Added(Track),
848
849 /// [`Track`] with the provided [`TrackId`] should be removed from the
850 /// `Peer`.
851 ///
852 /// Can only refer [`Track`]s already known to the `Peer`.
853 Removed(TrackId),
854
855 /// [`Track`] should be updated by this [`TrackPatchEvent`] in the `Peer`.
856 /// Can only refer tracks already known to the `Peer`.
857 Updated(TrackPatchEvent),
858
859 /// `Peer` should start ICE restart process on the next renegotiation.
860 IceRestart,
861}
862
863/// Representation of [RTCIceCandidateInit][1] object.
864///
865/// [1]: https://w3.org/TR/webrtc#dom-rtcicecandidateinit
866#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
867pub struct IceCandidate {
868 /// [`candidate-attribute`][0] of this [`IceCandidate`].
869 ///
870 /// If this [`IceCandidate`] represents an end-of-candidates indication,
871 /// then it's an empty string.
872 ///
873 /// [0]: https://w3.org/TR/webrtc#dfn-candidate-attribute
874 pub candidate: String,
875
876 /// Index (starting at zero) of the media description in the SDP this
877 /// [`IceCandidate`] is associated with.
878 pub sdp_m_line_index: Option<u16>,
879
880 /// [Media stream "identification-tag"] for the media component this
881 /// [`IceCandidate`] is associated with.
882 ///
883 /// [0]: https://w3.org/TR/webrtc#dfn-media-stream-identification-tag
884 pub sdp_mid: Option<String>,
885}
886
887/// Track with a [`Direction`].
888#[cfg_attr(feature = "client", derive(Deserialize))]
889#[cfg_attr(feature = "server", derive(Serialize))]
890#[derive(Clone, Debug, Eq, PartialEq)]
891pub struct Track {
892 /// ID of this [`Track`].
893 pub id: TrackId,
894
895 /// [`Direction`] of this [`Track`].
896 pub direction: Direction,
897
898 /// [`MediaDirection`] of this [`Track`].
899 pub media_direction: MediaDirection,
900
901 /// [`Track`]'s mute state.
902 pub muted: bool,
903
904 /// [`MediaType`] of this [`Track`].
905 pub media_type: MediaType,
906}
907
908impl Track {
909 /// Indicates whether this [`Track`] is required to call starting.
910 #[must_use]
911 pub const fn required(&self) -> bool {
912 self.media_type.required()
913 }
914}
915
916/// Patch of a [`Track`] which Web Client can request with a
917/// [`Command::UpdateTracks`].
918#[cfg_attr(feature = "client", derive(Serialize))]
919#[cfg_attr(feature = "server", derive(Deserialize))]
920#[derive(Clone, Copy, Debug, Eq, PartialEq)]
921pub struct TrackPatchCommand {
922 /// ID of the [`Track`] this patch is intended for.
923 pub id: TrackId,
924
925 /// [`Track`]'s media exchange state.
926 pub enabled: Option<bool>,
927
928 /// [`Track`]'s mute state.
929 ///
930 /// Muting and unmuting can be performed without adding/removing tracks
931 /// from transceivers, hence renegotiation is not required.
932 pub muted: Option<bool>,
933}
934
935/// Patch of a [`Track`] which Media Server can send with an
936/// [`Event::PeerUpdated`].
937#[cfg_attr(feature = "client", derive(Deserialize))]
938#[cfg_attr(feature = "server", derive(Serialize))]
939#[derive(Clone, Debug, Eq, PartialEq)]
940pub struct TrackPatchEvent {
941 /// ID of the [`Track`] which should be patched.
942 pub id: TrackId,
943
944 /// General media exchange direction of the `Track`.
945 pub media_direction: Option<MediaDirection>,
946
947 /// IDs of the `Member`s who should receive this outgoing [`Track`].
948 ///
949 /// If [`Some`], then it means there are some changes in this outgoing
950 /// [`Track`]'s `receivers` (or we just want to sync this outgoing
951 /// [`Track`]'s `receivers`). It describes not changes, but the actual
952 /// [`Vec<MemberId>`] of this outgoing [`Track`], that have to be reached
953 /// once this [`TrackPatchEvent`] applied.
954 ///
955 /// If [`None`], then it means there is no need to check and recalculate
956 /// this outgoing [`Track`]'s `receivers`.
957 pub receivers: Option<Vec<MemberId>>,
958
959 /// [`Track`]'s mute state.
960 ///
961 /// Muting and unmuting can be performed without adding/removing tracks
962 /// from transceivers, hence renegotiation is not required.
963 pub muted: Option<bool>,
964
965 /// [`EncodingParameters`] for the [`Track`] which should be patched.
966 pub encoding_parameters: Option<Vec<EncodingParameters>>,
967}
968
969/// Media exchange direction of a `Track`.
970#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
971pub enum MediaDirection {
972 /// `Track` is enabled on both receiver and sender sides.
973 SendRecv = 0,
974
975 /// `Track` is enabled on sender side only.
976 SendOnly = 1,
977
978 /// `Track` is enabled on receiver side only.
979 RecvOnly = 2,
980
981 /// `Track` is disabled on both sides.
982 Inactive = 3,
983}
984
985impl MediaDirection {
986 /// Indicates whether a `Track` is enabled on sender side only.
987 #[must_use]
988 pub const fn is_send_enabled(self) -> bool {
989 matches!(self, Self::SendRecv | Self::SendOnly)
990 }
991
992 /// Indicates whether a `Track` is enabled on receiver side only.
993 #[must_use]
994 pub const fn is_recv_enabled(self) -> bool {
995 matches!(self, Self::SendRecv | Self::RecvOnly)
996 }
997
998 /// Indicates whether a `Track` is enabled on both sender and receiver
999 /// sides.
1000 #[must_use]
1001 pub const fn is_enabled_general(self) -> bool {
1002 matches!(self, Self::SendRecv)
1003 }
1004}
1005
1006impl From<TrackPatchCommand> for TrackPatchEvent {
1007 fn from(from: TrackPatchCommand) -> Self {
1008 Self {
1009 id: from.id,
1010 muted: from.muted,
1011 media_direction: from.enabled.map(|enabled| {
1012 if enabled {
1013 MediaDirection::SendRecv
1014 } else {
1015 MediaDirection::Inactive
1016 }
1017 }),
1018 receivers: None,
1019 encoding_parameters: None,
1020 }
1021 }
1022}
1023
1024impl TrackPatchEvent {
1025 /// Returns a new empty [`TrackPatchEvent`] with the provided [`TrackId`].
1026 #[must_use]
1027 pub const fn new(id: TrackId) -> Self {
1028 Self {
1029 id,
1030 muted: None,
1031 media_direction: None,
1032 receivers: None,
1033 encoding_parameters: None,
1034 }
1035 }
1036
1037 /// Merges this [`TrackPatchEvent`] with the provided one.
1038 ///
1039 /// Does nothing if [`TrackId`] of this [`TrackPatchEvent`] and the
1040 /// provided [`TrackPatchEvent`] are different.
1041 pub fn merge(&mut self, another: &Self) {
1042 if self.id != another.id {
1043 return;
1044 }
1045
1046 if let Some(muted) = another.muted {
1047 self.muted = Some(muted);
1048 }
1049
1050 if let Some(direction) = another.media_direction {
1051 self.media_direction = Some(direction);
1052 }
1053
1054 if let Some(receivers) = &another.receivers {
1055 self.receivers = Some(receivers.clone());
1056 }
1057
1058 if let Some(encodings) = &another.encoding_parameters {
1059 self.encoding_parameters = Some(encodings.clone());
1060 }
1061 }
1062}
1063
1064/// Representation of [RTCIceServer][1] (item of `iceServers` field
1065/// from [RTCConfiguration][2]).
1066///
1067/// [1]: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer
1068/// [2]: https://developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration
1069#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1070pub struct IceServer {
1071 /// URLs of this [`IceServer`].
1072 pub urls: Vec<String>,
1073
1074 /// Optional username to authenticate on this [`IceServer`] with.
1075 #[serde(skip_serializing_if = "Option::is_none")]
1076 pub username: Option<String>,
1077
1078 /// Optional secret to authenticate on this [`IceServer`] with.
1079 #[serde(skip_serializing_if = "Option::is_none")]
1080 pub credential: Option<IcePassword>,
1081}
1082
1083/// Possible directions of a [`Track`].
1084#[cfg_attr(feature = "client", derive(Deserialize))]
1085#[cfg_attr(feature = "server", derive(Serialize))]
1086#[derive(Clone, Debug, Eq, PartialEq)]
1087// TODO: Use different struct without mids in PeerUpdated event.
1088pub enum Direction {
1089 /// Outgoing direction.
1090 Send {
1091 /// IDs of the `Member`s who should receive this outgoing [`Track`].
1092 receivers: Vec<MemberId>,
1093
1094 /// [Media stream "identification-tag"] of this outgoing [`Track`].
1095 ///
1096 /// [0]: https://w3.org/TR/webrtc#dfn-media-stream-identification-tag
1097 mid: Option<String>,
1098 },
1099
1100 /// Incoming direction.
1101 Recv {
1102 /// IDs of the `Member` this incoming [`Track`] is received from.
1103 sender: MemberId,
1104
1105 /// [Media stream "identification-tag"] of this incoming [`Track`].
1106 ///
1107 /// [0]: https://w3.org/TR/webrtc#dfn-media-stream-identification-tag
1108 mid: Option<String>,
1109 },
1110}
1111
1112/// Possible media types of a [`Track`].
1113#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1114pub enum MediaType {
1115 /// Audio [`Track`].
1116 Audio(AudioSettings),
1117
1118 /// Video [`Track`].
1119 Video(VideoSettings),
1120}
1121
1122impl MediaType {
1123 /// Indicates whether this [`MediaType`] is required to call starting.
1124 #[must_use]
1125 pub const fn required(&self) -> bool {
1126 match self {
1127 Self::Audio(audio) => audio.required,
1128 Self::Video(video) => video.required,
1129 }
1130 }
1131}
1132
1133/// Settings of an audio [`Track`].
1134#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
1135pub struct AudioSettings {
1136 /// Importance of the audio.
1137 ///
1138 /// If `false` then audio may be not published.
1139 pub required: bool,
1140}
1141
1142/// Settings of a video [`Track`].
1143#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1144pub struct VideoSettings {
1145 /// Importance of the video.
1146 ///
1147 /// If `false` then video may be not published.
1148 pub required: bool,
1149
1150 /// Source kind of these [`VideoSettings`].
1151 pub source_kind: MediaSourceKind,
1152
1153 /// [`EncodingParameters`] of these [`VideoSettings`].
1154 pub encoding_parameters: Vec<EncodingParameters>,
1155}
1156
1157/// Possible media sources of a video [`Track`].
1158#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
1159pub enum MediaSourceKind {
1160 /// Media is sourced by some media device (webcam or microphone).
1161 Device,
1162
1163 /// Media is obtained with screen-capture.
1164 Display,
1165}
1166
1167/// [Scalability mode] preference for [SVC (Scalable Video Coding)][SVC].
1168///
1169/// In [SVC], the scalability is typically defined in terms of layers (L) and
1170/// temporal (T) and spatial (S) levels.
1171///
1172/// The "L" part refers to the number of layers used in the encoding. Each layer
1173/// contains different information about the video, with higher layers typically
1174/// containing more detail or higher quality representations of the video.
1175///
1176/// The "T" part refers to temporal scalability layers count. Temporal
1177/// scalability allows for different frame rates to be encoded within the same
1178/// video stream, which can be useful for adaptive streaming or supporting
1179/// devices with varying display capabilities.
1180///
1181/// [SVC]: https://webrtcglossary.com/svc
1182/// [0]: https://w3.org/TR/webrtc-svc#scalabilitymodes*
1183#[derive(
1184 Clone, Copy, Debug, Deserialize, Display, Eq, PartialEq, Serialize,
1185)]
1186pub enum ScalabilityMode {
1187 /// [L1T1] mode.
1188 ///
1189 /// [L1T1]: https://w3.org/TR/webrtc-svc#L1T1*
1190 #[display("L1T1")]
1191 L1T1,
1192
1193 /// [L1T2] mode.
1194 ///
1195 /// [L1T2]: https://w3.org/TR/webrtc-svc#L1T2*
1196 #[display("L1T2")]
1197 L1T2,
1198
1199 /// [L1T3] mode.
1200 ///
1201 /// [L1T3]: https://w3.org/TR/webrtc-svc#L1T3*
1202 #[display("L1T3")]
1203 L1T3,
1204
1205 /// [L2T1] mode.
1206 ///
1207 /// [L2T1]: https://w3.org/TR/webrtc-svc#L2T1*
1208 #[display("L2T1")]
1209 L2T1,
1210
1211 /// [L2T2] mode.
1212 ///
1213 /// [L2T2]: https://w3.org/TR/webrtc-svc#L2T2*
1214 #[display("L2T2")]
1215 L2T2,
1216
1217 /// [L2T3] mode.
1218 ///
1219 /// [L2T3]: https://w3.org/TR/webrtc-svc#L2T3*
1220 #[display("L2T3")]
1221 L2T3,
1222
1223 /// [L3T1] mode.
1224 ///
1225 /// [L3T1]: https://w3.org/TR/webrtc-svc#L3T1*
1226 #[display("L3T1")]
1227 L3T1,
1228
1229 /// [L3T2] mode.
1230 ///
1231 /// [L3T2]: https://w3.org/TR/webrtc-svc#L3T2*
1232 #[display("L3T2")]
1233 L3T2,
1234
1235 /// [L3T3] mode.
1236 ///
1237 /// [L3T3]: https://w3.org/TR/webrtc-svc#L3T3*
1238 #[display("L3T3")]
1239 L3T3,
1240
1241 /// [S2T1] mode.
1242 ///
1243 /// [S2T1]: https://w3.org/TR/webrtc-svc#S2T1*
1244 #[display("S2T1")]
1245 S2T1,
1246
1247 /// [S2T2] mode.
1248 ///
1249 /// [S2T2]: https://w3.org/TR/webrtc-svc#S2T2*
1250 #[display("S2T2")]
1251 S2T2,
1252
1253 /// [S2T3] mode.
1254 ///
1255 /// [S2T3]: https://w3.org/TR/webrtc-svc#S2T3*
1256 #[display("S2T3")]
1257 S2T3,
1258
1259 /// [S3T1] mode.
1260 ///
1261 /// [S3T1]: https://w3.org/TR/webrtc-svc#S3T1*
1262 #[display("S3T1")]
1263 S3T1,
1264
1265 /// [S3T2] mode.
1266 ///
1267 /// [S3T2]: https://w3.org/TR/webrtc-svc#S3T2*
1268 #[display("S3T2")]
1269 S3T2,
1270
1271 /// [S3T3] mode.
1272 ///
1273 /// [S3T3]: https://w3.org/TR/webrtc-svc#S3T3*
1274 #[display("S3T3")]
1275 S3T3,
1276}
1277
1278/// Representation of an [RTCRtpEncodingParameters][0].
1279///
1280/// [0]: https://w3.org/TR/webrtc#dom-rtcrtpencodingparameters
1281#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
1282pub struct EncodingParameters {
1283 /// [RTP stream ID (RID)][RID] to be sent using the
1284 /// [RID header extension][0].
1285 ///
1286 /// [RID]: https://webrtcglossary.com/rid
1287 /// [0]: https://tools.ietf.org/html/rfc8852#section-3.3
1288 pub rid: String,
1289
1290 /// Indicator whether this encoding is actively being sent.
1291 ///
1292 /// Being `false` doesn't cause the [SSRC] to be removed, so an `RTCP BYE`
1293 /// is not sent.
1294 ///
1295 /// [SSRC]: https://webrtcglossary.com/ssrc
1296 pub active: bool,
1297
1298 /// Concrete [`Codec`] being used for this encoding's [RTP] stream.
1299 ///
1300 /// If [`None`], then any negotiated codec can be used.
1301 ///
1302 /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
1303 pub codec: Option<Codec>,
1304
1305 /// Maximum bitrate that can be used to send this encoding.
1306 ///
1307 /// User agent is free to allocate bandwidth between the encodings, as long
1308 /// as this value is not exceeded.
1309 pub max_bitrate: Option<u32>,
1310
1311 /// Factor for scaling down video's resolution in each dimension before
1312 /// sending.
1313 ///
1314 /// Only present for video encodings.
1315 ///
1316 /// For example, if this value is `2`, a video will be scaled down by a
1317 /// factor of 2 in each dimension, resulting in sending a video of one
1318 /// quarter the size. If this value is `1`, the video won't be affected.
1319 ///
1320 /// Must be greater than or equal to `1`.
1321 pub scale_resolution_down_by: Option<u8>,
1322
1323 /// [SVC (Scalable Video Coding)][SVC] scalability mode.
1324 ///
1325 /// [SVC]: https://webrtcglossary.com/svc
1326 pub scalability_mode: Option<ScalabilityMode>,
1327}
1328
1329/// Client capabilities (e.g. available codecs, platform).
1330#[cfg_attr(feature = "client", derive(Serialize))]
1331#[cfg_attr(feature = "server", derive(Deserialize))]
1332#[derive(Clone, Debug, Eq, Default, PartialEq)]
1333pub struct Capabilities {
1334 /// [`Codec`] capabilities for sending audio.
1335 pub audio_tx: Vec<Codec>,
1336
1337 /// [`Codec`] capabilities for receiving audio.
1338 pub audio_rx: Vec<Codec>,
1339
1340 /// [`Codec`] capabilities for sending video.
1341 pub video_tx: Vec<Codec>,
1342
1343 /// [`Codec`] capabilities for receiving video.
1344 pub video_rx: Vec<Codec>,
1345}
1346
1347/// Representation of an [RTCRtpCodec].
1348///
1349/// Provides information about codec objects.
1350///
1351/// [RTCRtpCodec]: https://w3.org/TR/webrtc#dom-rtcrtpcodec
1352#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1353pub struct Codec {
1354 /// [MIME] `type/subtype` of this [`Codec`].
1355 ///
1356 /// Valid values are listed in [IANA-RTP-2].
1357 ///
1358 /// [IANA-RTP-2]: https://tinyurl.com/IANA-RTP-2
1359 /// [MIME]: https://en.wikipedia.org/wiki/Media_type
1360 pub mime_type: String,
1361
1362 /// Clock rate expressed in [Hz (hertz)][hertz] of this [`Codec`].
1363 ///
1364 /// [hertz]: https://en.wikipedia.org/wiki/Hertz
1365 pub clock_rate: u32,
1366
1367 /// Maximum number of channels (`mono=1`, `stereo=2`), if any.
1368 pub channels: Option<u16>,
1369
1370 /// [`Codec`]-specific parameters that must be signaled to the remote party.
1371 ///
1372 /// Corresponds to `a=fmtp` parameters in [SDP].
1373 ///
1374 /// [SDP]: https://en.wikipedia.org/wiki/Session_Description_Protocol
1375 pub parameters: HashMap<String, String>,
1376}
1377
1378/// Estimated connection quality.
1379#[cfg_attr(feature = "client", derive(Deserialize))]
1380#[cfg_attr(feature = "server", derive(Serialize))]
1381#[derive(Clone, Copy, Debug, Display, Eq, Ord, PartialEq, PartialOrd)]
1382pub enum ConnectionQualityScore {
1383 /// Nearly all users dissatisfied.
1384 Poor = 1,
1385
1386 /// Many users dissatisfied.
1387 Low = 2,
1388
1389 /// Some users dissatisfied.
1390 Medium = 3,
1391
1392 /// Satisfied.
1393 High = 4,
1394}