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

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

Issue 8342040: Gather history of capture and encode time determine next recoring delay (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: tested and removed testing code Created 9 years, 2 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
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 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/host/screen_recorder.h" 5 #include "remoting/host/screen_recorder.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h" 11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop_proxy.h" 12 #include "base/message_loop_proxy.h"
13 #include "base/stl_util.h" 13 #include "base/stl_util.h"
14 #include "base/sys_info.h"
14 #include "base/task.h" 15 #include "base/task.h"
15 #include "base/time.h" 16 #include "base/time.h"
16 #include "remoting/base/capture_data.h" 17 #include "remoting/base/capture_data.h"
17 #include "remoting/proto/control.pb.h" 18 #include "remoting/proto/control.pb.h"
18 #include "remoting/proto/video.pb.h" 19 #include "remoting/proto/video.pb.h"
19 #include "remoting/protocol/client_stub.h" 20 #include "remoting/protocol/client_stub.h"
20 #include "remoting/protocol/connection_to_client.h" 21 #include "remoting/protocol/connection_to_client.h"
21 #include "remoting/protocol/message_decoder.h" 22 #include "remoting/protocol/message_decoder.h"
22 #include "remoting/protocol/util.h" 23 #include "remoting/protocol/util.h"
23 24
24 using remoting::protocol::ConnectionToClient; 25 using remoting::protocol::ConnectionToClient;
25 26
26 namespace remoting { 27 namespace remoting {
27 28
28 // By default we capture 20 times a second. This number is obtained by
29 // experiment to provide good latency.
30 static const double kDefaultCaptureRate = 20.0;
31
32 // Maximum number of frames that can be processed similtaneously. 29 // Maximum number of frames that can be processed similtaneously.
33 // TODO(sergeyu): Should this be set to 1? Or should we change
34 // dynamically depending on how fast network and CPU are? Experement
35 // with it.
36 static const int kMaxRecordings = 2; 30 static const int kMaxRecordings = 2;
Sergey Ulanov 2011/10/20 00:17:00 Can we move this to rate controller class? E.g. yo
Alpha Left Google 2011/10/31 18:47:24 I think it'll be better to do it separately. It's
Sergey Ulanov 2011/10/31 20:17:18 add TODO for this?
37 31
38 ScreenRecorder::ScreenRecorder( 32 ScreenRecorder::ScreenRecorder(
39 MessageLoop* capture_loop, 33 MessageLoop* capture_loop,
40 MessageLoop* encode_loop, 34 MessageLoop* encode_loop,
41 base::MessageLoopProxy* network_loop, 35 base::MessageLoopProxy* network_loop,
42 Capturer* capturer, 36 Capturer* capturer,
43 Encoder* encoder) 37 Encoder* encoder)
44 : capture_loop_(capture_loop), 38 : capture_loop_(capture_loop),
45 encode_loop_(encode_loop), 39 encode_loop_(encode_loop),
46 network_loop_(network_loop), 40 network_loop_(network_loop),
47 capturer_(capturer), 41 capturer_(capturer),
48 encoder_(encoder), 42 encoder_(encoder),
49 is_recording_(false), 43 is_recording_(false),
50 network_stopped_(false), 44 network_stopped_(false),
51 encoder_stopped_(false), 45 encoder_stopped_(false),
46 max_recordings_(kMaxRecordings),
52 recordings_(0), 47 recordings_(0),
53 frame_skipped_(false), 48 frame_skipped_(false),
54 max_rate_(kDefaultCaptureRate),
55 sequence_number_(0) { 49 sequence_number_(0) {
56 DCHECK(capture_loop_); 50 DCHECK(capture_loop_);
57 DCHECK(encode_loop_); 51 DCHECK(encode_loop_);
58 DCHECK(network_loop_); 52 DCHECK(network_loop_);
53
54 // If the system has only one processor there's no use to have simutaneous
Wez 2011/10/20 00:52:15 typo: "simutaneous"
Alpha Left Google 2011/10/31 18:47:24 Done.
Alpha Left Google 2011/10/31 18:47:24 Done.
55 // recording sessions, it will just create excessive system load.
Wez 2011/10/20 00:52:15 The rate-limiting should result in this, surely?
Alpha Left Google 2011/10/31 18:47:24 Actually max_recordings_ should be 1 here. My bad
56 if (base::SysInfo::NumberOfProcessors() < 2)
57 max_recordings_ = 2;
Sergey Ulanov 2011/10/20 00:17:00 Don't get it. kMaxRecording = 2, so max_recordings
Alpha Left Google 2011/10/31 18:47:24 I set it to 1, mistake here.
59 } 58 }
60 59
61 ScreenRecorder::~ScreenRecorder() { 60 ScreenRecorder::~ScreenRecorder() {
62 } 61 }
63 62
64 // Public methods -------------------------------------------------------------- 63 // Public methods --------------------------------------------------------------
65 64
66 void ScreenRecorder::Start() { 65 void ScreenRecorder::Start() {
67 capture_loop_->PostTask( 66 capture_loop_->PostTask(
68 FROM_HERE, NewRunnableMethod(this, &ScreenRecorder::DoStart)); 67 FROM_HERE, NewRunnableMethod(this, &ScreenRecorder::DoStart));
69 } 68 }
70 69
71 void ScreenRecorder::Stop(const base::Closure& done_task) { 70 void ScreenRecorder::Stop(const base::Closure& done_task) {
72 if (MessageLoop::current() != capture_loop_) { 71 if (MessageLoop::current() != capture_loop_) {
73 capture_loop_->PostTask(FROM_HERE, base::Bind( 72 capture_loop_->PostTask(FROM_HERE, base::Bind(
74 &ScreenRecorder::Stop, this, done_task)); 73 &ScreenRecorder::Stop, this, done_task));
75 return; 74 return;
76 } 75 }
77 76
78 DCHECK(!done_task.is_null()); 77 DCHECK(!done_task.is_null());
79 78
80 capture_timer_.Stop(); 79 capture_timer_.Stop();
81 is_recording_ = false; 80 is_recording_ = false;
82 81
83 network_loop_->PostTask(FROM_HERE, base::Bind( 82 network_loop_->PostTask(FROM_HERE, base::Bind(
84 &ScreenRecorder::DoStopOnNetworkThread, this, done_task)); 83 &ScreenRecorder::DoStopOnNetworkThread, this, done_task));
85 } 84 }
86 85
87 void ScreenRecorder::SetMaxRate(double rate) {
88 capture_loop_->PostTask(
89 FROM_HERE, NewRunnableMethod(this, &ScreenRecorder::DoSetMaxRate, rate));
90 }
91
92 void ScreenRecorder::AddConnection( 86 void ScreenRecorder::AddConnection(
93 scoped_refptr<ConnectionToClient> connection) { 87 scoped_refptr<ConnectionToClient> connection) {
94 capture_loop_->PostTask( 88 capture_loop_->PostTask(
95 FROM_HERE, 89 FROM_HERE,
96 NewRunnableMethod(this, &ScreenRecorder::DoInvalidateFullScreen)); 90 NewRunnableMethod(this, &ScreenRecorder::DoInvalidateFullScreen));
97 91
98 // Add the client to the list so it can receive update stream. 92 // Add the client to the list so it can receive update stream.
99 network_loop_->PostTask( 93 network_loop_->PostTask(
100 FROM_HERE, 94 FROM_HERE,
101 NewRunnableMethod(this, &ScreenRecorder::DoAddConnection, connection)); 95 NewRunnableMethod(this, &ScreenRecorder::DoAddConnection, connection));
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 139
146 void ScreenRecorder::DoStart() { 140 void ScreenRecorder::DoStart() {
147 DCHECK_EQ(capture_loop_, MessageLoop::current()); 141 DCHECK_EQ(capture_loop_, MessageLoop::current());
148 142
149 if (is_recording_) { 143 if (is_recording_) {
150 NOTREACHED() << "Record session already started."; 144 NOTREACHED() << "Record session already started.";
151 return; 145 return;
152 } 146 }
153 147
154 is_recording_ = true; 148 is_recording_ = true;
155 StartCaptureTimer(); 149 StartCaptureTimer();
Sergey Ulanov 2011/10/20 00:17:00 DoCapture() call below starts the timer, so I thin
Wez 2011/10/20 00:52:15 StartCaptureTimer() doesn't seem to be required at
Alpha Left Google 2011/10/31 18:47:24 Done.
156 150
157 // Capture first frame immedately. 151 // Capture first frame immedately.
158 DoCapture(); 152 DoCapture();
159 } 153 }
160 154
161 void ScreenRecorder::DoSetMaxRate(double max_rate) {
162 DCHECK_EQ(capture_loop_, MessageLoop::current());
163
164 // TODO(hclam): Should also check for small epsilon.
165 DCHECK_GT(max_rate, 0.0) << "Rate is too small.";
166
167 max_rate_ = max_rate;
168
169 // Restart the timer with the new rate.
170 if (is_recording_) {
171 capture_timer_.Stop();
172 StartCaptureTimer();
173 }
174 }
175
176 void ScreenRecorder::StartCaptureTimer() { 155 void ScreenRecorder::StartCaptureTimer() {
177 DCHECK_EQ(capture_loop_, MessageLoop::current()); 156 DCHECK_EQ(capture_loop_, MessageLoop::current());
178 157
179 base::TimeDelta interval = base::TimeDelta::FromMilliseconds( 158 capture_timer_.Start(FROM_HERE,
180 static_cast<int>(base::Time::kMillisecondsPerSecond / max_rate_)); 159 regulator_.NextRecordingDelay(),
181 capture_timer_.Start(FROM_HERE, interval, this, &ScreenRecorder::DoCapture); 160 this,
161 &ScreenRecorder::DoCapture);
182 } 162 }
183 163
184 void ScreenRecorder::DoCapture() { 164 void ScreenRecorder::DoCapture() {
185 DCHECK_EQ(capture_loop_, MessageLoop::current()); 165 DCHECK_EQ(capture_loop_, MessageLoop::current());
186 // Make sure we have at most two oustanding recordings. We can simply return 166 // Make sure we have at most two oustanding recordings. We can simply return
187 // if we can't make a capture now, the next capture will be started by the 167 // if we can't make a capture now, the next capture will be started by the
188 // end of an encode operation. 168 // end of an encode operation.
189 if (recordings_ >= kMaxRecordings || !is_recording_) { 169 if (recordings_ >= max_recordings_ || !is_recording_) {
190 frame_skipped_ = true; 170 frame_skipped_ = true;
191 return; 171 return;
192 } 172 }
193 173
194 if (frame_skipped_) { 174 if (frame_skipped_)
195 frame_skipped_ = false; 175 frame_skipped_ = false;
196 capture_timer_.Reset();
197 }
198 176
199 // At this point we are going to perform one capture so save the current time. 177 // At this point we are going to perform one capture so save the current time.
200 ++recordings_; 178 ++recordings_;
201 DCHECK_LE(recordings_, kMaxRecordings); 179 DCHECK_LE(recordings_, max_recordings_);
180
181 // Before doing a capture schedule for the next one.
182 capture_timer_.Stop();
Sergey Ulanov 2011/10/20 00:17:00 Do we need this? DoCapture() is supposed to be cal
Alpha Left Google 2011/10/31 18:47:24 We shouldn't hit this case but there's no harm doi
183 capture_timer_.Start(FROM_HERE,
184 regulator_.NextRecordingDelay(),
185 this,
186 &ScreenRecorder::DoCapture);
202 187
203 // And finally perform one capture. 188 // And finally perform one capture.
204 capture_start_time_ = base::Time::Now(); 189 capture_start_time_ = base::Time::Now();
205 capturer()->CaptureInvalidRegion( 190 capturer()->CaptureInvalidRegion(
206 NewCallback(this, &ScreenRecorder::CaptureDoneCallback)); 191 NewCallback(this, &ScreenRecorder::CaptureDoneCallback));
207 } 192 }
208 193
209 void ScreenRecorder::CaptureDoneCallback( 194 void ScreenRecorder::CaptureDoneCallback(
210 scoped_refptr<CaptureData> capture_data) { 195 scoped_refptr<CaptureData> capture_data) {
211 DCHECK_EQ(capture_loop_, MessageLoop::current()); 196 DCHECK_EQ(capture_loop_, MessageLoop::current());
212 197
213 if (!is_recording_) 198 if (!is_recording_)
214 return; 199 return;
215 200
216 if (capture_data) { 201 if (capture_data) {
217 int capture_time = static_cast<int>( 202 int capture_time = static_cast<int>(
218 (base::Time::Now() - capture_start_time_).InMilliseconds()); 203 (base::Time::Now() - capture_start_time_).InMilliseconds());
219 capture_data->set_capture_time_ms(capture_time); 204 capture_data->set_capture_time_ms(capture_time);
205 regulator_.RecordCaptureTime(capture_time);
220 206
221 // The best way to get this value is by binding the sequence number to 207 // The best way to get this value is by binding the sequence number to
222 // the callback when calling CaptureInvalidRects(). However the callback 208 // the callback when calling CaptureInvalidRects(). However the callback
223 // system doesn't allow this. Reading from the member variable is 209 // system doesn't allow this. Reading from the member variable is
224 // accurate as long as capture is synchronous as the following statement 210 // accurate as long as capture is synchronous as the following statement
225 // will obtain the most recent sequence number received. 211 // will obtain the most recent sequence number received.
226 capture_data->set_client_sequence_number(sequence_number_); 212 capture_data->set_client_sequence_number(sequence_number_);
227 } 213 }
228 214
229 encode_loop_->PostTask( 215 encode_loop_->PostTask(
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
377 DCHECK_EQ(encode_loop_, MessageLoop::current()); 363 DCHECK_EQ(encode_loop_, MessageLoop::current());
378 364
379 if (encoder_stopped_) 365 if (encoder_stopped_)
380 return; 366 return;
381 367
382 bool last = (packet->flags() & VideoPacket::LAST_PACKET) != 0; 368 bool last = (packet->flags() & VideoPacket::LAST_PACKET) != 0;
383 if (last) { 369 if (last) {
384 int encode_time = static_cast<int>( 370 int encode_time = static_cast<int>(
385 (base::Time::Now() - encode_start_time_).InMilliseconds()); 371 (base::Time::Now() - encode_start_time_).InMilliseconds());
386 packet->set_encode_time_ms(encode_time); 372 packet->set_encode_time_ms(encode_time);
373 regulator_.RecordEncodeTime(encode_time);
387 } 374 }
388 375
389 network_loop_->PostTask( 376 network_loop_->PostTask(
390 FROM_HERE, 377 FROM_HERE,
391 NewRunnableMethod(this, &ScreenRecorder::DoSendVideoPacket, packet)); 378 NewRunnableMethod(this, &ScreenRecorder::DoSendVideoPacket, packet));
392 } 379 }
393 380
394 } // namespace remoting 381 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698