summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/mediastream/MediaStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/mediastream/MediaStream.cpp')
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStream.cpp473
1 files changed, 244 insertions, 229 deletions
diff --git a/Source/WebCore/Modules/mediastream/MediaStream.cpp b/Source/WebCore/Modules/mediastream/MediaStream.cpp
index 22ec3d4cc..dcacf8f82 100644
--- a/Source/WebCore/Modules/mediastream/MediaStream.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaStream.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
- * Copyright (C) 2011, 2012 Ericsson AB. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012, 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
@@ -30,356 +30,366 @@
#if ENABLE(MEDIA_STREAM)
-#include "AudioStreamTrack.h"
+#include "Document.h"
#include "Event.h"
-#include "ExceptionCode.h"
-#include "MediaStreamCenter.h"
+#include "EventNames.h"
+#include "Logging.h"
#include "MediaStreamRegistry.h"
-#include "MediaStreamSource.h"
#include "MediaStreamTrackEvent.h"
-#include "VideoStreamTrack.h"
-#include <wtf/NeverDestroyed.h>
+#include "Page.h"
+#include "RealtimeMediaSource.h"
+#include "URL.h"
namespace WebCore {
-PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext& context)
+Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context)
{
- return MediaStream::create(context, MediaStreamPrivate::create(Vector<RefPtr<MediaStreamSource>>(), Vector<RefPtr<MediaStreamSource>>()));
+ return MediaStream::create(context, MediaStreamPrivate::create(Vector<RefPtr<MediaStreamTrackPrivate>>()));
}
-PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext& context, PassRefPtr<MediaStream> stream)
+Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, MediaStream& stream)
{
- ASSERT(stream);
-
- Vector<RefPtr<MediaStreamTrackPrivate>> audioTracks;
- Vector<RefPtr<MediaStreamTrackPrivate>> videoTracks;
-
- for (size_t i = 0; i < stream->m_audioTracks.size(); ++i)
- audioTracks.append(&stream->m_audioTracks[i]->privateTrack());
-
- for (size_t i = 0; i < stream->m_videoTracks.size(); ++i)
- videoTracks.append(&stream->m_videoTracks[i]->privateTrack());
-
- return MediaStream::create(context, MediaStreamPrivate::create(audioTracks, videoTracks));
+ return adoptRef(*new MediaStream(context, stream.getTracks()));
}
-PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext& context, const Vector<RefPtr<MediaStreamTrack>>& tracks)
+Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
{
- Vector<RefPtr<MediaStreamTrackPrivate>> audioTracks;
- Vector<RefPtr<MediaStreamTrackPrivate>> videoTracks;
-
- for (size_t i = 0; i < tracks.size(); ++i) {
- if (tracks[i]->kind() == "audio")
- audioTracks.append(&tracks[i]->privateTrack());
- else
- videoTracks.append(&tracks[i]->privateTrack());
- }
+ return adoptRef(*new MediaStream(context, tracks));
+}
- return MediaStream::create(context, MediaStreamPrivate::create(audioTracks, videoTracks));
+Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, RefPtr<MediaStreamPrivate>&& streamPrivate)
+{
+ return adoptRef(*new MediaStream(context, WTFMove(streamPrivate)));
}
-PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext& context, PassRefPtr<MediaStreamPrivate> privateStream)
+MediaStream::MediaStream(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
+ : ContextDestructionObserver(&context)
+ , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
{
- return adoptRef(new MediaStream(context, privateStream));
+ // This constructor preserves MediaStreamTrack instances and must be used by calls originating
+ // from the JavaScript MediaStream constructor.
+ MediaStreamTrackPrivateVector trackPrivates;
+ trackPrivates.reserveCapacity(tracks.size());
+
+ for (auto& track : tracks) {
+ track->addObserver(*this);
+ m_trackSet.add(track->id(), track);
+ trackPrivates.append(&track->privateTrack());
+ }
+
+ m_private = MediaStreamPrivate::create(trackPrivates);
+ setIsActive(m_private->active());
+ m_private->addObserver(*this);
+ MediaStreamRegistry::shared().registerStream(*this);
+ document()->addAudioProducer(this);
}
-MediaStream::MediaStream(ScriptExecutionContext& context, PassRefPtr<MediaStreamPrivate> privateStream)
+MediaStream::MediaStream(ScriptExecutionContext& context, RefPtr<MediaStreamPrivate>&& streamPrivate)
: ContextDestructionObserver(&context)
- , m_private(privateStream)
- , m_scheduledEventTimer(this, &MediaStream::scheduledEventTimerFired)
+ , m_private(streamPrivate)
+ , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
{
ASSERT(m_private);
- m_private->setClient(this);
-
- RefPtr<MediaStreamTrack> track;
- size_t numberOfAudioTracks = m_private->numberOfAudioTracks();
- m_audioTracks.reserveCapacity(numberOfAudioTracks);
- for (size_t i = 0; i < numberOfAudioTracks; i++) {
- track = AudioStreamTrack::create(context, *m_private->audioTracks(i));
- track->addObserver(this);
- m_audioTracks.append(track.release());
+ setIsActive(m_private->active());
+ m_private->addObserver(*this);
+ MediaStreamRegistry::shared().registerStream(*this);
+
+ for (auto& trackPrivate : m_private->tracks()) {
+ auto track = MediaStreamTrack::create(context, *trackPrivate);
+ track->addObserver(*this);
+ m_trackSet.add(track->id(), WTFMove(track));
}
+ document()->addAudioProducer(this);
+}
- size_t numberOfVideoTracks = m_private->numberOfVideoTracks();
- m_videoTracks.reserveCapacity(numberOfVideoTracks);
- for (size_t i = 0; i < numberOfVideoTracks; i++) {
- track = VideoStreamTrack::create(context, *m_private->videoTracks(i));
- track->addObserver(this);
- m_videoTracks.append(track.release());
+MediaStream::~MediaStream()
+{
+ // Set isActive to false immediately so an callbacks triggered by shutting down, e.g.
+ // mediaState(), are short circuited.
+ m_isActive = false;
+ MediaStreamRegistry::shared().unregisterStream(*this);
+ m_private->removeObserver(*this);
+ for (auto& track : m_trackSet.values())
+ track->removeObserver(*this);
+ if (Document* document = this->document()) {
+ document->removeAudioProducer(this);
+ if (m_isWaitingUntilMediaCanStart)
+ document->removeMediaCanStartListener(this);
}
}
-MediaStream::~MediaStream()
+RefPtr<MediaStream> MediaStream::clone()
{
- m_private->setClient(0);
+ MediaStreamTrackVector clonedTracks;
+ clonedTracks.reserveCapacity(m_trackSet.size());
+
+ for (auto& track : m_trackSet.values())
+ clonedTracks.append(track->clone());
+
+ return MediaStream::create(*scriptExecutionContext(), clonedTracks);
}
-bool MediaStream::ended() const
+void MediaStream::addTrack(MediaStreamTrack& track)
{
- return m_private->ended();
+ if (!internalAddTrack(track, StreamModifier::DomAPI))
+ return;
+
+ for (auto& observer : m_observers)
+ observer->didAddOrRemoveTrack();
}
-void MediaStream::setEnded()
+void MediaStream::removeTrack(MediaStreamTrack& track)
{
- if (ended())
+ if (!internalRemoveTrack(track.id(), StreamModifier::DomAPI))
return;
- m_private->setEnded();
+
+ for (auto& observer : m_observers)
+ observer->didAddOrRemoveTrack();
}
-PassRefPtr<MediaStream> MediaStream::clone()
+MediaStreamTrack* MediaStream::getTrackById(String id)
{
- Vector<RefPtr<MediaStreamTrack>> trackSet;
+ auto it = m_trackSet.find(id);
+ if (it != m_trackSet.end())
+ return it->value.get();
- cloneMediaStreamTrackVector(trackSet, getAudioTracks());
- cloneMediaStreamTrackVector(trackSet, getVideoTracks());
- return MediaStream::create(*scriptExecutionContext(), trackSet);
+ return nullptr;
}
-void MediaStream::cloneMediaStreamTrackVector(Vector<RefPtr<MediaStreamTrack>>& destination, const Vector<RefPtr<MediaStreamTrack>>& source)
+MediaStreamTrackVector MediaStream::getAudioTracks() const
{
- for (auto it = source.begin(), end = source.end(); it != end; ++it)
- destination.append((*it)->clone());
+ return trackVectorForType(RealtimeMediaSource::Audio);
}
-void MediaStream::addTrack(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionCode& ec)
+MediaStreamTrackVector MediaStream::getVideoTracks() const
{
- if (ended()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ return trackVectorForType(RealtimeMediaSource::Video);
+}
- if (!prpTrack) {
- ec = TYPE_MISMATCH_ERR;
- return;
- }
+MediaStreamTrackVector MediaStream::getTracks() const
+{
+ MediaStreamTrackVector tracks;
+ tracks.reserveCapacity(m_trackSet.size());
+ copyValuesToVector(m_trackSet, tracks);
- if (addTrack(prpTrack)) {
- for (auto observer = m_observers.begin(), end = m_observers.end(); observer != end; ++observer)
- (*observer)->didAddOrRemoveTrack();
- }
+ return tracks;
}
-bool MediaStream::addTrack(PassRefPtr<MediaStreamTrack> prpTrack)
+void MediaStream::contextDestroyed()
{
- // This is a common part used by addTrack called by JavaScript
- // and addRemoteTrack and only addRemoteTrack must fire addtrack event
- RefPtr<MediaStreamTrack> track = prpTrack;
- if (getTrackById(track->id()))
- return false;
-
- Vector<RefPtr<MediaStreamTrack>>* tracks = trackVectorForType(track->source()->type());
+ ContextDestructionObserver::contextDestroyed();
+}
- tracks->append(track);
- track->addObserver(this);
- m_private->addTrack(&track->privateTrack());
- return true;
+void MediaStream::trackDidEnd()
+{
+ m_private->updateActiveState(MediaStreamPrivate::NotifyClientOption::Notify);
}
-void MediaStream::removeTrack(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionCode& ec)
+void MediaStream::activeStatusChanged()
{
- if (ended()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ // Schedule the active state change and event dispatch since this callback may be called
+ // synchronously from the DOM API (e.g. as a result of addTrack()).
+ scheduleActiveStateChange();
+}
- if (!prpTrack) {
- ec = TYPE_MISMATCH_ERR;
+void MediaStream::didAddTrack(MediaStreamTrackPrivate& trackPrivate)
+{
+ ScriptExecutionContext* context = scriptExecutionContext();
+ if (!context)
return;
- }
- if (removeTrack(prpTrack)) {
- for (auto observer = m_observers.begin(), end = m_observers.end(); observer != end; ++observer)
- (*observer)->didAddOrRemoveTrack();
- }
+ if (!getTrackById(trackPrivate.id()))
+ internalAddTrack(MediaStreamTrack::create(*context, trackPrivate), StreamModifier::Platform);
}
-bool MediaStream::removeTrack(PassRefPtr<MediaStreamTrack> prpTrack)
+void MediaStream::didRemoveTrack(MediaStreamTrackPrivate& trackPrivate)
{
- // This is a common part used by removeTrack called by JavaScript
- // and removeRemoteTrack and only removeRemoteTrack must fire removetrack event
- RefPtr<MediaStreamTrack> track = prpTrack;
- Vector<RefPtr<MediaStreamTrack>>* tracks = trackVectorForType(track->source()->type());
+ internalRemoveTrack(trackPrivate.id(), StreamModifier::Platform);
+}
- size_t pos = tracks->find(track);
- if (pos == notFound)
+bool MediaStream::internalAddTrack(Ref<MediaStreamTrack>&& trackToAdd, StreamModifier streamModifier)
+{
+ auto result = m_trackSet.add(trackToAdd->id(), WTFMove(trackToAdd));
+ if (!result.isNewEntry)
return false;
- tracks->remove(pos);
- m_private->removeTrack(&track->privateTrack());
- // There can be other tracks using the same source in the same MediaStream,
- // like when MediaStreamTrack::clone() is called, for instance.
- // Spec says that a source can be shared, so we must assure that there is no
- // other track using it.
- if (!haveTrackWithSource(track->source()))
- m_private->removeSource(track->source());
+ ASSERT(result.iterator->value);
+ auto& track = *result.iterator->value;
+ track.addObserver(*this);
- track->removeObserver(this);
- if (!m_audioTracks.size() && !m_videoTracks.size())
- setEnded();
+ if (streamModifier == StreamModifier::DomAPI)
+ m_private->addTrack(&track.privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
+ else
+ dispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, &track));
return true;
}
-bool MediaStream::haveTrackWithSource(PassRefPtr<MediaStreamSource> source)
+bool MediaStream::internalRemoveTrack(const String& trackId, StreamModifier streamModifier)
{
- if (source->type() == MediaStreamSource::Audio) {
- for (auto it = m_audioTracks.begin(), end = m_audioTracks.end(); it != end; ++it) {
- if ((*it)->source() == source.get())
- return true;
- }
+ auto track = m_trackSet.take(trackId);
+ if (!track)
return false;
- }
- for (auto it = m_videoTracks.begin(), end = m_videoTracks.end(); it != end; ++it) {
- if ((*it)->source() == source.get())
- return true;
- }
+ track->removeObserver(*this);
+
+ if (streamModifier == StreamModifier::DomAPI)
+ m_private->removeTrack(track->privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
+ else
+ dispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, WTFMove(track)));
- return false;
+ return true;
}
-MediaStreamTrack* MediaStream::getTrackById(String id)
+void MediaStream::setIsActive(bool active)
{
- for (auto it = m_audioTracks.begin(), end = m_audioTracks.end(); it != end; ++it) {
- if ((*it)->id() == id)
- return (*it).get();
- }
-
- for (auto it = m_videoTracks.begin(), end = m_videoTracks.end(); it != end; ++it) {
- if ((*it)->id() == id)
- return (*it).get();
- }
+ if (m_isActive == active)
+ return;
- return nullptr;
+ m_isActive = active;
+ statusDidChange();
}
-void MediaStream::trackDidEnd()
+void MediaStream::mediaCanStart(Document& document)
{
- for (auto it = m_audioTracks.begin(), end = m_audioTracks.end(); it != end; ++it) {
- if (!(*it)->ended())
- return;
- }
- for (auto it = m_videoTracks.begin(), end = m_videoTracks.end(); it != end; ++it) {
- if (!(*it)->ended())
- return;
+ ASSERT_UNUSED(document, &document == this->document());
+ ASSERT(m_isWaitingUntilMediaCanStart);
+ if (m_isWaitingUntilMediaCanStart) {
+ m_isWaitingUntilMediaCanStart = false;
+ startProducingData();
}
-
- setEnded();
}
-void MediaStream::streamDidEnd()
+void MediaStream::startProducingData()
{
- if (ended())
+ Document* document = this->document();
+ if (!document || !document->page())
return;
- scheduleDispatchEvent(Event::create(eventNames().endedEvent, false, false));
-}
+ // If we can't start a load right away, start it later.
+ if (!document->page()->canStartMedia()) {
+ LOG(Media, "MediaStream::startProducingData(%p) - not allowed to start in background, waiting", this);
+ if (m_isWaitingUntilMediaCanStart)
+ return;
-void MediaStream::contextDestroyed()
-{
- ContextDestructionObserver::contextDestroyed();
+ m_isWaitingUntilMediaCanStart = true;
+ document->addMediaCanStartListener(this);
+ return;
+ }
+
+ m_private->startProducingData();
}
-void MediaStream::addRemoteSource(MediaStreamSource* source)
+void MediaStream::stopProducingData()
{
- ASSERT(source);
- addRemoteTrack(MediaStreamTrackPrivate::create(source).get());
+ m_private->stopProducingData();
}
-void MediaStream::removeRemoteSource(MediaStreamSource* source)
+void MediaStream::pageMutedStateDidChange()
{
- ASSERT(source);
- if (ended())
+ if (!m_isActive)
return;
- Vector<RefPtr<MediaStreamTrack>>* tracks = trackVectorForType(source->type());
-
- for (int i = tracks->size() - 1; i >= 0; --i) {
- if ((*tracks)[i]->source() != source)
- continue;
+ Document* document = this->document();
+ if (!document)
+ return;
- RefPtr<MediaStreamTrack> track = (*tracks)[i];
- track->removeObserver(this);
- tracks->remove(i);
- m_private->removeTrack(&track->privateTrack());
- scheduleDispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, track.release()));
- }
+ bool pageMuted = document->page()->isMediaCaptureMuted();
+ if (m_externallyMuted == pageMuted)
+ return;
- m_private->removeSource(source);
+ m_externallyMuted = pageMuted;
+ if (pageMuted)
+ stopProducingData();
+ else
+ startProducingData();
}
-void MediaStream::addRemoteTrack(MediaStreamTrackPrivate* privateTrack)
+MediaProducer::MediaStateFlags MediaStream::mediaState() const
{
- ASSERT(privateTrack);
- if (ended())
- return;
+ MediaStateFlags state = IsNotPlaying;
- RefPtr<MediaStreamTrack> track;
- switch (privateTrack->type()) {
- case MediaStreamSource::Audio:
- track = AudioStreamTrack::create(*scriptExecutionContext(), *privateTrack);
- break;
- case MediaStreamSource::Video:
- track = VideoStreamTrack::create(*scriptExecutionContext(), *privateTrack);
- break;
- case MediaStreamSource::None:
- ASSERT_NOT_REACHED();
- break;
+ if (!m_isActive)
+ return state;
+
+ if (m_private->hasAudio()) {
+ state |= HasAudioOrVideo;
+ if (m_private->hasLocalAudioSource() && m_private->isProducingData())
+ state |= HasActiveAudioCaptureDevice;
}
- if (!track)
- return;
+ if (m_private->hasVideo()) {
+ state |= HasAudioOrVideo;
+ if (m_private->hasLocalVideoSource() && m_private->isProducingData())
+ state |= HasActiveVideoCaptureDevice;
+ }
- if (addTrack(track))
- scheduleDispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, track));
+ return state;
}
-void MediaStream::removeRemoteTrack(MediaStreamTrackPrivate* privateTrack)
+void MediaStream::statusDidChange()
{
- ASSERT(privateTrack);
- if (ended())
- return;
+ if (Document* document = this->document()) {
+ if (m_isActive)
+ document->setHasActiveMediaStreamTrack();
+ document->updateIsPlayingMedia();
+ }
+}
- RefPtr<MediaStreamTrack> track = getTrackById(privateTrack->id());
- if (removeTrack(track))
- scheduleDispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, track.release()));
+void MediaStream::characteristicsChanged()
+{
+ bool muted = m_private->muted();
+ if (m_isMuted != muted) {
+ m_isMuted = muted;
+ statusDidChange();
+ }
}
-void MediaStream::scheduleDispatchEvent(PassRefPtr<Event> event)
+void MediaStream::scheduleActiveStateChange()
{
- m_scheduledEvents.append(event);
+ bool active = false;
+ for (auto& track : m_trackSet.values()) {
+ if (!track->ended()) {
+ active = true;
+ break;
+ }
+ }
+
+ if (m_isActive == active)
+ return;
+
+ setIsActive(active);
+
+ const AtomicString& eventName = m_isActive ? eventNames().inactiveEvent : eventNames().activeEvent;
+ m_scheduledActivityEvents.append(Event::create(eventName, false, false));
- if (!m_scheduledEventTimer.isActive())
- m_scheduledEventTimer.startOneShot(0);
+ if (!m_activityEventTimer.isActive())
+ m_activityEventTimer.startOneShot(0);
}
-void MediaStream::scheduledEventTimerFired(Timer<MediaStream>*)
+void MediaStream::activityEventTimerFired()
{
- Vector<RefPtr<Event>> events;
- events.swap(m_scheduledEvents);
+ Vector<Ref<Event>> events;
+ events.swap(m_scheduledActivityEvents);
- for (auto it = events.begin(), end = events.end(); it != end; ++it)
- dispatchEvent((*it).release());
-
- events.clear();
+ for (auto& event : events)
+ dispatchEvent(event);
}
URLRegistry& MediaStream::registry() const
{
- return MediaStreamRegistry::registry();
+ return MediaStreamRegistry::shared();
}
-Vector<RefPtr<MediaStreamTrack>>* MediaStream::trackVectorForType(MediaStreamSource::Type type)
+MediaStreamTrackVector MediaStream::trackVectorForType(RealtimeMediaSource::Type filterType) const
{
- switch (type) {
- case MediaStreamSource::Audio:
- return &m_audioTracks;
- case MediaStreamSource::Video:
- return &m_videoTracks;
- case MediaStreamSource::None:
- ASSERT_NOT_REACHED();
+ MediaStreamTrackVector tracks;
+ for (auto& track : m_trackSet.values()) {
+ if (track->source().type() == filterType)
+ tracks.append(track);
}
- return nullptr;
+
+ return tracks;
}
void MediaStream::addObserver(MediaStream::Observer* observer)
@@ -395,6 +405,11 @@ void MediaStream::removeObserver(MediaStream::Observer* observer)
m_observers.remove(pos);
}
+Document* MediaStream::document() const
+{
+ return downcast<Document>(scriptExecutionContext());
+}
+
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)