1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/client/chromoting_client.h"
7 #include "base/message_loop.h"
8 #include "remoting/base/tracer.h"
9 #include "remoting/client/chromoting_view.h"
10 #include "remoting/client/client_context.h"
11 #include "remoting/client/client_logger.h"
12 #include "remoting/client/input_handler.h"
13 #include "remoting/client/rectangle_update_decoder.h"
14 #include "remoting/protocol/connection_to_host.h"
15 #include "remoting/protocol/session_config.h"
19 ChromotingClient::ChromotingClient(const ClientConfig
& config
,
20 ClientContext
* context
,
21 protocol::ConnectionToHost
* connection
,
23 RectangleUpdateDecoder
* rectangle_decoder
,
24 InputHandler
* input_handler
,
29 connection_(connection
),
31 rectangle_decoder_(rectangle_decoder
),
32 input_handler_(input_handler
),
34 client_done_(client_done
),
36 packet_being_processed_(false),
37 last_sequence_number_(0) {
40 ChromotingClient::~ChromotingClient() {
43 void ChromotingClient::Start() {
44 if (message_loop() != MessageLoop::current()) {
45 message_loop()->PostTask(
47 NewRunnableMethod(this, &ChromotingClient::Start
));
51 connection_
->Connect(config_
.username
, config_
.auth_token
, config_
.host_jid
,
52 config_
.nonce
, this, this, this);
54 if (!view_
->Initialize()) {
59 void ChromotingClient::StartSandboxed(scoped_refptr
<XmppProxy
> xmpp_proxy
,
60 const std::string
& your_jid
,
61 const std::string
& host_jid
) {
62 // TODO(ajwong): Merge this with Start(), and just change behavior based on
64 if (message_loop() != MessageLoop::current()) {
65 message_loop()->PostTask(
67 NewRunnableMethod(this, &ChromotingClient::StartSandboxed
, xmpp_proxy
,
72 connection_
->ConnectSandboxed(xmpp_proxy
, your_jid
, host_jid
, config_
.nonce
,
75 if (!view_
->Initialize()) {
80 void ChromotingClient::Stop() {
81 if (message_loop() != MessageLoop::current()) {
82 message_loop()->PostTask(
84 NewRunnableMethod(this, &ChromotingClient::Stop
));
88 connection_
->Disconnect();
93 void ChromotingClient::ClientDone() {
94 if (client_done_
!= NULL
) {
95 message_loop()->PostTask(FROM_HERE
, client_done_
);
99 ChromotingStats
* ChromotingClient::GetStats() {
103 void ChromotingClient::Repaint() {
104 if (message_loop() != MessageLoop::current()) {
105 message_loop()->PostTask(
107 NewRunnableMethod(this, &ChromotingClient::Repaint
));
114 void ChromotingClient::SetViewport(int x
, int y
, int width
, int height
) {
115 if (message_loop() != MessageLoop::current()) {
116 message_loop()->PostTask(
118 NewRunnableMethod(this, &ChromotingClient::SetViewport
,
119 x
, y
, width
, height
));
123 view_
->SetViewport(x
, y
, width
, height
);
126 void ChromotingClient::ProcessVideoPacket(const VideoPacket
* packet
,
128 if (message_loop() != MessageLoop::current()) {
129 message_loop()->PostTask(
131 NewRunnableMethod(this, &ChromotingClient::ProcessVideoPacket
,
136 // Record size of the packet for statistics.
137 stats_
.video_bandwidth()->Record(packet
->data().size());
139 // Record statistics received from host.
140 if (packet
->has_capture_time_ms())
141 stats_
.video_capture_ms()->Record(packet
->capture_time_ms());
142 if (packet
->has_encode_time_ms())
143 stats_
.video_encode_ms()->Record(packet
->encode_time_ms());
144 if (packet
->has_client_sequence_number() &&
145 packet
->client_sequence_number() > last_sequence_number_
) {
146 last_sequence_number_
= packet
->client_sequence_number();
147 base::TimeDelta round_trip_latency
=
149 base::Time::FromInternalValue(packet
->client_sequence_number());
150 stats_
.round_trip_ms()->Record(round_trip_latency
.InMilliseconds());
153 received_packets_
.push_back(QueuedVideoPacket(packet
, done
));
154 if (!packet_being_processed_
)
158 int ChromotingClient::GetPendingPackets() {
159 return received_packets_
.size();
162 void ChromotingClient::DispatchPacket() {
163 DCHECK_EQ(message_loop(), MessageLoop::current());
164 CHECK(!packet_being_processed_
);
166 if (received_packets_
.empty()) {
171 const VideoPacket
* packet
= received_packets_
.front().packet
;
172 packet_being_processed_
= true;
174 ScopedTracer
tracer("Handle video packet");
176 // Measure the latency between the last packet being received and presented.
177 bool last_packet
= (packet
->flags() & VideoPacket::LAST_PACKET
) != 0;
178 base::Time decode_start
;
180 decode_start
= base::Time::Now();
182 rectangle_decoder_
->DecodePacket(
183 packet
, NewTracedMethod(this, &ChromotingClient::OnPacketDone
,
184 last_packet
, decode_start
));
187 void ChromotingClient::OnConnectionOpened(protocol::ConnectionToHost
* conn
) {
188 logger_
->VLog(1, "ChromotingClient::OnConnectionOpened");
190 SetConnectionState(CONNECTED
);
193 void ChromotingClient::OnConnectionClosed(protocol::ConnectionToHost
* conn
) {
194 logger_
->VLog(1, "ChromotingClient::OnConnectionClosed");
195 SetConnectionState(DISCONNECTED
);
198 void ChromotingClient::OnConnectionFailed(protocol::ConnectionToHost
* conn
) {
199 logger_
->VLog(1, "ChromotingClient::OnConnectionFailed");
200 SetConnectionState(FAILED
);
203 MessageLoop
* ChromotingClient::message_loop() {
204 return context_
->jingle_thread()->message_loop();
207 void ChromotingClient::SetConnectionState(ConnectionState s
) {
208 // TODO(ajwong): We actually may want state to be a shared variable. Think
210 if (message_loop() != MessageLoop::current()) {
211 message_loop()->PostTask(
213 NewRunnableMethod(this, &ChromotingClient::SetConnectionState
, s
));
218 view_
->SetConnectionState(s
);
223 void ChromotingClient::OnPacketDone(bool last_packet
,
224 base::Time decode_start
) {
225 if (message_loop() != MessageLoop::current()) {
226 message_loop()->PostTask(
228 NewTracedMethod(this, &ChromotingClient::OnPacketDone
,
229 last_packet
, decode_start
));
233 TraceContext::tracer()->PrintString("Packet done");
235 // Record the latency between the final packet being received and
238 stats_
.video_decode_ms()->Record(
239 (base::Time::Now() - decode_start
).InMilliseconds());
242 received_packets_
.front().done
->Run();
243 delete received_packets_
.front().done
;
244 received_packets_
.pop_front();
246 packet_being_processed_
= false;
248 // Process the next video packet.
252 void ChromotingClient::Initialize() {
253 if (message_loop() != MessageLoop::current()) {
254 message_loop()->PostTask(
256 NewTracedMethod(this, &ChromotingClient::Initialize
));
260 TraceContext::tracer()->PrintString("Initializing client.");
262 const protocol::SessionConfig
* config
= connection_
->config();
264 // Resize the window.
265 int width
= config
->initial_resolution().width
;
266 int height
= config
->initial_resolution().height
;
267 logger_
->VLog(1, "Initial screen geometry: %dx%d", width
, height
);
269 // TODO(ajwong): What to do here? Does the decoder actually need to request
270 // the right frame size? This is mainly an optimization right?
271 // rectangle_decoder_->SetOutputFrameSize(width, height);
272 view_
->SetViewport(0, 0, width
, height
);
274 // Initialize the decoder.
275 rectangle_decoder_
->Initialize(config
);
277 // Schedule the input handler to process the event queue.
278 input_handler_
->Initialize();
281 ////////////////////////////////////////////////////////////////////////////
282 // ClientStub control channel interface.
283 void ChromotingClient::NotifyResolution(
284 const protocol::NotifyResolutionRequest
* msg
, Task
* done
) {
285 logger_
->Log(logging::LOG_INFO
, "NotifyResolution change");
291 void ChromotingClient::BeginSessionResponse(
292 const protocol::LocalLoginStatus
* msg
, Task
* done
) {
293 if (message_loop() != MessageLoop::current()) {
294 message_loop()->PostTask(
296 NewRunnableMethod(this, &ChromotingClient::BeginSessionResponse
,
301 logger_
->Log(logging::LOG_INFO
, "BeginSessionResponse received");
303 // Inform the connection that the client has been authenticated. This will
304 // enable the communication channels.
305 if (msg
->success()) {
306 connection_
->OnClientAuthenticated();
309 view_
->UpdateLoginStatus(msg
->success(), msg
->error_info());
314 } // namespace remoting