Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / remoting / codec / codec_test.cc
blob8b9511ce7bea16809d286058e00ce7fc7a95144e
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.
5 #include <deque>
6 #include <stdlib.h>
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"
19 using webrtc::BasicDesktopFrame;
20 using webrtc::DesktopFrame;
21 using webrtc::DesktopRect;
22 using webrtc::DesktopRegion;
23 using webrtc::DesktopSize;
25 namespace {
27 const int kBytesPerPixel = 4;
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;
51 } // namespace
53 namespace remoting {
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()));
72 void Reset() {
73 expected_region_.Clear();
74 update_region_.Clear();
77 void ResetRenderedData() {
78 memset(image_data_.get(), 0,
79 view_size_.width() * view_size_.height() * kBytesPerPixel);
82 void ReceivedPacket(VideoPacket* packet) {
83 ASSERT_TRUE(decoder_->DecodePacket(*packet));
85 RenderFrame();
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_);
96 void ReceivedScopedPacket(scoped_ptr<VideoPacket> packet) {
97 ReceivedPacket(packet.get());
100 void set_strict(bool strict) {
101 strict_ = strict;
104 void set_frame(DesktopFrame* frame) {
105 frame_ = frame;
108 void AddRects(const DesktopRect* rects, int count) {
109 for (int i = 0; i < count; ++i) {
110 expected_region_.AddRect(rects[i]);
114 void AddRegion(const DesktopRegion& region) {
115 expected_region_.AddRegion(region);
118 void VerifyResults() {
119 if (!strict_)
120 return;
122 ASSERT_TRUE(frame_);
124 // Test the content of the update region.
125 EXPECT_TRUE(expected_region_.Equals(update_region_));
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;
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;
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;
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;
186 original++;
187 decoded++;
188 return sqrt(error_sum_squares / 3.0);
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_;
201 DISALLOW_COPY_AND_ASSIGN(VideoDecoderTester);
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) {
213 ~VideoEncoderTester() {
214 EXPECT_GT(data_available_, 0);
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());
225 void set_decoder_tester(VideoDecoderTester* decoder_tester) {
226 decoder_tester_ = decoder_tester;
229 private:
230 VideoDecoderTester* decoder_tester_;
231 int data_available_;
233 DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester);
236 scoped_ptr<DesktopFrame> PrepareFrame(const DesktopSize& size) {
237 scoped_ptr<DesktopFrame> frame(new BasicDesktopFrame(size));
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;
245 return frame.Pass();
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]);
258 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame);
259 tester->DataAvailable(packet.Pass());
262 void TestVideoEncoder(VideoEncoder* encoder, bool strict) {
263 const int kSizes[] = {320, 319, 317, 150};
265 VideoEncoderTester tester;
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());
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]);
291 decoder_tester->AddRects(rects, count);
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();
308 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame);
309 encoder_tester->DataAvailable(packet.Pass());
310 decoder_tester->VerifyResults();
311 decoder_tester->Reset();
314 void TestVideoEncoderDecoder(
315 VideoEncoder* encoder, VideoDecoder* decoder, bool strict) {
316 DesktopSize kSize = DesktopSize(320, 240);
318 VideoEncoderTester encoder_tester;
320 scoped_ptr<DesktopFrame> frame = PrepareFrame(kSize);
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);
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());
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;
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));
360 scoped_ptr<BasicDesktopFrame> expected_result(
361 new BasicDesktopFrame(view_size));
362 FillWithGradient(expected_result.get());
364 VideoDecoderTester decoder_tester(decoder, screen_size, view_size);
365 decoder_tester.set_frame(frame.get());
366 decoder_tester.AddRegion(frame->updated_region());
368 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame);
369 decoder_tester.ReceivedScopedPacket(packet.Pass());
371 decoder_tester.VerifyResultsApprox(expected_result->data(),
372 max_error_limit, mean_error_limit);
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);
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);
395 float MeasureVideoEncoderFpsWithFrames(VideoEncoder* encoder,
396 const std::list<DesktopFrame*>& frames) {
397 const base::TimeDelta kTestTime = base::TimeDelta::FromSeconds(1);
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();
416 if (test_frames.empty()) {
417 test_frames = frames;
419 scoped_ptr<VideoPacket> packet = encoder->Encode(*test_frames.front());
420 test_frames.pop_front();
422 if (frame_count >= kWarmUpFrameCount) {
423 elapsed = base::TimeTicks::Now() - start_time;
427 return (frame_count * base::TimeDelta::FromSeconds(1)) / elapsed;
430 } // namespace remoting