Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / remoting / codec / codec_test.cc
blobcec00434b7cb3936093de538d99f4f00dcde548c
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/base/util.h"
13 #include "remoting/codec/codec_test.h"
14 #include "remoting/codec/video_decoder.h"
15 #include "remoting/codec/video_encoder.h"
16 #include "remoting/proto/video.pb.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
20 using webrtc::BasicDesktopFrame;
21 using webrtc::DesktopFrame;
22 using webrtc::DesktopRect;
23 using webrtc::DesktopRegion;
24 using webrtc::DesktopSize;
26 namespace {
28 const int kBytesPerPixel = 4;
30 // Some sample rects for testing.
31 std::vector<DesktopRegion> MakeTestRegionLists(DesktopSize size) {
32 std::vector<DesktopRegion> region_lists;
33 DesktopRegion region;
34 region.AddRect(DesktopRect::MakeXYWH(0, 0, size.width(), size.height()));
35 region_lists.push_back(region);
36 region.Clear();
37 region.AddRect(
38 DesktopRect::MakeXYWH(0, 0, size.width() / 2, size.height() / 2));
39 region_lists.push_back(region);
40 region.Clear();
41 region.AddRect(DesktopRect::MakeXYWH(size.width() / 2, size.height() / 2,
42 size.width() / 2, size.height() / 2));
43 region_lists.push_back(region);
44 region.Clear();
45 region.AddRect(DesktopRect::MakeXYWH(16, 16, 16, 16));
46 region.AddRect(DesktopRect::MakeXYWH(32, 32, 32, 32));
47 region.IntersectWith(DesktopRect::MakeSize(size));
48 region_lists.push_back(region);
49 return region_lists;
52 } // namespace
54 namespace remoting {
56 class VideoDecoderTester {
57 public:
58 VideoDecoderTester(VideoDecoder* decoder, const DesktopSize& screen_size)
59 : strict_(false),
60 decoder_(decoder),
61 frame_(new BasicDesktopFrame(screen_size)),
62 expected_frame_(nullptr) {}
64 void Reset() {
65 frame_.reset(new BasicDesktopFrame(frame_->size()));
66 expected_region_.Clear();
69 void ResetRenderedData() {
70 memset(frame_->data(), 0,
71 frame_->size().width() * frame_->size().height() * kBytesPerPixel);
74 void ReceivedPacket(scoped_ptr<VideoPacket> packet) {
75 ASSERT_TRUE(decoder_->DecodePacket(*packet, frame_.get()));
78 void set_strict(bool strict) {
79 strict_ = strict;
82 void set_expected_frame(DesktopFrame* frame) {
83 expected_frame_ = frame;
86 void AddRegion(const DesktopRegion& region) {
87 expected_region_.AddRegion(region);
90 void VerifyResults() {
91 if (!strict_)
92 return;
94 ASSERT_TRUE(expected_frame_);
96 // Test the content of the update region.
97 EXPECT_TRUE(expected_region_.Equals(frame_->updated_region()));
99 for (DesktopRegion::Iterator i(frame_->updated_region()); !i.IsAtEnd();
100 i.Advance()) {
101 const uint8_t* original =
102 expected_frame_->GetFrameDataAtPos(i.rect().top_left());
103 const uint8_t* decoded =
104 frame_->GetFrameDataAtPos(i.rect().top_left());
105 const int row_size = kBytesPerPixel * i.rect().width();
106 for (int y = 0; y < i.rect().height(); ++y) {
107 EXPECT_EQ(0, memcmp(original, decoded, row_size))
108 << "Row " << y << " is different";
109 original += expected_frame_->stride();
110 decoded += frame_->stride();
115 // The error at each pixel is the root mean square of the errors in
116 // the R, G, and B components, each normalized to [0, 1]. This routine
117 // checks that the maximum and mean pixel errors do not exceed given limits.
118 void VerifyResultsApprox(double max_error_limit, double mean_error_limit) {
119 double max_error = 0.0;
120 double sum_error = 0.0;
121 int error_num = 0;
122 for (DesktopRegion::Iterator i(frame_->updated_region()); !i.IsAtEnd();
123 i.Advance()) {
124 const uint8_t* expected =
125 expected_frame_->GetFrameDataAtPos(i.rect().top_left());
126 const uint8_t* actual =
127 frame_->GetFrameDataAtPos(i.rect().top_left());
128 for (int y = 0; y < i.rect().height(); ++y) {
129 for (int x = 0; x < i.rect().width(); ++x) {
130 double error = CalculateError(expected + x * kBytesPerPixel,
131 actual + x * kBytesPerPixel);
132 max_error = std::max(max_error, error);
133 sum_error += error;
134 ++error_num;
136 expected += expected_frame_->stride();
137 actual += frame_->stride();
140 EXPECT_LE(max_error, max_error_limit);
141 double mean_error = sum_error / error_num;
142 EXPECT_LE(mean_error, mean_error_limit);
143 VLOG(0) << "Max error: " << max_error;
144 VLOG(0) << "Mean error: " << mean_error;
147 double CalculateError(const uint8_t* original, const uint8_t* decoded) {
148 double error_sum_squares = 0.0;
149 for (int i = 0; i < 3; i++) {
150 double error = static_cast<double>(*original++) -
151 static_cast<double>(*decoded++);
152 error /= 255.0;
153 error_sum_squares += error * error;
155 original++;
156 decoded++;
157 return sqrt(error_sum_squares / 3.0);
160 private:
161 bool strict_;
162 DesktopRegion expected_region_;
163 VideoDecoder* decoder_;
164 scoped_ptr<DesktopFrame> frame_;
165 DesktopFrame* expected_frame_;
167 DISALLOW_COPY_AND_ASSIGN(VideoDecoderTester);
170 // The VideoEncoderTester provides a hook for retrieving the data, and passing
171 // the message to other subprograms for validaton.
172 class VideoEncoderTester {
173 public:
174 VideoEncoderTester() : decoder_tester_(nullptr), data_available_(0) {}
176 ~VideoEncoderTester() {
177 EXPECT_GT(data_available_, 0);
180 void DataAvailable(scoped_ptr<VideoPacket> packet) {
181 ++data_available_;
182 // Send the message to the VideoDecoderTester.
183 if (decoder_tester_) {
184 decoder_tester_->ReceivedPacket(packet.Pass());
188 void set_decoder_tester(VideoDecoderTester* decoder_tester) {
189 decoder_tester_ = decoder_tester;
192 private:
193 VideoDecoderTester* decoder_tester_;
194 int data_available_;
196 DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester);
199 scoped_ptr<DesktopFrame> PrepareFrame(const DesktopSize& size) {
200 scoped_ptr<DesktopFrame> frame(new BasicDesktopFrame(size));
202 srand(0);
203 int memory_size = size.width() * size.height() * kBytesPerPixel;
204 for (int i = 0; i < memory_size; ++i) {
205 frame->data()[i] = rand() % 256;
208 return frame.Pass();
211 static void TestEncodingRects(VideoEncoder* encoder,
212 VideoEncoderTester* tester,
213 DesktopFrame* frame,
214 const DesktopRegion& region) {
215 *frame->mutable_updated_region() = region;
216 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame);
217 tester->DataAvailable(packet.Pass());
220 void TestVideoEncoder(VideoEncoder* encoder, bool strict) {
221 const int kSizes[] = {80, 79, 77, 54};
223 VideoEncoderTester tester;
225 for (size_t xi = 0; xi < arraysize(kSizes); ++xi) {
226 for (size_t yi = 0; yi < arraysize(kSizes); ++yi) {
227 DesktopSize size(kSizes[xi], kSizes[yi]);
228 scoped_ptr<DesktopFrame> frame = PrepareFrame(size);
229 for (const DesktopRegion& region : MakeTestRegionLists(size)) {
230 TestEncodingRects(encoder, &tester, frame.get(), region);
233 // Pass some empty frames through the encoder.
234 for (int i = 0; i < 5; ++i) {
235 TestEncodingRects(encoder, &tester, frame.get(), DesktopRegion());
241 void TestVideoEncoderEmptyFrames(VideoEncoder* encoder,
242 int max_topoff_frames) {
243 const DesktopSize kSize(100, 100);
244 scoped_ptr<DesktopFrame> frame(PrepareFrame(kSize));
246 frame->mutable_updated_region()->SetRect(
247 webrtc::DesktopRect::MakeSize(kSize));
248 EXPECT_TRUE(encoder->Encode(*frame));
250 int topoff_frames = 0;
251 frame->mutable_updated_region()->Clear();
252 for (int i = 0; i < max_topoff_frames + 1; ++i) {
253 if (!encoder->Encode(*frame))
254 break;
255 topoff_frames++;
258 // If top-off is enabled then our random frame contents should always
259 // trigger it, so expect at least one top-off frame - strictly, though,
260 // an encoder may not always need to top-off.
261 EXPECT_GE(topoff_frames, max_topoff_frames ? 1 : 0);
262 EXPECT_LE(topoff_frames, max_topoff_frames);
265 static void TestEncodeDecodeRects(VideoEncoder* encoder,
266 VideoEncoderTester* encoder_tester,
267 VideoDecoderTester* decoder_tester,
268 DesktopFrame* frame,
269 const DesktopRegion& region) {
270 *frame->mutable_updated_region() = region;
271 decoder_tester->AddRegion(region);
273 // Generate random data for the updated region.
274 srand(0);
275 for (DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) {
276 const int row_size = DesktopFrame::kBytesPerPixel * i.rect().width();
277 uint8_t* memory = frame->data() + frame->stride() * i.rect().top() +
278 DesktopFrame::kBytesPerPixel * i.rect().left();
279 for (int y = 0; y < i.rect().height(); ++y) {
280 for (int x = 0; x < row_size; ++x)
281 memory[x] = rand() % 256;
282 memory += frame->stride();
286 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame);
287 encoder_tester->DataAvailable(packet.Pass());
288 decoder_tester->VerifyResults();
289 decoder_tester->Reset();
292 void TestVideoEncoderDecoder(VideoEncoder* encoder,
293 VideoDecoder* decoder,
294 bool strict) {
295 DesktopSize kSize = DesktopSize(160, 120);
297 VideoEncoderTester encoder_tester;
299 scoped_ptr<DesktopFrame> frame = PrepareFrame(kSize);
301 VideoDecoderTester decoder_tester(decoder, kSize);
302 decoder_tester.set_strict(strict);
303 decoder_tester.set_expected_frame(frame.get());
304 encoder_tester.set_decoder_tester(&decoder_tester);
306 for (const DesktopRegion& region : MakeTestRegionLists(kSize)) {
307 TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester,
308 frame.get(), region);
312 static void FillWithGradient(DesktopFrame* frame) {
313 for (int j = 0; j < frame->size().height(); ++j) {
314 uint8_t* p = frame->data() + j * frame->stride();
315 for (int i = 0; i < frame->size().width(); ++i) {
316 *p++ = (255.0 * i) / frame->size().width();
317 *p++ = (164.0 * j) / frame->size().height();
318 *p++ = (82.0 * (i + j)) /
319 (frame->size().width() + frame->size().height());
320 *p++ = 0;
325 void TestVideoEncoderDecoderGradient(VideoEncoder* encoder,
326 VideoDecoder* decoder,
327 const DesktopSize& screen_size,
328 double max_error_limit,
329 double mean_error_limit) {
330 scoped_ptr<BasicDesktopFrame> frame(
331 new BasicDesktopFrame(screen_size));
332 FillWithGradient(frame.get());
333 frame->mutable_updated_region()->SetRect(DesktopRect::MakeSize(screen_size));
335 VideoDecoderTester decoder_tester(decoder, screen_size);
336 decoder_tester.set_expected_frame(frame.get());
337 decoder_tester.AddRegion(frame->updated_region());
339 scoped_ptr<VideoPacket> packet = encoder->Encode(*frame);
340 decoder_tester.ReceivedPacket(packet.Pass());
342 decoder_tester.VerifyResultsApprox(max_error_limit, mean_error_limit);
345 float MeasureVideoEncoderFpsWithSize(VideoEncoder* encoder,
346 const DesktopSize& size) {
347 scoped_ptr<DesktopFrame> frame(PrepareFrame(size));
348 frame->mutable_updated_region()->SetRect(DesktopRect::MakeSize(size));
349 std::list<DesktopFrame*> frames;
350 frames.push_back(frame.get());
351 return MeasureVideoEncoderFpsWithFrames(encoder, frames);
354 float MeasureVideoEncoderFpsWithFrames(VideoEncoder* encoder,
355 const std::list<DesktopFrame*>& frames) {
356 const base::TimeDelta kTestTime = base::TimeDelta::FromSeconds(1);
358 // Encode some frames to "warm up" the encoder (i.e. to let it set up initial
359 // structures, establish a stable working set, etc), then encode at least
360 // kMinimumFrameCount frames to measure the encoder's performance.
361 const int kWarmUpFrameCount = 10;
362 const int kMinimumFrameCount = 10;
363 base::TimeTicks start_time;
364 base::TimeDelta elapsed;
365 std::list<DesktopFrame*> test_frames;
366 int frame_count;
367 for (frame_count = 0;
368 (frame_count < kMinimumFrameCount + kWarmUpFrameCount ||
369 elapsed < kTestTime);
370 ++frame_count) {
371 if (frame_count == kWarmUpFrameCount) {
372 start_time = base::TimeTicks::Now();
375 if (test_frames.empty()) {
376 test_frames = frames;
378 scoped_ptr<VideoPacket> packet = encoder->Encode(*test_frames.front());
379 test_frames.pop_front();
381 if (frame_count >= kWarmUpFrameCount) {
382 elapsed = base::TimeTicks::Now() - start_time;
386 return (frame_count * base::TimeDelta::FromSeconds(1)) / elapsed;
389 } // namespace remoting