niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 1 | /* |
pwestin@webrtc.org | cac7878 | 2012-04-05 08:30:10 +0000 | [diff] [blame] | 2 | * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
pbos@webrtc.org | a048d7c | 2013-05-29 14:27:38 +0000 | [diff] [blame] | 11 | #include "webrtc/modules/rtp_rtcp/source/tmmbr_help.h" |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 12 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 13 | #include <algorithm> |
danilchap | b8b6fbb | 2015-12-10 05:05:27 -0800 | [diff] [blame] | 14 | #include <limits> |
Danil Chapovalov | daa90a7 | 2016-08-10 11:29:50 +0200 | [diff] [blame^] | 15 | #include <utility> |
danilchap | b8b6fbb | 2015-12-10 05:05:27 -0800 | [diff] [blame] | 16 | |
danilchap | f6ff971 | 2016-02-24 09:23:37 -0800 | [diff] [blame] | 17 | #include "webrtc/base/checks.h" |
pbos@webrtc.org | a048d7c | 2013-05-29 14:27:38 +0000 | [diff] [blame] | 18 | #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h" |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 19 | |
| 20 | namespace webrtc { |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 21 | void TMMBRSet::VerifyAndAllocateSet(uint32_t minimumSize) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 22 | clear(); |
| 23 | reserve(minimumSize); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 24 | } |
| 25 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 26 | void TMMBRSet::VerifyAndAllocateSetKeepingData(uint32_t minimumSize) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 27 | reserve(minimumSize); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 28 | } |
| 29 | |
| 30 | void TMMBRSet::SetEntry(unsigned int i, |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 31 | uint32_t tmmbrSet, |
| 32 | uint32_t packetOHSet, |
| 33 | uint32_t ssrcSet) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 34 | RTC_DCHECK_LT(i, capacity()); |
| 35 | if (i >= size()) { |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 36 | resize(i + 1); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 37 | } |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 38 | (*this)[i].set_bitrate_bps(tmmbrSet * 1000); |
| 39 | (*this)[i].set_packet_overhead(packetOHSet); |
| 40 | (*this)[i].set_ssrc(ssrcSet); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 41 | } |
| 42 | |
pbos@webrtc.org | 2f44673 | 2013-04-08 11:08:41 +0000 | [diff] [blame] | 43 | void TMMBRSet::AddEntry(uint32_t tmmbrSet, |
| 44 | uint32_t packetOHSet, |
| 45 | uint32_t ssrcSet) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 46 | RTC_DCHECK_LT(size(), capacity()); |
| 47 | SetEntry(size(), tmmbrSet, packetOHSet, ssrcSet); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 48 | } |
| 49 | |
pbos@webrtc.org | 2f44673 | 2013-04-08 11:08:41 +0000 | [diff] [blame] | 50 | void TMMBRSet::RemoveEntry(uint32_t sourceIdx) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 51 | RTC_DCHECK_LT(sourceIdx, size()); |
| 52 | erase(begin() + sourceIdx); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 53 | } |
| 54 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 55 | TMMBRSet* TMMBRHelp::VerifyAndAllocateCandidateSet(uint32_t minimumSize) { |
| 56 | _candidateSet.VerifyAndAllocateSet(minimumSize); |
| 57 | return &_candidateSet; |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 58 | } |
| 59 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 60 | TMMBRSet* TMMBRHelp::CandidateSet() { |
| 61 | return &_candidateSet; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 62 | } |
| 63 | |
Danil Chapovalov | daa90a7 | 2016-08-10 11:29:50 +0200 | [diff] [blame^] | 64 | std::vector<rtcp::TmmbItem> TMMBRHelp::FindTMMBRBoundingSet() { |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 65 | // Work on local variable, will be modified |
| 66 | TMMBRSet candidateSet; |
| 67 | candidateSet.VerifyAndAllocateSet(_candidateSet.capacity()); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 68 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 69 | for (size_t i = 0; i < _candidateSet.size(); i++) { |
| 70 | if (_candidateSet.Tmmbr(i)) { |
| 71 | candidateSet.AddEntry(_candidateSet.Tmmbr(i), _candidateSet.PacketOH(i), |
| 72 | _candidateSet.Ssrc(i)); |
danilchap | f6ff971 | 2016-02-24 09:23:37 -0800 | [diff] [blame] | 73 | } else { |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 74 | // make sure this is zero if tmmbr = 0 |
| 75 | RTC_DCHECK_EQ(_candidateSet.PacketOH(i), 0u); |
| 76 | // Old code: |
| 77 | // _candidateSet.ptrPacketOHSet[i] = 0; |
danilchap | f6ff971 | 2016-02-24 09:23:37 -0800 | [diff] [blame] | 78 | } |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 79 | } |
tommi@webrtc.org | eec6ecd | 2014-07-11 19:09:59 +0000 | [diff] [blame] | 80 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 81 | // Number of set candidates |
| 82 | int32_t numSetCandidates = candidateSet.lengthOfSet(); |
| 83 | // Find bounding set |
Danil Chapovalov | daa90a7 | 2016-08-10 11:29:50 +0200 | [diff] [blame^] | 84 | std::vector<rtcp::TmmbItem> bounding; |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 85 | if (numSetCandidates > 0) { |
Danil Chapovalov | daa90a7 | 2016-08-10 11:29:50 +0200 | [diff] [blame^] | 86 | FindBoundingSet(std::move(candidateSet), &bounding); |
| 87 | size_t numBoundingSet = bounding.size(); |
| 88 | RTC_DCHECK_GE(numBoundingSet, 1u); |
| 89 | RTC_DCHECK_LE(numBoundingSet, _candidateSet.size()); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 90 | } |
Danil Chapovalov | daa90a7 | 2016-08-10 11:29:50 +0200 | [diff] [blame^] | 91 | return bounding; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 92 | } |
| 93 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 94 | void TMMBRHelp::FindBoundingSet(std::vector<rtcp::TmmbItem> candidates, |
| 95 | std::vector<rtcp::TmmbItem>* bounding_set) { |
| 96 | RTC_DCHECK(bounding_set); |
| 97 | RTC_DCHECK(!candidates.empty()); |
| 98 | size_t num_candidates = candidates.size(); |
| 99 | |
| 100 | if (num_candidates == 1) { |
| 101 | RTC_DCHECK(candidates[0].bitrate_bps()); |
| 102 | *bounding_set = std::move(candidates); |
| 103 | return; |
| 104 | } |
| 105 | |
| 106 | // 1. Sort by increasing packet overhead. |
| 107 | std::sort(candidates.begin(), candidates.end(), |
| 108 | [](const rtcp::TmmbItem& lhs, const rtcp::TmmbItem& rhs) { |
| 109 | return lhs.packet_overhead() < rhs.packet_overhead(); |
| 110 | }); |
| 111 | |
| 112 | // 2. For tuples with same overhead, keep the one with the lowest bitrate. |
| 113 | for (auto it = candidates.begin(); it != candidates.end();) { |
| 114 | RTC_DCHECK(it->bitrate_bps()); |
| 115 | auto current_min = it; |
| 116 | auto next_it = it + 1; |
| 117 | // Use fact candidates are sorted by overhead, so candidates with same |
| 118 | // overhead are adjusted. |
| 119 | while (next_it != candidates.end() && |
| 120 | next_it->packet_overhead() == current_min->packet_overhead()) { |
| 121 | if (next_it->bitrate_bps() < current_min->bitrate_bps()) { |
| 122 | current_min->set_bitrate_bps(0); |
| 123 | current_min = next_it; |
| 124 | } else { |
| 125 | next_it->set_bitrate_bps(0); |
| 126 | } |
| 127 | ++next_it; |
| 128 | --num_candidates; |
| 129 | } |
| 130 | it = next_it; |
| 131 | } |
| 132 | |
| 133 | // 3. Select and remove tuple with lowest tmmbr. |
| 134 | // (If more than 1, choose the one with highest overhead). |
| 135 | auto min_bitrate_it = candidates.end(); |
| 136 | for (auto it = candidates.begin(); it != candidates.end(); ++it) { |
| 137 | if (it->bitrate_bps()) { |
| 138 | min_bitrate_it = it; |
| 139 | break; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | for (auto it = min_bitrate_it; it != candidates.end(); ++it) { |
| 144 | if (it->bitrate_bps() && |
| 145 | it->bitrate_bps() <= min_bitrate_it->bitrate_bps()) { |
| 146 | // Get min bitrate. |
| 147 | min_bitrate_it = it; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | bounding_set->clear(); |
| 152 | bounding_set->reserve(num_candidates); |
| 153 | std::vector<float> intersection(num_candidates); |
| 154 | std::vector<float> max_packet_rate(num_candidates); |
| 155 | |
| 156 | // First member of selected list. |
| 157 | bounding_set->push_back(*min_bitrate_it); |
| 158 | intersection[0] = 0; |
| 159 | // Calculate its maximum packet rate (where its line crosses x-axis). |
| 160 | uint16_t packet_overhead = bounding_set->back().packet_overhead(); |
| 161 | if (packet_overhead == 0) { |
| 162 | // Avoid division by zero. |
| 163 | max_packet_rate[0] = std::numeric_limits<float>::max(); |
| 164 | } else { |
| 165 | max_packet_rate[0] = bounding_set->back().bitrate_bps() / |
| 166 | static_cast<float>(packet_overhead); |
| 167 | } |
| 168 | // Remove from candidate list. |
| 169 | min_bitrate_it->set_bitrate_bps(0); |
| 170 | --num_candidates; |
| 171 | |
| 172 | // 4. Discard from candidate list all tuple with lower overhead |
| 173 | // (next tuple must be steeper). |
| 174 | for (auto it = candidates.begin(); it != candidates.end(); ++it) { |
| 175 | if (it->bitrate_bps() && |
| 176 | it->packet_overhead() < bounding_set->front().packet_overhead()) { |
| 177 | it->set_bitrate_bps(0); |
| 178 | --num_candidates; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | bool get_new_candidate = true; |
| 183 | rtcp::TmmbItem cur_candidate; |
| 184 | while (num_candidates > 0) { |
| 185 | if (get_new_candidate) { |
| 186 | // 5. Remove first remaining tuple from candidate list. |
| 187 | for (auto it = candidates.begin(); it != candidates.end(); ++it) { |
| 188 | if (it->bitrate_bps()) { |
| 189 | cur_candidate = *it; |
| 190 | it->set_bitrate_bps(0); |
| 191 | break; |
| 192 | } |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // 6. Calculate packet rate and intersection of the current |
| 197 | // line with line of last tuple in selected list. |
| 198 | RTC_DCHECK_NE(cur_candidate.packet_overhead(), |
| 199 | bounding_set->back().packet_overhead()); |
| 200 | float packet_rate = static_cast<float>(cur_candidate.bitrate_bps() - |
| 201 | bounding_set->back().bitrate_bps()) / |
| 202 | (cur_candidate.packet_overhead() - |
| 203 | bounding_set->back().packet_overhead()); |
| 204 | |
| 205 | // 7. If the packet rate is equal or lower than intersection of |
| 206 | // last tuple in selected list, |
| 207 | // remove last tuple in selected list & go back to step 6. |
| 208 | if (packet_rate <= intersection[bounding_set->size() - 1]) { |
| 209 | // Remove last tuple and goto step 6. |
| 210 | bounding_set->pop_back(); |
| 211 | get_new_candidate = false; |
| 212 | } else { |
| 213 | // 8. If packet rate is lower than maximum packet rate of |
| 214 | // last tuple in selected list, add current tuple to selected |
| 215 | // list. |
| 216 | if (packet_rate < max_packet_rate[bounding_set->size() - 1]) { |
| 217 | bounding_set->push_back(cur_candidate); |
| 218 | intersection[bounding_set->size() - 1] = packet_rate; |
| 219 | uint16_t packet_overhead = bounding_set->back().packet_overhead(); |
| 220 | RTC_DCHECK_NE(packet_overhead, 0); |
| 221 | max_packet_rate[bounding_set->size() - 1] = |
| 222 | bounding_set->back().bitrate_bps() / |
| 223 | static_cast<float>(packet_overhead); |
| 224 | } |
| 225 | --num_candidates; |
| 226 | get_new_candidate = true; |
| 227 | } |
| 228 | |
| 229 | // 9. Go back to step 5 if any tuple remains in candidate list. |
| 230 | } |
| 231 | } |
| 232 | |
Danil Chapovalov | daa90a7 | 2016-08-10 11:29:50 +0200 | [diff] [blame^] | 233 | bool TMMBRHelp::IsOwner(const std::vector<rtcp::TmmbItem>& bounding, |
| 234 | uint32_t ssrc) { |
| 235 | for (const rtcp::TmmbItem& item : bounding) { |
| 236 | if (item.ssrc() == ssrc) { |
pwestin@webrtc.org | cac7878 | 2012-04-05 08:30:10 +0000 | [diff] [blame] | 237 | return true; |
| 238 | } |
| 239 | } |
| 240 | return false; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 241 | } |
| 242 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 243 | bool TMMBRHelp::CalcMinBitRate(uint32_t* minBitrateKbit) const { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 244 | if (_candidateSet.size() == 0) { |
pwestin@webrtc.org | cac7878 | 2012-04-05 08:30:10 +0000 | [diff] [blame] | 245 | // Empty bounding set. |
| 246 | return false; |
| 247 | } |
| 248 | *minBitrateKbit = std::numeric_limits<uint32_t>::max(); |
| 249 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 250 | for (size_t i = 0; i < _candidateSet.lengthOfSet(); ++i) { |
pbos@webrtc.org | 2f44673 | 2013-04-08 11:08:41 +0000 | [diff] [blame] | 251 | uint32_t curNetBitRateKbit = _candidateSet.Tmmbr(i); |
pwestin@webrtc.org | cac7878 | 2012-04-05 08:30:10 +0000 | [diff] [blame] | 252 | if (curNetBitRateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) { |
| 253 | curNetBitRateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 254 | } |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 255 | *minBitrateKbit = curNetBitRateKbit < *minBitrateKbit ? curNetBitRateKbit |
| 256 | : *minBitrateKbit; |
pwestin@webrtc.org | cac7878 | 2012-04-05 08:30:10 +0000 | [diff] [blame] | 257 | } |
| 258 | return true; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 259 | } |
pbos@webrtc.org | d900e8b | 2013-07-03 15:12:26 +0000 | [diff] [blame] | 260 | } // namespace webrtc |