OLD | NEW |
(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 |
OLD | NEW |