rtc/media_stream/mod.rs
1//! MediaStream API
2//!
3//! This module implements the Media Capture and Streams API as defined in the
4//! [W3C Media Capture and Streams specification](https://www.w3.org/TR/mediacapture-streams/).
5//!
6//! The API provides the means to access media streams from local or remote media devices,
7//! including audio and video tracks. Each [`MediaStream`] can contain multiple
8//! [`MediaStreamTrack`] objects representing individual media sources.
9//!
10//! # Core Concepts
11//!
12//! - **[`MediaStream`]**: A container for one or more [`MediaStreamTrack`] objects
13//! - **[`MediaStreamTrack`]**: Represents a single media track (audio or video)
14//! - **[`MediaTrackCapabilities`]**: The inherent capabilities of a track
15//! - **[`MediaTrackConstraints`]**: Constraints to apply to a track
16//! - **[`MediaStreamTrackState`]**: The lifecycle state of a track (live or ended)
17//! - **[`MediaTrackSettings`]**: Current settings of a track
18//! - **[`MediaTrackSupportConstraints`]**: Indicates which constraints are supported by the user agent
19//!
20//! # Examples
21//!
22//! ## Creating a media stream with tracks
23//!
24//! ```
25//! use rtc::media_stream::{MediaStream, MediaStreamId};
26//! use rtc::media_stream::MediaStreamTrack;
27//! use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
28//! use rtc::rtp_transceiver::rtp_sender::{RTCRtpEncodingParameters, RTCRtpCodingParameters};
29//!
30//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
31//! // Create audio track
32//! let audio_track = MediaStreamTrack::new(
33//! "stream-id".to_string(),
34//! "audio-track-id".to_string(),
35//! "Microphone".to_string(),
36//! RtpCodecKind::Audio,
37//! vec![RTCRtpEncodingParameters {
38//! rtp_coding_parameters: RTCRtpCodingParameters {
39//! ssrc: Some(12345),
40//! ..Default::default()
41//! },
42//! codec: RTCRtpCodec::default(),
43//! ..Default::default()
44//! }],
45//! );
46//!
47//! // Create video track
48//! let video_track = MediaStreamTrack::new(
49//! "stream-id".to_string(),
50//! "video-track-id".to_string(),
51//! "Camera".to_string(),
52//! RtpCodecKind::Video,
53//! vec![RTCRtpEncodingParameters {
54//! rtp_coding_parameters: RTCRtpCodingParameters {
55//! ssrc: Some(67890),
56//! ..Default::default()
57//! },
58//! codec: RTCRtpCodec::default(),
59//! ..Default::default()
60//! }],
61//! );
62//!
63//! // Create stream with both tracks
64//! let stream = MediaStream::new(
65//! "my-stream-id".to_string(),
66//! vec![audio_track, video_track],
67//! );
68//!
69//! assert_eq!(stream.stream_id(), "my-stream-id");
70//! assert!(stream.active());
71//! # Ok(())
72//! # }
73//! ```
74//!
75//! ## Filtering tracks by type
76//!
77//! ```
78//! # use rtc::media_stream::{MediaStream, MediaStreamId};
79//! # use rtc::media_stream::MediaStreamTrack;
80//! # use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
81//! # fn example(stream: MediaStream) {
82//! // Get all audio tracks
83//! for audio_track in stream.get_audio_tracks() {
84//! println!("Audio track: {}", audio_track.label());
85//! }
86//!
87//! // Get all video tracks
88//! for video_track in stream.get_video_tracks() {
89//! println!("Video track: {}", video_track.label());
90//! }
91//! # }
92//! ```
93//!
94//! ## Managing tracks
95//!
96//! ```
97//! # use rtc::media_stream::{MediaStream, MediaStreamId};
98//! # use rtc::media_stream::MediaStreamTrack;
99//! # use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
100//! # use rtc::rtp_transceiver::rtp_sender::{RTCRtpEncodingParameters, RTCRtpCodingParameters};
101//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
102//! let mut stream = MediaStream::new("stream-id".to_string(), vec![]);
103//!
104//! // Add a track
105//! let track = MediaStreamTrack::new(
106//! "stream-id".to_string(),
107//! "track-id".to_string(),
108//! "My Track".to_string(),
109//! RtpCodecKind::Audio,
110//! vec![RTCRtpEncodingParameters {
111//! rtp_coding_parameters: RTCRtpCodingParameters {
112//! ssrc: Some(12345),
113//! ..Default::default()
114//! },
115//! codec: RTCRtpCodec::default(),
116//! ..Default::default()
117//! }],
118//! );
119//! stream.add_track(track);
120//!
121//! // Retrieve track by ID
122//! if let Some(track) = stream.get_track_by_id(&"track-id".to_string()) {
123//! println!("Found track: {}", track.label());
124//! }
125//!
126//! // Remove track
127//! let removed_track = stream.remove_track(&"track-id".to_string());
128//! assert!(removed_track.is_some());
129//! # Ok(())
130//! # }
131//! ```
132//!
133//! # Specifications
134//!
135//! - [W3C Media Capture and Streams](https://www.w3.org/TR/mediacapture-streams/)
136//! - [W3C MediaStream](https://www.w3.org/TR/mediacapture-streams/#mediastream)
137//! - [W3C MediaStreamTrack](https://www.w3.org/TR/mediacapture-streams/#mediastreamtrack)
138//! - [MDN MediaStream API](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream)
139
140pub(crate) mod track;
141pub(crate) mod track_capabilities;
142pub(crate) mod track_constraints;
143pub(crate) mod track_settings;
144pub(crate) mod track_state;
145pub(crate) mod track_supported_constraints;
146
147use crate::rtp_transceiver::rtp_sender::RtpCodecKind;
148use std::collections::HashMap;
149
150pub use track::{MediaStreamTrack, MediaStreamTrackId};
151pub use track_capabilities::MediaTrackCapabilities;
152pub use track_constraints::{MediaTrackConstraintSet, MediaTrackConstraints};
153pub use track_settings::MediaTrackSettings;
154pub use track_state::MediaStreamTrackState;
155pub use track_supported_constraints::MediaTrackSupportConstraints;
156
157/// Unique identifier for a media stream.
158///
159/// As defined in [MediaStream.id](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-id).
160pub type MediaStreamId = String;
161
162/// Represents a stream of media content.
163///
164/// A `MediaStream` is a collection of zero or more [`MediaStreamTrack`] objects,
165/// representing audio or video tracks. Each stream has a unique identifier and can
166/// be in an active or inactive state.
167///
168/// # Specification
169///
170/// See [MediaStream](https://www.w3.org/TR/mediacapture-streams/#mediastream) in the
171/// W3C Media Capture and Streams specification.
172///
173/// # Examples
174///
175/// ```
176/// use rtc::media_stream::{MediaStream, MediaStreamId};
177/// use rtc::media_stream::MediaStreamTrack;
178/// use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
179/// use rtc::rtp_transceiver::rtp_sender::{RTCRtpEncodingParameters, RTCRtpCodingParameters};
180///
181/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
182/// let track = MediaStreamTrack::new(
183/// "stream-id".to_string(),
184/// "track-id".to_string(),
185/// "My Track".to_string(),
186/// RtpCodecKind::Audio,
187/// vec![RTCRtpEncodingParameters {
188/// rtp_coding_parameters: RTCRtpCodingParameters {
189/// ssrc: Some(12345),
190/// ..Default::default()
191/// },
192/// codec: RTCRtpCodec::default(),
193/// ..Default::default()
194/// }],
195/// );
196///
197/// let stream = MediaStream::new("my-stream".to_string(), vec![track]);
198/// assert_eq!(stream.stream_id(), "my-stream");
199/// # Ok(())
200/// # }
201/// ```
202#[derive(Default, Debug, Clone)]
203pub struct MediaStream {
204 stream_id: MediaStreamId,
205 tracks: HashMap<MediaStreamTrackId, MediaStreamTrack>,
206 active: bool,
207}
208
209impl MediaStream {
210 /// Creates a new media stream with the given ID and tracks.
211 ///
212 /// # Parameters
213 ///
214 /// * `stream_id` - A unique identifier for this stream
215 /// * `tracks` - A vector of tracks to add to the stream
216 ///
217 /// # Specification
218 ///
219 /// See [MediaStream constructor](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-constructor).
220 ///
221 /// # Examples
222 ///
223 /// ```
224 /// use rtc::media_stream::{MediaStream, MediaStreamId};
225 /// use rtc::media_stream::MediaStreamTrack;
226 /// use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
227 /// use rtc::rtp_transceiver::rtp_sender::{RTCRtpEncodingParameters, RTCRtpCodingParameters};
228 ///
229 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
230 /// let track = MediaStreamTrack::new(
231 /// "stream-1".to_string(),
232 /// "track-1".to_string(),
233 /// "Microphone".to_string(),
234 /// RtpCodecKind::Audio,
235 /// vec![RTCRtpEncodingParameters {
236 /// rtp_coding_parameters: RTCRtpCodingParameters {
237 /// ssrc: Some(12345),
238 /// ..Default::default()
239 /// },
240 /// codec: RTCRtpCodec::default(),
241 /// ..Default::default()
242 /// }],
243 /// );
244 ///
245 /// let stream = MediaStream::new("stream-1".to_string(), vec![track]);
246 /// # Ok(())
247 /// # }
248 /// ```
249 pub fn new(stream_id: MediaStreamId, tracks: Vec<MediaStreamTrack>) -> Self {
250 Self {
251 stream_id,
252 tracks: tracks
253 .into_iter()
254 .map(|track| (track.stream_id().to_string(), track))
255 .collect(),
256 active: true,
257 }
258 }
259
260 /// Returns the unique identifier of this stream.
261 ///
262 /// The identifier is a 36-character Universally Unique Identifier (UUID) generated
263 /// when the stream is created.
264 ///
265 /// # Specification
266 ///
267 /// See [MediaStream.id](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-id).
268 pub fn stream_id(&self) -> &MediaStreamId {
269 &self.stream_id
270 }
271
272 /// Returns whether this stream is active.
273 ///
274 /// A stream is active if it has at least one track that is not in the "ended" state.
275 ///
276 /// # Specification
277 ///
278 /// See [MediaStream.active](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-active).
279 pub fn active(&self) -> bool {
280 self.active
281 }
282
283 /// Returns an iterator over all audio tracks in this stream.
284 ///
285 /// # Specification
286 ///
287 /// See [MediaStream.getAudioTracks()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-getaudiotracks).
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// # use rtc::media_stream::{MediaStream, MediaStreamId};
293 /// # use rtc::media_stream::MediaStreamTrack;
294 /// # use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
295 /// # fn example(stream: MediaStream) {
296 /// for track in stream.get_audio_tracks() {
297 /// println!("Audio track: {} ({})", track.label(), track.track_id());
298 /// }
299 /// # }
300 /// ```
301 pub fn get_audio_tracks(&self) -> impl Iterator<Item = &MediaStreamTrack> {
302 self.tracks
303 .values()
304 .filter(|track| track.kind() == RtpCodecKind::Audio)
305 }
306
307 /// Returns a mutable iterator over all audio tracks in this stream.
308 ///
309 /// # Specification
310 ///
311 /// See [MediaStream.getAudioTracks()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-getaudiotracks).
312 pub fn get_audio_tracks_mut(&mut self) -> impl Iterator<Item = &mut MediaStreamTrack> {
313 self.tracks
314 .values_mut()
315 .filter(|track| track.kind() == RtpCodecKind::Audio)
316 }
317
318 /// Returns an iterator over all video tracks in this stream.
319 ///
320 /// # Specification
321 ///
322 /// See [MediaStream.getVideoTracks()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-getvideotracks).
323 ///
324 /// # Examples
325 ///
326 /// ```
327 /// # use rtc::media_stream::{MediaStream, MediaStreamId};
328 /// # use rtc::media_stream::MediaStreamTrack;
329 /// # use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
330 /// # fn example(stream: MediaStream) {
331 /// for track in stream.get_video_tracks() {
332 /// println!("Video track: {} ({})", track.label(), track.track_id());
333 /// }
334 /// # }
335 /// ```
336 pub fn get_video_tracks(&self) -> impl Iterator<Item = &MediaStreamTrack> {
337 self.tracks
338 .values()
339 .filter(|track| track.kind() == RtpCodecKind::Video)
340 }
341
342 /// Returns a mutable iterator over all video tracks in this stream.
343 ///
344 /// # Specification
345 ///
346 /// See [MediaStream.getVideoTracks()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-getvideotracks).
347 pub fn get_video_tracks_mut(&mut self) -> impl Iterator<Item = &mut MediaStreamTrack> {
348 self.tracks
349 .values_mut()
350 .filter(|track| track.kind() == RtpCodecKind::Video)
351 }
352
353 /// Returns an iterator over all tracks in this stream.
354 ///
355 /// # Specification
356 ///
357 /// See [MediaStream.getTracks()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-gettracks).
358 ///
359 /// # Examples
360 ///
361 /// ```
362 /// # use rtc::media_stream::{MediaStream, MediaStreamId};
363 /// # use rtc::media_stream::MediaStreamTrack;
364 /// # use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
365 /// # fn example(stream: MediaStream) {
366 /// println!("Stream has {} tracks", stream.get_tracks().count());
367 /// # }
368 /// ```
369 pub fn get_tracks(&self) -> impl Iterator<Item = &MediaStreamTrack> {
370 self.tracks.values()
371 }
372
373 /// Returns a mutable iterator over all tracks in this stream.
374 ///
375 /// # Specification
376 ///
377 /// See [MediaStream.getTracks()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-gettracks).
378 pub fn get_tracks_mut(&mut self) -> impl Iterator<Item = &mut MediaStreamTrack> {
379 self.tracks.values_mut()
380 }
381
382 /// Returns a reference to the track with the specified ID, if it exists.
383 ///
384 /// # Parameters
385 ///
386 /// * `track_id` - The unique identifier of the track to retrieve
387 ///
388 /// # Returns
389 ///
390 /// Returns `Some(&MediaStreamTrack)` if a track with the given ID exists,
391 /// or `None` otherwise.
392 ///
393 /// # Specification
394 ///
395 /// See [MediaStream.getTrackById()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-gettrackbyid).
396 ///
397 /// # Examples
398 ///
399 /// ```
400 /// # use rtc::media_stream::{MediaStream, MediaStreamId};
401 /// # use rtc::media_stream::MediaStreamTrack;
402 /// # use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
403 /// # fn example(stream: MediaStream) {
404 /// if let Some(track) = stream.get_track_by_id(&"track-id".to_string()) {
405 /// println!("Found track: {}", track.label());
406 /// } else {
407 /// println!("Track not found");
408 /// }
409 /// # }
410 /// ```
411 pub fn get_track_by_id(&self, track_id: &MediaStreamTrackId) -> Option<&MediaStreamTrack> {
412 self.tracks.get(track_id)
413 }
414
415 /// Returns a mutable reference to the track with the specified ID, if it exists.
416 ///
417 /// # Parameters
418 ///
419 /// * `track_id` - The unique identifier of the track to retrieve
420 ///
421 /// # Returns
422 ///
423 /// Returns `Some(&mut MediaStreamTrack)` if a track with the given ID exists,
424 /// or `None` otherwise.
425 ///
426 /// # Specification
427 ///
428 /// See [MediaStream.getTrackById()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-gettrackbyid).
429 pub fn get_track_by_id_mut(
430 &mut self,
431 track_id: &MediaStreamTrackId,
432 ) -> Option<&mut MediaStreamTrack> {
433 self.tracks.get_mut(track_id)
434 }
435
436 /// Adds a track to this stream.
437 ///
438 /// If a track with the same ID already exists, it will be replaced.
439 ///
440 /// # Parameters
441 ///
442 /// * `track` - The track to add to the stream
443 ///
444 /// # Specification
445 ///
446 /// See [MediaStream.addTrack()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-addtrack).
447 ///
448 /// # Examples
449 ///
450 /// ```
451 /// # use rtc::media_stream::{MediaStream, MediaStreamId};
452 /// # use rtc::media_stream::MediaStreamTrack;
453 /// # use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
454 /// # use rtc::rtp_transceiver::rtp_sender::{RTCRtpEncodingParameters, RTCRtpCodingParameters};
455 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
456 /// let mut stream = MediaStream::new("stream-id".to_string(), vec![]);
457 ///
458 /// let track = MediaStreamTrack::new(
459 /// "stream-id".to_string(),
460 /// "track-id".to_string(),
461 /// "Microphone".to_string(),
462 /// RtpCodecKind::Audio,
463 /// vec![RTCRtpEncodingParameters {
464 /// rtp_coding_parameters: RTCRtpCodingParameters {
465 /// ssrc: Some(12345),
466 /// ..Default::default()
467 /// },
468 /// codec: RTCRtpCodec::default(),
469 /// ..Default::default()
470 /// }],
471 /// );
472 ///
473 /// stream.add_track(track);
474 /// assert_eq!(stream.get_tracks().count(), 1);
475 /// # Ok(())
476 /// # }
477 /// ```
478 pub fn add_track(&mut self, track: MediaStreamTrack) {
479 self.tracks.insert(track.track_id().to_string(), track);
480 }
481
482 /// Removes a track from this stream and returns it.
483 ///
484 /// # Parameters
485 ///
486 /// * `track_id` - The unique identifier of the track to remove
487 ///
488 /// # Returns
489 ///
490 /// Returns `Some(MediaStreamTrack)` if the track was found and removed,
491 /// or `None` if no track with the given ID exists.
492 ///
493 /// # Specification
494 ///
495 /// See [MediaStream.removeTrack()](https://www.w3.org/TR/mediacapture-streams/#dom-mediastream-removetrack).
496 ///
497 /// # Examples
498 ///
499 /// ```
500 /// # use rtc::media_stream::{MediaStream, MediaStreamId};
501 /// # use rtc::media_stream::MediaStreamTrack;
502 /// # use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};
503 /// # use rtc::rtp_transceiver::rtp_sender::{RTCRtpEncodingParameters, RTCRtpCodingParameters};
504 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
505 /// # let mut stream = MediaStream::new("stream-id".to_string(), vec![]);
506 /// # let track = MediaStreamTrack::new(
507 /// # "stream-id".to_string(), "track-id".to_string(), "Microphone".to_string(),
508 /// # RtpCodecKind::Audio, vec![RTCRtpEncodingParameters {
509 /// # rtp_coding_parameters: RTCRtpCodingParameters {
510 /// # ssrc: Some(12345), ..Default::default()
511 /// # },
512 /// # codec: RTCRtpCodec::default(),
513 /// # ..Default::default()
514 /// # }],
515 /// # );
516 /// # stream.add_track(track);
517 /// let removed_track = stream.remove_track(&"track-id".to_string());
518 /// assert!(removed_track.is_some());
519 /// # Ok(())
520 /// # }
521 /// ```
522 pub fn remove_track(&mut self, track_id: &MediaStreamTrackId) -> Option<MediaStreamTrack> {
523 self.tracks.remove(track_id)
524 }
525}