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

Side by Side Diff: webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc

Issue 1193513006: In screenshare mode, suppress VP8 bitrate overshoot and increase quality (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Created 5 years, 6 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
OLDNEW
1 /* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 1 /* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
2 * 2 *
3 * Use of this source code is governed by a BSD-style license 3 * Use of this source code is governed by a BSD-style license
4 * that can be found in the LICENSE file in the root of the source 4 * that can be found in the LICENSE file in the root of the source
5 * tree. An additional intellectual property rights grant can be found 5 * tree. An additional intellectual property rights grant can be found
6 * in the file PATENTS. All contributing project authors may 6 * in the file PATENTS. All contributing project authors may
7 * be found in the AUTHORS file in the root of the source tree. 7 * be found in the AUTHORS file in the root of the source tree.
8 */ 8 */
9 9
10 #include "webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h" 10 #include "webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h"
11 11
12 #include <stdlib.h> 12 #include <stdlib.h>
13 13
14 #include "webrtc/base/checks.h"
14 #include "vpx/vpx_encoder.h" 15 #include "vpx/vpx_encoder.h"
15 #include "vpx/vp8cx.h" 16 #include "vpx/vp8cx.h"
16 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" 17 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
17 18
18 namespace webrtc { 19 namespace webrtc {
19 20
20 enum { kOneSecond90Khz = 90000 }; 21 static const int kOneSecond90Khz = 90000;
22 static const int kMinTimeBetweenSyncs = kOneSecond90Khz * 5;
23 static const int kMaxTimeBetweenSyncs = kOneSecond90Khz * 10;
24 static const int kMaxQpDeltaForSync = 8;
21 25
22 const double ScreenshareLayers::kMaxTL0FpsReduction = 2.5; 26 const double ScreenshareLayers::kMaxTL0FpsReduction = 2.5;
23 const double ScreenshareLayers::kAcceptableTargetOvershoot = 2.0; 27 const double ScreenshareLayers::kAcceptableTargetOvershoot = 2.0;
24 28
25 ScreenshareLayers::ScreenshareLayers(int num_temporal_layers, 29 ScreenshareLayers::ScreenshareLayers(int num_temporal_layers,
26 uint8_t initial_tl0_pic_idx, 30 uint8_t initial_tl0_pic_idx)
27 FrameDropper* tl0_frame_dropper, 31 : number_of_temporal_layers_(num_temporal_layers),
28 FrameDropper* tl1_frame_dropper)
29 : tl0_frame_dropper_(tl0_frame_dropper),
30 tl1_frame_dropper_(tl1_frame_dropper),
31 number_of_temporal_layers_(num_temporal_layers),
32 last_base_layer_sync_(false), 32 last_base_layer_sync_(false),
33 tl0_pic_idx_(initial_tl0_pic_idx), 33 tl0_pic_idx_(initial_tl0_pic_idx),
34 active_layer_(0), 34 active_layer_(0),
35 framerate_(5), 35 last_timestamp_(0),
36 last_sync_timestamp_(-1) { 36 last_sync_timestamp_(0),
37 min_qp_(-1),
38 max_qp_(-1),
39 max_debt_bytes_(0) {
37 assert(num_temporal_layers > 0); 40 assert(num_temporal_layers > 0);
38 assert(num_temporal_layers <= 2); 41 assert(num_temporal_layers <= 2);
39 assert(tl0_frame_dropper && tl1_frame_dropper);
40 } 42 }
41 43
42 int ScreenshareLayers::CurrentLayerId() const { 44 int ScreenshareLayers::CurrentLayerId() const {
43 // Codec does not use temporal layers for screenshare. 45 // Codec does not use temporal layers for screenshare.
44 return 0; 46 return 0;
45 } 47 }
46 48
47 int ScreenshareLayers::EncodeFlags(uint32_t timestamp) { 49 int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
48 if (number_of_temporal_layers_ <= 1) { 50 if (number_of_temporal_layers_ <= 1) {
49 // No flags needed for 1 layer screenshare. 51 // No flags needed for 1 layer screenshare.
50 return 0; 52 return 0;
51 } 53 }
52 CalculateFramerate(timestamp); 54
53 int flags = 0; 55 int flags = 0;
54 // Note that ARF on purpose isn't used in this scheme since it is allocated 56 int temporal_layer;
55 // for the last key frame to make key frame caching possible. 57
56 if (tl0_frame_dropper_->DropFrame()) { 58 if (active_layer_ != -1 &&
57 // Must drop TL0, encode TL1 instead. 59 layers_[active_layer_].state == TemporalLayer::State::kDropped) {
58 if (tl1_frame_dropper_->DropFrame()) { 60 temporal_layer = active_layer_;
59 // Must drop both TL0 and TL1. 61 } else {
60 flags = -1; 62 if (layers_[0].debt_bytes_ > max_debt_bytes_) {
63 // Must drop TL0, encode TL1 instead.
64 if (layers_[1].debt_bytes_ > max_debt_bytes_) {
65 // Must drop both TL0 and TL1.
66 temporal_layer = -1;
67 } else {
68 temporal_layer = 1;
69 }
61 } else { 70 } else {
62 active_layer_ = 1; 71 temporal_layer = 0;
72 }
73 }
74
75 switch (temporal_layer) {
76 case 0:
77 // Since this is TL0 we only allow updating and predicting from the LAST
78 // reference frame.
79 flags = VP8_EFLAG_NO_UPD_GF;
80 flags |= VP8_EFLAG_NO_UPD_ARF;
81 flags |= VP8_EFLAG_NO_REF_GF;
82 flags |= VP8_EFLAG_NO_REF_ARF;
83 break;
84 case 1:
63 if (TimeToSync(timestamp)) { 85 if (TimeToSync(timestamp)) {
64 last_sync_timestamp_ = timestamp; 86 last_sync_timestamp_ = timestamp;
65 // Allow predicting from only TL0 to allow participants to switch to the 87 // Allow predicting from only TL0 to allow participants to switch to the
66 // high bitrate stream. This means predicting only from the LAST 88 // high bitrate stream. This means predicting only from the LAST
67 // reference frame, but only updating GF to not corrupt TL0. 89 // reference frame, but only updating GF to not corrupt TL0.
68 flags = VP8_EFLAG_NO_REF_ARF; 90 flags = VP8_EFLAG_NO_REF_ARF;
69 flags |= VP8_EFLAG_NO_REF_GF; 91 flags |= VP8_EFLAG_NO_REF_GF;
70 flags |= VP8_EFLAG_NO_UPD_ARF; 92 flags |= VP8_EFLAG_NO_UPD_ARF;
71 flags |= VP8_EFLAG_NO_UPD_LAST; 93 flags |= VP8_EFLAG_NO_UPD_LAST;
72 } else { 94 } else {
73 // Allow predicting from both TL0 and TL1. 95 // Allow predicting from both TL0 and TL1.
74 flags = VP8_EFLAG_NO_REF_ARF; 96 flags = VP8_EFLAG_NO_REF_ARF;
75 flags |= VP8_EFLAG_NO_UPD_ARF; 97 flags |= VP8_EFLAG_NO_UPD_ARF;
76 flags |= VP8_EFLAG_NO_UPD_LAST; 98 flags |= VP8_EFLAG_NO_UPD_LAST;
77 } 99 }
78 } 100 break;
79 } else { 101 case -1:
80 active_layer_ = 0; 102 flags = -1;
81 // Since this is TL0 we only allow updating and predicting from the LAST 103 break;
82 // reference frame. 104 default:
83 flags = VP8_EFLAG_NO_UPD_GF; 105 flags = -1;
84 flags |= VP8_EFLAG_NO_UPD_ARF; 106 RTC_NOTREACHED();
85 flags |= VP8_EFLAG_NO_REF_GF;
86 flags |= VP8_EFLAG_NO_REF_ARF;
87 } 107 }
108
109 active_layer_ = temporal_layer;
110
88 // Make sure both frame droppers leak out bits. 111 // Make sure both frame droppers leak out bits.
89 tl0_frame_dropper_->Leak(framerate_); 112 if (last_timestamp_ != 0) {
90 tl1_frame_dropper_->Leak(framerate_); 113 int64_t ts_diff = timestamp - static_cast<int64_t>(last_timestamp_);
114 if (ts_diff < 0)
115 ts_diff += (1l << 32);
116
117 layers_[0].UpdateDebt(ts_diff / 90);
118 layers_[1].UpdateDebt(ts_diff / 90);
119 }
120 last_timestamp_ = timestamp;
91 return flags; 121 return flags;
92 } 122 }
93 123
94 bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbit, 124 bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbit,
95 int max_bitrate_kbit, 125 int max_bitrate_kbit,
stefan-webrtc 2015/06/18 09:39:02 Should these say kbps instead?
sprang_webrtc 2015/06/18 13:05:02 Done.
96 int framerate, 126 int framerate,
97 vpx_codec_enc_cfg_t* cfg) { 127 vpx_codec_enc_cfg_t* cfg) {
98 if (framerate > 0) 128 int avg_frame_size = (bitrate_kbit * 1000) / (8 * framerate);
99 framerate_ = framerate; 129 max_debt_bytes_ = 5 * avg_frame_size;
100 130
101 tl0_frame_dropper_->SetRates(bitrate_kbit, framerate_); 131 layers_[0].target_rate_kbps_ = bitrate_kbit;
102 tl1_frame_dropper_->SetRates(max_bitrate_kbit, framerate_); 132 layers_[1].target_rate_kbps_ = max_bitrate_kbit;
103 133
104 if (cfg != nullptr) { 134 if (cfg != nullptr) {
105 // Calculate a codec target bitrate. This may be higher than TL0, gaining 135 // Calculate a codec target bitrate. This may be higher than TL0, gaining
106 // quality at the expense of frame rate at TL0. Constraints: 136 // quality at the expense of frame rate at TL0. Constraints:
107 // - TL0 frame rate should not be less than framerate / kMaxTL0FpsReduction. 137 // - TL0 frame rate should not be less than framerate / kMaxTL0FpsReduction.
108 // - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate. 138 // - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate.
109 double target_bitrate = 139 double target_bitrate =
110 std::min(bitrate_kbit * kMaxTL0FpsReduction, 140 std::min(bitrate_kbit * kMaxTL0FpsReduction,
111 max_bitrate_kbit / kAcceptableTargetOvershoot); 141 max_bitrate_kbit / kAcceptableTargetOvershoot);
142
143 if (layers_[active_layer_].state != TemporalLayer::State::kQualityBoost) {
144 min_qp_ = cfg->rc_min_quantizer;
145 max_qp_ = cfg->rc_max_quantizer;
146 layers_[0].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 80) / 100);
147 layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100);
stefan-webrtc 2015/06/18 09:39:02 80% more in the lower layer and 85% more in the hi
sprang_webrtc 2015/06/18 13:05:02 Done.
148 }
149
112 cfg->rc_target_bitrate = 150 cfg->rc_target_bitrate =
113 std::max(static_cast<unsigned int>(bitrate_kbit), 151 std::max(static_cast<unsigned int>(bitrate_kbit),
114 static_cast<unsigned int>(target_bitrate + 0.5)); 152 static_cast<unsigned int>(target_bitrate + 0.5));
115 } 153 }
116 154
117 return true; 155 return true;
118 } 156 }
119 157
120 void ScreenshareLayers::FrameEncoded(unsigned int size, uint32_t timestamp) { 158 void ScreenshareLayers::FrameEncoded(unsigned int size,
121 if (active_layer_ == 0) { 159 uint32_t timestamp,
122 tl0_frame_dropper_->Fill(size, true); 160 int qp) {
161 if (size == 0) {
162 layers_[active_layer_].state = TemporalLayer::State::kDropped;
163 return;
123 } 164 }
124 tl1_frame_dropper_->Fill(size, true); 165 if (layers_[active_layer_].state == TemporalLayer::State::kDropped) {
166 layers_[active_layer_].state = TemporalLayer::State::kQualityBoost;
stefan-webrtc 2015/06/18 09:39:02 This means we always boost quality after a drop? D
sprang_webrtc 2015/06/18 13:05:02 The rate control will be reset to start at max qp,
167 }
168
169 if (qp != -1)
170 layers_[active_layer_].last_qp = qp;
171 if (active_layer_ == 0)
172 layers_[0].debt_bytes_ += size;
173 layers_[1].debt_bytes_ += size;
125 } 174 }
126 175
127 void ScreenshareLayers::PopulateCodecSpecific(bool base_layer_sync, 176 void ScreenshareLayers::PopulateCodecSpecific(bool base_layer_sync,
128 CodecSpecificInfoVP8 *vp8_info, 177 CodecSpecificInfoVP8 *vp8_info,
129 uint32_t timestamp) { 178 uint32_t timestamp) {
130 if (number_of_temporal_layers_ == 1) { 179 if (number_of_temporal_layers_ == 1) {
131 vp8_info->temporalIdx = kNoTemporalIdx; 180 vp8_info->temporalIdx = kNoTemporalIdx;
132 vp8_info->layerSync = false; 181 vp8_info->layerSync = false;
133 vp8_info->tl0PicIdx = kNoTl0PicIdx; 182 vp8_info->tl0PicIdx = kNoTl0PicIdx;
134 } else { 183 } else {
(...skipping 10 matching lines...) Expand all
145 if (vp8_info->temporalIdx == 0) { 194 if (vp8_info->temporalIdx == 0) {
146 tl0_pic_idx_++; 195 tl0_pic_idx_++;
147 } 196 }
148 last_base_layer_sync_ = base_layer_sync; 197 last_base_layer_sync_ = base_layer_sync;
149 vp8_info->tl0PicIdx = tl0_pic_idx_; 198 vp8_info->tl0PicIdx = tl0_pic_idx_;
150 } 199 }
151 } 200 }
152 201
153 bool ScreenshareLayers::TimeToSync(uint32_t timestamp) const { 202 bool ScreenshareLayers::TimeToSync(uint32_t timestamp) const {
154 const uint32_t timestamp_diff = timestamp - last_sync_timestamp_; 203 const uint32_t timestamp_diff = timestamp - last_sync_timestamp_;
155 return last_sync_timestamp_ < 0 || timestamp_diff > kOneSecond90Khz; 204 if (last_sync_timestamp_ == 0 || timestamp_diff > kMaxTimeBetweenSyncs)
205 return true;
206 if (layers_[0].last_qp == -1 || layers_[1].last_qp == -1)
stefan-webrtc 2015/06/18 09:39:02 This means that the first frame should be a sync f
sprang_webrtc 2015/06/18 13:05:02 Done.
207 return true;
208 if (timestamp_diff < kMinTimeBetweenSyncs)
209 return false;
210 if (layers_[1].last_qp - layers_[0].last_qp < kMaxQpDeltaForSync)
211 return true;
212 return false;
156 } 213 }
157 214
158 void ScreenshareLayers::CalculateFramerate(uint32_t timestamp) { 215 bool ScreenshareLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
159 timestamp_list_.push_front(timestamp); 216 if (max_qp_ == -1)
160 // Remove timestamps older than 1 second from the list. 217 return false;
161 uint32_t timestamp_diff = timestamp - timestamp_list_.back(); 218
162 while (timestamp_diff > kOneSecond90Khz) { 219 unsigned int adjusted_max_qp;
163 timestamp_list_.pop_back(); 220 if (layers_[active_layer_].state == TemporalLayer::State::kQualityBoost &&
stefan-webrtc 2015/06/18 09:39:02 Comment to describe what you are doing with this i
sprang_webrtc 2015/06/18 13:05:02 Done.
164 timestamp_diff = timestamp - timestamp_list_.back(); 221 layers_[active_layer_].enhanced_max_qp != -1) {
222 adjusted_max_qp = layers_[active_layer_].enhanced_max_qp;
223 layers_[active_layer_].state = TemporalLayer::State::kNormal;
224 } else {
225 if (max_qp_ == -1)
226 return false;
227 adjusted_max_qp = max_qp_;
165 } 228 }
166 // If we have encoded frames within the last second, that number of frames 229
167 // is a reasonable first estimate of the framerate. 230 if (adjusted_max_qp == cfg->rc_max_quantizer)
168 framerate_ = timestamp_list_.size(); 231 return false;
169 if (timestamp_diff > 0) { 232
170 // Estimate the framerate by dividing the number of timestamp diffs with 233 cfg->rc_max_quantizer = adjusted_max_qp;
171 // the sum of the timestamp diffs (with rounding). 234 return true;
172 framerate_ = (kOneSecond90Khz * (timestamp_list_.size() - 1) + 235 }
173 timestamp_diff / 2) / timestamp_diff; 236
237 void ScreenshareLayers::TemporalLayer::UpdateDebt(int64_t delta_ms) {
238 uint32_t debt_reduction_bytes = target_rate_kbps_ * delta_ms / 8;
239 if (debt_reduction_bytes >= debt_bytes_) {
240 debt_bytes_ = 0;
241 } else {
242 debt_bytes_ -= debt_reduction_bytes;
174 } 243 }
175 } 244 }
176 245
177 } // namespace webrtc 246 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698