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.
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
;
27 const int kBytesPerPixel
= 4;
29 // Some sample rects for testing.
30 std::vector
<DesktopRegion
> MakeTestRegionLists(DesktopSize size
) {
31 std::vector
<DesktopRegion
> region_lists
;
33 region
.AddRect(DesktopRect::MakeXYWH(0, 0, size
.width(), size
.height()));
34 region_lists
.push_back(region
);
37 DesktopRect::MakeXYWH(0, 0, size
.width() / 2, size
.height() / 2));
38 region_lists
.push_back(region
);
40 region
.AddRect(DesktopRect::MakeXYWH(size
.width() / 2, size
.height() / 2,
41 size
.width() / 2, size
.height() / 2));
42 region_lists
.push_back(region
);
44 region
.AddRect(DesktopRect::MakeXYWH(16, 16, 16, 16));
45 region
.AddRect(DesktopRect::MakeXYWH(32, 32, 32, 32));
46 region
.IntersectWith(DesktopRect::MakeSize(size
));
47 region_lists
.push_back(region
);
55 class VideoDecoderTester
{
57 VideoDecoderTester(VideoDecoder
* decoder
,
58 const DesktopSize
& screen_size
,
59 const DesktopSize
& view_size
)
60 : screen_size_(screen_size
),
61 view_size_(view_size
),
65 image_data_
.reset(new uint8
[
66 view_size_
.width() * view_size_
.height() * kBytesPerPixel
]);
67 EXPECT_TRUE(image_data_
.get());
69 DesktopSize(screen_size_
.width(), screen_size_
.height()));
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
));
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
,
96 void ReceivedScopedPacket(scoped_ptr
<VideoPacket
> packet
) {
97 ReceivedPacket(packet
.get());
100 void set_strict(bool strict
) {
104 void set_frame(DesktopFrame
* frame
) {
108 void AddRegion(const DesktopRegion
& region
) {
109 expected_region_
.AddRegion(region
);
112 void VerifyResults() {
118 // Test the content of the update region.
119 EXPECT_TRUE(expected_region_
.Equals(update_region_
));
121 for (DesktopRegion::Iterator
i(update_region_
); !i
.IsAtEnd();
123 const int stride
= view_size_
.width() * kBytesPerPixel
;
124 EXPECT_EQ(stride
, frame_
->stride());
125 const int offset
= stride
* i
.rect().top() +
126 kBytesPerPixel
* i
.rect().left();
127 const uint8
* original
= frame_
->data() + offset
;
128 const uint8
* decoded
= image_data_
.get() + offset
;
129 const int row_size
= kBytesPerPixel
* i
.rect().width();
130 for (int y
= 0; y
< i
.rect().height(); ++y
) {
131 EXPECT_EQ(0, memcmp(original
, decoded
, row_size
))
132 << "Row " << y
<< " is different";
139 // The error at each pixel is the root mean square of the errors in
140 // the R, G, and B components, each normalized to [0, 1]. This routine
141 // checks that the maximum and mean pixel errors do not exceed given limits.
142 void VerifyResultsApprox(const uint8
* expected_view_data
,
143 double max_error_limit
, double mean_error_limit
) {
144 double max_error
= 0.0;
145 double sum_error
= 0.0;
147 for (DesktopRegion::Iterator
i(update_region_
); !i
.IsAtEnd();
149 const int stride
= view_size_
.width() * kBytesPerPixel
;
150 const int offset
= stride
* i
.rect().top() +
151 kBytesPerPixel
* i
.rect().left();
152 const uint8
* expected
= expected_view_data
+ offset
;
153 const uint8
* actual
= image_data_
.get() + offset
;
154 for (int y
= 0; y
< i
.rect().height(); ++y
) {
155 for (int x
= 0; x
< i
.rect().width(); ++x
) {
156 double error
= CalculateError(expected
, actual
);
157 max_error
= std::max(max_error
, error
);
165 EXPECT_LE(max_error
, max_error_limit
);
166 double mean_error
= sum_error
/ error_num
;
167 EXPECT_LE(mean_error
, mean_error_limit
);
168 VLOG(0) << "Max error: " << max_error
;
169 VLOG(0) << "Mean error: " << mean_error
;
172 double CalculateError(const uint8
* original
, const uint8
* decoded
) {
173 double error_sum_squares
= 0.0;
174 for (int i
= 0; i
< 3; i
++) {
175 double error
= static_cast<double>(*original
++) -
176 static_cast<double>(*decoded
++);
178 error_sum_squares
+= error
* error
;
182 return sqrt(error_sum_squares
/ 3.0);
186 DesktopSize screen_size_
;
187 DesktopSize view_size_
;
189 DesktopRegion expected_region_
;
190 DesktopRegion update_region_
;
191 VideoDecoder
* decoder_
;
192 scoped_ptr
<uint8
[]> image_data_
;
193 DesktopFrame
* frame_
;
195 DISALLOW_COPY_AND_ASSIGN(VideoDecoderTester
);
198 // The VideoEncoderTester provides a hook for retrieving the data, and passing
199 // the message to other subprograms for validaton.
200 class VideoEncoderTester
{
202 VideoEncoderTester() : decoder_tester_(nullptr), data_available_(0) {}
204 ~VideoEncoderTester() {
205 EXPECT_GT(data_available_
, 0);
208 void DataAvailable(scoped_ptr
<VideoPacket
> packet
) {
210 // Send the message to the VideoDecoderTester.
211 if (decoder_tester_
) {
212 decoder_tester_
->ReceivedPacket(packet
.get());
216 void set_decoder_tester(VideoDecoderTester
* decoder_tester
) {
217 decoder_tester_
= decoder_tester
;
221 VideoDecoderTester
* decoder_tester_
;
224 DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester
);
227 scoped_ptr
<DesktopFrame
> PrepareFrame(const DesktopSize
& size
) {
228 scoped_ptr
<DesktopFrame
> frame(new BasicDesktopFrame(size
));
231 int memory_size
= size
.width() * size
.height() * kBytesPerPixel
;
232 for (int i
= 0; i
< memory_size
; ++i
) {
233 frame
->data()[i
] = rand() % 256;
239 static void TestEncodingRects(VideoEncoder
* encoder
,
240 VideoEncoderTester
* tester
,
242 const DesktopRegion
& region
) {
243 *frame
->mutable_updated_region() = region
;
244 scoped_ptr
<VideoPacket
> packet
= encoder
->Encode(*frame
);
245 tester
->DataAvailable(packet
.Pass());
248 void TestVideoEncoder(VideoEncoder
* encoder
, bool strict
) {
249 const int kSizes
[] = {80, 79, 77, 54};
251 VideoEncoderTester tester
;
253 for (size_t xi
= 0; xi
< arraysize(kSizes
); ++xi
) {
254 for (size_t yi
= 0; yi
< arraysize(kSizes
); ++yi
) {
255 DesktopSize
size(kSizes
[xi
], kSizes
[yi
]);
256 scoped_ptr
<DesktopFrame
> frame
= PrepareFrame(size
);
257 for (const DesktopRegion
& region
: MakeTestRegionLists(size
)) {
258 TestEncodingRects(encoder
, &tester
, frame
.get(), region
);
261 // Pass some empty frames through the encoder.
262 for (int i
= 0; i
< 5; ++i
) {
263 TestEncodingRects(encoder
, &tester
, frame
.get(), DesktopRegion());
269 void TestVideoEncoderEmptyFrames(VideoEncoder
* encoder
,
270 int max_topoff_frames
) {
271 const DesktopSize
kSize(100, 100);
272 scoped_ptr
<DesktopFrame
> frame(PrepareFrame(kSize
));
274 frame
->mutable_updated_region()->SetRect(
275 webrtc::DesktopRect::MakeSize(kSize
));
276 EXPECT_TRUE(encoder
->Encode(*frame
));
278 int topoff_frames
= 0;
279 frame
->mutable_updated_region()->Clear();
280 for (int i
= 0; i
< max_topoff_frames
+ 1; ++i
) {
281 if (!encoder
->Encode(*frame
))
286 // If top-off is enabled then our random frame contents should always
287 // trigger it, so expect at least one top-off frame - strictly, though,
288 // an encoder may not always need to top-off.
289 EXPECT_GE(topoff_frames
, max_topoff_frames
? 1 : 0);
290 EXPECT_LE(topoff_frames
, max_topoff_frames
);
293 static void TestEncodeDecodeRects(VideoEncoder
* encoder
,
294 VideoEncoderTester
* encoder_tester
,
295 VideoDecoderTester
* decoder_tester
,
297 const DesktopRegion
& region
) {
298 *frame
->mutable_updated_region() = region
;
299 decoder_tester
->AddRegion(region
);
301 // Generate random data for the updated region.
303 for (DesktopRegion::Iterator
i(region
); !i
.IsAtEnd(); i
.Advance()) {
304 const int row_size
= DesktopFrame::kBytesPerPixel
* i
.rect().width();
305 uint8
* memory
= frame
->data() + frame
->stride() * i
.rect().top() +
306 DesktopFrame::kBytesPerPixel
* i
.rect().left();
307 for (int y
= 0; y
< i
.rect().height(); ++y
) {
308 for (int x
= 0; x
< row_size
; ++x
)
309 memory
[x
] = rand() % 256;
310 memory
+= frame
->stride();
314 scoped_ptr
<VideoPacket
> packet
= encoder
->Encode(*frame
);
315 encoder_tester
->DataAvailable(packet
.Pass());
316 decoder_tester
->VerifyResults();
317 decoder_tester
->Reset();
320 void TestVideoEncoderDecoder(VideoEncoder
* encoder
,
321 VideoDecoder
* decoder
,
323 DesktopSize kSize
= DesktopSize(160, 120);
325 VideoEncoderTester encoder_tester
;
327 scoped_ptr
<DesktopFrame
> frame
= PrepareFrame(kSize
);
329 VideoDecoderTester
decoder_tester(decoder
, kSize
, kSize
);
330 decoder_tester
.set_strict(strict
);
331 decoder_tester
.set_frame(frame
.get());
332 encoder_tester
.set_decoder_tester(&decoder_tester
);
334 for (const DesktopRegion
& region
: MakeTestRegionLists(kSize
)) {
335 TestEncodeDecodeRects(encoder
, &encoder_tester
, &decoder_tester
,
336 frame
.get(), region
);
340 static void FillWithGradient(DesktopFrame
* frame
) {
341 for (int j
= 0; j
< frame
->size().height(); ++j
) {
342 uint8
* p
= frame
->data() + j
* frame
->stride();
343 for (int i
= 0; i
< frame
->size().width(); ++i
) {
344 *p
++ = (255.0 * i
) / frame
->size().width();
345 *p
++ = (164.0 * j
) / frame
->size().height();
346 *p
++ = (82.0 * (i
+ j
)) /
347 (frame
->size().width() + frame
->size().height());
353 void TestVideoEncoderDecoderGradient(VideoEncoder
* encoder
,
354 VideoDecoder
* decoder
,
355 const DesktopSize
& screen_size
,
356 const DesktopSize
& view_size
,
357 double max_error_limit
,
358 double mean_error_limit
) {
359 scoped_ptr
<BasicDesktopFrame
> frame(
360 new BasicDesktopFrame(screen_size
));
361 FillWithGradient(frame
.get());
362 frame
->mutable_updated_region()->SetRect(DesktopRect::MakeSize(screen_size
));
364 scoped_ptr
<BasicDesktopFrame
> expected_result(
365 new BasicDesktopFrame(view_size
));
366 FillWithGradient(expected_result
.get());
368 VideoDecoderTester
decoder_tester(decoder
, screen_size
, view_size
);
369 decoder_tester
.set_frame(frame
.get());
370 decoder_tester
.AddRegion(frame
->updated_region());
372 scoped_ptr
<VideoPacket
> packet
= encoder
->Encode(*frame
);
373 decoder_tester
.ReceivedScopedPacket(packet
.Pass());
375 decoder_tester
.VerifyResultsApprox(expected_result
->data(),
376 max_error_limit
, mean_error_limit
);
378 // Check that the decoder correctly re-renders the frame if its client
379 // invalidates the frame.
380 decoder_tester
.ResetRenderedData();
382 DesktopSize(view_size
.width(), view_size
.height()),
384 DesktopRect::MakeWH(view_size
.width(), view_size
.height())));
385 decoder_tester
.RenderFrame();
386 decoder_tester
.VerifyResultsApprox(expected_result
->data(),
387 max_error_limit
, mean_error_limit
);
390 float MeasureVideoEncoderFpsWithSize(VideoEncoder
* encoder
,
391 const DesktopSize
& size
) {
392 scoped_ptr
<DesktopFrame
> frame(PrepareFrame(size
));
393 frame
->mutable_updated_region()->SetRect(DesktopRect::MakeSize(size
));
394 std::list
<DesktopFrame
*> frames
;
395 frames
.push_back(frame
.get());
396 return MeasureVideoEncoderFpsWithFrames(encoder
, frames
);
399 float MeasureVideoEncoderFpsWithFrames(VideoEncoder
* encoder
,
400 const std::list
<DesktopFrame
*>& frames
) {
401 const base::TimeDelta kTestTime
= base::TimeDelta::FromSeconds(1);
403 // Encode some frames to "warm up" the encoder (i.e. to let it set up initial
404 // structures, establish a stable working set, etc), then encode at least
405 // kMinimumFrameCount frames to measure the encoder's performance.
406 const int kWarmUpFrameCount
= 10;
407 const int kMinimumFrameCount
= 10;
408 base::TimeTicks start_time
;
409 base::TimeDelta elapsed
;
410 std::list
<DesktopFrame
*> test_frames
;
412 for (frame_count
= 0;
413 (frame_count
< kMinimumFrameCount
+ kWarmUpFrameCount
||
414 elapsed
< kTestTime
);
416 if (frame_count
== kWarmUpFrameCount
) {
417 start_time
= base::TimeTicks::Now();
420 if (test_frames
.empty()) {
421 test_frames
= frames
;
423 scoped_ptr
<VideoPacket
> packet
= encoder
->Encode(*test_frames
.front());
424 test_frames
.pop_front();
426 if (frame_count
>= kWarmUpFrameCount
) {
427 elapsed
= base::TimeTicks::Now() - start_time
;
431 return (frame_count
* base::TimeDelta::FromSeconds(1)) / elapsed
;
434 } // namespace remoting