Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(950)

Side by Side Diff: remoting/host/session_manager.cc

Issue 2690003: Copy the (early prototype of) remoting in Chrome into the public tree.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « remoting/host/session_manager.h ('k') | remoting/host/session_manager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // 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"
10 #include "base/stl_util-inl.h"
11 #include "media/base/data_buffer.h"
12 #include "remoting/base/protocol_decoder.h"
13 #include "remoting/host/client_connection.h"
14 #include "remoting/host/encoder.h"
15
16 namespace remoting {
17
18 // By default we capture 20 times a second. This number is obtained by
19 // experiment to provide good latency.
20 static const double kDefaultCaptureRate = 20.0;
21
22 // Interval that we perform rate regulation.
23 static const base::TimeDelta kRateControlInterval =
24 base::TimeDelta::FromSeconds(1);
25
26 // We divide the pending update stream number by this value to determine the
27 // rate divider.
28 static const int kSlowDownFactor = 10;
29
30 // A list of dividers used to divide the max rate to determine the current
31 // capture rate.
32 static const int kRateDividers[] = {1, 2, 4, 8, 16};
33
34 SessionManager::SessionManager(
35 MessageLoop* capture_loop,
36 MessageLoop* encode_loop,
37 MessageLoop* network_loop,
38 Capturer* capturer,
39 Encoder* encoder)
40 : capture_loop_(capture_loop),
41 encode_loop_(encode_loop),
42 network_loop_(network_loop),
43 capturer_(capturer),
44 encoder_(encoder),
45 rate_(kDefaultCaptureRate),
46 max_rate_(kDefaultCaptureRate),
47 started_(false),
48 recordings_(0),
49 rate_control_started_(false),
50 capture_width_(0),
51 capture_height_(0),
52 capture_pixel_format_(chromotocol_pb::PixelFormatInvalid),
53 encode_stream_started_(false),
54 encode_done_(false) {
55 DCHECK(capture_loop_);
56 DCHECK(encode_loop_);
57 DCHECK(network_loop_);
58 }
59
60 SessionManager::~SessionManager() {
61 clients_.clear();
62 DCHECK_EQ(0u, clients_.size());
63 }
64
65 void SessionManager::Start() {
66 capture_loop_->PostTask(
67 FROM_HERE, NewRunnableMethod(this, &SessionManager::DoStart));
68 }
69
70 void SessionManager::DoStart() {
71 DCHECK_EQ(capture_loop_, MessageLoop::current());
72
73 if (started_) {
74 NOTREACHED() << "Record session already started";
75 return;
76 }
77
78 started_ = true;
79 DoCapture();
80
81 // Starts the rate regulation.
82 network_loop_->PostTask(
83 FROM_HERE,
84 NewRunnableMethod(this, &SessionManager::DoStartRateControl));
85 }
86
87 void SessionManager::DoStartRateControl() {
88 DCHECK_EQ(network_loop_, MessageLoop::current());
89
90 if (rate_control_started_) {
91 NOTREACHED() << "Rate regulation already started";
92 return;
93 }
94 rate_control_started_ = true;
95 ScheduleNextRateControl();
96 }
97
98 void SessionManager::Pause() {
99 capture_loop_->PostTask(
100 FROM_HERE, NewRunnableMethod(this, &SessionManager::DoPause));
101 }
102
103 void SessionManager::DoPause() {
104 DCHECK_EQ(capture_loop_, MessageLoop::current());
105
106 if (!started_) {
107 NOTREACHED() << "Record session not started";
108 return;
109 }
110
111 started_ = false;
112
113 // Pause the rate regulation.
114 network_loop_->PostTask(
115 FROM_HERE,
116 NewRunnableMethod(this, &SessionManager::DoPauseRateControl));
117 }
118
119 void SessionManager::DoPauseRateControl() {
120 DCHECK_EQ(network_loop_, MessageLoop::current());
121
122 if (!rate_control_started_) {
123 NOTREACHED() << "Rate regulation not started";
124 return;
125 }
126 rate_control_started_ = false;
127 }
128
129 void SessionManager::SetMaxRate(double rate) {
130 capture_loop_->PostTask(
131 FROM_HERE, NewRunnableMethod(this, &SessionManager::DoSetMaxRate, rate));
132 }
133
134 void SessionManager::AddClient(scoped_refptr<ClientConnection> client) {
135 network_loop_->PostTask(
136 FROM_HERE,
137 NewRunnableMethod(this, &SessionManager::DoAddClient, client));
138 }
139
140 void SessionManager::RemoveClient(scoped_refptr<ClientConnection> client) {
141 network_loop_->PostTask(
142 FROM_HERE,
143 NewRunnableMethod(this, &SessionManager::DoRemoveClient, client));
144 }
145
146 void SessionManager::DoCapture() {
147 DCHECK_EQ(capture_loop_, MessageLoop::current());
148
149 // Make sure we have at most two oustanding recordings. We can simply return
150 // if we can't make a capture now, the next capture will be started by the
151 // end of an encode operation.
152 if (recordings_ >= 2 || !started_)
153 return;
154
155 base::Time now = base::Time::Now();
156 base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
157 static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
158 base::TimeDelta elapsed = now - last_capture_time_;
159
160 // If this method is called sonner than the required interval we return
161 // immediately
162 if (elapsed < interval)
163 return;
164
165 // At this point we are going to perform one capture so save the current time.
166 last_capture_time_ = now;
167 ++recordings_;
168
169 // Before we actually do a capture, schedule the next one.
170 ScheduleNextCapture();
171
172 // And finally perform one capture.
173 DCHECK(capturer_.get());
174 capturer_->CaptureDirtyRects(
175 NewRunnableMethod(this, &SessionManager::CaptureDoneTask));
176 }
177
178 void SessionManager::DoFinishEncode() {
179 DCHECK_EQ(capture_loop_, MessageLoop::current());
180
181 // Decrement the number of recording in process since we have completed
182 // one cycle.
183 --recordings_;
184
185 // Try to do a capture again. Note that the following method may do nothing
186 // if it is too early to perform a capture.
187 if (rate_ > 0)
188 DoCapture();
189 }
190
191 void SessionManager::DoEncode() {
192 DCHECK_EQ(encode_loop_, MessageLoop::current());
193
194 // Reset states about the encode stream.
195 encode_done_ = false;
196 encode_stream_started_ = false;
197
198 DCHECK(!encoded_data_.get());
199 DCHECK(encoder_.get());
200
201 // TODO(hclam): Enable |force_refresh| if a new client was
202 // added.
203 encoder_->SetSize(capture_width_, capture_height_);
204 encoder_->SetPixelFormat(capture_pixel_format_);
205 encoder_->Encode(
206 capture_dirty_rects_,
207 capture_data_,
208 capture_data_strides_,
209 false,
210 &encoded_data_header_,
211 &encoded_data_,
212 &encode_done_,
213 NewRunnableMethod(this, &SessionManager::EncodeDataAvailableTask));
214 }
215
216 void SessionManager::DoSendUpdate(
217 chromotocol_pb::UpdateStreamPacketHeader* header,
218 scoped_refptr<media::DataBuffer> encoded_data,
219 bool begin_update, bool end_update) {
220 DCHECK_EQ(network_loop_, MessageLoop::current());
221
222 for (size_t i = 0; i < clients_.size(); ++i) {
223 if (begin_update)
224 clients_[i]->SendBeginUpdateStreamMessage();
225
226 // This will pass the ownership of the DataBuffer to the ClientConnection.
227 clients_[i]->SendUpdateStreamPacketMessage(header, encoded_data);
228
229 if (end_update)
230 clients_[i]->SendEndUpdateStreamMessage();
231 }
232 }
233
234 void SessionManager::DoSendInit(scoped_refptr<ClientConnection> client,
235 int width, int height) {
236 DCHECK_EQ(network_loop_, MessageLoop::current());
237
238 // Sends the client init information.
239 client->SendInitClientMessage(width, height);
240 }
241
242 void SessionManager::DoGetInitInfo(scoped_refptr<ClientConnection> client) {
243 DCHECK_EQ(capture_loop_, MessageLoop::current());
244
245 network_loop_->PostTask(
246 FROM_HERE,
247 NewRunnableMethod(this, &SessionManager::DoSendInit, client,
248 capturer_->GetWidth(), capturer_->GetHeight()));
249 }
250
251 void SessionManager::DoSetRate(double rate) {
252 DCHECK_EQ(capture_loop_, MessageLoop::current());
253 if (rate == rate_)
254 return;
255
256 // Change the current capture rate.
257 rate_ = rate;
258
259 // If we have already started then schedule the next capture with the new
260 // rate.
261 if (started_)
262 ScheduleNextCapture();
263 }
264
265 void SessionManager::DoSetMaxRate(double max_rate) {
266 DCHECK_EQ(capture_loop_, MessageLoop::current());
267
268 // TODO(hclam): Should also check for small epsilon.
269 if (max_rate != 0) {
270 max_rate_ = max_rate;
271 DoSetRate(max_rate);
272 } else {
273 NOTREACHED() << "Rate is too small.";
274 }
275 }
276
277 void SessionManager::DoAddClient(scoped_refptr<ClientConnection> client) {
278 DCHECK_EQ(network_loop_, MessageLoop::current());
279
280 // TODO(hclam): Force a full frame for next encode.
281 clients_.push_back(client);
282
283 // Gets the init information for the client.
284 capture_loop_->PostTask(
285 FROM_HERE,
286 NewRunnableMethod(this, &SessionManager::DoGetInitInfo, client));
287 }
288
289 void SessionManager::DoRemoveClient(scoped_refptr<ClientConnection> client) {
290 DCHECK_EQ(network_loop_, MessageLoop::current());
291
292 // TODO(hclam): Is it correct to do to a scoped_refptr?
293 ClientConnectionList::iterator it
294 = std::find(clients_.begin(), clients_.end(), client);
295 if (it != clients_.end())
296 clients_.erase(it);
297 }
298
299 void SessionManager::DoRateControl() {
300 DCHECK_EQ(network_loop_, MessageLoop::current());
301
302 // If we have been paused then shutdown the rate regulation loop.
303 if (!rate_control_started_)
304 return;
305
306 int max_pending_update_streams = 0;
307 for (size_t i = 0; i < clients_.size(); ++i) {
308 max_pending_update_streams =
309 std::max(max_pending_update_streams,
310 clients_[i]->GetPendingUpdateStreamMessages());
311 }
312
313 // If |slow_down| equals zero, we have no slow down.
314 int slow_down = max_pending_update_streams / kSlowDownFactor;
315 // Set new_rate to -1 for checking later.
316 double new_rate = -1;
317 // If the slow down is too large.
318 if (slow_down >= arraysize(kRateDividers)) {
319 // Then we stop the capture completely.
320 new_rate = 0;
321 } else {
322 // Slow down the capture rate using the divider.
323 new_rate = max_rate_ / kRateDividers[slow_down];
324 }
325 DCHECK_NE(new_rate, -1.0);
326
327 // Then set the rate.
328 capture_loop_->PostTask(
329 FROM_HERE,
330 NewRunnableMethod(this, &SessionManager::DoSetRate, new_rate));
331 ScheduleNextRateControl();
332 }
333
334 void SessionManager::ScheduleNextCapture() {
335 DCHECK_EQ(capture_loop_, MessageLoop::current());
336
337 if (rate_ == 0)
338 return;
339
340 base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
341 static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
342 capture_loop_->PostDelayedTask(
343 FROM_HERE,
344 NewRunnableMethod(this, &SessionManager::DoCapture),
345 interval.InMilliseconds());
346 }
347
348 void SessionManager::ScheduleNextRateControl() {
349 network_loop_->PostDelayedTask(
350 FROM_HERE,
351 NewRunnableMethod(this, &SessionManager::DoRateControl),
352 kRateControlInterval.InMilliseconds());
353 }
354
355 void SessionManager::CaptureDoneTask() {
356 DCHECK_EQ(capture_loop_, MessageLoop::current());
357
358 // Save results of the capture.
359 capturer_->GetData(capture_data_);
360 capturer_->GetDataStride(capture_data_strides_);
361 capture_dirty_rects_.clear();
362 capturer_->GetDirtyRects(&capture_dirty_rects_);
363 capture_pixel_format_ = capturer_->GetPixelFormat();
364 capture_width_ = capturer_->GetWidth();
365 capture_height_ = capturer_->GetHeight();
366
367 encode_loop_->PostTask(
368 FROM_HERE, NewRunnableMethod(this, &SessionManager::DoEncode));
369 }
370
371 void SessionManager::EncodeDataAvailableTask() {
372 DCHECK_EQ(encode_loop_, MessageLoop::current());
373
374 // Before a new encode task starts, notify clients a new update
375 // stream is coming.
376 // Notify this will keep a reference to the DataBuffer in the
377 // task. The ownership will eventually pass to the ClientConnections.
378 network_loop_->PostTask(
379 FROM_HERE,
380 NewRunnableMethod(this,
381 &SessionManager::DoSendUpdate,
382 &encoded_data_header_,
383 encoded_data_,
384 !encode_stream_started_,
385 encode_done_));
386
387 // Since we have received data from the Encoder, mark the encode
388 // stream has started.
389 encode_stream_started_ = true;
390
391 // Give up the ownership of DataBuffer since it is passed to
392 // the ClientConnections.
393 encoded_data_ = NULL;
394
395 if (encode_done_) {
396 capture_loop_->PostTask(
397 FROM_HERE, NewRunnableMethod(this, &SessionManager::DoFinishEncode));
398 }
399 }
400
401 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/session_manager.h ('k') | remoting/host/session_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698