Line data Source code
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 "webrtc/modules/video_coding/media_optimization.h"
12 :
13 : #include "webrtc/base/logging.h"
14 : #include "webrtc/modules/video_coding/utility/frame_dropper.h"
15 : #include "webrtc/system_wrappers/include/clock.h"
16 :
17 : namespace webrtc {
18 : namespace media_optimization {
19 :
20 : struct MediaOptimization::EncodedFrameSample {
21 0 : EncodedFrameSample(size_t size_bytes,
22 : uint32_t timestamp,
23 : int64_t time_complete_ms)
24 0 : : size_bytes(size_bytes),
25 : timestamp(timestamp),
26 0 : time_complete_ms(time_complete_ms) {}
27 :
28 : size_t size_bytes;
29 : uint32_t timestamp;
30 : int64_t time_complete_ms;
31 : };
32 :
33 0 : MediaOptimization::MediaOptimization(Clock* clock)
34 : : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
35 : clock_(clock),
36 : max_bit_rate_(0),
37 : codec_width_(0),
38 : codec_height_(0),
39 : user_frame_rate_(0),
40 0 : frame_dropper_(new FrameDropper),
41 : send_statistics_zero_encode_(0),
42 : max_payload_size_(1460),
43 : video_target_bitrate_(0),
44 : incoming_frame_rate_(0),
45 : encoded_frame_samples_(),
46 : avg_sent_bit_rate_bps_(0),
47 : avg_sent_framerate_(0),
48 : num_layers_(0),
49 0 : loadstate_(kLoadNormal) {
50 0 : memset(send_statistics_, 0, sizeof(send_statistics_));
51 0 : memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_));
52 0 : }
53 :
54 0 : MediaOptimization::~MediaOptimization(void) {
55 0 : }
56 :
57 0 : void MediaOptimization::Reset() {
58 0 : CriticalSectionScoped lock(crit_sect_.get());
59 0 : SetEncodingDataInternal(0, 0, 0, 0, 0, 1, 0, max_payload_size_);
60 0 : memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_));
61 0 : incoming_frame_rate_ = 0.0;
62 0 : frame_dropper_->Reset();
63 0 : frame_dropper_->SetRates(0, 0);
64 0 : send_statistics_zero_encode_ = 0;
65 0 : video_target_bitrate_ = 0;
66 0 : codec_width_ = 0;
67 0 : codec_height_ = 0;
68 0 : min_width_ = 0;
69 0 : min_height_ = 0;
70 0 : user_frame_rate_ = 0;
71 0 : encoded_frame_samples_.clear();
72 0 : avg_sent_bit_rate_bps_ = 0;
73 0 : num_layers_ = 1;
74 0 : }
75 :
76 : // Euclid's algorithm
77 : // on arm, binary may be faster, but we do this rarely
78 0 : static int GreatestCommonDenominator(int a, int b) {
79 0 : while (b != 0) {
80 0 : int t = b;
81 0 : b = a % b;
82 0 : a = t;
83 : }
84 0 : return a;
85 : }
86 :
87 0 : void MediaOptimization::SetEncodingData(int32_t max_bit_rate, // bps
88 : uint32_t target_bitrate, // bps
89 : uint16_t width,
90 : uint16_t height,
91 : uint32_t frame_rate, // fps*1000
92 : uint8_t divisor,
93 : int num_layers,
94 : int32_t mtu) {
95 0 : CriticalSectionScoped lock(crit_sect_.get());
96 0 : SetEncodingDataInternal(max_bit_rate, target_bitrate, width,
97 0 : height, num_layers, frame_rate, divisor, mtu);
98 0 : }
99 :
100 0 : void MediaOptimization::SetEncodingDataInternal(int32_t max_bit_rate, // bps
101 : uint32_t target_bitrate, // bps
102 : uint16_t width,
103 : uint16_t height,
104 : uint32_t frame_rate, // in fps*1000
105 : uint8_t divisor,
106 : int num_layers,
107 : int32_t mtu) {
108 : // Everything codec specific should be reset here since this means the codec
109 : // has changed.
110 :
111 0 : max_bit_rate_ = max_bit_rate;
112 0 : video_target_bitrate_ = target_bitrate;
113 0 : float target_bitrate_kbps = static_cast<float>(target_bitrate) / 1000.0f;
114 0 : frame_dropper_->Reset();
115 0 : frame_dropper_->SetRates(target_bitrate_kbps, static_cast<float>(frame_rate) / 1000.0f);
116 0 : user_frame_rate_ = static_cast<float>(frame_rate) / 1000.0f;
117 0 : codec_width_ = width;
118 0 : codec_height_ = height;
119 0 : int gcd = GreatestCommonDenominator(codec_width_, codec_height_);
120 0 : min_width_ = gcd ? (codec_width_/gcd * divisor) : 0;
121 0 : min_height_ = gcd ? (codec_height_/gcd * divisor) : 0;
122 0 : num_layers_ = (num_layers <= 1) ? 1 : num_layers; // Can also be zero.
123 0 : max_payload_size_ = mtu;
124 0 : }
125 :
126 0 : uint32_t MediaOptimization::SetTargetRates(uint32_t target_bitrate) {
127 0 : CriticalSectionScoped lock(crit_sect_.get());
128 0 : LOG(LS_INFO) << "SetTargetRates: " << target_bitrate << " bps ";
129 :
130 0 : video_target_bitrate_ = target_bitrate;
131 :
132 : // Cap target video bitrate to codec maximum.
133 0 : if (max_bit_rate_ > 0 && video_target_bitrate_ > max_bit_rate_) {
134 0 : video_target_bitrate_ = max_bit_rate_;
135 : }
136 :
137 : // Update encoding rates following protection settings.
138 : float target_video_bitrate_kbps =
139 0 : static_cast<float>(video_target_bitrate_) / 1000.0f;
140 0 : frame_dropper_->SetRates(target_video_bitrate_kbps, incoming_frame_rate_);
141 :
142 0 : return video_target_bitrate_;
143 : }
144 :
145 0 : uint32_t MediaOptimization::InputFrameRate() {
146 0 : CriticalSectionScoped lock(crit_sect_.get());
147 0 : return InputFrameRateInternal();
148 : }
149 :
150 0 : uint32_t MediaOptimization::InputFrameRateInternal() {
151 0 : ProcessIncomingFrameRate(clock_->TimeInMilliseconds());
152 0 : return uint32_t(incoming_frame_rate_ + 0.5f);
153 : }
154 :
155 0 : uint32_t MediaOptimization::SentFrameRate() {
156 0 : CriticalSectionScoped lock(crit_sect_.get());
157 0 : return SentFrameRateInternal();
158 : }
159 :
160 0 : uint32_t MediaOptimization::SentFrameRateInternal() {
161 0 : PurgeOldFrameSamples(clock_->TimeInMilliseconds());
162 0 : UpdateSentFramerate();
163 0 : return avg_sent_framerate_;
164 : }
165 :
166 0 : uint32_t MediaOptimization::SentBitRate() {
167 0 : CriticalSectionScoped lock(crit_sect_.get());
168 0 : const int64_t now_ms = clock_->TimeInMilliseconds();
169 0 : PurgeOldFrameSamples(now_ms);
170 0 : UpdateSentBitrate(now_ms);
171 0 : return avg_sent_bit_rate_bps_;
172 : }
173 :
174 0 : int32_t MediaOptimization::UpdateWithEncodedData(
175 : const EncodedImage& encoded_image) {
176 0 : size_t encoded_length = encoded_image._length;
177 0 : uint32_t timestamp = encoded_image._timeStamp;
178 0 : CriticalSectionScoped lock(crit_sect_.get());
179 0 : const int64_t now_ms = clock_->TimeInMilliseconds();
180 0 : PurgeOldFrameSamples(now_ms);
181 0 : if (encoded_frame_samples_.size() > 0 &&
182 0 : encoded_frame_samples_.back().timestamp == timestamp) {
183 : // Frames having the same timestamp are generated from the same input
184 : // frame. We don't want to double count them, but only increment the
185 : // size_bytes.
186 0 : encoded_frame_samples_.back().size_bytes += encoded_length;
187 0 : encoded_frame_samples_.back().time_complete_ms = now_ms;
188 : } else {
189 0 : encoded_frame_samples_.push_back(
190 0 : EncodedFrameSample(encoded_length, timestamp, now_ms));
191 : }
192 0 : UpdateSentBitrate(now_ms);
193 0 : UpdateSentFramerate();
194 0 : if (encoded_length > 0) {
195 0 : const bool delta_frame = encoded_image._frameType != kVideoFrameKey;
196 : // XXX TODO(jesup): if same_frame is true, we should be considering it a single
197 : // frame here.
198 0 : frame_dropper_->Fill(encoded_length, delta_frame);
199 : }
200 :
201 0 : return VCM_OK;
202 : }
203 :
204 0 : void MediaOptimization::EnableFrameDropper(bool enable) {
205 0 : CriticalSectionScoped lock(crit_sect_.get());
206 0 : frame_dropper_->Enable(enable);
207 0 : }
208 :
209 0 : bool MediaOptimization::DropFrame() {
210 0 : CriticalSectionScoped lock(crit_sect_.get());
211 0 : UpdateIncomingFrameRate();
212 : // Leak appropriate number of bytes.
213 0 : frame_dropper_->Leak((uint32_t)(InputFrameRateInternal() + 0.5f));
214 0 : return frame_dropper_->DropFrame();
215 : }
216 :
217 0 : void MediaOptimization::UpdateIncomingFrameRate() {
218 0 : int64_t now = clock_->TimeInMilliseconds();
219 0 : if (incoming_frame_times_[0] == 0) {
220 : // No shifting if this is the first time.
221 : } else {
222 : // Shift all times one step.
223 0 : for (int32_t i = (kFrameCountHistorySize - 2); i >= 0; i--) {
224 0 : incoming_frame_times_[i + 1] = incoming_frame_times_[i];
225 : }
226 : }
227 0 : incoming_frame_times_[0] = now;
228 0 : ProcessIncomingFrameRate(now);
229 0 : }
230 :
231 0 : void MediaOptimization::PurgeOldFrameSamples(int64_t now_ms) {
232 0 : while (!encoded_frame_samples_.empty()) {
233 0 : if (now_ms - encoded_frame_samples_.front().time_complete_ms >
234 : kBitrateAverageWinMs) {
235 0 : encoded_frame_samples_.pop_front();
236 : } else {
237 0 : break;
238 : }
239 : }
240 0 : }
241 :
242 0 : void MediaOptimization::UpdateSentBitrate(int64_t now_ms) {
243 0 : if (encoded_frame_samples_.empty()) {
244 0 : avg_sent_bit_rate_bps_ = 0;
245 0 : return;
246 : }
247 0 : size_t framesize_sum = 0;
248 0 : for (FrameSampleList::iterator it = encoded_frame_samples_.begin();
249 0 : it != encoded_frame_samples_.end(); ++it) {
250 0 : framesize_sum += it->size_bytes;
251 : }
252 : float denom = static_cast<float>(
253 0 : now_ms - encoded_frame_samples_.front().time_complete_ms);
254 0 : if (denom >= 1.0f) {
255 0 : avg_sent_bit_rate_bps_ =
256 0 : static_cast<uint32_t>(framesize_sum * 8.0f * 1000.0f / denom + 0.5f);
257 : } else {
258 0 : avg_sent_bit_rate_bps_ = framesize_sum * 8;
259 : }
260 : }
261 :
262 0 : void MediaOptimization::UpdateSentFramerate() {
263 0 : if (encoded_frame_samples_.size() <= 1) {
264 0 : avg_sent_framerate_ = encoded_frame_samples_.size();
265 0 : return;
266 : }
267 0 : int denom = encoded_frame_samples_.back().timestamp -
268 0 : encoded_frame_samples_.front().timestamp;
269 0 : if (denom > 0) {
270 0 : avg_sent_framerate_ =
271 0 : (90000 * (encoded_frame_samples_.size() - 1) + denom / 2) / denom;
272 : } else {
273 0 : avg_sent_framerate_ = encoded_frame_samples_.size();
274 : }
275 : }
276 :
277 : // Allowing VCM to keep track of incoming frame rate.
278 0 : void MediaOptimization::ProcessIncomingFrameRate(int64_t now) {
279 0 : int32_t num = 0;
280 0 : int32_t nr_of_frames = 0;
281 0 : for (num = 1; num < (kFrameCountHistorySize - 1); ++num) {
282 0 : if (incoming_frame_times_[num] <= 0 ||
283 : // don't use data older than 2 s
284 0 : now - incoming_frame_times_[num] > kFrameHistoryWinMs) {
285 : break;
286 : } else {
287 0 : nr_of_frames++;
288 : }
289 : }
290 0 : if (num > 1) {
291 : const int64_t diff =
292 0 : incoming_frame_times_[0] - incoming_frame_times_[num - 1];
293 0 : incoming_frame_rate_ = 0.0; // No frame rate estimate available.
294 0 : if (diff > 0) {
295 0 : incoming_frame_rate_ = nr_of_frames * 1000.0f / static_cast<float>(diff);
296 : }
297 : }
298 0 : }
299 :
300 0 : void MediaOptimization::SetCPULoadState(CPULoadState state) {
301 0 : CriticalSectionScoped lock(crit_sect_.get());
302 0 : loadstate_ = state;
303 0 : }
304 :
305 : } // namespace media_optimization
306 : } // namespace webrtc
|