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