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 |