99534f1bf7960599be7c005db7819b3fc17576af
[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-2016 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 "Frame.h"
37 #include "Logging.h"
38 #include "MediaStreamRegistry.h"
39 #include "MediaStreamTrackEvent.h"
40 #include "NetworkingContext.h"
41 #include "Page.h"
42 #include "RealtimeMediaSource.h"
43 #include "URL.h"
44
45 namespace WebCore {
46
47 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context)
48 {
49     return MediaStream::create(context, MediaStreamPrivate::create(Vector<RefPtr<MediaStreamTrackPrivate>>()));
50 }
51
52 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, MediaStream& stream)
53 {
54     return adoptRef(*new MediaStream(context, stream.getTracks()));
55 }
56
57 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
58 {
59     return adoptRef(*new MediaStream(context, tracks));
60 }
61
62 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, Ref<MediaStreamPrivate>&& streamPrivate)
63 {
64     return adoptRef(*new MediaStream(context, WTFMove(streamPrivate)));
65 }
66
67 static inline MediaStreamTrackPrivateVector createTrackPrivateVector(const MediaStreamTrackVector& tracks)
68 {
69     MediaStreamTrackPrivateVector trackPrivates;
70     trackPrivates.reserveCapacity(tracks.size());
71     for (auto& track : tracks)
72         trackPrivates.append(&track->privateTrack());
73     return trackPrivates;
74 }
75
76 MediaStream::MediaStream(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
77     : ActiveDOMObject(&context)
78     , m_private(MediaStreamPrivate::create(createTrackPrivateVector(tracks)))
79     , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
80     , m_mediaSession(PlatformMediaSession::create(*this))
81 {
82     // This constructor preserves MediaStreamTrack instances and must be used by calls originating
83     // from the JavaScript MediaStream constructor.
84
85     for (auto& track : tracks) {
86         track->addObserver(*this);
87         m_trackSet.add(track->id(), track);
88     }
89
90     setIsActive(m_private->active());
91     m_private->addObserver(*this);
92     MediaStreamRegistry::shared().registerStream(*this);
93     suspendIfNeeded();
94 }
95
96 MediaStream::MediaStream(ScriptExecutionContext& context, Ref<MediaStreamPrivate>&& streamPrivate)
97     : ActiveDOMObject(&context)
98     , m_private(WTFMove(streamPrivate))
99     , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
100     , m_mediaSession(PlatformMediaSession::create(*this))
101 {
102     setIsActive(m_private->active());
103     if (document()->page() && document()->page()->isMediaCaptureMuted())
104         m_private->setCaptureTracksMuted(true);
105     m_private->addObserver(*this);
106     MediaStreamRegistry::shared().registerStream(*this);
107
108     for (auto& trackPrivate : m_private->tracks()) {
109         auto track = MediaStreamTrack::create(context, *trackPrivate);
110         track->addObserver(*this);
111         m_trackSet.add(track->id(), WTFMove(track));
112     }
113     suspendIfNeeded();
114 }
115
116 MediaStream::~MediaStream()
117 {
118     // Set isActive to false immediately so any callbacks triggered by shutting down, e.g.
119     // mediaState(), are short circuited.
120     m_isActive = false;
121     MediaStreamRegistry::shared().unregisterStream(*this);
122     m_private->removeObserver(*this);
123     for (auto& track : m_trackSet.values())
124         track->removeObserver(*this);
125     if (Document* document = this->document()) {
126         if (m_isWaitingUntilMediaCanStart)
127             document->removeMediaCanStartListener(this);
128     }
129 }
130
131 RefPtr<MediaStream> MediaStream::clone()
132 {
133     MediaStreamTrackVector clonedTracks;
134     clonedTracks.reserveInitialCapacity(m_trackSet.size());
135
136     for (auto& track : m_trackSet.values())
137         clonedTracks.uncheckedAppend(track->clone());
138
139     return MediaStream::create(*scriptExecutionContext(), clonedTracks);
140 }
141
142 void MediaStream::addTrack(MediaStreamTrack& track)
143 {
144     if (!internalAddTrack(track, StreamModifier::DomAPI))
145         return;
146
147     for (auto& observer : m_observers)
148         observer->didAddOrRemoveTrack();
149 }
150
151 void MediaStream::removeTrack(MediaStreamTrack& track)
152 {
153     if (!internalRemoveTrack(track.id(), StreamModifier::DomAPI))
154         return;
155
156     for (auto& observer : m_observers)
157         observer->didAddOrRemoveTrack();
158 }
159
160 MediaStreamTrack* MediaStream::getTrackById(String id)
161 {
162     auto it = m_trackSet.find(id);
163     if (it != m_trackSet.end())
164         return it->value.get();
165
166     return nullptr;
167 }
168
169 MediaStreamTrackVector MediaStream::getAudioTracks() const
170 {
171     return trackVectorForType(RealtimeMediaSource::Type::Audio);
172 }
173
174 MediaStreamTrackVector MediaStream::getVideoTracks() const
175 {
176     return trackVectorForType(RealtimeMediaSource::Type::Video);
177 }
178
179 MediaStreamTrackVector MediaStream::getTracks() const
180 {
181     MediaStreamTrackVector tracks;
182     tracks.reserveCapacity(m_trackSet.size());
183     copyValuesToVector(m_trackSet, tracks);
184
185     return tracks;
186 }
187
188 void MediaStream::trackDidEnd()
189 {
190     m_private->updateActiveState(MediaStreamPrivate::NotifyClientOption::Notify);
191 }
192
193 void MediaStream::activeStatusChanged()
194 {
195     // Schedule the active state change and event dispatch since this callback may be called
196     // synchronously from the DOM API (e.g. as a result of addTrack()).
197     scheduleActiveStateChange();
198 }
199
200 void MediaStream::didAddTrack(MediaStreamTrackPrivate& trackPrivate)
201 {
202     ScriptExecutionContext* context = scriptExecutionContext();
203     if (!context)
204         return;
205
206     if (!getTrackById(trackPrivate.id()))
207         internalAddTrack(MediaStreamTrack::create(*context, trackPrivate), StreamModifier::Platform);
208 }
209
210 void MediaStream::didRemoveTrack(MediaStreamTrackPrivate& trackPrivate)
211 {
212     internalRemoveTrack(trackPrivate.id(), StreamModifier::Platform);
213 }
214
215 void MediaStream::addTrackFromPlatform(Ref<MediaStreamTrack>&& track)
216 {
217     auto* privateTrack = &track->privateTrack();
218     internalAddTrack(WTFMove(track), StreamModifier::Platform);
219     m_private->addTrack(privateTrack, MediaStreamPrivate::NotifyClientOption::Notify);
220 }
221
222 bool MediaStream::internalAddTrack(Ref<MediaStreamTrack>&& trackToAdd, StreamModifier streamModifier)
223 {
224     auto result = m_trackSet.add(trackToAdd->id(), WTFMove(trackToAdd));
225     if (!result.isNewEntry)
226         return false;
227
228     ASSERT(result.iterator->value);
229     auto& track = *result.iterator->value;
230     track.addObserver(*this);
231
232     if (streamModifier == StreamModifier::DomAPI)
233         m_private->addTrack(&track.privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
234     else
235         dispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, &track));
236
237     return true;
238 }
239
240 bool MediaStream::internalRemoveTrack(const String& trackId, StreamModifier streamModifier)
241 {
242     auto track = m_trackSet.take(trackId);
243     if (!track)
244         return false;
245
246     track->removeObserver(*this);
247
248     if (streamModifier == StreamModifier::DomAPI)
249         m_private->removeTrack(track->privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
250     else
251         dispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, WTFMove(track)));
252
253     return true;
254 }
255
256 void MediaStream::setIsActive(bool active)
257 {
258     if (m_isActive == active)
259         return;
260
261     m_isActive = active;
262     statusDidChange();
263 }
264
265 void MediaStream::mediaCanStart(Document& document)
266 {
267     ASSERT_UNUSED(document, &document == this->document());
268     ASSERT(m_isWaitingUntilMediaCanStart);
269     if (m_isWaitingUntilMediaCanStart) {
270         m_isWaitingUntilMediaCanStart = false;
271         startProducingData();
272     }
273 }
274
275 void MediaStream::startProducingData()
276 {
277     Document* document = this->document();
278     if (!document || !document->page())
279         return;
280
281     // If we can't start a load right away, start it later.
282     if (!document->page()->canStartMedia()) {
283         LOG(Media, "MediaStream::startProducingData(%p) - not allowed to start in background, waiting", this);
284         if (m_isWaitingUntilMediaCanStart)
285             return;
286
287         m_isWaitingUntilMediaCanStart = true;
288         document->addMediaCanStartListener(this);
289         return;
290     }
291
292     if (m_isProducingData)
293         return;
294     m_isProducingData = true;
295
296     m_mediaSession->canProduceAudioChanged();
297     m_private->startProducingData();
298
299     if (document->page()->isMediaCaptureMuted())
300         m_private->setCaptureTracksMuted(true);
301 }
302
303 void MediaStream::stopProducingData()
304 {
305     if (!m_isProducingData)
306         return;
307     m_isProducingData = false;
308
309     m_mediaSession->canProduceAudioChanged();
310
311     m_private->stopProducingData();
312 }
313
314 void MediaStream::endCaptureTracks()
315 {
316     for (auto& track : m_trackSet.values()) {
317         if (track->isCaptureTrack())
318             track->stopTrack(MediaStreamTrack::StopMode::PostEvent);
319     }
320 }
321
322 MediaProducer::MediaStateFlags MediaStream::mediaState() const
323 {
324     MediaProducer::MediaStateFlags state = MediaProducer::IsNotPlaying;
325
326     if (!m_isActive || !document() || !document()->page())
327         return state;
328
329     for (const auto& track : m_trackSet.values())
330         state |= track->mediaState();
331
332     return state;
333 }
334
335 void MediaStream::statusDidChange()
336 {
337     m_mediaSession->canProduceAudioChanged();
338
339     if (Document* document = this->document()) {
340         if (m_isActive)
341             document->setHasActiveMediaStreamTrack();
342     }
343 }
344
345 void MediaStream::characteristicsChanged()
346 {
347     auto state = mediaState();
348     if (m_state != state) {
349         m_state = state;
350         statusDidChange();
351     }
352 }
353
354 void MediaStream::scheduleActiveStateChange()
355 {
356     bool active = false;
357     for (auto& track : m_trackSet.values()) {
358         if (!track->ended()) {
359             active = true;
360             break;
361         }
362     }
363
364     if (m_isActive == active)
365         return;
366
367     setIsActive(active);
368
369     const AtomicString& eventName = m_isActive ? eventNames().inactiveEvent : eventNames().activeEvent;
370     m_scheduledActivityEvents.append(Event::create(eventName, false, false));
371
372     if (!m_activityEventTimer.isActive())
373         m_activityEventTimer.startOneShot(0_s);
374 }
375
376 void MediaStream::activityEventTimerFired()
377 {
378     Vector<Ref<Event>> events;
379     events.swap(m_scheduledActivityEvents);
380
381     for (auto& event : events)
382         dispatchEvent(event);
383 }
384
385 URLRegistry& MediaStream::registry() const
386 {
387     return MediaStreamRegistry::shared();
388 }
389
390 MediaStreamTrackVector MediaStream::trackVectorForType(RealtimeMediaSource::Type filterType) const
391 {
392     MediaStreamTrackVector tracks;
393     for (auto& track : m_trackSet.values()) {
394         if (track->source().type() == filterType)
395             tracks.append(track);
396     }
397
398     return tracks;
399 }
400
401 void MediaStream::addObserver(MediaStream::Observer* observer)
402 {
403     if (m_observers.find(observer) == notFound)
404         m_observers.append(observer);
405 }
406
407 void MediaStream::removeObserver(MediaStream::Observer* observer)
408 {
409     size_t pos = m_observers.find(observer);
410     if (pos != notFound)
411         m_observers.remove(pos);
412 }
413
414 Document* MediaStream::document() const
415 {
416     return downcast<Document>(scriptExecutionContext());
417 }
418
419 PlatformMediaSession::MediaType MediaStream::mediaType() const
420 {
421     // We only need to override the type when capturing audio, HTMLMediaElement and/or WebAudio
422     // will do the right thing when a stream is attached to a media element or an audio context.
423     if (m_private->hasAudio() && m_isProducingData && m_private->hasCaptureAudioSource())
424         return PlatformMediaSession::MediaStreamCapturingAudio;
425
426     return PlatformMediaSession::None;
427 }
428
429 PlatformMediaSession::MediaType MediaStream::presentationType() const
430 {
431     return mediaType();
432 }
433
434 PlatformMediaSession::CharacteristicsFlags MediaStream::characteristics() const
435 {
436     PlatformMediaSession::CharacteristicsFlags state = PlatformMediaSession::HasNothing;
437
438     if (!m_isProducingData)
439         return state;
440
441     if (m_private->hasAudio())
442         state |= PlatformMediaSession::HasAudio;
443
444     if (m_private->hasVideo())
445         state |= PlatformMediaSession::HasVideo;
446
447     return state;
448 }
449
450 void MediaStream::mayResumePlayback(bool)
451 {
452     // FIXME: should a media stream pay attention to this directly, or only when attached to a media element?
453 }
454
455 void MediaStream::suspendPlayback()
456 {
457     // FIXME: should a media stream pay attention to this directly, or only when attached to a media element?
458 }
459
460 String MediaStream::sourceApplicationIdentifier() const
461 {
462     Document* document = this->document();
463     if (document && document->frame()) {
464         if (NetworkingContext* networkingContext = document->frame()->loader().networkingContext())
465             return networkingContext->sourceApplicationIdentifier();
466     }
467
468     return emptyString();
469 }
470
471 bool MediaStream::canProduceAudio() const
472 {
473     return !muted() && active() && m_private->hasAudio() && m_isProducingData;
474 }
475
476 bool MediaStream::processingUserGestureForMedia() const
477 {
478     return document() ? document()->processingUserGestureForMedia() : false;
479 }
480
481 void MediaStream::stop()
482 {
483     m_isActive = false;
484     endCaptureTracks();
485 }
486
487 const char* MediaStream::activeDOMObjectName() const
488 {
489     return "MediaStream";
490 }
491
492 bool MediaStream::canSuspendForDocumentSuspension() const
493 {
494     return !hasPendingActivity();
495 }
496
497 bool MediaStream::hasPendingActivity() const
498 {
499     return m_isActive || !m_scheduledActivityEvents.isEmpty();
500 }
501
502 } // namespace WebCore
503
504 #endif // ENABLE(MEDIA_STREAM)