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/video_coding/codecs/vp8/libvpx_vp8_encoder.h"
12
13 #include <string.h>
14
15 #include <algorithm>
16 #include <cstdint>
17 #include <iterator>
18 #include <memory>
19 #include <string>
20 #include <utility>
21 #include <vector>
22
23 #include "absl/algorithm/container.h"
24 #include "api/scoped_refptr.h"
25 #include "api/video/video_content_type.h"
26 #include "api/video/video_frame_buffer.h"
27 #include "api/video/video_timing.h"
28 #include "api/video_codecs/vp8_temporal_layers.h"
29 #include "api/video_codecs/vp8_temporal_layers_factory.h"
30 #include "modules/video_coding/codecs/interface/common_constants.h"
31 #include "modules/video_coding/codecs/vp8/include/vp8.h"
32 #include "modules/video_coding/codecs/vp8/vp8_scalability.h"
33 #include "modules/video_coding/include/video_error_codes.h"
34 #include "modules/video_coding/svc/scalability_mode_util.h"
35 #include "modules/video_coding/utility/simulcast_rate_allocator.h"
36 #include "modules/video_coding/utility/simulcast_utility.h"
37 #include "rtc_base/checks.h"
38 #include "rtc_base/experiments/field_trial_parser.h"
39 #include "rtc_base/experiments/field_trial_units.h"
40 #include "rtc_base/logging.h"
41 #include "rtc_base/trace_event.h"
42 #include "system_wrappers/include/field_trial.h"
43 #include "third_party/libyuv/include/libyuv/scale.h"
44 #include "vpx/vp8cx.h"
45
46 namespace webrtc {
47 namespace {
48 #if defined(WEBRTC_IOS)
49 constexpr char kVP8IosMaxNumberOfThreadFieldTrial[] =
50 "WebRTC-VP8IosMaxNumberOfThread";
51 constexpr char kVP8IosMaxNumberOfThreadFieldTrialParameter[] = "max_thread";
52 #endif
53
54 constexpr char kVp8ForcePartitionResilience[] =
55 "WebRTC-VP8-ForcePartitionResilience";
56
57 // QP is obtained from VP8-bitstream for HW, so the QP corresponds to the
58 // bitstream range of [0, 127] and not the user-level range of [0,63].
59 constexpr int kLowVp8QpThreshold = 29;
60 constexpr int kHighVp8QpThreshold = 95;
61
62 constexpr int kTokenPartitions = VP8_ONE_TOKENPARTITION;
63 constexpr uint32_t kVp832ByteAlign = 32u;
64
65 constexpr int kRtpTicksPerSecond = 90000;
66 constexpr int kRtpTicksPerMs = kRtpTicksPerSecond / 1000;
67
68 // VP8 denoiser states.
69 enum denoiserState : uint32_t {
70 kDenoiserOff,
71 kDenoiserOnYOnly,
72 kDenoiserOnYUV,
73 kDenoiserOnYUVAggressive,
74 // Adaptive mode defaults to kDenoiserOnYUV on key frame, but may switch
75 // to kDenoiserOnYUVAggressive based on a computed noise metric.
76 kDenoiserOnAdaptive
77 };
78
79 // Greatest common divisior
GCD(int a,int b)80 int GCD(int a, int b) {
81 int c = a % b;
82 while (c != 0) {
83 a = b;
84 b = c;
85 c = a % b;
86 }
87 return b;
88 }
89
90 static_assert(Vp8EncoderConfig::TemporalLayerConfig::kMaxPeriodicity ==
91 VPX_TS_MAX_PERIODICITY,
92 "Vp8EncoderConfig::kMaxPeriodicity must be kept in sync with the "
93 "constant in libvpx.");
94 static_assert(Vp8EncoderConfig::TemporalLayerConfig::kMaxLayers ==
95 VPX_TS_MAX_LAYERS,
96 "Vp8EncoderConfig::kMaxLayers must be kept in sync with the "
97 "constant in libvpx.");
98
99 // Allow a newer value to override a current value only if the new value
100 // is set.
101 template <typename T>
MaybeSetNewValue(const absl::optional<T> & new_value,absl::optional<T> * base_value)102 bool MaybeSetNewValue(const absl::optional<T>& new_value,
103 absl::optional<T>* base_value) {
104 if (new_value.has_value() && new_value != *base_value) {
105 *base_value = new_value;
106 return true;
107 } else {
108 return false;
109 }
110 }
111
112 // Adds configuration from `new_config` to `base_config`. Both configs consist
113 // of optionals, and only optionals which are set in `new_config` can have
114 // an effect. (That is, set values in `base_config` cannot be unset.)
115 // Returns `true` iff any changes were made to `base_config`.
MaybeExtendVp8EncoderConfig(const Vp8EncoderConfig & new_config,Vp8EncoderConfig * base_config)116 bool MaybeExtendVp8EncoderConfig(const Vp8EncoderConfig& new_config,
117 Vp8EncoderConfig* base_config) {
118 bool changes_made = false;
119 changes_made |= MaybeSetNewValue(new_config.temporal_layer_config,
120 &base_config->temporal_layer_config);
121 changes_made |= MaybeSetNewValue(new_config.rc_target_bitrate,
122 &base_config->rc_target_bitrate);
123 changes_made |= MaybeSetNewValue(new_config.rc_max_quantizer,
124 &base_config->rc_max_quantizer);
125 changes_made |= MaybeSetNewValue(new_config.g_error_resilient,
126 &base_config->g_error_resilient);
127 return changes_made;
128 }
129
ApplyVp8EncoderConfigToVpxConfig(const Vp8EncoderConfig & encoder_config,vpx_codec_enc_cfg_t * vpx_config)130 void ApplyVp8EncoderConfigToVpxConfig(const Vp8EncoderConfig& encoder_config,
131 vpx_codec_enc_cfg_t* vpx_config) {
132 if (encoder_config.temporal_layer_config.has_value()) {
133 const Vp8EncoderConfig::TemporalLayerConfig& ts_config =
134 encoder_config.temporal_layer_config.value();
135 vpx_config->ts_number_layers = ts_config.ts_number_layers;
136 std::copy(ts_config.ts_target_bitrate.begin(),
137 ts_config.ts_target_bitrate.end(),
138 std::begin(vpx_config->ts_target_bitrate));
139 std::copy(ts_config.ts_rate_decimator.begin(),
140 ts_config.ts_rate_decimator.end(),
141 std::begin(vpx_config->ts_rate_decimator));
142 vpx_config->ts_periodicity = ts_config.ts_periodicity;
143 std::copy(ts_config.ts_layer_id.begin(), ts_config.ts_layer_id.end(),
144 std::begin(vpx_config->ts_layer_id));
145 } else {
146 vpx_config->ts_number_layers = 1;
147 vpx_config->ts_rate_decimator[0] = 1;
148 vpx_config->ts_periodicity = 1;
149 vpx_config->ts_layer_id[0] = 0;
150 }
151
152 if (encoder_config.rc_target_bitrate.has_value()) {
153 vpx_config->rc_target_bitrate = encoder_config.rc_target_bitrate.value();
154 }
155
156 if (encoder_config.rc_max_quantizer.has_value()) {
157 vpx_config->rc_max_quantizer = encoder_config.rc_max_quantizer.value();
158 }
159
160 if (encoder_config.g_error_resilient.has_value()) {
161 vpx_config->g_error_resilient = encoder_config.g_error_resilient.value();
162 }
163 }
164
IsCompatibleVideoFrameBufferType(VideoFrameBuffer::Type left,VideoFrameBuffer::Type right)165 bool IsCompatibleVideoFrameBufferType(VideoFrameBuffer::Type left,
166 VideoFrameBuffer::Type right) {
167 if (left == VideoFrameBuffer::Type::kI420 ||
168 left == VideoFrameBuffer::Type::kI420A) {
169 // LibvpxVp8Encoder does not care about the alpha channel, I420A and I420
170 // are considered compatible.
171 return right == VideoFrameBuffer::Type::kI420 ||
172 right == VideoFrameBuffer::Type::kI420A;
173 }
174 return left == right;
175 }
176
SetRawImagePlanes(vpx_image_t * raw_image,VideoFrameBuffer * buffer)177 void SetRawImagePlanes(vpx_image_t* raw_image, VideoFrameBuffer* buffer) {
178 switch (buffer->type()) {
179 case VideoFrameBuffer::Type::kI420:
180 case VideoFrameBuffer::Type::kI420A: {
181 const I420BufferInterface* i420_buffer = buffer->GetI420();
182 RTC_DCHECK(i420_buffer);
183 raw_image->planes[VPX_PLANE_Y] =
184 const_cast<uint8_t*>(i420_buffer->DataY());
185 raw_image->planes[VPX_PLANE_U] =
186 const_cast<uint8_t*>(i420_buffer->DataU());
187 raw_image->planes[VPX_PLANE_V] =
188 const_cast<uint8_t*>(i420_buffer->DataV());
189 raw_image->stride[VPX_PLANE_Y] = i420_buffer->StrideY();
190 raw_image->stride[VPX_PLANE_U] = i420_buffer->StrideU();
191 raw_image->stride[VPX_PLANE_V] = i420_buffer->StrideV();
192 break;
193 }
194 case VideoFrameBuffer::Type::kNV12: {
195 const NV12BufferInterface* nv12_buffer = buffer->GetNV12();
196 RTC_DCHECK(nv12_buffer);
197 raw_image->planes[VPX_PLANE_Y] =
198 const_cast<uint8_t*>(nv12_buffer->DataY());
199 raw_image->planes[VPX_PLANE_U] =
200 const_cast<uint8_t*>(nv12_buffer->DataUV());
201 raw_image->planes[VPX_PLANE_V] = raw_image->planes[VPX_PLANE_U] + 1;
202 raw_image->stride[VPX_PLANE_Y] = nv12_buffer->StrideY();
203 raw_image->stride[VPX_PLANE_U] = nv12_buffer->StrideUV();
204 raw_image->stride[VPX_PLANE_V] = nv12_buffer->StrideUV();
205 break;
206 }
207 default:
208 RTC_DCHECK_NOTREACHED();
209 }
210 }
211
212 } // namespace
213
Create()214 std::unique_ptr<VideoEncoder> VP8Encoder::Create() {
215 return std::make_unique<LibvpxVp8Encoder>(LibvpxInterface::Create(),
216 VP8Encoder::Settings());
217 }
218
Create(VP8Encoder::Settings settings)219 std::unique_ptr<VideoEncoder> VP8Encoder::Create(
220 VP8Encoder::Settings settings) {
221 return std::make_unique<LibvpxVp8Encoder>(LibvpxInterface::Create(),
222 std::move(settings));
223 }
224
EncodeFlags(const Vp8FrameConfig & references)225 vpx_enc_frame_flags_t LibvpxVp8Encoder::EncodeFlags(
226 const Vp8FrameConfig& references) {
227 RTC_DCHECK(!references.drop_frame);
228
229 vpx_enc_frame_flags_t flags = 0;
230
231 if ((references.last_buffer_flags &
232 Vp8FrameConfig::BufferFlags::kReference) == 0)
233 flags |= VP8_EFLAG_NO_REF_LAST;
234 if ((references.last_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) ==
235 0)
236 flags |= VP8_EFLAG_NO_UPD_LAST;
237 if ((references.golden_buffer_flags &
238 Vp8FrameConfig::BufferFlags::kReference) == 0)
239 flags |= VP8_EFLAG_NO_REF_GF;
240 if ((references.golden_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) ==
241 0)
242 flags |= VP8_EFLAG_NO_UPD_GF;
243 if ((references.arf_buffer_flags & Vp8FrameConfig::BufferFlags::kReference) ==
244 0)
245 flags |= VP8_EFLAG_NO_REF_ARF;
246 if ((references.arf_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) == 0)
247 flags |= VP8_EFLAG_NO_UPD_ARF;
248 if (references.freeze_entropy)
249 flags |= VP8_EFLAG_NO_UPD_ENTROPY;
250
251 return flags;
252 }
253
LibvpxVp8Encoder(std::unique_ptr<LibvpxInterface> interface,VP8Encoder::Settings settings)254 LibvpxVp8Encoder::LibvpxVp8Encoder(std::unique_ptr<LibvpxInterface> interface,
255 VP8Encoder::Settings settings)
256 : libvpx_(std::move(interface)),
257 rate_control_settings_(RateControlSettings::ParseFromFieldTrials()),
258 frame_buffer_controller_factory_(
259 std::move(settings.frame_buffer_controller_factory)),
260 resolution_bitrate_limits_(std::move(settings.resolution_bitrate_limits)),
261 key_frame_request_(kMaxSimulcastStreams, false),
262 variable_framerate_experiment_(ParseVariableFramerateConfig(
263 "WebRTC-VP8VariableFramerateScreenshare")),
264 framerate_controller_(variable_framerate_experiment_.framerate_limit) {
265 // TODO(eladalon/ilnik): These reservations might be wasting memory.
266 // InitEncode() is resizing to the actual size, which might be smaller.
267 raw_images_.reserve(kMaxSimulcastStreams);
268 encoded_images_.reserve(kMaxSimulcastStreams);
269 send_stream_.reserve(kMaxSimulcastStreams);
270 cpu_speed_.assign(kMaxSimulcastStreams, cpu_speed_default_);
271 encoders_.reserve(kMaxSimulcastStreams);
272 vpx_configs_.reserve(kMaxSimulcastStreams);
273 config_overrides_.reserve(kMaxSimulcastStreams);
274 downsampling_factors_.reserve(kMaxSimulcastStreams);
275 }
276
~LibvpxVp8Encoder()277 LibvpxVp8Encoder::~LibvpxVp8Encoder() {
278 Release();
279 }
280
Release()281 int LibvpxVp8Encoder::Release() {
282 int ret_val = WEBRTC_VIDEO_CODEC_OK;
283
284 encoded_images_.clear();
285
286 if (inited_) {
287 for (auto it = encoders_.rbegin(); it != encoders_.rend(); ++it) {
288 if (libvpx_->codec_destroy(&*it)) {
289 ret_val = WEBRTC_VIDEO_CODEC_MEMORY;
290 }
291 }
292 }
293 encoders_.clear();
294
295 vpx_configs_.clear();
296 config_overrides_.clear();
297 send_stream_.clear();
298 cpu_speed_.clear();
299
300 for (auto it = raw_images_.rbegin(); it != raw_images_.rend(); ++it) {
301 libvpx_->img_free(&*it);
302 }
303 raw_images_.clear();
304
305 frame_buffer_controller_.reset();
306 inited_ = false;
307 return ret_val;
308 }
309
SetRates(const RateControlParameters & parameters)310 void LibvpxVp8Encoder::SetRates(const RateControlParameters& parameters) {
311 if (!inited_) {
312 RTC_LOG(LS_WARNING) << "SetRates() while not initialize";
313 return;
314 }
315
316 if (encoders_[0].err) {
317 RTC_LOG(LS_WARNING) << "Encoder in error state.";
318 return;
319 }
320
321 if (parameters.framerate_fps < 1.0) {
322 RTC_LOG(LS_WARNING) << "Unsupported framerate (must be >= 1.0): "
323 << parameters.framerate_fps;
324 return;
325 }
326
327 if (parameters.bitrate.get_sum_bps() == 0) {
328 // Encoder paused, turn off all encoding.
329 const int num_streams = static_cast<size_t>(encoders_.size());
330 for (int i = 0; i < num_streams; ++i)
331 SetStreamState(false, i);
332 return;
333 }
334
335 codec_.maxFramerate = static_cast<uint32_t>(parameters.framerate_fps + 0.5);
336
337 if (encoders_.size() > 1) {
338 // If we have more than 1 stream, reduce the qp_max for the low resolution
339 // stream if frame rate is not too low. The trade-off with lower qp_max is
340 // possibly more dropped frames, so we only do this if the frame rate is
341 // above some threshold (base temporal layer is down to 1/4 for 3 layers).
342 // We may want to condition this on bitrate later.
343 if (rate_control_settings_.Vp8BoostBaseLayerQuality() &&
344 parameters.framerate_fps > 20.0) {
345 vpx_configs_[encoders_.size() - 1].rc_max_quantizer = 45;
346 } else {
347 // Go back to default value set in InitEncode.
348 vpx_configs_[encoders_.size() - 1].rc_max_quantizer = qp_max_;
349 }
350 }
351
352 for (size_t i = 0; i < encoders_.size(); ++i) {
353 const size_t stream_idx = encoders_.size() - 1 - i;
354
355 unsigned int target_bitrate_kbps =
356 parameters.bitrate.GetSpatialLayerSum(stream_idx) / 1000;
357
358 bool send_stream = target_bitrate_kbps > 0;
359 if (send_stream || encoders_.size() > 1)
360 SetStreamState(send_stream, stream_idx);
361
362 vpx_configs_[i].rc_target_bitrate = target_bitrate_kbps;
363 if (send_stream) {
364 frame_buffer_controller_->OnRatesUpdated(
365 stream_idx, parameters.bitrate.GetTemporalLayerAllocation(stream_idx),
366 static_cast<int>(parameters.framerate_fps + 0.5));
367 }
368
369 UpdateVpxConfiguration(stream_idx);
370
371 vpx_codec_err_t err =
372 libvpx_->codec_enc_config_set(&encoders_[i], &vpx_configs_[i]);
373 if (err != VPX_CODEC_OK) {
374 RTC_LOG(LS_WARNING) << "Error configuring codec, error code: " << err
375 << ", details: "
376 << libvpx_->codec_error_detail(&encoders_[i]);
377 }
378 }
379 }
380
OnPacketLossRateUpdate(float packet_loss_rate)381 void LibvpxVp8Encoder::OnPacketLossRateUpdate(float packet_loss_rate) {
382 // TODO(bugs.webrtc.org/10431): Replace condition by DCHECK.
383 if (frame_buffer_controller_) {
384 frame_buffer_controller_->OnPacketLossRateUpdate(packet_loss_rate);
385 }
386 }
387
OnRttUpdate(int64_t rtt_ms)388 void LibvpxVp8Encoder::OnRttUpdate(int64_t rtt_ms) {
389 // TODO(bugs.webrtc.org/10431): Replace condition by DCHECK.
390 if (frame_buffer_controller_) {
391 frame_buffer_controller_->OnRttUpdate(rtt_ms);
392 }
393 }
394
OnLossNotification(const LossNotification & loss_notification)395 void LibvpxVp8Encoder::OnLossNotification(
396 const LossNotification& loss_notification) {
397 if (frame_buffer_controller_) {
398 frame_buffer_controller_->OnLossNotification(loss_notification);
399 }
400 }
401
SetStreamState(bool send_stream,int stream_idx)402 void LibvpxVp8Encoder::SetStreamState(bool send_stream, int stream_idx) {
403 if (send_stream && !send_stream_[stream_idx]) {
404 // Need a key frame if we have not sent this stream before.
405 key_frame_request_[stream_idx] = true;
406 }
407 send_stream_[stream_idx] = send_stream;
408 }
409
SetFecControllerOverride(FecControllerOverride * fec_controller_override)410 void LibvpxVp8Encoder::SetFecControllerOverride(
411 FecControllerOverride* fec_controller_override) {
412 // TODO(bugs.webrtc.org/10769): Update downstream and remove ability to
413 // pass nullptr.
414 // RTC_DCHECK(fec_controller_override);
415 RTC_DCHECK(!fec_controller_override_);
416 fec_controller_override_ = fec_controller_override;
417 }
418
419 // TODO(eladalon): s/inst/codec_settings/g.
InitEncode(const VideoCodec * inst,const VideoEncoder::Settings & settings)420 int LibvpxVp8Encoder::InitEncode(const VideoCodec* inst,
421 const VideoEncoder::Settings& settings) {
422 if (inst == NULL) {
423 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
424 }
425 if (inst->maxFramerate < 1) {
426 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
427 }
428 // allow zero to represent an unspecified maxBitRate
429 if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {
430 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
431 }
432 if (inst->width < 1 || inst->height < 1) {
433 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
434 }
435 if (settings.number_of_cores < 1) {
436 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
437 }
438
439 if (absl::optional<ScalabilityMode> scalability_mode =
440 inst->GetScalabilityMode();
441 scalability_mode.has_value() &&
442 !VP8SupportsScalabilityMode(*scalability_mode)) {
443 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
444 }
445
446 num_active_streams_ = 0;
447 for (int i = 0; i < inst->numberOfSimulcastStreams; ++i) {
448 if (inst->simulcastStream[i].active) {
449 ++num_active_streams_;
450 }
451 }
452 if (inst->numberOfSimulcastStreams == 0 && inst->active) {
453 num_active_streams_ = 1;
454 }
455
456 if (inst->VP8().automaticResizeOn && num_active_streams_ > 1) {
457 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
458 }
459
460 // Use the previous pixel format to avoid extra image allocations.
461 vpx_img_fmt_t pixel_format =
462 raw_images_.empty() ? VPX_IMG_FMT_I420 : raw_images_[0].fmt;
463
464 int retVal = Release();
465 if (retVal < 0) {
466 return retVal;
467 }
468
469 int number_of_streams = SimulcastUtility::NumberOfSimulcastStreams(*inst);
470 if (number_of_streams > 1 &&
471 !SimulcastUtility::ValidSimulcastParameters(*inst, number_of_streams)) {
472 return WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED;
473 }
474
475 RTC_DCHECK(!frame_buffer_controller_);
476 if (frame_buffer_controller_factory_) {
477 frame_buffer_controller_ = frame_buffer_controller_factory_->Create(
478 *inst, settings, fec_controller_override_);
479 } else {
480 Vp8TemporalLayersFactory factory;
481 frame_buffer_controller_ =
482 factory.Create(*inst, settings, fec_controller_override_);
483 }
484 RTC_DCHECK(frame_buffer_controller_);
485
486 number_of_cores_ = settings.number_of_cores;
487 timestamp_ = 0;
488 codec_ = *inst;
489
490 // Code expects simulcastStream resolutions to be correct, make sure they are
491 // filled even when there are no simulcast layers.
492 if (codec_.numberOfSimulcastStreams == 0) {
493 codec_.simulcastStream[0].width = codec_.width;
494 codec_.simulcastStream[0].height = codec_.height;
495 }
496
497 encoded_images_.resize(number_of_streams);
498 encoders_.resize(number_of_streams);
499 vpx_configs_.resize(number_of_streams);
500 config_overrides_.resize(number_of_streams);
501 downsampling_factors_.resize(number_of_streams);
502 raw_images_.resize(number_of_streams);
503 send_stream_.resize(number_of_streams);
504 send_stream_[0] = true; // For non-simulcast case.
505 cpu_speed_.resize(number_of_streams);
506 std::fill(key_frame_request_.begin(), key_frame_request_.end(), false);
507
508 int idx = number_of_streams - 1;
509 for (int i = 0; i < (number_of_streams - 1); ++i, --idx) {
510 int gcd = GCD(inst->simulcastStream[idx].width,
511 inst->simulcastStream[idx - 1].width);
512 downsampling_factors_[i].num = inst->simulcastStream[idx].width / gcd;
513 downsampling_factors_[i].den = inst->simulcastStream[idx - 1].width / gcd;
514 send_stream_[i] = false;
515 }
516 if (number_of_streams > 1) {
517 send_stream_[number_of_streams - 1] = false;
518 downsampling_factors_[number_of_streams - 1].num = 1;
519 downsampling_factors_[number_of_streams - 1].den = 1;
520 }
521
522 // populate encoder configuration with default values
523 if (libvpx_->codec_enc_config_default(vpx_codec_vp8_cx(), &vpx_configs_[0],
524 0)) {
525 return WEBRTC_VIDEO_CODEC_ERROR;
526 }
527 // setting the time base of the codec
528 vpx_configs_[0].g_timebase.num = 1;
529 vpx_configs_[0].g_timebase.den = kRtpTicksPerSecond;
530 vpx_configs_[0].g_lag_in_frames = 0; // 0- no frame lagging
531
532 // Set the error resilience mode for temporal layers (but not simulcast).
533 vpx_configs_[0].g_error_resilient =
534 (SimulcastUtility::NumberOfTemporalLayers(*inst, 0) > 1)
535 ? VPX_ERROR_RESILIENT_DEFAULT
536 : 0;
537
538 // Override the error resilience mode if this is not simulcast, but we are
539 // using temporal layers.
540 if (field_trial::IsEnabled(kVp8ForcePartitionResilience) &&
541 (number_of_streams == 1) &&
542 (SimulcastUtility::NumberOfTemporalLayers(*inst, 0) > 1)) {
543 RTC_LOG(LS_INFO) << "Overriding g_error_resilient from "
544 << vpx_configs_[0].g_error_resilient << " to "
545 << VPX_ERROR_RESILIENT_PARTITIONS;
546 vpx_configs_[0].g_error_resilient = VPX_ERROR_RESILIENT_PARTITIONS;
547 }
548
549 // rate control settings
550 vpx_configs_[0].rc_dropframe_thresh = FrameDropThreshold(0);
551 vpx_configs_[0].rc_end_usage = VPX_CBR;
552 vpx_configs_[0].g_pass = VPX_RC_ONE_PASS;
553 // Handle resizing outside of libvpx.
554 vpx_configs_[0].rc_resize_allowed = 0;
555 vpx_configs_[0].rc_min_quantizer =
556 codec_.mode == VideoCodecMode::kScreensharing ? 12 : 2;
557 if (inst->qpMax >= vpx_configs_[0].rc_min_quantizer) {
558 qp_max_ = inst->qpMax;
559 }
560 if (rate_control_settings_.LibvpxVp8QpMax()) {
561 qp_max_ = std::max(rate_control_settings_.LibvpxVp8QpMax().value(),
562 static_cast<int>(vpx_configs_[0].rc_min_quantizer));
563 }
564 vpx_configs_[0].rc_max_quantizer = qp_max_;
565 vpx_configs_[0].rc_undershoot_pct = 100;
566 vpx_configs_[0].rc_overshoot_pct = 15;
567 vpx_configs_[0].rc_buf_initial_sz = 500;
568 vpx_configs_[0].rc_buf_optimal_sz = 600;
569 vpx_configs_[0].rc_buf_sz = 1000;
570
571 // Set the maximum target size of any key-frame.
572 rc_max_intra_target_ = MaxIntraTarget(vpx_configs_[0].rc_buf_optimal_sz);
573
574 if (inst->VP8().keyFrameInterval > 0) {
575 vpx_configs_[0].kf_mode = VPX_KF_AUTO;
576 vpx_configs_[0].kf_max_dist = inst->VP8().keyFrameInterval;
577 } else {
578 vpx_configs_[0].kf_mode = VPX_KF_DISABLED;
579 }
580
581 // Allow the user to set the complexity for the base stream.
582 switch (inst->GetVideoEncoderComplexity()) {
583 case VideoCodecComplexity::kComplexityHigh:
584 cpu_speed_[0] = -5;
585 break;
586 case VideoCodecComplexity::kComplexityHigher:
587 cpu_speed_[0] = -4;
588 break;
589 case VideoCodecComplexity::kComplexityMax:
590 cpu_speed_[0] = -3;
591 break;
592 default:
593 cpu_speed_[0] = -6;
594 break;
595 }
596 cpu_speed_default_ = cpu_speed_[0];
597 // Set encoding complexity (cpu_speed) based on resolution and/or platform.
598 cpu_speed_[0] = GetCpuSpeed(inst->width, inst->height);
599 for (int i = 1; i < number_of_streams; ++i) {
600 cpu_speed_[i] =
601 GetCpuSpeed(inst->simulcastStream[number_of_streams - 1 - i].width,
602 inst->simulcastStream[number_of_streams - 1 - i].height);
603 }
604 vpx_configs_[0].g_w = inst->width;
605 vpx_configs_[0].g_h = inst->height;
606
607 // Determine number of threads based on the image size and #cores.
608 // TODO(fbarchard): Consider number of Simulcast layers.
609 vpx_configs_[0].g_threads = NumberOfThreads(
610 vpx_configs_[0].g_w, vpx_configs_[0].g_h, settings.number_of_cores);
611
612 // Creating a wrapper to the image - setting image data to NULL.
613 // Actual pointer will be set in encode. Setting align to 1, as it
614 // is meaningless (no memory allocation is done here).
615 libvpx_->img_wrap(&raw_images_[0], pixel_format, inst->width, inst->height, 1,
616 NULL);
617
618 // Note the order we use is different from webm, we have lowest resolution
619 // at position 0 and they have highest resolution at position 0.
620 const size_t stream_idx_cfg_0 = encoders_.size() - 1;
621 SimulcastRateAllocator init_allocator(codec_);
622 VideoBitrateAllocation allocation =
623 init_allocator.Allocate(VideoBitrateAllocationParameters(
624 inst->startBitrate * 1000, inst->maxFramerate));
625 std::vector<uint32_t> stream_bitrates;
626 for (int i = 0; i == 0 || i < inst->numberOfSimulcastStreams; ++i) {
627 uint32_t bitrate = allocation.GetSpatialLayerSum(i) / 1000;
628 stream_bitrates.push_back(bitrate);
629 }
630
631 vpx_configs_[0].rc_target_bitrate = stream_bitrates[stream_idx_cfg_0];
632 if (stream_bitrates[stream_idx_cfg_0] > 0) {
633 uint32_t maxFramerate =
634 inst->simulcastStream[stream_idx_cfg_0].maxFramerate;
635 if (!maxFramerate) {
636 maxFramerate = inst->maxFramerate;
637 }
638
639 frame_buffer_controller_->OnRatesUpdated(
640 stream_idx_cfg_0,
641 allocation.GetTemporalLayerAllocation(stream_idx_cfg_0), maxFramerate);
642 }
643 frame_buffer_controller_->SetQpLimits(stream_idx_cfg_0,
644 vpx_configs_[0].rc_min_quantizer,
645 vpx_configs_[0].rc_max_quantizer);
646 UpdateVpxConfiguration(stream_idx_cfg_0);
647 vpx_configs_[0].rc_dropframe_thresh = FrameDropThreshold(stream_idx_cfg_0);
648
649 for (size_t i = 1; i < encoders_.size(); ++i) {
650 const size_t stream_idx = encoders_.size() - 1 - i;
651 memcpy(&vpx_configs_[i], &vpx_configs_[0], sizeof(vpx_configs_[0]));
652
653 vpx_configs_[i].g_w = inst->simulcastStream[stream_idx].width;
654 vpx_configs_[i].g_h = inst->simulcastStream[stream_idx].height;
655
656 // Use 1 thread for lower resolutions.
657 vpx_configs_[i].g_threads = 1;
658
659 vpx_configs_[i].rc_dropframe_thresh = FrameDropThreshold(stream_idx);
660
661 // Setting alignment to 32 - as that ensures at least 16 for all
662 // planes (32 for Y, 16 for U,V). Libvpx sets the requested stride for
663 // the y plane, but only half of it to the u and v planes.
664 libvpx_->img_alloc(
665 &raw_images_[i], pixel_format, inst->simulcastStream[stream_idx].width,
666 inst->simulcastStream[stream_idx].height, kVp832ByteAlign);
667 SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
668 vpx_configs_[i].rc_target_bitrate = stream_bitrates[stream_idx];
669 if (stream_bitrates[stream_idx] > 0) {
670 uint32_t maxFramerate = inst->simulcastStream[stream_idx].maxFramerate;
671 if (!maxFramerate) {
672 maxFramerate = inst->maxFramerate;
673 }
674 frame_buffer_controller_->OnRatesUpdated(
675 stream_idx, allocation.GetTemporalLayerAllocation(stream_idx),
676 maxFramerate);
677 }
678 frame_buffer_controller_->SetQpLimits(stream_idx,
679 vpx_configs_[i].rc_min_quantizer,
680 vpx_configs_[i].rc_max_quantizer);
681 UpdateVpxConfiguration(stream_idx);
682 }
683
684 return InitAndSetControlSettings();
685 }
686
GetCpuSpeed(int width,int height)687 int LibvpxVp8Encoder::GetCpuSpeed(int width, int height) {
688 #if defined(WEBRTC_ARCH_ARM) || defined(WEBRTC_ARCH_ARM64) || \
689 defined(WEBRTC_ANDROID)
690 // On mobile platform, use a lower speed setting for lower resolutions for
691 // CPUs with 4 or more cores.
692 RTC_DCHECK_GT(number_of_cores_, 0);
693 if (experimental_cpu_speed_config_arm_
694 .GetValue(width * height, number_of_cores_)
695 .has_value()) {
696 return experimental_cpu_speed_config_arm_
697 .GetValue(width * height, number_of_cores_)
698 .value();
699 }
700
701 if (number_of_cores_ <= 3)
702 return -12;
703
704 if (width * height <= 352 * 288)
705 return -8;
706 else if (width * height <= 640 * 480)
707 return -10;
708 else
709 return -12;
710 #else
711 // For non-ARM, increase encoding complexity (i.e., use lower speed setting)
712 // if resolution is below CIF. Otherwise, keep the default/user setting
713 // (`cpu_speed_default_`) set on InitEncode via VP8().complexity.
714 if (width * height < 352 * 288)
715 return (cpu_speed_default_ < -4) ? -4 : cpu_speed_default_;
716 else
717 return cpu_speed_default_;
718 #endif
719 }
720
NumberOfThreads(int width,int height,int cpus)721 int LibvpxVp8Encoder::NumberOfThreads(int width, int height, int cpus) {
722 #if defined(WEBRTC_ANDROID)
723 if (width * height >= 320 * 180) {
724 if (cpus >= 4) {
725 // 3 threads for CPUs with 4 and more cores since most of times only 4
726 // cores will be active.
727 return 3;
728 } else if (cpus == 3 || cpus == 2) {
729 return 2;
730 } else {
731 return 1;
732 }
733 }
734 return 1;
735 #else
736 #if defined(WEBRTC_IOS)
737 std::string trial_string =
738 field_trial::FindFullName(kVP8IosMaxNumberOfThreadFieldTrial);
739 FieldTrialParameter<int> max_thread_number(
740 kVP8IosMaxNumberOfThreadFieldTrialParameter, 0);
741 ParseFieldTrial({&max_thread_number}, trial_string);
742 if (max_thread_number.Get() > 0) {
743 if (width * height < 320 * 180) {
744 return 1; // Use single thread for small screens
745 }
746 // thread number must be less than or equal to the number of CPUs.
747 return std::min(cpus, max_thread_number.Get());
748 }
749 #endif // defined(WEBRTC_IOS)
750 if (width * height >= 1920 * 1080 && cpus > 8) {
751 return 8; // 8 threads for 1080p on high perf machines.
752 } else if (width * height > 1280 * 960 && cpus >= 6) {
753 // 3 threads for 1080p.
754 return 3;
755 } else if (width * height > 640 * 480 && cpus >= 3) {
756 // Default 2 threads for qHD/HD, but allow 3 if core count is high enough,
757 // as this will allow more margin for high-core/low clock machines or if
758 // not built with highest optimization.
759 if (cpus >= 6) {
760 return 3;
761 }
762 return 2;
763 } else {
764 // 1 thread for VGA or less.
765 return 1;
766 }
767 #endif
768 }
769
InitAndSetControlSettings()770 int LibvpxVp8Encoder::InitAndSetControlSettings() {
771 vpx_codec_flags_t flags = 0;
772 flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
773
774 if (encoders_.size() > 1) {
775 int error = libvpx_->codec_enc_init_multi(
776 &encoders_[0], vpx_codec_vp8_cx(), &vpx_configs_[0], encoders_.size(),
777 flags, &downsampling_factors_[0]);
778 if (error) {
779 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
780 }
781 } else {
782 if (libvpx_->codec_enc_init(&encoders_[0], vpx_codec_vp8_cx(),
783 &vpx_configs_[0], flags)) {
784 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
785 }
786 }
787 // Enable denoising for the highest resolution stream, and for
788 // the second highest resolution if we are doing more than 2
789 // spatial layers/streams.
790 // TODO(holmer): Investigate possibility of adding a libvpx API
791 // for getting the denoised frame from the encoder and using that
792 // when encoding lower resolution streams. Would it work with the
793 // multi-res encoding feature?
794 denoiserState denoiser_state = kDenoiserOnYOnly;
795 #if defined(WEBRTC_ARCH_ARM) || defined(WEBRTC_ARCH_ARM64) || \
796 defined(WEBRTC_ANDROID)
797 denoiser_state = kDenoiserOnYOnly;
798 #else
799 denoiser_state = kDenoiserOnAdaptive;
800 #endif
801 libvpx_->codec_control(
802 &encoders_[0], VP8E_SET_NOISE_SENSITIVITY,
803 codec_.VP8()->denoisingOn ? denoiser_state : kDenoiserOff);
804 if (encoders_.size() > 2) {
805 libvpx_->codec_control(
806 &encoders_[1], VP8E_SET_NOISE_SENSITIVITY,
807 codec_.VP8()->denoisingOn ? denoiser_state : kDenoiserOff);
808 }
809 for (size_t i = 0; i < encoders_.size(); ++i) {
810 // Allow more screen content to be detected as static.
811 libvpx_->codec_control(
812 &(encoders_[i]), VP8E_SET_STATIC_THRESHOLD,
813 codec_.mode == VideoCodecMode::kScreensharing ? 100u : 1u);
814 libvpx_->codec_control(&(encoders_[i]), VP8E_SET_CPUUSED, cpu_speed_[i]);
815 libvpx_->codec_control(
816 &(encoders_[i]), VP8E_SET_TOKEN_PARTITIONS,
817 static_cast<vp8e_token_partitions>(kTokenPartitions));
818 libvpx_->codec_control(&(encoders_[i]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
819 rc_max_intra_target_);
820 // VP8E_SET_SCREEN_CONTENT_MODE 2 = screen content with more aggressive
821 // rate control (drop frames on large target bitrate overshoot)
822 libvpx_->codec_control(
823 &(encoders_[i]), VP8E_SET_SCREEN_CONTENT_MODE,
824 codec_.mode == VideoCodecMode::kScreensharing ? 2u : 0u);
825 }
826 inited_ = true;
827 return WEBRTC_VIDEO_CODEC_OK;
828 }
829
MaxIntraTarget(uint32_t optimalBuffersize)830 uint32_t LibvpxVp8Encoder::MaxIntraTarget(uint32_t optimalBuffersize) {
831 // Set max to the optimal buffer level (normalized by target BR),
832 // and scaled by a scalePar.
833 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps].
834 // This values is presented in percentage of perFrameBw:
835 // perFrameBw = targetBR[Kbps] * 1000 / frameRate.
836 // The target in % is as follows:
837
838 float scalePar = 0.5;
839 uint32_t targetPct = optimalBuffersize * scalePar * codec_.maxFramerate / 10;
840
841 // Don't go below 3 times the per frame bandwidth.
842 const uint32_t minIntraTh = 300;
843 return (targetPct < minIntraTh) ? minIntraTh : targetPct;
844 }
845
FrameDropThreshold(size_t spatial_idx) const846 uint32_t LibvpxVp8Encoder::FrameDropThreshold(size_t spatial_idx) const {
847 if (!codec_.GetFrameDropEnabled()) {
848 return 0;
849 }
850
851 // If temporal layers are used, they get to override the frame dropping
852 // setting, as eg. ScreenshareLayers does not work as intended with frame
853 // dropping on and DefaultTemporalLayers will have performance issues with
854 // frame dropping off.
855 RTC_DCHECK(frame_buffer_controller_);
856 RTC_DCHECK_LT(spatial_idx, frame_buffer_controller_->StreamCount());
857 return frame_buffer_controller_->SupportsEncoderFrameDropping(spatial_idx)
858 ? 30
859 : 0;
860 }
861
SteadyStateSize(int sid,int tid)862 size_t LibvpxVp8Encoder::SteadyStateSize(int sid, int tid) {
863 const int encoder_id = encoders_.size() - 1 - sid;
864 size_t bitrate_bps;
865 float fps;
866 if ((SimulcastUtility::IsConferenceModeScreenshare(codec_) && sid == 0) ||
867 vpx_configs_[encoder_id].ts_number_layers <= 1) {
868 // In conference screenshare there's no defined per temporal layer bitrate
869 // and framerate.
870 bitrate_bps = vpx_configs_[encoder_id].rc_target_bitrate * 1000;
871 fps = codec_.maxFramerate;
872 } else {
873 bitrate_bps = vpx_configs_[encoder_id].ts_target_bitrate[tid] * 1000;
874 fps = codec_.maxFramerate /
875 fmax(vpx_configs_[encoder_id].ts_rate_decimator[tid], 1.0);
876 if (tid > 0) {
877 // Layer bitrate and fps are counted as a partial sums.
878 bitrate_bps -= vpx_configs_[encoder_id].ts_target_bitrate[tid - 1] * 1000;
879 fps = codec_.maxFramerate /
880 fmax(vpx_configs_[encoder_id].ts_rate_decimator[tid - 1], 1.0);
881 }
882 }
883
884 if (fps < 1e-9)
885 return 0;
886 return static_cast<size_t>(
887 bitrate_bps / (8 * fps) *
888 (100 -
889 variable_framerate_experiment_.steady_state_undershoot_percentage) /
890 100 +
891 0.5);
892 }
893
UpdateVpxConfiguration(size_t stream_index)894 bool LibvpxVp8Encoder::UpdateVpxConfiguration(size_t stream_index) {
895 RTC_DCHECK(frame_buffer_controller_);
896
897 const size_t config_index = vpx_configs_.size() - 1 - stream_index;
898
899 RTC_DCHECK_LT(config_index, config_overrides_.size());
900 Vp8EncoderConfig* config = &config_overrides_[config_index];
901
902 const Vp8EncoderConfig new_config =
903 frame_buffer_controller_->UpdateConfiguration(stream_index);
904
905 if (new_config.reset_previous_configuration_overrides) {
906 *config = new_config;
907 return true;
908 }
909
910 const bool changes_made = MaybeExtendVp8EncoderConfig(new_config, config);
911
912 // Note that overrides must be applied even if they haven't changed.
913 RTC_DCHECK_LT(config_index, vpx_configs_.size());
914 vpx_codec_enc_cfg_t* vpx_config = &vpx_configs_[config_index];
915 ApplyVp8EncoderConfigToVpxConfig(*config, vpx_config);
916
917 return changes_made;
918 }
919
Encode(const VideoFrame & frame,const std::vector<VideoFrameType> * frame_types)920 int LibvpxVp8Encoder::Encode(const VideoFrame& frame,
921 const std::vector<VideoFrameType>* frame_types) {
922 RTC_DCHECK_EQ(frame.width(), codec_.width);
923 RTC_DCHECK_EQ(frame.height(), codec_.height);
924
925 if (!inited_)
926 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
927 if (encoded_complete_callback_ == NULL)
928 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
929
930 bool key_frame_requested = false;
931 for (size_t i = 0; i < key_frame_request_.size() && i < send_stream_.size();
932 ++i) {
933 if (key_frame_request_[i] && send_stream_[i]) {
934 key_frame_requested = true;
935 break;
936 }
937 }
938 if (!key_frame_requested && frame_types) {
939 for (size_t i = 0; i < frame_types->size() && i < send_stream_.size();
940 ++i) {
941 if ((*frame_types)[i] == VideoFrameType::kVideoFrameKey &&
942 send_stream_[i]) {
943 key_frame_requested = true;
944 break;
945 }
946 }
947 }
948
949 if (frame.update_rect().IsEmpty() && num_steady_state_frames_ >= 3 &&
950 !key_frame_requested) {
951 if (variable_framerate_experiment_.enabled &&
952 framerate_controller_.DropFrame(frame.timestamp() / kRtpTicksPerMs)) {
953 return WEBRTC_VIDEO_CODEC_OK;
954 }
955 framerate_controller_.AddFrame(frame.timestamp() / kRtpTicksPerMs);
956 }
957
958 bool send_key_frame = key_frame_requested;
959 bool drop_frame = false;
960 bool retransmission_allowed = true;
961 Vp8FrameConfig tl_configs[kMaxSimulcastStreams];
962 for (size_t i = 0; i < encoders_.size(); ++i) {
963 tl_configs[i] =
964 frame_buffer_controller_->NextFrameConfig(i, frame.timestamp());
965 send_key_frame |= tl_configs[i].IntraFrame();
966 drop_frame |= tl_configs[i].drop_frame;
967 RTC_DCHECK(i == 0 ||
968 retransmission_allowed == tl_configs[i].retransmission_allowed);
969 retransmission_allowed = tl_configs[i].retransmission_allowed;
970 }
971
972 if (drop_frame && !send_key_frame) {
973 return WEBRTC_VIDEO_CODEC_OK;
974 }
975
976 vpx_enc_frame_flags_t flags[kMaxSimulcastStreams];
977 for (size_t i = 0; i < encoders_.size(); ++i) {
978 flags[i] = send_key_frame ? VPX_EFLAG_FORCE_KF : EncodeFlags(tl_configs[i]);
979 }
980
981 // Scale and map buffers and set `raw_images_` to hold pointers to the result.
982 // Because `raw_images_` are set to hold pointers to the prepared buffers, we
983 // need to keep these buffers alive through reference counting until after
984 // encoding is complete.
985 std::vector<rtc::scoped_refptr<VideoFrameBuffer>> prepared_buffers =
986 PrepareBuffers(frame.video_frame_buffer());
987 if (prepared_buffers.empty()) {
988 return WEBRTC_VIDEO_CODEC_ERROR;
989 }
990 struct CleanUpOnExit {
991 explicit CleanUpOnExit(
992 vpx_image_t* raw_image,
993 std::vector<rtc::scoped_refptr<VideoFrameBuffer>> prepared_buffers)
994 : raw_image_(raw_image),
995 prepared_buffers_(std::move(prepared_buffers)) {}
996 ~CleanUpOnExit() {
997 raw_image_->planes[VPX_PLANE_Y] = nullptr;
998 raw_image_->planes[VPX_PLANE_U] = nullptr;
999 raw_image_->planes[VPX_PLANE_V] = nullptr;
1000 }
1001 vpx_image_t* raw_image_;
1002 std::vector<rtc::scoped_refptr<VideoFrameBuffer>> prepared_buffers_;
1003 } clean_up_on_exit(&raw_images_[0], std::move(prepared_buffers));
1004
1005 if (send_key_frame) {
1006 // Adapt the size of the key frame when in screenshare with 1 temporal
1007 // layer.
1008 if (encoders_.size() == 1 &&
1009 codec_.mode == VideoCodecMode::kScreensharing &&
1010 codec_.VP8()->numberOfTemporalLayers <= 1) {
1011 const uint32_t forceKeyFrameIntraTh = 100;
1012 libvpx_->codec_control(&(encoders_[0]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
1013 forceKeyFrameIntraTh);
1014 }
1015
1016 std::fill(key_frame_request_.begin(), key_frame_request_.end(), false);
1017 }
1018
1019 // Set the encoder frame flags and temporal layer_id for each spatial stream.
1020 // Note that streams are defined starting from lowest resolution at
1021 // position 0 to highest resolution at position |encoders_.size() - 1|,
1022 // whereas `encoder_` is from highest to lowest resolution.
1023 for (size_t i = 0; i < encoders_.size(); ++i) {
1024 const size_t stream_idx = encoders_.size() - 1 - i;
1025
1026 if (UpdateVpxConfiguration(stream_idx)) {
1027 if (libvpx_->codec_enc_config_set(&encoders_[i], &vpx_configs_[i]))
1028 return WEBRTC_VIDEO_CODEC_ERROR;
1029 }
1030
1031 libvpx_->codec_control(&encoders_[i], VP8E_SET_FRAME_FLAGS,
1032 static_cast<int>(flags[stream_idx]));
1033 libvpx_->codec_control(&encoders_[i], VP8E_SET_TEMPORAL_LAYER_ID,
1034 tl_configs[i].encoder_layer_id);
1035 }
1036 // TODO(holmer): Ideally the duration should be the timestamp diff of this
1037 // frame and the next frame to be encoded, which we don't have. Instead we
1038 // would like to use the duration of the previous frame. Unfortunately the
1039 // rate control seems to be off with that setup. Using the average input
1040 // frame rate to calculate an average duration for now.
1041 RTC_DCHECK_GT(codec_.maxFramerate, 0);
1042 uint32_t duration = kRtpTicksPerSecond / codec_.maxFramerate;
1043
1044 int error = WEBRTC_VIDEO_CODEC_OK;
1045 int num_tries = 0;
1046 // If the first try returns WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT
1047 // the frame must be reencoded with the same parameters again because
1048 // target bitrate is exceeded and encoder state has been reset.
1049 while (num_tries == 0 ||
1050 (num_tries == 1 &&
1051 error == WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT)) {
1052 ++num_tries;
1053 // Note we must pass 0 for `flags` field in encode call below since they are
1054 // set above in `libvpx_interface_->vpx_codec_control_` function for each
1055 // encoder/spatial layer.
1056 error = libvpx_->codec_encode(&encoders_[0], &raw_images_[0], timestamp_,
1057 duration, 0, VPX_DL_REALTIME);
1058 // Reset specific intra frame thresholds, following the key frame.
1059 if (send_key_frame) {
1060 libvpx_->codec_control(&(encoders_[0]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
1061 rc_max_intra_target_);
1062 }
1063 if (error)
1064 return WEBRTC_VIDEO_CODEC_ERROR;
1065 // Examines frame timestamps only.
1066 error = GetEncodedPartitions(frame, retransmission_allowed);
1067 }
1068 // TODO(sprang): Shouldn't we use the frame timestamp instead?
1069 timestamp_ += duration;
1070 return error;
1071 }
1072
PopulateCodecSpecific(CodecSpecificInfo * codec_specific,const vpx_codec_cx_pkt_t & pkt,int stream_idx,int encoder_idx,uint32_t timestamp)1073 void LibvpxVp8Encoder::PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
1074 const vpx_codec_cx_pkt_t& pkt,
1075 int stream_idx,
1076 int encoder_idx,
1077 uint32_t timestamp) {
1078 RTC_DCHECK(codec_specific);
1079 codec_specific->codecType = kVideoCodecVP8;
1080 codec_specific->codecSpecific.VP8.keyIdx =
1081 kNoKeyIdx; // TODO(hlundin) populate this
1082 codec_specific->codecSpecific.VP8.nonReference =
1083 (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0;
1084
1085 int qp = 0;
1086 vpx_codec_control(&encoders_[encoder_idx], VP8E_GET_LAST_QUANTIZER_64, &qp);
1087 bool is_keyframe = (pkt.data.frame.flags & VPX_FRAME_IS_KEY) != 0;
1088 frame_buffer_controller_->OnEncodeDone(stream_idx, timestamp,
1089 encoded_images_[encoder_idx].size(),
1090 is_keyframe, qp, codec_specific);
1091 if (is_keyframe && codec_specific->template_structure != absl::nullopt) {
1092 // Number of resolutions must match number of spatial layers, VP8 structures
1093 // expected to use single spatial layer. Templates must be ordered by
1094 // spatial_id, so assumption there is exactly one spatial layer is same as
1095 // assumption last template uses spatial_id = 0.
1096 // This check catches potential scenario where template_structure is shared
1097 // across multiple vp8 streams and they are distinguished using spatial_id.
1098 // Assigning single resolution doesn't support such scenario, i.e. assumes
1099 // vp8 simulcast is sent using multiple ssrcs.
1100 RTC_DCHECK(!codec_specific->template_structure->templates.empty());
1101 RTC_DCHECK_EQ(
1102 codec_specific->template_structure->templates.back().spatial_id, 0);
1103 codec_specific->template_structure->resolutions = {
1104 RenderResolution(pkt.data.frame.width[0], pkt.data.frame.height[0])};
1105 }
1106 }
1107
GetEncodedPartitions(const VideoFrame & input_image,bool retransmission_allowed)1108 int LibvpxVp8Encoder::GetEncodedPartitions(const VideoFrame& input_image,
1109 bool retransmission_allowed) {
1110 int stream_idx = static_cast<int>(encoders_.size()) - 1;
1111 int result = WEBRTC_VIDEO_CODEC_OK;
1112 for (size_t encoder_idx = 0; encoder_idx < encoders_.size();
1113 ++encoder_idx, --stream_idx) {
1114 vpx_codec_iter_t iter = NULL;
1115 encoded_images_[encoder_idx].set_size(0);
1116 encoded_images_[encoder_idx]._frameType = VideoFrameType::kVideoFrameDelta;
1117 CodecSpecificInfo codec_specific;
1118 const vpx_codec_cx_pkt_t* pkt = NULL;
1119
1120 size_t encoded_size = 0;
1121 while ((pkt = libvpx_->codec_get_cx_data(&encoders_[encoder_idx], &iter)) !=
1122 NULL) {
1123 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
1124 encoded_size += pkt->data.frame.sz;
1125 }
1126 }
1127
1128 auto buffer = EncodedImageBuffer::Create(encoded_size);
1129
1130 iter = NULL;
1131 size_t encoded_pos = 0;
1132 while ((pkt = libvpx_->codec_get_cx_data(&encoders_[encoder_idx], &iter)) !=
1133 NULL) {
1134 switch (pkt->kind) {
1135 case VPX_CODEC_CX_FRAME_PKT: {
1136 RTC_CHECK_LE(encoded_pos + pkt->data.frame.sz, buffer->size());
1137 memcpy(&buffer->data()[encoded_pos], pkt->data.frame.buf,
1138 pkt->data.frame.sz);
1139 encoded_pos += pkt->data.frame.sz;
1140 break;
1141 }
1142 default:
1143 break;
1144 }
1145 // End of frame
1146 if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) {
1147 // check if encoded frame is a key frame
1148 if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
1149 encoded_images_[encoder_idx]._frameType =
1150 VideoFrameType::kVideoFrameKey;
1151 }
1152 encoded_images_[encoder_idx].SetEncodedData(buffer);
1153 encoded_images_[encoder_idx].set_size(encoded_pos);
1154 encoded_images_[encoder_idx].SetSpatialIndex(stream_idx);
1155 PopulateCodecSpecific(&codec_specific, *pkt, stream_idx, encoder_idx,
1156 input_image.timestamp());
1157 if (codec_specific.codecSpecific.VP8.temporalIdx != kNoTemporalIdx) {
1158 encoded_images_[encoder_idx].SetTemporalIndex(
1159 codec_specific.codecSpecific.VP8.temporalIdx);
1160 }
1161 break;
1162 }
1163 }
1164 encoded_images_[encoder_idx].SetTimestamp(input_image.timestamp());
1165 encoded_images_[encoder_idx].SetColorSpace(input_image.color_space());
1166 encoded_images_[encoder_idx].SetRetransmissionAllowed(
1167 retransmission_allowed);
1168
1169 if (send_stream_[stream_idx]) {
1170 if (encoded_images_[encoder_idx].size() > 0) {
1171 TRACE_COUNTER_ID1("webrtc", "EncodedFrameSize", encoder_idx,
1172 encoded_images_[encoder_idx].size());
1173 encoded_images_[encoder_idx]._encodedHeight =
1174 codec_.simulcastStream[stream_idx].height;
1175 encoded_images_[encoder_idx]._encodedWidth =
1176 codec_.simulcastStream[stream_idx].width;
1177 int qp_128 = -1;
1178 libvpx_->codec_control(&encoders_[encoder_idx], VP8E_GET_LAST_QUANTIZER,
1179 &qp_128);
1180 encoded_images_[encoder_idx].qp_ = qp_128;
1181 encoded_complete_callback_->OnEncodedImage(encoded_images_[encoder_idx],
1182 &codec_specific);
1183 const size_t steady_state_size = SteadyStateSize(
1184 stream_idx, codec_specific.codecSpecific.VP8.temporalIdx);
1185 if (qp_128 > variable_framerate_experiment_.steady_state_qp ||
1186 encoded_images_[encoder_idx].size() > steady_state_size) {
1187 num_steady_state_frames_ = 0;
1188 } else {
1189 ++num_steady_state_frames_;
1190 }
1191 } else if (!frame_buffer_controller_->SupportsEncoderFrameDropping(
1192 stream_idx)) {
1193 result = WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT;
1194 if (encoded_images_[encoder_idx].size() == 0) {
1195 // Dropped frame that will be re-encoded.
1196 frame_buffer_controller_->OnFrameDropped(stream_idx,
1197 input_image.timestamp());
1198 }
1199 }
1200 }
1201 }
1202 return result;
1203 }
1204
GetEncoderInfo() const1205 VideoEncoder::EncoderInfo LibvpxVp8Encoder::GetEncoderInfo() const {
1206 EncoderInfo info;
1207 info.supports_native_handle = false;
1208 info.implementation_name = "libvpx";
1209 info.has_trusted_rate_controller =
1210 rate_control_settings_.LibvpxVp8TrustedRateController();
1211 info.is_hardware_accelerated = false;
1212 info.supports_simulcast = true;
1213 if (!resolution_bitrate_limits_.empty()) {
1214 info.resolution_bitrate_limits = resolution_bitrate_limits_;
1215 }
1216 if (encoder_info_override_.requested_resolution_alignment()) {
1217 info.requested_resolution_alignment =
1218 *encoder_info_override_.requested_resolution_alignment();
1219 info.apply_alignment_to_all_simulcast_layers =
1220 encoder_info_override_.apply_alignment_to_all_simulcast_layers();
1221 }
1222 if (!encoder_info_override_.resolution_bitrate_limits().empty()) {
1223 info.resolution_bitrate_limits =
1224 encoder_info_override_.resolution_bitrate_limits();
1225 }
1226
1227 const bool enable_scaling =
1228 num_active_streams_ == 1 &&
1229 (vpx_configs_.empty() || vpx_configs_[0].rc_dropframe_thresh > 0) &&
1230 codec_.VP8().automaticResizeOn;
1231
1232 info.scaling_settings = enable_scaling
1233 ? VideoEncoder::ScalingSettings(
1234 kLowVp8QpThreshold, kHighVp8QpThreshold)
1235 : VideoEncoder::ScalingSettings::kOff;
1236 if (rate_control_settings_.LibvpxVp8MinPixels()) {
1237 info.scaling_settings.min_pixels_per_frame =
1238 rate_control_settings_.LibvpxVp8MinPixels().value();
1239 }
1240 info.preferred_pixel_formats = {VideoFrameBuffer::Type::kI420,
1241 VideoFrameBuffer::Type::kNV12};
1242
1243 if (inited_) {
1244 // `encoder_idx` is libvpx index where 0 is highest resolution.
1245 // `si` is simulcast index, where 0 is lowest resolution.
1246 for (size_t si = 0, encoder_idx = encoders_.size() - 1;
1247 si < encoders_.size(); ++si, --encoder_idx) {
1248 info.fps_allocation[si].clear();
1249 if ((codec_.numberOfSimulcastStreams > si &&
1250 !codec_.simulcastStream[si].active) ||
1251 (si == 0 && SimulcastUtility::IsConferenceModeScreenshare(codec_))) {
1252 // No defined frame rate fractions if not active or if using
1253 // ScreenshareLayers, leave vector empty and continue;
1254 continue;
1255 }
1256 if (vpx_configs_[encoder_idx].ts_number_layers <= 1) {
1257 info.fps_allocation[si].push_back(EncoderInfo::kMaxFramerateFraction);
1258 } else {
1259 for (size_t ti = 0; ti < vpx_configs_[encoder_idx].ts_number_layers;
1260 ++ti) {
1261 RTC_DCHECK_GT(vpx_configs_[encoder_idx].ts_rate_decimator[ti], 0);
1262 info.fps_allocation[si].push_back(rtc::saturated_cast<uint8_t>(
1263 EncoderInfo::kMaxFramerateFraction /
1264 vpx_configs_[encoder_idx].ts_rate_decimator[ti] +
1265 0.5));
1266 }
1267 }
1268 }
1269 }
1270
1271 return info;
1272 }
1273
RegisterEncodeCompleteCallback(EncodedImageCallback * callback)1274 int LibvpxVp8Encoder::RegisterEncodeCompleteCallback(
1275 EncodedImageCallback* callback) {
1276 encoded_complete_callback_ = callback;
1277 return WEBRTC_VIDEO_CODEC_OK;
1278 }
1279
MaybeUpdatePixelFormat(vpx_img_fmt fmt)1280 void LibvpxVp8Encoder::MaybeUpdatePixelFormat(vpx_img_fmt fmt) {
1281 RTC_DCHECK(!raw_images_.empty());
1282 if (raw_images_[0].fmt == fmt) {
1283 RTC_DCHECK(std::all_of(
1284 std::next(raw_images_.begin()), raw_images_.end(),
1285 [fmt](const vpx_image_t& raw_img) { return raw_img.fmt == fmt; }))
1286 << "Not all raw images had the right format!";
1287 return;
1288 }
1289 RTC_LOG(LS_INFO) << "Updating vp8 encoder pixel format to "
1290 << (fmt == VPX_IMG_FMT_NV12 ? "NV12" : "I420");
1291 for (size_t i = 0; i < raw_images_.size(); ++i) {
1292 vpx_image_t& img = raw_images_[i];
1293 auto d_w = img.d_w;
1294 auto d_h = img.d_h;
1295 libvpx_->img_free(&img);
1296 // First image is wrapping the input frame, the rest are allocated.
1297 if (i == 0) {
1298 libvpx_->img_wrap(&img, fmt, d_w, d_h, 1, NULL);
1299 } else {
1300 libvpx_->img_alloc(&img, fmt, d_w, d_h, kVp832ByteAlign);
1301 }
1302 }
1303 }
1304
1305 std::vector<rtc::scoped_refptr<VideoFrameBuffer>>
PrepareBuffers(rtc::scoped_refptr<VideoFrameBuffer> buffer)1306 LibvpxVp8Encoder::PrepareBuffers(rtc::scoped_refptr<VideoFrameBuffer> buffer) {
1307 RTC_DCHECK_EQ(buffer->width(), raw_images_[0].d_w);
1308 RTC_DCHECK_EQ(buffer->height(), raw_images_[0].d_h);
1309 absl::InlinedVector<VideoFrameBuffer::Type, kMaxPreferredPixelFormats>
1310 supported_formats = {VideoFrameBuffer::Type::kI420,
1311 VideoFrameBuffer::Type::kNV12};
1312
1313 rtc::scoped_refptr<VideoFrameBuffer> mapped_buffer;
1314 if (buffer->type() != VideoFrameBuffer::Type::kNative) {
1315 // `buffer` is already mapped.
1316 mapped_buffer = buffer;
1317 } else {
1318 // Attempt to map to one of the supported formats.
1319 mapped_buffer = buffer->GetMappedFrameBuffer(supported_formats);
1320 }
1321 if (!mapped_buffer ||
1322 (absl::c_find(supported_formats, mapped_buffer->type()) ==
1323 supported_formats.end() &&
1324 mapped_buffer->type() != VideoFrameBuffer::Type::kI420A)) {
1325 // Unknown pixel format or unable to map, convert to I420 and prepare that
1326 // buffer instead to ensure Scale() is safe to use.
1327 auto converted_buffer = buffer->ToI420();
1328 if (!converted_buffer) {
1329 RTC_LOG(LS_ERROR) << "Failed to convert "
1330 << VideoFrameBufferTypeToString(buffer->type())
1331 << " image to I420. Can't encode frame.";
1332 return {};
1333 }
1334 RTC_CHECK(converted_buffer->type() == VideoFrameBuffer::Type::kI420 ||
1335 converted_buffer->type() == VideoFrameBuffer::Type::kI420A);
1336
1337 // Because `buffer` had to be converted, use `converted_buffer` instead...
1338 buffer = mapped_buffer = converted_buffer;
1339 }
1340
1341 // Maybe update pixel format.
1342 absl::InlinedVector<VideoFrameBuffer::Type, kMaxPreferredPixelFormats>
1343 mapped_type = {mapped_buffer->type()};
1344 switch (mapped_buffer->type()) {
1345 case VideoFrameBuffer::Type::kI420:
1346 case VideoFrameBuffer::Type::kI420A:
1347 MaybeUpdatePixelFormat(VPX_IMG_FMT_I420);
1348 break;
1349 case VideoFrameBuffer::Type::kNV12:
1350 MaybeUpdatePixelFormat(VPX_IMG_FMT_NV12);
1351 break;
1352 default:
1353 RTC_DCHECK_NOTREACHED();
1354 }
1355
1356 // Prepare `raw_images_` from `mapped_buffer` and, if simulcast, scaled
1357 // versions of `buffer`.
1358 std::vector<rtc::scoped_refptr<VideoFrameBuffer>> prepared_buffers;
1359 SetRawImagePlanes(&raw_images_[0], mapped_buffer.get());
1360 prepared_buffers.push_back(mapped_buffer);
1361 for (size_t i = 1; i < encoders_.size(); ++i) {
1362 // Native buffers should implement optimized scaling and is the preferred
1363 // buffer to scale. But if the buffer isn't native, it should be cheaper to
1364 // scale from the previously prepared buffer which is smaller than `buffer`.
1365 VideoFrameBuffer* buffer_to_scale =
1366 buffer->type() == VideoFrameBuffer::Type::kNative
1367 ? buffer.get()
1368 : prepared_buffers.back().get();
1369
1370 auto scaled_buffer =
1371 buffer_to_scale->Scale(raw_images_[i].d_w, raw_images_[i].d_h);
1372 if (scaled_buffer->type() == VideoFrameBuffer::Type::kNative) {
1373 auto mapped_scaled_buffer =
1374 scaled_buffer->GetMappedFrameBuffer(mapped_type);
1375 RTC_DCHECK(mapped_scaled_buffer) << "Unable to map the scaled buffer.";
1376 if (!mapped_scaled_buffer) {
1377 RTC_LOG(LS_ERROR) << "Failed to map scaled "
1378 << VideoFrameBufferTypeToString(scaled_buffer->type())
1379 << " image to "
1380 << VideoFrameBufferTypeToString(mapped_buffer->type())
1381 << ". Can't encode frame.";
1382 return {};
1383 }
1384 scaled_buffer = mapped_scaled_buffer;
1385 }
1386 if (!IsCompatibleVideoFrameBufferType(scaled_buffer->type(),
1387 mapped_buffer->type())) {
1388 RTC_LOG(LS_ERROR) << "When scaling "
1389 << VideoFrameBufferTypeToString(buffer_to_scale->type())
1390 << ", the image was unexpectedly converted to "
1391 << VideoFrameBufferTypeToString(scaled_buffer->type())
1392 << " instead of "
1393 << VideoFrameBufferTypeToString(mapped_buffer->type())
1394 << ". Can't encode frame.";
1395 RTC_DCHECK_NOTREACHED()
1396 << "Scaled buffer type "
1397 << VideoFrameBufferTypeToString(scaled_buffer->type())
1398 << " is not compatible with mapped buffer type "
1399 << VideoFrameBufferTypeToString(mapped_buffer->type());
1400 return {};
1401 }
1402 SetRawImagePlanes(&raw_images_[i], scaled_buffer.get());
1403 prepared_buffers.push_back(scaled_buffer);
1404 }
1405 return prepared_buffers;
1406 }
1407
1408 // static
1409 LibvpxVp8Encoder::VariableFramerateExperiment
ParseVariableFramerateConfig(std::string group_name)1410 LibvpxVp8Encoder::ParseVariableFramerateConfig(std::string group_name) {
1411 FieldTrialFlag disabled = FieldTrialFlag("Disabled");
1412 FieldTrialParameter<double> framerate_limit("min_fps", 5.0);
1413 FieldTrialParameter<int> qp("min_qp", 15);
1414 FieldTrialParameter<int> undershoot_percentage("undershoot", 30);
1415 ParseFieldTrial({&disabled, &framerate_limit, &qp, &undershoot_percentage},
1416 field_trial::FindFullName(group_name));
1417 VariableFramerateExperiment config;
1418 config.enabled = !disabled.Get();
1419 config.framerate_limit = framerate_limit.Get();
1420 config.steady_state_qp = qp.Get();
1421 config.steady_state_undershoot_percentage = undershoot_percentage.Get();
1422
1423 return config;
1424 }
1425
1426 } // namespace webrtc
1427