1 // Copyright (c) 2012 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.
5 #include "base/message_loop/message_loop.h"
6 #include "media/base/video_frame.h"
7 #include "media/base/video_util.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "third_party/skia/include/core/SkCanvas.h"
10 #include "media/filters/skcanvas_video_renderer.h"
12 using media::VideoFrame;
16 static const int kWidth = 320;
17 static const int kHeight = 240;
18 static const gfx::Rect kNaturalRect(0, 0, kWidth, kHeight);
20 // Helper for filling a |canvas| with a solid |color|.
21 void FillCanvas(SkCanvas* canvas, SkColor color) {
25 // Helper for returning the color of a solid |canvas|.
26 SkColor GetColorAt(SkCanvas* canvas, int x, int y) {
28 if (!bitmap.tryAllocN32Pixels(1, 1))
30 if (!canvas->readPixels(&bitmap, x, y))
32 return bitmap.getColor(0, 0);
35 SkColor GetColor(SkCanvas* canvas) {
36 return GetColorAt(canvas, 0, 0);
39 class SkCanvasVideoRendererTest : public testing::Test {
48 SkCanvasVideoRendererTest();
49 ~SkCanvasVideoRendererTest() override;
51 // Paints to |canvas| using |renderer_| without any frame data.
52 void PaintWithoutFrame(SkCanvas* canvas);
54 // Paints the |video_frame| to the |canvas| using |renderer_|, setting the
55 // color of |video_frame| to |color| first.
56 void Paint(const scoped_refptr<VideoFrame>& video_frame,
59 void PaintRotated(const scoped_refptr<VideoFrame>& video_frame,
61 const gfx::RectF& dest_rect,
63 SkXfermode::Mode mode,
64 VideoRotation video_rotation);
66 void Copy(const scoped_refptr<VideoFrame>& video_frame, SkCanvas* canvas);
68 // Getters for various frame sizes.
69 scoped_refptr<VideoFrame> natural_frame() { return natural_frame_; }
70 scoped_refptr<VideoFrame> larger_frame() { return larger_frame_; }
71 scoped_refptr<VideoFrame> smaller_frame() { return smaller_frame_; }
72 scoped_refptr<VideoFrame> cropped_frame() { return cropped_frame_; }
75 SkCanvas* target_canvas() { return &target_canvas_; }
78 SkCanvasVideoRenderer renderer_;
80 scoped_refptr<VideoFrame> natural_frame_;
81 scoped_refptr<VideoFrame> larger_frame_;
82 scoped_refptr<VideoFrame> smaller_frame_;
83 scoped_refptr<VideoFrame> cropped_frame_;
85 SkCanvas target_canvas_;
86 base::MessageLoop message_loop_;
88 DISALLOW_COPY_AND_ASSIGN(SkCanvasVideoRendererTest);
91 static SkBitmap AllocBitmap(int width, int height) {
93 bitmap.allocPixels(SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType));
98 SkCanvasVideoRendererTest::SkCanvasVideoRendererTest()
99 : natural_frame_(VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight))),
100 larger_frame_(VideoFrame::CreateBlackFrame(
101 gfx::Size(kWidth * 2, kHeight * 2))),
102 smaller_frame_(VideoFrame::CreateBlackFrame(
103 gfx::Size(kWidth / 2, kHeight / 2))),
104 cropped_frame_(VideoFrame::CreateFrame(
107 gfx::Rect(6, 6, 8, 6),
109 base::TimeDelta::FromMilliseconds(4))),
110 target_canvas_(AllocBitmap(kWidth, kHeight)) {
111 // Give each frame a unique timestamp.
112 natural_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(1));
113 larger_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(2));
114 smaller_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(3));
116 // Make sure the cropped video frame's aspect ratio matches the output device.
117 // Update cropped_frame_'s crop dimensions if this is not the case.
118 EXPECT_EQ(cropped_frame()->visible_rect().width() * kHeight,
119 cropped_frame()->visible_rect().height() * kWidth);
121 // Fill in the cropped frame's entire data with colors:
123 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
124 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
125 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
126 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
127 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
128 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
129 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
130 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
131 // G G G G G G G G B B B B B B B B
132 // G G G G G G G G B B B B B B B B
133 // G G G G G G G G B B B B B B B B
134 // G G G G G G G G B B B B B B B B
135 // G G G G G G G G B B B B B B B B
136 // G G G G G G G G B B B B B B B B
137 // G G G G G G G G B B B B B B B B
138 // G G G G G G G G B B B B B B B B
140 // The visible crop of the frame (as set by its visible_rect_) has contents:
149 // Each color region in the cropped frame is on a 2x2 block granularity, to
150 // avoid sharing UV samples between regions.
152 static const uint8 cropped_y_plane[] = {
153 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
154 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
155 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
156 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
157 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
158 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
159 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
160 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
161 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
162 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
163 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
164 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
165 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
166 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
167 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
168 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
171 static const uint8 cropped_u_plane[] = {
172 128, 128, 128, 128, 84, 84, 84, 84,
173 128, 128, 128, 128, 84, 84, 84, 84,
174 128, 128, 128, 128, 84, 84, 84, 84,
175 128, 128, 128, 128, 84, 84, 84, 84,
176 43, 43, 43, 43, 255, 255, 255, 255,
177 43, 43, 43, 43, 255, 255, 255, 255,
178 43, 43, 43, 43, 255, 255, 255, 255,
179 43, 43, 43, 43, 255, 255, 255, 255,
181 static const uint8 cropped_v_plane[] = {
182 128, 128, 128, 128, 255, 255, 255, 255,
183 128, 128, 128, 128, 255, 255, 255, 255,
184 128, 128, 128, 128, 255, 255, 255, 255,
185 128, 128, 128, 128, 255, 255, 255, 255,
186 21, 21, 21, 21, 107, 107, 107, 107,
187 21, 21, 21, 21, 107, 107, 107, 107,
188 21, 21, 21, 21, 107, 107, 107, 107,
189 21, 21, 21, 21, 107, 107, 107, 107,
192 media::CopyYPlane(cropped_y_plane, 16, 16, cropped_frame().get());
193 media::CopyUPlane(cropped_u_plane, 8, 8, cropped_frame().get());
194 media::CopyVPlane(cropped_v_plane, 8, 8, cropped_frame().get());
197 SkCanvasVideoRendererTest::~SkCanvasVideoRendererTest() {}
199 void SkCanvasVideoRendererTest::PaintWithoutFrame(SkCanvas* canvas) {
200 renderer_.Paint(NULL,
204 SkXfermode::kSrcOver_Mode,
208 void SkCanvasVideoRendererTest::Paint(
209 const scoped_refptr<VideoFrame>& video_frame,
212 PaintRotated(video_frame,
216 SkXfermode::kSrcOver_Mode,
220 void SkCanvasVideoRendererTest::PaintRotated(
221 const scoped_refptr<VideoFrame>& video_frame,
223 const gfx::RectF& dest_rect,
225 SkXfermode::Mode mode,
226 VideoRotation video_rotation) {
231 media::FillYUV(video_frame.get(), 76, 84, 255);
234 media::FillYUV(video_frame.get(), 149, 43, 21);
237 media::FillYUV(video_frame.get(), 29, 255, 107);
240 renderer_.Paint(video_frame, canvas, dest_rect, 0xFF, mode, video_rotation);
243 void SkCanvasVideoRendererTest::Copy(
244 const scoped_refptr<VideoFrame>& video_frame,
246 renderer_.Copy(video_frame, canvas);
249 TEST_F(SkCanvasVideoRendererTest, NoFrame) {
250 // Test that black gets painted over canvas.
251 FillCanvas(target_canvas(), SK_ColorRED);
252 PaintWithoutFrame(target_canvas());
253 EXPECT_EQ(SK_ColorBLACK, GetColor(target_canvas()));
256 TEST_F(SkCanvasVideoRendererTest, TransparentFrame) {
257 FillCanvas(target_canvas(), SK_ColorRED);
259 VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
263 SkXfermode::kSrcOver_Mode,
265 EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), GetColor(target_canvas()));
268 TEST_F(SkCanvasVideoRendererTest, TransparentFrameSrcMode) {
269 FillCanvas(target_canvas(), SK_ColorRED);
270 // SRC mode completely overwrites the buffer.
272 VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
276 SkXfermode::kSrc_Mode,
278 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
279 GetColor(target_canvas()));
282 TEST_F(SkCanvasVideoRendererTest, CopyTransparentFrame) {
283 FillCanvas(target_canvas(), SK_ColorRED);
284 Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
286 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
287 GetColor(target_canvas()));
290 TEST_F(SkCanvasVideoRendererTest, Natural) {
291 Paint(natural_frame(), target_canvas(), kRed);
292 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
295 TEST_F(SkCanvasVideoRendererTest, Larger) {
296 Paint(natural_frame(), target_canvas(), kRed);
297 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
299 Paint(larger_frame(), target_canvas(), kBlue);
300 EXPECT_EQ(SK_ColorBLUE, GetColor(target_canvas()));
303 TEST_F(SkCanvasVideoRendererTest, Smaller) {
304 Paint(natural_frame(), target_canvas(), kRed);
305 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
307 Paint(smaller_frame(), target_canvas(), kBlue);
308 EXPECT_EQ(SK_ColorBLUE, GetColor(target_canvas()));
311 TEST_F(SkCanvasVideoRendererTest, NoTimestamp) {
312 VideoFrame* video_frame = natural_frame().get();
313 video_frame->set_timestamp(media::kNoTimestamp());
314 Paint(video_frame, target_canvas(), kRed);
315 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
318 TEST_F(SkCanvasVideoRendererTest, SameVideoFrame) {
319 Paint(natural_frame(), target_canvas(), kRed);
320 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
322 // Slow paints can get cached, expect the old color value.
323 Paint(natural_frame(), target_canvas(), kBlue);
324 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
327 TEST_F(SkCanvasVideoRendererTest, CroppedFrame) {
328 Paint(cropped_frame(), target_canvas(), kNone);
329 // Check the corners.
330 EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), 0, 0));
331 EXPECT_EQ(SK_ColorRED, GetColorAt(target_canvas(), kWidth - 1, 0));
332 EXPECT_EQ(SK_ColorGREEN, GetColorAt(target_canvas(), 0, kHeight - 1));
333 EXPECT_EQ(SK_ColorBLUE, GetColorAt(target_canvas(), kWidth - 1,
335 // Check the interior along the border between color regions. Note that we're
336 // bilinearly upscaling, so we'll need to take care to pick sample points that
337 // are just outside the "zone of resampling".
338 EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), kWidth * 1 / 8 - 1,
339 kHeight * 1 / 6 - 1));
340 EXPECT_EQ(SK_ColorRED, GetColorAt(target_canvas(), kWidth * 3 / 8,
341 kHeight * 1 / 6 - 1));
342 EXPECT_EQ(SK_ColorGREEN, GetColorAt(target_canvas(), kWidth * 1 / 8 - 1,
344 EXPECT_EQ(SK_ColorBLUE, GetColorAt(target_canvas(), kWidth * 3 / 8,
348 TEST_F(SkCanvasVideoRendererTest, CroppedFrame_NoScaling) {
349 SkCanvas canvas(AllocBitmap(kWidth, kHeight));
350 const gfx::Rect crop_rect = cropped_frame()->visible_rect();
352 // Force painting to a non-zero position on the destination bitmap, to check
353 // if the coordinates are calculated properly.
354 const int offset_x = 10;
355 const int offset_y = 15;
356 canvas.translate(offset_x, offset_y);
358 // Create a destination canvas with dimensions and scale which would not
360 canvas.scale(static_cast<SkScalar>(crop_rect.width()) / kWidth,
361 static_cast<SkScalar>(crop_rect.height()) / kHeight);
363 Paint(cropped_frame(), &canvas, kNone);
365 // Check the corners.
366 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, offset_x, offset_y));
367 EXPECT_EQ(SK_ColorRED,
368 GetColorAt(&canvas, offset_x + crop_rect.width() - 1, offset_y));
369 EXPECT_EQ(SK_ColorGREEN,
370 GetColorAt(&canvas, offset_x, offset_y + crop_rect.height() - 1));
371 EXPECT_EQ(SK_ColorBLUE,
373 offset_x + crop_rect.width() - 1,
374 offset_y + crop_rect.height() - 1));
377 TEST_F(SkCanvasVideoRendererTest, Video_Rotation_90) {
378 SkCanvas canvas(AllocBitmap(kWidth, kHeight));
379 PaintRotated(cropped_frame(),
383 SkXfermode::kSrcOver_Mode,
385 // Check the corners.
386 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, 0, 0));
387 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, 0));
388 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
389 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, kHeight - 1));
392 TEST_F(SkCanvasVideoRendererTest, Video_Rotation_180) {
393 SkCanvas canvas(AllocBitmap(kWidth, kHeight));
394 PaintRotated(cropped_frame(),
398 SkXfermode::kSrcOver_Mode,
400 // Check the corners.
401 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, 0));
402 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, 0));
403 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
404 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, kHeight - 1));
407 TEST_F(SkCanvasVideoRendererTest, Video_Rotation_270) {
408 SkCanvas canvas(AllocBitmap(kWidth, kHeight));
409 PaintRotated(cropped_frame(),
413 SkXfermode::kSrcOver_Mode,
415 // Check the corners.
416 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, 0));
417 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, 0));
418 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
419 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, 0, kHeight - 1));
422 TEST_F(SkCanvasVideoRendererTest, Video_Translate) {
423 SkCanvas canvas(AllocBitmap(kWidth, kHeight));
424 FillCanvas(&canvas, SK_ColorMAGENTA);
426 PaintRotated(cropped_frame(),
428 gfx::Rect(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
430 SkXfermode::kSrcOver_Mode,
432 // Check the corners of quadrant 2 and 4.
433 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, 0));
434 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, (kWidth / 2) - 1, 0));
435 EXPECT_EQ(SK_ColorMAGENTA,
436 GetColorAt(&canvas, (kWidth / 2) - 1, (kHeight / 2) - 1));
437 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, (kHeight / 2) - 1));
438 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth / 2, kHeight / 2));
439 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight / 2));
440 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
441 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth / 2, kHeight - 1));
444 TEST_F(SkCanvasVideoRendererTest, Video_Translate_Rotation_90) {
445 SkCanvas canvas(AllocBitmap(kWidth, kHeight));
446 FillCanvas(&canvas, SK_ColorMAGENTA);
448 const gfx::Rect crop_rect = cropped_frame()->visible_rect();
449 PaintRotated(cropped_frame(),
451 gfx::Rect(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
453 SkXfermode::kSrcOver_Mode,
455 // Check the corners of quadrant 2 and 4.
456 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, 0));
457 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, (kWidth / 2) - 1, 0));
458 EXPECT_EQ(SK_ColorMAGENTA,
459 GetColorAt(&canvas, (kWidth / 2) - 1, (kHeight / 2) - 1));
460 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, (kHeight / 2) - 1));
461 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth / 2, kHeight / 2));
462 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight / 2));
463 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
464 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth / 2, kHeight - 1));
467 TEST_F(SkCanvasVideoRendererTest, Video_Translate_Rotation_180) {
468 SkCanvas canvas(AllocBitmap(kWidth, kHeight));
469 FillCanvas(&canvas, SK_ColorMAGENTA);
471 PaintRotated(cropped_frame(),
473 gfx::Rect(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
475 SkXfermode::kSrcOver_Mode,
477 // Check the corners of quadrant 2 and 4.
478 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, 0));
479 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, (kWidth / 2) - 1, 0));
480 EXPECT_EQ(SK_ColorMAGENTA,
481 GetColorAt(&canvas, (kWidth / 2) - 1, (kHeight / 2) - 1));
482 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, (kHeight / 2) - 1));
483 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth / 2, kHeight / 2));
484 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight / 2));
485 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
486 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth / 2, kHeight - 1));
489 TEST_F(SkCanvasVideoRendererTest, Video_Translate_Rotation_270) {
490 SkCanvas canvas(AllocBitmap(kWidth, kHeight));
491 FillCanvas(&canvas, SK_ColorMAGENTA);
493 PaintRotated(cropped_frame(),
495 gfx::Rect(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
497 SkXfermode::kSrcOver_Mode,
499 // Check the corners of quadrant 2 and 4.
500 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, 0));
501 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, (kWidth / 2) - 1, 0));
502 EXPECT_EQ(SK_ColorMAGENTA,
503 GetColorAt(&canvas, (kWidth / 2) - 1, (kHeight / 2) - 1));
504 EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, (kHeight / 2) - 1));
505 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth / 2, kHeight / 2));
506 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, kHeight / 2));
507 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
508 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth / 2, kHeight - 1));