script/dom/media/
mediastream.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use js::rust::HandleObject;
7use servo_media::streams::MediaStreamType;
8use servo_media::streams::registry::MediaStreamId;
9
10use crate::dom::bindings::cell::{DomRefCell, Ref};
11use crate::dom::bindings::codegen::Bindings::MediaStreamBinding::MediaStreamMethods;
12use crate::dom::bindings::error::Fallible;
13use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto_and_cx};
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::bindings::str::DOMString;
16use crate::dom::eventtarget::EventTarget;
17use crate::dom::globalscope::GlobalScope;
18use crate::dom::media::mediastreamtrack::MediaStreamTrack;
19use crate::dom::window::Window;
20
21#[dom_struct]
22pub(crate) struct MediaStream {
23    eventtarget: EventTarget,
24    tracks: DomRefCell<Vec<Dom<MediaStreamTrack>>>,
25}
26
27impl MediaStream {
28    pub(crate) fn new_inherited() -> MediaStream {
29        MediaStream {
30            eventtarget: EventTarget::new_inherited(),
31            tracks: DomRefCell::new(vec![]),
32        }
33    }
34
35    pub(crate) fn new(
36        cx: &mut js::context::JSContext,
37        global: &GlobalScope,
38    ) -> DomRoot<MediaStream> {
39        Self::new_with_proto(cx, global, None)
40    }
41
42    fn new_with_proto(
43        cx: &mut js::context::JSContext,
44        global: &GlobalScope,
45        proto: Option<HandleObject>,
46    ) -> DomRoot<MediaStream> {
47        reflect_dom_object_with_proto_and_cx(
48            Box::new(MediaStream::new_inherited()),
49            global,
50            proto,
51            cx,
52        )
53    }
54
55    pub(crate) fn new_single(
56        cx: &mut js::context::JSContext,
57        global: &GlobalScope,
58        id: MediaStreamId,
59        ty: MediaStreamType,
60    ) -> DomRoot<MediaStream> {
61        let this = Self::new(cx, global);
62        let track = MediaStreamTrack::new(cx, global, id, ty);
63        this.AddTrack(&track);
64        this
65    }
66
67    pub(crate) fn get_tracks(&self) -> Ref<'_, [Dom<MediaStreamTrack>]> {
68        Ref::map(self.tracks.borrow(), |tracks| &**tracks)
69    }
70
71    pub(crate) fn add_track(&self, track: &MediaStreamTrack) {
72        self.tracks.borrow_mut().push(Dom::from_ref(track))
73    }
74}
75
76impl MediaStreamMethods<crate::DomTypeHolder> for MediaStream {
77    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-constructor>
78    fn Constructor(
79        cx: &mut js::context::JSContext,
80        global: &Window,
81        proto: Option<HandleObject>,
82    ) -> Fallible<DomRoot<MediaStream>> {
83        Ok(MediaStream::new_with_proto(cx, &global.global(), proto))
84    }
85
86    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-constructor>
87    fn Constructor_(
88        cx: &mut js::context::JSContext,
89        _: &Window,
90        proto: Option<HandleObject>,
91        stream: &MediaStream,
92    ) -> Fallible<DomRoot<MediaStream>> {
93        Ok(stream.clone_with_proto(cx, proto))
94    }
95
96    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-constructor>
97    fn Constructor__(
98        cx: &mut js::context::JSContext,
99        global: &Window,
100        proto: Option<HandleObject>,
101        tracks: Vec<DomRoot<MediaStreamTrack>>,
102    ) -> Fallible<DomRoot<MediaStream>> {
103        let new = MediaStream::new_with_proto(cx, &global.global(), proto);
104        for track in tracks {
105            // this is quadratic, but shouldn't matter much
106            // if this becomes a problem we can use a hash map
107            new.AddTrack(&track)
108        }
109        Ok(new)
110    }
111
112    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-gettracks>
113    fn GetTracks(&self) -> Vec<DomRoot<MediaStreamTrack>> {
114        self.tracks
115            .borrow()
116            .iter()
117            .map(|x| DomRoot::from_ref(&**x))
118            .collect()
119    }
120
121    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-getaudiotracks>
122    fn GetAudioTracks(&self) -> Vec<DomRoot<MediaStreamTrack>> {
123        self.tracks
124            .borrow()
125            .iter()
126            .filter(|x| x.ty() == MediaStreamType::Audio)
127            .map(|x| DomRoot::from_ref(&**x))
128            .collect()
129    }
130
131    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-getvideotracks>
132    fn GetVideoTracks(&self) -> Vec<DomRoot<MediaStreamTrack>> {
133        self.tracks
134            .borrow()
135            .iter()
136            .filter(|x| x.ty() == MediaStreamType::Video)
137            .map(|x| DomRoot::from_ref(&**x))
138            .collect()
139    }
140
141    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-gettrackbyid>
142    fn GetTrackById(&self, id: DOMString) -> Option<DomRoot<MediaStreamTrack>> {
143        self.tracks
144            .borrow()
145            .iter()
146            .find(|x| x.id().id().to_string() == id)
147            .map(|x| DomRoot::from_ref(&**x))
148    }
149
150    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-addtrack>
151    fn AddTrack(&self, track: &MediaStreamTrack) {
152        let existing = self.tracks.borrow().iter().any(|x| x == &track);
153
154        if existing {
155            return;
156        }
157        self.add_track(track)
158    }
159
160    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-removetrack>
161    fn RemoveTrack(&self, track: &MediaStreamTrack) {
162        self.tracks.borrow_mut().retain(|x| *x != track);
163    }
164
165    /// <https://w3c.github.io/mediacapture-main/#dom-mediastream-clone>
166    fn Clone(&self, cx: &mut js::context::JSContext) -> DomRoot<MediaStream> {
167        self.clone_with_proto(cx, None)
168    }
169}
170
171impl MediaStream {
172    fn clone_with_proto(
173        &self,
174        cx: &mut js::context::JSContext,
175        proto: Option<HandleObject>,
176    ) -> DomRoot<MediaStream> {
177        let new = MediaStream::new_with_proto(cx, &self.global(), proto);
178        for track in &*self.tracks.borrow() {
179            new.add_track(track)
180        }
181        new
182    }
183}