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; | |
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 Loading... | |
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 |
OLD | NEW |