7508a03254050b360ad6257c6133d920915ebaca
[WebKit-https.git] / Source / WebCore / Modules / mediastream / MediaStream.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2011, 2012, 2015 Ericsson AB. All rights reserved.
4  * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
5  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "MediaStream.h"
30
31 #if ENABLE(MEDIA_STREAM)
32
33 #include "Document.h"
34 #include "Event.h"
35 #include "EventNames.h"
36 #include "ExceptionCode.h"
37 #include "MediaStreamRegistry.h"
38 #include "MediaStreamTrackEvent.h"
39 #include "Page.h"
40 #include "RealtimeMediaSource.h"
41 #include "URL.h"
42
43 namespace WebCore {
44
45 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context)
46 {
47     return MediaStream::create(context, MediaStreamPrivate::create(Vector<RefPtr<MediaStreamTrackPrivate>>()));
48 }
49
50 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, MediaStream& stream)
51 {
52     return adoptRef(*new MediaStream(context, stream.getTracks()));
53 }
54
55 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
56 {
57     return adoptRef(*new MediaStream(context, tracks));
58 }
59
60 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, RefPtr<MediaStreamPrivate>&& streamPrivate)
61 {
62     return adoptRef(*new MediaStream(context, WTFMove(streamPrivate)));
63 }
64
65 MediaStream::MediaStream(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
66     : ContextDestructionObserver(&context)
67     , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
68 {
69     // This constructor preserves MediaStreamTrack instances and must be used by calls originating
70     // from the JavaScript MediaStream constructor.
71     MediaStreamTrackPrivateVector trackPrivates;
72     trackPrivates.reserveCapacity(tracks.size());
73
74     for (auto& track : tracks) {
75         track->addObserver(this);
76         m_trackSet.add(track->id(), track);
77         trackPrivates.append(&track->privateTrack());
78     }
79
80     m_private = MediaStreamPrivate::create(trackPrivates);
81     setIsActive(m_private->active());
82     m_private->addObserver(*this);
83     MediaStreamRegistry::shared().registerStream(*this);
84     document()->addAudioProducer(this);
85 }
86
87 MediaStream::MediaStream(ScriptExecutionContext& context, RefPtr<MediaStreamPrivate>&& streamPrivate)
88     : ContextDestructionObserver(&context)
89     , m_private(streamPrivate)
90     , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
91 {
92     ASSERT(m_private);
93     setIsActive(m_private->active());
94     m_private->addObserver(*this);
95     MediaStreamRegistry::shared().registerStream(*this);
96
97     for (auto& trackPrivate : m_private->tracks()) {
98         auto track = MediaStreamTrack::create(context, *trackPrivate);
99         track->addObserver(this);
100         m_trackSet.add(track->id(), WTFMove(track));
101     }
102     document()->addAudioProducer(this);
103 }
104
105 MediaStream::~MediaStream()
106 {
107     // Set isActive to false immediately so an callbacks triggered by shutting down, e.g.
108     // mediaState(), are short circuited.
109     m_isActive = false;
110     MediaStreamRegistry::shared().unregisterStream(*this);
111     m_private->removeObserver(*this);
112     for (auto& track : m_trackSet.values())
113         track->removeObserver(this);
114     if (Document* document = this->document())
115         document->removeAudioProducer(this);
116 }
117
118 RefPtr<MediaStream> MediaStream::clone()
119 {
120     MediaStreamTrackVector clonedTracks;
121     clonedTracks.reserveCapacity(m_trackSet.size());
122
123     for (auto& track : m_trackSet.values())
124         clonedTracks.append(track->clone());
125
126     return MediaStream::create(*scriptExecutionContext(), clonedTracks);
127 }
128
129 void MediaStream::addTrack(MediaStreamTrack& track)
130 {
131     if (!internalAddTrack(track, StreamModifier::DomAPI))
132         return;
133
134     for (auto& observer : m_observers)
135         observer->didAddOrRemoveTrack();
136 }
137
138 void MediaStream::removeTrack(MediaStreamTrack& track)
139 {
140     if (!internalRemoveTrack(track.id(), StreamModifier::DomAPI))
141         return;
142
143     for (auto& observer : m_observers)
144         observer->didAddOrRemoveTrack();
145 }
146
147 MediaStreamTrack* MediaStream::getTrackById(String id)
148 {
149     auto it = m_trackSet.find(id);
150     if (it != m_trackSet.end())
151         return it->value.get();
152
153     return nullptr;
154 }
155
156 MediaStreamTrackVector MediaStream::getAudioTracks() const
157 {
158     return trackVectorForType(RealtimeMediaSource::Audio);
159 }
160
161 MediaStreamTrackVector MediaStream::getVideoTracks() const
162 {
163     return trackVectorForType(RealtimeMediaSource::Video);
164 }
165
166 MediaStreamTrackVector MediaStream::getTracks() const
167 {
168     MediaStreamTrackVector tracks;
169     tracks.reserveCapacity(m_trackSet.size());
170     copyValuesToVector(m_trackSet, tracks);
171
172     return tracks;
173 }
174
175 void MediaStream::contextDestroyed()
176 {
177     ContextDestructionObserver::contextDestroyed();
178 }
179
180 void MediaStream::trackDidEnd()
181 {
182     m_private->updateActiveState(MediaStreamPrivate::NotifyClientOption::Notify);
183 }
184
185 void MediaStream::activeStatusChanged()
186 {
187     // Schedule the active state change and event dispatch since this callback may be called
188     // synchronously from the DOM API (e.g. as a result of addTrack()).
189     scheduleActiveStateChange();
190 }
191
192 void MediaStream::didAddTrack(MediaStreamTrackPrivate& trackPrivate)
193 {
194     ScriptExecutionContext* context = scriptExecutionContext();
195     if (!context)
196         return;
197
198     if (!getTrackById(trackPrivate.id()))
199         internalAddTrack(MediaStreamTrack::create(*context, trackPrivate), StreamModifier::Platform);
200 }
201
202 void MediaStream::didRemoveTrack(MediaStreamTrackPrivate& trackPrivate)
203 {
204     internalRemoveTrack(trackPrivate.id(), StreamModifier::Platform);
205 }
206
207 bool MediaStream::internalAddTrack(Ref<MediaStreamTrack>&& trackToAdd, StreamModifier streamModifier)
208 {
209     auto result = m_trackSet.add(trackToAdd->id(), WTFMove(trackToAdd));
210     if (!result.isNewEntry)
211         return false;
212
213     ASSERT(result.iterator->value);
214     auto& track = *result.iterator->value;
215     track.addObserver(this);
216
217     if (streamModifier == StreamModifier::DomAPI)
218         m_private->addTrack(&track.privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
219     else
220         dispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, &track));
221
222     return true;
223 }
224
225 bool MediaStream::internalRemoveTrack(const String& trackId, StreamModifier streamModifier)
226 {
227     auto track = m_trackSet.take(trackId);
228     if (!track)
229         return false;
230
231     track->removeObserver(this);
232
233     if (streamModifier == StreamModifier::DomAPI)
234         m_private->removeTrack(track->privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
235     else
236         dispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, WTFMove(track)));
237
238     return true;
239 }
240
241 void MediaStream::setIsActive(bool active)
242 {
243     if (m_isActive == active)
244         return;
245
246     m_isActive = active;
247     statusDidChange();
248 }
249
250 void MediaStream::pageMutedStateDidChange()
251 {
252     if (!m_isActive)
253         return;
254
255     Document* document = this->document();
256     if (!document)
257         return;
258
259     bool pageMuted = document->page()->isMediaCaptureMuted();
260     if (m_externallyMuted == pageMuted)
261         return;
262
263     m_externallyMuted = pageMuted;
264     if (pageMuted)
265         m_private->stopProducingData();
266     else
267         m_private->startProducingData();
268 }
269
270 MediaProducer::MediaStateFlags MediaStream::mediaState() const
271 {
272     MediaStateFlags state = IsNotPlaying;
273
274     if (!m_isActive)
275         return state;
276
277     state |= HasMediaCaptureDevice;
278     if (m_private->isProducingData())
279         state |= HasActiveMediaCaptureDevice;
280
281     if (m_private->hasAudio() || m_private->hasVideo())
282         state |= HasAudioOrVideo;
283
284     return state;
285 }
286
287 void MediaStream::statusDidChange()
288 {
289     if (Document* document = this->document()) {
290         if (m_isActive)
291             document->setHasActiveMediaStreamTrack();
292         document->updateIsPlayingMedia();
293     }
294 }
295
296 void MediaStream::characteristicsChanged()
297 {
298     bool muted = m_private->muted();
299     if (m_isMuted != muted) {
300         m_isMuted = muted;
301         statusDidChange();
302     }
303 }
304
305 void MediaStream::scheduleActiveStateChange()
306 {
307     bool active = false;
308     for (auto& track : m_trackSet.values()) {
309         if (!track->ended()) {
310             active = true;
311             break;
312         }
313     }
314
315     if (m_isActive == active)
316         return;
317
318     setIsActive(active);
319
320     const AtomicString& eventName = m_isActive ? eventNames().inactiveEvent : eventNames().activeEvent;
321     m_scheduledActivityEvents.append(Event::create(eventName, false, false));
322
323     if (!m_activityEventTimer.isActive())
324         m_activityEventTimer.startOneShot(0);
325 }
326
327 void MediaStream::activityEventTimerFired()
328 {
329     Vector<Ref<Event>> events;
330     events.swap(m_scheduledActivityEvents);
331
332     for (auto& event : events)
333         dispatchEvent(event);
334 }
335
336 URLRegistry& MediaStream::registry() const
337 {
338     return MediaStreamRegistry::shared();
339 }
340
341 MediaStreamTrackVector MediaStream::trackVectorForType(RealtimeMediaSource::Type filterType) const
342 {
343     MediaStreamTrackVector tracks;
344     for (auto& track : m_trackSet.values()) {
345         if (track->source().type() == filterType)
346             tracks.append(track);
347     }
348
349     return tracks;
350 }
351
352 void MediaStream::addObserver(MediaStream::Observer* observer)
353 {
354     if (m_observers.find(observer) == notFound)
355         m_observers.append(observer);
356 }
357
358 void MediaStream::removeObserver(MediaStream::Observer* observer)
359 {
360     size_t pos = m_observers.find(observer);
361     if (pos != notFound)
362         m_observers.remove(pos);
363 }
364
365 Document* MediaStream::document() const
366 {
367     return downcast<Document>(scriptExecutionContext());
368 }
369
370 } // namespace WebCore
371
372 #endif // ENABLE(MEDIA_STREAM)