Home | History | Annotate | Download | only in codec
      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