| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * Copyright (C) 2011, 2012 Ericsson AB. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/modules/mediastream/media_stream.h" |
| |
| #include "third_party/blink/public/platform/task_type.h" |
| #include "third_party/blink/renderer/core/execution_context/execution_context.h" |
| #include "third_party/blink/renderer/core/frame/deprecation.h" |
| #include "third_party/blink/renderer/modules/mediastream/media_stream_track_event.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_state.h" |
| #include "third_party/blink/renderer/platform/bindings/script_state.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_stream_center.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h" |
| |
| namespace blink { |
| |
| static bool ContainsSource(MediaStreamTrackVector& track_vector, |
| MediaStreamSource* source) { |
| for (size_t i = 0; i < track_vector.size(); ++i) { |
| if (source->Id() == track_vector[i]->Component()->Source()->Id()) |
| return true; |
| } |
| return false; |
| } |
| |
| static void ProcessTrack(MediaStreamTrack* track, |
| MediaStreamTrackVector& track_vector) { |
| MediaStreamSource* source = track->Component()->Source(); |
| if (!ContainsSource(track_vector, source)) |
| track_vector.push_back(track); |
| } |
| |
| MediaStream* MediaStream::Create(ExecutionContext* context) { |
| MediaStreamTrackVector audio_tracks; |
| MediaStreamTrackVector video_tracks; |
| |
| return new MediaStream(context, audio_tracks, video_tracks); |
| } |
| |
| MediaStream* MediaStream::Create(ExecutionContext* context, |
| MediaStream* stream) { |
| DCHECK(stream); |
| |
| MediaStreamTrackVector audio_tracks; |
| MediaStreamTrackVector video_tracks; |
| |
| for (size_t i = 0; i < stream->audio_tracks_.size(); ++i) |
| ProcessTrack(stream->audio_tracks_[i].Get(), audio_tracks); |
| |
| for (size_t i = 0; i < stream->video_tracks_.size(); ++i) |
| ProcessTrack(stream->video_tracks_[i].Get(), video_tracks); |
| |
| return new MediaStream(context, audio_tracks, video_tracks); |
| } |
| |
| MediaStream* MediaStream::Create(ExecutionContext* context, |
| const MediaStreamTrackVector& tracks) { |
| MediaStreamTrackVector audio_tracks; |
| MediaStreamTrackVector video_tracks; |
| |
| for (size_t i = 0; i < tracks.size(); ++i) |
| ProcessTrack(tracks[i].Get(), |
| tracks[i]->kind() == "audio" ? audio_tracks : video_tracks); |
| |
| return new MediaStream(context, audio_tracks, video_tracks); |
| } |
| |
| MediaStream* MediaStream::Create(ExecutionContext* context, |
| MediaStreamDescriptor* stream_descriptor) { |
| return new MediaStream(context, stream_descriptor); |
| } |
| |
| MediaStream* MediaStream::Create(ExecutionContext* context, |
| MediaStreamDescriptor* stream_descriptor, |
| const MediaStreamTrackVector& audio_tracks, |
| const MediaStreamTrackVector& video_tracks) { |
| return new MediaStream(context, stream_descriptor, audio_tracks, |
| video_tracks); |
| } |
| |
| MediaStream::MediaStream(ExecutionContext* context, |
| MediaStreamDescriptor* stream_descriptor) |
| : ContextClient(context), |
| descriptor_(stream_descriptor), |
| scheduled_event_timer_( |
| context->GetTaskRunner(TaskType::kMediaElementEvent), |
| this, |
| &MediaStream::ScheduledEventTimerFired) { |
| descriptor_->SetClient(this); |
| |
| size_t number_of_audio_tracks = descriptor_->NumberOfAudioComponents(); |
| audio_tracks_.ReserveCapacity(number_of_audio_tracks); |
| for (size_t i = 0; i < number_of_audio_tracks; i++) { |
| MediaStreamTrack* new_track = |
| MediaStreamTrack::Create(context, descriptor_->AudioComponent(i)); |
| new_track->RegisterMediaStream(this); |
| audio_tracks_.push_back(new_track); |
| } |
| |
| size_t number_of_video_tracks = descriptor_->NumberOfVideoComponents(); |
| video_tracks_.ReserveCapacity(number_of_video_tracks); |
| for (size_t i = 0; i < number_of_video_tracks; i++) { |
| MediaStreamTrack* new_track = |
| MediaStreamTrack::Create(context, descriptor_->VideoComponent(i)); |
| new_track->RegisterMediaStream(this); |
| video_tracks_.push_back(new_track); |
| } |
| |
| if (EmptyOrOnlyEndedTracks()) { |
| descriptor_->SetActive(false); |
| } |
| } |
| |
| MediaStream::MediaStream(ExecutionContext* context, |
| MediaStreamDescriptor* stream_descriptor, |
| const MediaStreamTrackVector& audio_tracks, |
| const MediaStreamTrackVector& video_tracks) |
| : ContextClient(context), |
| descriptor_(stream_descriptor), |
| scheduled_event_timer_( |
| context->GetTaskRunner(TaskType::kMediaElementEvent), |
| this, |
| &MediaStream::ScheduledEventTimerFired) { |
| descriptor_->SetClient(this); |
| |
| audio_tracks_.ReserveCapacity(audio_tracks.size()); |
| for (size_t i = 0; i < audio_tracks.size(); ++i) { |
| MediaStreamTrack* audio_track = audio_tracks[i]; |
| DCHECK_EQ("audio", audio_track->kind()); |
| audio_track->RegisterMediaStream(this); |
| audio_tracks_.push_back(audio_track); |
| } |
| video_tracks_.ReserveCapacity(video_tracks.size()); |
| for (size_t i = 0; i < video_tracks.size(); ++i) { |
| MediaStreamTrack* video_track = video_tracks[i]; |
| DCHECK_EQ("video", video_track->kind()); |
| video_track->RegisterMediaStream(this); |
| video_tracks_.push_back(video_track); |
| } |
| DCHECK(TracksMatchDescriptor()); |
| |
| if (EmptyOrOnlyEndedTracks()) { |
| descriptor_->SetActive(false); |
| } |
| } |
| |
| MediaStream::MediaStream(ExecutionContext* context, |
| const MediaStreamTrackVector& audio_tracks, |
| const MediaStreamTrackVector& video_tracks) |
| : ContextClient(context), |
| scheduled_event_timer_( |
| context->GetTaskRunner(TaskType::kMediaElementEvent), |
| this, |
| &MediaStream::ScheduledEventTimerFired) { |
| MediaStreamComponentVector audio_components; |
| MediaStreamComponentVector video_components; |
| |
| MediaStreamTrackVector::const_iterator iter; |
| for (iter = audio_tracks.begin(); iter != audio_tracks.end(); ++iter) { |
| (*iter)->RegisterMediaStream(this); |
| audio_components.push_back((*iter)->Component()); |
| } |
| for (iter = video_tracks.begin(); iter != video_tracks.end(); ++iter) { |
| (*iter)->RegisterMediaStream(this); |
| video_components.push_back((*iter)->Component()); |
| } |
| |
| descriptor_ = |
| MediaStreamDescriptor::Create(audio_components, video_components); |
| descriptor_->SetClient(this); |
| |
| audio_tracks_ = audio_tracks; |
| video_tracks_ = video_tracks; |
| if (EmptyOrOnlyEndedTracks()) { |
| descriptor_->SetActive(false); |
| } |
| } |
| |
| MediaStream::~MediaStream() = default; |
| |
| bool MediaStream::EmptyOrOnlyEndedTracks() { |
| if (!audio_tracks_.size() && !video_tracks_.size()) { |
| return true; |
| } |
| for (MediaStreamTrackVector::iterator iter = audio_tracks_.begin(); |
| iter != audio_tracks_.end(); ++iter) { |
| if (!iter->Get()->Ended()) |
| return false; |
| } |
| for (MediaStreamTrackVector::iterator iter = video_tracks_.begin(); |
| iter != video_tracks_.end(); ++iter) { |
| if (!iter->Get()->Ended()) |
| return false; |
| } |
| return true; |
| } |
| |
| bool MediaStream::TracksMatchDescriptor() { |
| if (audio_tracks_.size() != descriptor_->NumberOfAudioComponents()) |
| return false; |
| for (size_t i = 0; i < audio_tracks_.size(); i++) { |
| if (audio_tracks_[i]->Component() != descriptor_->AudioComponent(i)) |
| return false; |
| } |
| if (video_tracks_.size() != descriptor_->NumberOfVideoComponents()) |
| return false; |
| for (size_t i = 0; i < video_tracks_.size(); i++) { |
| if (video_tracks_[i]->Component() != descriptor_->VideoComponent(i)) |
| return false; |
| } |
| return true; |
| } |
| |
| MediaStreamTrackVector MediaStream::getTracks() { |
| MediaStreamTrackVector tracks; |
| for (MediaStreamTrackVector::iterator iter = audio_tracks_.begin(); |
| iter != audio_tracks_.end(); ++iter) |
| tracks.push_back(iter->Get()); |
| for (MediaStreamTrackVector::iterator iter = video_tracks_.begin(); |
| iter != video_tracks_.end(); ++iter) |
| tracks.push_back(iter->Get()); |
| return tracks; |
| } |
| |
| void MediaStream::addTrack(MediaStreamTrack* track, |
| ExceptionState& exception_state) { |
| if (!track) { |
| exception_state.ThrowDOMException( |
| DOMExceptionCode::kTypeMismatchError, |
| "The MediaStreamTrack provided is invalid."); |
| return; |
| } |
| |
| if (getTrackById(track->id())) |
| return; |
| |
| switch (track->Component()->Source()->GetType()) { |
| case MediaStreamSource::kTypeAudio: |
| audio_tracks_.push_back(track); |
| break; |
| case MediaStreamSource::kTypeVideo: |
| video_tracks_.push_back(track); |
| break; |
| } |
| track->RegisterMediaStream(this); |
| descriptor_->AddComponent(track->Component()); |
| |
| if (!active() && !track->Ended()) { |
| descriptor_->SetActive(true); |
| ScheduleDispatchEvent(Event::Create(EventTypeNames::active)); |
| } |
| |
| for (auto& observer : observers_) |
| observer->OnStreamAddTrack(this, track); |
| } |
| |
| void MediaStream::removeTrack(MediaStreamTrack* track, |
| ExceptionState& exception_state) { |
| if (!track) { |
| exception_state.ThrowDOMException( |
| DOMExceptionCode::kTypeMismatchError, |
| "The MediaStreamTrack provided is invalid."); |
| return; |
| } |
| |
| size_t pos = kNotFound; |
| switch (track->Component()->Source()->GetType()) { |
| case MediaStreamSource::kTypeAudio: |
| pos = audio_tracks_.Find(track); |
| if (pos != kNotFound) |
| audio_tracks_.EraseAt(pos); |
| break; |
| case MediaStreamSource::kTypeVideo: |
| pos = video_tracks_.Find(track); |
| if (pos != kNotFound) |
| video_tracks_.EraseAt(pos); |
| break; |
| } |
| |
| if (pos == kNotFound) |
| return; |
| track->UnregisterMediaStream(this); |
| descriptor_->RemoveComponent(track->Component()); |
| |
| if (active() && EmptyOrOnlyEndedTracks()) { |
| descriptor_->SetActive(false); |
| ScheduleDispatchEvent(Event::Create(EventTypeNames::inactive)); |
| } |
| |
| for (auto& observer : observers_) |
| observer->OnStreamRemoveTrack(this, track); |
| } |
| |
| MediaStreamTrack* MediaStream::getTrackById(String id) { |
| for (MediaStreamTrackVector::iterator iter = audio_tracks_.begin(); |
| iter != audio_tracks_.end(); ++iter) { |
| if ((*iter)->id() == id) |
| return iter->Get(); |
| } |
| |
| for (MediaStreamTrackVector::iterator iter = video_tracks_.begin(); |
| iter != video_tracks_.end(); ++iter) { |
| if ((*iter)->id() == id) |
| return iter->Get(); |
| } |
| |
| return nullptr; |
| } |
| |
| MediaStream* MediaStream::clone(ScriptState* script_state) { |
| MediaStreamTrackVector tracks; |
| ExecutionContext* context = ExecutionContext::From(script_state); |
| for (MediaStreamTrackVector::iterator iter = audio_tracks_.begin(); |
| iter != audio_tracks_.end(); ++iter) |
| tracks.push_back((*iter)->clone(script_state)); |
| for (MediaStreamTrackVector::iterator iter = video_tracks_.begin(); |
| iter != video_tracks_.end(); ++iter) |
| tracks.push_back((*iter)->clone(script_state)); |
| return MediaStream::Create(context, tracks); |
| } |
| |
| void MediaStream::TrackEnded() { |
| for (MediaStreamTrackVector::iterator iter = audio_tracks_.begin(); |
| iter != audio_tracks_.end(); ++iter) { |
| if (!(*iter)->Ended()) |
| return; |
| } |
| |
| for (MediaStreamTrackVector::iterator iter = video_tracks_.begin(); |
| iter != video_tracks_.end(); ++iter) { |
| if (!(*iter)->Ended()) |
| return; |
| } |
| |
| StreamEnded(); |
| } |
| |
| void MediaStream::RegisterObserver(MediaStreamObserver* observer) { |
| DCHECK(observer); |
| observers_.insert(observer); |
| } |
| |
| void MediaStream::UnregisterObserver(MediaStreamObserver* observer) { |
| observers_.erase(observer); |
| } |
| |
| void MediaStream::StreamEnded() { |
| if (!GetExecutionContext()) |
| return; |
| |
| if (active()) { |
| descriptor_->SetActive(false); |
| ScheduleDispatchEvent(Event::Create(EventTypeNames::inactive)); |
| } |
| } |
| |
| bool MediaStream::AddEventListenerInternal( |
| const AtomicString& event_type, |
| EventListener* listener, |
| const AddEventListenerOptionsResolved& options) { |
| if (event_type == EventTypeNames::active) { |
| UseCounter::Count(GetExecutionContext(), WebFeature::kMediaStreamOnActive); |
| } else if (event_type == EventTypeNames::inactive) { |
| UseCounter::Count(GetExecutionContext(), |
| WebFeature::kMediaStreamOnInactive); |
| } |
| |
| return EventTargetWithInlineData::AddEventListenerInternal(event_type, |
| listener, options); |
| } |
| |
| const AtomicString& MediaStream::InterfaceName() const { |
| return EventTargetNames::MediaStream; |
| } |
| |
| void MediaStream::AddTrackByComponentAndFireEvents( |
| MediaStreamComponent* component) { |
| DCHECK(component); |
| if (!GetExecutionContext()) |
| return; |
| MediaStreamTrack* track = |
| MediaStreamTrack::Create(GetExecutionContext(), component); |
| AddTrackAndFireEvents(track); |
| } |
| |
| void MediaStream::RemoveTrackByComponentAndFireEvents( |
| MediaStreamComponent* component) { |
| DCHECK(component); |
| if (!GetExecutionContext()) |
| return; |
| |
| MediaStreamTrackVector* tracks = nullptr; |
| switch (component->Source()->GetType()) { |
| case MediaStreamSource::kTypeAudio: |
| tracks = &audio_tracks_; |
| break; |
| case MediaStreamSource::kTypeVideo: |
| tracks = &video_tracks_; |
| break; |
| } |
| |
| size_t index = kNotFound; |
| for (size_t i = 0; i < tracks->size(); ++i) { |
| if ((*tracks)[i]->Component() == component) { |
| index = i; |
| break; |
| } |
| } |
| if (index == kNotFound) |
| return; |
| |
| descriptor_->RemoveComponent(component); |
| |
| MediaStreamTrack* track = (*tracks)[index]; |
| track->UnregisterMediaStream(this); |
| tracks->EraseAt(index); |
| ScheduleDispatchEvent( |
| MediaStreamTrackEvent::Create(EventTypeNames::removetrack, track)); |
| |
| if (active() && EmptyOrOnlyEndedTracks()) { |
| descriptor_->SetActive(false); |
| ScheduleDispatchEvent(Event::Create(EventTypeNames::inactive)); |
| } |
| } |
| |
| void MediaStream::AddTrackAndFireEvents(MediaStreamTrack* track) { |
| DCHECK(track); |
| switch (track->Component()->Source()->GetType()) { |
| case MediaStreamSource::kTypeAudio: |
| audio_tracks_.push_back(track); |
| break; |
| case MediaStreamSource::kTypeVideo: |
| video_tracks_.push_back(track); |
| break; |
| } |
| track->RegisterMediaStream(this); |
| descriptor_->AddComponent(track->Component()); |
| |
| ScheduleDispatchEvent( |
| MediaStreamTrackEvent::Create(EventTypeNames::addtrack, track)); |
| |
| if (!active() && !track->Ended()) { |
| descriptor_->SetActive(true); |
| ScheduleDispatchEvent(Event::Create(EventTypeNames::active)); |
| } |
| } |
| |
| void MediaStream::RemoveTrackAndFireEvents(MediaStreamTrack* track) { |
| DCHECK(track); |
| RemoveTrackByComponentAndFireEvents(track->Component()); |
| } |
| |
| void MediaStream::ScheduleDispatchEvent(Event* event) { |
| scheduled_events_.push_back(event); |
| |
| if (!scheduled_event_timer_.IsActive()) |
| scheduled_event_timer_.StartOneShot(TimeDelta(), FROM_HERE); |
| } |
| |
| void MediaStream::ScheduledEventTimerFired(TimerBase*) { |
| if (!GetExecutionContext()) |
| return; |
| |
| HeapVector<Member<Event>> events; |
| events.swap(scheduled_events_); |
| |
| HeapVector<Member<Event>>::iterator it = events.begin(); |
| for (; it != events.end(); ++it) |
| DispatchEvent(*it->Release()); |
| |
| events.clear(); |
| } |
| |
| void MediaStream::Trace(blink::Visitor* visitor) { |
| visitor->Trace(audio_tracks_); |
| visitor->Trace(video_tracks_); |
| visitor->Trace(descriptor_); |
| visitor->Trace(observers_); |
| visitor->Trace(scheduled_events_); |
| EventTargetWithInlineData::Trace(visitor); |
| ContextClient::Trace(visitor); |
| MediaStreamDescriptorClient::Trace(visitor); |
| } |
| |
| MediaStream* ToMediaStream(MediaStreamDescriptor* descriptor) { |
| return static_cast<MediaStream*>(descriptor->Client()); |
| } |
| |
| } // namespace blink |