xref: /aosp_15_r20/external/webrtc/api/video_codecs/video_encoder_software_fallback_wrapper.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2016 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 "api/video_codecs/video_encoder_software_fallback_wrapper.h"
12 
13 #include <stdint.h>
14 
15 #include <cstdio>
16 #include <memory>
17 #include <string>
18 #include <vector>
19 
20 #include "absl/strings/match.h"
21 #include "absl/types/optional.h"
22 #include "api/fec_controller_override.h"
23 #include "api/video/i420_buffer.h"
24 #include "api/video/video_bitrate_allocation.h"
25 #include "api/video/video_frame.h"
26 #include "api/video_codecs/video_codec.h"
27 #include "api/video_codecs/video_encoder.h"
28 #include "media/base/video_common.h"
29 #include "modules/video_coding/include/video_error_codes.h"
30 #include "modules/video_coding/utility/simulcast_utility.h"
31 #include "rtc_base/checks.h"
32 #include "rtc_base/logging.h"
33 #include "system_wrappers/include/field_trial.h"
34 
35 namespace webrtc {
36 
37 namespace {
38 
39 // If forced fallback is allowed, either:
40 //
41 // 1) The forced fallback is requested if the resolution is less than or equal
42 //    to `max_pixels_`. The resolution is allowed to be scaled down to
43 //    `min_pixels_`.
44 //
45 // 2) The forced fallback is requested if temporal support is preferred and the
46 //    SW fallback supports temporal layers while the HW encoder does not.
47 
48 struct ForcedFallbackParams {
49  public:
SupportsResolutionBasedSwitchwebrtc::__anonbaca671f0111::ForcedFallbackParams50   bool SupportsResolutionBasedSwitch(const VideoCodec& codec) const {
51     return enable_resolution_based_switch &&
52            codec.codecType == kVideoCodecVP8 &&
53            codec.numberOfSimulcastStreams <= 1 &&
54            codec.width * codec.height <= max_pixels;
55   }
56 
SupportsTemporalBasedSwitchwebrtc::__anonbaca671f0111::ForcedFallbackParams57   bool SupportsTemporalBasedSwitch(const VideoCodec& codec) const {
58     return enable_temporal_based_switch &&
59            SimulcastUtility::NumberOfTemporalLayers(codec, 0) != 1;
60   }
61 
62   bool enable_temporal_based_switch = false;
63   bool enable_resolution_based_switch = false;
64   int min_pixels = 320 * 180;
65   int max_pixels = 320 * 240;
66 };
67 
68 const char kVp8ForceFallbackEncoderFieldTrial[] =
69     "WebRTC-VP8-Forced-Fallback-Encoder-v2";
70 
ParseFallbackParamsFromFieldTrials(const VideoEncoder & main_encoder)71 absl::optional<ForcedFallbackParams> ParseFallbackParamsFromFieldTrials(
72     const VideoEncoder& main_encoder) {
73   const std::string field_trial =
74       webrtc::field_trial::FindFullName(kVp8ForceFallbackEncoderFieldTrial);
75   if (!absl::StartsWith(field_trial, "Enabled")) {
76     return absl::nullopt;
77   }
78 
79   int max_pixels_lower_bound =
80       main_encoder.GetEncoderInfo().scaling_settings.min_pixels_per_frame - 1;
81 
82   ForcedFallbackParams params;
83   params.enable_resolution_based_switch = true;
84 
85   int min_bps = 0;
86   if (sscanf(field_trial.c_str(), "Enabled-%d,%d,%d", &params.min_pixels,
87              &params.max_pixels, &min_bps) != 3) {
88     RTC_LOG(LS_WARNING)
89         << "Invalid number of forced fallback parameters provided.";
90     return absl::nullopt;
91   } else if (params.min_pixels <= 0 ||
92              params.max_pixels < max_pixels_lower_bound ||
93              params.max_pixels < params.min_pixels || min_bps <= 0) {
94     RTC_LOG(LS_WARNING) << "Invalid forced fallback parameter value provided.";
95     return absl::nullopt;
96   }
97 
98   return params;
99 }
100 
GetForcedFallbackParams(bool prefer_temporal_support,const VideoEncoder & main_encoder)101 absl::optional<ForcedFallbackParams> GetForcedFallbackParams(
102     bool prefer_temporal_support,
103     const VideoEncoder& main_encoder) {
104   absl::optional<ForcedFallbackParams> params =
105       ParseFallbackParamsFromFieldTrials(main_encoder);
106   if (prefer_temporal_support) {
107     if (!params.has_value()) {
108       params.emplace();
109     }
110     params->enable_temporal_based_switch = prefer_temporal_support;
111   }
112   return params;
113 }
114 
115 class VideoEncoderSoftwareFallbackWrapper final : public VideoEncoder {
116  public:
117   VideoEncoderSoftwareFallbackWrapper(
118       std::unique_ptr<webrtc::VideoEncoder> sw_encoder,
119       std::unique_ptr<webrtc::VideoEncoder> hw_encoder,
120       bool prefer_temporal_support);
121   ~VideoEncoderSoftwareFallbackWrapper() override;
122 
123   void SetFecControllerOverride(
124       FecControllerOverride* fec_controller_override) override;
125 
126   int32_t InitEncode(const VideoCodec* codec_settings,
127                      const VideoEncoder::Settings& settings) override;
128 
129   int32_t RegisterEncodeCompleteCallback(
130       EncodedImageCallback* callback) override;
131 
132   int32_t Release() override;
133 
134   int32_t Encode(const VideoFrame& frame,
135                  const std::vector<VideoFrameType>* frame_types) override;
136 
137   void OnPacketLossRateUpdate(float packet_loss_rate) override;
138 
139   void OnRttUpdate(int64_t rtt_ms) override;
140 
141   void OnLossNotification(const LossNotification& loss_notification) override;
142 
143   void SetRates(const RateControlParameters& parameters) override;
144 
145   EncoderInfo GetEncoderInfo() const override;
146 
147  private:
148   bool InitFallbackEncoder(bool is_forced);
149   bool TryInitForcedFallbackEncoder();
150   bool IsFallbackActive() const;
151 
current_encoder()152   VideoEncoder* current_encoder() {
153     switch (encoder_state_) {
154       case EncoderState::kUninitialized:
155         RTC_LOG(LS_WARNING)
156             << "Trying to access encoder in uninitialized fallback wrapper.";
157         // Return main encoder to preserve previous behavior.
158         [[fallthrough]];
159       case EncoderState::kMainEncoderUsed:
160         return encoder_.get();
161       case EncoderState::kFallbackDueToFailure:
162       case EncoderState::kForcedFallback:
163         return fallback_encoder_.get();
164     }
165     RTC_CHECK_NOTREACHED();
166   }
167 
168   // Updates encoder with last observed parameters, such as callbacks, rates,
169   // etc.
170   void PrimeEncoder(VideoEncoder* encoder) const;
171 
172   // Settings used in the last InitEncode call and used if a dynamic fallback to
173   // software is required.
174   VideoCodec codec_settings_;
175   absl::optional<VideoEncoder::Settings> encoder_settings_;
176 
177   // The last rate control settings, if set.
178   absl::optional<RateControlParameters> rate_control_parameters_;
179 
180   // The last channel parameters set.
181   absl::optional<float> packet_loss_;
182   absl::optional<int64_t> rtt_;
183   absl::optional<LossNotification> loss_notification_;
184 
185   enum class EncoderState {
186     kUninitialized,
187     kMainEncoderUsed,
188     kFallbackDueToFailure,
189     kForcedFallback
190   };
191 
192   EncoderState encoder_state_;
193   const std::unique_ptr<webrtc::VideoEncoder> encoder_;
194   const std::unique_ptr<webrtc::VideoEncoder> fallback_encoder_;
195 
196   EncodedImageCallback* callback_;
197 
198   const absl::optional<ForcedFallbackParams> fallback_params_;
199   int32_t EncodeWithMainEncoder(const VideoFrame& frame,
200                                 const std::vector<VideoFrameType>* frame_types);
201 };
202 
VideoEncoderSoftwareFallbackWrapper(std::unique_ptr<webrtc::VideoEncoder> sw_encoder,std::unique_ptr<webrtc::VideoEncoder> hw_encoder,bool prefer_temporal_support)203 VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
204     std::unique_ptr<webrtc::VideoEncoder> sw_encoder,
205     std::unique_ptr<webrtc::VideoEncoder> hw_encoder,
206     bool prefer_temporal_support)
207     : encoder_state_(EncoderState::kUninitialized),
208       encoder_(std::move(hw_encoder)),
209       fallback_encoder_(std::move(sw_encoder)),
210       callback_(nullptr),
211       fallback_params_(
212           GetForcedFallbackParams(prefer_temporal_support, *encoder_)) {
213   RTC_DCHECK(fallback_encoder_);
214 }
215 
216 VideoEncoderSoftwareFallbackWrapper::~VideoEncoderSoftwareFallbackWrapper() =
217     default;
218 
PrimeEncoder(VideoEncoder * encoder) const219 void VideoEncoderSoftwareFallbackWrapper::PrimeEncoder(
220     VideoEncoder* encoder) const {
221   RTC_DCHECK(encoder);
222   // Replay callback, rates, and channel parameters.
223   if (callback_) {
224     encoder->RegisterEncodeCompleteCallback(callback_);
225   }
226   if (rate_control_parameters_) {
227     encoder->SetRates(*rate_control_parameters_);
228   }
229   if (rtt_.has_value()) {
230     encoder->OnRttUpdate(rtt_.value());
231   }
232   if (packet_loss_.has_value()) {
233     encoder->OnPacketLossRateUpdate(packet_loss_.value());
234   }
235 
236   if (loss_notification_.has_value()) {
237     encoder->OnLossNotification(loss_notification_.value());
238   }
239 }
240 
InitFallbackEncoder(bool is_forced)241 bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder(bool is_forced) {
242   RTC_LOG(LS_WARNING) << "Encoder falling back to software encoding.";
243 
244   RTC_DCHECK(encoder_settings_.has_value());
245   const int ret = fallback_encoder_->InitEncode(&codec_settings_,
246                                                 encoder_settings_.value());
247 
248   if (ret != WEBRTC_VIDEO_CODEC_OK) {
249     RTC_LOG(LS_ERROR) << "Failed to initialize software-encoder fallback.";
250     fallback_encoder_->Release();
251     return false;
252   }
253 
254   if (encoder_state_ == EncoderState::kMainEncoderUsed) {
255     // Since we're switching to the fallback encoder, Release the real encoder.
256     // It may be re-initialized via InitEncode later, and it will continue to
257     // get Set calls for rates and channel parameters in the meantime.
258     encoder_->Release();
259   }
260 
261   if (is_forced) {
262     encoder_state_ = EncoderState::kForcedFallback;
263   } else {
264     encoder_state_ = EncoderState::kFallbackDueToFailure;
265   }
266 
267   return true;
268 }
269 
SetFecControllerOverride(FecControllerOverride * fec_controller_override)270 void VideoEncoderSoftwareFallbackWrapper::SetFecControllerOverride(
271     FecControllerOverride* fec_controller_override) {
272   // It is important that only one of those would ever interact with the
273   // `fec_controller_override` at a given time. This is the responsibility
274   // of `this` to maintain.
275 
276   encoder_->SetFecControllerOverride(fec_controller_override);
277   fallback_encoder_->SetFecControllerOverride(fec_controller_override);
278 }
279 
InitEncode(const VideoCodec * codec_settings,const VideoEncoder::Settings & settings)280 int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
281     const VideoCodec* codec_settings,
282     const VideoEncoder::Settings& settings) {
283   // Store settings, in case we need to dynamically switch to the fallback
284   // encoder after a failed Encode call.
285   codec_settings_ = *codec_settings;
286   encoder_settings_ = settings;
287   // Clear stored rate/channel parameters.
288   rate_control_parameters_ = absl::nullopt;
289 
290   RTC_DCHECK_EQ(encoder_state_, EncoderState::kUninitialized)
291       << "InitEncode() should never be called on an active instance!";
292 
293   // Try to init forced software codec if it should be used.
294   if (TryInitForcedFallbackEncoder()) {
295     PrimeEncoder(current_encoder());
296     return WEBRTC_VIDEO_CODEC_OK;
297   }
298 
299   int32_t ret = encoder_->InitEncode(codec_settings, settings);
300   if (ret == WEBRTC_VIDEO_CODEC_OK) {
301     encoder_state_ = EncoderState::kMainEncoderUsed;
302     PrimeEncoder(current_encoder());
303     return ret;
304   }
305 
306   // Try to instantiate software codec.
307   if (InitFallbackEncoder(/*is_forced=*/false)) {
308     PrimeEncoder(current_encoder());
309     return WEBRTC_VIDEO_CODEC_OK;
310   }
311 
312   // Software encoder failed too, use original return code.
313   encoder_state_ = EncoderState::kUninitialized;
314   return ret;
315 }
316 
RegisterEncodeCompleteCallback(EncodedImageCallback * callback)317 int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback(
318     EncodedImageCallback* callback) {
319   callback_ = callback;
320   return current_encoder()->RegisterEncodeCompleteCallback(callback);
321 }
322 
Release()323 int32_t VideoEncoderSoftwareFallbackWrapper::Release() {
324   if (encoder_state_ == EncoderState::kUninitialized) {
325     return WEBRTC_VIDEO_CODEC_OK;
326   }
327   int32_t ret = current_encoder()->Release();
328   encoder_state_ = EncoderState::kUninitialized;
329   return ret;
330 }
331 
Encode(const VideoFrame & frame,const std::vector<VideoFrameType> * frame_types)332 int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
333     const VideoFrame& frame,
334     const std::vector<VideoFrameType>* frame_types) {
335   switch (encoder_state_) {
336     case EncoderState::kUninitialized:
337       return WEBRTC_VIDEO_CODEC_ERROR;
338     case EncoderState::kMainEncoderUsed: {
339       return EncodeWithMainEncoder(frame, frame_types);
340     }
341     case EncoderState::kFallbackDueToFailure:
342     case EncoderState::kForcedFallback:
343       return fallback_encoder_->Encode(frame, frame_types);
344   }
345   RTC_CHECK_NOTREACHED();
346 }
347 
EncodeWithMainEncoder(const VideoFrame & frame,const std::vector<VideoFrameType> * frame_types)348 int32_t VideoEncoderSoftwareFallbackWrapper::EncodeWithMainEncoder(
349     const VideoFrame& frame,
350     const std::vector<VideoFrameType>* frame_types) {
351   int32_t ret = encoder_->Encode(frame, frame_types);
352   // If requested, try a software fallback.
353   bool fallback_requested = (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE);
354   if (fallback_requested && InitFallbackEncoder(/*is_forced=*/false)) {
355     // Start using the fallback with this frame.
356     PrimeEncoder(current_encoder());
357     if (frame.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative &&
358         fallback_encoder_->GetEncoderInfo().supports_native_handle) {
359       return fallback_encoder_->Encode(frame, frame_types);
360     } else {
361       RTC_LOG(LS_INFO) << "Fallback encoder does not support native handle - "
362                           "converting frame to I420";
363       rtc::scoped_refptr<I420BufferInterface> src_buffer =
364           frame.video_frame_buffer()->ToI420();
365       if (!src_buffer) {
366         RTC_LOG(LS_ERROR) << "Failed to convert from to I420";
367         return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE;
368       }
369       rtc::scoped_refptr<VideoFrameBuffer> dst_buffer =
370           src_buffer->Scale(codec_settings_.width, codec_settings_.height);
371       if (!dst_buffer) {
372         RTC_LOG(LS_ERROR) << "Failed to scale video frame.";
373         return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE;
374       }
375       VideoFrame scaled_frame = frame;
376       scaled_frame.set_video_frame_buffer(dst_buffer);
377       scaled_frame.set_update_rect(VideoFrame::UpdateRect{
378           0, 0, scaled_frame.width(), scaled_frame.height()});
379       return fallback_encoder_->Encode(scaled_frame, frame_types);
380     }
381   }
382   // Fallback encoder failed too, return original error code.
383   return ret;
384 }
385 
SetRates(const RateControlParameters & parameters)386 void VideoEncoderSoftwareFallbackWrapper::SetRates(
387     const RateControlParameters& parameters) {
388   rate_control_parameters_ = parameters;
389   return current_encoder()->SetRates(parameters);
390 }
391 
OnPacketLossRateUpdate(float packet_loss_rate)392 void VideoEncoderSoftwareFallbackWrapper::OnPacketLossRateUpdate(
393     float packet_loss_rate) {
394   packet_loss_ = packet_loss_rate;
395   current_encoder()->OnPacketLossRateUpdate(packet_loss_rate);
396 }
397 
OnRttUpdate(int64_t rtt_ms)398 void VideoEncoderSoftwareFallbackWrapper::OnRttUpdate(int64_t rtt_ms) {
399   rtt_ = rtt_ms;
400   current_encoder()->OnRttUpdate(rtt_ms);
401 }
402 
OnLossNotification(const LossNotification & loss_notification)403 void VideoEncoderSoftwareFallbackWrapper::OnLossNotification(
404     const LossNotification& loss_notification) {
405   loss_notification_ = loss_notification;
406   current_encoder()->OnLossNotification(loss_notification);
407 }
408 
GetEncoderInfo() const409 VideoEncoder::EncoderInfo VideoEncoderSoftwareFallbackWrapper::GetEncoderInfo()
410     const {
411   EncoderInfo fallback_encoder_info = fallback_encoder_->GetEncoderInfo();
412   EncoderInfo default_encoder_info = encoder_->GetEncoderInfo();
413 
414   EncoderInfo info =
415       IsFallbackActive() ? fallback_encoder_info : default_encoder_info;
416 
417   info.requested_resolution_alignment = cricket::LeastCommonMultiple(
418       fallback_encoder_info.requested_resolution_alignment,
419       default_encoder_info.requested_resolution_alignment);
420   info.apply_alignment_to_all_simulcast_layers =
421       fallback_encoder_info.apply_alignment_to_all_simulcast_layers ||
422       default_encoder_info.apply_alignment_to_all_simulcast_layers;
423 
424   if (fallback_params_.has_value()) {
425     const auto settings = (encoder_state_ == EncoderState::kForcedFallback)
426                               ? fallback_encoder_info.scaling_settings
427                               : default_encoder_info.scaling_settings;
428     info.scaling_settings =
429         settings.thresholds
430             ? VideoEncoder::ScalingSettings(settings.thresholds->low,
431                                             settings.thresholds->high,
432                                             fallback_params_->min_pixels)
433             : VideoEncoder::ScalingSettings::kOff;
434   } else {
435     info.scaling_settings = default_encoder_info.scaling_settings;
436   }
437 
438   return info;
439 }
440 
IsFallbackActive() const441 bool VideoEncoderSoftwareFallbackWrapper::IsFallbackActive() const {
442   return encoder_state_ == EncoderState::kForcedFallback ||
443          encoder_state_ == EncoderState::kFallbackDueToFailure;
444 }
445 
TryInitForcedFallbackEncoder()446 bool VideoEncoderSoftwareFallbackWrapper::TryInitForcedFallbackEncoder() {
447   if (!fallback_params_) {
448     return false;
449   }
450 
451   RTC_DCHECK_EQ(encoder_state_, EncoderState::kUninitialized);
452 
453   if (fallback_params_->SupportsResolutionBasedSwitch(codec_settings_)) {
454     // Settings valid, try to instantiate software codec.
455     RTC_LOG(LS_INFO) << "Request forced SW encoder fallback: "
456                      << codec_settings_.width << "x" << codec_settings_.height;
457     return InitFallbackEncoder(/*is_forced=*/true);
458   }
459 
460   if (fallback_params_->SupportsTemporalBasedSwitch(codec_settings_)) {
461     // First init main encoder to see if that supports temporal layers.
462     if (encoder_->InitEncode(&codec_settings_, encoder_settings_.value()) ==
463         WEBRTC_VIDEO_CODEC_OK) {
464       encoder_state_ = EncoderState::kMainEncoderUsed;
465     }
466 
467     if (encoder_state_ == EncoderState::kMainEncoderUsed &&
468         encoder_->GetEncoderInfo().fps_allocation[0].size() != 1) {
469       // Primary encoder already supports temporal layers, use that instead.
470       return true;
471     }
472 
473     // Try to initialize fallback and check if it supports temporal layers.
474     if (fallback_encoder_->InitEncode(&codec_settings_,
475                                       encoder_settings_.value()) ==
476         WEBRTC_VIDEO_CODEC_OK) {
477       if (fallback_encoder_->GetEncoderInfo().fps_allocation[0].size() != 1) {
478         // Fallback encoder available and supports temporal layers, use it!
479         if (encoder_state_ == EncoderState::kMainEncoderUsed) {
480           // Main encoder initialized but does not support temporal layers,
481           // release it again.
482           encoder_->Release();
483         }
484         encoder_state_ = EncoderState::kForcedFallback;
485         RTC_LOG(LS_INFO)
486             << "Forced switch to SW encoder due to temporal support.";
487         return true;
488       } else {
489         // Fallback encoder intialization succeeded, but it does not support
490         // temporal layers either - release it.
491         fallback_encoder_->Release();
492       }
493     }
494 
495     if (encoder_state_ == EncoderState::kMainEncoderUsed) {
496       // Main encoder already initialized - make use of it.
497       RTC_LOG(LS_INFO)
498           << "Cannot fall back for temporal support since fallback that "
499              "supports is not available. Using main encoder instead.";
500       return true;
501     }
502   }
503 
504   // Neither forced fallback mode supported.
505   return false;
506 }
507 
508 }  // namespace
509 
CreateVideoEncoderSoftwareFallbackWrapper(std::unique_ptr<VideoEncoder> sw_fallback_encoder,std::unique_ptr<VideoEncoder> hw_encoder,bool prefer_temporal_support)510 std::unique_ptr<VideoEncoder> CreateVideoEncoderSoftwareFallbackWrapper(
511     std::unique_ptr<VideoEncoder> sw_fallback_encoder,
512     std::unique_ptr<VideoEncoder> hw_encoder,
513     bool prefer_temporal_support) {
514   return std::make_unique<VideoEncoderSoftwareFallbackWrapper>(
515       std::move(sw_fallback_encoder), std::move(hw_encoder),
516       prefer_temporal_support);
517 }
518 
519 }  // namespace webrtc
520