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