Skip to main content

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}