| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/client/chromoting_client.h" | 5 #include "remoting/client/chromoting_client.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "remoting/client/audio_decode_scheduler.h" | 8 #include "remoting/client/audio_decode_scheduler.h" |
| 9 #include "remoting/client/audio_player.h" | 9 #include "remoting/client/audio_player.h" |
| 10 #include "remoting/client/client_context.h" | 10 #include "remoting/client/client_context.h" |
| 11 #include "remoting/client/client_user_interface.h" | 11 #include "remoting/client/client_user_interface.h" |
| 12 #include "remoting/client/rectangle_update_decoder.h" | 12 #include "remoting/client/rectangle_update_decoder.h" |
| 13 #include "remoting/proto/audio.pb.h" | 13 #include "remoting/proto/audio.pb.h" |
| 14 #include "remoting/proto/video.pb.h" | 14 #include "remoting/proto/video.pb.h" |
| 15 #include "remoting/protocol/authentication_method.h" | 15 #include "remoting/protocol/authentication_method.h" |
| 16 #include "remoting/protocol/connection_to_host.h" | 16 #include "remoting/protocol/connection_to_host.h" |
| 17 #include "remoting/protocol/negotiating_authenticator.h" | 17 #include "remoting/protocol/negotiating_authenticator.h" |
| 18 #include "remoting/protocol/session_config.h" | 18 #include "remoting/protocol/session_config.h" |
| 19 #include "remoting/protocol/transport.h" | 19 #include "remoting/protocol/transport.h" |
| 20 | 20 |
| 21 namespace remoting { | 21 namespace remoting { |
| 22 | 22 |
| 23 using protocol::AuthenticationMethod; | 23 using protocol::AuthenticationMethod; |
| 24 | 24 |
| 25 ChromotingClient::QueuedVideoPacket::QueuedVideoPacket( | |
| 26 scoped_ptr<VideoPacket> packet, const base::Closure& done) | |
| 27 : packet(packet.release()), done(done) { | |
| 28 } | |
| 29 | |
| 30 ChromotingClient::QueuedVideoPacket::~QueuedVideoPacket() { | |
| 31 } | |
| 32 | |
| 33 ChromotingClient::ChromotingClient( | 25 ChromotingClient::ChromotingClient( |
| 34 const ClientConfig& config, | 26 const ClientConfig& config, |
| 35 ClientContext* client_context, | 27 ClientContext* client_context, |
| 36 protocol::ConnectionToHost* connection, | 28 protocol::ConnectionToHost* connection, |
| 37 ClientUserInterface* user_interface, | 29 ClientUserInterface* user_interface, |
| 38 RectangleUpdateDecoder* rectangle_decoder, | 30 RectangleUpdateDecoder* rectangle_decoder, |
| 39 scoped_ptr<AudioPlayer> audio_player) | 31 scoped_ptr<AudioPlayer> audio_player) |
| 40 : config_(config), | 32 : config_(config), |
| 41 task_runner_(client_context->main_task_runner()), | 33 task_runner_(client_context->main_task_runner()), |
| 42 connection_(connection), | 34 connection_(connection), |
| 43 user_interface_(user_interface), | 35 user_interface_(user_interface), |
| 44 rectangle_decoder_(rectangle_decoder), | 36 rectangle_decoder_(rectangle_decoder), |
| 45 packet_being_processed_(false), | |
| 46 last_sequence_number_(0), | |
| 47 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | 37 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| 48 audio_decode_scheduler_.reset(new AudioDecodeScheduler( | 38 audio_decode_scheduler_.reset(new AudioDecodeScheduler( |
| 49 client_context->main_task_runner(), | 39 client_context->main_task_runner(), |
| 50 client_context->audio_decode_task_runner(), | 40 client_context->audio_decode_task_runner(), |
| 51 audio_player.Pass())); | 41 audio_player.Pass())); |
| 52 } | 42 } |
| 53 | 43 |
| 54 ChromotingClient::~ChromotingClient() { | 44 ChromotingClient::~ChromotingClient() { |
| 55 } | 45 } |
| 56 | 46 |
| 57 void ChromotingClient::Start( | 47 void ChromotingClient::Start( |
| 58 scoped_refptr<XmppProxy> xmpp_proxy, | 48 scoped_refptr<XmppProxy> xmpp_proxy, |
| 59 scoped_ptr<protocol::TransportFactory> transport_factory) { | 49 scoped_ptr<protocol::TransportFactory> transport_factory) { |
| 60 DCHECK(task_runner_->BelongsToCurrentThread()); | 50 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 61 | 51 |
| 62 scoped_ptr<protocol::Authenticator> authenticator( | 52 scoped_ptr<protocol::Authenticator> authenticator( |
| 63 protocol::NegotiatingAuthenticator::CreateForClient( | 53 protocol::NegotiatingAuthenticator::CreateForClient( |
| 64 config_.authentication_tag, | 54 config_.authentication_tag, |
| 65 config_.shared_secret, config_.authentication_methods)); | 55 config_.shared_secret, config_.authentication_methods)); |
| 66 | 56 |
| 67 // Create a WeakPtr to ourself for to use for all posted tasks. | 57 // Create a WeakPtr to ourself for to use for all posted tasks. |
| 68 weak_ptr_ = weak_factory_.GetWeakPtr(); | 58 weak_ptr_ = weak_factory_.GetWeakPtr(); |
| 69 | 59 |
| 70 connection_->Connect(xmpp_proxy, config_.local_jid, config_.host_jid, | 60 connection_->Connect(xmpp_proxy, config_.local_jid, config_.host_jid, |
| 71 config_.host_public_key, transport_factory.Pass(), | 61 config_.host_public_key, transport_factory.Pass(), |
| 72 authenticator.Pass(), this, this, this, this, | 62 authenticator.Pass(), this, this, this, |
| 63 rectangle_decoder_, |
| 73 audio_decode_scheduler_.get()); | 64 audio_decode_scheduler_.get()); |
| 74 } | 65 } |
| 75 | 66 |
| 76 void ChromotingClient::Stop(const base::Closure& shutdown_task) { | 67 void ChromotingClient::Stop(const base::Closure& shutdown_task) { |
| 77 DCHECK(task_runner_->BelongsToCurrentThread()); | 68 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 78 | 69 |
| 79 // Drop all pending packets. | 70 // Drop all pending packets. |
| 80 while(!received_packets_.empty()) { | 71 rectangle_decoder_->DropAllPackets(); |
| 81 delete received_packets_.front().packet; | |
| 82 received_packets_.front().done.Run(); | |
| 83 received_packets_.pop_front(); | |
| 84 } | |
| 85 | 72 |
| 86 connection_->Disconnect(base::Bind(&ChromotingClient::OnDisconnected, | 73 connection_->Disconnect(base::Bind(&ChromotingClient::OnDisconnected, |
| 87 weak_ptr_, shutdown_task)); | 74 weak_ptr_, shutdown_task)); |
| 88 } | 75 } |
| 89 | 76 |
| 90 void ChromotingClient::OnDisconnected(const base::Closure& shutdown_task) { | 77 void ChromotingClient::OnDisconnected(const base::Closure& shutdown_task) { |
| 91 shutdown_task.Run(); | 78 shutdown_task.Run(); |
| 92 } | 79 } |
| 93 | 80 |
| 94 ChromotingStats* ChromotingClient::GetStats() { | 81 ChromotingStats* ChromotingClient::GetStats() { |
| 95 DCHECK(task_runner_->BelongsToCurrentThread()); | 82 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 96 return &stats_; | 83 return rectangle_decoder_->GetStats(); |
| 97 } | 84 } |
| 98 | 85 |
| 99 void ChromotingClient::InjectClipboardEvent( | 86 void ChromotingClient::InjectClipboardEvent( |
| 100 const protocol::ClipboardEvent& event) { | 87 const protocol::ClipboardEvent& event) { |
| 101 DCHECK(task_runner_->BelongsToCurrentThread()); | 88 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 102 user_interface_->GetClipboardStub()->InjectClipboardEvent(event); | 89 user_interface_->GetClipboardStub()->InjectClipboardEvent(event); |
| 103 } | 90 } |
| 104 | 91 |
| 105 void ChromotingClient::SetCursorShape( | 92 void ChromotingClient::SetCursorShape( |
| 106 const protocol::CursorShapeInfo& cursor_shape) { | 93 const protocol::CursorShapeInfo& cursor_shape) { |
| 107 user_interface_->GetCursorShapeStub()->SetCursorShape(cursor_shape); | 94 user_interface_->GetCursorShapeStub()->SetCursorShape(cursor_shape); |
| 108 } | 95 } |
| 109 | 96 |
| 110 void ChromotingClient::ProcessVideoPacket(scoped_ptr<VideoPacket> packet, | |
| 111 const base::Closure& done) { | |
| 112 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 113 | |
| 114 // If the video packet is empty then drop it. Empty packets are used to | |
| 115 // maintain activity on the network. | |
| 116 if (!packet->has_data() || packet->data().size() == 0) { | |
| 117 done.Run(); | |
| 118 return; | |
| 119 } | |
| 120 | |
| 121 // Add one frame to the counter. | |
| 122 stats_.video_frame_rate()->Record(1); | |
| 123 | |
| 124 // Record other statistics received from host. | |
| 125 stats_.video_bandwidth()->Record(packet->data().size()); | |
| 126 if (packet->has_capture_time_ms()) | |
| 127 stats_.video_capture_ms()->Record(packet->capture_time_ms()); | |
| 128 if (packet->has_encode_time_ms()) | |
| 129 stats_.video_encode_ms()->Record(packet->encode_time_ms()); | |
| 130 if (packet->has_client_sequence_number() && | |
| 131 packet->client_sequence_number() > last_sequence_number_) { | |
| 132 last_sequence_number_ = packet->client_sequence_number(); | |
| 133 base::TimeDelta round_trip_latency = | |
| 134 base::Time::Now() - | |
| 135 base::Time::FromInternalValue(packet->client_sequence_number()); | |
| 136 stats_.round_trip_ms()->Record(round_trip_latency.InMilliseconds()); | |
| 137 } | |
| 138 | |
| 139 received_packets_.push_back(QueuedVideoPacket(packet.Pass(), done)); | |
| 140 if (!packet_being_processed_) | |
| 141 DispatchPacket(); | |
| 142 } | |
| 143 | |
| 144 int ChromotingClient::GetPendingVideoPackets() { | |
| 145 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 146 return received_packets_.size(); | |
| 147 } | |
| 148 | |
| 149 void ChromotingClient::DispatchPacket() { | |
| 150 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 151 CHECK(!packet_being_processed_); | |
| 152 | |
| 153 if (received_packets_.empty()) { | |
| 154 // Nothing to do! | |
| 155 return; | |
| 156 } | |
| 157 | |
| 158 scoped_ptr<VideoPacket> packet(received_packets_.front().packet); | |
| 159 received_packets_.front().packet = NULL; | |
| 160 packet_being_processed_ = true; | |
| 161 | |
| 162 // Measure the latency between the last packet being received and presented. | |
| 163 bool last_packet = (packet->flags() & VideoPacket::LAST_PACKET) != 0; | |
| 164 base::Time decode_start; | |
| 165 if (last_packet) | |
| 166 decode_start = base::Time::Now(); | |
| 167 | |
| 168 rectangle_decoder_->DecodePacket( | |
| 169 packet.Pass(), | |
| 170 base::Bind(&ChromotingClient::OnPacketDone, base::Unretained(this), | |
| 171 last_packet, decode_start)); | |
| 172 } | |
| 173 | |
| 174 void ChromotingClient::OnConnectionState( | 97 void ChromotingClient::OnConnectionState( |
| 175 protocol::ConnectionToHost::State state, | 98 protocol::ConnectionToHost::State state, |
| 176 protocol::ErrorCode error) { | 99 protocol::ErrorCode error) { |
| 177 DCHECK(task_runner_->BelongsToCurrentThread()); | 100 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 178 VLOG(1) << "ChromotingClient::OnConnectionState(" << state << ")"; | 101 VLOG(1) << "ChromotingClient::OnConnectionState(" << state << ")"; |
| 179 if (state == protocol::ConnectionToHost::CONNECTED) | 102 if (state == protocol::ConnectionToHost::CONNECTED) |
| 180 Initialize(); | 103 Initialize(); |
| 181 user_interface_->OnConnectionState(state, error); | 104 user_interface_->OnConnectionState(state, error); |
| 182 } | 105 } |
| 183 | 106 |
| 184 void ChromotingClient::OnConnectionReady(bool ready) { | 107 void ChromotingClient::OnConnectionReady(bool ready) { |
| 185 VLOG(1) << "ChromotingClient::OnConnectionReady(" << ready << ")"; | 108 VLOG(1) << "ChromotingClient::OnConnectionReady(" << ready << ")"; |
| 186 user_interface_->OnConnectionReady(ready); | 109 user_interface_->OnConnectionReady(ready); |
| 187 } | 110 } |
| 188 | 111 |
| 189 void ChromotingClient::OnPacketDone(bool last_packet, | |
| 190 base::Time decode_start) { | |
| 191 if (!task_runner_->BelongsToCurrentThread()) { | |
| 192 task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 193 &ChromotingClient::OnPacketDone, base::Unretained(this), | |
| 194 last_packet, decode_start)); | |
| 195 return; | |
| 196 } | |
| 197 | |
| 198 // Record the latency between the final packet being received and | |
| 199 // presented. | |
| 200 if (last_packet) { | |
| 201 stats_.video_decode_ms()->Record( | |
| 202 (base::Time::Now() - decode_start).InMilliseconds()); | |
| 203 } | |
| 204 | |
| 205 received_packets_.front().done.Run(); | |
| 206 received_packets_.pop_front(); | |
| 207 | |
| 208 packet_being_processed_ = false; | |
| 209 | |
| 210 // Process the next video packet. | |
| 211 DispatchPacket(); | |
| 212 } | |
| 213 | |
| 214 void ChromotingClient::Initialize() { | 112 void ChromotingClient::Initialize() { |
| 215 DCHECK(task_runner_->BelongsToCurrentThread()); | 113 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 216 | 114 |
| 217 // Initialize the decoder. | 115 // Initialize the decoder. |
| 218 rectangle_decoder_->Initialize(connection_->config()); | 116 rectangle_decoder_->Initialize(connection_->config()); |
| 219 if (connection_->config().is_audio_enabled()) | 117 if (connection_->config().is_audio_enabled()) |
| 220 audio_decode_scheduler_->Initialize(connection_->config()); | 118 audio_decode_scheduler_->Initialize(connection_->config()); |
| 221 } | 119 } |
| 222 | 120 |
| 223 } // namespace remoting | 121 } // namespace remoting |
| OLD | NEW |