blob: 3be95fdb083e699e6d796fa57bba228786d1e209 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/web_media_stream.h"
#include "third_party/blink/public/platform/web_media_stream_track.h"
#include "third_party/blink/renderer/core/dom/events/event_listener.h"
#include "third_party/blink/renderer/core/html/media/html_media_element.h"
#include "third_party/blink/renderer/core/html/track/audio_track_list.h"
#include "third_party/blink/renderer/core/html/track/video_track_list.h"
#include "third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.h"
#include "third_party/blink/renderer/modules/encryptedmedia/media_keys.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_center.h"
namespace blink {
namespace {
// Class to register to the events of |m_mediaElement|, acting accordingly on
// the tracks of |m_mediaStream|.
class MediaElementEventListener final : public EventListener {
WTF_MAKE_NONCOPYABLE(MediaElementEventListener);
public:
MediaElementEventListener(HTMLMediaElement*, MediaStream*);
void UpdateSources(ExecutionContext*);
void Trace(blink::Visitor*) override;
private:
// EventListener implementation.
void Invoke(ExecutionContext*, Event*) override;
bool operator==(const EventListener& other) const override {
return this == &other;
}
Member<HTMLMediaElement> media_element_;
Member<MediaStream> media_stream_;
HeapHashSet<WeakMember<MediaStreamSource>> sources_;
};
MediaElementEventListener::MediaElementEventListener(HTMLMediaElement* element,
MediaStream* stream)
: EventListener(kCPPEventListenerType),
media_element_(element),
media_stream_(stream) {
UpdateSources(element->GetExecutionContext());
}
void MediaElementEventListener::Invoke(ExecutionContext* context,
Event* event) {
DVLOG(2) << __func__ << " " << event->type();
DCHECK(media_stream_);
if (event->type() == event_type_names::kEnded) {
const MediaStreamTrackVector tracks = media_stream_->getTracks();
for (const auto& track : tracks) {
track->stopTrack(context);
media_stream_->RemoveTrackByComponentAndFireEvents(track->Component());
}
media_stream_->StreamEnded();
return;
}
if (event->type() != event_type_names::kLoadedmetadata)
return;
// If |media_element_| is a MediaStream, clone the new tracks.
if (media_element_->GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream) {
const MediaStreamTrackVector tracks = media_stream_->getTracks();
for (const auto& track : tracks) {
track->stopTrack(context);
media_stream_->RemoveTrackByComponentAndFireEvents(track->Component());
}
MediaStreamDescriptor* const descriptor = media_element_->GetSrcObject();
DCHECK(descriptor);
for (unsigned i = 0; i < descriptor->NumberOfAudioComponents(); i++) {
media_stream_->AddTrackByComponentAndFireEvents(
descriptor->AudioComponent(i));
}
for (unsigned i = 0; i < descriptor->NumberOfVideoComponents(); i++) {
media_stream_->AddTrackByComponentAndFireEvents(
descriptor->VideoComponent(i));
}
UpdateSources(context);
return;
}
WebMediaStream web_stream;
web_stream.Initialize(WebVector<WebMediaStreamTrack>(),
WebVector<WebMediaStreamTrack>());
if (media_element_->HasVideo()) {
Platform::Current()->CreateHTMLVideoElementCapturer(
&web_stream, media_element_->GetWebMediaPlayer(),
media_element_->GetExecutionContext()->GetTaskRunner(
TaskType::kInternalMediaRealTime));
}
if (media_element_->HasAudio()) {
Platform::Current()->CreateHTMLAudioElementCapturer(
&web_stream, media_element_->GetWebMediaPlayer());
}
WebVector<WebMediaStreamTrack> video_tracks = web_stream.VideoTracks();
for (const auto& track : video_tracks)
media_stream_->AddTrackByComponentAndFireEvents(track);
WebVector<WebMediaStreamTrack> audio_tracks = web_stream.AudioTracks();
for (const auto& track : audio_tracks)
media_stream_->AddTrackByComponentAndFireEvents(track);
DVLOG(2) << "#videotracks: " << video_tracks.size()
<< " #audiotracks: " << audio_tracks.size();
UpdateSources(context);
}
void MediaElementEventListener::UpdateSources(ExecutionContext* context) {
for (auto track : media_stream_->getTracks())
sources_.insert(track->Component()->Source());
if (!media_element_->currentSrc().IsEmpty() &&
!media_element_->IsMediaDataCorsSameOrigin()) {
for (auto source : sources_)
MediaStreamCenter::Instance().DidStopMediaStreamSource(source);
}
}
void MediaElementEventListener::Trace(blink::Visitor* visitor) {
visitor->Trace(media_element_);
visitor->Trace(media_stream_);
visitor->Trace(sources_);
EventListener::Trace(visitor);
}
} // anonymous namespace
// static
MediaStream* HTMLMediaElementCapture::captureStream(
ScriptState* script_state,
HTMLMediaElement& element,
ExceptionState& exception_state) {
// Avoid capturing from EME-protected Media Elements.
if (HTMLMediaElementEncryptedMedia::mediaKeys(element)) {
// This exception is not defined in the spec, see
// https://github.com/w3c/mediacapture-fromelement/issues/20.
exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
"Stream capture not supported with EME");
return nullptr;
}
ExecutionContext* context = ExecutionContext::From(script_state);
if (!element.currentSrc().IsEmpty() && !element.IsMediaDataCorsSameOrigin()) {
exception_state.ThrowSecurityError(
"Cannot capture from element with cross-origin data");
return nullptr;
}
WebMediaStream web_stream;
web_stream.Initialize(WebVector<WebMediaStreamTrack>(),
WebVector<WebMediaStreamTrack>());
// Create() duplicates the MediaStreamTracks inside |webStream|.
MediaStream* stream = MediaStream::Create(context, web_stream);
MediaElementEventListener* listener =
MakeGarbageCollected<MediaElementEventListener>(&element, stream);
element.addEventListener(event_type_names::kLoadedmetadata, listener, false);
element.addEventListener(event_type_names::kEnded, listener, false);
// If |element| is actually playing a MediaStream, just clone it.
if (element.GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream) {
MediaStreamDescriptor* const descriptor = element.GetSrcObject();
DCHECK(descriptor);
return MediaStream::Create(context, descriptor);
}
if (element.HasVideo()) {
Platform::Current()->CreateHTMLVideoElementCapturer(
&web_stream, element.GetWebMediaPlayer(),
element.GetExecutionContext()->GetTaskRunner(
TaskType::kInternalMediaRealTime));
}
if (element.HasAudio()) {
Platform::Current()->CreateHTMLAudioElementCapturer(
&web_stream, element.GetWebMediaPlayer());
}
listener->UpdateSources(context);
// If element.currentSrc().isNull() then |stream| will have no tracks, those
// will be added eventually afterwards via MediaElementEventListener.
return stream;
}
} // namespace blink