summaryrefslogtreecommitdiffstats
path: root/remoting/host
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/host')
-rw-r--r--remoting/host/video_scheduler.cc69
-rw-r--r--remoting/host/video_scheduler.h12
2 files changed, 58 insertions, 23 deletions
diff --git a/remoting/host/video_scheduler.cc b/remoting/host/video_scheduler.cc
index 7fc1983..a346d04 100644
--- a/remoting/host/video_scheduler.cc
+++ b/remoting/host/video_scheduler.cc
@@ -30,6 +30,10 @@ namespace remoting {
// TODO(hclam): Move this value to CaptureScheduler.
static const int kMaxPendingFrames = 2;
+// Interval between empty keep-alive frames. These frames are sent only
+// when there are no real video frames being sent.
+static const int kKeepAlivePacketIntervalMs = 500;
+
VideoScheduler::VideoScheduler(
scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner,
@@ -68,16 +72,22 @@ void VideoScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) {
capture_pending_ = false;
+ if (!frame) {
+ LOG(ERROR) << "Capture failed.";
+ return;
+ }
+
scoped_ptr<webrtc::DesktopFrame> owned_frame(frame);
- if (frame) {
- scheduler_.RecordCaptureTime(
- base::TimeDelta::FromMilliseconds(frame->capture_time_ms()));
- }
+ scheduler_.RecordCaptureTime(
+ base::TimeDelta::FromMilliseconds(frame->capture_time_ms()));
- encode_task_runner_->PostTask(
- FROM_HERE, base::Bind(&VideoScheduler::EncodeFrame, this,
- base::Passed(&owned_frame), sequence_number_));
+ // Encode and send only non-empty frames.
+ if (!frame->updated_region().is_empty()) {
+ encode_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&VideoScheduler::EncodeFrame, this,
+ base::Passed(&owned_frame), sequence_number_));
+ }
// If a frame was skipped, try to capture it again.
if (did_skip_frame_) {
@@ -123,8 +133,10 @@ void VideoScheduler::Stop() {
cursor_stub_ = NULL;
video_stub_ = NULL;
- capture_task_runner_->PostTask(FROM_HERE,
- base::Bind(&VideoScheduler::StopOnCaptureThread, this));
+ keep_alive_timer_.reset();
+
+ capture_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&VideoScheduler::StopOnCaptureThread, this));
}
void VideoScheduler::Pause(bool pause) {
@@ -172,6 +184,9 @@ void VideoScheduler::StartOnCaptureThread() {
capturer_->Start(this);
capture_timer_.reset(new base::OneShotTimer<VideoScheduler>());
+ keep_alive_timer_.reset(new base::DelayTimer<VideoScheduler>(
+ FROM_HERE, base::TimeDelta::FromMilliseconds(kKeepAlivePacketIntervalMs),
+ this, &VideoScheduler::SendKeepAlivePacket));
// Capture first frame immedately.
CaptureNextFrame();
@@ -249,19 +264,39 @@ void VideoScheduler::SendVideoPacket(scoped_ptr<VideoPacket> packet) {
return;
video_stub_->ProcessVideoPacket(
- packet.Pass(), base::Bind(&VideoScheduler::VideoFrameSentCallback, this));
+ packet.Pass(), base::Bind(&VideoScheduler::OnVideoPacketSent, this));
}
-void VideoScheduler::VideoFrameSentCallback() {
+void VideoScheduler::OnVideoPacketSent() {
DCHECK(network_task_runner_->BelongsToCurrentThread());
if (!video_stub_)
return;
+ keep_alive_timer_->Reset();
+
capture_task_runner_->PostTask(
FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this));
}
+void VideoScheduler::SendKeepAlivePacket() {
+ DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+ if (!video_stub_)
+ return;
+
+ video_stub_->ProcessVideoPacket(
+ scoped_ptr<VideoPacket>(new VideoPacket()),
+ base::Bind(&VideoScheduler::OnKeepAlivePacketSent, this));
+}
+
+void VideoScheduler::OnKeepAlivePacketSent() {
+ DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+ if (keep_alive_timer_)
+ keep_alive_timer_->Reset();
+}
+
void VideoScheduler::SendCursorShape(
scoped_ptr<protocol::CursorShapeInfo> cursor_shape) {
DCHECK(network_task_runner_->BelongsToCurrentThread());
@@ -278,17 +313,7 @@ void VideoScheduler::EncodeFrame(
scoped_ptr<webrtc::DesktopFrame> frame,
int64 sequence_number) {
DCHECK(encode_task_runner_->BelongsToCurrentThread());
-
- // If there is nothing to encode then send an empty keep-alive packet.
- if (!frame || frame->updated_region().is_empty()) {
- scoped_ptr<VideoPacket> packet(new VideoPacket());
- packet->set_client_sequence_number(sequence_number);
- network_task_runner_->PostTask(
- FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this,
- base::Passed(&packet)));
- capture_task_runner_->DeleteSoon(FROM_HERE, frame.release());
- return;
- }
+ DCHECK(!frame->updated_region().is_empty());
scoped_ptr<VideoPacket> packet = encoder_->Encode(*frame);
packet->set_client_sequence_number(sequence_number);
diff --git a/remoting/host/video_scheduler.h b/remoting/host/video_scheduler.h
index f2eabd9..94fc795 100644
--- a/remoting/host/video_scheduler.h
+++ b/remoting/host/video_scheduler.h
@@ -140,7 +140,13 @@ class VideoScheduler : public base::RefCountedThreadSafe<VideoScheduler>,
// Callback passed to |video_stub_| for the last packet in each frame, to
// rate-limit frame captures to network throughput.
- void VideoFrameSentCallback();
+ void OnVideoPacketSent();
+
+ // Called by |keep_alive_timer_|.
+ void SendKeepAlivePacket();
+
+ // Callback for |video_stub_| called after a keep-alive packet is sent.
+ void OnKeepAlivePacketSent();
// Send updated cursor shape to client.
void SendCursorShape(scoped_ptr<protocol::CursorShapeInfo> cursor_shape);
@@ -173,6 +179,10 @@ class VideoScheduler : public base::RefCountedThreadSafe<VideoScheduler>,
// Timer used to schedule CaptureNextFrame().
scoped_ptr<base::OneShotTimer<VideoScheduler> > capture_timer_;
+ // Timer used to ensure that we send empty keep-alive frames to the client
+ // even when the video stream is paused or encoder is busy.
+ scoped_ptr<base::DelayTimer<VideoScheduler> > keep_alive_timer_;
+
// The number of frames being processed, i.e. frames that we are currently
// capturing, encoding or sending. The value is capped at 2 to minimize
// latency.