magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 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 | |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 11 | #include "api/video_codecs/video_encoder_software_fallback_wrapper.h" |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 12 | |
Yves Gerey | 3e70781 | 2018-11-28 15:47:49 | [diff] [blame] | 13 | #include <stdint.h> |
Jonas Olsson | a4d8737 | 2019-07-05 17:08:33 | [diff] [blame] | 14 | |
Piotr Tworek | 5e4833c | 2017-12-12 11:09:31 | [diff] [blame] | 15 | #include <cstdio> |
Mirko Bonadei | 317a1f0 | 2019-09-17 15:06:18 | [diff] [blame] | 16 | #include <memory> |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 17 | #include <string> |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 18 | #include <vector> |
Steve Anton | e78bcb9 | 2017-10-31 16:53:08 | [diff] [blame] | 19 | |
Mirko Bonadei | 06d3559 | 2020-04-01 11:43:08 | [diff] [blame] | 20 | #include "absl/strings/match.h" |
Yves Gerey | 3e70781 | 2018-11-28 15:47:49 | [diff] [blame] | 21 | #include "absl/types/optional.h" |
Elad Alon | 8f01c4e | 2019-06-28 13:19:43 | [diff] [blame] | 22 | #include "api/fec_controller_override.h" |
Evan Shrubsole | 546a9e4 | 2020-02-11 15:18:07 | [diff] [blame] | 23 | #include "api/video/i420_buffer.h" |
Yves Gerey | 3e70781 | 2018-11-28 15:47:49 | [diff] [blame] | 24 | #include "api/video/video_bitrate_allocation.h" |
| 25 | #include "api/video/video_frame.h" |
| 26 | #include "api/video_codecs/video_codec.h" |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 27 | #include "api/video_codecs/video_encoder.h" |
Ilya Nikolaevskiy | c98aebb | 2021-07-13 14:27:11 | [diff] [blame] | 28 | #include "media/base/video_common.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 29 | #include "modules/video_coding/include/video_error_codes.h" |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 30 | #include "modules/video_coding/utility/simulcast_utility.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 31 | #include "rtc_base/checks.h" |
| 32 | #include "rtc_base/logging.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 33 | #include "system_wrappers/include/field_trial.h" |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 34 | |
| 35 | namespace webrtc { |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 36 | |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 37 | namespace { |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 38 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 39 | // If forced fallback is allowed, either: |
| 40 | // |
| 41 | // 1) The forced fallback is requested if the resolution is less than or equal |
Artem Titov | 0e61fdd | 2021-07-25 19:50:14 | [diff] [blame] | 42 | // to `max_pixels_`. The resolution is allowed to be scaled down to |
| 43 | // `min_pixels_`. |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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: |
| 50 | bool SupportsResolutionBasedSwitch(const VideoCodec& codec) const { |
| 51 | return enable_resolution_based_switch && |
| 52 | codec.codecType == kVideoCodecVP8 && |
| 53 | codec.numberOfSimulcastStreams <= 1 && |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 54 | codec.width * codec.height <= max_pixels; |
| 55 | } |
| 56 | |
| 57 | bool SupportsTemporalBasedSwitch(const VideoCodec& codec) const { |
| 58 | return enable_temporal_based_switch && |
Jakob Ivarsson | a9961b3 | 2020-11-18 09:18:35 | [diff] [blame] | 59 | SimulcastUtility::NumberOfTemporalLayers(codec, 0) != 1; |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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 | |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 68 | const char kVp8ForceFallbackEncoderFieldTrial[] = |
Åsa Persson | 45bbc8a | 2017-11-13 09:16:47 | [diff] [blame] | 69 | "WebRTC-VP8-Forced-Fallback-Encoder-v2"; |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 70 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 71 | absl::optional<ForcedFallbackParams> ParseFallbackParamsFromFieldTrials( |
| 72 | const VideoEncoder& main_encoder) { |
| 73 | const std::string field_trial = |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 74 | webrtc::field_trial::FindFullName(kVp8ForceFallbackEncoderFieldTrial); |
Mirko Bonadei | 06d3559 | 2020-04-01 11:43:08 | [diff] [blame] | 75 | if (!absl::StartsWith(field_trial, "Enabled")) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 76 | return absl::nullopt; |
| 77 | } |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 78 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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", ¶ms.min_pixels, |
| 87 | ¶ms.max_pixels, &min_bps) != 3) { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 88 | RTC_LOG(LS_WARNING) |
| 89 | << "Invalid number of forced fallback parameters provided."; |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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) { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 94 | RTC_LOG(LS_WARNING) << "Invalid forced fallback parameter value provided."; |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 95 | return absl::nullopt; |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 96 | } |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 97 | |
| 98 | return params; |
| 99 | } |
| 100 | |
| 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; |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 113 | } |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 114 | |
| 115 | class VideoEncoderSoftwareFallbackWrapper final : public VideoEncoder { |
| 116 | public: |
| 117 | VideoEncoderSoftwareFallbackWrapper( |
| 118 | std::unique_ptr<webrtc::VideoEncoder> sw_encoder, |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 119 | std::unique_ptr<webrtc::VideoEncoder> hw_encoder, |
| 120 | bool prefer_temporal_support); |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 121 | ~VideoEncoderSoftwareFallbackWrapper() override; |
| 122 | |
Elad Alon | 8f01c4e | 2019-06-28 13:19:43 | [diff] [blame] | 123 | void SetFecControllerOverride( |
| 124 | FecControllerOverride* fec_controller_override) override; |
| 125 | |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 126 | int32_t InitEncode(const VideoCodec* codec_settings, |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 127 | const VideoEncoder::Settings& settings) override; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 128 | |
| 129 | int32_t RegisterEncodeCompleteCallback( |
| 130 | EncodedImageCallback* callback) override; |
| 131 | |
| 132 | int32_t Release() override; |
Elad Alon | 65764e4 | 2019-06-28 16:43:44 | [diff] [blame] | 133 | |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 134 | int32_t Encode(const VideoFrame& frame, |
Niels Möller | 87e2d78 | 2019-03-07 09:18:23 | [diff] [blame] | 135 | const std::vector<VideoFrameType>* frame_types) override; |
Elad Alon | 65764e4 | 2019-06-28 16:43:44 | [diff] [blame] | 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 | |
Erik Språng | 16cb8f5 | 2019-04-12 11:59:09 | [diff] [blame] | 143 | void SetRates(const RateControlParameters& parameters) override; |
Elad Alon | 65764e4 | 2019-06-28 16:43:44 | [diff] [blame] | 144 | |
Erik Språng | e2fd86a7 | 2018-10-24 09:32:39 | [diff] [blame] | 145 | EncoderInfo GetEncoderInfo() const override; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 146 | |
| 147 | private: |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 148 | bool InitFallbackEncoder(bool is_forced); |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 149 | bool TryInitForcedFallbackEncoder(); |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 150 | bool IsFallbackActive() const; |
| 151 | |
| 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. |
Danil Chapovalov | 46cc32d | 2022-01-17 13:41:13 | [diff] [blame] | 158 | [[fallthrough]]; |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 159 | case EncoderState::kMainEncoderUsed: |
| 160 | return encoder_.get(); |
| 161 | case EncoderState::kFallbackDueToFailure: |
| 162 | case EncoderState::kForcedFallback: |
| 163 | return fallback_encoder_.get(); |
| 164 | } |
Karl Wiberg | c95b939 | 2020-11-07 23:49:37 | [diff] [blame] | 165 | RTC_CHECK_NOTREACHED(); |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | // Updates encoder with last observed parameters, such as callbacks, rates, |
| 169 | // etc. |
| 170 | void PrimeEncoder(VideoEncoder* encoder) const; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 171 | |
| 172 | // Settings used in the last InitEncode call and used if a dynamic fallback to |
| 173 | // software is required. |
| 174 | VideoCodec codec_settings_; |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 175 | absl::optional<VideoEncoder::Settings> encoder_settings_; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 176 | |
Erik Språng | 16cb8f5 | 2019-04-12 11:59:09 | [diff] [blame] | 177 | // The last rate control settings, if set. |
| 178 | absl::optional<RateControlParameters> rate_control_parameters_; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 179 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 180 | // The last channel parameters set. |
| 181 | absl::optional<float> packet_loss_; |
| 182 | absl::optional<int64_t> rtt_; |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 183 | absl::optional<LossNotification> loss_notification_; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 184 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 185 | enum class EncoderState { |
| 186 | kUninitialized, |
| 187 | kMainEncoderUsed, |
| 188 | kFallbackDueToFailure, |
| 189 | kForcedFallback |
| 190 | }; |
| 191 | |
| 192 | EncoderState encoder_state_; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 193 | const std::unique_ptr<webrtc::VideoEncoder> encoder_; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 194 | const std::unique_ptr<webrtc::VideoEncoder> fallback_encoder_; |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 195 | |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 196 | EncodedImageCallback* callback_; |
| 197 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 198 | const absl::optional<ForcedFallbackParams> fallback_params_; |
Evan Shrubsole | 546a9e4 | 2020-02-11 15:18:07 | [diff] [blame] | 199 | int32_t EncodeWithMainEncoder(const VideoFrame& frame, |
| 200 | const std::vector<VideoFrameType>* frame_types); |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 201 | }; |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 202 | |
| 203 | VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper( |
Magnus Jedvert | ee92d62 | 2017-11-13 14:26:17 | [diff] [blame] | 204 | std::unique_ptr<webrtc::VideoEncoder> sw_encoder, |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 205 | std::unique_ptr<webrtc::VideoEncoder> hw_encoder, |
| 206 | bool prefer_temporal_support) |
Jiwon Jung | 3791d33 | 2021-10-08 09:22:57 | [diff] [blame] | 207 | : encoder_state_(EncoderState::kUninitialized), |
Magnus Jedvert | ee92d62 | 2017-11-13 14:26:17 | [diff] [blame] | 208 | encoder_(std::move(hw_encoder)), |
| 209 | fallback_encoder_(std::move(sw_encoder)), |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 210 | callback_(nullptr), |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 211 | fallback_params_( |
| 212 | GetForcedFallbackParams(prefer_temporal_support, *encoder_)) { |
Elad Alon | 8f01c4e | 2019-06-28 13:19:43 | [diff] [blame] | 213 | RTC_DCHECK(fallback_encoder_); |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 214 | } |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 215 | |
Paulina Hensman | a680a6a | 2018-04-05 09:42:24 | [diff] [blame] | 216 | VideoEncoderSoftwareFallbackWrapper::~VideoEncoderSoftwareFallbackWrapper() = |
| 217 | default; |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 218 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 219 | 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 | } |
Jiwon Jung | 3791d33 | 2021-10-08 09:22:57 | [diff] [blame] | 235 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 236 | if (loss_notification_.has_value()) { |
| 237 | encoder->OnLossNotification(loss_notification_.value()); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder(bool is_forced) { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 242 | RTC_LOG(LS_WARNING) << "Encoder falling back to software encoding."; |
Magnus Jedvert | ee92d62 | 2017-11-13 14:26:17 | [diff] [blame] | 243 | |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 244 | RTC_DCHECK(encoder_settings_.has_value()); |
| 245 | const int ret = fallback_encoder_->InitEncode(&codec_settings_, |
| 246 | encoder_settings_.value()); |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 247 | |
| 248 | if (ret != WEBRTC_VIDEO_CODEC_OK) { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 249 | RTC_LOG(LS_ERROR) << "Failed to initialize software-encoder fallback."; |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 250 | fallback_encoder_->Release(); |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 251 | return false; |
| 252 | } |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 253 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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 | |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 267 | return true; |
| 268 | } |
| 269 | |
Elad Alon | 8f01c4e | 2019-06-28 13:19:43 | [diff] [blame] | 270 | void VideoEncoderSoftwareFallbackWrapper::SetFecControllerOverride( |
| 271 | FecControllerOverride* fec_controller_override) { |
| 272 | // It is important that only one of those would ever interact with the |
Artem Titov | 0e61fdd | 2021-07-25 19:50:14 | [diff] [blame] | 273 | // `fec_controller_override` at a given time. This is the responsibility |
| 274 | // of `this` to maintain. |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 275 | |
Jiwon Jung | 3791d33 | 2021-10-08 09:22:57 | [diff] [blame] | 276 | encoder_->SetFecControllerOverride(fec_controller_override); |
| 277 | fallback_encoder_->SetFecControllerOverride(fec_controller_override); |
Elad Alon | 8f01c4e | 2019-06-28 13:19:43 | [diff] [blame] | 278 | } |
| 279 | |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 280 | int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode( |
| 281 | const VideoCodec* codec_settings, |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 282 | const VideoEncoder::Settings& settings) { |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 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; |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 286 | encoder_settings_ = settings; |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 287 | // Clear stored rate/channel parameters. |
Erik Språng | 16cb8f5 | 2019-04-12 11:59:09 | [diff] [blame] | 288 | rate_control_parameters_ = absl::nullopt; |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 289 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 290 | RTC_DCHECK_EQ(encoder_state_, EncoderState::kUninitialized) |
| 291 | << "InitEncode() should never be called on an active instance!"; |
| 292 | |
Åsa Persson | 45bbc8a | 2017-11-13 09:16:47 | [diff] [blame] | 293 | // Try to init forced software codec if it should be used. |
| 294 | if (TryInitForcedFallbackEncoder()) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 295 | PrimeEncoder(current_encoder()); |
Åsa Persson | 45bbc8a | 2017-11-13 09:16:47 | [diff] [blame] | 296 | return WEBRTC_VIDEO_CODEC_OK; |
| 297 | } |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 298 | |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 299 | int32_t ret = encoder_->InitEncode(codec_settings, settings); |
Magnus Jedvert | ee92d62 | 2017-11-13 14:26:17 | [diff] [blame] | 300 | if (ret == WEBRTC_VIDEO_CODEC_OK) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 301 | encoder_state_ = EncoderState::kMainEncoderUsed; |
| 302 | PrimeEncoder(current_encoder()); |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 303 | return ret; |
| 304 | } |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 305 | |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 306 | // Try to instantiate software codec. |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 307 | if (InitFallbackEncoder(/*is_forced=*/false)) { |
| 308 | PrimeEncoder(current_encoder()); |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 309 | return WEBRTC_VIDEO_CODEC_OK; |
| 310 | } |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 311 | |
| 312 | // Software encoder failed too, use original return code. |
| 313 | encoder_state_ = EncoderState::kUninitialized; |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 314 | return ret; |
| 315 | } |
| 316 | |
| 317 | int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback( |
| 318 | EncodedImageCallback* callback) { |
| 319 | callback_ = callback; |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 320 | return current_encoder()->RegisterEncodeCompleteCallback(callback); |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 321 | } |
| 322 | |
| 323 | int32_t VideoEncoderSoftwareFallbackWrapper::Release() { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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; |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 330 | } |
| 331 | |
| 332 | int32_t VideoEncoderSoftwareFallbackWrapper::Encode( |
| 333 | const VideoFrame& frame, |
Niels Möller | 87e2d78 | 2019-03-07 09:18:23 | [diff] [blame] | 334 | const std::vector<VideoFrameType>* frame_types) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 335 | switch (encoder_state_) { |
| 336 | case EncoderState::kUninitialized: |
| 337 | return WEBRTC_VIDEO_CODEC_ERROR; |
| 338 | case EncoderState::kMainEncoderUsed: { |
Evan Shrubsole | 546a9e4 | 2020-02-11 15:18:07 | [diff] [blame] | 339 | return EncodeWithMainEncoder(frame, frame_types); |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 340 | } |
| 341 | case EncoderState::kFallbackDueToFailure: |
| 342 | case EncoderState::kForcedFallback: |
| 343 | return fallback_encoder_->Encode(frame, frame_types); |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 344 | } |
Karl Wiberg | c95b939 | 2020-11-07 23:49:37 | [diff] [blame] | 345 | RTC_CHECK_NOTREACHED(); |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 346 | } |
Fabien Vallée | f8b5bfe | 2020-10-22 08:08:50 | [diff] [blame] | 347 | |
Evan Shrubsole | 546a9e4 | 2020-02-11 15:18:07 | [diff] [blame] | 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 { |
Harald Alvestrand | 97597c0 | 2021-11-04 12:01:23 | [diff] [blame] | 361 | RTC_LOG(LS_INFO) << "Fallback encoder does not support native handle - " |
| 362 | "converting frame to I420"; |
Evan Shrubsole | 546a9e4 | 2020-02-11 15:18:07 | [diff] [blame] | 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 | } |
Evan Shrubsole | 5089a8e | 2020-10-12 12:43:39 | [diff] [blame] | 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 | } |
Evan Shrubsole | 546a9e4 | 2020-02-11 15:18:07 | [diff] [blame] | 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 | } |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 385 | |
Erik Språng | 16cb8f5 | 2019-04-12 11:59:09 | [diff] [blame] | 386 | void VideoEncoderSoftwareFallbackWrapper::SetRates( |
| 387 | const RateControlParameters& parameters) { |
| 388 | rate_control_parameters_ = parameters; |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 389 | return current_encoder()->SetRates(parameters); |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 390 | } |
| 391 | |
Elad Alon | 65764e4 | 2019-06-28 16:43:44 | [diff] [blame] | 392 | void VideoEncoderSoftwareFallbackWrapper::OnPacketLossRateUpdate( |
| 393 | float packet_loss_rate) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 394 | packet_loss_ = packet_loss_rate; |
| 395 | current_encoder()->OnPacketLossRateUpdate(packet_loss_rate); |
Elad Alon | 65764e4 | 2019-06-28 16:43:44 | [diff] [blame] | 396 | } |
| 397 | |
| 398 | void VideoEncoderSoftwareFallbackWrapper::OnRttUpdate(int64_t rtt_ms) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 399 | rtt_ = rtt_ms; |
| 400 | current_encoder()->OnRttUpdate(rtt_ms); |
Elad Alon | 65764e4 | 2019-06-28 16:43:44 | [diff] [blame] | 401 | } |
| 402 | |
| 403 | void VideoEncoderSoftwareFallbackWrapper::OnLossNotification( |
| 404 | const LossNotification& loss_notification) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 405 | loss_notification_ = loss_notification; |
| 406 | current_encoder()->OnLossNotification(loss_notification); |
Elad Alon | 65764e4 | 2019-06-28 16:43:44 | [diff] [blame] | 407 | } |
| 408 | |
Erik Språng | e2fd86a7 | 2018-10-24 09:32:39 | [diff] [blame] | 409 | VideoEncoder::EncoderInfo VideoEncoderSoftwareFallbackWrapper::GetEncoderInfo() |
| 410 | const { |
| 411 | EncoderInfo fallback_encoder_info = fallback_encoder_->GetEncoderInfo(); |
| 412 | EncoderInfo default_encoder_info = encoder_->GetEncoderInfo(); |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 413 | |
Erik Språng | e2fd86a7 | 2018-10-24 09:32:39 | [diff] [blame] | 414 | EncoderInfo info = |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 415 | IsFallbackActive() ? fallback_encoder_info : default_encoder_info; |
Erik Språng | e2fd86a7 | 2018-10-24 09:32:39 | [diff] [blame] | 416 | |
Ilya Nikolaevskiy | c98aebb | 2021-07-13 14:27:11 | [diff] [blame] | 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 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 424 | if (fallback_params_.has_value()) { |
| 425 | const auto settings = (encoder_state_ == EncoderState::kForcedFallback) |
Erik Språng | e2fd86a7 | 2018-10-24 09:32:39 | [diff] [blame] | 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, |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 432 | fallback_params_->min_pixels) |
Erik Språng | e2fd86a7 | 2018-10-24 09:32:39 | [diff] [blame] | 433 | : VideoEncoder::ScalingSettings::kOff; |
| 434 | } else { |
| 435 | info.scaling_settings = default_encoder_info.scaling_settings; |
Åsa Persson | 45bbc8a | 2017-11-13 09:16:47 | [diff] [blame] | 436 | } |
kthelgason | 876222f | 2016-11-29 09:44:11 | [diff] [blame] | 437 | |
Erik Språng | e2fd86a7 | 2018-10-24 09:32:39 | [diff] [blame] | 438 | return info; |
kthelgason | 535dbd3 | 2017-01-26 08:36:31 | [diff] [blame] | 439 | } |
| 440 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 441 | bool VideoEncoderSoftwareFallbackWrapper::IsFallbackActive() const { |
| 442 | return encoder_state_ == EncoderState::kForcedFallback || |
| 443 | encoder_state_ == EncoderState::kFallbackDueToFailure; |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 444 | } |
| 445 | |
Åsa Persson | 45bbc8a | 2017-11-13 09:16:47 | [diff] [blame] | 446 | bool VideoEncoderSoftwareFallbackWrapper::TryInitForcedFallbackEncoder() { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 447 | if (!fallback_params_) { |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 448 | return false; |
Åsa Persson | 45bbc8a | 2017-11-13 09:16:47 | [diff] [blame] | 449 | } |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 450 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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); |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 458 | } |
Elad Alon | 370f93a | 2019-06-11 12:57:57 | [diff] [blame] | 459 | |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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; |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 465 | } |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 466 | |
| 467 | if (encoder_state_ == EncoderState::kMainEncoderUsed && |
Jakob Ivarsson | a9961b3 | 2020-11-18 09:18:35 | [diff] [blame] | 468 | encoder_->GetEncoderInfo().fps_allocation[0].size() != 1) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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) { |
Jakob Ivarsson | a9961b3 | 2020-11-18 09:18:35 | [diff] [blame] | 477 | if (fallback_encoder_->GetEncoderInfo().fps_allocation[0].size() != 1) { |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 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 | } |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 502 | } |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 503 | |
| 504 | // Neither forced fallback mode supported. |
| 505 | return false; |
asapersson | 22c76c4 | 2017-08-16 07:53:59 | [diff] [blame] | 506 | } |
| 507 | |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 508 | } // namespace |
| 509 | |
| 510 | std::unique_ptr<VideoEncoder> CreateVideoEncoderSoftwareFallbackWrapper( |
| 511 | std::unique_ptr<VideoEncoder> sw_fallback_encoder, |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 512 | std::unique_ptr<VideoEncoder> hw_encoder, |
| 513 | bool prefer_temporal_support) { |
Mirko Bonadei | 317a1f0 | 2019-09-17 15:06:18 | [diff] [blame] | 514 | return std::make_unique<VideoEncoderSoftwareFallbackWrapper>( |
Erik Språng | 261f792 | 2020-01-31 12:51:12 | [diff] [blame] | 515 | std::move(sw_fallback_encoder), std::move(hw_encoder), |
| 516 | prefer_temporal_support); |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 517 | } |
| 518 | |
magjed | 614d5b7 | 2016-11-15 14:30:54 | [diff] [blame] | 519 | } // namespace webrtc |