| Index: remoting/test/protocol_perftest.cc
|
| diff --git a/remoting/test/protocol_perftest.cc b/remoting/test/protocol_perftest.cc
|
| index bbafe9455247c34cb7744735ed2c99400706eaad..3695b75b2bca63106a7b05ca3a21cd5bf766c7f5 100644
|
| --- a/remoting/test/protocol_perftest.cc
|
| +++ b/remoting/test/protocol_perftest.cc
|
| @@ -12,6 +12,7 @@
|
| #include "base/run_loop.h"
|
| #include "base/single_thread_task_runner.h"
|
| #include "base/synchronization/waitable_event.h"
|
| +#include "base/task_runner_util.h"
|
| #include "base/thread_task_runner_handle.h"
|
| #include "jingle/glue/thread_wrapper.h"
|
| #include "net/base/test_data_directory.h"
|
| @@ -21,9 +22,12 @@
|
| #include "remoting/client/chromoting_client.h"
|
| #include "remoting/client/client_context.h"
|
| #include "remoting/client/client_user_interface.h"
|
| +#include "remoting/codec/video_decoder_verbatim.h"
|
| +#include "remoting/codec/video_decoder_vpx.h"
|
| #include "remoting/host/chromoting_host.h"
|
| #include "remoting/host/chromoting_host_context.h"
|
| #include "remoting/host/fake_desktop_environment.h"
|
| +#include "remoting/protocol/frame_consumer.h"
|
| #include "remoting/protocol/jingle_session_manager.h"
|
| #include "remoting/protocol/me2me_host_authenticator_factory.h"
|
| #include "remoting/protocol/negotiating_client_authenticator.h"
|
| @@ -32,6 +36,7 @@
|
| #include "remoting/protocol/video_frame_pump.h"
|
| #include "remoting/protocol/video_renderer.h"
|
| #include "remoting/signaling/fake_signal_strategy.h"
|
| +#include "remoting/test/cyclic_frame_generator.h"
|
| #include "remoting/test/fake_network_dispatcher.h"
|
| #include "remoting/test/fake_port_allocator.h"
|
| #include "remoting/test/fake_socket_factory.h"
|
| @@ -41,6 +46,8 @@ namespace remoting {
|
|
|
| using protocol::ChannelConfig;
|
|
|
| +namespace {
|
| +
|
| const char kHostJid[] = "host_jid@example.com/host";
|
| const char kHostOwner[] = "jane.doe@example.com";
|
| const char kClientJid[] = "jane.doe@example.com/client";
|
| @@ -73,23 +80,37 @@ class FakeCursorShapeStub : public protocol::CursorShapeStub {
|
| void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override{};
|
| };
|
|
|
| +scoped_ptr<webrtc::DesktopFrame> DoDecodeFrame(
|
| + VideoDecoder* decoder,
|
| + VideoPacket* packet,
|
| + scoped_ptr<webrtc::DesktopFrame> frame) {
|
| + if (!decoder->DecodePacket(*packet, frame.get()))
|
| + frame.reset();
|
| + return frame;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| class ProtocolPerfTest
|
| : public testing::Test,
|
| public testing::WithParamInterface<NetworkPerformanceParams>,
|
| public ClientUserInterface,
|
| public protocol::VideoRenderer,
|
| public protocol::VideoStub,
|
| + public protocol::FrameConsumer,
|
| public HostStatusObserver {
|
| public:
|
| ProtocolPerfTest()
|
| : host_thread_("host"),
|
| capture_thread_("capture"),
|
| - encode_thread_("encode") {
|
| + encode_thread_("encode"),
|
| + decode_thread_("decode") {
|
| protocol::VideoFramePump::EnableTimestampsForTests();
|
| host_thread_.StartWithOptions(
|
| base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
|
| capture_thread_.Start();
|
| encode_thread_.Start();
|
| + decode_thread_.Start();
|
| }
|
|
|
| virtual ~ProtocolPerfTest() {
|
| @@ -123,26 +144,57 @@ class ProtocolPerfTest
|
| // VideoRenderer interface.
|
| void OnSessionConfig(const protocol::SessionConfig& config) override {}
|
| protocol::VideoStub* GetVideoStub() override { return this; }
|
| - protocol::FrameConsumer* GetFrameConsumer() override {
|
| - NOTREACHED();
|
| - return nullptr;
|
| - }
|
| + protocol::FrameConsumer* GetFrameConsumer() override { return this; }
|
|
|
| // protocol::VideoStub interface.
|
| - void ProcessVideoPacket(scoped_ptr<VideoPacket> video_packet,
|
| + void ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
|
| const base::Closure& done) override {
|
| - if (video_packet->data().empty()) {
|
| + if (packet->data().empty()) {
|
| // Ignore keep-alive packets
|
| done.Run();
|
| return;
|
| }
|
|
|
| - last_video_packet_ = std::move(video_packet);
|
| + if (packet->format().has_screen_width() &&
|
| + packet->format().has_screen_height()) {
|
| + frame_size_.set(packet->format().screen_width(),
|
| + packet->format().screen_height());
|
| + }
|
| +
|
| + scoped_ptr<webrtc::DesktopFrame> frame(
|
| + new webrtc::BasicDesktopFrame(frame_size_));
|
| + base::PostTaskAndReplyWithResult(
|
| + decode_thread_.task_runner().get(), FROM_HERE,
|
| + base::Bind(&DoDecodeFrame, video_decoder_.get(), packet.get(),
|
| + base::Passed(&frame)),
|
| + base::Bind(&ProtocolPerfTest::OnFrameDecoded, base::Unretained(this),
|
| + base::Passed(&packet), done));
|
| + }
|
| +
|
| + void OnFrameDecoded(scoped_ptr<VideoPacket> packet,
|
| + const base::Closure& done,
|
| + scoped_ptr<webrtc::DesktopFrame> frame) {
|
| + last_video_packet_ = std::move(packet);
|
| + DrawFrame(std::move(frame), done);
|
| + }
|
| +
|
| + // protocol::FrameConsumer interface.
|
| + scoped_ptr<webrtc::DesktopFrame> AllocateFrame(
|
| + const webrtc::DesktopSize& size) override {
|
| + return make_scoped_ptr(new webrtc::BasicDesktopFrame(size));
|
| + }
|
|
|
| + void DrawFrame(scoped_ptr<webrtc::DesktopFrame> frame,
|
| + const base::Closure& done) override {
|
| + last_video_frame_ = std::move(frame);
|
| if (!on_frame_task_.is_null())
|
| on_frame_task_.Run();
|
| + if (!done.is_null())
|
| + done.Run();
|
| + }
|
|
|
| - done.Run();
|
| + protocol::FrameConsumer::PixelFormat GetPixelFormat() override {
|
| + return FORMAT_BGRA;
|
| }
|
|
|
| // HostStatusObserver interface.
|
| @@ -170,11 +222,22 @@ class ProtocolPerfTest
|
| connecting_loop_->Quit();
|
| }
|
|
|
| - void ReceiveFrame(base::TimeDelta* latency) {
|
| + scoped_ptr<webrtc::DesktopFrame> ReceiveFrame() {
|
| + last_video_frame_.reset();
|
| +
|
| waiting_frames_loop_.reset(new base::RunLoop());
|
| on_frame_task_ = waiting_frames_loop_->QuitClosure();
|
| waiting_frames_loop_->Run();
|
|
|
| + EXPECT_TRUE(last_video_frame_);
|
| + return std::move(last_video_frame_);
|
| + }
|
| +
|
| + void ReceiveFrameAndGetLatency(base::TimeDelta* latency) {
|
| + last_video_packet_.reset();
|
| +
|
| + ReceiveFrame();
|
| +
|
| if (latency) {
|
| base::TimeTicks timestamp =
|
| base::TimeTicks::FromInternalValue(last_video_packet_->timestamp());
|
| @@ -182,14 +245,15 @@ class ProtocolPerfTest
|
| }
|
| }
|
|
|
| - void ReceiveFrames(int frames, base::TimeDelta* max_latency) {
|
| + void ReceiveMultipleFramesAndGetMaxLatency(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);
|
| + ReceiveFrameAndGetLatency(&latency);
|
|
|
| if (max_latency && latency > *max_latency) {
|
| *max_latency = latency;
|
| @@ -201,7 +265,8 @@ class ProtocolPerfTest
|
| // should call WaitConnected() to wait until connection is established. The
|
| // host is started on |host_thread_| while the client works on the main
|
| // thread.
|
| - void StartHostAndClient(protocol::ChannelConfig::Codec video_codec) {
|
| + void StartHostAndClient(bool use_webrtc,
|
| + protocol::ChannelConfig::Codec video_codec) {
|
| fake_network_dispatcher_ = new FakeNetworkDispatcher();
|
|
|
| client_signaling_.reset(new FakeSignalStrategy(kClientJid));
|
| @@ -214,6 +279,19 @@ class ProtocolPerfTest
|
| protocol_config_->mutable_video_configs()->push_back(
|
| protocol::ChannelConfig(
|
| protocol::ChannelConfig::TRANSPORT_STREAM, 2, video_codec));
|
| + protocol_config_->set_webrtc_supported(use_webrtc);
|
| + protocol_config_->set_ice_supported(!use_webrtc);
|
| +
|
| + switch (video_codec) {
|
| + case ChannelConfig::CODEC_VERBATIM:
|
| + video_decoder_.reset(new VideoDecoderVerbatim());
|
| + break;
|
| + case ChannelConfig::CODEC_VP8:
|
| + video_decoder_ = VideoDecoderVpx::CreateForVP8();
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + }
|
|
|
| host_thread_.task_runner()->PostTask(
|
| FROM_HERE,
|
| @@ -307,7 +385,7 @@ class ProtocolPerfTest
|
| scoped_refptr<protocol::TransportContext> transport_context(
|
| new protocol::TransportContext(
|
| host_signaling_.get(), std::move(port_allocator_factory),
|
| - network_settings, protocol::TransportRole::SERVER));
|
| + network_settings, protocol::TransportRole::CLIENT));
|
|
|
| std::vector<protocol::AuthenticationMethod> auth_methods;
|
| auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
|
| @@ -332,6 +410,61 @@ class ProtocolPerfTest
|
| secret_fetched_callback.Run("123456");
|
| }
|
|
|
| + void MeasureTotalLatency(bool webrtc) {
|
| + scoped_refptr<test::CyclicFrameGenerator> frame_generator =
|
| + test::CyclicFrameGenerator::Create();
|
| + frame_generator->set_draw_barcode(true);
|
| +
|
| + desktop_environment_factory_.set_frame_generator(base::Bind(
|
| + &test::CyclicFrameGenerator::GenerateFrame, frame_generator));
|
| +
|
| + StartHostAndClient(webrtc, protocol::ChannelConfig::CODEC_VP8);
|
| + ASSERT_NO_FATAL_FAILURE(WaitConnected());
|
| +
|
| + base::TimeDelta total_latency_big_frames;
|
| + int big_frame_count = 0;
|
| + base::TimeDelta total_latency_small_frames;
|
| + int small_frame_count = 0;
|
| +
|
| + int last_frame_id = -1;
|
| + for (int i = 0; i < 30; ++i) {
|
| + scoped_ptr<webrtc::DesktopFrame> frame = ReceiveFrame();
|
| + test::CyclicFrameGenerator::FrameInfo frame_info =
|
| + frame_generator->IdentifyFrame(frame.get());
|
| + base::TimeDelta latency = base::TimeTicks::Now() - frame_info.timestamp;
|
| +
|
| + if (frame_info.frame_id > last_frame_id) {
|
| + last_frame_id = frame_info.frame_id;
|
| +
|
| + switch (frame_info.type) {
|
| + case test::CyclicFrameGenerator::FrameType::EMPTY:
|
| + NOTREACHED();
|
| + break;
|
| + case test::CyclicFrameGenerator::FrameType::FULL:
|
| + total_latency_big_frames += latency;
|
| + ++big_frame_count;
|
| + break;
|
| + case test::CyclicFrameGenerator::FrameType::CURSOR:
|
| + total_latency_small_frames += latency;
|
| + ++small_frame_count;
|
| + break;
|
| + }
|
| + } else {
|
| + LOG(ERROR) << "Unexpected duplicate frame " << frame_info.frame_id;
|
| + }
|
| + }
|
| +
|
| + CHECK(big_frame_count);
|
| + VLOG(0) << "Average latency for big frames: "
|
| + << (total_latency_big_frames / big_frame_count).InMillisecondsF();
|
| +
|
| + if (small_frame_count) {
|
| + VLOG(0)
|
| + << "Average latency for small frames: "
|
| + << (total_latency_small_frames / small_frame_count).InMillisecondsF();
|
| + }
|
| + }
|
| +
|
| base::MessageLoopForIO message_loop_;
|
|
|
| scoped_refptr<FakeNetworkDispatcher> fake_network_dispatcher_;
|
| @@ -339,6 +472,7 @@ class ProtocolPerfTest
|
| base::Thread host_thread_;
|
| base::Thread capture_thread_;
|
| base::Thread encode_thread_;
|
| + base::Thread decode_thread_;
|
| FakeDesktopEnvironmentFactory desktop_environment_factory_;
|
|
|
| FakeCursorShapeStub cursor_shape_stub_;
|
| @@ -351,6 +485,8 @@ class ProtocolPerfTest
|
| scoped_ptr<ChromotingHost> host_;
|
| scoped_ptr<ClientContext> client_context_;
|
| scoped_ptr<ChromotingClient> client_;
|
| + webrtc::DesktopSize frame_size_;
|
| + scoped_ptr<VideoDecoder> video_decoder_;
|
|
|
| scoped_ptr<base::RunLoop> connecting_loop_;
|
| scoped_ptr<base::RunLoop> waiting_frames_loop_;
|
| @@ -361,6 +497,7 @@ class ProtocolPerfTest
|
| base::Closure on_frame_task_;
|
|
|
| scoped_ptr<VideoPacket> last_video_packet_;
|
| + scoped_ptr<webrtc::DesktopFrame> last_video_frame_;
|
|
|
| private:
|
| DISALLOW_COPY_AND_ASSIGN(ProtocolPerfTest);
|
| @@ -400,17 +537,17 @@ INSTANTIATE_TEST_CASE_P(
|
| NetworkPerformanceParams(100000, 200000, 130, 5, 0.01)));
|
|
|
| TEST_P(ProtocolPerfTest, StreamFrameRate) {
|
| - StartHostAndClient(protocol::ChannelConfig::CODEC_VP8);
|
| + StartHostAndClient(false, protocol::ChannelConfig::CODEC_VP8);
|
| ASSERT_NO_FATAL_FAILURE(WaitConnected());
|
|
|
| base::TimeDelta latency;
|
|
|
| - ReceiveFrame(&latency);
|
| + ReceiveFrameAndGetLatency(&latency);
|
| LOG(INFO) << "First frame latency: " << latency.InMillisecondsF() << "ms";
|
| - ReceiveFrames(20, nullptr);
|
| + ReceiveMultipleFramesAndGetMaxLatency(20, nullptr);
|
|
|
| base::TimeTicks started = base::TimeTicks::Now();
|
| - ReceiveFrames(40, &latency);
|
| + ReceiveMultipleFramesAndGetMaxLatency(40, &latency);
|
| base::TimeDelta elapsed = base::TimeTicks::Now() - started;
|
| LOG(INFO) << "Frame rate: " << (40.0 / elapsed.InSecondsF());
|
| LOG(INFO) << "Maximum latency: " << latency.InMillisecondsF() << "ms";
|
| @@ -464,10 +601,10 @@ TEST_P(ProtocolPerfTest, IntermittentChanges) {
|
| base::Bind(&IntermittentChangeFrameGenerator::GenerateFrame,
|
| new IntermittentChangeFrameGenerator()));
|
|
|
| - StartHostAndClient(protocol::ChannelConfig::CODEC_VERBATIM);
|
| + StartHostAndClient(false, protocol::ChannelConfig::CODEC_VERBATIM);
|
| ASSERT_NO_FATAL_FAILURE(WaitConnected());
|
|
|
| - ReceiveFrame(nullptr);
|
| + ReceiveFrameAndGetLatency(nullptr);
|
|
|
| base::TimeDelta expected = GetParam().latency_average;
|
| if (GetParam().bandwidth > 0) {
|
| @@ -481,7 +618,7 @@ TEST_P(ProtocolPerfTest, IntermittentChanges) {
|
| const int kFrames = 5;
|
| for (int i = 0; i < kFrames; ++i) {
|
| base::TimeDelta latency;
|
| - ReceiveFrame(&latency);
|
| + ReceiveFrameAndGetLatency(&latency);
|
| LOG(INFO) << "Latency: " << latency.InMillisecondsF()
|
| << "ms Encode: " << last_video_packet_->encode_time_ms()
|
| << "ms Capture: " << last_video_packet_->capture_time_ms()
|
| @@ -492,4 +629,12 @@ TEST_P(ProtocolPerfTest, IntermittentChanges) {
|
| LOG(INFO) << "Average: " << (sum / kFrames).InMillisecondsF();
|
| }
|
|
|
| +TEST_P(ProtocolPerfTest, TotalLatencyIce) {
|
| + MeasureTotalLatency(false);
|
| +}
|
| +
|
| +TEST_P(ProtocolPerfTest, TotalLatencyWebrtc) {
|
| + MeasureTotalLatency(true);
|
| +}
|
| +
|
| } // namespace remoting
|
|
|