Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2020 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 | #include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" |
| 11 | |
| 12 | #include <stddef.h> |
| 13 | #include <stdint.h> |
| 14 | |
| 15 | #include <memory> |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 16 | #include <utility> |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 17 | #include <vector> |
| 18 | |
| 19 | #include "absl/algorithm/container.h" |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 20 | #include "absl/base/macros.h" |
Danil Chapovalov | b4913a5 | 2024-03-11 12:17:45 | [diff] [blame] | 21 | #include "absl/base/nullability.h" |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 22 | #include "absl/strings/match.h" |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 23 | #include "absl/types/optional.h" |
Danil Chapovalov | b4913a5 | 2024-03-11 12:17:45 | [diff] [blame] | 24 | #include "api/environment/environment.h" |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 25 | #include "api/field_trials_view.h" |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 26 | #include "api/scoped_refptr.h" |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 27 | #include "api/transport/field_trial_based_config.h" |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 28 | #include "api/video/encoded_image.h" |
| 29 | #include "api/video/i420_buffer.h" |
| 30 | #include "api/video/video_frame.h" |
Evan Shrubsole | 9b235cd | 2022-12-06 10:09:10 | [diff] [blame] | 31 | #include "api/video_codecs/scalability_mode.h" |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 32 | #include "api/video_codecs/video_codec.h" |
| 33 | #include "api/video_codecs/video_encoder.h" |
| 34 | #include "modules/video_coding/include/video_codec_interface.h" |
| 35 | #include "modules/video_coding/include/video_error_codes.h" |
Danil Chapovalov | 9f4859e | 2020-10-16 15:45:41 | [diff] [blame] | 36 | #include "modules/video_coding/svc/create_scalability_structure.h" |
Danil Chapovalov | da7fe39 | 2020-10-15 13:57:17 | [diff] [blame] | 37 | #include "modules/video_coding/svc/scalable_video_controller.h" |
| 38 | #include "modules/video_coding/svc/scalable_video_controller_no_layering.h" |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 39 | #include "rtc_base/checks.h" |
Michael Horowitz | b27efd4 | 2023-02-28 15:59:50 | [diff] [blame] | 40 | #include "rtc_base/experiments/encoder_info_settings.h" |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 41 | #include "rtc_base/logging.h" |
| 42 | #include "third_party/libaom/source/libaom/aom/aom_codec.h" |
| 43 | #include "third_party/libaom/source/libaom/aom/aom_encoder.h" |
| 44 | #include "third_party/libaom/source/libaom/aom/aomcx.h" |
| 45 | |
Sergey Silkin | a4b2b95 | 2023-10-06 11:18:41 | [diff] [blame] | 46 | #if (defined(WEBRTC_ARCH_ARM) || defined(WEBRTC_ARCH_ARM64)) && \ |
| 47 | (defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)) |
| 48 | #define MOBILE_ARM |
| 49 | #endif |
| 50 | |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 51 | #define SET_ENCODER_PARAM_OR_RETURN_ERROR(param_id, param_value) \ |
| 52 | do { \ |
| 53 | if (!SetEncoderControlParameters(param_id, param_value)) { \ |
| 54 | return WEBRTC_VIDEO_CODEC_ERROR; \ |
| 55 | } \ |
| 56 | } while (0) |
| 57 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 58 | namespace webrtc { |
| 59 | namespace { |
| 60 | |
| 61 | // Encoder configuration parameters |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 62 | constexpr int kQpMin = 10; |
Fyodor Kyslov | b454767 | 2021-04-05 21:37:32 | [diff] [blame] | 63 | constexpr int kUsageProfile = AOM_USAGE_REALTIME; |
Florent Castelli | 90b7438 | 2022-04-25 15:28:00 | [diff] [blame] | 64 | constexpr int kMinQindex = 145; // Min qindex threshold for QP scaling. |
| 65 | constexpr int kMaxQindex = 205; // Max qindex threshold for QP scaling. |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 66 | constexpr int kBitDepth = 8; |
| 67 | constexpr int kLagInFrames = 0; // No look ahead. |
| 68 | constexpr int kRtpTicksPerSecond = 90000; |
Wan-Teh Chang | f6eb9d6 | 2023-02-14 22:28:47 | [diff] [blame] | 69 | constexpr double kMinimumFrameRate = 1.0; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 70 | |
Jerome Jiang | 04a6529 | 2021-02-18 23:21:39 | [diff] [blame] | 71 | aom_superblock_size_t GetSuperblockSize(int width, int height, int threads) { |
| 72 | int resolution = width * height; |
| 73 | if (threads >= 4 && resolution >= 960 * 540 && resolution < 1920 * 1080) |
| 74 | return AOM_SUPERBLOCK_SIZE_64X64; |
| 75 | else |
| 76 | return AOM_SUPERBLOCK_SIZE_DYNAMIC; |
| 77 | } |
| 78 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 79 | class LibaomAv1Encoder final : public VideoEncoder { |
| 80 | public: |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 81 | LibaomAv1Encoder(const absl::optional<LibaomAv1EncoderAuxConfig>& aux_config, |
| 82 | const FieldTrialsView& trials); |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 83 | ~LibaomAv1Encoder(); |
| 84 | |
| 85 | int InitEncode(const VideoCodec* codec_settings, |
| 86 | const Settings& settings) override; |
| 87 | |
| 88 | int32_t RegisterEncodeCompleteCallback( |
| 89 | EncodedImageCallback* encoded_image_callback) override; |
| 90 | |
| 91 | int32_t Release() override; |
| 92 | |
| 93 | int32_t Encode(const VideoFrame& frame, |
| 94 | const std::vector<VideoFrameType>* frame_types) override; |
| 95 | |
| 96 | void SetRates(const RateControlParameters& parameters) override; |
| 97 | |
| 98 | EncoderInfo GetEncoderInfo() const override; |
| 99 | |
| 100 | private: |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 101 | template <typename P> |
| 102 | bool SetEncoderControlParameters(int param_id, P param_value); |
| 103 | |
“Michael | 15ee87f | 2022-03-03 18:09:02 | [diff] [blame] | 104 | // Get value to be used for encoder cpu_speed setting |
| 105 | int GetCpuSpeed(int width, int height); |
| 106 | |
Jerome Jiang | 39d1f74 | 2020-12-17 01:54:33 | [diff] [blame] | 107 | // Determine number of encoder threads to use. |
| 108 | int NumberOfThreads(int width, int height, int number_of_cores); |
| 109 | |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 110 | bool SvcEnabled() const { return svc_params_.has_value(); } |
| 111 | // Fills svc_params_ memeber value. Returns false on error. |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 112 | bool SetSvcParams(ScalableVideoController::StreamLayersConfig svc_config); |
| 113 | // Configures the encoder with layer for the next frame. |
| 114 | void SetSvcLayerId( |
| 115 | const ScalableVideoController::LayerFrameConfig& layer_frame); |
| 116 | // Configures the encoder which buffers next frame updates and can reference. |
| 117 | void SetSvcRefFrameConfig( |
| 118 | const ScalableVideoController::LayerFrameConfig& layer_frame); |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 119 | // If pixel format doesn't match, then reallocate. |
| 120 | void MaybeRewrapImgWithFormat(const aom_img_fmt_t fmt); |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 121 | |
Danil Chapovalov | 9f4859e | 2020-10-16 15:45:41 | [diff] [blame] | 122 | std::unique_ptr<ScalableVideoController> svc_controller_; |
Evan Shrubsole | 9b235cd | 2022-12-06 10:09:10 | [diff] [blame] | 123 | absl::optional<ScalabilityMode> scalability_mode_; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 124 | bool inited_; |
philipel | 0763ccc | 2021-12-01 14:12:54 | [diff] [blame] | 125 | bool rates_configured_; |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 126 | absl::optional<aom_svc_params_t> svc_params_; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 127 | VideoCodec encoder_settings_; |
philipel | 30ec725 | 2022-05-04 09:20:04 | [diff] [blame] | 128 | absl::optional<LibaomAv1EncoderAuxConfig> aux_config_; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 129 | aom_image_t* frame_for_encode_; |
| 130 | aom_codec_ctx_t ctx_; |
| 131 | aom_codec_enc_cfg_t cfg_; |
| 132 | EncodedImageCallback* encoded_image_callback_; |
Erik SprĂĄng | ff1cf61 | 2023-02-23 17:41:13 | [diff] [blame] | 133 | int64_t timestamp_; |
Michael Horowitz | b27efd4 | 2023-02-28 15:59:50 | [diff] [blame] | 134 | const LibaomAv1EncoderInfoSettings encoder_info_override_; |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 135 | // TODO(webrtc:15225): Kill switch for disabling frame dropping. Remove it |
| 136 | // after frame dropping is fully rolled out. |
| 137 | bool disable_frame_dropping_; |
Dan Tan | 4860148 | 2024-01-31 17:11:18 | [diff] [blame] | 138 | int max_consec_frame_drop_; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 139 | }; |
| 140 | |
| 141 | int32_t VerifyCodecSettings(const VideoCodec& codec_settings) { |
| 142 | if (codec_settings.width < 1) { |
| 143 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 144 | } |
| 145 | if (codec_settings.height < 1) { |
| 146 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 147 | } |
| 148 | // maxBitrate == 0 represents an unspecified maxBitRate. |
| 149 | if (codec_settings.maxBitrate > 0 && |
| 150 | codec_settings.minBitrate > codec_settings.maxBitrate) { |
| 151 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 152 | } |
| 153 | if (codec_settings.maxBitrate > 0 && |
| 154 | codec_settings.startBitrate > codec_settings.maxBitrate) { |
| 155 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 156 | } |
| 157 | if (codec_settings.startBitrate < codec_settings.minBitrate) { |
| 158 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 159 | } |
| 160 | if (codec_settings.maxFramerate < 1) { |
| 161 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 162 | } |
Wan-Teh Chang | 8f29b42 | 2023-03-16 01:39:57 | [diff] [blame] | 163 | if (codec_settings.qpMax < kQpMin || codec_settings.qpMax > 63) { |
| 164 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 165 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 166 | return WEBRTC_VIDEO_CODEC_OK; |
| 167 | } |
| 168 | |
Dan Tan | 4860148 | 2024-01-31 17:11:18 | [diff] [blame] | 169 | int GetMaxConsecutiveFrameDrop(const FieldTrialsView& field_trials) { |
| 170 | webrtc::FieldTrialParameter<int> maxdrop("maxdrop", 0); |
| 171 | webrtc::ParseFieldTrial( |
| 172 | {&maxdrop}, |
| 173 | field_trials.Lookup("WebRTC-LibaomAv1Encoder-MaxConsecFrameDrop")); |
| 174 | return maxdrop; |
| 175 | } |
| 176 | |
philipel | 30ec725 | 2022-05-04 09:20:04 | [diff] [blame] | 177 | LibaomAv1Encoder::LibaomAv1Encoder( |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 178 | const absl::optional<LibaomAv1EncoderAuxConfig>& aux_config, |
| 179 | const FieldTrialsView& trials) |
Danil Chapovalov | 11215fe | 2021-01-21 14:56:05 | [diff] [blame] | 180 | : inited_(false), |
philipel | 0763ccc | 2021-12-01 14:12:54 | [diff] [blame] | 181 | rates_configured_(false), |
philipel | 30ec725 | 2022-05-04 09:20:04 | [diff] [blame] | 182 | aux_config_(aux_config), |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 183 | frame_for_encode_(nullptr), |
Erik SprĂĄng | ff1cf61 | 2023-02-23 17:41:13 | [diff] [blame] | 184 | encoded_image_callback_(nullptr), |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 185 | timestamp_(0), |
| 186 | disable_frame_dropping_(absl::StartsWith( |
| 187 | trials.Lookup("WebRTC-LibaomAv1Encoder-DisableFrameDropping"), |
Dan Tan | 4860148 | 2024-01-31 17:11:18 | [diff] [blame] | 188 | "Enabled")), |
| 189 | max_consec_frame_drop_(GetMaxConsecutiveFrameDrop(trials)) {} |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 190 | |
| 191 | LibaomAv1Encoder::~LibaomAv1Encoder() { |
| 192 | Release(); |
| 193 | } |
| 194 | |
| 195 | int LibaomAv1Encoder::InitEncode(const VideoCodec* codec_settings, |
| 196 | const Settings& settings) { |
| 197 | if (codec_settings == nullptr) { |
| 198 | RTC_LOG(LS_WARNING) << "No codec settings provided to " |
| 199 | "LibaomAv1Encoder."; |
| 200 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 201 | } |
| 202 | if (settings.number_of_cores < 1) { |
| 203 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 204 | } |
| 205 | if (inited_) { |
| 206 | RTC_LOG(LS_WARNING) << "Initing LibaomAv1Encoder without first releasing."; |
| 207 | Release(); |
| 208 | } |
| 209 | encoder_settings_ = *codec_settings; |
| 210 | |
| 211 | // Sanity checks for encoder configuration. |
| 212 | const int32_t result = VerifyCodecSettings(encoder_settings_); |
| 213 | if (result < 0) { |
| 214 | RTC_LOG(LS_WARNING) << "Incorrect codec settings provided to " |
| 215 | "LibaomAv1Encoder."; |
| 216 | return result; |
| 217 | } |
Danil Chapovalov | 9f4859e | 2020-10-16 15:45:41 | [diff] [blame] | 218 | if (encoder_settings_.numberOfSimulcastStreams > 1) { |
| 219 | RTC_LOG(LS_WARNING) << "Simulcast is not implemented by LibaomAv1Encoder."; |
| 220 | return result; |
| 221 | } |
Evan Shrubsole | 9b235cd | 2022-12-06 10:09:10 | [diff] [blame] | 222 | scalability_mode_ = encoder_settings_.GetScalabilityMode(); |
| 223 | if (!scalability_mode_.has_value()) { |
Niels Möller | cc17195 | 2022-04-06 11:37:40 | [diff] [blame] | 224 | RTC_LOG(LS_WARNING) << "Scalability mode is not set, using 'L1T1'."; |
Evan Shrubsole | 9b235cd | 2022-12-06 10:09:10 | [diff] [blame] | 225 | scalability_mode_ = ScalabilityMode::kL1T1; |
Danil Chapovalov | 9f4859e | 2020-10-16 15:45:41 | [diff] [blame] | 226 | } |
Evan Shrubsole | 9b235cd | 2022-12-06 10:09:10 | [diff] [blame] | 227 | svc_controller_ = CreateScalabilityStructure(*scalability_mode_); |
Danil Chapovalov | 9f4859e | 2020-10-16 15:45:41 | [diff] [blame] | 228 | if (svc_controller_ == nullptr) { |
| 229 | RTC_LOG(LS_WARNING) << "Failed to set scalability mode " |
Evan Shrubsole | 9b235cd | 2022-12-06 10:09:10 | [diff] [blame] | 230 | << static_cast<int>(*scalability_mode_); |
Danil Chapovalov | 9f4859e | 2020-10-16 15:45:41 | [diff] [blame] | 231 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 232 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 233 | |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 234 | if (!SetSvcParams(svc_controller_->StreamConfig())) { |
| 235 | return WEBRTC_VIDEO_CODEC_ERROR; |
| 236 | } |
| 237 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 238 | // Initialize encoder configuration structure with default values |
| 239 | aom_codec_err_t ret = |
Fyodor Kyslov | b454767 | 2021-04-05 21:37:32 | [diff] [blame] | 240 | aom_codec_enc_config_default(aom_codec_av1_cx(), &cfg_, kUsageProfile); |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 241 | if (ret != AOM_CODEC_OK) { |
| 242 | RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret |
| 243 | << " on aom_codec_enc_config_default."; |
| 244 | return WEBRTC_VIDEO_CODEC_ERROR; |
| 245 | } |
| 246 | |
| 247 | // Overwrite default config with input encoder settings & RTC-relevant values. |
| 248 | cfg_.g_w = encoder_settings_.width; |
| 249 | cfg_.g_h = encoder_settings_.height; |
Jerome Jiang | 39d1f74 | 2020-12-17 01:54:33 | [diff] [blame] | 250 | cfg_.g_threads = |
| 251 | NumberOfThreads(cfg_.g_w, cfg_.g_h, settings.number_of_cores); |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 252 | cfg_.g_timebase.num = 1; |
| 253 | cfg_.g_timebase.den = kRtpTicksPerSecond; |
Yu-Chen (Eric) Sun | 35f2b89 | 2023-04-05 20:29:21 | [diff] [blame] | 254 | cfg_.rc_target_bitrate = encoder_settings_.startBitrate; // kilobits/sec. |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 255 | cfg_.rc_dropframe_thresh = |
| 256 | (!disable_frame_dropping_ && encoder_settings_.GetFrameDropEnabled()) ? 30 |
| 257 | : 0; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 258 | cfg_.g_input_bit_depth = kBitDepth; |
| 259 | cfg_.kf_mode = AOM_KF_DISABLED; |
| 260 | cfg_.rc_min_quantizer = kQpMin; |
Jerome Jiang | 7f7fb83 | 2020-06-04 03:44:50 | [diff] [blame] | 261 | cfg_.rc_max_quantizer = encoder_settings_.qpMax; |
Jerome Jiang | d45f930 | 2021-07-01 04:10:10 | [diff] [blame] | 262 | cfg_.rc_undershoot_pct = 50; |
| 263 | cfg_.rc_overshoot_pct = 50; |
| 264 | cfg_.rc_buf_initial_sz = 600; |
| 265 | cfg_.rc_buf_optimal_sz = 600; |
| 266 | cfg_.rc_buf_sz = 1000; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 267 | cfg_.g_usage = kUsageProfile; |
Jerome Jiang | 7a9b96f | 2020-07-20 21:31:34 | [diff] [blame] | 268 | cfg_.g_error_resilient = 0; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 269 | // Low-latency settings. |
| 270 | cfg_.rc_end_usage = AOM_CBR; // Constant Bit Rate (CBR) mode |
| 271 | cfg_.g_pass = AOM_RC_ONE_PASS; // One-pass rate control |
| 272 | cfg_.g_lag_in_frames = kLagInFrames; // No look ahead when lag equals 0. |
| 273 | |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 274 | if (frame_for_encode_ != nullptr) { |
| 275 | aom_img_free(frame_for_encode_); |
| 276 | frame_for_encode_ = nullptr; |
| 277 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 278 | |
| 279 | // Flag options: AOM_CODEC_USE_PSNR and AOM_CODEC_USE_HIGHBITDEPTH |
| 280 | aom_codec_flags_t flags = 0; |
| 281 | |
| 282 | // Initialize an encoder instance. |
| 283 | ret = aom_codec_enc_init(&ctx_, aom_codec_av1_cx(), &cfg_, flags); |
| 284 | if (ret != AOM_CODEC_OK) { |
| 285 | RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret |
| 286 | << " on aom_codec_enc_init."; |
| 287 | return WEBRTC_VIDEO_CODEC_ERROR; |
| 288 | } |
| 289 | inited_ = true; |
| 290 | |
| 291 | // Set control parameters |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 292 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AOME_SET_CPUUSED, |
| 293 | GetCpuSpeed(cfg_.g_w, cfg_.g_h)); |
| 294 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_CDEF, 1); |
| 295 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_TPL_MODEL, 0); |
| 296 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_DELTAQ_MODE, 0); |
| 297 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_ORDER_HINT, 0); |
| 298 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_AQ_MODE, 3); |
| 299 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AOME_SET_MAX_INTRA_BITRATE_PCT, 300); |
| 300 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_COEFF_COST_UPD_FREQ, 3); |
| 301 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_MODE_COST_UPD_FREQ, 3); |
| 302 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_MV_COST_UPD_FREQ, 3); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 303 | |
Jerome Jiang | a78c949 | 2022-04-04 18:24:53 | [diff] [blame] | 304 | if (codec_settings->mode == VideoCodecMode::kScreensharing) { |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 305 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TUNE_CONTENT, |
| 306 | AOM_CONTENT_SCREEN); |
| 307 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_PALETTE, 1); |
| 308 | } else { |
| 309 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_PALETTE, 0); |
Jerome Jiang | a78c949 | 2022-04-04 18:24:53 | [diff] [blame] | 310 | } |
| 311 | |
Dan Tan | 4860148 | 2024-01-31 17:11:18 | [diff] [blame] | 312 | if (codec_settings->mode == VideoCodecMode::kRealtimeVideo && |
| 313 | encoder_settings_.GetFrameDropEnabled() && max_consec_frame_drop_ > 0) { |
| 314 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_MAX_CONSEC_FRAME_DROP_CBR, |
| 315 | max_consec_frame_drop_); |
| 316 | } |
| 317 | |
Jerome Jiang | 3403acb | 2023-06-07 14:17:41 | [diff] [blame] | 318 | if (cfg_.g_threads == 8) { |
| 319 | // Values passed to AV1E_SET_TILE_ROWS and AV1E_SET_TILE_COLUMNS are log2() |
| 320 | // based. |
| 321 | // Use 4 tile columns x 2 tile rows for 8 threads. |
| 322 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TILE_ROWS, 1); |
| 323 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TILE_COLUMNS, 2); |
| 324 | } else if (cfg_.g_threads == 4) { |
| 325 | // Use 2 tile columns x 2 tile rows for 4 threads. |
| 326 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TILE_ROWS, 1); |
| 327 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TILE_COLUMNS, 1); |
Jerome Jiang | 90ea0a6 | 2021-02-22 17:37:37 | [diff] [blame] | 328 | } else { |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 329 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TILE_COLUMNS, |
| 330 | static_cast<int>(log2(cfg_.g_threads))); |
Jerome Jiang | 39d1f74 | 2020-12-17 01:54:33 | [diff] [blame] | 331 | } |
| 332 | |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 333 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ROW_MT, 1); |
| 334 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_OBMC, 0); |
| 335 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_NOISE_SENSITIVITY, 0); |
| 336 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_WARPED_MOTION, 0); |
| 337 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_GLOBAL_MOTION, 0); |
| 338 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_REF_FRAME_MVS, 0); |
| 339 | SET_ENCODER_PARAM_OR_RETURN_ERROR( |
| 340 | AV1E_SET_SUPERBLOCK_SIZE, |
| 341 | GetSuperblockSize(cfg_.g_w, cfg_.g_h, cfg_.g_threads)); |
| 342 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_CFL_INTRA, 0); |
| 343 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_SMOOTH_INTRA, 0); |
| 344 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_ANGLE_DELTA, 0); |
| 345 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_FILTER_INTRA, 0); |
| 346 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1); |
| 347 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_DISABLE_TRELLIS_QUANT, 1); |
| 348 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_DIST_WTD_COMP, 0); |
| 349 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_DIFF_WTD_COMP, 0); |
| 350 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_DUAL_FILTER, 0); |
| 351 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_INTERINTRA_COMP, 0); |
| 352 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_INTERINTRA_WEDGE, 0); |
| 353 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_INTRA_EDGE_FILTER, 0); |
| 354 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_INTRABC, 0); |
| 355 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_MASKED_COMP, 0); |
| 356 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_PAETH_INTRA, 0); |
| 357 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_QM, 0); |
| 358 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_RECT_PARTITIONS, 0); |
| 359 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_RESTORATION, 0); |
| 360 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0); |
| 361 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_TX64, 0); |
| 362 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_MAX_REFERENCE_FRAMES, 3); |
Fyodor Kyslov | 26abdaf | 2021-03-22 20:53:57 | [diff] [blame] | 363 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 364 | return WEBRTC_VIDEO_CODEC_OK; |
| 365 | } |
| 366 | |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 367 | template <typename P> |
| 368 | bool LibaomAv1Encoder::SetEncoderControlParameters(int param_id, |
| 369 | P param_value) { |
| 370 | aom_codec_err_t error_code = aom_codec_control(&ctx_, param_id, param_value); |
| 371 | if (error_code != AOM_CODEC_OK) { |
| 372 | RTC_LOG(LS_WARNING) |
| 373 | << "LibaomAv1Encoder::SetEncoderControlParameters returned " |
| 374 | << error_code << " on id: " << param_id << "."; |
| 375 | } |
| 376 | return error_code == AOM_CODEC_OK; |
| 377 | } |
| 378 | |
Johannes Kron | 17e3580 | 2024-03-07 15:41:34 | [diff] [blame] | 379 | // Only positive speeds, range for real-time coding currently is: 6 - 10. |
| 380 | // Speed 11 is used for screen sharing. |
“Michael | 15ee87f | 2022-03-03 18:09:02 | [diff] [blame] | 381 | // Lower means slower/better quality, higher means fastest/lower quality. |
| 382 | int LibaomAv1Encoder::GetCpuSpeed(int width, int height) { |
philipel | 30ec725 | 2022-05-04 09:20:04 | [diff] [blame] | 383 | if (aux_config_) { |
| 384 | if (auto it = aux_config_->max_pixel_count_to_cpu_speed.lower_bound(width * |
| 385 | height); |
| 386 | it != aux_config_->max_pixel_count_to_cpu_speed.end()) { |
| 387 | return it->second; |
| 388 | } |
| 389 | |
| 390 | return 10; |
| 391 | } else { |
Johannes Kron | 17e3580 | 2024-03-07 15:41:34 | [diff] [blame] | 392 | if (encoder_settings_.mode == VideoCodecMode::kScreensharing) { |
| 393 | return 11; |
| 394 | } |
philipel | 30ec725 | 2022-05-04 09:20:04 | [diff] [blame] | 395 | // For smaller resolutions, use lower speed setting (get some coding gain at |
| 396 | // the cost of increased encoding complexity). |
| 397 | switch (encoder_settings_.GetVideoEncoderComplexity()) { |
| 398 | case VideoCodecComplexity::kComplexityHigh: |
| 399 | if (width * height <= 320 * 180) |
| 400 | return 8; |
| 401 | else if (width * height <= 640 * 360) |
| 402 | return 9; |
| 403 | else |
| 404 | return 10; |
| 405 | case VideoCodecComplexity::kComplexityHigher: |
| 406 | if (width * height <= 320 * 180) |
| 407 | return 7; |
| 408 | else if (width * height <= 640 * 360) |
| 409 | return 8; |
| 410 | else if (width * height <= 1280 * 720) |
| 411 | return 9; |
| 412 | else |
| 413 | return 10; |
| 414 | case VideoCodecComplexity::kComplexityMax: |
| 415 | if (width * height <= 320 * 180) |
| 416 | return 6; |
| 417 | else if (width * height <= 640 * 360) |
| 418 | return 7; |
| 419 | else if (width * height <= 1280 * 720) |
| 420 | return 8; |
| 421 | else |
| 422 | return 9; |
| 423 | default: |
“Michael | 15ee87f | 2022-03-03 18:09:02 | [diff] [blame] | 424 | return 10; |
philipel | 30ec725 | 2022-05-04 09:20:04 | [diff] [blame] | 425 | } |
“Michael | 15ee87f | 2022-03-03 18:09:02 | [diff] [blame] | 426 | } |
| 427 | } |
| 428 | |
Jerome Jiang | 39d1f74 | 2020-12-17 01:54:33 | [diff] [blame] | 429 | int LibaomAv1Encoder::NumberOfThreads(int width, |
| 430 | int height, |
| 431 | int number_of_cores) { |
Jerome Jiang | 90ea0a6 | 2021-02-22 17:37:37 | [diff] [blame] | 432 | // Keep the number of encoder threads equal to the possible number of |
| 433 | // column/row tiles, which is (1, 2, 4, 8). See comments below for |
| 434 | // AV1E_SET_TILE_COLUMNS/ROWS. |
Jerome Jiang | 3403acb | 2023-06-07 14:17:41 | [diff] [blame] | 435 | if (width * height > 1280 * 720 && number_of_cores > 8) { |
| 436 | return 8; |
| 437 | } else if (width * height >= 640 * 360 && number_of_cores > 4) { |
Jerome Jiang | 39d1f74 | 2020-12-17 01:54:33 | [diff] [blame] | 438 | return 4; |
Jerome Jiang | 90ea0a6 | 2021-02-22 17:37:37 | [diff] [blame] | 439 | } else if (width * height >= 320 * 180 && number_of_cores > 2) { |
Jerome Jiang | 39d1f74 | 2020-12-17 01:54:33 | [diff] [blame] | 440 | return 2; |
| 441 | } else { |
| 442 | // Use 2 threads for low res on ARM. |
Sergey Silkin | a4b2b95 | 2023-10-06 11:18:41 | [diff] [blame] | 443 | #ifdef MOBILE_ARM |
Jerome Jiang | 39d1f74 | 2020-12-17 01:54:33 | [diff] [blame] | 444 | if (width * height >= 320 * 180 && number_of_cores > 2) { |
| 445 | return 2; |
| 446 | } |
| 447 | #endif |
| 448 | // 1 thread less than VGA. |
| 449 | return 1; |
| 450 | } |
| 451 | } |
| 452 | |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 453 | bool LibaomAv1Encoder::SetSvcParams( |
| 454 | ScalableVideoController::StreamLayersConfig svc_config) { |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 455 | bool svc_enabled = |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 456 | svc_config.num_spatial_layers > 1 || svc_config.num_temporal_layers > 1; |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 457 | if (!svc_enabled) { |
| 458 | svc_params_ = absl::nullopt; |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 459 | return true; |
| 460 | } |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 461 | if (svc_config.num_spatial_layers < 1 || svc_config.num_spatial_layers > 4) { |
| 462 | RTC_LOG(LS_WARNING) << "Av1 supports up to 4 spatial layers. " |
| 463 | << svc_config.num_spatial_layers << " configured."; |
| 464 | return false; |
| 465 | } |
| 466 | if (svc_config.num_temporal_layers < 1 || |
| 467 | svc_config.num_temporal_layers > 8) { |
| 468 | RTC_LOG(LS_WARNING) << "Av1 supports up to 8 temporal layers. " |
| 469 | << svc_config.num_temporal_layers << " configured."; |
| 470 | return false; |
| 471 | } |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 472 | aom_svc_params_t& svc_params = svc_params_.emplace(); |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 473 | svc_params.number_spatial_layers = svc_config.num_spatial_layers; |
| 474 | svc_params.number_temporal_layers = svc_config.num_temporal_layers; |
| 475 | |
| 476 | int num_layers = |
| 477 | svc_config.num_spatial_layers * svc_config.num_temporal_layers; |
| 478 | for (int i = 0; i < num_layers; ++i) { |
| 479 | svc_params.min_quantizers[i] = kQpMin; |
Jerome Jiang | 7f7fb83 | 2020-06-04 03:44:50 | [diff] [blame] | 480 | svc_params.max_quantizers[i] = encoder_settings_.qpMax; |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 481 | } |
| 482 | |
| 483 | // Assume each temporal layer doubles framerate. |
| 484 | for (int tid = 0; tid < svc_config.num_temporal_layers; ++tid) { |
| 485 | svc_params.framerate_factor[tid] = |
| 486 | 1 << (svc_config.num_temporal_layers - tid - 1); |
| 487 | } |
| 488 | |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 489 | for (int sid = 0; sid < svc_config.num_spatial_layers; ++sid) { |
Danil Chapovalov | 1e10a61 | 2020-07-02 13:27:03 | [diff] [blame] | 490 | svc_params.scaling_factor_num[sid] = svc_config.scaling_factor_num[sid]; |
| 491 | svc_params.scaling_factor_den[sid] = svc_config.scaling_factor_den[sid]; |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 492 | } |
| 493 | |
Wan-Teh Chang | 8f29b42 | 2023-03-16 01:39:57 | [diff] [blame] | 494 | // svc_params.layer_target_bitrate is set in SetRates() before svc_params is |
Wan-Teh Chang | ad192a8 | 2023-03-16 23:15:17 | [diff] [blame] | 495 | // passed to SetEncoderControlParameters(AV1E_SET_SVC_PARAMS). |
Wan-Teh Chang | 8f29b42 | 2023-03-16 01:39:57 | [diff] [blame] | 496 | |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 497 | return true; |
| 498 | } |
| 499 | |
| 500 | void LibaomAv1Encoder::SetSvcLayerId( |
| 501 | const ScalableVideoController::LayerFrameConfig& layer_frame) { |
| 502 | aom_svc_layer_id_t layer_id = {}; |
Danil Chapovalov | a48dd43 | 2020-06-08 17:47:08 | [diff] [blame] | 503 | layer_id.spatial_layer_id = layer_frame.SpatialId(); |
| 504 | layer_id.temporal_layer_id = layer_frame.TemporalId(); |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 505 | SetEncoderControlParameters(AV1E_SET_SVC_LAYER_ID, &layer_id); |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 506 | } |
| 507 | |
| 508 | void LibaomAv1Encoder::SetSvcRefFrameConfig( |
| 509 | const ScalableVideoController::LayerFrameConfig& layer_frame) { |
| 510 | // Buffer name to use for each layer_frame.buffers position. In particular |
| 511 | // when there are 2 buffers are referenced, prefer name them last and golden, |
| 512 | // because av1 bitstream format has dedicated fields for these two names. |
| 513 | // See last_frame_idx and golden_frame_idx in the av1 spec |
| 514 | // https://aomediacodec.github.io/av1-spec/av1-spec.pdf |
| 515 | static constexpr int kPreferedSlotName[] = {0, // Last |
| 516 | 3, // Golden |
| 517 | 1, 2, 4, 5, 6}; |
| 518 | static constexpr int kAv1NumBuffers = 8; |
| 519 | |
| 520 | aom_svc_ref_frame_config_t ref_frame_config = {}; |
Danil Chapovalov | a48dd43 | 2020-06-08 17:47:08 | [diff] [blame] | 521 | RTC_CHECK_LE(layer_frame.Buffers().size(), ABSL_ARRAYSIZE(kPreferedSlotName)); |
| 522 | for (size_t i = 0; i < layer_frame.Buffers().size(); ++i) { |
| 523 | const CodecBufferUsage& buffer = layer_frame.Buffers()[i]; |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 524 | int slot_name = kPreferedSlotName[i]; |
| 525 | RTC_CHECK_GE(buffer.id, 0); |
| 526 | RTC_CHECK_LT(buffer.id, kAv1NumBuffers); |
| 527 | ref_frame_config.ref_idx[slot_name] = buffer.id; |
| 528 | if (buffer.referenced) { |
| 529 | ref_frame_config.reference[slot_name] = 1; |
| 530 | } |
| 531 | if (buffer.updated) { |
| 532 | ref_frame_config.refresh[buffer.id] = 1; |
| 533 | } |
| 534 | } |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 535 | |
| 536 | SetEncoderControlParameters(AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config); |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 537 | } |
| 538 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 539 | int32_t LibaomAv1Encoder::RegisterEncodeCompleteCallback( |
| 540 | EncodedImageCallback* encoded_image_callback) { |
| 541 | encoded_image_callback_ = encoded_image_callback; |
| 542 | return WEBRTC_VIDEO_CODEC_OK; |
| 543 | } |
| 544 | |
| 545 | int32_t LibaomAv1Encoder::Release() { |
| 546 | if (frame_for_encode_ != nullptr) { |
| 547 | aom_img_free(frame_for_encode_); |
| 548 | frame_for_encode_ = nullptr; |
| 549 | } |
| 550 | if (inited_) { |
| 551 | if (aom_codec_destroy(&ctx_)) { |
| 552 | return WEBRTC_VIDEO_CODEC_MEMORY; |
| 553 | } |
| 554 | inited_ = false; |
| 555 | } |
philipel | 0763ccc | 2021-12-01 14:12:54 | [diff] [blame] | 556 | rates_configured_ = false; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 557 | return WEBRTC_VIDEO_CODEC_OK; |
| 558 | } |
| 559 | |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 560 | void LibaomAv1Encoder::MaybeRewrapImgWithFormat(const aom_img_fmt_t fmt) { |
| 561 | if (!frame_for_encode_) { |
| 562 | frame_for_encode_ = |
| 563 | aom_img_wrap(nullptr, fmt, cfg_.g_w, cfg_.g_h, 1, nullptr); |
| 564 | |
| 565 | } else if (frame_for_encode_->fmt != fmt) { |
| 566 | RTC_LOG(LS_INFO) << "Switching AV1 encoder pixel format to " |
| 567 | << (fmt == AOM_IMG_FMT_NV12 ? "NV12" : "I420"); |
| 568 | aom_img_free(frame_for_encode_); |
| 569 | frame_for_encode_ = |
| 570 | aom_img_wrap(nullptr, fmt, cfg_.g_w, cfg_.g_h, 1, nullptr); |
| 571 | } |
| 572 | // else no-op since the image is already in the right format. |
| 573 | } |
| 574 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 575 | int32_t LibaomAv1Encoder::Encode( |
| 576 | const VideoFrame& frame, |
| 577 | const std::vector<VideoFrameType>* frame_types) { |
philipel | 0763ccc | 2021-12-01 14:12:54 | [diff] [blame] | 578 | if (!inited_ || encoded_image_callback_ == nullptr || !rates_configured_) { |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 579 | return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| 580 | } |
| 581 | |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 582 | bool keyframe_required = |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 583 | frame_types != nullptr && |
| 584 | absl::c_linear_search(*frame_types, VideoFrameType::kVideoFrameKey); |
| 585 | |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 586 | std::vector<ScalableVideoController::LayerFrameConfig> layer_frames = |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 587 | svc_controller_->NextFrameConfig(keyframe_required); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 588 | |
| 589 | if (layer_frames.empty()) { |
| 590 | RTC_LOG(LS_ERROR) << "SVCController returned no configuration for a frame."; |
| 591 | return WEBRTC_VIDEO_CODEC_ERROR; |
| 592 | } |
| 593 | |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 594 | rtc::scoped_refptr<VideoFrameBuffer> buffer = frame.video_frame_buffer(); |
| 595 | absl::InlinedVector<VideoFrameBuffer::Type, kMaxPreferredPixelFormats> |
| 596 | supported_formats = {VideoFrameBuffer::Type::kI420, |
| 597 | VideoFrameBuffer::Type::kNV12}; |
| 598 | rtc::scoped_refptr<VideoFrameBuffer> mapped_buffer; |
| 599 | if (buffer->type() != VideoFrameBuffer::Type::kNative) { |
| 600 | // `buffer` is already mapped. |
| 601 | mapped_buffer = buffer; |
| 602 | } else { |
| 603 | // Attempt to map to one of the supported formats. |
| 604 | mapped_buffer = buffer->GetMappedFrameBuffer(supported_formats); |
| 605 | } |
| 606 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 607 | // Convert input frame to I420, if needed. |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 608 | if (!mapped_buffer || |
| 609 | (absl::c_find(supported_formats, mapped_buffer->type()) == |
| 610 | supported_formats.end() && |
| 611 | mapped_buffer->type() != VideoFrameBuffer::Type::kI420A)) { |
philipel | ab68a91 | 2022-03-30 11:55:49 | [diff] [blame] | 612 | rtc::scoped_refptr<I420BufferInterface> converted_buffer(buffer->ToI420()); |
Evan Shrubsole | f906ec4 | 2021-06-21 11:47:44 | [diff] [blame] | 613 | if (!converted_buffer) { |
| 614 | RTC_LOG(LS_ERROR) << "Failed to convert " |
| 615 | << VideoFrameBufferTypeToString( |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 616 | frame.video_frame_buffer()->type()) |
Evan Shrubsole | f906ec4 | 2021-06-21 11:47:44 | [diff] [blame] | 617 | << " image to I420. Can't encode frame."; |
| 618 | return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; |
| 619 | } |
Byoungchan Lee | 75ac5ab | 2021-07-27 12:27:46 | [diff] [blame] | 620 | RTC_CHECK(converted_buffer->type() == VideoFrameBuffer::Type::kI420 || |
| 621 | converted_buffer->type() == VideoFrameBuffer::Type::kI420A); |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 622 | |
| 623 | mapped_buffer = converted_buffer; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 624 | } |
| 625 | |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 626 | switch (mapped_buffer->type()) { |
| 627 | case VideoFrameBuffer::Type::kI420: |
| 628 | case VideoFrameBuffer::Type::kI420A: { |
| 629 | // Set frame_for_encode_ data pointers and strides. |
| 630 | MaybeRewrapImgWithFormat(AOM_IMG_FMT_I420); |
| 631 | auto i420_buffer = mapped_buffer->GetI420(); |
| 632 | RTC_DCHECK(i420_buffer); |
| 633 | frame_for_encode_->planes[AOM_PLANE_Y] = |
| 634 | const_cast<unsigned char*>(i420_buffer->DataY()); |
| 635 | frame_for_encode_->planes[AOM_PLANE_U] = |
| 636 | const_cast<unsigned char*>(i420_buffer->DataU()); |
| 637 | frame_for_encode_->planes[AOM_PLANE_V] = |
| 638 | const_cast<unsigned char*>(i420_buffer->DataV()); |
| 639 | frame_for_encode_->stride[AOM_PLANE_Y] = i420_buffer->StrideY(); |
| 640 | frame_for_encode_->stride[AOM_PLANE_U] = i420_buffer->StrideU(); |
| 641 | frame_for_encode_->stride[AOM_PLANE_V] = i420_buffer->StrideV(); |
| 642 | break; |
| 643 | } |
| 644 | case VideoFrameBuffer::Type::kNV12: { |
| 645 | MaybeRewrapImgWithFormat(AOM_IMG_FMT_NV12); |
| 646 | const NV12BufferInterface* nv12_buffer = mapped_buffer->GetNV12(); |
| 647 | RTC_DCHECK(nv12_buffer); |
| 648 | frame_for_encode_->planes[AOM_PLANE_Y] = |
| 649 | const_cast<unsigned char*>(nv12_buffer->DataY()); |
| 650 | frame_for_encode_->planes[AOM_PLANE_U] = |
| 651 | const_cast<unsigned char*>(nv12_buffer->DataUV()); |
| 652 | frame_for_encode_->planes[AOM_PLANE_V] = nullptr; |
| 653 | frame_for_encode_->stride[AOM_PLANE_Y] = nv12_buffer->StrideY(); |
| 654 | frame_for_encode_->stride[AOM_PLANE_U] = nv12_buffer->StrideUV(); |
| 655 | frame_for_encode_->stride[AOM_PLANE_V] = 0; |
| 656 | break; |
| 657 | } |
| 658 | default: |
| 659 | return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; |
| 660 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 661 | |
| 662 | const uint32_t duration = |
| 663 | kRtpTicksPerSecond / static_cast<float>(encoder_settings_.maxFramerate); |
Erik SprĂĄng | ff1cf61 | 2023-02-23 17:41:13 | [diff] [blame] | 664 | timestamp_ += duration; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 665 | |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 666 | const size_t num_spatial_layers = |
| 667 | svc_params_ ? svc_params_->number_spatial_layers : 1; |
philipel | d3eb8f1 | 2021-11-01 10:58:50 | [diff] [blame] | 668 | auto next_layer_frame = layer_frames.begin(); |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 669 | for (size_t i = 0; i < num_spatial_layers; ++i) { |
| 670 | // The libaom AV1 encoder requires that `aom_codec_encode` is called for |
| 671 | // every spatial layer, even if the configured bitrate for that layer is |
| 672 | // zero. For zero bitrate spatial layers no frames will be produced. |
| 673 | absl::optional<ScalableVideoController::LayerFrameConfig> |
| 674 | non_encoded_layer_frame; |
| 675 | ScalableVideoController::LayerFrameConfig* layer_frame; |
philipel | d3eb8f1 | 2021-11-01 10:58:50 | [diff] [blame] | 676 | if (next_layer_frame != layer_frames.end() && |
| 677 | next_layer_frame->SpatialId() == static_cast<int>(i)) { |
| 678 | layer_frame = &*next_layer_frame; |
| 679 | ++next_layer_frame; |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 680 | } else { |
| 681 | // For layers that are not encoded only the spatial id matters. |
| 682 | non_encoded_layer_frame.emplace().S(i); |
| 683 | layer_frame = &*non_encoded_layer_frame; |
| 684 | } |
philipel | d3eb8f1 | 2021-11-01 10:58:50 | [diff] [blame] | 685 | const bool end_of_picture = (next_layer_frame == layer_frames.end()); |
Danil Chapovalov | 06bbeb3 | 2020-11-11 11:42:56 | [diff] [blame] | 686 | |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 687 | aom_enc_frame_flags_t flags = |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 688 | layer_frame->IsKeyframe() ? AOM_EFLAG_FORCE_KF : 0; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 689 | |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 690 | if (SvcEnabled()) { |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 691 | SetSvcLayerId(*layer_frame); |
| 692 | SetSvcRefFrameConfig(*layer_frame); |
Danil Chapovalov | a4d70a8 | 2020-05-28 06:47:37 | [diff] [blame] | 693 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 694 | |
Erik SprĂĄng | ff1cf61 | 2023-02-23 17:41:13 | [diff] [blame] | 695 | // Encode a frame. The presentation timestamp `pts` should not use real |
| 696 | // timestamps from frames or the wall clock, as that can cause the rate |
| 697 | // controller to misbehave. |
| 698 | aom_codec_err_t ret = |
| 699 | aom_codec_encode(&ctx_, frame_for_encode_, timestamp_, duration, flags); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 700 | if (ret != AOM_CODEC_OK) { |
| 701 | RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encode returned " << ret |
| 702 | << " on aom_codec_encode."; |
| 703 | return WEBRTC_VIDEO_CODEC_ERROR; |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 704 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 705 | |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 706 | if (non_encoded_layer_frame) { |
| 707 | continue; |
| 708 | } |
| 709 | |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 710 | // Get encoded image data. |
| 711 | EncodedImage encoded_image; |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 712 | aom_codec_iter_t iter = nullptr; |
| 713 | int data_pkt_count = 0; |
| 714 | while (const aom_codec_cx_pkt_t* pkt = |
| 715 | aom_codec_get_cx_data(&ctx_, &iter)) { |
| 716 | if (pkt->kind == AOM_CODEC_CX_FRAME_PKT && pkt->data.frame.sz > 0) { |
| 717 | if (data_pkt_count > 0) { |
| 718 | RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encoder returned more than " |
| 719 | "one data packet for an input video frame."; |
| 720 | Release(); |
| 721 | } |
Danil Chapovalov | 222fdfd | 2020-06-11 17:44:24 | [diff] [blame] | 722 | encoded_image.SetEncodedData(EncodedImageBuffer::Create( |
| 723 | /*data=*/static_cast<const uint8_t*>(pkt->data.frame.buf), |
| 724 | /*size=*/pkt->data.frame.sz)); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 725 | |
Danil Chapovalov | a48dd43 | 2020-06-08 17:47:08 | [diff] [blame] | 726 | if ((pkt->data.frame.flags & AOM_EFLAG_FORCE_KF) != 0) { |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 727 | layer_frame->Keyframe(); |
Danil Chapovalov | a48dd43 | 2020-06-08 17:47:08 | [diff] [blame] | 728 | } |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 729 | |
| 730 | encoded_image._frameType = layer_frame->IsKeyframe() |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 731 | ? VideoFrameType::kVideoFrameKey |
| 732 | : VideoFrameType::kVideoFrameDelta; |
Danil Chapovalov | 9c58483 | 2023-09-18 13:48:49 | [diff] [blame] | 733 | encoded_image.SetRtpTimestamp(frame.timestamp()); |
Palak Agarwal | a09f21b | 2023-02-22 13:46:23 | [diff] [blame] | 734 | encoded_image.SetCaptureTimeIdentifier(frame.capture_time_identifier()); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 735 | encoded_image.capture_time_ms_ = frame.render_time_ms(); |
| 736 | encoded_image.rotation_ = frame.rotation(); |
| 737 | encoded_image.content_type_ = VideoContentType::UNSPECIFIED; |
| 738 | // If encoded image width/height info are added to aom_codec_cx_pkt_t, |
| 739 | // use those values in lieu of the values in frame. |
Erik SprĂĄng | f8654448 | 2021-06-01 14:52:24 | [diff] [blame] | 740 | if (svc_params_) { |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 741 | int n = svc_params_->scaling_factor_num[layer_frame->SpatialId()]; |
| 742 | int d = svc_params_->scaling_factor_den[layer_frame->SpatialId()]; |
Erik SprĂĄng | f8654448 | 2021-06-01 14:52:24 | [diff] [blame] | 743 | encoded_image._encodedWidth = cfg_.g_w * n / d; |
| 744 | encoded_image._encodedHeight = cfg_.g_h * n / d; |
Zhaoliang Ma | 528e489 | 2021-11-12 05:05:07 | [diff] [blame] | 745 | encoded_image.SetSpatialIndex(layer_frame->SpatialId()); |
Florent Castelli | 90b7438 | 2022-04-25 15:28:00 | [diff] [blame] | 746 | encoded_image.SetTemporalIndex(layer_frame->TemporalId()); |
Erik SprĂĄng | f8654448 | 2021-06-01 14:52:24 | [diff] [blame] | 747 | } else { |
| 748 | encoded_image._encodedWidth = cfg_.g_w; |
| 749 | encoded_image._encodedHeight = cfg_.g_h; |
| 750 | } |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 751 | encoded_image.timing_.flags = VideoSendTiming::kInvalid; |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 752 | |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 753 | int qp = -1; |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 754 | SET_ENCODER_PARAM_OR_RETURN_ERROR(AOME_GET_LAST_QUANTIZER, &qp); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 755 | encoded_image.qp_ = qp; |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 756 | |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 757 | encoded_image.SetColorSpace(frame.color_space()); |
| 758 | ++data_pkt_count; |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | // Deliver encoded image data. |
| 763 | if (encoded_image.size() > 0) { |
| 764 | CodecSpecificInfo codec_specific_info; |
| 765 | codec_specific_info.codecType = kVideoCodecAV1; |
Danil Chapovalov | 06bbeb3 | 2020-11-11 11:42:56 | [diff] [blame] | 766 | codec_specific_info.end_of_picture = end_of_picture; |
Evan Shrubsole | 9b235cd | 2022-12-06 10:09:10 | [diff] [blame] | 767 | codec_specific_info.scalability_mode = scalability_mode_; |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 768 | bool is_keyframe = layer_frame->IsKeyframe(); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 769 | codec_specific_info.generic_frame_info = |
philipel | d3eb8f1 | 2021-11-01 10:58:50 | [diff] [blame] | 770 | svc_controller_->OnEncodeDone(*layer_frame); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 771 | if (is_keyframe && codec_specific_info.generic_frame_info) { |
| 772 | codec_specific_info.template_structure = |
| 773 | svc_controller_->DependencyStructure(); |
Danil Chapovalov | 1e10a61 | 2020-07-02 13:27:03 | [diff] [blame] | 774 | auto& resolutions = codec_specific_info.template_structure->resolutions; |
| 775 | if (SvcEnabled()) { |
| 776 | resolutions.resize(svc_params_->number_spatial_layers); |
| 777 | for (int sid = 0; sid < svc_params_->number_spatial_layers; ++sid) { |
| 778 | int n = svc_params_->scaling_factor_num[sid]; |
| 779 | int d = svc_params_->scaling_factor_den[sid]; |
| 780 | resolutions[sid] = |
| 781 | RenderResolution(cfg_.g_w * n / d, cfg_.g_h * n / d); |
| 782 | } |
| 783 | } else { |
| 784 | resolutions = {RenderResolution(cfg_.g_w, cfg_.g_h)}; |
| 785 | } |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 786 | } |
| 787 | encoded_image_callback_->OnEncodedImage(encoded_image, |
Danil Chapovalov | 2549f17 | 2020-08-12 15:30:36 | [diff] [blame] | 788 | &codec_specific_info); |
Danil Chapovalov | b471ac79 | 2020-05-15 12:21:03 | [diff] [blame] | 789 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 790 | } |
| 791 | |
| 792 | return WEBRTC_VIDEO_CODEC_OK; |
| 793 | } |
| 794 | |
| 795 | void LibaomAv1Encoder::SetRates(const RateControlParameters& parameters) { |
| 796 | if (!inited_) { |
| 797 | RTC_LOG(LS_WARNING) << "SetRates() while encoder is not initialized"; |
| 798 | return; |
| 799 | } |
| 800 | if (parameters.framerate_fps < kMinimumFrameRate) { |
| 801 | RTC_LOG(LS_WARNING) << "Unsupported framerate (must be >= " |
| 802 | << kMinimumFrameRate |
| 803 | << " ): " << parameters.framerate_fps; |
| 804 | return; |
| 805 | } |
| 806 | if (parameters.bitrate.get_sum_bps() == 0) { |
| 807 | RTC_LOG(LS_WARNING) << "Attempt to set target bit rate to zero"; |
| 808 | return; |
| 809 | } |
| 810 | |
philipel | d3eb8f1 | 2021-11-01 10:58:50 | [diff] [blame] | 811 | // The bitrates caluclated internally in libaom when `AV1E_SET_SVC_PARAMS` is |
| 812 | // called depends on the currently configured `rc_target_bitrate`. If the |
| 813 | // total target bitrate is not updated first a division by zero could happen. |
Danil Chapovalov | bf18161 | 2020-06-25 13:47:57 | [diff] [blame] | 814 | svc_controller_->OnRatesUpdated(parameters.bitrate); |
philipel | b315951 | 2021-03-15 13:42:40 | [diff] [blame] | 815 | cfg_.rc_target_bitrate = parameters.bitrate.get_sum_kbps(); |
philipel | 448231d | 2021-10-27 09:29:45 | [diff] [blame] | 816 | aom_codec_err_t error_code = aom_codec_enc_config_set(&ctx_, &cfg_); |
philipel | d3eb8f1 | 2021-11-01 10:58:50 | [diff] [blame] | 817 | if (error_code != AOM_CODEC_OK) { |
| 818 | RTC_LOG(LS_WARNING) << "Error configuring encoder, error code: " |
| 819 | << error_code; |
| 820 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 821 | |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 822 | if (SvcEnabled()) { |
| 823 | for (int sid = 0; sid < svc_params_->number_spatial_layers; ++sid) { |
| 824 | // libaom bitrate for spatial id S and temporal id T means bitrate |
| 825 | // of frames with spatial_id=S and temporal_id<=T |
| 826 | // while `parameters.bitrate` provdies bitrate of frames with |
| 827 | // spatial_id=S and temporal_id=T |
| 828 | int accumulated_bitrate_bps = 0; |
| 829 | for (int tid = 0; tid < svc_params_->number_temporal_layers; ++tid) { |
| 830 | int layer_index = sid * svc_params_->number_temporal_layers + tid; |
| 831 | accumulated_bitrate_bps += parameters.bitrate.GetBitrate(sid, tid); |
Wan-Teh Chang | 8f29b42 | 2023-03-16 01:39:57 | [diff] [blame] | 832 | // `svc_params_->layer_target_bitrate` expects bitrate in kbps. |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 833 | svc_params_->layer_target_bitrate[layer_index] = |
| 834 | accumulated_bitrate_bps / 1000; |
| 835 | } |
| 836 | } |
philipel | 8c35488 | 2022-05-02 08:27:29 | [diff] [blame] | 837 | SetEncoderControlParameters(AV1E_SET_SVC_PARAMS, &*svc_params_); |
Danil Chapovalov | 54544ec | 2020-06-25 19:44:31 | [diff] [blame] | 838 | } |
| 839 | |
philipel | 0763ccc | 2021-12-01 14:12:54 | [diff] [blame] | 840 | rates_configured_ = true; |
| 841 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 842 | // Set frame rate to closest integer value. |
| 843 | encoder_settings_.maxFramerate = |
| 844 | static_cast<uint32_t>(parameters.framerate_fps + 0.5); |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 845 | } |
| 846 | |
| 847 | VideoEncoder::EncoderInfo LibaomAv1Encoder::GetEncoderInfo() const { |
| 848 | EncoderInfo info; |
| 849 | info.supports_native_handle = false; |
| 850 | info.implementation_name = "libaom"; |
| 851 | info.has_trusted_rate_controller = true; |
| 852 | info.is_hardware_accelerated = false; |
philipel | 31718d7 | 2023-09-13 12:44:23 | [diff] [blame] | 853 | info.scaling_settings = |
| 854 | (inited_ && !encoder_settings_.AV1().automatic_resize_on) |
| 855 | ? VideoEncoder::ScalingSettings::kOff |
| 856 | : VideoEncoder::ScalingSettings(kMinQindex, kMaxQindex); |
Ilya Nikolaevskiy | 1bcdafc | 2022-03-09 15:01:07 | [diff] [blame] | 857 | info.preferred_pixel_formats = {VideoFrameBuffer::Type::kI420, |
| 858 | VideoFrameBuffer::Type::kNV12}; |
Danil Chapovalov | b942d45 | 2021-01-12 13:45:07 | [diff] [blame] | 859 | if (SvcEnabled()) { |
| 860 | for (int sid = 0; sid < svc_params_->number_spatial_layers; ++sid) { |
| 861 | info.fps_allocation[sid].resize(svc_params_->number_temporal_layers); |
| 862 | for (int tid = 0; tid < svc_params_->number_temporal_layers; ++tid) { |
Wan-Teh Chang | 1f39162 | 2023-02-09 11:41:48 | [diff] [blame] | 863 | info.fps_allocation[sid][tid] = EncoderInfo::kMaxFramerateFraction / |
| 864 | svc_params_->framerate_factor[tid]; |
Danil Chapovalov | b942d45 | 2021-01-12 13:45:07 | [diff] [blame] | 865 | } |
| 866 | } |
| 867 | } |
Michael Horowitz | b27efd4 | 2023-02-28 15:59:50 | [diff] [blame] | 868 | if (!encoder_info_override_.resolution_bitrate_limits().empty()) { |
| 869 | info.resolution_bitrate_limits = |
| 870 | encoder_info_override_.resolution_bitrate_limits(); |
| 871 | } |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 872 | return info; |
| 873 | } |
| 874 | |
| 875 | } // namespace |
| 876 | |
Danil Chapovalov | b4913a5 | 2024-03-11 12:17:45 | [diff] [blame] | 877 | absl::Nonnull<std::unique_ptr<VideoEncoder>> CreateLibaomAv1Encoder( |
| 878 | const Environment& env, |
| 879 | LibaomAv1EncoderSettings settings) { |
| 880 | if (settings.max_pixel_count_to_cpu_speed.empty()) { |
| 881 | return std::make_unique<LibaomAv1Encoder>(absl::nullopt, |
| 882 | env.field_trials()); |
| 883 | } else { |
| 884 | return std::make_unique<LibaomAv1Encoder>(settings, env.field_trials()); |
| 885 | } |
| 886 | } |
| 887 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 888 | std::unique_ptr<VideoEncoder> CreateLibaomAv1Encoder() { |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 889 | return std::make_unique<LibaomAv1Encoder>(absl::nullopt, |
| 890 | FieldTrialBasedConfig()); |
philipel | 30ec725 | 2022-05-04 09:20:04 | [diff] [blame] | 891 | } |
| 892 | |
| 893 | std::unique_ptr<VideoEncoder> CreateLibaomAv1Encoder( |
| 894 | const LibaomAv1EncoderAuxConfig& aux_config) { |
Sergey Silkin | d615704 | 2023-06-07 11:45:14 | [diff] [blame] | 895 | return std::make_unique<LibaomAv1Encoder>(aux_config, |
| 896 | FieldTrialBasedConfig()); |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 897 | } |
| 898 | |
Danil Chapovalov | 976cc1a | 2020-03-20 09:36:13 | [diff] [blame] | 899 | } // namespace webrtc |