blob: 2a4d4379d1fb3305cc36d93c359dfc87be098229 [file] [log] [blame]
[email protected]cb3b1f9312010-06-07 19:58:231// Copyright (c) 2010 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.
4
5#include "remoting/host/session_manager.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
[email protected]7c186b16f2010-06-15 16:35:3310#include "base/scoped_ptr.h"
[email protected]cb3b1f9312010-06-07 19:58:2311#include "base/stl_util-inl.h"
[email protected]be2da4d2010-07-23 00:54:4712#include "remoting/base/capture_data.h"
[email protected]8ea7a1672010-10-04 19:48:4213#include "remoting/base/tracer.h"
[email protected]35b9c562010-11-09 02:22:4314#include "remoting/proto/control.pb.h"
15#include "remoting/protocol/client_stub.h"
[email protected]cd8d2372010-11-04 01:18:0416#include "remoting/protocol/connection_to_client.h"
[email protected]4d10ede2010-10-28 18:43:3717#include "remoting/protocol/message_decoder.h"
[email protected]35b9c562010-11-09 02:22:4318#include "remoting/protocol/util.h"
[email protected]cb3b1f9312010-06-07 19:58:2319
[email protected]cd8d2372010-11-04 01:18:0420using remoting::protocol::ConnectionToClient;
21
[email protected]cb3b1f9312010-06-07 19:58:2322namespace remoting {
23
24// By default we capture 20 times a second. This number is obtained by
25// experiment to provide good latency.
26static const double kDefaultCaptureRate = 20.0;
27
28// Interval that we perform rate regulation.
29static const base::TimeDelta kRateControlInterval =
30 base::TimeDelta::FromSeconds(1);
31
32// We divide the pending update stream number by this value to determine the
33// rate divider.
34static const int kSlowDownFactor = 10;
35
36// A list of dividers used to divide the max rate to determine the current
37// capture rate.
38static const int kRateDividers[] = {1, 2, 4, 8, 16};
39
40SessionManager::SessionManager(
41 MessageLoop* capture_loop,
42 MessageLoop* encode_loop,
43 MessageLoop* network_loop,
44 Capturer* capturer,
45 Encoder* encoder)
46 : capture_loop_(capture_loop),
47 encode_loop_(encode_loop),
48 network_loop_(network_loop),
49 capturer_(capturer),
50 encoder_(encoder),
51 rate_(kDefaultCaptureRate),
[email protected]cb3b1f9312010-06-07 19:58:2352 started_(false),
53 recordings_(0),
[email protected]887cec82010-06-15 22:13:4354 max_rate_(kDefaultCaptureRate),
[email protected]7c186b16f2010-06-15 16:35:3355 rate_control_started_(false) {
[email protected]cb3b1f9312010-06-07 19:58:2356 DCHECK(capture_loop_);
57 DCHECK(encode_loop_);
58 DCHECK(network_loop_);
59}
60
61SessionManager::~SessionManager() {
[email protected]cd8d2372010-11-04 01:18:0462 connections_.clear();
[email protected]cb3b1f9312010-06-07 19:58:2363}
64
[email protected]3b3d3af2010-07-02 22:23:1765// Public methods --------------------------------------------------------------
66
[email protected]cb3b1f9312010-06-07 19:58:2367void SessionManager::Start() {
68 capture_loop_->PostTask(
[email protected]8ea7a1672010-10-04 19:48:4269 FROM_HERE, NewTracedMethod(this, &SessionManager::DoStart));
[email protected]cb3b1f9312010-06-07 19:58:2370}
71
[email protected]cb3b1f9312010-06-07 19:58:2372void SessionManager::Pause() {
73 capture_loop_->PostTask(
[email protected]8ea7a1672010-10-04 19:48:4274 FROM_HERE, NewTracedMethod(this, &SessionManager::DoPause));
[email protected]cb3b1f9312010-06-07 19:58:2375}
76
[email protected]cb3b1f9312010-06-07 19:58:2377void SessionManager::SetMaxRate(double rate) {
78 capture_loop_->PostTask(
[email protected]8ea7a1672010-10-04 19:48:4279 FROM_HERE, NewTracedMethod(this, &SessionManager::DoSetMaxRate, rate));
[email protected]cb3b1f9312010-06-07 19:58:2380}
81
[email protected]cd8d2372010-11-04 01:18:0482void SessionManager::AddConnection(
83 scoped_refptr<ConnectionToClient> connection) {
84 // Gets the init information for the connection.
[email protected]ef0a59a62010-06-09 21:56:3985 capture_loop_->PostTask(
[email protected]cb3b1f9312010-06-07 19:58:2386 FROM_HERE,
[email protected]cd8d2372010-11-04 01:18:0487 NewTracedMethod(this, &SessionManager::DoGetInitInfo, connection));
[email protected]cb3b1f9312010-06-07 19:58:2388}
89
[email protected]cd8d2372010-11-04 01:18:0490void SessionManager::RemoveConnection(
91 scoped_refptr<ConnectionToClient> connection) {
[email protected]cb3b1f9312010-06-07 19:58:2392 network_loop_->PostTask(
93 FROM_HERE,
[email protected]cd8d2372010-11-04 01:18:0494 NewTracedMethod(this, &SessionManager::DoRemoveClient, connection));
[email protected]cb3b1f9312010-06-07 19:58:2395}
96
[email protected]cd8d2372010-11-04 01:18:0497void SessionManager::RemoveAllConnections() {
[email protected]92698ce2010-06-28 21:49:3098 network_loop_->PostTask(
99 FROM_HERE,
[email protected]8ea7a1672010-10-04 19:48:42100 NewTracedMethod(this, &SessionManager::DoRemoveAllClients));
[email protected]92698ce2010-06-28 21:49:30101}
102
[email protected]3b3d3af2010-07-02 22:23:17103// Private accessors -----------------------------------------------------------
104
105Capturer* SessionManager::capturer() {
106 DCHECK_EQ(capture_loop_, MessageLoop::current());
107 return capturer_.get();
108}
109
110Encoder* SessionManager::encoder() {
111 DCHECK_EQ(encode_loop_, MessageLoop::current());
112 return encoder_.get();
113}
114
115// Capturer thread -------------------------------------------------------------
116
117void SessionManager::DoStart() {
118 DCHECK_EQ(capture_loop_, MessageLoop::current());
119
120 if (started_) {
[email protected]58b48972010-09-03 00:45:32121 NOTREACHED() << "Record session already started.";
[email protected]3b3d3af2010-07-02 22:23:17122 return;
123 }
124
125 started_ = true;
126 DoCapture();
127
128 // Starts the rate regulation.
129 network_loop_->PostTask(
130 FROM_HERE,
[email protected]8ea7a1672010-10-04 19:48:42131 NewTracedMethod(this, &SessionManager::DoStartRateControl));
[email protected]3b3d3af2010-07-02 22:23:17132}
133
134void SessionManager::DoPause() {
135 DCHECK_EQ(capture_loop_, MessageLoop::current());
136
137 if (!started_) {
[email protected]58b48972010-09-03 00:45:32138 NOTREACHED() << "Record session not started.";
[email protected]3b3d3af2010-07-02 22:23:17139 return;
140 }
141
142 started_ = false;
143
144 // Pause the rate regulation.
145 network_loop_->PostTask(
146 FROM_HERE,
[email protected]8ea7a1672010-10-04 19:48:42147 NewTracedMethod(this, &SessionManager::DoPauseRateControl));
[email protected]3b3d3af2010-07-02 22:23:17148}
149
150void SessionManager::DoSetRate(double rate) {
151 DCHECK_EQ(capture_loop_, MessageLoop::current());
152 if (rate == rate_)
153 return;
154
155 // Change the current capture rate.
156 rate_ = rate;
157
158 // If we have already started then schedule the next capture with the new
159 // rate.
160 if (started_)
161 ScheduleNextCapture();
162}
163
164void SessionManager::DoSetMaxRate(double max_rate) {
165 DCHECK_EQ(capture_loop_, MessageLoop::current());
166
167 // TODO(hclam): Should also check for small epsilon.
168 if (max_rate != 0) {
169 max_rate_ = max_rate;
170 DoSetRate(max_rate);
171 } else {
172 NOTREACHED() << "Rate is too small.";
173 }
174}
175
176void SessionManager::ScheduleNextCapture() {
177 DCHECK_EQ(capture_loop_, MessageLoop::current());
178
[email protected]8ea7a1672010-10-04 19:48:42179 ScopedTracer tracer("capture");
180
181 TraceContext::tracer()->PrintString("Capture Scheduled");
182
[email protected]3b3d3af2010-07-02 22:23:17183 if (rate_ == 0)
184 return;
185
186 base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
187 static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
188 capture_loop_->PostDelayedTask(
189 FROM_HERE,
[email protected]8ea7a1672010-10-04 19:48:42190 NewTracedMethod(this, &SessionManager::DoCapture),
[email protected]3b3d3af2010-07-02 22:23:17191 interval.InMilliseconds());
192}
193
[email protected]cb3b1f9312010-06-07 19:58:23194void SessionManager::DoCapture() {
195 DCHECK_EQ(capture_loop_, MessageLoop::current());
[email protected]cb3b1f9312010-06-07 19:58:23196 // Make sure we have at most two oustanding recordings. We can simply return
197 // if we can't make a capture now, the next capture will be started by the
198 // end of an encode operation.
[email protected]804c99622010-07-01 15:02:53199 if (recordings_ >= 2 || !started_) {
[email protected]cb3b1f9312010-06-07 19:58:23200 return;
[email protected]804c99622010-07-01 15:02:53201 }
[email protected]8ea7a1672010-10-04 19:48:42202 TraceContext::tracer()->PrintString("Capture Started");
[email protected]cb3b1f9312010-06-07 19:58:23203
204 base::Time now = base::Time::Now();
205 base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
206 static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
207 base::TimeDelta elapsed = now - last_capture_time_;
208
[email protected]804c99622010-07-01 15:02:53209 // If this method is called sooner than the required interval we return
[email protected]cb3b1f9312010-06-07 19:58:23210 // immediately
[email protected]804c99622010-07-01 15:02:53211 if (elapsed < interval) {
[email protected]cb3b1f9312010-06-07 19:58:23212 return;
[email protected]804c99622010-07-01 15:02:53213 }
[email protected]cb3b1f9312010-06-07 19:58:23214
215 // At this point we are going to perform one capture so save the current time.
216 last_capture_time_ = now;
217 ++recordings_;
218
219 // Before we actually do a capture, schedule the next one.
220 ScheduleNextCapture();
221
222 // And finally perform one capture.
[email protected]8ea7a1672010-10-04 19:48:42223 DCHECK(capturer());
[email protected]804c99622010-07-01 15:02:53224
[email protected]8ea7a1672010-10-04 19:48:42225 capturer()->CaptureInvalidRects(
[email protected]804c99622010-07-01 15:02:53226 NewCallback(this, &SessionManager::CaptureDoneCallback));
[email protected]cb3b1f9312010-06-07 19:58:23227}
228
[email protected]3b3d3af2010-07-02 22:23:17229void SessionManager::CaptureDoneCallback(
[email protected]be2da4d2010-07-23 00:54:47230 scoped_refptr<CaptureData> capture_data) {
[email protected]88552a92010-08-06 22:50:00231 // TODO(hclam): There is a bug if the capturer doesn't produce any dirty
232 // rects.
[email protected]3b3d3af2010-07-02 22:23:17233 DCHECK_EQ(capture_loop_, MessageLoop::current());
[email protected]8ea7a1672010-10-04 19:48:42234 TraceContext::tracer()->PrintString("Capture Done");
[email protected]3b3d3af2010-07-02 22:23:17235 encode_loop_->PostTask(
236 FROM_HERE,
[email protected]8ea7a1672010-10-04 19:48:42237 NewTracedMethod(this, &SessionManager::DoEncode, capture_data));
[email protected]3b3d3af2010-07-02 22:23:17238}
239
[email protected]cb3b1f9312010-06-07 19:58:23240void SessionManager::DoFinishEncode() {
241 DCHECK_EQ(capture_loop_, MessageLoop::current());
242
243 // Decrement the number of recording in process since we have completed
244 // one cycle.
245 --recordings_;
246
247 // Try to do a capture again. Note that the following method may do nothing
248 // if it is too early to perform a capture.
249 if (rate_ > 0)
250 DoCapture();
251}
252
[email protected]d87c4042010-11-04 00:46:01253void SessionManager::DoGetInitInfo(
[email protected]cd8d2372010-11-04 01:18:04254 scoped_refptr<ConnectionToClient> connection) {
[email protected]cb3b1f9312010-06-07 19:58:23255 DCHECK_EQ(capture_loop_, MessageLoop::current());
256
[email protected]8ea7a1672010-10-04 19:48:42257 ScopedTracer tracer("init");
258
[email protected]cd8d2372010-11-04 01:18:04259 // Sends the init message to the connection.
[email protected]cb3b1f9312010-06-07 19:58:23260 network_loop_->PostTask(
261 FROM_HERE,
[email protected]cd8d2372010-11-04 01:18:04262 NewTracedMethod(this, &SessionManager::DoSendInit, connection,
[email protected]804c99622010-07-01 15:02:53263 capturer()->width(), capturer()->height()));
[email protected]ef0a59a62010-06-09 21:56:39264
[email protected]cd8d2372010-11-04 01:18:04265 // And then add the connection to the list so it can receive update stream.
266 // It is important we do so in such order or the connection will receive
[email protected]ef0a59a62010-06-09 21:56:39267 // update stream before init message.
268 network_loop_->PostTask(
269 FROM_HERE,
[email protected]cd8d2372010-11-04 01:18:04270 NewTracedMethod(this, &SessionManager::DoAddClient, connection));
[email protected]cb3b1f9312010-06-07 19:58:23271}
272
[email protected]3b3d3af2010-07-02 22:23:17273// Network thread --------------------------------------------------------------
274
275void SessionManager::DoStartRateControl() {
276 DCHECK_EQ(network_loop_, MessageLoop::current());
277
278 if (rate_control_started_) {
279 NOTREACHED() << "Rate regulation already started";
[email protected]cb3b1f9312010-06-07 19:58:23280 return;
[email protected]cb3b1f9312010-06-07 19:58:23281 }
[email protected]3b3d3af2010-07-02 22:23:17282 rate_control_started_ = true;
283 ScheduleNextRateControl();
[email protected]cb3b1f9312010-06-07 19:58:23284}
285
[email protected]3b3d3af2010-07-02 22:23:17286void SessionManager::DoPauseRateControl() {
[email protected]cb3b1f9312010-06-07 19:58:23287 DCHECK_EQ(network_loop_, MessageLoop::current());
288
[email protected]3b3d3af2010-07-02 22:23:17289 if (!rate_control_started_) {
290 NOTREACHED() << "Rate regulation not started";
291 return;
[email protected]92698ce2010-06-28 21:49:30292 }
[email protected]3b3d3af2010-07-02 22:23:17293 rate_control_started_ = false;
[email protected]92698ce2010-06-28 21:49:30294}
295
[email protected]3b3d3af2010-07-02 22:23:17296void SessionManager::ScheduleNextRateControl() {
[email protected]8ea7a1672010-10-04 19:48:42297 ScopedTracer tracer("Rate Control");
[email protected]3b3d3af2010-07-02 22:23:17298 network_loop_->PostDelayedTask(
299 FROM_HERE,
[email protected]8ea7a1672010-10-04 19:48:42300 NewTracedMethod(this, &SessionManager::DoRateControl),
[email protected]3b3d3af2010-07-02 22:23:17301 kRateControlInterval.InMilliseconds());
[email protected]cb3b1f9312010-06-07 19:58:23302}
303
304void SessionManager::DoRateControl() {
305 DCHECK_EQ(network_loop_, MessageLoop::current());
306
307 // If we have been paused then shutdown the rate regulation loop.
308 if (!rate_control_started_)
309 return;
310
311 int max_pending_update_streams = 0;
[email protected]cd8d2372010-11-04 01:18:04312 for (size_t i = 0; i < connections_.size(); ++i) {
[email protected]cb3b1f9312010-06-07 19:58:23313 max_pending_update_streams =
314 std::max(max_pending_update_streams,
[email protected]cd8d2372010-11-04 01:18:04315 connections_[i]->GetPendingUpdateStreamMessages());
[email protected]cb3b1f9312010-06-07 19:58:23316 }
317
318 // If |slow_down| equals zero, we have no slow down.
[email protected]887cec82010-06-15 22:13:43319 size_t slow_down = max_pending_update_streams / kSlowDownFactor;
[email protected]cb3b1f9312010-06-07 19:58:23320 // Set new_rate to -1 for checking later.
321 double new_rate = -1;
322 // If the slow down is too large.
323 if (slow_down >= arraysize(kRateDividers)) {
324 // Then we stop the capture completely.
325 new_rate = 0;
326 } else {
327 // Slow down the capture rate using the divider.
328 new_rate = max_rate_ / kRateDividers[slow_down];
329 }
330 DCHECK_NE(new_rate, -1.0);
331
332 // Then set the rate.
333 capture_loop_->PostTask(
334 FROM_HERE,
[email protected]8ea7a1672010-10-04 19:48:42335 NewTracedMethod(this, &SessionManager::DoSetRate, new_rate));
[email protected]cb3b1f9312010-06-07 19:58:23336 ScheduleNextRateControl();
337}
338
[email protected]04b36142010-11-02 01:08:19339void SessionManager::DoSendVideoPacket(VideoPacket* packet) {
[email protected]3b3d3af2010-07-02 22:23:17340 DCHECK_EQ(network_loop_, MessageLoop::current());
[email protected]cb3b1f9312010-06-07 19:58:23341
[email protected]8ea7a1672010-10-04 19:48:42342 TraceContext::tracer()->PrintString("DoSendUpdate");
343
[email protected]cd8d2372010-11-04 01:18:04344 for (ConnectionToClientList::const_iterator i = connections_.begin();
345 i < connections_.end(); ++i) {
[email protected]04b36142010-11-02 01:08:19346 (*i)->SendVideoPacket(*packet);
[email protected]3b3d3af2010-07-02 22:23:17347 }
[email protected]04b36142010-11-02 01:08:19348 delete packet;
[email protected]c3af26f332010-10-06 22:46:00349
[email protected]8ea7a1672010-10-04 19:48:42350 TraceContext::tracer()->PrintString("DoSendUpdate done");
[email protected]cb3b1f9312010-06-07 19:58:23351}
352
[email protected]cd8d2372010-11-04 01:18:04353void SessionManager::DoSendInit(scoped_refptr<ConnectionToClient> connection,
354 int width, int height) {
[email protected]3b3d3af2010-07-02 22:23:17355 DCHECK_EQ(network_loop_, MessageLoop::current());
356
[email protected]cd8d2372010-11-04 01:18:04357 // Sends the connection init information.
[email protected]35b9c562010-11-09 02:22:43358 protocol::NotifyResolutionRequest* message =
359 new protocol::NotifyResolutionRequest();
360 message->set_width(width);
361 message->set_height(height);
362 connection->client_stub()->NotifyResolution(message,
363 NewDeleteMessageTask(message));
[email protected]cb3b1f9312010-06-07 19:58:23364}
365
[email protected]cd8d2372010-11-04 01:18:04366void SessionManager::DoAddClient(scoped_refptr<ConnectionToClient> connection) {
[email protected]3b3d3af2010-07-02 22:23:17367 DCHECK_EQ(network_loop_, MessageLoop::current());
368
369 // TODO(hclam): Force a full frame for next encode.
[email protected]cd8d2372010-11-04 01:18:04370 connections_.push_back(connection);
[email protected]3b3d3af2010-07-02 22:23:17371}
372
[email protected]d87c4042010-11-04 00:46:01373void SessionManager::DoRemoveClient(
[email protected]cd8d2372010-11-04 01:18:04374 scoped_refptr<ConnectionToClient> connection) {
[email protected]3b3d3af2010-07-02 22:23:17375 DCHECK_EQ(network_loop_, MessageLoop::current());
376
377 // TODO(hclam): Is it correct to do to a scoped_refptr?
[email protected]cd8d2372010-11-04 01:18:04378 ConnectionToClientList::iterator it
379 = std::find(connections_.begin(), connections_.end(), connection);
380 if (it != connections_.end()) {
381 connections_.erase(it);
[email protected]3b3d3af2010-07-02 22:23:17382 }
383}
384
385void SessionManager::DoRemoveAllClients() {
386 DCHECK_EQ(network_loop_, MessageLoop::current());
387
[email protected]cd8d2372010-11-04 01:18:04388 // Clear the list of connections.
389 connections_.clear();
[email protected]3b3d3af2010-07-02 22:23:17390}
391
392// Encoder thread --------------------------------------------------------------
393
394void SessionManager::DoEncode(
[email protected]be2da4d2010-07-23 00:54:47395 scoped_refptr<CaptureData> capture_data) {
[email protected]3b3d3af2010-07-02 22:23:17396 DCHECK_EQ(encode_loop_, MessageLoop::current());
[email protected]8ea7a1672010-10-04 19:48:42397 TraceContext::tracer()->PrintString("DoEncode called");
[email protected]3b3d3af2010-07-02 22:23:17398
[email protected]8ea7a1672010-10-04 19:48:42399 // Early out if there's nothing to encode.
[email protected]5a196bd2010-08-18 21:37:59400 if (!capture_data->dirty_rects().size()) {
401 capture_loop_->PostTask(
[email protected]8ea7a1672010-10-04 19:48:42402 FROM_HERE, NewTracedMethod(this, &SessionManager::DoFinishEncode));
403 return;
[email protected]5a196bd2010-08-18 21:37:59404 }
405
[email protected]cd8d2372010-11-04 01:18:04406 // TODO(hclam): Enable |force_refresh| if a new connection was
[email protected]3b3d3af2010-07-02 22:23:17407 // added.
[email protected]8ea7a1672010-10-04 19:48:42408 TraceContext::tracer()->PrintString("Encode start");
[email protected]3b3d3af2010-07-02 22:23:17409 encoder_->Encode(capture_data, false,
410 NewCallback(this, &SessionManager::EncodeDataAvailableTask));
[email protected]8ea7a1672010-10-04 19:48:42411 TraceContext::tracer()->PrintString("Encode Done");
[email protected]cb3b1f9312010-06-07 19:58:23412}
413
[email protected]04b36142010-11-02 01:08:19414void SessionManager::EncodeDataAvailableTask(VideoPacket* packet) {
[email protected]cb3b1f9312010-06-07 19:58:23415 DCHECK_EQ(encode_loop_, MessageLoop::current());
416
[email protected]04b36142010-11-02 01:08:19417 bool last = (packet->flags() & VideoPacket::LAST_PACKET) != 0;
418
[email protected]cd8d2372010-11-04 01:18:04419 // Before a new encode task starts, notify connected clients a new update
[email protected]cb3b1f9312010-06-07 19:58:23420 // stream is coming.
421 // Notify this will keep a reference to the DataBuffer in the
[email protected]cd8d2372010-11-04 01:18:04422 // task. The ownership will eventually pass to the ConnectionToClients.
[email protected]cb3b1f9312010-06-07 19:58:23423 network_loop_->PostTask(
424 FROM_HERE,
[email protected]04b36142010-11-02 01:08:19425 NewTracedMethod(this, &SessionManager::DoSendVideoPacket, packet));
[email protected]cb3b1f9312010-06-07 19:58:23426
[email protected]04b36142010-11-02 01:08:19427 if (last) {
[email protected]cb3b1f9312010-06-07 19:58:23428 capture_loop_->PostTask(
[email protected]8ea7a1672010-10-04 19:48:42429 FROM_HERE, NewTracedMethod(this, &SessionManager::DoFinishEncode));
[email protected]cb3b1f9312010-06-07 19:58:23430 }
431}
432
[email protected]cb3b1f9312010-06-07 19:58:23433} // namespace remoting