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