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::DesktopRect
;
20 using webrtc::DesktopRegion
;
21 using webrtc::DesktopSize
;
25 const int kBytesPerPixel
= 4;
27 // Some sample rects for testing.
28 std::vector
<std::vector
<DesktopRect
> > MakeTestRectLists(DesktopSize size
) {
29 std::vector
<std::vector
<DesktopRect
> > rect_lists
;
30 std::vector
<DesktopRect
> rects
;
31 rects
.push_back(DesktopRect::MakeXYWH(0, 0, size
.width(), size
.height()));
32 rect_lists
.push_back(rects
);
34 rects
.push_back(DesktopRect::MakeXYWH(
35 0, 0, size
.width() / 2, size
.height() / 2));
36 rect_lists
.push_back(rects
);
38 rects
.push_back(DesktopRect::MakeXYWH(
39 size
.width() / 2, size
.height() / 2,
40 size
.width() / 2, size
.height() / 2));
41 rect_lists
.push_back(rects
);
43 rects
.push_back(DesktopRect::MakeXYWH(16, 16, 16, 16));
44 rects
.push_back(DesktopRect::MakeXYWH(128, 64, 32, 32));
45 rect_lists
.push_back(rects
);
53 class VideoDecoderTester
{
55 VideoDecoderTester(VideoDecoder
* decoder
,
56 const DesktopSize
& screen_size
,
57 const DesktopSize
& view_size
)
58 : screen_size_(screen_size
),
59 view_size_(view_size
),
63 image_data_
.reset(new uint8
[
64 view_size_
.width() * view_size_
.height() * kBytesPerPixel
]);
65 EXPECT_TRUE(image_data_
.get());
67 webrtc::DesktopSize(screen_size_
.width(), screen_size_
.height()));
71 expected_region_
.Clear();
72 update_region_
.Clear();
75 void ResetRenderedData() {
76 memset(image_data_
.get(), 0,
77 view_size_
.width() * view_size_
.height() * kBytesPerPixel
);
80 void ReceivedPacket(VideoPacket
* packet
) {
81 ASSERT_TRUE(decoder_
->DecodePacket(*packet
));
87 decoder_
->RenderFrame(
88 webrtc::DesktopSize(view_size_
.width(), view_size_
.height()),
89 webrtc::DesktopRect::MakeWH(view_size_
.width(), view_size_
.height()),
90 image_data_
.get(), view_size_
.width() * kBytesPerPixel
,
94 void ReceivedScopedPacket(scoped_ptr
<VideoPacket
> packet
) {
95 ReceivedPacket(packet
.get());
98 void set_strict(bool strict
) {
102 void set_frame(webrtc::DesktopFrame
* frame
) {
106 void AddRects(const DesktopRect
* rects
, int count
) {
107 for (int i
= 0; i
< count
; ++i
) {
108 expected_region_
.AddRect(rects
[i
]);
112 void AddRegion(const webrtc::DesktopRegion
& region
) {
113 expected_region_
.AddRegion(region
);
116 void VerifyResults() {
122 // Test the content of the update region.
123 EXPECT_TRUE(expected_region_
.Equals(update_region_
));
125 for (webrtc::DesktopRegion::Iterator
i(update_region_
); !i
.IsAtEnd();
127 const int stride
= view_size_
.width() * kBytesPerPixel
;
128 EXPECT_EQ(stride
, frame_
->stride());
129 const int offset
= stride
* i
.rect().top() +
130 kBytesPerPixel
* i
.rect().left();
131 const uint8
* original
= frame_
->data() + offset
;
132 const uint8
* decoded
= image_data_
.get() + offset
;
133 const int row_size
= kBytesPerPixel
* i
.rect().width();
134 for (int y
= 0; y
< i
.rect().height(); ++y
) {
135 EXPECT_EQ(0, memcmp(original
, decoded
, row_size
))
136 << "Row " << y
<< " is different";
143 // The error at each pixel is the root mean square of the errors in
144 // the R, G, and B components, each normalized to [0, 1]. This routine
145 // checks that the maximum and mean pixel errors do not exceed given limits.
146 void VerifyResultsApprox(const uint8
* expected_view_data
,
147 double max_error_limit
, double mean_error_limit
) {
148 double max_error
= 0.0;
149 double sum_error
= 0.0;
151 for (webrtc::DesktopRegion::Iterator
i(update_region_
); !i
.IsAtEnd();
153 const int stride
= view_size_
.width() * kBytesPerPixel
;
154 const int offset
= stride
* i
.rect().top() +
155 kBytesPerPixel
* i
.rect().left();
156 const uint8
* expected
= expected_view_data
+ offset
;
157 const uint8
* actual
= image_data_
.get() + offset
;
158 for (int y
= 0; y
< i
.rect().height(); ++y
) {
159 for (int x
= 0; x
< i
.rect().width(); ++x
) {
160 double error
= CalculateError(expected
, actual
);
161 max_error
= std::max(max_error
, error
);
169 EXPECT_LE(max_error
, max_error_limit
);
170 double mean_error
= sum_error
/ error_num
;
171 EXPECT_LE(mean_error
, mean_error_limit
);
172 VLOG(0) << "Max error: " << max_error
;
173 VLOG(0) << "Mean error: " << mean_error
;
176 double CalculateError(const uint8
* original
, const uint8
* decoded
) {
177 double error_sum_squares
= 0.0;
178 for (int i
= 0; i
< 3; i
++) {
179 double error
= static_cast<double>(*original
++) -
180 static_cast<double>(*decoded
++);
182 error_sum_squares
+= error
* error
;
186 return sqrt(error_sum_squares
/ 3.0);
190 DesktopSize screen_size_
;
191 DesktopSize view_size_
;
193 webrtc::DesktopRegion expected_region_
;
194 webrtc::DesktopRegion update_region_
;
195 VideoDecoder
* decoder_
;
196 scoped_ptr
<uint8
[]> image_data_
;
197 webrtc::DesktopFrame
* frame_
;
199 DISALLOW_COPY_AND_ASSIGN(VideoDecoderTester
);
202 // The VideoEncoderTester provides a hook for retrieving the data, and passing
203 // the message to other subprograms for validaton.
204 class VideoEncoderTester
{
207 : decoder_tester_(NULL
),
211 ~VideoEncoderTester() {
212 EXPECT_GT(data_available_
, 0);
215 void DataAvailable(scoped_ptr
<VideoPacket
> packet
) {
217 // Send the message to the VideoDecoderTester.
218 if (decoder_tester_
) {
219 decoder_tester_
->ReceivedPacket(packet
.get());
223 void set_decoder_tester(VideoDecoderTester
* decoder_tester
) {
224 decoder_tester_
= decoder_tester
;
228 VideoDecoderTester
* decoder_tester_
;
231 DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester
);
234 scoped_ptr
<webrtc::DesktopFrame
> PrepareFrame(const DesktopSize
& size
) {
235 scoped_ptr
<webrtc::DesktopFrame
> frame(new webrtc::BasicDesktopFrame(size
));
238 int memory_size
= size
.width() * size
.height() * kBytesPerPixel
;
239 for (int i
= 0; i
< memory_size
; ++i
) {
240 frame
->data()[i
] = rand() % 256;
246 static void TestEncodingRects(VideoEncoder
* encoder
,
247 VideoEncoderTester
* tester
,
248 webrtc::DesktopFrame
* frame
,
249 const DesktopRect
* rects
,
251 frame
->mutable_updated_region()->Clear();
252 for (int i
= 0; i
< count
; ++i
) {
253 frame
->mutable_updated_region()->AddRect(rects
[i
]);
256 scoped_ptr
<VideoPacket
> packet
= encoder
->Encode(*frame
);
257 tester
->DataAvailable(packet
.Pass());
260 void TestVideoEncoder(VideoEncoder
* encoder
, bool strict
) {
261 const int kSizes
[] = {320, 319, 317, 150};
263 VideoEncoderTester tester
;
265 for (size_t xi
= 0; xi
< arraysize(kSizes
); ++xi
) {
266 for (size_t yi
= 0; yi
< arraysize(kSizes
); ++yi
) {
267 DesktopSize size
= DesktopSize(kSizes
[xi
], kSizes
[yi
]);
268 scoped_ptr
<webrtc::DesktopFrame
> frame
= PrepareFrame(size
);
269 std::vector
<std::vector
<DesktopRect
> > test_rect_lists
=
270 MakeTestRectLists(size
);
271 for (size_t i
= 0; i
< test_rect_lists
.size(); ++i
) {
272 const std::vector
<DesktopRect
>& test_rects
= test_rect_lists
[i
];
273 TestEncodingRects(encoder
, &tester
, frame
.get(),
274 &test_rects
[0], test_rects
.size());
280 static void TestEncodeDecodeRects(VideoEncoder
* encoder
,
281 VideoEncoderTester
* encoder_tester
,
282 VideoDecoderTester
* decoder_tester
,
283 webrtc::DesktopFrame
* frame
,
284 const DesktopRect
* rects
, int count
) {
285 frame
->mutable_updated_region()->Clear();
286 for (int i
= 0; i
< count
; ++i
) {
287 frame
->mutable_updated_region()->AddRect(rects
[i
]);
289 decoder_tester
->AddRects(rects
, count
);
291 // Generate random data for the updated region.
293 for (int i
= 0; i
< count
; ++i
) {
295 webrtc::DesktopFrame::kBytesPerPixel
* rects
[i
].width();
296 uint8
* memory
= frame
->data() +
297 frame
->stride() * rects
[i
].top() +
298 webrtc::DesktopFrame::kBytesPerPixel
* rects
[i
].left();
299 for (int y
= 0; y
< rects
[i
].height(); ++y
) {
300 for (int x
= 0; x
< row_size
; ++x
)
301 memory
[x
] = rand() % 256;
302 memory
+= frame
->stride();
306 scoped_ptr
<VideoPacket
> packet
= encoder
->Encode(*frame
);
307 encoder_tester
->DataAvailable(packet
.Pass());
308 decoder_tester
->VerifyResults();
309 decoder_tester
->Reset();
312 void TestVideoEncoderDecoder(
313 VideoEncoder
* encoder
, VideoDecoder
* decoder
, bool strict
) {
314 DesktopSize kSize
= DesktopSize(320, 240);
316 VideoEncoderTester encoder_tester
;
318 scoped_ptr
<webrtc::DesktopFrame
> frame
= PrepareFrame(kSize
);
320 VideoDecoderTester
decoder_tester(decoder
, kSize
, kSize
);
321 decoder_tester
.set_strict(strict
);
322 decoder_tester
.set_frame(frame
.get());
323 encoder_tester
.set_decoder_tester(&decoder_tester
);
325 std::vector
<std::vector
<DesktopRect
> > test_rect_lists
=
326 MakeTestRectLists(kSize
);
327 for (size_t i
= 0; i
< test_rect_lists
.size(); ++i
) {
328 const std::vector
<DesktopRect
> test_rects
= test_rect_lists
[i
];
329 TestEncodeDecodeRects(encoder
, &encoder_tester
, &decoder_tester
,
330 frame
.get(), &test_rects
[0], test_rects
.size());
334 static void FillWithGradient(webrtc::DesktopFrame
* frame
) {
335 for (int j
= 0; j
< frame
->size().height(); ++j
) {
336 uint8
* p
= frame
->data() + j
* frame
->stride();
337 for (int i
= 0; i
< frame
->size().width(); ++i
) {
338 *p
++ = (255.0 * i
) / frame
->size().width();
339 *p
++ = (164.0 * j
) / frame
->size().height();
340 *p
++ = (82.0 * (i
+ j
)) /
341 (frame
->size().width() + frame
->size().height());
347 void TestVideoEncoderDecoderGradient(VideoEncoder
* encoder
,
348 VideoDecoder
* decoder
,
349 const DesktopSize
& screen_size
,
350 const DesktopSize
& view_size
,
351 double max_error_limit
,
352 double mean_error_limit
) {
353 scoped_ptr
<webrtc::BasicDesktopFrame
> frame(
354 new webrtc::BasicDesktopFrame(screen_size
));
355 FillWithGradient(frame
.get());
356 frame
->mutable_updated_region()->SetRect(DesktopRect::MakeSize(screen_size
));
358 scoped_ptr
<webrtc::BasicDesktopFrame
> expected_result(
359 new webrtc::BasicDesktopFrame(view_size
));
360 FillWithGradient(expected_result
.get());
362 VideoDecoderTester
decoder_tester(decoder
, screen_size
, view_size
);
363 decoder_tester
.set_frame(frame
.get());
364 decoder_tester
.AddRegion(frame
->updated_region());
366 scoped_ptr
<VideoPacket
> packet
= encoder
->Encode(*frame
);
367 decoder_tester
.ReceivedScopedPacket(packet
.Pass());
369 decoder_tester
.VerifyResultsApprox(expected_result
->data(),
370 max_error_limit
, mean_error_limit
);
372 // Check that the decoder correctly re-renders the frame if its client
373 // invalidates the frame.
374 decoder_tester
.ResetRenderedData();
376 webrtc::DesktopSize(view_size
.width(), view_size
.height()),
377 webrtc::DesktopRegion(
378 webrtc::DesktopRect::MakeWH(view_size
.width(), view_size
.height())));
379 decoder_tester
.RenderFrame();
380 decoder_tester
.VerifyResultsApprox(expected_result
->data(),
381 max_error_limit
, mean_error_limit
);
384 } // namespace remoting