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}