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