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