Skip to main content

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