| // 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 "base/bind.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "content/renderer/media/html_video_element_capturer_source.h" |
| #include "media/base/limits.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/WebKit/public/platform/WebMediaPlayer.h" |
| #include "third_party/WebKit/public/platform/WebString.h" |
| |
| using ::testing::_; |
| using ::testing::InSequence; |
| using ::testing::Mock; |
| using ::testing::SaveArg; |
| |
| namespace content { |
| |
| ACTION_P(RunClosure, closure) { |
| closure.Run(); |
| } |
| |
| // An almost empty WebMediaPlayer to override paint() method. |
| class MockWebMediaPlayer : public blink::WebMediaPlayer, |
| public base::SupportsWeakPtr<MockWebMediaPlayer> { |
| public: |
| MockWebMediaPlayer() = default; |
| ~MockWebMediaPlayer() override = default; |
| |
| void load(LoadType, const blink::WebMediaPlayerSource&, CORSMode) override {} |
| void play() override {} |
| void pause() override {} |
| bool supportsSave() const override { return true; } |
| void seek(double seconds) override {} |
| void setRate(double) override {} |
| void setVolume(double) override {} |
| blink::WebTimeRanges buffered() const override { |
| return blink::WebTimeRanges(); |
| } |
| blink::WebTimeRanges seekable() const override { |
| return blink::WebTimeRanges(); |
| } |
| void setSinkId(const blink::WebString& sinkId, |
| const blink::WebSecurityOrigin&, |
| blink::WebSetSinkIdCallbacks*) override {} |
| bool hasVideo() const override { return true; } |
| bool hasAudio() const override { return false; } |
| blink::WebSize naturalSize() const override { |
| return blink::WebSize(16, 10); |
| } |
| bool paused() const override { return false; } |
| bool seeking() const override { return false; } |
| double duration() const override { return 0.0; } |
| double currentTime() const override { return 0.0; } |
| NetworkState getNetworkState() const override { |
| return NetworkStateEmpty; |
| } |
| ReadyState getReadyState() const override { |
| return ReadyStateHaveNothing; |
| } |
| blink::WebString getErrorMessage() override { return blink::WebString(); } |
| bool didLoadingProgress() override { return true; } |
| bool hasSingleSecurityOrigin() const override { return true; } |
| bool didPassCORSAccessCheck() const override { return true; } |
| double mediaTimeForTimeValue(double timeValue) const override { return 0.0; } |
| unsigned decodedFrameCount() const override { return 0; } |
| unsigned droppedFrameCount() const override { return 0; } |
| unsigned corruptedFrameCount() const override { return 0; } |
| size_t audioDecodedByteCount() const override { return 0; } |
| size_t videoDecodedByteCount() const override { return 0; } |
| |
| void paint(blink::WebCanvas* canvas, |
| const blink::WebRect& paint_rectangle, |
| SkPaint&) override { |
| // We could fill in |canvas| with a meaningful pattern in ARGB and verify |
| // that is correctly captured (as I420) by HTMLVideoElementCapturerSource |
| // but I don't think that'll be easy/useful/robust, so just let go here. |
| return; |
| } |
| }; |
| |
| class HTMLVideoElementCapturerSourceTest : public testing::Test { |
| public: |
| HTMLVideoElementCapturerSourceTest() |
| : web_media_player_(new MockWebMediaPlayer()), |
| html_video_capturer_(new HtmlVideoElementCapturerSource( |
| web_media_player_->AsWeakPtr(), |
| base::ThreadTaskRunnerHandle::Get())) {} |
| |
| // Necessary callbacks and MOCK_METHODS for them. |
| MOCK_METHOD2(DoOnDeliverFrame, |
| void(const scoped_refptr<media::VideoFrame>&, base::TimeTicks)); |
| void OnDeliverFrame(const scoped_refptr<media::VideoFrame>& video_frame, |
| base::TimeTicks estimated_capture_time) { |
| DoOnDeliverFrame(video_frame, estimated_capture_time); |
| } |
| |
| MOCK_METHOD1(DoOnVideoCaptureDeviceFormats, |
| void(const media::VideoCaptureFormats&)); |
| void OnVideoCaptureDeviceFormats(const media::VideoCaptureFormats& formats) { |
| DoOnVideoCaptureDeviceFormats(formats); |
| } |
| |
| MOCK_METHOD1(DoOnRunning, void(bool)); |
| void OnRunning(bool state) { DoOnRunning(state); } |
| |
| protected: |
| // We need some kind of message loop to allow |html_video_capturer_| to |
| // schedule capture events. |
| const base::MessageLoopForUI message_loop_; |
| |
| std::unique_ptr<MockWebMediaPlayer> web_media_player_; |
| std::unique_ptr<HtmlVideoElementCapturerSource> html_video_capturer_; |
| }; |
| |
| // Constructs and destructs all objects, in particular |html_video_capturer_| |
| // and its inner object(s). This is a non trivial sequence. |
| TEST_F(HTMLVideoElementCapturerSourceTest, ConstructAndDestruct) {} |
| |
| // Checks that the usual sequence of GetCurrentSupportedFormats() -> |
| // StartCapture() -> StopCapture() works as expected and let it capture two |
| // frames. |
| TEST_F(HTMLVideoElementCapturerSourceTest, GetFormatsAndStartAndStop) { |
| InSequence s; |
| media::VideoCaptureFormats formats; |
| EXPECT_CALL(*this, DoOnVideoCaptureDeviceFormats(_)) |
| .Times(1) |
| .WillOnce(SaveArg<0>(&formats)); |
| |
| html_video_capturer_->GetCurrentSupportedFormats( |
| media::limits::kMaxCanvas /* max_requesteed_width */, |
| media::limits::kMaxCanvas /* max_requesteed_height */, |
| media::limits::kMaxFramesPerSecond /* max_requested_frame_rate */, |
| base::Bind( |
| &HTMLVideoElementCapturerSourceTest::OnVideoCaptureDeviceFormats, |
| base::Unretained(this))); |
| ASSERT_EQ(1u, formats.size()); |
| EXPECT_EQ(web_media_player_->naturalSize().width, |
| formats[0].frame_size.width()); |
| EXPECT_EQ(web_media_player_->naturalSize().height, |
| formats[0].frame_size.height()); |
| |
| media::VideoCaptureParams params; |
| params.requested_format = formats[0]; |
| |
| EXPECT_CALL(*this, DoOnRunning(true)).Times(1); |
| |
| base::RunLoop run_loop; |
| base::Closure quit_closure = run_loop.QuitClosure(); |
| EXPECT_CALL(*this, DoOnDeliverFrame(_, _)).Times(1); |
| EXPECT_CALL(*this, DoOnDeliverFrame(_, _)) |
| .Times(1) |
| .WillOnce(RunClosure(quit_closure)); |
| |
| html_video_capturer_->StartCapture( |
| params, base::Bind(&HTMLVideoElementCapturerSourceTest::OnDeliverFrame, |
| base::Unretained(this)), |
| base::Bind(&HTMLVideoElementCapturerSourceTest::OnRunning, |
| base::Unretained(this))); |
| |
| run_loop.Run(); |
| |
| html_video_capturer_->StopCapture(); |
| Mock::VerifyAndClearExpectations(this); |
| } |
| |
| } // namespace content |