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
<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
);
36 rects
.push_back(DesktopRect::MakeXYWH(
37 0, 0, size
.width() / 2, size
.height() / 2));
38 rect_lists
.push_back(rects
);
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
);
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
);
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 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() {
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();
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";
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;
153 for (DesktopRegion::Iterator
i(update_region_
); !i
.IsAtEnd();
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
);
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
++);
184 error_sum_squares
+= error
* error
;
188 return sqrt(error_sum_squares
/ 3.0);
192 DesktopSize screen_size_
;
193 DesktopSize view_size_
;
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
{
209 : decoder_tester_(NULL
),
213 ~VideoEncoderTester() {
214 EXPECT_GT(data_available_
, 0);
217 void DataAvailable(scoped_ptr
<VideoPacket
> packet
) {
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
;
230 VideoDecoderTester
* decoder_tester_
;
233 DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester
);
236 scoped_ptr
<DesktopFrame
> PrepareFrame(const DesktopSize
& size
) {
237 scoped_ptr
<DesktopFrame
> frame(new BasicDesktopFrame(size
));
240 int memory_size
= size
.width() * size
.height() * kBytesPerPixel
;
241 for (int i
= 0; i
< memory_size
; ++i
) {
242 frame
->data()[i
] = rand() % 256;
248 static void TestEncodingRects(VideoEncoder
* encoder
,
249 VideoEncoderTester
* tester
,
250 webrtc::DesktopFrame
* frame
,
251 const DesktopRect
* rects
,
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
,
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.
295 for (int i
= 0; i
< count
; ++i
) {
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());
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();
378 DesktopSize(view_size
.width(), view_size
.height()),
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
;
408 for (frame_count
= 0;
409 (frame_count
< kMinimumFrameCount
+ kWarmUpFrameCount
||
410 elapsed
< kTestTime
);
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