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. 4 5#include "media/base/video_frame.h" 6#include "media/base/video_util.h" 7#include "testing/gtest/include/gtest/gtest.h" 8#include "third_party/skia/include/core/SkCanvas.h" 9#include "media/filters/skcanvas_video_renderer.h" 10 11using media::VideoFrame; 12 13namespace media { 14 15static const int kWidth = 320; 16static const int kHeight = 240; 17static const gfx::Rect kNaturalRect(0, 0, kWidth, kHeight); 18 19// Helper for filling a |canvas| with a solid |color|. 20void FillCanvas(SkCanvas* canvas, SkColor color) { 21 canvas->clear(color); 22} 23 24// Helper for returning the color of a solid |canvas|. 25SkColor GetColorAt(SkCanvas* canvas, int x, int y) { 26 SkBitmap bitmap; 27 if (!bitmap.tryAllocN32Pixels(1, 1)) 28 return 0; 29 if (!canvas->readPixels(&bitmap, x, y)) 30 return 0; 31 return bitmap.getColor(0, 0); 32} 33 34SkColor GetColor(SkCanvas* canvas) { 35 return GetColorAt(canvas, 0, 0); 36} 37 38class SkCanvasVideoRendererTest : public testing::Test { 39 public: 40 enum Color { 41 kNone, 42 kRed, 43 kGreen, 44 kBlue, 45 }; 46 47 SkCanvasVideoRendererTest(); 48 virtual ~SkCanvasVideoRendererTest(); 49 50 // Paints to |canvas| using |renderer_| without any frame data. 51 void PaintWithoutFrame(SkCanvas* canvas); 52 53 // Paints the |video_frame| to the |canvas| using |renderer_|, setting the 54 // color of |video_frame| to |color| first. 55 void Paint(const scoped_refptr<VideoFrame>& video_frame, 56 SkCanvas* canvas, 57 Color color); 58 void PaintRotated(const scoped_refptr<VideoFrame>& video_frame, 59 SkCanvas* canvas, 60 Color color, 61 SkXfermode::Mode mode, 62 VideoRotation video_rotation); 63 64 void Copy(const scoped_refptr<VideoFrame>& video_frame, SkCanvas* canvas); 65 66 // Getters for various frame sizes. 67 scoped_refptr<VideoFrame> natural_frame() { return natural_frame_; } 68 scoped_refptr<VideoFrame> larger_frame() { return larger_frame_; } 69 scoped_refptr<VideoFrame> smaller_frame() { return smaller_frame_; } 70 scoped_refptr<VideoFrame> cropped_frame() { return cropped_frame_; } 71 72 // Standard canvas. 73 SkCanvas* target_canvas() { return &target_canvas_; } 74 75 private: 76 SkCanvasVideoRenderer renderer_; 77 78 scoped_refptr<VideoFrame> natural_frame_; 79 scoped_refptr<VideoFrame> larger_frame_; 80 scoped_refptr<VideoFrame> smaller_frame_; 81 scoped_refptr<VideoFrame> cropped_frame_; 82 83 SkCanvas target_canvas_; 84 85 DISALLOW_COPY_AND_ASSIGN(SkCanvasVideoRendererTest); 86}; 87 88static SkBitmap AllocBitmap(int width, int height) { 89 SkBitmap bitmap; 90 bitmap.allocPixels(SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType)); 91 bitmap.eraseColor(0); 92 return bitmap; 93} 94 95SkCanvasVideoRendererTest::SkCanvasVideoRendererTest() 96 : natural_frame_(VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight))), 97 larger_frame_(VideoFrame::CreateBlackFrame( 98 gfx::Size(kWidth * 2, kHeight * 2))), 99 smaller_frame_(VideoFrame::CreateBlackFrame( 100 gfx::Size(kWidth / 2, kHeight / 2))), 101 cropped_frame_(VideoFrame::CreateFrame( 102 VideoFrame::YV12, 103 gfx::Size(16, 16), 104 gfx::Rect(6, 6, 8, 6), 105 gfx::Size(8, 6), 106 base::TimeDelta::FromMilliseconds(4))), 107 target_canvas_(AllocBitmap(kWidth, kHeight)) { 108 // Give each frame a unique timestamp. 109 natural_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(1)); 110 larger_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(2)); 111 smaller_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(3)); 112 113 // Make sure the cropped video frame's aspect ratio matches the output device. 114 // Update cropped_frame_'s crop dimensions if this is not the case. 115 EXPECT_EQ(cropped_frame()->visible_rect().width() * kHeight, 116 cropped_frame()->visible_rect().height() * kWidth); 117 118 // Fill in the cropped frame's entire data with colors: 119 // 120 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R 121 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R 122 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R 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 // G G G G G G G G B B B B B B B B 129 // G G G G G G G G B B B B B B B B 130 // G G G G G G G G B B B B B B B B 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 // 137 // The visible crop of the frame (as set by its visible_rect_) has contents: 138 // 139 // Bl Bl R R R R R R 140 // Bl Bl R R R R R R 141 // G G B B B B B B 142 // G G B B B B B B 143 // G G B B B B B B 144 // G G B B B B B B 145 // 146 // Each color region in the cropped frame is on a 2x2 block granularity, to 147 // avoid sharing UV samples between regions. 148 149 static const uint8 cropped_y_plane[] = { 150 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76, 151 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76, 152 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76, 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 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29, 159 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29, 160 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29, 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 }; 167 168 static const uint8 cropped_u_plane[] = { 169 128, 128, 128, 128, 84, 84, 84, 84, 170 128, 128, 128, 128, 84, 84, 84, 84, 171 128, 128, 128, 128, 84, 84, 84, 84, 172 128, 128, 128, 128, 84, 84, 84, 84, 173 43, 43, 43, 43, 255, 255, 255, 255, 174 43, 43, 43, 43, 255, 255, 255, 255, 175 43, 43, 43, 43, 255, 255, 255, 255, 176 43, 43, 43, 43, 255, 255, 255, 255, 177 }; 178 static const uint8 cropped_v_plane[] = { 179 128, 128, 128, 128, 255, 255, 255, 255, 180 128, 128, 128, 128, 255, 255, 255, 255, 181 128, 128, 128, 128, 255, 255, 255, 255, 182 128, 128, 128, 128, 255, 255, 255, 255, 183 21, 21, 21, 21, 107, 107, 107, 107, 184 21, 21, 21, 21, 107, 107, 107, 107, 185 21, 21, 21, 21, 107, 107, 107, 107, 186 21, 21, 21, 21, 107, 107, 107, 107, 187 }; 188 189 media::CopyYPlane(cropped_y_plane, 16, 16, cropped_frame().get()); 190 media::CopyUPlane(cropped_u_plane, 8, 8, cropped_frame().get()); 191 media::CopyVPlane(cropped_v_plane, 8, 8, cropped_frame().get()); 192} 193 194SkCanvasVideoRendererTest::~SkCanvasVideoRendererTest() {} 195 196void SkCanvasVideoRendererTest::PaintWithoutFrame(SkCanvas* canvas) { 197 renderer_.Paint(NULL, 198 canvas, 199 kNaturalRect, 200 0xFF, 201 SkXfermode::kSrcOver_Mode, 202 VIDEO_ROTATION_0); 203} 204 205void SkCanvasVideoRendererTest::Paint( 206 const scoped_refptr<VideoFrame>& video_frame, 207 SkCanvas* canvas, 208 Color color) { 209 PaintRotated( 210 video_frame, canvas, color, SkXfermode::kSrcOver_Mode, VIDEO_ROTATION_0); 211} 212 213void SkCanvasVideoRendererTest::PaintRotated( 214 const scoped_refptr<VideoFrame>& video_frame, 215 SkCanvas* canvas, 216 Color color, 217 SkXfermode::Mode mode, 218 VideoRotation video_rotation) { 219 switch (color) { 220 case kNone: 221 break; 222 case kRed: 223 media::FillYUV(video_frame.get(), 76, 84, 255); 224 break; 225 case kGreen: 226 media::FillYUV(video_frame.get(), 149, 43, 21); 227 break; 228 case kBlue: 229 media::FillYUV(video_frame.get(), 29, 255, 107); 230 break; 231 } 232 renderer_.Paint( 233 video_frame, canvas, kNaturalRect, 0xFF, mode, video_rotation); 234} 235 236void SkCanvasVideoRendererTest::Copy( 237 const scoped_refptr<VideoFrame>& video_frame, 238 SkCanvas* canvas) { 239 renderer_.Copy(video_frame, canvas); 240} 241 242TEST_F(SkCanvasVideoRendererTest, NoFrame) { 243 // Test that black gets painted over canvas. 244 FillCanvas(target_canvas(), SK_ColorRED); 245 PaintWithoutFrame(target_canvas()); 246 EXPECT_EQ(SK_ColorBLACK, GetColor(target_canvas())); 247} 248 249TEST_F(SkCanvasVideoRendererTest, TransparentFrame) { 250 FillCanvas(target_canvas(), SK_ColorRED); 251 PaintRotated( 252 VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(), 253 target_canvas(), 254 kNone, 255 SkXfermode::kSrcOver_Mode, 256 VIDEO_ROTATION_0); 257 EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), GetColor(target_canvas())); 258} 259 260TEST_F(SkCanvasVideoRendererTest, TransparentFrameSrcMode) { 261 FillCanvas(target_canvas(), SK_ColorRED); 262 // SRC mode completely overwrites the buffer. 263 PaintRotated( 264 VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(), 265 target_canvas(), 266 kNone, 267 SkXfermode::kSrc_Mode, 268 VIDEO_ROTATION_0); 269 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), 270 GetColor(target_canvas())); 271} 272 273TEST_F(SkCanvasVideoRendererTest, CopyTransparentFrame) { 274 FillCanvas(target_canvas(), SK_ColorRED); 275 Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(), 276 target_canvas()); 277 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), 278 GetColor(target_canvas())); 279} 280 281TEST_F(SkCanvasVideoRendererTest, Natural) { 282 Paint(natural_frame(), target_canvas(), kRed); 283 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas())); 284} 285 286TEST_F(SkCanvasVideoRendererTest, Larger) { 287 Paint(natural_frame(), target_canvas(), kRed); 288 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas())); 289 290 Paint(larger_frame(), target_canvas(), kBlue); 291 EXPECT_EQ(SK_ColorBLUE, GetColor(target_canvas())); 292} 293 294TEST_F(SkCanvasVideoRendererTest, Smaller) { 295 Paint(natural_frame(), target_canvas(), kRed); 296 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas())); 297 298 Paint(smaller_frame(), target_canvas(), kBlue); 299 EXPECT_EQ(SK_ColorBLUE, GetColor(target_canvas())); 300} 301 302TEST_F(SkCanvasVideoRendererTest, NoTimestamp) { 303 VideoFrame* video_frame = natural_frame().get(); 304 video_frame->set_timestamp(media::kNoTimestamp()); 305 Paint(video_frame, target_canvas(), kRed); 306 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas())); 307} 308 309TEST_F(SkCanvasVideoRendererTest, SameVideoFrame) { 310 Paint(natural_frame(), target_canvas(), kRed); 311 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas())); 312 313 // Slow paints can get cached, expect the old color value. 314 Paint(natural_frame(), target_canvas(), kBlue); 315 EXPECT_EQ(SK_ColorRED, GetColor(target_canvas())); 316} 317 318TEST_F(SkCanvasVideoRendererTest, CroppedFrame) { 319 Paint(cropped_frame(), target_canvas(), kNone); 320 // Check the corners. 321 EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), 0, 0)); 322 EXPECT_EQ(SK_ColorRED, GetColorAt(target_canvas(), kWidth - 1, 0)); 323 EXPECT_EQ(SK_ColorGREEN, GetColorAt(target_canvas(), 0, kHeight - 1)); 324 EXPECT_EQ(SK_ColorBLUE, GetColorAt(target_canvas(), kWidth - 1, 325 kHeight - 1)); 326 // Check the interior along the border between color regions. Note that we're 327 // bilinearly upscaling, so we'll need to take care to pick sample points that 328 // are just outside the "zone of resampling". 329 EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), kWidth * 1 / 8 - 1, 330 kHeight * 1 / 6 - 1)); 331 EXPECT_EQ(SK_ColorRED, GetColorAt(target_canvas(), kWidth * 3 / 8, 332 kHeight * 1 / 6 - 1)); 333 EXPECT_EQ(SK_ColorGREEN, GetColorAt(target_canvas(), kWidth * 1 / 8 - 1, 334 kHeight * 3 / 6)); 335 EXPECT_EQ(SK_ColorBLUE, GetColorAt(target_canvas(), kWidth * 3 / 8, 336 kHeight * 3 / 6)); 337} 338 339TEST_F(SkCanvasVideoRendererTest, CroppedFrame_NoScaling) { 340 SkCanvas canvas(AllocBitmap(kWidth, kHeight)); 341 const gfx::Rect crop_rect = cropped_frame()->visible_rect(); 342 343 // Force painting to a non-zero position on the destination bitmap, to check 344 // if the coordinates are calculated properly. 345 const int offset_x = 10; 346 const int offset_y = 15; 347 canvas.translate(offset_x, offset_y); 348 349 // Create a destination canvas with dimensions and scale which would not 350 // cause scaling. 351 canvas.scale(static_cast<SkScalar>(crop_rect.width()) / kWidth, 352 static_cast<SkScalar>(crop_rect.height()) / kHeight); 353 354 Paint(cropped_frame(), &canvas, kNone); 355 356 // Check the corners. 357 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, offset_x, offset_y)); 358 EXPECT_EQ(SK_ColorRED, 359 GetColorAt(&canvas, offset_x + crop_rect.width() - 1, offset_y)); 360 EXPECT_EQ(SK_ColorGREEN, 361 GetColorAt(&canvas, offset_x, offset_y + crop_rect.height() - 1)); 362 EXPECT_EQ(SK_ColorBLUE, 363 GetColorAt(&canvas, 364 offset_x + crop_rect.width() - 1, 365 offset_y + crop_rect.height() - 1)); 366} 367 368TEST_F(SkCanvasVideoRendererTest, Video_Rotation_90) { 369 SkCanvas canvas(AllocBitmap(kWidth, kHeight)); 370 const gfx::Rect crop_rect = cropped_frame()->visible_rect(); 371 PaintRotated(cropped_frame(), 372 &canvas, 373 kNone, 374 SkXfermode::kSrcOver_Mode, 375 VIDEO_ROTATION_90); 376 // Check the corners. 377 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, 0, 0)); 378 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, 0)); 379 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight - 1)); 380 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, kHeight - 1)); 381} 382 383TEST_F(SkCanvasVideoRendererTest, Video_Rotation_180) { 384 SkCanvas canvas(AllocBitmap(kWidth, kHeight)); 385 const gfx::Rect crop_rect = cropped_frame()->visible_rect(); 386 PaintRotated(cropped_frame(), 387 &canvas, 388 kNone, 389 SkXfermode::kSrcOver_Mode, 390 VIDEO_ROTATION_180); 391 // Check the corners. 392 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, 0)); 393 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, 0)); 394 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight - 1)); 395 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, kHeight - 1)); 396} 397 398TEST_F(SkCanvasVideoRendererTest, Video_Rotation_270) { 399 SkCanvas canvas(AllocBitmap(kWidth, kHeight)); 400 const gfx::Rect crop_rect = cropped_frame()->visible_rect(); 401 PaintRotated(cropped_frame(), 402 &canvas, 403 kNone, 404 SkXfermode::kSrcOver_Mode, 405 VIDEO_ROTATION_270); 406 // Check the corners. 407 EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, 0)); 408 EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, 0)); 409 EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight - 1)); 410 EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, 0, kHeight - 1)); 411} 412 413} // namespace media 414