1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h"
6 
7 #include "base/bind.h"
8 #include "base/run_loop.h"
9 #include "base/test/gmock_callback_support.h"
10 #include "media/base/limits.h"
11 #include "testing/gmock/include/gmock/gmock.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
14 #include "third_party/blink/public/platform/web_size.h"
15 #include "third_party/blink/public/web/web_heap.h"
16 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h"
17 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
18 #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
19 #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
20 #include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
21 #include "third_party/skia/include/core/SkImage.h"
22 #include "third_party/skia/include/core/SkRefCnt.h"
23 
24 using base::test::RunOnceClosure;
25 using ::testing::_;
26 using ::testing::InSequence;
27 using ::testing::Mock;
28 using ::testing::SaveArg;
29 using ::testing::Test;
30 using ::testing::TestWithParam;
31 
32 namespace blink {
33 
34 namespace {
35 
36 static const int kTestCanvasCaptureWidth = 320;
37 static const int kTestCanvasCaptureHeight = 240;
38 static const double kTestCanvasCaptureFramesPerSecond = 55.5;
39 
40 static const int kTestCanvasCaptureFrameEvenSize = 2;
41 static const int kTestCanvasCaptureFrameOddSize = 3;
42 static const int kTestCanvasCaptureFrameColorErrorTolerance = 2;
43 static const int kTestAlphaValue = 175;
44 
45 }  // namespace
46 
47 class CanvasCaptureHandlerTest
48     : public TestWithParam<testing::tuple<bool, int, int>> {
49  public:
50   CanvasCaptureHandlerTest() = default;
51 
SetUp()52   void SetUp() override {
53     MediaStreamComponent* component = nullptr;
54     canvas_capture_handler_ = CanvasCaptureHandler::CreateCanvasCaptureHandler(
55         /*LocalFrame =*/nullptr,
56         blink::WebSize(kTestCanvasCaptureWidth, kTestCanvasCaptureHeight),
57         kTestCanvasCaptureFramesPerSecond,
58         blink::scheduler::GetSingleThreadTaskRunnerForTesting(), &component);
59     component_ = component;
60   }
61 
TearDown()62   void TearDown() override {
63     component_ = nullptr;
64     blink::WebHeap::CollectAllGarbageForTesting();
65     canvas_capture_handler_.reset();
66 
67     // Let the message loop run to finish destroying the capturer.
68     base::RunLoop().RunUntilIdle();
69   }
70 
71   // Necessary callbacks and MOCK_METHODS for VideoCapturerSource.
72   MOCK_METHOD2(DoOnDeliverFrame,
73                void(scoped_refptr<media::VideoFrame>, base::TimeTicks));
OnDeliverFrame(scoped_refptr<media::VideoFrame> video_frame,base::TimeTicks estimated_capture_time)74   void OnDeliverFrame(scoped_refptr<media::VideoFrame> video_frame,
75                       base::TimeTicks estimated_capture_time) {
76     DoOnDeliverFrame(std::move(video_frame), estimated_capture_time);
77   }
78 
79   MOCK_METHOD1(DoOnRunning, void(bool));
OnRunning(bool state)80   void OnRunning(bool state) { DoOnRunning(state); }
81 
82   // Verify returned frames.
GenerateTestImage(bool opaque,int width,int height)83   static scoped_refptr<StaticBitmapImage> GenerateTestImage(bool opaque,
84                                                             int width,
85                                                             int height) {
86     SkImageInfo info = SkImageInfo::MakeN32(
87         width, height, opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType,
88         SkColorSpace::MakeSRGB());
89     SkBitmap testBitmap;
90     testBitmap.allocPixels(info);
91     testBitmap.eraseARGB(opaque ? 255 : kTestAlphaValue, 30, 60, 200);
92     return UnacceleratedStaticBitmapImage::Create(
93         SkImage::MakeFromBitmap(testBitmap));
94   }
95 
OnVerifyDeliveredFrame(bool opaque,int expected_width,int expected_height,scoped_refptr<media::VideoFrame> video_frame,base::TimeTicks estimated_capture_time)96   void OnVerifyDeliveredFrame(bool opaque,
97                               int expected_width,
98                               int expected_height,
99                               scoped_refptr<media::VideoFrame> video_frame,
100                               base::TimeTicks estimated_capture_time) {
101     if (opaque)
102       EXPECT_EQ(media::PIXEL_FORMAT_I420, video_frame->format());
103     else
104       EXPECT_EQ(media::PIXEL_FORMAT_I420A, video_frame->format());
105 
106     const gfx::Size& size = video_frame->visible_rect().size();
107     EXPECT_EQ(expected_width, size.width());
108     EXPECT_EQ(expected_height, size.height());
109     const uint8_t* y_plane =
110         video_frame->visible_data(media::VideoFrame::kYPlane);
111     EXPECT_NEAR(74, y_plane[0], kTestCanvasCaptureFrameColorErrorTolerance);
112     const uint8_t* u_plane =
113         video_frame->visible_data(media::VideoFrame::kUPlane);
114     EXPECT_NEAR(193, u_plane[0], kTestCanvasCaptureFrameColorErrorTolerance);
115     const uint8_t* v_plane =
116         video_frame->visible_data(media::VideoFrame::kVPlane);
117     EXPECT_NEAR(105, v_plane[0], kTestCanvasCaptureFrameColorErrorTolerance);
118     if (!opaque) {
119       const uint8_t* a_plane =
120           video_frame->visible_data(media::VideoFrame::kAPlane);
121       EXPECT_EQ(kTestAlphaValue, a_plane[0]);
122     }
123   }
124 
125   Persistent<MediaStreamComponent> component_;
126   // The Class under test. Needs to be scoped_ptr to force its destruction.
127   std::unique_ptr<CanvasCaptureHandler> canvas_capture_handler_;
128 
129  protected:
GetVideoCapturerSource(blink::MediaStreamVideoCapturerSource * ms_source)130   media::VideoCapturerSource* GetVideoCapturerSource(
131       blink::MediaStreamVideoCapturerSource* ms_source) {
132     return ms_source->GetSourceForTesting();
133   }
134 
135   ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
136 
137  private:
138   DISALLOW_COPY_AND_ASSIGN(CanvasCaptureHandlerTest);
139 };
140 
141 // Checks that the initialization-destruction sequence works fine.
TEST_F(CanvasCaptureHandlerTest,ConstructAndDestruct)142 TEST_F(CanvasCaptureHandlerTest, ConstructAndDestruct) {
143   EXPECT_TRUE(canvas_capture_handler_->NeedsNewFrame());
144   base::RunLoop().RunUntilIdle();
145 }
146 
147 // Checks that the destruction sequence works fine.
TEST_F(CanvasCaptureHandlerTest,DestructTrack)148 TEST_F(CanvasCaptureHandlerTest, DestructTrack) {
149   EXPECT_TRUE(canvas_capture_handler_->NeedsNewFrame());
150   component_ = nullptr;
151   base::RunLoop().RunUntilIdle();
152 }
153 
154 // Checks that the destruction sequence works fine.
TEST_F(CanvasCaptureHandlerTest,DestructHandler)155 TEST_F(CanvasCaptureHandlerTest, DestructHandler) {
156   EXPECT_TRUE(canvas_capture_handler_->NeedsNewFrame());
157   canvas_capture_handler_.reset();
158   base::RunLoop().RunUntilIdle();
159 }
160 
161 // Checks that VideoCapturerSource call sequence works fine.
TEST_P(CanvasCaptureHandlerTest,GetFormatsStartAndStop)162 TEST_P(CanvasCaptureHandlerTest, GetFormatsStartAndStop) {
163   InSequence s;
164   MediaStreamSource* const media_stream_source = component_->Source();
165   EXPECT_TRUE(media_stream_source);
166   blink::MediaStreamVideoCapturerSource* const ms_source =
167       static_cast<blink::MediaStreamVideoCapturerSource*>(
168           media_stream_source->GetPlatformSource());
169   EXPECT_TRUE(ms_source);
170   media::VideoCapturerSource* source = GetVideoCapturerSource(ms_source);
171   EXPECT_TRUE(source);
172 
173   media::VideoCaptureFormats formats = source->GetPreferredFormats();
174   ASSERT_EQ(2u, formats.size());
175   EXPECT_EQ(kTestCanvasCaptureWidth, formats[0].frame_size.width());
176   EXPECT_EQ(kTestCanvasCaptureHeight, formats[0].frame_size.height());
177   media::VideoCaptureParams params;
178   params.requested_format = formats[0];
179 
180   base::RunLoop run_loop;
181   base::RepeatingClosure quit_closure = run_loop.QuitClosure();
182   EXPECT_CALL(*this, DoOnRunning(true)).Times(1);
183   EXPECT_CALL(*this, DoOnDeliverFrame(_, _))
184       .Times(1)
185       .WillOnce(RunOnceClosure(std::move(quit_closure)));
186   source->StartCapture(
187       params,
188       base::BindRepeating(&CanvasCaptureHandlerTest::OnDeliverFrame,
189                           base::Unretained(this)),
190       base::BindRepeating(&CanvasCaptureHandlerTest::OnRunning,
191                           base::Unretained(this)));
192   canvas_capture_handler_->SendNewFrame(
193       GenerateTestImage(testing::get<0>(GetParam()),
194                         testing::get<1>(GetParam()),
195                         testing::get<2>(GetParam())),
196       nullptr);
197   run_loop.Run();
198 
199   source->StopCapture();
200 }
201 
202 // Verifies that SkImage is processed and produces VideoFrame as expected.
TEST_P(CanvasCaptureHandlerTest,VerifyFrame)203 TEST_P(CanvasCaptureHandlerTest, VerifyFrame) {
204   const bool opaque_frame = testing::get<0>(GetParam());
205   const bool width = testing::get<1>(GetParam());
206   const bool height = testing::get<1>(GetParam());
207   InSequence s;
208   media::VideoCapturerSource* const source = GetVideoCapturerSource(
209       static_cast<blink::MediaStreamVideoCapturerSource*>(
210           component_->Source()->GetPlatformSource()));
211   EXPECT_TRUE(source);
212 
213   base::RunLoop run_loop;
214   EXPECT_CALL(*this, DoOnRunning(true)).Times(1);
215   media::VideoCaptureParams params;
216   source->StartCapture(
217       params,
218       base::BindRepeating(&CanvasCaptureHandlerTest::OnVerifyDeliveredFrame,
219                           base::Unretained(this), opaque_frame, width, height),
220       base::BindRepeating(&CanvasCaptureHandlerTest::OnRunning,
221                           base::Unretained(this)));
222   canvas_capture_handler_->SendNewFrame(
223       GenerateTestImage(opaque_frame, width, height), nullptr);
224   run_loop.RunUntilIdle();
225 }
226 
227 // Checks that needsNewFrame() works as expected.
TEST_F(CanvasCaptureHandlerTest,CheckNeedsNewFrame)228 TEST_F(CanvasCaptureHandlerTest, CheckNeedsNewFrame) {
229   InSequence s;
230   media::VideoCapturerSource* source = GetVideoCapturerSource(
231       static_cast<blink::MediaStreamVideoCapturerSource*>(
232           component_->Source()->GetPlatformSource()));
233   EXPECT_TRUE(source);
234   EXPECT_TRUE(canvas_capture_handler_->NeedsNewFrame());
235   source->StopCapture();
236   EXPECT_FALSE(canvas_capture_handler_->NeedsNewFrame());
237 }
238 
239 INSTANTIATE_TEST_SUITE_P(
240     All,
241     CanvasCaptureHandlerTest,
242     ::testing::Combine(::testing::Bool(),
243                        ::testing::Values(kTestCanvasCaptureFrameEvenSize,
244                                          kTestCanvasCaptureFrameOddSize),
245                        ::testing::Values(kTestCanvasCaptureFrameEvenSize,
246                                          kTestCanvasCaptureFrameOddSize)));
247 
248 }  // namespace blink
249