blob: cf3417ce4129cc102007c17ec40f1244bbfd312c [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
[email protected]2cb5bd42010-12-06 23:06:075#include "remoting/host/screen_recorder.h"
[email protected]cb3b1f9312010-06-07 19:58:236
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]c56acf54ff2010-11-19 23:44:4512#include "base/task.h"
[email protected]5bc71832010-12-09 01:34:0813#include "base/time.h"
[email protected]be2da4d2010-07-23 00:54:4714#include "remoting/base/capture_data.h"
[email protected]8ea7a1672010-10-04 19:48:4215#include "remoting/base/tracer.h"
[email protected]35b9c562010-11-09 02:22:4316#include "remoting/proto/control.pb.h"
[email protected]3adf1b22010-11-09 23:22:2017#include "remoting/proto/video.pb.h"
[email protected]35b9c562010-11-09 02:22:4318#include "remoting/protocol/client_stub.h"
[email protected]cd8d2372010-11-04 01:18:0419#include "remoting/protocol/connection_to_client.h"
[email protected]4d10ede2010-10-28 18:43:3720#include "remoting/protocol/message_decoder.h"
[email protected]35b9c562010-11-09 02:22:4321#include "remoting/protocol/util.h"
[email protected]cb3b1f9312010-06-07 19:58:2322
[email protected]cd8d2372010-11-04 01:18:0423using remoting::protocol::ConnectionToClient;
24
[email protected]cb3b1f9312010-06-07 19:58:2325namespace remoting {
26
27// By default we capture 20 times a second. This number is obtained by
28// experiment to provide good latency.
29static const double kDefaultCaptureRate = 20.0;
30
[email protected]5bc71832010-12-09 01:34:0831// Maximum number of frames that can be processed similtaneously.
32// TODO(sergeyu): Should this be set to 1? Or should we change
33// dynamically depending on how fast network and CPU are? Experement
34// with it.
35static const int kMaxRecordings = 2;
[email protected]cb3b1f9312010-06-07 19:58:2336
[email protected]2cb5bd42010-12-06 23:06:0737ScreenRecorder::ScreenRecorder(
[email protected]cb3b1f9312010-06-07 19:58:2338 MessageLoop* capture_loop,
39 MessageLoop* encode_loop,
40 MessageLoop* network_loop,
41 Capturer* capturer,
42 Encoder* encoder)
43 : capture_loop_(capture_loop),
44 encode_loop_(encode_loop),
45 network_loop_(network_loop),
46 capturer_(capturer),
47 encoder_(encoder),
[email protected]cb3b1f9312010-06-07 19:58:2348 started_(false),
49 recordings_(0),
[email protected]5bc71832010-12-09 01:34:0850 frame_skipped_(false),
51 max_rate_(kDefaultCaptureRate) {
[email protected]cb3b1f9312010-06-07 19:58:2352 DCHECK(capture_loop_);
53 DCHECK(encode_loop_);
54 DCHECK(network_loop_);
55}
56
[email protected]2cb5bd42010-12-06 23:06:0757ScreenRecorder::~ScreenRecorder() {
[email protected]cd8d2372010-11-04 01:18:0458 connections_.clear();
[email protected]cb3b1f9312010-06-07 19:58:2359}
60
[email protected]3b3d3af2010-07-02 22:23:1761// Public methods --------------------------------------------------------------
62
[email protected]2cb5bd42010-12-06 23:06:0763void ScreenRecorder::Start() {
[email protected]cb3b1f9312010-06-07 19:58:2364 capture_loop_->PostTask(
[email protected]2cb5bd42010-12-06 23:06:0765 FROM_HERE, NewTracedMethod(this, &ScreenRecorder::DoStart));
[email protected]cb3b1f9312010-06-07 19:58:2366}
67
[email protected]2cb5bd42010-12-06 23:06:0768void ScreenRecorder::Pause() {
[email protected]cb3b1f9312010-06-07 19:58:2369 capture_loop_->PostTask(
[email protected]2cb5bd42010-12-06 23:06:0770 FROM_HERE, NewTracedMethod(this, &ScreenRecorder::DoPause));
[email protected]cb3b1f9312010-06-07 19:58:2371}
72
[email protected]2cb5bd42010-12-06 23:06:0773void ScreenRecorder::SetMaxRate(double rate) {
[email protected]cb3b1f9312010-06-07 19:58:2374 capture_loop_->PostTask(
[email protected]2cb5bd42010-12-06 23:06:0775 FROM_HERE, NewTracedMethod(this, &ScreenRecorder::DoSetMaxRate, rate));
[email protected]cb3b1f9312010-06-07 19:58:2376}
77
[email protected]2cb5bd42010-12-06 23:06:0778void ScreenRecorder::AddConnection(
[email protected]cd8d2372010-11-04 01:18:0479 scoped_refptr<ConnectionToClient> connection) {
[email protected]5bc71832010-12-09 01:34:0880 ScopedTracer tracer("AddConnection");
81
82 // Add the client to the list so it can receive update stream.
83 network_loop_->PostTask(
[email protected]cb3b1f9312010-06-07 19:58:2384 FROM_HERE,
[email protected]5bc71832010-12-09 01:34:0885 NewTracedMethod(this, &ScreenRecorder::DoAddConnection, connection));
[email protected]cb3b1f9312010-06-07 19:58:2386}
87
[email protected]2cb5bd42010-12-06 23:06:0788void ScreenRecorder::RemoveConnection(
[email protected]cd8d2372010-11-04 01:18:0489 scoped_refptr<ConnectionToClient> connection) {
[email protected]cb3b1f9312010-06-07 19:58:2390 network_loop_->PostTask(
91 FROM_HERE,
[email protected]2cb5bd42010-12-06 23:06:0792 NewTracedMethod(this, &ScreenRecorder::DoRemoveClient, connection));
[email protected]cb3b1f9312010-06-07 19:58:2393}
94
[email protected]2cb5bd42010-12-06 23:06:0795void ScreenRecorder::RemoveAllConnections() {
[email protected]92698ce2010-06-28 21:49:3096 network_loop_->PostTask(
97 FROM_HERE,
[email protected]2cb5bd42010-12-06 23:06:0798 NewTracedMethod(this, &ScreenRecorder::DoRemoveAllClients));
[email protected]92698ce2010-06-28 21:49:3099}
100
[email protected]3b3d3af2010-07-02 22:23:17101// Private accessors -----------------------------------------------------------
102
[email protected]2cb5bd42010-12-06 23:06:07103Capturer* ScreenRecorder::capturer() {
[email protected]3b3d3af2010-07-02 22:23:17104 DCHECK_EQ(capture_loop_, MessageLoop::current());
[email protected]5bc71832010-12-09 01:34:08105 DCHECK(capturer_.get());
[email protected]3b3d3af2010-07-02 22:23:17106 return capturer_.get();
107}
108
[email protected]2cb5bd42010-12-06 23:06:07109Encoder* ScreenRecorder::encoder() {
[email protected]3b3d3af2010-07-02 22:23:17110 DCHECK_EQ(encode_loop_, MessageLoop::current());
[email protected]5bc71832010-12-09 01:34:08111 DCHECK(encoder_.get());
[email protected]3b3d3af2010-07-02 22:23:17112 return encoder_.get();
113}
114
115// Capturer thread -------------------------------------------------------------
116
[email protected]2cb5bd42010-12-06 23:06:07117void ScreenRecorder::DoStart() {
[email protected]3b3d3af2010-07-02 22:23:17118 DCHECK_EQ(capture_loop_, MessageLoop::current());
[email protected]5bc71832010-12-09 01:34:08119 DCHECK(!started_);
[email protected]3b3d3af2010-07-02 22:23:17120
121 if (started_) {
[email protected]58b48972010-09-03 00:45:32122 NOTREACHED() << "Record session already started.";
[email protected]3b3d3af2010-07-02 22:23:17123 return;
124 }
125
126 started_ = true;
[email protected]5bc71832010-12-09 01:34:08127 StartCaptureTimer();
[email protected]3b3d3af2010-07-02 22:23:17128
[email protected]5bc71832010-12-09 01:34:08129 // Capture first frame immedately.
130 DoCapture();
[email protected]3b3d3af2010-07-02 22:23:17131}
132
[email protected]2cb5bd42010-12-06 23:06:07133void ScreenRecorder::DoPause() {
[email protected]3b3d3af2010-07-02 22:23:17134 DCHECK_EQ(capture_loop_, MessageLoop::current());
135
136 if (!started_) {
[email protected]58b48972010-09-03 00:45:32137 NOTREACHED() << "Record session not started.";
[email protected]3b3d3af2010-07-02 22:23:17138 return;
139 }
140
[email protected]5bc71832010-12-09 01:34:08141 capture_timer_.Stop();
[email protected]3b3d3af2010-07-02 22:23:17142 started_ = false;
[email protected]3b3d3af2010-07-02 22:23:17143}
144
[email protected]2cb5bd42010-12-06 23:06:07145void ScreenRecorder::DoSetMaxRate(double max_rate) {
[email protected]3b3d3af2010-07-02 22:23:17146 DCHECK_EQ(capture_loop_, MessageLoop::current());
147
148 // TODO(hclam): Should also check for small epsilon.
[email protected]5bc71832010-12-09 01:34:08149 DCHECK_GT(max_rate, 0.0) << "Rate is too small.";
150
151 max_rate_ = max_rate;
152
153 // Restart the timer with the new rate.
154 if (started_) {
155 capture_timer_.Stop();
156 StartCaptureTimer();
[email protected]3b3d3af2010-07-02 22:23:17157 }
158}
159
[email protected]5bc71832010-12-09 01:34:08160void ScreenRecorder::StartCaptureTimer() {
[email protected]3b3d3af2010-07-02 22:23:17161 DCHECK_EQ(capture_loop_, MessageLoop::current());
162
[email protected]3b3d3af2010-07-02 22:23:17163 base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
[email protected]5bc71832010-12-09 01:34:08164 static_cast<int>(base::Time::kMillisecondsPerSecond / max_rate_));
165 capture_timer_.Start(interval, this, &ScreenRecorder::DoCapture);
[email protected]3b3d3af2010-07-02 22:23:17166}
167
[email protected]2cb5bd42010-12-06 23:06:07168void ScreenRecorder::DoCapture() {
[email protected]cb3b1f9312010-06-07 19:58:23169 DCHECK_EQ(capture_loop_, MessageLoop::current());
[email protected]cb3b1f9312010-06-07 19:58:23170 // Make sure we have at most two oustanding recordings. We can simply return
171 // if we can't make a capture now, the next capture will be started by the
172 // end of an encode operation.
[email protected]5bc71832010-12-09 01:34:08173 if (recordings_ >= kMaxRecordings || !started_) {
174 frame_skipped_ = true;
[email protected]cb3b1f9312010-06-07 19:58:23175 return;
[email protected]804c99622010-07-01 15:02:53176 }
[email protected]5bc71832010-12-09 01:34:08177
178 if (frame_skipped_) {
179 frame_skipped_ = false;
180 capture_timer_.Reset();
181 }
182
[email protected]8ea7a1672010-10-04 19:48:42183 TraceContext::tracer()->PrintString("Capture Started");
[email protected]cb3b1f9312010-06-07 19:58:23184
[email protected]cb3b1f9312010-06-07 19:58:23185 // At this point we are going to perform one capture so save the current time.
[email protected]cb3b1f9312010-06-07 19:58:23186 ++recordings_;
187
[email protected]cb3b1f9312010-06-07 19:58:23188 // And finally perform one capture.
[email protected]8ea7a1672010-10-04 19:48:42189 capturer()->CaptureInvalidRects(
[email protected]2cb5bd42010-12-06 23:06:07190 NewCallback(this, &ScreenRecorder::CaptureDoneCallback));
[email protected]cb3b1f9312010-06-07 19:58:23191}
192
[email protected]2cb5bd42010-12-06 23:06:07193void ScreenRecorder::CaptureDoneCallback(
[email protected]be2da4d2010-07-23 00:54:47194 scoped_refptr<CaptureData> capture_data) {
[email protected]88552a92010-08-06 22:50:00195 // TODO(hclam): There is a bug if the capturer doesn't produce any dirty
196 // rects.
[email protected]3b3d3af2010-07-02 22:23:17197 DCHECK_EQ(capture_loop_, MessageLoop::current());
[email protected]8ea7a1672010-10-04 19:48:42198 TraceContext::tracer()->PrintString("Capture Done");
[email protected]3b3d3af2010-07-02 22:23:17199 encode_loop_->PostTask(
200 FROM_HERE,
[email protected]2cb5bd42010-12-06 23:06:07201 NewTracedMethod(this, &ScreenRecorder::DoEncode, capture_data));
[email protected]3b3d3af2010-07-02 22:23:17202}
203
[email protected]5bc71832010-12-09 01:34:08204void ScreenRecorder::DoFinishSend() {
[email protected]cb3b1f9312010-06-07 19:58:23205 DCHECK_EQ(capture_loop_, MessageLoop::current());
206
207 // Decrement the number of recording in process since we have completed
208 // one cycle.
209 --recordings_;
210
211 // Try to do a capture again. Note that the following method may do nothing
212 // if it is too early to perform a capture.
[email protected]5bc71832010-12-09 01:34:08213 DoCapture();
[email protected]cb3b1f9312010-06-07 19:58:23214}
215
[email protected]3b3d3af2010-07-02 22:23:17216// Network thread --------------------------------------------------------------
217
[email protected]2cb5bd42010-12-06 23:06:07218void ScreenRecorder::DoSendVideoPacket(VideoPacket* packet) {
[email protected]3b3d3af2010-07-02 22:23:17219 DCHECK_EQ(network_loop_, MessageLoop::current());
[email protected]cb3b1f9312010-06-07 19:58:23220
[email protected]5bc71832010-12-09 01:34:08221 TraceContext::tracer()->PrintString("DoSendVideoPacket");
222
223 bool last = (packet->flags() & VideoPacket::LAST_PARTITION) != 0;
[email protected]8ea7a1672010-10-04 19:48:42224
[email protected]cd8d2372010-11-04 01:18:04225 for (ConnectionToClientList::const_iterator i = connections_.begin();
226 i < connections_.end(); ++i) {
[email protected]5bc71832010-12-09 01:34:08227 Task* done_task = NULL;
228
229 // Call OnFrameSent() only for the last packet in the first connection.
230 if (last && i == connections_.begin()) {
231 done_task = NewTracedMethod(this, &ScreenRecorder::OnFrameSent, packet);
232 } else {
233 done_task = new DeleteTask<VideoPacket>(packet);
234 }
235
236 (*i)->video_stub()->ProcessVideoPacket(packet, done_task);
[email protected]3b3d3af2010-07-02 22:23:17237 }
[email protected]c3af26f332010-10-06 22:46:00238
[email protected]5bc71832010-12-09 01:34:08239 TraceContext::tracer()->PrintString("DoSendVideoPacket done");
[email protected]cb3b1f9312010-06-07 19:58:23240}
241
[email protected]5bc71832010-12-09 01:34:08242void ScreenRecorder::OnFrameSent(VideoPacket* packet) {
243 delete packet;
244 capture_loop_->PostTask(
245 FROM_HERE, NewTracedMethod(this, &ScreenRecorder::DoFinishSend));
246}
247
248void ScreenRecorder::DoAddConnection(
249 scoped_refptr<ConnectionToClient> connection) {
[email protected]3b3d3af2010-07-02 22:23:17250 DCHECK_EQ(network_loop_, MessageLoop::current());
251
252 // TODO(hclam): Force a full frame for next encode.
[email protected]cd8d2372010-11-04 01:18:04253 connections_.push_back(connection);
[email protected]3b3d3af2010-07-02 22:23:17254}
255
[email protected]2cb5bd42010-12-06 23:06:07256void ScreenRecorder::DoRemoveClient(
[email protected]cd8d2372010-11-04 01:18:04257 scoped_refptr<ConnectionToClient> connection) {
[email protected]3b3d3af2010-07-02 22:23:17258 DCHECK_EQ(network_loop_, MessageLoop::current());
259
260 // TODO(hclam): Is it correct to do to a scoped_refptr?
[email protected]5bc71832010-12-09 01:34:08261 ConnectionToClientList::iterator it =
262 std::find(connections_.begin(), connections_.end(), connection);
[email protected]cd8d2372010-11-04 01:18:04263 if (it != connections_.end()) {
264 connections_.erase(it);
[email protected]3b3d3af2010-07-02 22:23:17265 }
266}
267
[email protected]2cb5bd42010-12-06 23:06:07268void ScreenRecorder::DoRemoveAllClients() {
[email protected]3b3d3af2010-07-02 22:23:17269 DCHECK_EQ(network_loop_, MessageLoop::current());
270
[email protected]cd8d2372010-11-04 01:18:04271 // Clear the list of connections.
272 connections_.clear();
[email protected]3b3d3af2010-07-02 22:23:17273}
274
275// Encoder thread --------------------------------------------------------------
276
[email protected]2cb5bd42010-12-06 23:06:07277void ScreenRecorder::DoEncode(
[email protected]be2da4d2010-07-23 00:54:47278 scoped_refptr<CaptureData> capture_data) {
[email protected]3b3d3af2010-07-02 22:23:17279 DCHECK_EQ(encode_loop_, MessageLoop::current());
[email protected]8ea7a1672010-10-04 19:48:42280 TraceContext::tracer()->PrintString("DoEncode called");
[email protected]3b3d3af2010-07-02 22:23:17281
[email protected]8ea7a1672010-10-04 19:48:42282 // Early out if there's nothing to encode.
[email protected]5a196bd2010-08-18 21:37:59283 if (!capture_data->dirty_rects().size()) {
284 capture_loop_->PostTask(
[email protected]5bc71832010-12-09 01:34:08285 FROM_HERE, NewTracedMethod(this, &ScreenRecorder::DoFinishSend));
[email protected]8ea7a1672010-10-04 19:48:42286 return;
[email protected]5a196bd2010-08-18 21:37:59287 }
288
[email protected]cd8d2372010-11-04 01:18:04289 // TODO(hclam): Enable |force_refresh| if a new connection was
[email protected]3b3d3af2010-07-02 22:23:17290 // added.
[email protected]8ea7a1672010-10-04 19:48:42291 TraceContext::tracer()->PrintString("Encode start");
[email protected]5bc71832010-12-09 01:34:08292 encoder()->Encode(capture_data, false,
[email protected]2cb5bd42010-12-06 23:06:07293 NewCallback(this, &ScreenRecorder::EncodeDataAvailableTask));
[email protected]8ea7a1672010-10-04 19:48:42294 TraceContext::tracer()->PrintString("Encode Done");
[email protected]cb3b1f9312010-06-07 19:58:23295}
296
[email protected]2cb5bd42010-12-06 23:06:07297void ScreenRecorder::EncodeDataAvailableTask(VideoPacket* packet) {
[email protected]cb3b1f9312010-06-07 19:58:23298 DCHECK_EQ(encode_loop_, MessageLoop::current());
299
[email protected]cb3b1f9312010-06-07 19:58:23300 network_loop_->PostTask(
301 FROM_HERE,
[email protected]2cb5bd42010-12-06 23:06:07302 NewTracedMethod(this, &ScreenRecorder::DoSendVideoPacket, packet));
[email protected]cb3b1f9312010-06-07 19:58:23303}
304
[email protected]cb3b1f9312010-06-07 19:58:23305} // namespace remoting