MediaCaptureFromElement: add support for audio captureStream().
This CL extends support for capturing the audio part of
a <video> or <audio> tags ( "capture" here means creating
a MediaStream out of the HTMLElement)
It introduces an HtmlAudioCapturerSource is-a AudioCapturerSource
wrapped into an ExternalMediaStreamAudioSource to produce data
towards the audio track.
HtmlAudioCapturerSource also plugs into the
WebMediaPlayer's WebAudioSourceProviderImpl to get
a copy of the audio being rendered.
Unit tests are added, and the existing LayouTests
revamped (and split into several files for clarity).
BUG=569976, 575492
TEST= run chromium with
--enable-blink-features=MediaCaptureFromVideo
against e.g.
https://rawgit.com/Miguelao/demos/master/videoelementcapture.html
Review-Url: https://codereview.chromium.org/1599533003
Cr-Commit-Position: refs/heads/master@{#395205}
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index 2e4eb60..5861d36 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -314,6 +314,8 @@
'renderer/media/cdm/ppapi_decryptor.h',
'renderer/media/cdm/render_cdm_factory.cc',
'renderer/media/cdm/render_cdm_factory.h',
+ 'renderer/media/html_audio_element_capturer_source.cc',
+ 'renderer/media/html_audio_element_capturer_source.h',
'renderer/media/external_media_stream_audio_source.cc',
'renderer/media/external_media_stream_audio_source.h',
'renderer/media/media_permission_dispatcher.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 12a4b1a..061b6476 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -722,6 +722,7 @@
'renderer/media/android/media_info_loader_unittest.cc',
'renderer/media/audio_message_filter_unittest.cc',
'renderer/media/audio_renderer_mixer_manager_unittest.cc',
+ 'renderer/media/html_audio_element_capturer_source_unittest.cc',
'renderer/media/media_stream_audio_unittest.cc',
'renderer/media/midi_message_filter_unittest.cc',
'renderer/media/mock_audio_device_factory.cc',
diff --git a/content/renderer/media/html_audio_element_capturer_source.cc b/content/renderer/media/html_audio_element_capturer_source.cc
new file mode 100644
index 0000000..399c221
--- /dev/null
+++ b/content/renderer/media/html_audio_element_capturer_source.cc
@@ -0,0 +1,89 @@
+// Copyright 2016 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 "content/renderer/media/html_audio_element_capturer_source.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/audio_renderer_sink.h"
+#include "media/blink/webaudiosourceprovider_impl.h"
+#include "media/blink/webmediaplayer_impl.h"
+#include "third_party/WebKit/public/platform/WebMediaPlayer.h"
+
+namespace content {
+
+//static
+HtmlAudioElementCapturerSource*
+HtmlAudioElementCapturerSource::CreateFromWebMediaPlayerImpl(
+ blink::WebMediaPlayer* player) {
+ DCHECK(player);
+ return new HtmlAudioElementCapturerSource(
+ static_cast<media::WebAudioSourceProviderImpl*>(
+ player->getAudioSourceProvider()));
+}
+
+HtmlAudioElementCapturerSource::HtmlAudioElementCapturerSource(
+ media::WebAudioSourceProviderImpl* audio_source)
+ : MediaStreamAudioSource(true /* is_local_source */),
+ audio_source_(audio_source),
+ is_started_(false),
+ last_sample_rate_(0),
+ last_num_channels_(0),
+ last_bus_frames_(0) {
+ DCHECK(audio_source_);
+}
+
+HtmlAudioElementCapturerSource::~HtmlAudioElementCapturerSource() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ EnsureSourceIsStopped();
+}
+
+bool HtmlAudioElementCapturerSource::EnsureSourceIsStarted() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (audio_source_ && !is_started_) {
+ // base:Unretained() is safe here since EnsureSourceIsStopped() guarantees
+ // no more calls to OnAudioBus().
+ audio_source_->SetCopyAudioCallback(base::Bind(
+ &HtmlAudioElementCapturerSource::OnAudioBus, base::Unretained(this)));
+ is_started_ = true;
+ }
+ return is_started_;
+}
+
+void HtmlAudioElementCapturerSource::EnsureSourceIsStopped() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!is_started_)
+ return;
+
+ if (audio_source_) {
+ audio_source_->ClearCopyAudioCallback();
+ audio_source_ = nullptr;
+ }
+ is_started_ = false;
+}
+
+void HtmlAudioElementCapturerSource::OnAudioBus(
+ std::unique_ptr<media::AudioBus> audio_bus,
+ uint32_t delay_milliseconds,
+ int sample_rate) {
+ const base::TimeTicks capture_time =
+ base::TimeTicks::Now() -
+ base::TimeDelta::FromMilliseconds(delay_milliseconds);
+
+ if (sample_rate != last_sample_rate_ ||
+ audio_bus->channels() != last_num_channels_ ||
+ audio_bus->frames() != last_bus_frames_) {
+ MediaStreamAudioSource::SetFormat(
+ media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::GuessChannelLayout(audio_bus->channels()),
+ sample_rate, 16, audio_bus->frames()));
+ last_sample_rate_ = sample_rate;
+ last_num_channels_ = audio_bus->channels();
+ last_bus_frames_ = audio_bus->frames();
+ }
+
+ MediaStreamAudioSource::DeliverDataToTracks(*audio_bus, capture_time);
+}
+
+} // namespace content
diff --git a/content/renderer/media/html_audio_element_capturer_source.h b/content/renderer/media/html_audio_element_capturer_source.h
new file mode 100644
index 0000000..2bbfb3c
--- /dev/null
+++ b/content/renderer/media/html_audio_element_capturer_source.h
@@ -0,0 +1,63 @@
+// Copyright 2016 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.
+
+#ifndef CONTENT_RENDERER_MEDIA_HTML_AUDIO_ELEMENT_CAPTURER_SOURCE_H_
+#define CONTENT_RENDERER_MEDIA_HTML_AUDIO_ELEMENT_CAPTURER_SOURCE_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "content/common/content_export.h"
+#include "content/renderer/media/media_stream_audio_source.h"
+
+namespace blink {
+class WebMediaPlayer;
+} // namespace blink
+
+namespace media {
+class AudioBus;
+class WebAudioSourceProviderImpl;
+} // namespace media
+
+namespace content {
+
+// This class is a MediaStreamAudioSource that registers to the constructor-
+// passed weak WebAudioSourceProviderImpl to receive a copy of the audio data
+// intended for rendering. This copied data is received on OnAudioBus() and sent
+// to all the registered Tracks.
+class CONTENT_EXPORT HtmlAudioElementCapturerSource final
+ : NON_EXPORTED_BASE(public MediaStreamAudioSource) {
+ public:
+ static HtmlAudioElementCapturerSource*
+ CreateFromWebMediaPlayerImpl(blink::WebMediaPlayer* player);
+
+ explicit HtmlAudioElementCapturerSource(
+ media::WebAudioSourceProviderImpl* audio_source);
+ ~HtmlAudioElementCapturerSource() override;
+
+ private:
+ // MediaStreamAudioSource implementation.
+ bool EnsureSourceIsStarted() final;
+ void EnsureSourceIsStopped() final;
+
+ // To act as an WebAudioSourceProviderImpl::CopyAudioCB.
+ void OnAudioBus(std::unique_ptr<media::AudioBus> audio_bus,
+ uint32_t delay_milliseconds,
+ int sample_rate);
+
+ scoped_refptr<media::WebAudioSourceProviderImpl> audio_source_;
+
+ bool is_started_;
+ int last_sample_rate_;
+ int last_num_channels_;
+ int last_bus_frames_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(HtmlAudioElementCapturerSource);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_MEDIA_HTML_AUDIO_ELEMENT_CAPTURER_SOURCE_H_
diff --git a/content/renderer/media/html_audio_element_capturer_source_unittest.cc b/content/renderer/media/html_audio_element_capturer_source_unittest.cc
new file mode 100644
index 0000000..10d24ed
--- /dev/null
+++ b/content/renderer/media/html_audio_element_capturer_source_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright 2016 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 "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/renderer/media_stream_audio_sink.h"
+#include "content/renderer/media/html_audio_element_capturer_source.h"
+#include "content/renderer/media/media_stream_audio_track.h"
+#include "media/audio/null_audio_sink.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/fake_audio_render_callback.h"
+#include "media/blink/webaudiosourceprovider_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/web/WebHeap.h"
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::InSequence;
+using ::testing::Mock;
+using ::testing::Property;
+
+namespace content {
+
+static const int kNumChannelsForTest = 1;
+static const int kBufferDurationMs = 10;
+
+static const int kAudioTrackSampleRate = 48000;
+static const int kAudioTrackSamplesPerBuffer =
+ kAudioTrackSampleRate * kBufferDurationMs /
+ base::Time::kMillisecondsPerSecond;
+
+ACTION_P(RunClosure, closure) {
+ closure.Run();
+}
+
+//
+class MockMediaStreamAudioSink final : public MediaStreamAudioSink {
+ public:
+ MockMediaStreamAudioSink() : MediaStreamAudioSink() {}
+ ~MockMediaStreamAudioSink() = default;
+
+ MOCK_METHOD1(OnSetFormat, void(const media::AudioParameters& params));
+ MOCK_METHOD2(OnData,
+ void(const media::AudioBus& audio_bus,
+ base::TimeTicks estimated_capture_time));
+
+ DISALLOW_COPY_AND_ASSIGN(MockMediaStreamAudioSink);
+};
+
+// This test needs to bundle together plenty of objects, namely:
+// - a WebAudioSourceProviderImpl, which in turn needs an Audio Sink, in this
+// case a NullAudioSink. This is needed to plug HTMLAudioElementCapturerSource
+// and inject audio.
+// - a WebMediaStreamSource, that owns the HTMLAudioElementCapturerSource under
+// test, and a WebMediaStreamAudioTrack, that the class under test needs to
+// connect to in order to operate correctly. This class has an inner content
+// MediaStreamAudioTrack.
+// - finally, a MockMediaStreamAudioSink to observe captured audio frames, and
+// that plugs into the former MediaStreamAudioTrack.
+class HTMLAudioElementCapturerSourceTest : public testing::Test {
+ public:
+ HTMLAudioElementCapturerSourceTest()
+ : fake_callback_(0.1),
+ audio_source_(new media::WebAudioSourceProviderImpl(
+ new media::NullAudioSink(base::ThreadTaskRunnerHandle::Get()))) {}
+
+ void SetUp() final {
+ const media::AudioParameters params(
+ media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::GuessChannelLayout(kNumChannelsForTest),
+ kAudioTrackSampleRate /* sample_rate */, 16 /* bits_per_sample */,
+ kAudioTrackSamplesPerBuffer /* frames_per_buffer */);
+ audio_source_->Initialize(params, &fake_callback_);
+
+ blink_audio_source_.initialize(blink::WebString::fromUTF8("audio_id"),
+ blink::WebMediaStreamSource::TypeAudio,
+ blink::WebString::fromUTF8("audio_track"),
+ false /* remote */);
+ blink_audio_track_.initialize(blink_audio_source_.id(),
+ blink_audio_source_);
+
+ // |blink_audio_source_| takes ownership of HtmlAudioElementCapturerSource.
+ blink_audio_source_.setExtraData(
+ new HtmlAudioElementCapturerSource(audio_source_.get()));
+ ASSERT_TRUE(source()->ConnectToTrack(blink_audio_track_));
+ }
+
+ void TearDown() override {
+ blink_audio_track_.reset();
+ blink_audio_source_.reset();
+ blink::WebHeap::collectAllGarbageForTesting();
+ }
+
+ HtmlAudioElementCapturerSource* source() const {
+ return static_cast<HtmlAudioElementCapturerSource*>(
+ MediaStreamAudioSource::From(blink_audio_source_));
+ }
+
+ MediaStreamAudioTrack* track() const {
+ return MediaStreamAudioTrack::From(blink_audio_track_);
+ }
+
+ int InjectAudio(media::AudioBus* audio_bus) {
+ return audio_source_->RenderForTesting(audio_bus);
+ }
+
+ protected:
+ const base::MessageLoop message_loop_;
+
+ blink::WebMediaStreamSource blink_audio_source_;
+ blink::WebMediaStreamTrack blink_audio_track_;
+
+ media::FakeAudioRenderCallback fake_callback_;
+ scoped_refptr<media::WebAudioSourceProviderImpl> audio_source_;
+};
+
+// Constructs and destructs all objects. This is a non trivial sequence.
+TEST_F(HTMLAudioElementCapturerSourceTest, ConstructAndDestruct) {
+}
+
+// This test verifies that Audio can be properly captured when injected in the
+// WebAudioSourceProviderImpl.
+TEST_F(HTMLAudioElementCapturerSourceTest, CaptureAudio) {
+ InSequence s;
+
+ base::RunLoop run_loop;
+ base::Closure quit_closure = run_loop.QuitClosure();
+
+ MockMediaStreamAudioSink sink;
+ track()->AddSink(&sink);
+ EXPECT_CALL(sink, OnSetFormat(_)).Times(1);
+ EXPECT_CALL(
+ sink,
+ OnData(AllOf(Property(&media::AudioBus::channels, kNumChannelsForTest),
+ Property(&media::AudioBus::frames,
+ kAudioTrackSamplesPerBuffer)),
+ _))
+ .Times(1)
+ .WillOnce(RunClosure(quit_closure));
+
+ std::unique_ptr<media::AudioBus> bus = media::AudioBus::Create(
+ kNumChannelsForTest, kAudioTrackSamplesPerBuffer);
+ InjectAudio(bus.get());
+ run_loop.Run();
+
+ track()->Stop();
+ track()->RemoveSink(&sink);
+}
+
+} // namespace content
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 478cf81..e0cb4c1 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -8,6 +8,7 @@
#include "base/command_line.h"
#include "base/files/file_path.h"
+#include "base/guid.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/logging.h"
@@ -61,6 +62,7 @@
#include "content/renderer/gamepad_shared_memory_reader.h"
#include "content/renderer/media/audio_decoder.h"
#include "content/renderer/media/canvas_capture_handler.h"
+#include "content/renderer/media/html_audio_element_capturer_source.h"
#include "content/renderer/media/html_video_element_capturer_source.h"
#include "content/renderer/media/image_capture_frame_grabber.h"
#include "content/renderer/media/media_recorder_handler.h"
@@ -975,6 +977,33 @@
#endif
}
+void RendererBlinkPlatformImpl::createHTMLAudioElementCapturer(
+ WebMediaStream* web_media_stream,
+ WebMediaPlayer* web_media_player) {
+ DCHECK(web_media_stream);
+ DCHECK(web_media_player);
+
+ blink::WebMediaStreamSource web_media_stream_source;
+ blink::WebMediaStreamTrack web_media_stream_track;
+ const WebString track_id = WebString::fromUTF8(base::GenerateGUID());
+
+ web_media_stream_source.initialize(track_id,
+ blink::WebMediaStreamSource::TypeAudio,
+ track_id,
+ false /* is_remote */);
+ web_media_stream_track.initialize(web_media_stream_source);
+
+ MediaStreamAudioSource* const media_stream_source =
+ HtmlAudioElementCapturerSource::CreateFromWebMediaPlayerImpl(
+ web_media_player);
+
+ // Takes ownership of |media_stream_source|.
+ web_media_stream_source.setExtraData(media_stream_source);
+
+ media_stream_source->ConnectToTrack(web_media_stream_track);
+ web_media_stream->addTrack(web_media_stream_track);
+}
+
//------------------------------------------------------------------------------
WebImageCaptureFrameGrabber*
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 85b8fb8b..dab5beff 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -177,6 +177,9 @@
void createHTMLVideoElementCapturer(
blink::WebMediaStream* web_media_stream,
blink::WebMediaPlayer* web_media_player) override;
+ void createHTMLAudioElementCapturer(
+ blink::WebMediaStream* web_media_stream,
+ blink::WebMediaPlayer* web_media_player) override;
blink::WebImageCaptureFrameGrabber* createImageCaptureFrameGrabber() override;
blink::WebGraphicsContext3DProvider* createOffscreenGraphicsContext3DProvider(
const blink::Platform::ContextAttributes& attributes,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 698024c..3d88c79 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -728,6 +728,7 @@
".",
"//content")
deps += [
+ "//media/blink",
"//third_party/libjingle:libjingle_webrtc",
"//third_party/webrtc/base:rtc_base",
"//third_party/webrtc/modules/desktop_capture:primitives",
diff --git a/media/blink/webaudiosourceprovider_impl.cc b/media/blink/webaudiosourceprovider_impl.cc
index 3f052db..31bb5aa 100644
--- a/media/blink/webaudiosourceprovider_impl.cc
+++ b/media/blink/webaudiosourceprovider_impl.cc
@@ -233,6 +233,10 @@
void WebAudioSourceProviderImpl::SetCopyAudioCallback(
const CopyAudioCB& callback) {
DCHECK(!callback.is_null());
+
+ // Use |sink_lock_| to protect |tee_filter_| too since they go in lockstep.
+ base::AutoLock auto_lock(sink_lock_);
+
DCHECK(tee_filter_);
tee_filter_->set_copy_audio_bus_callback(callback);
}
@@ -242,6 +246,10 @@
tee_filter_->set_copy_audio_bus_callback(CopyAudioCB());
}
+int WebAudioSourceProviderImpl::RenderForTesting(AudioBus* audio_bus) {
+ return tee_filter_->Render(audio_bus, 0, 0);
+}
+
void WebAudioSourceProviderImpl::OnSetFormat() {
base::AutoLock auto_lock(sink_lock_);
if (!client_)
@@ -251,10 +259,6 @@
client_->setFormat(tee_filter_->channels(), tee_filter_->sample_rate());
}
-int WebAudioSourceProviderImpl::RenderForTesting(AudioBus* audio_bus) {
- return tee_filter_->Render(audio_bus, 0, 0);
-}
-
int WebAudioSourceProviderImpl::TeeFilter::Render(AudioBus* audio_bus,
uint32_t delay_milliseconds,
uint32_t frames_skipped) {
diff --git a/media/blink/webaudiosourceprovider_impl.h b/media/blink/webaudiosourceprovider_impl.h
index 6bf3b0a..eea08ca1 100644
--- a/media/blink/webaudiosourceprovider_impl.h
+++ b/media/blink/webaudiosourceprovider_impl.h
@@ -72,6 +72,8 @@
void SetCopyAudioCallback(const CopyAudioCB& callback);
void ClearCopyAudioCallback();
+ int RenderForTesting(AudioBus* audio_bus);
+
private:
friend class WebAudioSourceProviderImplTest;
~WebAudioSourceProviderImpl() override;
@@ -79,8 +81,6 @@
// Calls setFormat() on |client_| from the Blink renderer thread.
void OnSetFormat();
- int RenderForTesting(AudioBus* audio_bus);
-
// Used to keep the volume across reconfigurations.
double volume_;
diff --git a/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-EME-content.html b/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-EME-content.html
new file mode 100644
index 0000000..0c2b0db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-EME-content.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script src=../../resources/testharness.js></script>
+<script src=../../resources/testharnessreport.js></script>
+<script>
+
+// Run createStream() on a <video> source with protected content.
+
+test(function() {
+ var video = document.createElement('video');
+
+ assert_equals(video.error, null);
+ assert_equals(video.mediaKeys, null);
+ video.onencrypted = this.step_func_done();
+
+ navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
+ return access.createMediaKeys();
+ }).then(function(mediaKeys) {
+ return video.setMediaKeys(mediaKeys);
+ }).then(function(result) {
+ video.src = "../../media/content/test-encrypted.webm";
+ assert_throws("NotSupportedError",
+ function() { var stream = video.captureStream(); },
+ "Cannot create a captureStream() out of a protected <video>");
+ });
+
+}, 'check <video> captureStream() fails on an encrypted/protected media');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-capture.html b/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-capture.html
new file mode 100644
index 0000000..120711b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-capture.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<script src=../../resources/testharness.js></script>
+<script src=../../resources/testharnessreport.js></script>
+<script>
+
+// Run captureStream() on different videos, and assert data is flowing.
+
+var makeAsyncTest = function(filename) {
+ async_test(function(test) {
+ var video = document.createElement('video');
+ video.src = "../../http/tests/media/resources/media-source/webm/" + filename;
+ video.onerror = this.unreached_func("<video> error");
+
+ video.onloadedmetadata = this.step_func(function() {
+ var stream = video.captureStream();
+ var recorder = new MediaRecorder(stream);
+ recorder.ondataavailable = test.step_func_done(function(event) {
+ assert_true(event.data.size > 0, 'Recorded data size should be > 0');
+ });
+
+ recorder.start();
+ video.play();
+
+ });
+
+ video.load();
+ }), "<video>.captureStream() and assert data flows.";
+};
+
+generate_tests(makeAsyncTest,
+ [[ "video-only", "test-v-128k-320x240-24fps-8kfr.webm"],
+ [ "audio-only", "test-a-128k-44100Hz-1ch.webm"],
+ [ "video+audio", "test.webm"]]);
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-creation.html b/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-creation.html
index d481fc8..a9be507 100644
--- a/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-creation.html
+++ b/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/HTMLMediaElementCapture-creation.html
@@ -2,7 +2,8 @@
<script src=../../resources/testharness.js></script>
<script src=../../resources/testharnessreport.js></script>
<script>
-// Run createStream() on <video>s and <audio>s.
+
+// Run captureStream() on <video>/<audio>s and inspect the generated Stream.
test(function() {
var video = document.createElement('video');
@@ -16,49 +17,30 @@
"captureStream() cannot be created out of a source-less <audio>" );
}, 'check that captureStream() raises an exception on an <audio> with no source.');
-test(function() {
- var video = document.createElement('video');
- video.src = "file:///super_duper_videos/amazing_video.webm";
- video.onloadstart = function() {
- var stream = video.captureStream();
+var makeAsyncTest = function(filename, num_video_tracks, num_audio_tracks) {
+ async_test(function() {
+ var video = document.createElement('video');
+ video.src = "../../http/tests/media/resources/media-source/webm/" + filename;
+ video.onerror = this.unreached_func("<video> error");
- assert_not_equals(stream, null);
- assert_equals(1, stream.getVideoTracks().length);
- assert_equals(0, stream.getAudioTracks().length);
- }
-}, 'check <video> captureStream().');
+ video.onloadedmetadata = this.step_func_done(function() {
+ assert_equals(video.audioTracks.length, num_audio_tracks);
+ assert_equals(video.videoTracks.length, num_video_tracks);
-test(function() {
- var audio = document.createElement('audio');
- audio.src = "file:///super_duper_videos/amazing_audio_file.webm";
- audio.onloadstart = function() {
- var stream = audio.captureStream();
+ var stream = video.captureStream();
+ assert_not_equals(stream, null, "error generating stream");
- // TODO(mcasas): http://crbug.com/575492, implement <audio>.captureStream().
- assert_equals(stream, null);
- };
-}, 'check <audio> captureStream().');
+ assert_equals(stream.getAudioTracks().length, num_audio_tracks);
+ assert_equals(stream.getVideoTracks().length, num_video_tracks);
+ });
-test(function() {
- var video = document.createElement('video');
+ video.load();
+ }), "<video>.captureStream()";
+};
- const onEncrypted = this.step_func_done();
-
- assert_equals(null, video.error);
- assert_equals(null, video.mediaKeys);
- video.onencrypted = onEncrypted;
-
- navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
- return access.createMediaKeys();
- }).then(function(mediaKeys) {
- return video.setMediaKeys(mediaKeys);
- }).then(function(result) {
- video.src = "../../media/content/test-encrypted.webm";
- assert_throws("NotSupportedError",
- function() { var stream = video.captureStream(); },
- "Cannot create a captureStream() out of a protected <video>");
- });
-
-}, 'check <video> captureStream() fails on an encrypted/protected media');
+generate_tests(makeAsyncTest,
+ [[ "video-only", "test-v-128k-320x240-24fps-8kfr.webm", 1, 0 ],
+ [ "audio-only", "test-a-128k-44100Hz-1ch.webm", 0, 1 ],
+ [ "video+audio", "test.webm", 1, 1 ]]);
</script>
diff --git a/third_party/WebKit/Source/modules/mediacapturefromelement/HTMLMediaElementCapture.cpp b/third_party/WebKit/Source/modules/mediacapturefromelement/HTMLMediaElementCapture.cpp
index 478a74d..aad4dab 100644
--- a/third_party/WebKit/Source/modules/mediacapturefromelement/HTMLMediaElementCapture.cpp
+++ b/third_party/WebKit/Source/modules/mediacapturefromelement/HTMLMediaElementCapture.cpp
@@ -6,6 +6,8 @@
#include "core/dom/ExceptionCode.h"
#include "core/html/HTMLMediaElement.h"
+#include "core/html/track/AudioTrackList.h"
+#include "core/html/track/VideoTrackList.h"
#include "modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h"
#include "modules/encryptedmedia/MediaKeys.h"
#include "modules/mediastream/MediaStream.h"
@@ -37,17 +39,14 @@
return MediaStream::create(element.getExecutionContext(), MediaStreamRegistry::registry().lookupMediaStreamDescriptor(element.currentSrc().getString()));
}
- // TODO(mcasas): Only <video> tags are supported at the moment.
- if (element.isHTMLAudioElement()) {
- NOTIMPLEMENTED();
- return nullptr;
- }
-
WebMediaStream webStream;
webStream.initialize(WebVector<WebMediaStreamTrack>(), WebVector<WebMediaStreamTrack>());
MediaStreamCenter::instance().didCreateMediaStream(webStream);
- Platform::current()->createHTMLVideoElementCapturer(&webStream, element.webMediaPlayer());
+ if (element.hasVideo())
+ Platform::current()->createHTMLVideoElementCapturer(&webStream, element.webMediaPlayer());
+ if (element.hasAudio())
+ Platform::current()->createHTMLAudioElementCapturer(&webStream, element.webMediaPlayer());
return MediaStream::create(element.getExecutionContext(), webStream);
}
diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h
index d948c461..751a5fd0 100644
--- a/third_party/WebKit/public/platform/Platform.h
+++ b/third_party/WebKit/public/platform/Platform.h
@@ -515,7 +515,8 @@
// Fills in the WebMediaStream to capture from the WebMediaPlayer identified
// by the second parameter.
- virtual void createHTMLVideoElementCapturer(WebMediaStream*, WebMediaPlayer*) {}
+ virtual void createHTMLVideoElementCapturer(WebMediaStream*, WebMediaPlayer*) { }
+ virtual void createHTMLAudioElementCapturer(WebMediaStream*, WebMediaPlayer*) { }
// Creates a WebImageCaptureFrameGrabber to take a snapshot of a Video Tracks.
// May return null if the functionality is not available.