Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: remoting/test/test_video_renderer_unittest.cc

Issue 1214113009: Update TestVideoRenderer with video decoding capabilities (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: "Minor changes on naming and comments" Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/test/test_video_renderer.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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.
4
5 #include "remoting/test/test_video_renderer.h"
6
7 #include <cmath>
8
9 #include "base/memory/scoped_vector.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/timer/timer.h"
13 #include "media/base/video_frame.h"
14 #include "remoting/codec/video_encoder.h"
15 #include "remoting/codec/video_encoder_verbatim.h"
16 #include "remoting/codec/video_encoder_vpx.h"
17 #include "remoting/proto/video.pb.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
20 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
21
22 namespace {
23 const int kBytesPerPixel = 4;
24 const int kDefaultScreenWidth = 1024;
25 const int kDefaultScreenHeight = 768;
26 const double kDefaultErrorLimit = 0.02;
27 }
28
29 namespace remoting {
30 namespace test {
31
32 // Provides basic functionality for for the TestVideoRenderer Tests below.
33 // This fixture also creates an MessageLoop to test decoding video packets.
34 class TestVideoRendererTest : public testing::Test {
35 public:
36 TestVideoRendererTest();
37 ~TestVideoRendererTest() override;
38
39 // Generate a frame containing a gradient and test decoding of
40 // TestVideoRenderer. The original frame is compared to the one obtained from
41 // decoding the video packet, and the error at each pixel is the root mean
42 // square of the errors in the R, G and B components, each normalized to
43 // [0, 1]. This routine checks that the mean error over all pixels do not
44 // exceed a given limit.
45 void TestVideoPacketProcessing(int screen_width, int screen_height,
46 double error_limit);
47
48 // Generate a basic desktop frame containing a gradient.
49 scoped_ptr<webrtc::DesktopFrame> CreateDesktopFrameWithGradient(
50 int screen_width, int screen_height) const;
51
52 protected:
53 // Used to post tasks to the message loop.
54 scoped_ptr<base::RunLoop> run_loop_;
55
56 // Used to set timeouts and delays.
57 scoped_ptr<base::Timer> timer_;
58
59 // Manages the decoder and process generated video packets.
60 scoped_ptr<TestVideoRenderer> test_video_renderer_;
61
62 // Used to encode desktop frames to generate video packets.
63 scoped_ptr<VideoEncoder> encoder_;
64
65 private:
66 // testing::Test interface.
67 void SetUp() override;
68
69 // return the mean error of two frames.
70 double CalculateError(const webrtc::DesktopFrame* original_frame,
71 const webrtc::DesktopFrame* decoded_frame) const;
72
73 // Fill a desktop frame with a gradient.
74 void FillFrameWithGradient(webrtc::DesktopFrame* frame) const;
75
76 // The thread's message loop. Valid only when the thread is alive.
77 scoped_ptr<base::MessageLoop> message_loop_;
78
79 DISALLOW_COPY_AND_ASSIGN(TestVideoRendererTest);
80 };
81
82 TestVideoRendererTest::TestVideoRendererTest()
83 : timer_(new base::Timer(true, false)) {}
84
85 TestVideoRendererTest::~TestVideoRendererTest() {}
86
87 void TestVideoRendererTest::SetUp() {
88 if (!base::MessageLoop::current()) {
89 // Create a temporary message loop if the current thread does not already
90 // have one.
91 message_loop_.reset(new base::MessageLoop);
92 }
93 test_video_renderer_.reset(new TestVideoRenderer());
94 }
95
96 void TestVideoRendererTest::TestVideoPacketProcessing(int screen_width,
97 int screen_height,
98 double error_limit) {
99 DCHECK(encoder_);
100 DCHECK(test_video_renderer_);
101
102 scoped_ptr<webrtc::DesktopFrame> original_frame =
103 CreateDesktopFrameWithGradient(screen_width, screen_height);
104 EXPECT_TRUE(original_frame);
105 scoped_ptr<VideoPacket> packet = encoder_->Encode(*original_frame.get());
106 DCHECK(!run_loop_ || !run_loop_->running());
107 run_loop_.reset(new base::RunLoop());
108
109 // Wait for the video packet to be processed and rendered to buffer.
110 test_video_renderer_->ProcessVideoPacket(packet.Pass(),
111 run_loop_->QuitClosure());
112 run_loop_->Run();
113
114 scoped_ptr<webrtc::DesktopFrame> buffer_copy =
115 test_video_renderer_->GetBufferForTest();
116 EXPECT_NE(buffer_copy, nullptr);
117 double error = CalculateError(original_frame.get(), buffer_copy.get());
118 EXPECT_LT(error, error_limit);
119 }
120
121 double TestVideoRendererTest::CalculateError(
122 const webrtc::DesktopFrame* original_frame,
123 const webrtc::DesktopFrame* decoded_frame) const {
124 DCHECK(original_frame);
125 DCHECK(decoded_frame);
126
127 // Check size remains the same after encoding and decoding.
128 EXPECT_EQ(original_frame->size().width(), decoded_frame->size().width());
129 EXPECT_EQ(original_frame->size().height(), decoded_frame->size().height());
130 EXPECT_EQ(original_frame->stride(), decoded_frame->stride());
131 int screen_width = original_frame->size().width();
132 int screen_height = original_frame->size().height();
133
134 // Error is calculated as the sum of the square error at each pixel in the
135 // R, G and B components, each normalized to [0, 1].
136 double error_sum_squares = 0.0;
137
138 // The mapping between the position of a pixel on 3-dimensional image
139 // (origin at top left corner) and its position in 1-dimensional buffer.
140 //
141 // _______________
142 // | | | stride = 4 * width;
143 // | | |
144 // | | height | height * stride + width + 0; Red channel.
145 // | | | => height * stride + width + 1; Green channel.
146 // |------- | height * stride + width + 2; Blue channel.
147 // | width |
148 // |_______________|
149 //
150 for (int height = 0; height < screen_height; ++height) {
151 uint8_t* original_ptr = original_frame->data() +
152 height * original_frame->stride();
153 uint8_t* decoded_ptr = decoded_frame->data() +
154 height * decoded_frame->stride();
155
156 for (int width = 0; width < screen_width; ++width) {
157 // Errors are calculated in the R, G, B components.
158 for (int j = 0; j < 3; ++j) {
159 int offset = kBytesPerPixel * width + j;
160 double original_value = static_cast<double>(*(original_ptr + offset));
161 double decoded_value = static_cast<double>(*(decoded_ptr + offset));
162 double error = original_value - decoded_value;
163
164 // Normalize the error to [0, 1].
165 error /= 255.0;
166 error_sum_squares += error * error;
167 }
168 }
169 }
170 return sqrt(error_sum_squares / (3 * screen_width * screen_height));
171 }
172
173 scoped_ptr<webrtc::DesktopFrame>
174 TestVideoRendererTest::CreateDesktopFrameWithGradient(
175 int screen_width, int screen_height) const {
176 webrtc::DesktopSize screen_size(screen_width, screen_height);
177 scoped_ptr<webrtc::DesktopFrame> frame(
178 new webrtc::BasicDesktopFrame(screen_size));
179 frame->mutable_updated_region()->SetRect(
180 webrtc::DesktopRect::MakeSize(screen_size));
181 FillFrameWithGradient(frame.get());
182 return frame.Pass();
183 }
184
185 void TestVideoRendererTest::FillFrameWithGradient(
186 webrtc::DesktopFrame* frame) const {
187 for (int y = 0; y < frame->size().height(); ++y) {
188 uint8* p = frame->data() + y * frame->stride();
189 for (int x = 0; x < frame->size().width(); ++x) {
190 *p++ = (255.0 * x) / frame->size().width();
191 *p++ = (164.0 * y) / frame->size().height();
192 *p++ = (82.0 * (x + y)) /
193 (frame->size().width() + frame->size().height());
194 *p++ = 0;
195 }
196 }
197 }
198
199 // Verify video decoding for VP8 Codec.
200 TEST_F(TestVideoRendererTest, VerifyVideoDecodingForVP8) {
201 encoder_ = VideoEncoderVpx::CreateForVP8();
202 test_video_renderer_->SetCodecForDecoding(
203 protocol::ChannelConfig::CODEC_VP8);
204 TestVideoPacketProcessing(kDefaultScreenWidth, kDefaultScreenHeight,
205 kDefaultErrorLimit);
206 }
207
208 // Verify video decoding for VP9 Codec.
209 TEST_F(TestVideoRendererTest, VerifyVideoDecodingForVP9) {
210 encoder_ = VideoEncoderVpx::CreateForVP9();
211 test_video_renderer_->SetCodecForDecoding(
212 protocol::ChannelConfig::CODEC_VP9);
213 TestVideoPacketProcessing(kDefaultScreenWidth, kDefaultScreenHeight,
214 kDefaultErrorLimit);
215 }
216
217
218 // Verify video decoding for VERBATIM Codec.
219 TEST_F(TestVideoRendererTest, VerifyVideoDecodingForVERBATIM) {
220 encoder_.reset(new VideoEncoderVerbatim());
221 test_video_renderer_->SetCodecForDecoding(
222 protocol::ChannelConfig::CODEC_VERBATIM);
223 TestVideoPacketProcessing(kDefaultScreenWidth, kDefaultScreenHeight,
224 kDefaultErrorLimit);
225 }
226
227 // Verify a set of video packets are processed correctly.
228 TEST_F(TestVideoRendererTest, VerifyMultipleVideoProcessing) {
229 encoder_ = VideoEncoderVpx::CreateForVP8();
230 test_video_renderer_->SetCodecForDecoding(
231 protocol::ChannelConfig::CODEC_VP8);
232
233 // Post multiple tasks to |test_video_renderer_|, and it should not crash.
234 // 20 is chosen because it's large enough to make sure that there will be
235 // more than one task on the video decode thread, while not too large to wait
236 // for too long for the unit test to complete.
237 const int task_num = 20;
238 ScopedVector<VideoPacket> video_packets;
239 for (int i = 0; i < task_num; ++i) {
240 scoped_ptr<webrtc::DesktopFrame> original_frame =
241 CreateDesktopFrameWithGradient(kDefaultScreenWidth,
242 kDefaultScreenHeight);
243 video_packets.push_back(encoder_->Encode(*original_frame.get()));
244 }
245
246 for (int i = 0; i < task_num; ++i) {
247 // Transfer ownership of video packet.
248 VideoPacket* packet = video_packets[i];
249 video_packets[i] = nullptr;
250 test_video_renderer_->ProcessVideoPacket(make_scoped_ptr(packet),
251 base::Bind(&base::DoNothing));
252 }
253 }
254
255 // Verify video packet size change is handled properly.
256 TEST_F(TestVideoRendererTest, VerifyVideoPacketSizeChange) {
257 encoder_ = VideoEncoderVpx::CreateForVP8();
258 test_video_renderer_->SetCodecForDecoding(
259 protocol::ChannelConfig::Codec::CODEC_VP8);
260
261 TestVideoPacketProcessing(kDefaultScreenWidth, kDefaultScreenHeight,
262 kDefaultErrorLimit);
263
264 TestVideoPacketProcessing(2 * kDefaultScreenWidth, 2 * kDefaultScreenHeight,
265 kDefaultErrorLimit);
266
267 TestVideoPacketProcessing(kDefaultScreenWidth / 2, kDefaultScreenHeight / 2,
268 kDefaultErrorLimit);
269 }
270
271 } // namespace test
272 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/test/test_video_renderer.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698