OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/video_scheduler.h" | 5 #include "remoting/host/video_scheduler.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
(...skipping 18 matching lines...) Expand all Loading... | |
29 | 29 |
30 // Maximum number of frames that can be processed simultaneously. | 30 // Maximum number of frames that can be processed simultaneously. |
31 // TODO(hclam): Move this value to CaptureScheduler. | 31 // TODO(hclam): Move this value to CaptureScheduler. |
32 static const int kMaxPendingCaptures = 2; | 32 static const int kMaxPendingCaptures = 2; |
33 | 33 |
34 // static | 34 // static |
35 scoped_refptr<VideoScheduler> VideoScheduler::Create( | 35 scoped_refptr<VideoScheduler> VideoScheduler::Create( |
36 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, | 36 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, |
37 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, | 37 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, |
38 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, | 38 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, |
39 VideoFrameCapturer* capturer, | 39 scoped_ptr<VideoFrameCapturer> capturer, |
40 scoped_ptr<VideoEncoder> encoder, | 40 scoped_ptr<VideoEncoder> encoder, |
41 protocol::CursorShapeStub* cursor_stub, | 41 protocol::CursorShapeStub* cursor_stub, |
42 protocol::VideoStub* video_stub) { | 42 protocol::VideoStub* video_stub) { |
43 DCHECK(network_task_runner->BelongsToCurrentThread()); | 43 DCHECK(network_task_runner->BelongsToCurrentThread()); |
44 DCHECK(capturer); | 44 DCHECK(capturer); |
45 DCHECK(encoder); | 45 DCHECK(encoder); |
46 DCHECK(cursor_stub); | 46 DCHECK(cursor_stub); |
47 DCHECK(video_stub); | 47 DCHECK(video_stub); |
48 | 48 |
49 scoped_refptr<VideoScheduler> scheduler = new VideoScheduler( | 49 scoped_refptr<VideoScheduler> scheduler = new VideoScheduler( |
50 capture_task_runner, encode_task_runner, network_task_runner, | 50 capture_task_runner, encode_task_runner, network_task_runner, |
51 capturer, encoder.Pass(), cursor_stub, video_stub); | 51 capturer.Pass(), encoder.Pass(), cursor_stub, video_stub); |
52 capture_task_runner->PostTask( | 52 capture_task_runner->PostTask( |
53 FROM_HERE, base::Bind(&VideoScheduler::StartOnCaptureThread, scheduler)); | 53 FROM_HERE, base::Bind(&VideoScheduler::StartOnCaptureThread, scheduler)); |
54 | 54 |
55 return scheduler; | 55 return scheduler; |
56 } | 56 } |
57 | 57 |
58 // Public methods -------------------------------------------------------------- | 58 // Public methods -------------------------------------------------------------- |
59 | 59 |
60 void VideoScheduler::OnCaptureCompleted( | 60 void VideoScheduler::OnCaptureCompleted( |
61 scoped_refptr<CaptureData> capture_data) { | 61 scoped_refptr<CaptureData> capture_data) { |
(...skipping 25 matching lines...) Expand all Loading... | |
87 cursor_proto->set_height(cursor_shape->size.height()); | 87 cursor_proto->set_height(cursor_shape->size.height()); |
88 cursor_proto->set_hotspot_x(cursor_shape->hotspot.x()); | 88 cursor_proto->set_hotspot_x(cursor_shape->hotspot.x()); |
89 cursor_proto->set_hotspot_y(cursor_shape->hotspot.y()); | 89 cursor_proto->set_hotspot_y(cursor_shape->hotspot.y()); |
90 cursor_proto->set_data(cursor_shape->data); | 90 cursor_proto->set_data(cursor_shape->data); |
91 | 91 |
92 network_task_runner_->PostTask( | 92 network_task_runner_->PostTask( |
93 FROM_HERE, base::Bind(&VideoScheduler::SendCursorShape, this, | 93 FROM_HERE, base::Bind(&VideoScheduler::SendCursorShape, this, |
94 base::Passed(&cursor_proto))); | 94 base::Passed(&cursor_proto))); |
95 } | 95 } |
96 | 96 |
97 void VideoScheduler::Stop(const base::Closure& done_task) { | 97 void VideoScheduler::Stop() { |
98 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 98 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
99 DCHECK(!done_task.is_null()); | |
100 | 99 |
101 // Clear stubs to prevent further updates reaching the client. | 100 // Clear stubs to prevent further updates reaching the client. |
102 cursor_stub_ = NULL; | 101 cursor_stub_ = NULL; |
103 video_stub_ = NULL; | 102 video_stub_ = NULL; |
104 | 103 |
105 capture_task_runner_->PostTask(FROM_HERE, | 104 capture_task_runner_->PostTask(FROM_HERE, |
106 base::Bind(&VideoScheduler::StopOnCaptureThread, this, done_task)); | 105 base::Bind(&VideoScheduler::StopOnCaptureThread, this)); |
107 } | 106 } |
108 | 107 |
109 void VideoScheduler::Pause(bool pause) { | 108 void VideoScheduler::Pause(bool pause) { |
110 if (!capture_task_runner_->BelongsToCurrentThread()) { | 109 if (!capture_task_runner_->BelongsToCurrentThread()) { |
111 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 110 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
112 capture_task_runner_->PostTask( | 111 capture_task_runner_->PostTask( |
113 FROM_HERE, base::Bind(&VideoScheduler::Pause, this, pause)); | 112 FROM_HERE, base::Bind(&VideoScheduler::Pause, this, pause)); |
114 return; | 113 return; |
115 } | 114 } |
116 | 115 |
(...skipping 17 matching lines...) Expand all Loading... | |
134 | 133 |
135 sequence_number_ = sequence_number; | 134 sequence_number_ = sequence_number; |
136 } | 135 } |
137 | 136 |
138 // Private methods ----------------------------------------------------------- | 137 // Private methods ----------------------------------------------------------- |
139 | 138 |
140 VideoScheduler::VideoScheduler( | 139 VideoScheduler::VideoScheduler( |
141 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, | 140 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, |
142 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, | 141 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, |
143 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, | 142 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, |
144 VideoFrameCapturer* capturer, | 143 scoped_ptr<VideoFrameCapturer> capturer, |
145 scoped_ptr<VideoEncoder> encoder, | 144 scoped_ptr<VideoEncoder> encoder, |
146 protocol::CursorShapeStub* cursor_stub, | 145 protocol::CursorShapeStub* cursor_stub, |
147 protocol::VideoStub* video_stub) | 146 protocol::VideoStub* video_stub) |
148 : capture_task_runner_(capture_task_runner), | 147 : capture_task_runner_(capture_task_runner), |
149 encode_task_runner_(encode_task_runner), | 148 encode_task_runner_(encode_task_runner), |
150 network_task_runner_(network_task_runner), | 149 network_task_runner_(network_task_runner), |
151 capturer_(capturer), | 150 capturer_(capturer.Pass()), |
152 encoder_(encoder.Pass()), | 151 encoder_(encoder.Pass()), |
153 cursor_stub_(cursor_stub), | 152 cursor_stub_(cursor_stub), |
154 video_stub_(video_stub), | 153 video_stub_(video_stub), |
155 pending_captures_(0), | 154 pending_captures_(0), |
156 did_skip_frame_(false), | 155 did_skip_frame_(false), |
157 is_paused_(false), | 156 is_paused_(false), |
158 sequence_number_(0) { | 157 sequence_number_(0), |
158 size_most_recent_(SkISize::Make(0, 0)) { | |
159 } | 159 } |
160 | 160 |
161 VideoScheduler::~VideoScheduler() { | 161 VideoScheduler::~VideoScheduler() { |
162 } | 162 } |
163 | 163 |
164 // Capturer thread ------------------------------------------------------------- | 164 // Capturer thread ------------------------------------------------------------- |
165 | 165 |
166 void VideoScheduler::StartOnCaptureThread() { | 166 void VideoScheduler::StartOnCaptureThread() { |
167 DCHECK(capture_task_runner_->BelongsToCurrentThread()); | 167 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
168 | 168 |
169 // Start the capturer and let it notify us of cursor shape changes. | 169 // Start the capturer and let it notify us of cursor shape changes. |
170 capturer_->Start(this); | 170 capturer_->Start(this); |
171 | 171 |
172 capture_timer_.reset(new base::OneShotTimer<VideoScheduler>()); | 172 capture_timer_.reset(new base::OneShotTimer<VideoScheduler>()); |
173 | 173 |
174 // Capture first frame immedately. | 174 // Capture first frame immedately. |
175 CaptureNextFrame(); | 175 CaptureNextFrame(); |
176 } | 176 } |
177 | 177 |
178 void VideoScheduler::StopOnCaptureThread(const base::Closure& done_task) { | 178 void VideoScheduler::StopOnCaptureThread() { |
179 DCHECK(capture_task_runner_->BelongsToCurrentThread()); | 179 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
180 | 180 |
181 // Stop |capturer_| and clear it to prevent pending tasks from using it. | 181 // Stop |capturer_| and clear it to prevent pending tasks from using it. |
182 capturer_->Stop(); | 182 capturer_->Stop(); |
183 capturer_ = NULL; | |
184 | 183 |
185 // |capture_timer_| must be destroyed on the thread on which it is used. | 184 // |capture_timer_| must be destroyed on the thread on which it is used. |
186 capture_timer_.reset(); | 185 capture_timer_.reset(); |
187 | 186 |
188 // Ensure that the encode thread is no longer processing capture data, | 187 // Schedule deletion of |capturer_| once the encode thread is no longer |
189 // otherwise tearing down |capturer_| will crash it. See crbug.com/163641. | 188 // processing capture data. See http://crbug.com/163641. This also clears |
189 // |capturer_| pointer to prevent pending tasks from using it. | |
190 // TODO(wez): Make it safe to tear down capturer while buffers remain, and | 190 // TODO(wez): Make it safe to tear down capturer while buffers remain, and |
191 // remove this work-around. | 191 // remove this work-around. |
192 encode_task_runner_->PostTask(FROM_HERE, | 192 encode_task_runner_->PostTask( |
193 base::Bind(&VideoScheduler::StopOnEncodeThread, this, done_task)); | 193 FROM_HERE, base::Bind(&VideoScheduler::StopOnEncodeThread, this, |
194 base::Passed(&capturer_))); | |
194 } | 195 } |
195 | 196 |
196 void VideoScheduler::ScheduleNextCapture() { | 197 void VideoScheduler::ScheduleNextCapture() { |
197 DCHECK(capture_task_runner_->BelongsToCurrentThread()); | 198 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
198 | 199 |
199 capture_timer_->Start(FROM_HERE, | 200 capture_timer_->Start(FROM_HERE, |
200 scheduler_.NextCaptureDelay(), | 201 scheduler_.NextCaptureDelay(), |
201 this, | 202 this, |
202 &VideoScheduler::CaptureNextFrame); | 203 &VideoScheduler::CaptureNextFrame); |
203 } | 204 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
248 void VideoScheduler::SendVideoPacket(scoped_ptr<VideoPacket> packet) { | 249 void VideoScheduler::SendVideoPacket(scoped_ptr<VideoPacket> packet) { |
249 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 250 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
250 | 251 |
251 if (!video_stub_) | 252 if (!video_stub_) |
252 return; | 253 return; |
253 | 254 |
254 base::Closure callback; | 255 base::Closure callback; |
255 if ((packet->flags() & VideoPacket::LAST_PARTITION) != 0) | 256 if ((packet->flags() & VideoPacket::LAST_PARTITION) != 0) |
256 callback = base::Bind(&VideoScheduler::VideoFrameSentCallback, this); | 257 callback = base::Bind(&VideoScheduler::VideoFrameSentCallback, this); |
257 | 258 |
259 // Update saved screen size. | |
260 if (packet->format().has_screen_width() && | |
261 packet->format().has_screen_height()) { | |
262 size_most_recent_ = SkISize::Make(packet->format().screen_width(), | |
263 packet->format().screen_height()); | |
264 } | |
265 | |
258 video_stub_->ProcessVideoPacket(packet.Pass(), callback); | 266 video_stub_->ProcessVideoPacket(packet.Pass(), callback); |
259 } | 267 } |
260 | 268 |
261 void VideoScheduler::VideoFrameSentCallback() { | 269 void VideoScheduler::VideoFrameSentCallback() { |
262 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 270 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
263 | 271 |
264 if (!video_stub_) | 272 if (!video_stub_) |
265 return; | 273 return; |
266 | 274 |
267 capture_task_runner_->PostTask( | 275 capture_task_runner_->PostTask( |
268 FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this)); | 276 FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this)); |
269 } | 277 } |
270 | 278 |
271 void VideoScheduler::SendCursorShape( | 279 void VideoScheduler::SendCursorShape( |
272 scoped_ptr<protocol::CursorShapeInfo> cursor_shape) { | 280 scoped_ptr<protocol::CursorShapeInfo> cursor_shape) { |
273 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 281 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
274 | 282 |
275 if (!cursor_stub_) | 283 if (!cursor_stub_) |
276 return; | 284 return; |
277 | 285 |
278 cursor_stub_->SetCursorShape(*cursor_shape); | 286 cursor_stub_->SetCursorShape(*cursor_shape); |
279 } | 287 } |
280 | 288 |
289 void VideoScheduler::StopOnNetworkThread( | |
290 scoped_ptr<VideoFrameCapturer> capturer) { | |
291 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
292 | |
293 // This is posted by StopOnEncodeThread meaning that both capture and encode | |
294 // threads are stopped now and it is safe to delete |capturer|. | |
295 } | |
296 | |
281 // Encoder thread -------------------------------------------------------------- | 297 // Encoder thread -------------------------------------------------------------- |
282 | 298 |
283 void VideoScheduler::EncodeFrame( | 299 void VideoScheduler::EncodeFrame( |
284 scoped_refptr<CaptureData> capture_data) { | 300 scoped_refptr<CaptureData> capture_data) { |
285 DCHECK(encode_task_runner_->BelongsToCurrentThread()); | 301 DCHECK(encode_task_runner_->BelongsToCurrentThread()); |
286 | 302 |
287 // If there is nothing to encode then send an empty keep-alive packet. | 303 // If there is nothing to encode then send an empty keep-alive packet. |
288 if (!capture_data || capture_data->dirty_region().isEmpty()) { | 304 if (!capture_data || capture_data->dirty_region().isEmpty()) { |
289 scoped_ptr<VideoPacket> packet(new VideoPacket()); | 305 scoped_ptr<VideoPacket> packet(new VideoPacket()); |
290 packet->set_flags(VideoPacket::LAST_PARTITION); | 306 packet->set_flags(VideoPacket::LAST_PARTITION); |
(...skipping 16 matching lines...) Expand all Loading... | |
307 if (last) { | 323 if (last) { |
308 scheduler_.RecordEncodeTime( | 324 scheduler_.RecordEncodeTime( |
309 base::TimeDelta::FromMilliseconds(packet->encode_time_ms())); | 325 base::TimeDelta::FromMilliseconds(packet->encode_time_ms())); |
310 } | 326 } |
311 | 327 |
312 network_task_runner_->PostTask( | 328 network_task_runner_->PostTask( |
313 FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, | 329 FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, |
314 base::Passed(&packet))); | 330 base::Passed(&packet))); |
315 } | 331 } |
316 | 332 |
317 void VideoScheduler::StopOnEncodeThread(const base::Closure& done_task) { | 333 void VideoScheduler::StopOnEncodeThread( |
334 scoped_ptr<VideoFrameCapturer> capturer) { | |
318 DCHECK(encode_task_runner_->BelongsToCurrentThread()); | 335 DCHECK(encode_task_runner_->BelongsToCurrentThread()); |
319 | 336 |
320 // This is posted by StopOnCaptureThread, so we know that by the time we | 337 // This is posted by StopOnCaptureThread, so we know that by the time we |
321 // process it there are no more encode tasks queued. | 338 // process it there are no more encode tasks queued. Schedule deletion of |
322 network_task_runner_->PostTask(FROM_HERE, done_task); | 339 // |capturer_| on the same thread it was created on. |
Wez
2013/01/11 02:23:41
nit: Suggest "Pass |capturer_| for deletion on the
alexeypa (please no reviews)
2013/01/11 19:59:46
Done.
| |
340 network_task_runner_->PostTask( | |
341 FROM_HERE, base::Bind(&VideoScheduler::StopOnNetworkThread, this, | |
342 base::Passed(&capturer))); | |
323 } | 343 } |
324 | 344 |
325 } // namespace remoting | 345 } // namespace remoting |
OLD | NEW |