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}