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

Unified Diff: remoting/test/protocol_perftest.cc

Issue 394883008: Add basic perf tests for chromoting protocol. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « remoting/test/DEPS ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: remoting/test/protocol_perftest.cc
diff --git a/remoting/test/protocol_perftest.cc b/remoting/test/protocol_perftest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2ce73a1c45c2efa6fb481f50ef78a9fb4c991364
--- /dev/null
+++ b/remoting/test/protocol_perftest.cc
@@ -0,0 +1,357 @@
+// Copyright 2014 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 "base/base64.h"
+#include "base/file_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/thread_task_runner_handle.h"
+#include "net/base/test_data_directory.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "remoting/base/rsa_key_pair.h"
+#include "remoting/client/audio_player.h"
+#include "remoting/client/chromoting_client.h"
+#include "remoting/client/client_context.h"
+#include "remoting/client/client_user_interface.h"
+#include "remoting/client/video_renderer.h"
+#include "remoting/host/chromoting_host.h"
+#include "remoting/host/chromoting_host_context.h"
+#include "remoting/host/fake_desktop_environment.h"
+#include "remoting/host/video_scheduler.h"
+#include "remoting/protocol/chromium_port_allocator.h"
+#include "remoting/protocol/jingle_session_manager.h"
+#include "remoting/protocol/libjingle_transport_factory.h"
+#include "remoting/protocol/me2me_host_authenticator_factory.h"
+#include "remoting/protocol/negotiating_client_authenticator.h"
+#include "remoting/protocol/session_config.h"
+#include "remoting/signaling/fake_signal_strategy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+using protocol::ChannelConfig;
+
+const char kHostJid[] = "host_jid@example.com/host";
+const char kHostOwner[] = "jane.doe@example.com";
+const char kClientJid[] = "jane.doe@example.com/client";
+
+class ProtocolPerfTest : public testing::Test,
+ public ClientUserInterface,
+ public VideoRenderer,
+ public HostStatusObserver {
+ public:
+ ProtocolPerfTest()
+ : capture_thread_("capture"),
+ encode_thread_("encode") {
+ VideoScheduler::EnableTimestampsForTests();
+ capture_thread_.Start();
+ encode_thread_.Start();
+ }
+ virtual ~ProtocolPerfTest() {
+ message_loop_.RunUntilIdle();
+ }
+
+ // ClientUserInterface interface.
+ virtual void OnConnectionState(protocol::ConnectionToHost::State state,
+ protocol::ErrorCode error) OVERRIDE {
+ if (state == protocol::ConnectionToHost::CONNECTED) {
+ client_connected_ = true;
+ if (host_connected_)
+ connecting_loop_->Quit();
+ }
+ }
+ virtual void OnConnectionReady(bool ready) OVERRIDE {}
+ virtual void OnRouteChanged(const std::string& channel_name,
+ const protocol::TransportRoute& route) OVERRIDE {
+ }
+ virtual void SetCapabilities(const std::string& capabilities) OVERRIDE {}
+ virtual void SetPairingResponse(
+ const protocol::PairingResponse& pairing_response) OVERRIDE {}
+ virtual void DeliverHostMessage(
+ const protocol::ExtensionMessage& message) OVERRIDE {}
+ virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE {
+ return NULL;
+ }
+ virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE {
+ return NULL;
+ }
+
+ // VideoRenderer interface.
+ virtual void Initialize(const protocol::SessionConfig& config) OVERRIDE {}
+ virtual ChromotingStats* GetStats() OVERRIDE { return NULL; }
+ virtual void ProcessVideoPacket(scoped_ptr<VideoPacket> video_packet,
+ const base::Closure& done) OVERRIDE {
+ if (video_packet->data().empty()) {
+ // Ignore keep-alive packets
+ done.Run();
+ return;
+ }
+
+ last_video_packet_ = video_packet.Pass();
+
+ if (!on_frame_task_.is_null())
+ on_frame_task_.Run();
+
+ done.Run();
+ }
+
+ // HostStatusObserver interface.
+ virtual void OnClientConnected(const std::string& jid) OVERRIDE {
+ host_connected_ = true;
+ if (client_connected_)
+ connecting_loop_->Quit();
+ }
+
+ protected:
+ void WaitConnected() {
+ client_connected_ = false;
+ host_connected_ = false;
+
+ connecting_loop_.reset(new base::RunLoop());
+ connecting_loop_->Run();
+
+ ASSERT_TRUE(client_connected_ && host_connected_);
+ }
+
+ void ReceiveFrame(base::TimeDelta* latency) {
+ waiting_frames_loop_.reset(new base::RunLoop());
+ on_frame_task_ = waiting_frames_loop_->QuitClosure();
+ waiting_frames_loop_->Run();
+
+ if (latency) {
+ base::TimeTicks timestamp =
+ base::TimeTicks::FromInternalValue(last_video_packet_->timestamp());
+ *latency = base::TimeTicks::Now() - timestamp;
+ }
+ }
+
+ void ReceiveFrames(int frames, base::TimeDelta* max_latency) {
+ if (max_latency)
+ *max_latency = base::TimeDelta();
+
+ for (int i = 0; i < frames; ++i) {
+ base::TimeDelta latency;
+
+ ReceiveFrame(&latency);
+
+ if (max_latency && latency > *max_latency) {
+ *max_latency = latency;
+ }
+ }
+ }
+
+ void StartHostAndClient(protocol::ChannelConfig::Codec video_codec) {
+ host_signaling_.reset(new FakeSignalStrategy(kHostJid));
+ client_signaling_.reset(new FakeSignalStrategy(kClientJid));
+ FakeSignalStrategy::Connect(host_signaling_.get(), client_signaling_.get());
+
+ protocol::NetworkSettings network_settings(
+ protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING);
+
+ scoped_ptr<protocol::CandidateSessionConfig> protocol_config =
+ protocol::CandidateSessionConfig::CreateDefault();
+ protocol_config->DisableAudioChannel();
+ protocol_config->mutable_video_configs()->clear();
+ protocol_config->mutable_video_configs()->push_back(protocol::ChannelConfig(
+ protocol::ChannelConfig::TRANSPORT_STREAM, 2, video_codec));
+
+ // TODO(sergeyu): Replace with a fake port allocator.
+ scoped_ptr<cricket::HttpPortAllocatorBase> host_port_allocator =
+ protocol::ChromiumPortAllocator::Create(NULL, network_settings)
+ .PassAs<cricket::HttpPortAllocatorBase>();
+
+ scoped_ptr<protocol::TransportFactory> host_transport_factory(
+ new protocol::LibjingleTransportFactory(
+ host_signaling_.get(),
+ host_port_allocator.Pass(),
+ network_settings));
+
+ scoped_ptr<protocol::SessionManager> session_manager(
+ new protocol::JingleSessionManager(host_transport_factory.Pass()));
+
+ // Encoder runs on a separate thread, main thread is used for everything
+ // else.
+ host_.reset(new ChromotingHost(host_signaling_.get(),
+ &desktop_environment_factory_,
+ session_manager.Pass(),
+ message_loop_.message_loop_proxy(),
+ message_loop_.message_loop_proxy(),
+ capture_thread_.message_loop_proxy(),
+ encode_thread_.message_loop_proxy(),
+ message_loop_.message_loop_proxy(),
+ message_loop_.message_loop_proxy()));
+
+ base::FilePath certs_dir(net::GetTestCertsDirectory());
+
+ std::string host_cert;
+ ASSERT_TRUE(base::ReadFileToString(
+ certs_dir.AppendASCII("unittest.selfsigned.der"), &host_cert));
+
+ base::FilePath key_path = certs_dir.AppendASCII("unittest.key.bin");
+ std::string key_string;
+ ASSERT_TRUE(base::ReadFileToString(key_path, &key_string));
+ std::string key_base64;
+ base::Base64Encode(key_string, &key_base64);
+ scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(key_base64);
+ ASSERT_TRUE(key_pair.get());
+
+
+ protocol::SharedSecretHash host_secret;
+ host_secret.hash_function = protocol::AuthenticationMethod::NONE;
+ host_secret.value = "123456";
+ scoped_ptr<protocol::AuthenticatorFactory> auth_factory =
+ protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
+ true, kHostOwner, host_cert, key_pair, host_secret, NULL);
+ host_->SetAuthenticatorFactory(auth_factory.Pass());
+
+ host_->AddStatusObserver(this);
+ host_->set_protocol_config(protocol_config->Clone());
+ host_->Start(kHostOwner);
+
+ // Initialize client.
+ client_context_.reset(
+ new ClientContext(base::ThreadTaskRunnerHandle::Get()));
+
+ // TODO(sergeyu): Replace with a fake port allocator
+ scoped_ptr<cricket::HttpPortAllocatorBase> client_port_allocator =
+ protocol::ChromiumPortAllocator::Create(NULL, network_settings)
+ .PassAs<cricket::HttpPortAllocatorBase>();
+
+ scoped_ptr<protocol::TransportFactory> client_transport_factory(
+ new protocol::LibjingleTransportFactory(client_signaling_.get(),
+ client_port_allocator.Pass(),
+ network_settings));
+
+ std::vector<protocol::AuthenticationMethod> auth_methods;
+ auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
+ protocol::AuthenticationMethod::NONE));
+ scoped_ptr<protocol::Authenticator> client_authenticator(
+ new protocol::NegotiatingClientAuthenticator(
+ std::string(), // client_pairing_id
+ std::string(), // client_pairing_secret
+ std::string(), // authentication_tag
+ base::Bind(&ProtocolPerfTest::FetchPin, base::Unretained(this)),
+ scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(),
+ auth_methods));
+ client_.reset(new ChromotingClient(
+ client_context_.get(), this, this, scoped_ptr<AudioPlayer>()));
+ client_->SetProtocolConfigForTests(protocol_config->Clone());
+ client_->Start(
+ client_signaling_.get(), client_authenticator.Pass(),
+ client_transport_factory.Pass(), kHostJid, std::string());
+ }
+
+ void FetchPin(
+ bool pairing_supported,
+ const protocol::SecretFetchedCallback& secret_fetched_callback) {
+ secret_fetched_callback.Run("123456");
+ }
+
+ base::MessageLoopForIO message_loop_;
+
+ FakeDesktopEnvironmentFactory desktop_environment_factory_;
+ base::Thread capture_thread_;
+ base::Thread encode_thread_;
+
+ scoped_ptr<FakeSignalStrategy> host_signaling_;
+ scoped_ptr<FakeSignalStrategy> client_signaling_;
+
+ scoped_ptr<ChromotingHost> host_;
+ scoped_ptr<ClientContext> client_context_;
+ scoped_ptr<ChromotingClient> client_;
+
+ scoped_ptr<base::RunLoop> connecting_loop_;
+ scoped_ptr<base::RunLoop> waiting_frames_loop_;
+
+ bool client_connected_;
+ bool host_connected_;
+
+ base::Closure on_frame_task_;
+
+ scoped_ptr<VideoPacket> last_video_packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProtocolPerfTest);
+};
+
+TEST_F(ProtocolPerfTest, StreamFrameRate) {
+ StartHostAndClient(protocol::ChannelConfig::CODEC_VP8);
+ ASSERT_NO_FATAL_FAILURE(WaitConnected());
+
+ base::TimeDelta latency;
+
+ ReceiveFrame(&latency);
+ LOG(INFO) << "First frame latency: " << latency.InMillisecondsF() << "ms";
+ ReceiveFrames(20, NULL);
+
+ base::TimeTicks started = base::TimeTicks::Now();
+ ReceiveFrames(40, &latency);
+ base::TimeDelta elapsed = base::TimeTicks::Now() - started;
+ LOG(INFO) << "Frame rate: " << (40.0 / elapsed.InSecondsF());
+ LOG(INFO) << "Maximum latency: " << latency.InMillisecondsF() << "ms";
+}
+
+// Frame generator that rewrites the whole screen every 60th frame. Should only
+// be used with the VERBATIM codec as the allocated frame may contain arbitrary
+// data.
+class IntermittentChangeFrameGenerator
+ : public base::RefCountedThreadSafe<IntermittentChangeFrameGenerator> {
+ public:
+ IntermittentChangeFrameGenerator()
+ : frame_index_(0) {}
+
+ scoped_ptr<webrtc::DesktopFrame> GenerateFrame(
+ webrtc::ScreenCapturer::Callback* callback) {
+ const int kWidth = 800;
+ const int kHeight = 600;
+
+ bool fresh_frame = false;
+ if (frame_index_ % 60 == 0 || !current_frame_) {
+ current_frame_.reset(webrtc::SharedDesktopFrame::Wrap(
+ new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight))));
+ fresh_frame = true;
+ }
+ ++frame_index_;
+
+ scoped_ptr<webrtc::DesktopFrame> result(current_frame_->Share());
+ result->mutable_updated_region()->Clear();
+ if (fresh_frame) {
+ result->mutable_updated_region()->AddRect(
+ webrtc::DesktopRect::MakeXYWH(0, 0, kWidth, kHeight));
+ }
+ return result.Pass();
+ }
+
+ private:
+ ~IntermittentChangeFrameGenerator() {}
+ friend class base::RefCountedThreadSafe<IntermittentChangeFrameGenerator>;
+
+ int frame_index_;
+ scoped_ptr<webrtc::SharedDesktopFrame> current_frame_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntermittentChangeFrameGenerator);
+};
+
+TEST_F(ProtocolPerfTest, IntermittentChanges) {
+ desktop_environment_factory_.set_frame_generator(
+ base::Bind(&IntermittentChangeFrameGenerator::GenerateFrame,
+ new IntermittentChangeFrameGenerator()));
+
+ StartHostAndClient(protocol::ChannelConfig::CODEC_VERBATIM);
+ ASSERT_NO_FATAL_FAILURE(WaitConnected());
+
+ ReceiveFrame(NULL);
+
+ for (int i = 0; i < 5; ++i) {
+ base::TimeDelta latency;
+ ReceiveFrame(&latency);
+ LOG(INFO) << "Latency: " << latency.InMillisecondsF()
+ << "ms Encode: " << last_video_packet_->encode_time_ms()
+ << "ms Capture: " << last_video_packet_->capture_time_ms()
+ << "ms";
+ }
+}
+
+} // namespace remoting
« no previous file with comments | « remoting/test/DEPS ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698