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 19 using webrtc::BasicDesktopFrame; 20 using webrtc::DesktopFrame; 21 using webrtc::DesktopRect; 22 using webrtc::DesktopRegion; 23 using webrtc::DesktopSize; 24 25 namespace { 26 27 const int kBytesPerPixel = 4; 28 29 // Some sample rects for testing. 30 std::vector<std::vector<DesktopRect> > MakeTestRectLists(DesktopSize size) { 31 std::vector<std::vector<DesktopRect> > rect_lists; 32 std::vector<DesktopRect> rects; 33 rects.push_back(DesktopRect::MakeXYWH(0, 0, size.width(), size.height())); 34 rect_lists.push_back(rects); 35 rects.clear(); 36 rects.push_back(DesktopRect::MakeXYWH( 37 0, 0, size.width() / 2, size.height() / 2)); 38 rect_lists.push_back(rects); 39 rects.clear(); 40 rects.push_back(DesktopRect::MakeXYWH( 41 size.width() / 2, size.height() / 2, 42 size.width() / 2, size.height() / 2)); 43 rect_lists.push_back(rects); 44 rects.clear(); 45 rects.push_back(DesktopRect::MakeXYWH(16, 16, 16, 16)); 46 rects.push_back(DesktopRect::MakeXYWH(128, 64, 32, 32)); 47 rect_lists.push_back(rects); 48 return rect_lists; 49 } 50 51 } // namespace 52 53 namespace remoting { 54 55 class VideoDecoderTester { 56 public: 57 VideoDecoderTester(VideoDecoder* decoder, 58 const DesktopSize& screen_size, 59 const DesktopSize& view_size) 60 : screen_size_(screen_size), 61 view_size_(view_size), 62 strict_(false), 63 decoder_(decoder), 64 frame_(NULL) { 65 image_data_.reset(new uint8[ 66 view_size_.width() * view_size_.height() * kBytesPerPixel]); 67 EXPECT_TRUE(image_data_.get()); 68 decoder_->Initialize( 69 DesktopSize(screen_size_.width(), screen_size_.height())); 70 } 71 72 void Reset() { 73 expected_region_.Clear(); 74 update_region_.Clear(); 75 } 76 77 void ResetRenderedData() { 78 memset(image_data_.get(), 0, 79 view_size_.width() * view_size_.height() * kBytesPerPixel); 80 } 81 82 void ReceivedPacket(VideoPacket* packet) { 83 ASSERT_TRUE(decoder_->DecodePacket(*packet)); 84 85 RenderFrame(); 86 } 87 88 void RenderFrame() { 89 decoder_->RenderFrame( 90 DesktopSize(view_size_.width(), view_size_.height()), 91 DesktopRect::MakeWH(view_size_.width(), view_size_.height()), 92 image_data_.get(), view_size_.width() * kBytesPerPixel, 93 &update_region_); 94 } 95 96 void ReceivedScopedPacket(scoped_ptr<VideoPacket> packet) { 97 ReceivedPacket(packet.get()); 98 } 99 100 void set_strict(bool strict) { 101 strict_ = strict; 102 } 103 104 void set_frame(DesktopFrame* frame) { 105 frame_ = frame; 106 } 107 108 void AddRects(const DesktopRect* rects, int count) { 109 for (int i = 0; i < count; ++i) { 110 expected_region_.AddRect(rects[i]); 111 } 112 } 113 114 void AddRegion(const DesktopRegion& region) { 115 expected_region_.AddRegion(region); 116 } 117 118 void VerifyResults() { 119 if (!strict_) 120 return; 121 122 ASSERT_TRUE(frame_); 123 124 // Test the content of the update region. 125 EXPECT_TRUE(expected_region_.Equals(update_region_)); 126 127 for (DesktopRegion::Iterator i(update_region_); !i.IsAtEnd(); 128 i.Advance()) { 129 const int stride = view_size_.width() * kBytesPerPixel; 130 EXPECT_EQ(stride, frame_->stride()); 131 const int offset = stride * i.rect().top() + 132 kBytesPerPixel * i.rect().left(); 133 const uint8* original = frame_->data() + offset; 134 const uint8* decoded = image_data_.get() + offset; 135 const int row_size = kBytesPerPixel * i.rect().width(); 136 for (int y = 0; y < i.rect().height(); ++y) { 137 EXPECT_EQ(0, memcmp(original, decoded, row_size)) 138 << "Row " << y << " is different"; 139 original += stride; 140 decoded += stride; 141 } 142 } 143 } 144 145 // The error at each pixel is the root mean square of the errors in 146 // the R, G, and B components, each normalized to [0, 1]. This routine 147 // checks that the maximum and mean pixel errors do not exceed given limits. 148 void VerifyResultsApprox(const uint8* expected_view_data, 149 double max_error_limit, double mean_error_limit) { 150 double max_error = 0.0; 151 double sum_error = 0.0; 152 int error_num = 0; 153 for (DesktopRegion::Iterator i(update_region_); !i.IsAtEnd(); 154 i.Advance()) { 155 const int stride = view_size_.width() * kBytesPerPixel; 156 const int offset = stride * i.rect().top() + 157 kBytesPerPixel * i.rect().left(); 158 const uint8* expected = expected_view_data + offset; 159 const uint8* actual = image_data_.get() + offset; 160 for (int y = 0; y < i.rect().height(); ++y) { 161 for (int x = 0; x < i.rect().width(); ++x) { 162 double error = CalculateError(expected, actual); 163 max_error = std::max(max_error, error); 164 sum_error += error; 165 ++error_num; 166 expected += 4; 167 actual += 4; 168 } 169 } 170 } 171 EXPECT_LE(max_error, max_error_limit); 172 double mean_error = sum_error / error_num; 173 EXPECT_LE(mean_error, mean_error_limit); 174 VLOG(0) << "Max error: " << max_error; 175 VLOG(0) << "Mean error: " << mean_error; 176 } 177 178 double CalculateError(const uint8* original, const uint8* decoded) { 179 double error_sum_squares = 0.0; 180 for (int i = 0; i < 3; i++) { 181 double error = static_cast<double>(*original++) - 182 static_cast<double>(*decoded++); 183 error /= 255.0; 184 error_sum_squares += error * error; 185 } 186 original++; 187 decoded++; 188 return sqrt(error_sum_squares / 3.0); 189 } 190 191 private: 192 DesktopSize screen_size_; 193 DesktopSize view_size_; 194 bool strict_; 195 DesktopRegion expected_region_; 196 DesktopRegion update_region_; 197 VideoDecoder* decoder_; 198 scoped_ptr<uint8[]> image_data_; 199 DesktopFrame* frame_; 200 201 DISALLOW_COPY_AND_ASSIGN(VideoDecoderTester); 202 }; 203 204 // The VideoEncoderTester provides a hook for retrieving the data, and passing 205 // the message to other subprograms for validaton. 206 class VideoEncoderTester { 207 public: 208 VideoEncoderTester() 209 : decoder_tester_(NULL), 210 data_available_(0) { 211 } 212 213 ~VideoEncoderTester() { 214 EXPECT_GT(data_available_, 0); 215 } 216 217 void DataAvailable(scoped_ptr<VideoPacket> packet) { 218 ++data_available_; 219 // Send the message to the VideoDecoderTester. 220 if (decoder_tester_) { 221 decoder_tester_->ReceivedPacket(packet.get()); 222 } 223 } 224 225 void set_decoder_tester(VideoDecoderTester* decoder_tester) { 226 decoder_tester_ = decoder_tester; 227 } 228 229 private: 230 VideoDecoderTester* decoder_tester_; 231 int data_available_; 232 233 DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester); 234 }; 235 236 scoped_ptr<DesktopFrame> PrepareFrame(const DesktopSize& size) { 237 scoped_ptr<DesktopFrame> frame(new BasicDesktopFrame(size)); 238 239 srand(0); 240 int memory_size = size.width() * size.height() * kBytesPerPixel; 241 for (int i = 0; i < memory_size; ++i) { 242 frame->data()[i] = rand() % 256; 243 } 244 245 return frame.Pass(); 246 } 247 248 static void TestEncodingRects(VideoEncoder* encoder, 249 VideoEncoderTester* tester, 250 webrtc::DesktopFrame* frame, 251 const DesktopRect* rects, 252 int count) { 253 frame->mutable_updated_region()->Clear(); 254 for (int i = 0; i < count; ++i) { 255 frame->mutable_updated_region()->AddRect(rects[i]); 256 } 257 258 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame); 259 tester->DataAvailable(packet.Pass()); 260 } 261 262 void TestVideoEncoder(VideoEncoder* encoder, bool strict) { 263 const int kSizes[] = {320, 319, 317, 150}; 264 265 VideoEncoderTester tester; 266 267 for (size_t xi = 0; xi < arraysize(kSizes); ++xi) { 268 for (size_t yi = 0; yi < arraysize(kSizes); ++yi) { 269 DesktopSize size = DesktopSize(kSizes[xi], kSizes[yi]); 270 scoped_ptr<webrtc::DesktopFrame> frame = PrepareFrame(size); 271 std::vector<std::vector<DesktopRect> > test_rect_lists = 272 MakeTestRectLists(size); 273 for (size_t i = 0; i < test_rect_lists.size(); ++i) { 274 const std::vector<DesktopRect>& test_rects = test_rect_lists[i]; 275 TestEncodingRects(encoder, &tester, frame.get(), 276 &test_rects[0], test_rects.size()); 277 } 278 } 279 } 280 } 281 282 static void TestEncodeDecodeRects(VideoEncoder* encoder, 283 VideoEncoderTester* encoder_tester, 284 VideoDecoderTester* decoder_tester, 285 DesktopFrame* frame, 286 const DesktopRect* rects, int count) { 287 frame->mutable_updated_region()->Clear(); 288 for (int i = 0; i < count; ++i) { 289 frame->mutable_updated_region()->AddRect(rects[i]); 290 } 291 decoder_tester->AddRects(rects, count); 292 293 // Generate random data for the updated region. 294 srand(0); 295 for (int i = 0; i < count; ++i) { 296 const int row_size = 297 DesktopFrame::kBytesPerPixel * rects[i].width(); 298 uint8* memory = frame->data() + 299 frame->stride() * rects[i].top() + 300 DesktopFrame::kBytesPerPixel * rects[i].left(); 301 for (int y = 0; y < rects[i].height(); ++y) { 302 for (int x = 0; x < row_size; ++x) 303 memory[x] = rand() % 256; 304 memory += frame->stride(); 305 } 306 } 307 308 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame); 309 encoder_tester->DataAvailable(packet.Pass()); 310 decoder_tester->VerifyResults(); 311 decoder_tester->Reset(); 312 } 313 314 void TestVideoEncoderDecoder( 315 VideoEncoder* encoder, VideoDecoder* decoder, bool strict) { 316 DesktopSize kSize = DesktopSize(320, 240); 317 318 VideoEncoderTester encoder_tester; 319 320 scoped_ptr<DesktopFrame> frame = PrepareFrame(kSize); 321 322 VideoDecoderTester decoder_tester(decoder, kSize, kSize); 323 decoder_tester.set_strict(strict); 324 decoder_tester.set_frame(frame.get()); 325 encoder_tester.set_decoder_tester(&decoder_tester); 326 327 std::vector<std::vector<DesktopRect> > test_rect_lists = 328 MakeTestRectLists(kSize); 329 for (size_t i = 0; i < test_rect_lists.size(); ++i) { 330 const std::vector<DesktopRect> test_rects = test_rect_lists[i]; 331 TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, 332 frame.get(), &test_rects[0], test_rects.size()); 333 } 334 } 335 336 static void FillWithGradient(DesktopFrame* frame) { 337 for (int j = 0; j < frame->size().height(); ++j) { 338 uint8* p = frame->data() + j * frame->stride(); 339 for (int i = 0; i < frame->size().width(); ++i) { 340 *p++ = (255.0 * i) / frame->size().width(); 341 *p++ = (164.0 * j) / frame->size().height(); 342 *p++ = (82.0 * (i + j)) / 343 (frame->size().width() + frame->size().height()); 344 *p++ = 0; 345 } 346 } 347 } 348 349 void TestVideoEncoderDecoderGradient(VideoEncoder* encoder, 350 VideoDecoder* decoder, 351 const DesktopSize& screen_size, 352 const DesktopSize& view_size, 353 double max_error_limit, 354 double mean_error_limit) { 355 scoped_ptr<BasicDesktopFrame> frame( 356 new BasicDesktopFrame(screen_size)); 357 FillWithGradient(frame.get()); 358 frame->mutable_updated_region()->SetRect(DesktopRect::MakeSize(screen_size)); 359 360 scoped_ptr<BasicDesktopFrame> expected_result( 361 new BasicDesktopFrame(view_size)); 362 FillWithGradient(expected_result.get()); 363 364 VideoDecoderTester decoder_tester(decoder, screen_size, view_size); 365 decoder_tester.set_frame(frame.get()); 366 decoder_tester.AddRegion(frame->updated_region()); 367 368 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame); 369 decoder_tester.ReceivedScopedPacket(packet.Pass()); 370 371 decoder_tester.VerifyResultsApprox(expected_result->data(), 372 max_error_limit, mean_error_limit); 373 374 // Check that the decoder correctly re-renders the frame if its client 375 // invalidates the frame. 376 decoder_tester.ResetRenderedData(); 377 decoder->Invalidate( 378 DesktopSize(view_size.width(), view_size.height()), 379 DesktopRegion( 380 DesktopRect::MakeWH(view_size.width(), view_size.height()))); 381 decoder_tester.RenderFrame(); 382 decoder_tester.VerifyResultsApprox(expected_result->data(), 383 max_error_limit, mean_error_limit); 384 } 385 386 float MeasureVideoEncoderFpsWithSize(VideoEncoder* encoder, 387 const DesktopSize& size) { 388 scoped_ptr<DesktopFrame> frame(PrepareFrame(size)); 389 frame->mutable_updated_region()->SetRect(DesktopRect::MakeSize(size)); 390 std::list<DesktopFrame*> frames; 391 frames.push_back(frame.get()); 392 return MeasureVideoEncoderFpsWithFrames(encoder, frames); 393 } 394 395 float MeasureVideoEncoderFpsWithFrames(VideoEncoder* encoder, 396 const std::list<DesktopFrame*>& frames) { 397 const base::TimeDelta kTestTime = base::TimeDelta::FromSeconds(1); 398 399 // Encode some frames to "warm up" the encoder (i.e. to let it set up initial 400 // structures, establish a stable working set, etc), then encode at least 401 // kMinimumFrameCount frames to measure the encoder's performance. 402 const int kWarmUpFrameCount = 10; 403 const int kMinimumFrameCount = 10; 404 base::TimeTicks start_time; 405 base::TimeDelta elapsed; 406 std::list<DesktopFrame*> test_frames; 407 int frame_count; 408 for (frame_count = 0; 409 (frame_count < kMinimumFrameCount + kWarmUpFrameCount || 410 elapsed < kTestTime); 411 ++frame_count) { 412 if (frame_count == kWarmUpFrameCount) { 413 start_time = base::TimeTicks::Now(); 414 } 415 416 if (test_frames.empty()) { 417 test_frames = frames; 418 } 419 scoped_ptr<VideoPacket> packet = encoder->Encode(*test_frames.front()); 420 test_frames.pop_front(); 421 422 if (frame_count >= kWarmUpFrameCount) { 423 elapsed = base::TimeTicks::Now() - start_time; 424 } 425 } 426 427 return (frame_count * base::TimeDelta::FromSeconds(1)) / elapsed; 428 } 429 430 } // namespace remoting 431