codec_test.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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 <deque> 6#include <stdlib.h> 7 8#include "base/bind.h" 9#include "base/logging.h" 10#include "base/memory/scoped_ptr.h" 11#include "media/base/video_frame.h" 12#include "remoting/codec/codec_test.h" 13#include "remoting/codec/video_decoder.h" 14#include "remoting/codec/video_encoder.h" 15#include "remoting/base/util.h" 16#include "testing/gtest/include/gtest/gtest.h" 17#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 18 19using webrtc::DesktopRect; 20using webrtc::DesktopRegion; 21using webrtc::DesktopSize; 22 23namespace { 24 25const int kBytesPerPixel = 4; 26 27// Some sample rects for testing. 28std::vector<std::vector<DesktopRect> > MakeTestRectLists(DesktopSize size) { 29 std::vector<std::vector<DesktopRect> > rect_lists; 30 std::vector<DesktopRect> rects; 31 rects.push_back(DesktopRect::MakeXYWH(0, 0, size.width(), size.height())); 32 rect_lists.push_back(rects); 33 rects.clear(); 34 rects.push_back(DesktopRect::MakeXYWH( 35 0, 0, size.width() / 2, size.height() / 2)); 36 rect_lists.push_back(rects); 37 rects.clear(); 38 rects.push_back(DesktopRect::MakeXYWH( 39 size.width() / 2, size.height() / 2, 40 size.width() / 2, size.height() / 2)); 41 rect_lists.push_back(rects); 42 rects.clear(); 43 rects.push_back(DesktopRect::MakeXYWH(16, 16, 16, 16)); 44 rects.push_back(DesktopRect::MakeXYWH(128, 64, 32, 32)); 45 rect_lists.push_back(rects); 46 return rect_lists; 47} 48 49} // namespace 50 51namespace remoting { 52 53// A class to test the message output of the encoder. 54class VideoEncoderMessageTester { 55 public: 56 VideoEncoderMessageTester() 57 : begin_rect_(0), 58 rect_data_(0), 59 end_rect_(0), 60 state_(kWaitingForBeginRect), 61 strict_(false) { 62 } 63 64 ~VideoEncoderMessageTester() { 65 EXPECT_EQ(begin_rect_, end_rect_); 66 EXPECT_GT(begin_rect_, 0); 67 EXPECT_EQ(kWaitingForBeginRect, state_); 68 if (strict_) { 69 EXPECT_TRUE(region_.Equals(received_region_)); 70 } 71 } 72 73 // Test that we received the correct packet. 74 void ReceivedPacket(VideoPacket* packet) { 75 if (state_ == kWaitingForBeginRect) { 76 EXPECT_TRUE((packet->flags() & VideoPacket::FIRST_PACKET) != 0); 77 state_ = kWaitingForRectData; 78 ++begin_rect_; 79 80 if (strict_) { 81 received_region_.AddRect(webrtc::DesktopRect::MakeXYWH( 82 packet->format().x(), packet->format().y(), 83 packet->format().width(), packet->format().height())); 84 } 85 } else { 86 EXPECT_FALSE((packet->flags() & VideoPacket::FIRST_PACKET) != 0); 87 } 88 89 if (state_ == kWaitingForRectData) { 90 if (packet->has_data()) { 91 ++rect_data_; 92 } 93 94 if ((packet->flags() & VideoPacket::LAST_PACKET) != 0) { 95 // Expect that we have received some data. 96 EXPECT_GT(rect_data_, 0); 97 rect_data_ = 0; 98 state_ = kWaitingForBeginRect; 99 ++end_rect_; 100 } 101 102 if ((packet->flags() & VideoPacket::LAST_PARTITION) != 0) { 103 // LAST_PARTITION must always be marked with LAST_PACKET. 104 EXPECT_TRUE((packet->flags() & VideoPacket::LAST_PACKET) != 0); 105 } 106 } 107 } 108 109 void set_strict(bool strict) { 110 strict_ = strict; 111 } 112 113 void AddRects(const DesktopRect* rects, int count) { 114 region_.AddRects(rects, count); 115 } 116 117 private: 118 enum State { 119 kWaitingForBeginRect, 120 kWaitingForRectData, 121 }; 122 123 int begin_rect_; 124 int rect_data_; 125 int end_rect_; 126 State state_; 127 bool strict_; 128 129 DesktopRegion region_; 130 DesktopRegion received_region_; 131 132 DISALLOW_COPY_AND_ASSIGN(VideoEncoderMessageTester); 133}; 134 135class VideoDecoderTester { 136 public: 137 VideoDecoderTester(VideoDecoder* decoder, 138 const DesktopSize& screen_size, 139 const DesktopSize& view_size) 140 : screen_size_(screen_size), 141 view_size_(view_size), 142 strict_(false), 143 decoder_(decoder) { 144 image_data_.reset(new uint8[ 145 view_size_.width() * view_size_.height() * kBytesPerPixel]); 146 EXPECT_TRUE(image_data_.get()); 147 decoder_->Initialize( 148 SkISize::Make(screen_size_.width(), screen_size_.height())); 149 } 150 151 void Reset() { 152 expected_region_.Clear(); 153 update_region_.setEmpty(); 154 } 155 156 void ResetRenderedData() { 157 memset(image_data_.get(), 0, 158 view_size_.width() * view_size_.height() * kBytesPerPixel); 159 } 160 161 void ReceivedPacket(VideoPacket* packet) { 162 VideoDecoder::DecodeResult result = decoder_->DecodePacket(packet); 163 164 ASSERT_NE(VideoDecoder::DECODE_ERROR, result); 165 166 if (result == VideoDecoder::DECODE_DONE) { 167 RenderFrame(); 168 } 169 } 170 171 void RenderFrame() { 172 decoder_->RenderFrame( 173 SkISize::Make(view_size_.width(), view_size_.height()), 174 SkIRect::MakeWH(view_size_.width(), view_size_.height()), 175 image_data_.get(), 176 view_size_.width() * kBytesPerPixel, 177 &update_region_); 178 } 179 180 void ReceivedScopedPacket(scoped_ptr<VideoPacket> packet) { 181 ReceivedPacket(packet.get()); 182 } 183 184 void set_strict(bool strict) { 185 strict_ = strict; 186 } 187 188 void set_frame(webrtc::DesktopFrame* frame) { 189 frame_ = frame; 190 } 191 192 void AddRects(const DesktopRect* rects, int count) { 193 for (int i = 0; i < count; ++i) { 194 expected_region_.AddRect(rects[i]); 195 } 196 } 197 198 void AddRegion(const webrtc::DesktopRegion& region) { 199 expected_region_.AddRegion(region); 200 } 201 202 void VerifyResults() { 203 if (!strict_) 204 return; 205 206 ASSERT_TRUE(frame_); 207 208 // Test the content of the update region. 209 // 210 // TODO(sergeyu): Change this to use DesktopRegion when it's capable of 211 // merging the rectangles. 212 SkRegion expected_region; 213 for (webrtc::DesktopRegion::Iterator it(expected_region_); 214 !it.IsAtEnd(); it.Advance()) { 215 expected_region.op( 216 SkIRect::MakeXYWH(it.rect().top(), it.rect().left(), 217 it.rect().width(), it.rect().height()), 218 SkRegion::kUnion_Op); 219 } 220 EXPECT_EQ(expected_region, update_region_); 221 222 for (SkRegion::Iterator i(update_region_); !i.done(); i.next()) { 223 const int stride = view_size_.width() * kBytesPerPixel; 224 EXPECT_EQ(stride, frame_->stride()); 225 const int offset = stride * i.rect().top() + 226 kBytesPerPixel * i.rect().left(); 227 const uint8* original = frame_->data() + offset; 228 const uint8* decoded = image_data_.get() + offset; 229 const int row_size = kBytesPerPixel * i.rect().width(); 230 for (int y = 0; y < i.rect().height(); ++y) { 231 EXPECT_EQ(0, memcmp(original, decoded, row_size)) 232 << "Row " << y << " is different"; 233 original += stride; 234 decoded += stride; 235 } 236 } 237 } 238 239 // The error at each pixel is the root mean square of the errors in 240 // the R, G, and B components, each normalized to [0, 1]. This routine 241 // checks that the maximum and mean pixel errors do not exceed given limits. 242 void VerifyResultsApprox(const uint8* expected_view_data, 243 double max_error_limit, double mean_error_limit) { 244 double max_error = 0.0; 245 double sum_error = 0.0; 246 int error_num = 0; 247 for (SkRegion::Iterator i(update_region_); !i.done(); i.next()) { 248 const int stride = view_size_.width() * kBytesPerPixel; 249 const int offset = stride * i.rect().top() + 250 kBytesPerPixel * i.rect().left(); 251 const uint8* expected = expected_view_data + offset; 252 const uint8* actual = image_data_.get() + offset; 253 for (int y = 0; y < i.rect().height(); ++y) { 254 for (int x = 0; x < i.rect().width(); ++x) { 255 double error = CalculateError(expected, actual); 256 max_error = std::max(max_error, error); 257 sum_error += error; 258 ++error_num; 259 expected += 4; 260 actual += 4; 261 } 262 } 263 } 264 EXPECT_LE(max_error, max_error_limit); 265 double mean_error = sum_error / error_num; 266 EXPECT_LE(mean_error, mean_error_limit); 267 LOG(INFO) << "Max error: " << max_error; 268 LOG(INFO) << "Mean error: " << mean_error; 269 } 270 271 double CalculateError(const uint8* original, const uint8* decoded) { 272 double error_sum_squares = 0.0; 273 for (int i = 0; i < 3; i++) { 274 double error = static_cast<double>(*original++) - 275 static_cast<double>(*decoded++); 276 error /= 255.0; 277 error_sum_squares += error * error; 278 } 279 original++; 280 decoded++; 281 return sqrt(error_sum_squares / 3.0); 282 } 283 284 private: 285 DesktopSize screen_size_; 286 DesktopSize view_size_; 287 bool strict_; 288 webrtc::DesktopRegion expected_region_; 289 SkRegion update_region_; 290 VideoDecoder* decoder_; 291 scoped_ptr<uint8[]> image_data_; 292 webrtc::DesktopFrame* frame_; 293 294 DISALLOW_COPY_AND_ASSIGN(VideoDecoderTester); 295}; 296 297// The VideoEncoderTester provides a hook for retrieving the data, and passing 298// the message to other subprograms for validaton. 299class VideoEncoderTester { 300 public: 301 VideoEncoderTester(VideoEncoderMessageTester* message_tester) 302 : message_tester_(message_tester), 303 decoder_tester_(NULL), 304 data_available_(0) { 305 } 306 307 ~VideoEncoderTester() { 308 EXPECT_GT(data_available_, 0); 309 } 310 311 void DataAvailable(scoped_ptr<VideoPacket> packet) { 312 ++data_available_; 313 message_tester_->ReceivedPacket(packet.get()); 314 315 // Send the message to the VideoDecoderTester. 316 if (decoder_tester_) { 317 decoder_tester_->ReceivedPacket(packet.get()); 318 } 319 } 320 321 void AddRects(const DesktopRect* rects, int count) { 322 message_tester_->AddRects(rects, count); 323 } 324 325 void set_decoder_tester(VideoDecoderTester* decoder_tester) { 326 decoder_tester_ = decoder_tester; 327 } 328 329 private: 330 VideoEncoderMessageTester* message_tester_; 331 VideoDecoderTester* decoder_tester_; 332 int data_available_; 333 334 DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester); 335}; 336 337scoped_ptr<webrtc::DesktopFrame> PrepareFrame(const DesktopSize& size) { 338 scoped_ptr<webrtc::DesktopFrame> frame(new webrtc::BasicDesktopFrame(size)); 339 340 srand(0); 341 int memory_size = size.width() * size.height() * kBytesPerPixel; 342 for (int i = 0; i < memory_size; ++i) { 343 frame->data()[i] = rand() % 256; 344 } 345 346 return frame.Pass(); 347} 348 349static void TestEncodingRects(VideoEncoder* encoder, 350 VideoEncoderTester* tester, 351 webrtc::DesktopFrame* frame, 352 const DesktopRect* rects, 353 int count) { 354 frame->mutable_updated_region()->Clear(); 355 for (int i = 0; i < count; ++i) { 356 frame->mutable_updated_region()->AddRect(rects[i]); 357 } 358 tester->AddRects(rects, count); 359 360 encoder->Encode(frame, base::Bind( 361 &VideoEncoderTester::DataAvailable, base::Unretained(tester))); 362} 363 364void TestVideoEncoder(VideoEncoder* encoder, bool strict) { 365 const int kSizes[] = {320, 319, 317, 150}; 366 367 VideoEncoderMessageTester message_tester; 368 message_tester.set_strict(strict); 369 370 VideoEncoderTester tester(&message_tester); 371 372 for (size_t xi = 0; xi < arraysize(kSizes); ++xi) { 373 for (size_t yi = 0; yi < arraysize(kSizes); ++yi) { 374 DesktopSize size = DesktopSize(kSizes[xi], kSizes[yi]); 375 scoped_ptr<webrtc::DesktopFrame> frame = PrepareFrame(size); 376 std::vector<std::vector<DesktopRect> > test_rect_lists = 377 MakeTestRectLists(size); 378 for (size_t i = 0; i < test_rect_lists.size(); ++i) { 379 const std::vector<DesktopRect>& test_rects = test_rect_lists[i]; 380 TestEncodingRects(encoder, &tester, frame.get(), 381 &test_rects[0], test_rects.size()); 382 } 383 } 384 } 385} 386 387static void TestEncodeDecodeRects(VideoEncoder* encoder, 388 VideoEncoderTester* encoder_tester, 389 VideoDecoderTester* decoder_tester, 390 webrtc::DesktopFrame* frame, 391 const DesktopRect* rects, int count) { 392 frame->mutable_updated_region()->Clear(); 393 for (int i = 0; i < count; ++i) { 394 frame->mutable_updated_region()->AddRect(rects[i]); 395 } 396 encoder_tester->AddRects(rects, count); 397 decoder_tester->AddRects(rects, count); 398 399 // Generate random data for the updated region. 400 srand(0); 401 for (int i = 0; i < count; ++i) { 402 const int row_size = 403 webrtc::DesktopFrame::kBytesPerPixel * rects[i].width(); 404 uint8* memory = frame->data() + 405 frame->stride() * rects[i].top() + 406 webrtc::DesktopFrame::kBytesPerPixel * rects[i].left(); 407 for (int y = 0; y < rects[i].height(); ++y) { 408 for (int x = 0; x < row_size; ++x) 409 memory[x] = rand() % 256; 410 memory += frame->stride(); 411 } 412 } 413 414 encoder->Encode(frame, base::Bind(&VideoEncoderTester::DataAvailable, 415 base::Unretained(encoder_tester))); 416 decoder_tester->VerifyResults(); 417 decoder_tester->Reset(); 418} 419 420void TestVideoEncoderDecoder( 421 VideoEncoder* encoder, VideoDecoder* decoder, bool strict) { 422 DesktopSize kSize = DesktopSize(320, 240); 423 424 VideoEncoderMessageTester message_tester; 425 message_tester.set_strict(strict); 426 427 VideoEncoderTester encoder_tester(&message_tester); 428 429 scoped_ptr<webrtc::DesktopFrame> frame = PrepareFrame(kSize); 430 431 VideoDecoderTester decoder_tester(decoder, kSize, kSize); 432 decoder_tester.set_strict(strict); 433 decoder_tester.set_frame(frame.get()); 434 encoder_tester.set_decoder_tester(&decoder_tester); 435 436 std::vector<std::vector<DesktopRect> > test_rect_lists = 437 MakeTestRectLists(kSize); 438 for (size_t i = 0; i < test_rect_lists.size(); ++i) { 439 const std::vector<DesktopRect> test_rects = test_rect_lists[i]; 440 TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, 441 frame.get(), &test_rects[0], test_rects.size()); 442 } 443} 444 445static void FillWithGradient(webrtc::DesktopFrame* frame) { 446 for (int j = 0; j < frame->size().height(); ++j) { 447 uint8* p = frame->data() + j * frame->stride(); 448 for (int i = 0; i < frame->size().width(); ++i) { 449 *p++ = (255.0 * i) / frame->size().width(); 450 *p++ = (164.0 * j) / frame->size().height(); 451 *p++ = (82.0 * (i + j)) / 452 (frame->size().width() + frame->size().height()); 453 *p++ = 0; 454 } 455 } 456} 457 458void TestVideoEncoderDecoderGradient(VideoEncoder* encoder, 459 VideoDecoder* decoder, 460 const DesktopSize& screen_size, 461 const DesktopSize& view_size, 462 double max_error_limit, 463 double mean_error_limit) { 464 scoped_ptr<webrtc::BasicDesktopFrame> frame( 465 new webrtc::BasicDesktopFrame(screen_size)); 466 FillWithGradient(frame.get()); 467 frame->mutable_updated_region()->SetRect(DesktopRect::MakeSize(screen_size)); 468 469 scoped_ptr<webrtc::BasicDesktopFrame> expected_result( 470 new webrtc::BasicDesktopFrame(view_size)); 471 FillWithGradient(expected_result.get()); 472 473 VideoDecoderTester decoder_tester(decoder, screen_size, view_size); 474 decoder_tester.set_frame(frame.get()); 475 decoder_tester.AddRegion(frame->updated_region()); 476 477 encoder->Encode(frame.get(), 478 base::Bind(&VideoDecoderTester::ReceivedScopedPacket, 479 base::Unretained(&decoder_tester))); 480 481 decoder_tester.VerifyResultsApprox(expected_result->data(), 482 max_error_limit, mean_error_limit); 483 484 // Check that the decoder correctly re-renders the frame if its client 485 // invalidates the frame. 486 decoder_tester.ResetRenderedData(); 487 decoder->Invalidate( 488 SkISize::Make(view_size.width(), view_size.height()), 489 SkRegion(SkIRect::MakeWH(view_size.width(), view_size.height()))); 490 decoder_tester.RenderFrame(); 491 decoder_tester.VerifyResultsApprox(expected_result->data(), 492 max_error_limit, mean_error_limit); 493} 494 495} // namespace remoting 496