OLD | NEW |
(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 |
OLD | NEW |