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