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/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
;
28 const int kBytesPerPixel
= 4;
30 // Some sample rects for testing.
31 std::vector
<DesktopRegion
> MakeTestRegionLists(DesktopSize size
) {
32 std::vector
<DesktopRegion
> region_lists
;
34 region
.AddRect(DesktopRect::MakeXYWH(0, 0, size
.width(), size
.height()));
35 region_lists
.push_back(region
);
38 DesktopRect::MakeXYWH(0, 0, size
.width() / 2, size
.height() / 2));
39 region_lists
.push_back(region
);
41 region
.AddRect(DesktopRect::MakeXYWH(size
.width() / 2, size
.height() / 2,
42 size
.width() / 2, size
.height() / 2));
43 region_lists
.push_back(region
);
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
);
56 class VideoDecoderTester
{
58 VideoDecoderTester(VideoDecoder
* decoder
, const DesktopSize
& screen_size
)
61 frame_(new BasicDesktopFrame(screen_size
)),
62 expected_frame_(nullptr) {}
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
) {
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() {
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();
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;
122 for (DesktopRegion::Iterator
i(frame_
->updated_region()); !i
.IsAtEnd();
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
);
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
++);
153 error_sum_squares
+= error
* error
;
157 return sqrt(error_sum_squares
/ 3.0);
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
{
174 VideoEncoderTester() : decoder_tester_(nullptr), data_available_(0) {}
176 ~VideoEncoderTester() {
177 EXPECT_GT(data_available_
, 0);
180 void DataAvailable(scoped_ptr
<VideoPacket
> packet
) {
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
;
193 VideoDecoderTester
* decoder_tester_
;
196 DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester
);
199 scoped_ptr
<DesktopFrame
> PrepareFrame(const DesktopSize
& size
) {
200 scoped_ptr
<DesktopFrame
> frame(new BasicDesktopFrame(size
));
203 int memory_size
= size
.width() * size
.height() * kBytesPerPixel
;
204 for (int i
= 0; i
< memory_size
; ++i
) {
205 frame
->data()[i
] = rand() % 256;
211 static void TestEncodingRects(VideoEncoder
* encoder
,
212 VideoEncoderTester
* tester
,
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
))
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
,
269 const DesktopRegion
& region
) {
270 *frame
->mutable_updated_region() = region
;
271 decoder_tester
->AddRegion(region
);
273 // Generate random data for the updated region.
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
,
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());
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
;
367 for (frame_count
= 0;
368 (frame_count
< kMinimumFrameCount
+ kWarmUpFrameCount
||
369 elapsed
< kTestTime
);
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