diff options
Diffstat (limited to 'Source/WebCore/Modules/mediastream/MediaStream.cpp')
-rw-r--r-- | Source/WebCore/Modules/mediastream/MediaStream.cpp | 473 |
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) |