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

Side by Side Diff: remoting/protocol/webrtc_frame_scheduler_simple.cc

Issue 2381153002: Pace outgoing frames in frame scheduler to match target bitrate. (Closed)
Patch Set: . Created 4 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
« no previous file with comments | « remoting/protocol/webrtc_frame_scheduler_simple.h ('k') | remoting/test/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/protocol/webrtc_frame_scheduler_simple.h" 5 #include "remoting/protocol/webrtc_frame_scheduler_simple.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 9 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
10 10
(...skipping 13 matching lines...) Expand all
24 const int kTargetQuantizerForVp8TopOff = 30; 24 const int kTargetQuantizerForVp8TopOff = 30;
25 25
26 // Minimum target bitrate per megapixel. The value is chosen experimentally such 26 // Minimum target bitrate per megapixel. The value is chosen experimentally such
27 // that when screen is not changing the codec converges to the target quantizer 27 // that when screen is not changing the codec converges to the target quantizer
28 // above in less than 10 frames. 28 // above in less than 10 frames.
29 const int kVp8MinimumTargetBitrateKbpsPerMegapixel = 2500; 29 const int kVp8MinimumTargetBitrateKbpsPerMegapixel = 2500;
30 30
31 } // namespace 31 } // namespace
32 32
33 WebrtcFrameSchedulerSimple::WebrtcFrameSchedulerSimple() 33 WebrtcFrameSchedulerSimple::WebrtcFrameSchedulerSimple()
34 : frame_processing_delay_us_(kStatsWindow) {} 34 : pacing_bucket_(LeakyBucket::kUnlimitedDepth, 0),
35 frame_processing_delay_us_(kStatsWindow) {}
35 WebrtcFrameSchedulerSimple::~WebrtcFrameSchedulerSimple() {} 36 WebrtcFrameSchedulerSimple::~WebrtcFrameSchedulerSimple() {}
36 37
37 void WebrtcFrameSchedulerSimple::Start(const base::Closure& capture_callback) { 38 void WebrtcFrameSchedulerSimple::Start(const base::Closure& capture_callback) {
38 capture_callback_ = capture_callback; 39 capture_callback_ = capture_callback;
39 ScheduleNextFrame(); 40 ScheduleNextFrame(base::TimeTicks::Now());
40 } 41 }
41 42
42 void WebrtcFrameSchedulerSimple::Pause(bool pause) { 43 void WebrtcFrameSchedulerSimple::Pause(bool pause) {
43 paused_ = pause; 44 paused_ = pause;
44 if (paused_) { 45 if (paused_) {
45 capture_timer_.Stop(); 46 capture_timer_.Stop();
46 } else if (!capture_callback_.is_null()) { 47 } else if (!capture_callback_.is_null()) {
47 ScheduleNextFrame(); 48 ScheduleNextFrame(base::TimeTicks::Now());
48 } 49 }
49 } 50 }
50 51
51 void WebrtcFrameSchedulerSimple::SetKeyFrameRequest() { 52 void WebrtcFrameSchedulerSimple::SetKeyFrameRequest() {
52 key_frame_request_ = true; 53 key_frame_request_ = true;
53 } 54 }
54 55
55 void WebrtcFrameSchedulerSimple::SetTargetBitrate(int bitrate_kbps) { 56 void WebrtcFrameSchedulerSimple::SetTargetBitrate(int bitrate_kbps) {
56 target_bitrate_kbps_ = bitrate_kbps; 57 base::TimeTicks now = base::TimeTicks::Now();
58 pacing_bucket_.UpdateRate(bitrate_kbps * 1000 / 8, now);
59 ScheduleNextFrame(now);
57 } 60 }
58 61
59 bool WebrtcFrameSchedulerSimple::GetEncoderFrameParams( 62 bool WebrtcFrameSchedulerSimple::GetEncoderFrameParams(
60 const webrtc::DesktopFrame& frame, 63 const webrtc::DesktopFrame& frame,
61 WebrtcVideoEncoder::FrameParams* params_out) { 64 WebrtcVideoEncoder::FrameParams* params_out) {
65 base::TimeTicks now = base::TimeTicks::Now();
66
62 if (frame.updated_region().is_empty() && !top_off_is_active_ && 67 if (frame.updated_region().is_empty() && !top_off_is_active_ &&
63 !key_frame_request_) { 68 !key_frame_request_) {
64 ScheduleNextFrame(); 69 ScheduleNextFrame(now);
65 return false; 70 return false;
66 } 71 }
67 72
68 // TODO(sergeyu): This logic is applicable only to VP8. Reconsider it for VP9. 73 // TODO(sergeyu): This logic is applicable only to VP8. Reconsider it for VP9.
69 int minimum_bitrate = 74 int minimum_bitrate =
70 static_cast<uint64_t>(kVp8MinimumTargetBitrateKbpsPerMegapixel) * 75 static_cast<uint64_t>(kVp8MinimumTargetBitrateKbpsPerMegapixel) *
71 frame.size().width() * frame.size().height() / 1000000LL; 76 frame.size().width() * frame.size().height() / 1000000LL;
72 params_out->bitrate_kbps = std::max(minimum_bitrate, target_bitrate_kbps_); 77 params_out->bitrate_kbps =
78 std::max(minimum_bitrate, pacing_bucket_.rate() * 8 / 1000);
73 79
74 // TODO(sergeyu): Currently duration is always set to 1/15 of a second. 80 // TODO(sergeyu): Currently duration is always set to 1/15 of a second.
75 // Experiment with different values, and try changing it dynamically. 81 // Experiment with different values, and try changing it dynamically.
76 params_out->duration = base::TimeDelta::FromSeconds(1) / 15; 82 params_out->duration = base::TimeDelta::FromSeconds(1) / 15;
77 83
78 params_out->key_frame = key_frame_request_; 84 params_out->key_frame = key_frame_request_;
79 key_frame_request_ = false; 85 key_frame_request_ = false;
80 86
81 params_out->vpx_min_quantizer = 10; 87 params_out->vpx_min_quantizer = 10;
82 params_out->vpx_max_quantizer = 63; 88 params_out->vpx_max_quantizer = 63;
83 89
84 params_out->clear_active_map = !top_off_is_active_; 90 params_out->clear_active_map = !top_off_is_active_;
85 91
86 return true; 92 return true;
87 } 93 }
88 94
89 void WebrtcFrameSchedulerSimple::OnFrameEncoded( 95 void WebrtcFrameSchedulerSimple::OnFrameEncoded(
90 const WebrtcVideoEncoder::EncodedFrame& encoded_frame, 96 const WebrtcVideoEncoder::EncodedFrame& encoded_frame,
91 const webrtc::EncodedImageCallback::Result& send_result) { 97 const webrtc::EncodedImageCallback::Result& send_result) {
98 base::TimeTicks now = base::TimeTicks::Now();
99 pacing_bucket_.RefillOrSpill(encoded_frame.data.size(), now);
100
92 if (encoded_frame.data.empty()) { 101 if (encoded_frame.data.empty()) {
93 top_off_is_active_ = false; 102 top_off_is_active_ = false;
94 ScheduleNextFrame(); 103 } else {
95 return; 104 frame_processing_delay_us_.Record(
105 (now - last_capture_started_time_).InMicroseconds());
106
107 // Top-off until the target quantizer value is reached.
108 top_off_is_active_ = encoded_frame.quantizer > kTargetQuantizerForVp8TopOff;
96 } 109 }
97 110
98 frame_processing_delay_us_.Record( 111 ScheduleNextFrame(now);
99 (base::TimeTicks::Now() - last_capture_started_time_).InMicroseconds());
100
101 // Top-off until the target quantizer value is reached.
102 top_off_is_active_ = encoded_frame.quantizer > kTargetQuantizerForVp8TopOff;
103
104 // Capture next frame after we finish sending the current one.
105 const double kKiloBitsPerByte = 8.0 / 1000.0;
106 base::TimeDelta expected_send_delay = base::TimeDelta::FromSecondsD(
107 encoded_frame.data.size() * kKiloBitsPerByte / target_bitrate_kbps_);
108 last_frame_send_finish_time_ = base::TimeTicks::Now() + expected_send_delay;
109
110 ScheduleNextFrame();
111 } 112 }
112 113
113 void WebrtcFrameSchedulerSimple::ScheduleNextFrame() { 114 void WebrtcFrameSchedulerSimple::ScheduleNextFrame(base::TimeTicks now) {
114 if (paused_) 115 // Don't capture frames when paused or target bitrate is 0.
116 if (paused_ || pacing_bucket_.rate() == 0)
115 return; 117 return;
116 118
117 base::TimeTicks now = base::TimeTicks::Now();
118 base::TimeDelta delay;
119
120 // If this is not the first frame then capture next frame after the previous 119 // If this is not the first frame then capture next frame after the previous
121 // one has finished sending. 120 // one has finished sending.
122 if (!last_frame_send_finish_time_.is_null()) { 121 base::TimeDelta expected_processing_time =
123 base::TimeDelta expected_processing_time = 122 base::TimeDelta::FromMicroseconds(frame_processing_delay_us_.Max());
124 base::TimeDelta::FromMicroseconds(frame_processing_delay_us_.Max()); 123 base::TimeTicks target_capture_time =
125 delay = std::max(base::TimeDelta(), last_frame_send_finish_time_ - 124 pacing_bucket_.GetEmptyTime() - expected_processing_time;
126 expected_processing_time - now);
127 }
128 125
129 // Cap interval between frames to kTargetFrameInterval. 126 // Cap interval between frames to kTargetFrameInterval.
130 if (!last_capture_started_time_.is_null()) { 127 if (!last_capture_started_time_.is_null()) {
131 delay = std::max(delay, 128 target_capture_time = std::max(
132 last_capture_started_time_ + kTargetFrameInterval - now); 129 target_capture_time, last_capture_started_time_ + kTargetFrameInterval);
133 } 130 }
134 131
135 capture_timer_.Start(FROM_HERE, delay, 132 if (target_capture_time < now)
133 target_capture_time = now;
134
135 capture_timer_.Start(FROM_HERE, target_capture_time - now,
136 base::Bind(&WebrtcFrameSchedulerSimple::CaptureNextFrame, 136 base::Bind(&WebrtcFrameSchedulerSimple::CaptureNextFrame,
137 base::Unretained(this))); 137 base::Unretained(this)));
138 } 138 }
139 139
140 void WebrtcFrameSchedulerSimple::CaptureNextFrame() { 140 void WebrtcFrameSchedulerSimple::CaptureNextFrame() {
141 last_capture_started_time_ = base::TimeTicks::Now(); 141 last_capture_started_time_ = base::TimeTicks::Now();
142 capture_callback_.Run(); 142 capture_callback_.Run();
143 } 143 }
144 144
145 } // namespace protocol 145 } // namespace protocol
146 } // namespace remoting 146 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/protocol/webrtc_frame_scheduler_simple.h ('k') | remoting/test/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698