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> |
| 15 | |
danilchap | f6ff971 | 2016-02-24 09:23:37 -0800 | [diff] [blame] | 16 | #include "webrtc/base/checks.h" |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 17 | |
| 18 | namespace webrtc { |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 19 | void TMMBRSet::VerifyAndAllocateSet(uint32_t minimumSize) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 20 | clear(); |
| 21 | reserve(minimumSize); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 22 | } |
| 23 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 24 | void TMMBRSet::VerifyAndAllocateSetKeepingData(uint32_t minimumSize) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 25 | reserve(minimumSize); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 26 | } |
| 27 | |
| 28 | void TMMBRSet::SetEntry(unsigned int i, |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 29 | uint32_t tmmbrSet, |
| 30 | uint32_t packetOHSet, |
| 31 | uint32_t ssrcSet) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 32 | RTC_DCHECK_LT(i, capacity()); |
| 33 | if (i >= size()) { |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 34 | resize(i + 1); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 35 | } |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 36 | (*this)[i].set_bitrate_bps(tmmbrSet * 1000); |
| 37 | (*this)[i].set_packet_overhead(packetOHSet); |
| 38 | (*this)[i].set_ssrc(ssrcSet); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 39 | } |
| 40 | |
pbos@webrtc.org | 2f44673 | 2013-04-08 11:08:41 +0000 | [diff] [blame] | 41 | void TMMBRSet::AddEntry(uint32_t tmmbrSet, |
| 42 | uint32_t packetOHSet, |
| 43 | uint32_t ssrcSet) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 44 | RTC_DCHECK_LT(size(), capacity()); |
| 45 | SetEntry(size(), tmmbrSet, packetOHSet, ssrcSet); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 46 | } |
| 47 | |
pbos@webrtc.org | 2f44673 | 2013-04-08 11:08:41 +0000 | [diff] [blame] | 48 | void TMMBRSet::RemoveEntry(uint32_t sourceIdx) { |
danilchap | a4f31bd | 2016-02-29 05:26:01 -0800 | [diff] [blame] | 49 | RTC_DCHECK_LT(sourceIdx, size()); |
| 50 | erase(begin() + sourceIdx); |
hta@webrtc.org | 54536bb | 2012-05-03 14:07:23 +0000 | [diff] [blame] | 51 | } |
| 52 | |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 53 | std::vector<rtcp::TmmbItem> TMMBRHelp::FindBoundingSet( |
| 54 | std::vector<rtcp::TmmbItem> candidates) { |
| 55 | // Filter out candidates with 0 bitrate. |
| 56 | for (auto it = candidates.begin(); it != candidates.end();) { |
| 57 | if (!it->bitrate_bps()) |
| 58 | it = candidates.erase(it); |
| 59 | else |
| 60 | ++it; |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 61 | } |
tommi@webrtc.org | eec6ecd | 2014-07-11 19:09:59 +0000 | [diff] [blame] | 62 | |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 63 | if (candidates.size() <= 1) |
| 64 | return candidates; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 65 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 66 | size_t num_candidates = candidates.size(); |
| 67 | |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 68 | // 1. Sort by increasing packet overhead. |
| 69 | std::sort(candidates.begin(), candidates.end(), |
| 70 | [](const rtcp::TmmbItem& lhs, const rtcp::TmmbItem& rhs) { |
| 71 | return lhs.packet_overhead() < rhs.packet_overhead(); |
| 72 | }); |
| 73 | |
| 74 | // 2. For tuples with same overhead, keep the one with the lowest bitrate. |
| 75 | for (auto it = candidates.begin(); it != candidates.end();) { |
| 76 | RTC_DCHECK(it->bitrate_bps()); |
| 77 | auto current_min = it; |
| 78 | auto next_it = it + 1; |
| 79 | // Use fact candidates are sorted by overhead, so candidates with same |
| 80 | // overhead are adjusted. |
| 81 | while (next_it != candidates.end() && |
| 82 | next_it->packet_overhead() == current_min->packet_overhead()) { |
| 83 | if (next_it->bitrate_bps() < current_min->bitrate_bps()) { |
| 84 | current_min->set_bitrate_bps(0); |
| 85 | current_min = next_it; |
| 86 | } else { |
| 87 | next_it->set_bitrate_bps(0); |
| 88 | } |
| 89 | ++next_it; |
| 90 | --num_candidates; |
| 91 | } |
| 92 | it = next_it; |
| 93 | } |
| 94 | |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 95 | // 3. Select and remove tuple with lowest bitrate. |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 96 | // (If more than 1, choose the one with highest overhead). |
| 97 | auto min_bitrate_it = candidates.end(); |
| 98 | for (auto it = candidates.begin(); it != candidates.end(); ++it) { |
| 99 | if (it->bitrate_bps()) { |
| 100 | min_bitrate_it = it; |
| 101 | break; |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | for (auto it = min_bitrate_it; it != candidates.end(); ++it) { |
| 106 | if (it->bitrate_bps() && |
| 107 | it->bitrate_bps() <= min_bitrate_it->bitrate_bps()) { |
| 108 | // Get min bitrate. |
| 109 | min_bitrate_it = it; |
| 110 | } |
| 111 | } |
| 112 | |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 113 | std::vector<rtcp::TmmbItem> bounding_set; |
| 114 | bounding_set.reserve(num_candidates); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 115 | std::vector<float> intersection(num_candidates); |
| 116 | std::vector<float> max_packet_rate(num_candidates); |
| 117 | |
| 118 | // First member of selected list. |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 119 | bounding_set.push_back(*min_bitrate_it); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 120 | intersection[0] = 0; |
| 121 | // Calculate its maximum packet rate (where its line crosses x-axis). |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 122 | uint16_t packet_overhead = bounding_set.back().packet_overhead(); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 123 | if (packet_overhead == 0) { |
| 124 | // Avoid division by zero. |
| 125 | max_packet_rate[0] = std::numeric_limits<float>::max(); |
| 126 | } else { |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 127 | max_packet_rate[0] = |
| 128 | bounding_set.back().bitrate_bps() / static_cast<float>(packet_overhead); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 129 | } |
| 130 | // Remove from candidate list. |
| 131 | min_bitrate_it->set_bitrate_bps(0); |
| 132 | --num_candidates; |
| 133 | |
| 134 | // 4. Discard from candidate list all tuple with lower overhead |
| 135 | // (next tuple must be steeper). |
| 136 | for (auto it = candidates.begin(); it != candidates.end(); ++it) { |
| 137 | if (it->bitrate_bps() && |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 138 | it->packet_overhead() < bounding_set.front().packet_overhead()) { |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 139 | it->set_bitrate_bps(0); |
| 140 | --num_candidates; |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | bool get_new_candidate = true; |
| 145 | rtcp::TmmbItem cur_candidate; |
| 146 | while (num_candidates > 0) { |
| 147 | if (get_new_candidate) { |
| 148 | // 5. Remove first remaining tuple from candidate list. |
| 149 | for (auto it = candidates.begin(); it != candidates.end(); ++it) { |
| 150 | if (it->bitrate_bps()) { |
| 151 | cur_candidate = *it; |
| 152 | it->set_bitrate_bps(0); |
| 153 | break; |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | // 6. Calculate packet rate and intersection of the current |
| 159 | // line with line of last tuple in selected list. |
| 160 | RTC_DCHECK_NE(cur_candidate.packet_overhead(), |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 161 | bounding_set.back().packet_overhead()); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 162 | float packet_rate = static_cast<float>(cur_candidate.bitrate_bps() - |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 163 | bounding_set.back().bitrate_bps()) / |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 164 | (cur_candidate.packet_overhead() - |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 165 | bounding_set.back().packet_overhead()); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 166 | |
| 167 | // 7. If the packet rate is equal or lower than intersection of |
| 168 | // last tuple in selected list, |
| 169 | // remove last tuple in selected list & go back to step 6. |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 170 | if (packet_rate <= intersection[bounding_set.size() - 1]) { |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 171 | // Remove last tuple and goto step 6. |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 172 | bounding_set.pop_back(); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 173 | get_new_candidate = false; |
| 174 | } else { |
| 175 | // 8. If packet rate is lower than maximum packet rate of |
| 176 | // last tuple in selected list, add current tuple to selected |
| 177 | // list. |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 178 | if (packet_rate < max_packet_rate[bounding_set.size() - 1]) { |
| 179 | bounding_set.push_back(cur_candidate); |
| 180 | intersection[bounding_set.size() - 1] = packet_rate; |
| 181 | uint16_t packet_overhead = bounding_set.back().packet_overhead(); |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 182 | RTC_DCHECK_NE(packet_overhead, 0); |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 183 | max_packet_rate[bounding_set.size() - 1] = |
| 184 | bounding_set.back().bitrate_bps() / |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 185 | static_cast<float>(packet_overhead); |
| 186 | } |
| 187 | --num_candidates; |
| 188 | get_new_candidate = true; |
| 189 | } |
| 190 | |
| 191 | // 9. Go back to step 5 if any tuple remains in candidate list. |
| 192 | } |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 193 | RTC_DCHECK(!bounding_set.empty()); |
| 194 | return bounding_set; |
danilchap | 262e486 | 2016-08-05 03:37:38 -0700 | [diff] [blame] | 195 | } |
| 196 | |
Danil Chapovalov | daa90a7 | 2016-08-10 11:29:50 +0200 | [diff] [blame] | 197 | bool TMMBRHelp::IsOwner(const std::vector<rtcp::TmmbItem>& bounding, |
| 198 | uint32_t ssrc) { |
| 199 | for (const rtcp::TmmbItem& item : bounding) { |
| 200 | if (item.ssrc() == ssrc) { |
pwestin@webrtc.org | cac7878 | 2012-04-05 08:30:10 +0000 | [diff] [blame] | 201 | return true; |
| 202 | } |
| 203 | } |
| 204 | return false; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 205 | } |
| 206 | |
danilchap | 2f69ce9 | 2016-08-16 03:21:38 -0700 | [diff] [blame^] | 207 | uint64_t TMMBRHelp::CalcMinBitrateBps( |
| 208 | const std::vector<rtcp::TmmbItem>& candidates) { |
| 209 | RTC_DCHECK(!candidates.empty()); |
| 210 | uint64_t min_bitrate_bps = std::numeric_limits<uint64_t>::max(); |
| 211 | for (const rtcp::TmmbItem& item : candidates) |
| 212 | if (item.bitrate_bps() < min_bitrate_bps) |
| 213 | min_bitrate_bps = item.bitrate_bps(); |
| 214 | return min_bitrate_bps; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 215 | } |
pbos@webrtc.org | d900e8b | 2013-07-03 15:12:26 +0000 | [diff] [blame] | 216 | } // namespace webrtc |