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

Unified 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, 6 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « remoting/test/test_video_renderer.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: remoting/test/test_video_renderer_unittest.cc
diff --git a/remoting/test/test_video_renderer_unittest.cc b/remoting/test/test_video_renderer_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8c1d92f7fd04d30fefa57345ad3d167a0172310b
--- /dev/null
+++ b/remoting/test/test_video_renderer_unittest.cc
@@ -0,0 +1,272 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/test/test_video_renderer.h"
+
+#include <cmath>
+
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/timer/timer.h"
+#include "media/base/video_frame.h"
+#include "remoting/codec/video_encoder.h"
+#include "remoting/codec/video_encoder_verbatim.h"
+#include "remoting/codec/video_encoder_vpx.h"
+#include "remoting/proto/video.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
+
+namespace {
+const int kBytesPerPixel = 4;
+const int kDefaultScreenWidth = 1024;
+const int kDefaultScreenHeight = 768;
+const double kDefaultErrorLimit = 0.02;
+}
+
+namespace remoting {
+namespace test {
+
+// Provides basic functionality for for the TestVideoRenderer Tests below.
+// This fixture also creates an MessageLoop to test decoding video packets.
+class TestVideoRendererTest : public testing::Test {
+ public:
+ TestVideoRendererTest();
+ ~TestVideoRendererTest() override;
+
+ // Generate a frame containing a gradient and test decoding of
+ // TestVideoRenderer. The original frame is compared to the one obtained from
+ // decoding the video packet, and the error at each pixel is the root mean
+ // square of the errors in the R, G and B components, each normalized to
+ // [0, 1]. This routine checks that the mean error over all pixels do not
+ // exceed a given limit.
+ void TestVideoPacketProcessing(int screen_width, int screen_height,
+ double error_limit);
+
+ // Generate a basic desktop frame containing a gradient.
+ scoped_ptr<webrtc::DesktopFrame> CreateDesktopFrameWithGradient(
+ int screen_width, int screen_height) const;
+
+ protected:
+ // Used to post tasks to the message loop.
+ scoped_ptr<base::RunLoop> run_loop_;
+
+ // Used to set timeouts and delays.
+ scoped_ptr<base::Timer> timer_;
+
+ // Manages the decoder and process generated video packets.
+ scoped_ptr<TestVideoRenderer> test_video_renderer_;
+
+ // Used to encode desktop frames to generate video packets.
+ scoped_ptr<VideoEncoder> encoder_;
+
+ private:
+ // testing::Test interface.
+ void SetUp() override;
+
+ // return the mean error of two frames.
+ double CalculateError(const webrtc::DesktopFrame* original_frame,
+ const webrtc::DesktopFrame* decoded_frame) const;
+
+ // Fill a desktop frame with a gradient.
+ void FillFrameWithGradient(webrtc::DesktopFrame* frame) const;
+
+ // The thread's message loop. Valid only when the thread is alive.
+ scoped_ptr<base::MessageLoop> message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestVideoRendererTest);
+};
+
+TestVideoRendererTest::TestVideoRendererTest()
+ : timer_(new base::Timer(true, false)) {}
+
+TestVideoRendererTest::~TestVideoRendererTest() {}
+
+void TestVideoRendererTest::SetUp() {
+ if (!base::MessageLoop::current()) {
+ // Create a temporary message loop if the current thread does not already
+ // have one.
+ message_loop_.reset(new base::MessageLoop);
+ }
+ test_video_renderer_.reset(new TestVideoRenderer());
+}
+
+void TestVideoRendererTest::TestVideoPacketProcessing(int screen_width,
+ int screen_height,
+ double error_limit) {
+ DCHECK(encoder_);
+ DCHECK(test_video_renderer_);
+
+ scoped_ptr<webrtc::DesktopFrame> original_frame =
+ CreateDesktopFrameWithGradient(screen_width, screen_height);
+ EXPECT_TRUE(original_frame);
+ scoped_ptr<VideoPacket> packet = encoder_->Encode(*original_frame.get());
+ DCHECK(!run_loop_ || !run_loop_->running());
+ run_loop_.reset(new base::RunLoop());
+
+ // Wait for the video packet to be processed and rendered to buffer.
+ test_video_renderer_->ProcessVideoPacket(packet.Pass(),
+ run_loop_->QuitClosure());
+ run_loop_->Run();
+
+ scoped_ptr<webrtc::DesktopFrame> buffer_copy =
+ test_video_renderer_->GetBufferForTest();
+ EXPECT_NE(buffer_copy, nullptr);
+ double error = CalculateError(original_frame.get(), buffer_copy.get());
+ EXPECT_LT(error, error_limit);
+}
+
+double TestVideoRendererTest::CalculateError(
+ const webrtc::DesktopFrame* original_frame,
+ const webrtc::DesktopFrame* decoded_frame) const {
+ DCHECK(original_frame);
+ DCHECK(decoded_frame);
+
+ // Check size remains the same after encoding and decoding.
+ EXPECT_EQ(original_frame->size().width(), decoded_frame->size().width());
+ EXPECT_EQ(original_frame->size().height(), decoded_frame->size().height());
+ EXPECT_EQ(original_frame->stride(), decoded_frame->stride());
+ int screen_width = original_frame->size().width();
+ int screen_height = original_frame->size().height();
+
+ // Error is calculated as the sum of the square error at each pixel in the
+ // R, G and B components, each normalized to [0, 1].
+ double error_sum_squares = 0.0;
+
+ // The mapping between the position of a pixel on 3-dimensional image
+ // (origin at top left corner) and its position in 1-dimensional buffer.
+ //
+ // _______________
+ // | | | stride = 4 * width;
+ // | | |
+ // | | height | height * stride + width + 0; Red channel.
+ // | | | => height * stride + width + 1; Green channel.
+ // |------- | height * stride + width + 2; Blue channel.
+ // | width |
+ // |_______________|
+ //
+ for (int height = 0; height < screen_height; ++height) {
+ uint8_t* original_ptr = original_frame->data() +
+ height * original_frame->stride();
+ uint8_t* decoded_ptr = decoded_frame->data() +
+ height * decoded_frame->stride();
+
+ for (int width = 0; width < screen_width; ++width) {
+ // Errors are calculated in the R, G, B components.
+ for (int j = 0; j < 3; ++j) {
+ int offset = kBytesPerPixel * width + j;
+ double original_value = static_cast<double>(*(original_ptr + offset));
+ double decoded_value = static_cast<double>(*(decoded_ptr + offset));
+ double error = original_value - decoded_value;
+
+ // Normalize the error to [0, 1].
+ error /= 255.0;
+ error_sum_squares += error * error;
+ }
+ }
+ }
+ return sqrt(error_sum_squares / (3 * screen_width * screen_height));
+}
+
+scoped_ptr<webrtc::DesktopFrame>
+ TestVideoRendererTest::CreateDesktopFrameWithGradient(
+ int screen_width, int screen_height) const {
+ webrtc::DesktopSize screen_size(screen_width, screen_height);
+ scoped_ptr<webrtc::DesktopFrame> frame(
+ new webrtc::BasicDesktopFrame(screen_size));
+ frame->mutable_updated_region()->SetRect(
+ webrtc::DesktopRect::MakeSize(screen_size));
+ FillFrameWithGradient(frame.get());
+ return frame.Pass();
+}
+
+void TestVideoRendererTest::FillFrameWithGradient(
+ webrtc::DesktopFrame* frame) const {
+ for (int y = 0; y < frame->size().height(); ++y) {
+ uint8* p = frame->data() + y * frame->stride();
+ for (int x = 0; x < frame->size().width(); ++x) {
+ *p++ = (255.0 * x) / frame->size().width();
+ *p++ = (164.0 * y) / frame->size().height();
+ *p++ = (82.0 * (x + y)) /
+ (frame->size().width() + frame->size().height());
+ *p++ = 0;
+ }
+ }
+}
+
+// Verify video decoding for VP8 Codec.
+TEST_F(TestVideoRendererTest, VerifyVideoDecodingForVP8) {
+ encoder_ = VideoEncoderVpx::CreateForVP8();
+ test_video_renderer_->SetCodecForDecoding(
+ protocol::ChannelConfig::CODEC_VP8);
+ TestVideoPacketProcessing(kDefaultScreenWidth, kDefaultScreenHeight,
+ kDefaultErrorLimit);
+}
+
+// Verify video decoding for VP9 Codec.
+TEST_F(TestVideoRendererTest, VerifyVideoDecodingForVP9) {
+ encoder_ = VideoEncoderVpx::CreateForVP9();
+ test_video_renderer_->SetCodecForDecoding(
+ protocol::ChannelConfig::CODEC_VP9);
+ TestVideoPacketProcessing(kDefaultScreenWidth, kDefaultScreenHeight,
+ kDefaultErrorLimit);
+}
+
+
+// Verify video decoding for VERBATIM Codec.
+TEST_F(TestVideoRendererTest, VerifyVideoDecodingForVERBATIM) {
+ encoder_.reset(new VideoEncoderVerbatim());
+ test_video_renderer_->SetCodecForDecoding(
+ protocol::ChannelConfig::CODEC_VERBATIM);
+ TestVideoPacketProcessing(kDefaultScreenWidth, kDefaultScreenHeight,
+ kDefaultErrorLimit);
+}
+
+// Verify a set of video packets are processed correctly.
+TEST_F(TestVideoRendererTest, VerifyMultipleVideoProcessing) {
+ encoder_ = VideoEncoderVpx::CreateForVP8();
+ test_video_renderer_->SetCodecForDecoding(
+ protocol::ChannelConfig::CODEC_VP8);
+
+ // Post multiple tasks to |test_video_renderer_|, and it should not crash.
+ // 20 is chosen because it's large enough to make sure that there will be
+ // more than one task on the video decode thread, while not too large to wait
+ // for too long for the unit test to complete.
+ const int task_num = 20;
+ ScopedVector<VideoPacket> video_packets;
+ for (int i = 0; i < task_num; ++i) {
+ scoped_ptr<webrtc::DesktopFrame> original_frame =
+ CreateDesktopFrameWithGradient(kDefaultScreenWidth,
+ kDefaultScreenHeight);
+ video_packets.push_back(encoder_->Encode(*original_frame.get()));
+ }
+
+ for (int i = 0; i < task_num; ++i) {
+ // Transfer ownership of video packet.
+ VideoPacket* packet = video_packets[i];
+ video_packets[i] = nullptr;
+ test_video_renderer_->ProcessVideoPacket(make_scoped_ptr(packet),
+ base::Bind(&base::DoNothing));
+ }
+}
+
+// Verify video packet size change is handled properly.
+TEST_F(TestVideoRendererTest, VerifyVideoPacketSizeChange) {
+ encoder_ = VideoEncoderVpx::CreateForVP8();
+ test_video_renderer_->SetCodecForDecoding(
+ protocol::ChannelConfig::Codec::CODEC_VP8);
+
+ TestVideoPacketProcessing(kDefaultScreenWidth, kDefaultScreenHeight,
+ kDefaultErrorLimit);
+
+ TestVideoPacketProcessing(2 * kDefaultScreenWidth, 2 * kDefaultScreenHeight,
+ kDefaultErrorLimit);
+
+ TestVideoPacketProcessing(kDefaultScreenWidth / 2, kDefaultScreenHeight / 2,
+ kDefaultErrorLimit);
+}
+
+} // namespace test
+} // namespace remoting
« 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