Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| 11 namespace remoting { | 11 namespace remoting { |
| 12 namespace protocol { | 12 namespace protocol { |
| 13 | 13 |
| 14 namespace { | 14 namespace { |
| 15 | 15 |
| 16 // Number of samples used to estimate processing time for the next frame. | 16 // Number of samples used to estimate processing time for the next frame. |
| 17 const int kStatsWindow = 5; | 17 const int kStatsWindow = 5; |
| 18 | 18 |
| 19 const int kTargetFrameRate = 30; | 19 const int kTargetFrameRate = 30; |
| 20 constexpr base::TimeDelta kTargetFrameInterval = | 20 constexpr base::TimeDelta kTargetFrameInterval = |
| 21 base::TimeDelta::FromMilliseconds(1000 / kTargetFrameRate); | 21 base::TimeDelta::FromMilliseconds(1000 / kTargetFrameRate); |
| 22 | 22 |
| 23 // Target quantizer at which stop the encoding top-off. | 23 // Target quantizer at which stop the encoding top-off. |
| 24 const int kTargetQuantizerForVp8TopOff = 30; | 24 const int kTargetQuantizerForVp8TopOff = 30; |
| 25 | 25 |
| 26 const int64_t kPixelsPerMegapixel = 1000000; | |
| 27 | |
| 26 // Minimum target bitrate per megapixel. The value is chosen experimentally such | 28 // 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 | 29 // that when screen is not changing the codec converges to the target quantizer |
| 28 // above in less than 10 frames. | 30 // above in less than 10 frames. |
| 29 const int kVp8MinimumTargetBitrateKbpsPerMegapixel = 2500; | 31 const int kVp8MinimumTargetBitrateBytesPerMegapixel = 300000; |
|
Irfan
2016/10/03 20:21:49
"BytesPerMegapixel" sounds like size. I do not und
Sergey Ulanov
2016/10/06 21:54:34
The correct units would be bytes/(megapixel*second
| |
| 32 | |
| 33 // Estimated size in bytes per megapixel of encoded frame at target quality | |
| 34 // level. | |
| 35 const int kEstimatedBytesPerMegapixel = 150000; | |
|
Irfan
2016/10/03 20:21:49
Clarify what is "target quality" here ?
Sergey Ulanov
2016/10/06 21:54:34
Done.
| |
| 36 | |
| 37 int64_t GetRegionArea(const webrtc::DesktopRegion& region) { | |
| 38 int64_t result = 0; | |
| 39 for (webrtc::DesktopRegion::Iterator r(region); !r.IsAtEnd(); r.Advance()) { | |
| 40 result += r.rect().width() * r.rect().height(); | |
| 41 } | |
| 42 return result; | |
| 43 } | |
| 30 | 44 |
| 31 } // namespace | 45 } // namespace |
| 32 | 46 |
| 33 WebrtcFrameSchedulerSimple::WebrtcFrameSchedulerSimple() | 47 WebrtcFrameSchedulerSimple::WebrtcFrameSchedulerSimple() |
| 34 : pacing_bucket_(LeakyBucket::kUnlimitedDepth, 0), | 48 : pacing_bucket_(LeakyBucket::kUnlimitedDepth, 0), |
| 35 frame_processing_delay_us_(kStatsWindow) {} | 49 frame_processing_delay_us_(kStatsWindow), |
| 50 encoded_frame_size_(kStatsWindow) {} | |
|
Irfan
2016/10/03 20:21:49
may be encoded_frame_size_bytes_ ?
Sergey Ulanov
2016/10/06 21:54:34
Removed it now and replaced with updated_region_ar
| |
| 36 WebrtcFrameSchedulerSimple::~WebrtcFrameSchedulerSimple() {} | 51 WebrtcFrameSchedulerSimple::~WebrtcFrameSchedulerSimple() {} |
| 37 | 52 |
| 38 void WebrtcFrameSchedulerSimple::Start(const base::Closure& capture_callback) { | 53 void WebrtcFrameSchedulerSimple::Start(const base::Closure& capture_callback) { |
| 39 capture_callback_ = capture_callback; | 54 capture_callback_ = capture_callback; |
| 40 ScheduleNextFrame(base::TimeTicks::Now()); | 55 ScheduleNextFrame(base::TimeTicks::Now()); |
| 41 } | 56 } |
| 42 | 57 |
| 43 void WebrtcFrameSchedulerSimple::Pause(bool pause) { | 58 void WebrtcFrameSchedulerSimple::Pause(bool pause) { |
| 44 paused_ = pause; | 59 paused_ = pause; |
| 45 if (paused_) { | 60 if (paused_) { |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 64 WebrtcVideoEncoder::FrameParams* params_out) { | 79 WebrtcVideoEncoder::FrameParams* params_out) { |
| 65 base::TimeTicks now = base::TimeTicks::Now(); | 80 base::TimeTicks now = base::TimeTicks::Now(); |
| 66 | 81 |
| 67 if (frame.updated_region().is_empty() && !top_off_is_active_ && | 82 if (frame.updated_region().is_empty() && !top_off_is_active_ && |
| 68 !key_frame_request_) { | 83 !key_frame_request_) { |
| 69 ScheduleNextFrame(now); | 84 ScheduleNextFrame(now); |
| 70 return false; | 85 return false; |
| 71 } | 86 } |
| 72 | 87 |
| 73 // TODO(sergeyu): This logic is applicable only to VP8. Reconsider it for VP9. | 88 // TODO(sergeyu): This logic is applicable only to VP8. Reconsider it for VP9. |
| 74 int minimum_bitrate = | 89 int minimum_bitrate_bytes_per_second = |
| 75 static_cast<uint64_t>(kVp8MinimumTargetBitrateKbpsPerMegapixel) * | 90 kVp8MinimumTargetBitrateBytesPerMegapixel * frame.size().width() * |
| 76 frame.size().width() * frame.size().height() / 1000000LL; | 91 frame.size().height() / kPixelsPerMegapixel; |
| 77 params_out->bitrate_kbps = | 92 int bitrate_bytes_per_second = |
| 78 std::max(minimum_bitrate, pacing_bucket_.rate() * 8 / 1000); | 93 std::max(minimum_bitrate_bytes_per_second, pacing_bucket_.rate()); |
| 94 params_out->bitrate_kbps = bitrate_bytes_per_second * 8 / 1000; | |
| 79 | 95 |
| 80 // TODO(sergeyu): Currently duration is always set to 1/15 of a second. | 96 params_out->duration = kTargetFrameInterval; |
| 81 // Experiment with different values, and try changing it dynamically. | |
| 82 params_out->duration = base::TimeDelta::FromSeconds(1) / 15; | |
| 83 | |
| 84 params_out->key_frame = key_frame_request_; | 97 params_out->key_frame = key_frame_request_; |
| 85 key_frame_request_ = false; | 98 key_frame_request_ = false; |
| 86 | 99 |
| 87 params_out->vpx_min_quantizer = 10; | 100 params_out->vpx_min_quantizer = 10; |
| 101 | |
| 102 // If bandwidth is being underutilized then libvpx is likely to choose the | |
| 103 // minimum allowed quantizer value, which means that encoded frame size may be | |
| 104 // significantly bigger than the bandwidth allows. Detect this case and set | |
| 105 // vpx_min_quantizer to 60. The quality will be topped off later. | |
| 106 if (encoded_frame_size_.Average() * kTargetFrameRate < | |
|
Irfan
2016/10/03 20:21:49
Should we measure the frame rate to get real usage
Sergey Ulanov
2016/10/06 21:54:34
No. What matters here is the bitrate that libvpx o
| |
| 107 bitrate_bytes_per_second / 2) { | |
| 108 int expected_frame_size = GetRegionArea(frame.updated_region()) * | |
| 109 kEstimatedBytesPerMegapixel / kPixelsPerMegapixel; | |
| 110 base::TimeDelta expected_send_delay = base::TimeDelta::FromMicroseconds( | |
| 111 base::Time::kMicrosecondsPerSecond * expected_frame_size / | |
| 112 pacing_bucket_.rate()); | |
| 113 if (expected_send_delay > kTargetFrameInterval) | |
| 114 params_out->vpx_min_quantizer = 60; | |
| 115 } | |
| 116 | |
| 88 params_out->vpx_max_quantizer = 63; | 117 params_out->vpx_max_quantizer = 63; |
| 89 | 118 |
| 90 params_out->clear_active_map = !top_off_is_active_; | 119 params_out->clear_active_map = !top_off_is_active_; |
| 91 | 120 |
| 92 return true; | 121 return true; |
| 93 } | 122 } |
| 94 | 123 |
| 95 void WebrtcFrameSchedulerSimple::OnFrameEncoded( | 124 void WebrtcFrameSchedulerSimple::OnFrameEncoded( |
| 96 const WebrtcVideoEncoder::EncodedFrame& encoded_frame, | 125 const WebrtcVideoEncoder::EncodedFrame& encoded_frame, |
| 97 const webrtc::EncodedImageCallback::Result& send_result) { | 126 const webrtc::EncodedImageCallback::Result& send_result) { |
| 98 base::TimeTicks now = base::TimeTicks::Now(); | 127 base::TimeTicks now = base::TimeTicks::Now(); |
| 99 pacing_bucket_.RefillOrSpill(encoded_frame.data.size(), now); | 128 pacing_bucket_.RefillOrSpill(encoded_frame.data.size(), now); |
| 129 encoded_frame_size_.Record(encoded_frame.data.size()); | |
| 100 | 130 |
| 101 if (encoded_frame.data.empty()) { | 131 if (encoded_frame.data.empty()) { |
| 102 top_off_is_active_ = false; | 132 top_off_is_active_ = false; |
| 103 } else { | 133 } else { |
| 104 frame_processing_delay_us_.Record( | 134 frame_processing_delay_us_.Record( |
| 105 (now - last_capture_started_time_).InMicroseconds()); | 135 (now - last_capture_started_time_).InMicroseconds()); |
| 106 | 136 |
| 107 // Top-off until the target quantizer value is reached. | 137 // Top-off until the target quantizer value is reached. |
| 108 top_off_is_active_ = encoded_frame.quantizer > kTargetQuantizerForVp8TopOff; | 138 top_off_is_active_ = encoded_frame.quantizer > kTargetQuantizerForVp8TopOff; |
| 109 } | 139 } |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 137 base::Unretained(this))); | 167 base::Unretained(this))); |
| 138 } | 168 } |
| 139 | 169 |
| 140 void WebrtcFrameSchedulerSimple::CaptureNextFrame() { | 170 void WebrtcFrameSchedulerSimple::CaptureNextFrame() { |
| 141 last_capture_started_time_ = base::TimeTicks::Now(); | 171 last_capture_started_time_ = base::TimeTicks::Now(); |
| 142 capture_callback_.Run(); | 172 capture_callback_.Run(); |
| 143 } | 173 } |
| 144 | 174 |
| 145 } // namespace protocol | 175 } // namespace protocol |
| 146 } // namespace remoting | 176 } // namespace remoting |
| OLD | NEW |