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::BasicDesktopFrame;
20using webrtc::DesktopFrame;
21using webrtc::DesktopRect;
22using webrtc::DesktopRegion;
23using webrtc::DesktopSize;
24
25namespace {
26
27const int kBytesPerPixel = 4;
28
29// Some sample rects for testing.
30std::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
53namespace remoting {
54
55class 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.
206class 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
236scoped_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
248static 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
262void 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
282static 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
314void 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
336static 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
349void 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
386float 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
395float 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