OLD | NEW |
---|---|
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; | |
stefan-webrtc
2015/06/23 14:28:32
It's a bit strange to call this Max, since it can
sprang_webrtc
2015/06/23 15:07:30
kQpDeltaThersholdForSync?
stefan-webrtc
2015/06/23 15:45:13
No, but kQpDeltaThresholdForSync sounds good. :)
sprang_webrtc
2015/06/24 07:53:43
Done.
| |
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 |
29 // Since this is TL0 we only allow updating and predicting from the LAST | |
30 // reference frame. | |
31 const int ScreenshareLayers::kTl0Flags = | |
32 VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF | | |
33 VP8_EFLAG_NO_REF_ARF; | |
34 | |
35 // Allow predicting from both TL0 and TL1. | |
36 const int ScreenshareLayers::kTl1Flags = | |
37 VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST; | |
38 | |
39 // Allow predicting from only TL0 to allow participants to switch to the high | |
40 // bitrate stream. This means predicting only from the LAST reference frame, but | |
41 // only updating GF to not corrupt TL0. | |
42 const int ScreenshareLayers::kTl1SyncFlags = | |
43 VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF | | |
44 VP8_EFLAG_NO_UPD_LAST; | |
45 | |
25 ScreenshareLayers::ScreenshareLayers(int num_temporal_layers, | 46 ScreenshareLayers::ScreenshareLayers(int num_temporal_layers, |
26 uint8_t initial_tl0_pic_idx, | 47 uint8_t initial_tl0_pic_idx) |
27 FrameDropper* tl0_frame_dropper, | 48 : 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), | 49 last_base_layer_sync_(false), |
33 tl0_pic_idx_(initial_tl0_pic_idx), | 50 tl0_pic_idx_(initial_tl0_pic_idx), |
34 active_layer_(0), | 51 active_layer_(-1), |
35 framerate_(5), | 52 last_timestamp_(0), |
36 last_sync_timestamp_(-1) { | 53 last_sync_timestamp_(0), |
54 min_qp_(-1), | |
55 max_qp_(-1), | |
56 max_debt_bytes_(0), | |
57 frame_rate_(-1) { | |
37 assert(num_temporal_layers > 0); | 58 assert(num_temporal_layers > 0); |
38 assert(num_temporal_layers <= 2); | 59 assert(num_temporal_layers <= 2); |
39 assert(tl0_frame_dropper && tl1_frame_dropper); | |
40 } | 60 } |
41 | 61 |
42 int ScreenshareLayers::CurrentLayerId() const { | 62 int ScreenshareLayers::CurrentLayerId() const { |
43 // Codec does not use temporal layers for screenshare. | 63 // Codec does not use temporal layers for screenshare. |
44 return 0; | 64 return 0; |
45 } | 65 } |
46 | 66 |
47 int ScreenshareLayers::EncodeFlags(uint32_t timestamp) { | 67 int ScreenshareLayers::EncodeFlags(uint32_t timestamp) { |
48 if (number_of_temporal_layers_ <= 1) { | 68 if (number_of_temporal_layers_ <= 1) { |
49 // No flags needed for 1 layer screenshare. | 69 // No flags needed for 1 layer screenshare. |
50 return 0; | 70 return 0; |
51 } | 71 } |
52 CalculateFramerate(timestamp); | 72 |
53 int flags = 0; | 73 int flags = 0; |
54 // Note that ARF on purpose isn't used in this scheme since it is allocated | 74 |
55 // for the last key frame to make key frame caching possible. | 75 if (active_layer_ == -1 || |
56 if (tl0_frame_dropper_->DropFrame()) { | 76 layers_[active_layer_].state != TemporalLayer::State::kDropped) { |
57 // Must drop TL0, encode TL1 instead. | 77 if (layers_[0].debt_bytes_ > max_debt_bytes_) { |
58 if (tl1_frame_dropper_->DropFrame()) { | 78 // Must drop TL0, encode TL1 instead. |
59 // Must drop both TL0 and TL1. | 79 if (layers_[1].debt_bytes_ > max_debt_bytes_) { |
60 flags = -1; | 80 // Must drop both TL0 and TL1. |
81 active_layer_ = -1; | |
82 } else { | |
83 active_layer_ = 1; | |
84 } | |
61 } else { | 85 } else { |
62 active_layer_ = 1; | 86 active_layer_ = 0; |
87 } | |
88 } | |
89 | |
90 switch (active_layer_) { | |
91 case 0: | |
92 flags = kTl0Flags; | |
93 break; | |
94 case 1: | |
63 if (TimeToSync(timestamp)) { | 95 if (TimeToSync(timestamp)) { |
64 last_sync_timestamp_ = timestamp; | 96 last_sync_timestamp_ = timestamp; |
65 // Allow predicting from only TL0 to allow participants to switch to the | 97 flags = kTl1SyncFlags; |
66 // high bitrate stream. This means predicting only from the LAST | |
67 // reference frame, but only updating GF to not corrupt TL0. | |
68 flags = VP8_EFLAG_NO_REF_ARF; | |
69 flags |= VP8_EFLAG_NO_REF_GF; | |
70 flags |= VP8_EFLAG_NO_UPD_ARF; | |
71 flags |= VP8_EFLAG_NO_UPD_LAST; | |
72 } else { | 98 } else { |
73 // Allow predicting from both TL0 and TL1. | 99 flags = kTl1Flags; |
74 flags = VP8_EFLAG_NO_REF_ARF; | |
75 flags |= VP8_EFLAG_NO_UPD_ARF; | |
76 flags |= VP8_EFLAG_NO_UPD_LAST; | |
77 } | 100 } |
78 } | 101 break; |
102 case -1: | |
103 flags = -1; | |
104 break; | |
105 default: | |
106 flags = -1; | |
107 RTC_NOTREACHED(); | |
108 } | |
109 | |
110 // Make sure both frame droppers leak out bits. | |
111 int64_t ts_diff; | |
112 if (last_timestamp_ == 0) { | |
stefan-webrtc
2015/06/23 14:28:32
last_timestamp_ can become 0 mid stream, so it wou
sprang_webrtc
2015/06/23 15:07:30
Done.
Also switched to using rtc::TimestampWrapAro
| |
113 ts_diff = kOneSecond90Khz / (frame_rate_ <= 0 ? 5 : frame_rate_); | |
79 } else { | 114 } else { |
80 active_layer_ = 0; | 115 ts_diff = timestamp - static_cast<int64_t>(last_timestamp_); |
81 // Since this is TL0 we only allow updating and predicting from the LAST | |
82 // reference frame. | |
83 flags = VP8_EFLAG_NO_UPD_GF; | |
84 flags |= VP8_EFLAG_NO_UPD_ARF; | |
85 flags |= VP8_EFLAG_NO_REF_GF; | |
86 flags |= VP8_EFLAG_NO_REF_ARF; | |
87 } | 116 } |
88 // Make sure both frame droppers leak out bits. | 117 if (ts_diff < 0) |
89 tl0_frame_dropper_->Leak(framerate_); | 118 ts_diff += (1l << 32); |
90 tl1_frame_dropper_->Leak(framerate_); | 119 |
120 layers_[0].UpdateDebt(ts_diff / 90); | |
121 layers_[1].UpdateDebt(ts_diff / 90); | |
122 last_timestamp_ = timestamp; | |
91 return flags; | 123 return flags; |
92 } | 124 } |
93 | 125 |
94 bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbit, | 126 bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbps, |
95 int max_bitrate_kbit, | 127 int max_bitrate_kbps, |
96 int framerate, | 128 int framerate, |
97 vpx_codec_enc_cfg_t* cfg) { | 129 vpx_codec_enc_cfg_t* cfg) { |
98 if (framerate > 0) | 130 layers_[0].target_rate_kbps_ = bitrate_kbps; |
99 framerate_ = framerate; | 131 layers_[1].target_rate_kbps_ = max_bitrate_kbps; |
100 | 132 |
101 tl0_frame_dropper_->SetRates(bitrate_kbit, framerate_); | 133 int target_bitrate_kbps = bitrate_kbps; |
102 tl1_frame_dropper_->SetRates(max_bitrate_kbit, framerate_); | |
103 | 134 |
104 if (cfg != nullptr) { | 135 if (cfg != nullptr) { |
105 // Calculate a codec target bitrate. This may be higher than TL0, gaining | 136 // Calculate a codec target bitrate. This may be higher than TL0, gaining |
106 // quality at the expense of frame rate at TL0. Constraints: | 137 // quality at the expense of frame rate at TL0. Constraints: |
107 // - TL0 frame rate should not be less than framerate / kMaxTL0FpsReduction. | 138 // - TL0 frame rate should not be less than framerate / kMaxTL0FpsReduction. |
108 // - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate. | 139 // - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate. |
109 double target_bitrate = | 140 target_bitrate_kbps = |
110 std::min(bitrate_kbit * kMaxTL0FpsReduction, | 141 std::min(bitrate_kbps * kMaxTL0FpsReduction, |
111 max_bitrate_kbit / kAcceptableTargetOvershoot); | 142 max_bitrate_kbps / kAcceptableTargetOvershoot); |
112 cfg->rc_target_bitrate = | 143 |
113 std::max(static_cast<unsigned int>(bitrate_kbit), | 144 // Don't reconfigure qp limits during quality boost frames. |
114 static_cast<unsigned int>(target_bitrate + 0.5)); | 145 if (layers_[active_layer_].state != TemporalLayer::State::kQualityBoost) { |
146 min_qp_ = cfg->rc_min_quantizer; | |
147 max_qp_ = cfg->rc_max_quantizer; | |
148 // After a dropped frame, a frame with max qp will be encoded and the | |
149 // quality will then ramp up from there. To boost the speed of recovery, | |
150 // encode the next frame with lower max qp. TL0 is the most important to | |
151 // improve since the errors in this layer will propagate to TL1. | |
152 // Currently, reduce max qp by 20% for TL0 and 15% for TL1. | |
153 layers_[0].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 80) / 100); | |
154 layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100); | |
155 } | |
156 | |
157 cfg->rc_target_bitrate = std::max(bitrate_kbps, target_bitrate_kbps); | |
115 } | 158 } |
116 | 159 |
160 int avg_frame_size = (target_bitrate_kbps * 1000) / (8 * framerate); | |
161 max_debt_bytes_ = 4 * avg_frame_size; | |
162 | |
117 return true; | 163 return true; |
118 } | 164 } |
119 | 165 |
120 void ScreenshareLayers::FrameEncoded(unsigned int size, uint32_t timestamp) { | 166 void ScreenshareLayers::FrameEncoded(unsigned int size, |
167 uint32_t timestamp, | |
168 int qp) { | |
169 if (size == 0) { | |
170 layers_[active_layer_].state = TemporalLayer::State::kDropped; | |
171 return; | |
172 } | |
173 if (layers_[active_layer_].state == TemporalLayer::State::kDropped) { | |
174 layers_[active_layer_].state = TemporalLayer::State::kQualityBoost; | |
175 } | |
176 | |
177 if (qp != -1) | |
178 layers_[active_layer_].last_qp = qp; | |
179 | |
121 if (active_layer_ == 0) { | 180 if (active_layer_ == 0) { |
122 tl0_frame_dropper_->Fill(size, true); | 181 layers_[0].debt_bytes_ += size; |
182 layers_[1].debt_bytes_ += size; | |
183 } else if (active_layer_ == 1) { | |
184 layers_[1].debt_bytes_ += size; | |
123 } | 185 } |
124 tl1_frame_dropper_->Fill(size, true); | |
125 } | 186 } |
126 | 187 |
127 void ScreenshareLayers::PopulateCodecSpecific(bool base_layer_sync, | 188 void ScreenshareLayers::PopulateCodecSpecific(bool base_layer_sync, |
128 CodecSpecificInfoVP8 *vp8_info, | 189 CodecSpecificInfoVP8 *vp8_info, |
129 uint32_t timestamp) { | 190 uint32_t timestamp) { |
130 if (number_of_temporal_layers_ == 1) { | 191 if (number_of_temporal_layers_ == 1) { |
131 vp8_info->temporalIdx = kNoTemporalIdx; | 192 vp8_info->temporalIdx = kNoTemporalIdx; |
132 vp8_info->layerSync = false; | 193 vp8_info->layerSync = false; |
133 vp8_info->tl0PicIdx = kNoTl0PicIdx; | 194 vp8_info->tl0PicIdx = kNoTl0PicIdx; |
134 } else { | 195 } else { |
135 vp8_info->temporalIdx = active_layer_; | 196 vp8_info->temporalIdx = active_layer_; |
136 if (base_layer_sync) { | 197 if (base_layer_sync) { |
137 vp8_info->temporalIdx = 0; | 198 vp8_info->temporalIdx = 0; |
138 last_sync_timestamp_ = timestamp; | 199 last_sync_timestamp_ = timestamp; |
139 } else if (last_base_layer_sync_ && vp8_info->temporalIdx != 0) { | 200 } else if (last_base_layer_sync_ && vp8_info->temporalIdx != 0) { |
140 // Regardless of pattern the frame after a base layer sync will always | 201 // Regardless of pattern the frame after a base layer sync will always |
141 // be a layer sync. | 202 // be a layer sync. |
142 last_sync_timestamp_ = timestamp; | 203 last_sync_timestamp_ = timestamp; |
143 } | 204 } |
144 vp8_info->layerSync = (last_sync_timestamp_ == timestamp); | 205 vp8_info->layerSync = |
206 last_sync_timestamp_ != 0 && last_sync_timestamp_ == timestamp; | |
145 if (vp8_info->temporalIdx == 0) { | 207 if (vp8_info->temporalIdx == 0) { |
146 tl0_pic_idx_++; | 208 tl0_pic_idx_++; |
147 } | 209 } |
148 last_base_layer_sync_ = base_layer_sync; | 210 last_base_layer_sync_ = base_layer_sync; |
149 vp8_info->tl0PicIdx = tl0_pic_idx_; | 211 vp8_info->tl0PicIdx = tl0_pic_idx_; |
150 } | 212 } |
151 } | 213 } |
152 | 214 |
153 bool ScreenshareLayers::TimeToSync(uint32_t timestamp) const { | 215 bool ScreenshareLayers::TimeToSync(uint32_t timestamp) const { |
216 if (active_layer_ != 1) { | |
217 RTC_NOTREACHED(); | |
218 return false; | |
219 } | |
220 DCHECK_NE(-1, layers_[0].last_qp); | |
221 if (layers_[1].last_qp == -1) { | |
222 // First frame in TL1 should only depend on TL0 since there are not | |
stefan-webrtc
2015/06/23 14:28:32
there are no
sprang_webrtc
2015/06/23 15:07:31
Done.
| |
223 // previous frames in TL1. | |
224 return true; | |
225 } | |
226 | |
227 DCHECK_NE(0u, last_sync_timestamp_); | |
stefan-webrtc
2015/06/23 14:28:32
Can't this be 0 mid stream?
sprang_webrtc
2015/06/23 15:07:30
Updated to use time unwrapped to int64_t and -1 fo
| |
154 const uint32_t timestamp_diff = timestamp - last_sync_timestamp_; | 228 const uint32_t timestamp_diff = timestamp - last_sync_timestamp_; |
155 return last_sync_timestamp_ < 0 || timestamp_diff > kOneSecond90Khz; | 229 if (timestamp_diff > kMaxTimeBetweenSyncs) { |
230 // After a certain time, force a sync frame. | |
231 return true; | |
232 } else if (timestamp_diff < kMinTimeBetweenSyncs) { | |
233 // If too soon from previous sync frame, don't issue a new one. | |
234 return false; | |
235 } | |
236 // Issue a sync frame if difference in quality between TL0 and TL1 isn't too | |
237 // large. | |
238 if (layers_[0].last_qp - layers_[1].last_qp < kMaxQpDeltaForSync) | |
239 return true; | |
240 return false; | |
156 } | 241 } |
157 | 242 |
158 void ScreenshareLayers::CalculateFramerate(uint32_t timestamp) { | 243 bool ScreenshareLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) { |
159 timestamp_list_.push_front(timestamp); | 244 if (max_qp_ == -1) |
160 // Remove timestamps older than 1 second from the list. | 245 return false; |
161 uint32_t timestamp_diff = timestamp - timestamp_list_.back(); | 246 |
162 while (timestamp_diff > kOneSecond90Khz) { | 247 // If layer is in the quality boost state (following a dropped frame), update |
163 timestamp_list_.pop_back(); | 248 // the configuration with the adjusted (lower) qp and set the state back to |
164 timestamp_diff = timestamp - timestamp_list_.back(); | 249 // normal. |
250 unsigned int adjusted_max_qp; | |
251 if (layers_[active_layer_].state == TemporalLayer::State::kQualityBoost && | |
252 layers_[active_layer_].enhanced_max_qp != -1) { | |
253 adjusted_max_qp = layers_[active_layer_].enhanced_max_qp; | |
254 layers_[active_layer_].state = TemporalLayer::State::kNormal; | |
255 } else { | |
256 if (max_qp_ == -1) | |
257 return false; | |
258 adjusted_max_qp = max_qp_; // Set the normal max qp. | |
165 } | 259 } |
166 // If we have encoded frames within the last second, that number of frames | 260 |
167 // is a reasonable first estimate of the framerate. | 261 if (adjusted_max_qp == cfg->rc_max_quantizer) |
168 framerate_ = timestamp_list_.size(); | 262 return false; |
169 if (timestamp_diff > 0) { | 263 |
170 // Estimate the framerate by dividing the number of timestamp diffs with | 264 cfg->rc_max_quantizer = adjusted_max_qp; |
171 // the sum of the timestamp diffs (with rounding). | 265 return true; |
172 framerate_ = (kOneSecond90Khz * (timestamp_list_.size() - 1) + | 266 } |
173 timestamp_diff / 2) / timestamp_diff; | 267 |
268 void ScreenshareLayers::TemporalLayer::UpdateDebt(int64_t delta_ms) { | |
269 uint32_t debt_reduction_bytes = target_rate_kbps_ * delta_ms / 8; | |
270 if (debt_reduction_bytes >= debt_bytes_) { | |
271 debt_bytes_ = 0; | |
272 } else { | |
273 debt_bytes_ -= debt_reduction_bytes; | |
174 } | 274 } |
175 } | 275 } |
176 | 276 |
177 } // namespace webrtc | 277 } // namespace webrtc |
OLD | NEW |