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).
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
29 #include "MediaStream.h"
31 #if ENABLE(MEDIA_STREAM)
35 #include "EventNames.h"
38 #include "MediaStreamRegistry.h"
39 #include "MediaStreamTrackEvent.h"
40 #include "NetworkingContext.h"
42 #include "RealtimeMediaSource.h"
47 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context)
49 return MediaStream::create(context, MediaStreamPrivate::create(Vector<RefPtr<MediaStreamTrackPrivate>>()));
52 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, MediaStream& stream)
54 return adoptRef(*new MediaStream(context, stream.getTracks()));
57 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
59 return adoptRef(*new MediaStream(context, tracks));
62 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, Ref<MediaStreamPrivate>&& streamPrivate)
64 return adoptRef(*new MediaStream(context, WTFMove(streamPrivate)));
67 static inline MediaStreamTrackPrivateVector createTrackPrivateVector(const MediaStreamTrackVector& tracks)
69 MediaStreamTrackPrivateVector trackPrivates;
70 trackPrivates.reserveCapacity(tracks.size());
71 for (auto& track : tracks)
72 trackPrivates.append(&track->privateTrack());
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))
82 // This constructor preserves MediaStreamTrack instances and must be used by calls originating
83 // from the JavaScript MediaStream constructor.
85 for (auto& track : tracks) {
86 track->addObserver(*this);
87 m_trackSet.add(track->id(), track);
90 setIsActive(m_private->active());
91 m_private->addObserver(*this);
92 MediaStreamRegistry::shared().registerStream(*this);
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))
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);
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));
116 MediaStream::~MediaStream()
118 // Set isActive to false immediately so any callbacks triggered by shutting down, e.g.
119 // mediaState(), are short circuited.
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);
131 RefPtr<MediaStream> MediaStream::clone()
133 MediaStreamTrackVector clonedTracks;
134 clonedTracks.reserveInitialCapacity(m_trackSet.size());
136 for (auto& track : m_trackSet.values())
137 clonedTracks.uncheckedAppend(track->clone());
139 return MediaStream::create(*scriptExecutionContext(), clonedTracks);
142 void MediaStream::addTrack(MediaStreamTrack& track)
144 if (!internalAddTrack(track, StreamModifier::DomAPI))
147 for (auto& observer : m_observers)
148 observer->didAddOrRemoveTrack();
151 void MediaStream::removeTrack(MediaStreamTrack& track)
153 if (!internalRemoveTrack(track.id(), StreamModifier::DomAPI))
156 for (auto& observer : m_observers)
157 observer->didAddOrRemoveTrack();
160 MediaStreamTrack* MediaStream::getTrackById(String id)
162 auto it = m_trackSet.find(id);
163 if (it != m_trackSet.end())
164 return it->value.get();
169 MediaStreamTrackVector MediaStream::getAudioTracks() const
171 return trackVectorForType(RealtimeMediaSource::Type::Audio);
174 MediaStreamTrackVector MediaStream::getVideoTracks() const
176 return trackVectorForType(RealtimeMediaSource::Type::Video);
179 MediaStreamTrackVector MediaStream::getTracks() const
181 MediaStreamTrackVector tracks;
182 tracks.reserveCapacity(m_trackSet.size());
183 copyValuesToVector(m_trackSet, tracks);
188 void MediaStream::trackDidEnd()
190 m_private->updateActiveState(MediaStreamPrivate::NotifyClientOption::Notify);
193 void MediaStream::activeStatusChanged()
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();
200 void MediaStream::didAddTrack(MediaStreamTrackPrivate& trackPrivate)
202 ScriptExecutionContext* context = scriptExecutionContext();
206 if (!getTrackById(trackPrivate.id()))
207 internalAddTrack(MediaStreamTrack::create(*context, trackPrivate), StreamModifier::Platform);
210 void MediaStream::didRemoveTrack(MediaStreamTrackPrivate& trackPrivate)
212 internalRemoveTrack(trackPrivate.id(), StreamModifier::Platform);
215 void MediaStream::addTrackFromPlatform(Ref<MediaStreamTrack>&& track)
217 auto* privateTrack = &track->privateTrack();
218 internalAddTrack(WTFMove(track), StreamModifier::Platform);
219 m_private->addTrack(privateTrack, MediaStreamPrivate::NotifyClientOption::Notify);
222 bool MediaStream::internalAddTrack(Ref<MediaStreamTrack>&& trackToAdd, StreamModifier streamModifier)
224 auto result = m_trackSet.add(trackToAdd->id(), WTFMove(trackToAdd));
225 if (!result.isNewEntry)
228 ASSERT(result.iterator->value);
229 auto& track = *result.iterator->value;
230 track.addObserver(*this);
232 if (streamModifier == StreamModifier::DomAPI)
233 m_private->addTrack(&track.privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
235 dispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, &track));
240 bool MediaStream::internalRemoveTrack(const String& trackId, StreamModifier streamModifier)
242 auto track = m_trackSet.take(trackId);
246 track->removeObserver(*this);
248 if (streamModifier == StreamModifier::DomAPI)
249 m_private->removeTrack(track->privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
251 dispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, WTFMove(track)));
256 void MediaStream::setIsActive(bool active)
258 if (m_isActive == active)
265 void MediaStream::mediaCanStart(Document& document)
267 ASSERT_UNUSED(document, &document == this->document());
268 ASSERT(m_isWaitingUntilMediaCanStart);
269 if (m_isWaitingUntilMediaCanStart) {
270 m_isWaitingUntilMediaCanStart = false;
271 startProducingData();
275 void MediaStream::startProducingData()
277 Document* document = this->document();
278 if (!document || !document->page())
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)
287 m_isWaitingUntilMediaCanStart = true;
288 document->addMediaCanStartListener(this);
292 if (m_isProducingData)
294 m_isProducingData = true;
296 m_mediaSession->canProduceAudioChanged();
297 m_private->startProducingData();
299 if (document->page()->isMediaCaptureMuted())
300 m_private->setCaptureTracksMuted(true);
303 void MediaStream::stopProducingData()
305 if (!m_isProducingData)
307 m_isProducingData = false;
309 m_mediaSession->canProduceAudioChanged();
311 m_private->stopProducingData();
314 void MediaStream::endCaptureTracks()
316 for (auto& track : m_trackSet.values()) {
317 if (track->isCaptureTrack())
318 track->stopTrack(MediaStreamTrack::StopMode::PostEvent);
322 MediaProducer::MediaStateFlags MediaStream::mediaState() const
324 MediaProducer::MediaStateFlags state = MediaProducer::IsNotPlaying;
326 if (!m_isActive || !document() || !document()->page())
329 for (const auto& track : m_trackSet.values())
330 state |= track->mediaState();
335 void MediaStream::statusDidChange()
337 m_mediaSession->canProduceAudioChanged();
339 if (Document* document = this->document()) {
341 document->setHasActiveMediaStreamTrack();
345 void MediaStream::characteristicsChanged()
347 auto state = mediaState();
348 if (m_state != state) {
354 void MediaStream::scheduleActiveStateChange()
357 for (auto& track : m_trackSet.values()) {
358 if (!track->ended()) {
364 if (m_isActive == active)
369 const AtomicString& eventName = m_isActive ? eventNames().inactiveEvent : eventNames().activeEvent;
370 m_scheduledActivityEvents.append(Event::create(eventName, false, false));
372 if (!m_activityEventTimer.isActive())
373 m_activityEventTimer.startOneShot(0_s);
376 void MediaStream::activityEventTimerFired()
378 Vector<Ref<Event>> events;
379 events.swap(m_scheduledActivityEvents);
381 for (auto& event : events)
382 dispatchEvent(event);
385 URLRegistry& MediaStream::registry() const
387 return MediaStreamRegistry::shared();
390 MediaStreamTrackVector MediaStream::trackVectorForType(RealtimeMediaSource::Type filterType) const
392 MediaStreamTrackVector tracks;
393 for (auto& track : m_trackSet.values()) {
394 if (track->source().type() == filterType)
395 tracks.append(track);
401 void MediaStream::addObserver(MediaStream::Observer* observer)
403 if (m_observers.find(observer) == notFound)
404 m_observers.append(observer);
407 void MediaStream::removeObserver(MediaStream::Observer* observer)
409 size_t pos = m_observers.find(observer);
411 m_observers.remove(pos);
414 Document* MediaStream::document() const
416 return downcast<Document>(scriptExecutionContext());
419 PlatformMediaSession::MediaType MediaStream::mediaType() const
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;
426 return PlatformMediaSession::None;
429 PlatformMediaSession::MediaType MediaStream::presentationType() const
434 PlatformMediaSession::CharacteristicsFlags MediaStream::characteristics() const
436 PlatformMediaSession::CharacteristicsFlags state = PlatformMediaSession::HasNothing;
438 if (!m_isProducingData)
441 if (m_private->hasAudio())
442 state |= PlatformMediaSession::HasAudio;
444 if (m_private->hasVideo())
445 state |= PlatformMediaSession::HasVideo;
450 void MediaStream::mayResumePlayback(bool)
452 // FIXME: should a media stream pay attention to this directly, or only when attached to a media element?
455 void MediaStream::suspendPlayback()
457 // FIXME: should a media stream pay attention to this directly, or only when attached to a media element?
460 String MediaStream::sourceApplicationIdentifier() const
462 Document* document = this->document();
463 if (document && document->frame()) {
464 if (NetworkingContext* networkingContext = document->frame()->loader().networkingContext())
465 return networkingContext->sourceApplicationIdentifier();
468 return emptyString();
471 bool MediaStream::canProduceAudio() const
473 return !muted() && active() && m_private->hasAudio() && m_isProducingData;
476 bool MediaStream::processingUserGestureForMedia() const
478 return document() ? document()->processingUserGestureForMedia() : false;
481 void MediaStream::stop()
487 const char* MediaStream::activeDOMObjectName() const
489 return "MediaStream";
492 bool MediaStream::canSuspendForDocumentSuspension() const
494 return !hasPendingActivity();
497 bool MediaStream::hasPendingActivity() const
499 return m_isActive || !m_scheduledActivityEvents.isEmpty();
502 } // namespace WebCore
504 #endif // ENABLE(MEDIA_STREAM)