|  | /* | 
|  | *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "api/video_codecs/video_encoder_software_fallback_wrapper.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <cstdio> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/strings/match.h" | 
|  | #include "absl/types/optional.h" | 
|  | #include "api/environment/environment.h" | 
|  | #include "api/fec_controller_override.h" | 
|  | #include "api/field_trials_view.h" | 
|  | #include "api/transport/field_trial_based_config.h" | 
|  | #include "api/video/i420_buffer.h" | 
|  | #include "api/video/video_bitrate_allocation.h" | 
|  | #include "api/video/video_frame.h" | 
|  | #include "api/video_codecs/video_codec.h" | 
|  | #include "api/video_codecs/video_encoder.h" | 
|  | #include "media/base/video_common.h" | 
|  | #include "modules/video_coding/include/video_error_codes.h" | 
|  | #include "modules/video_coding/include/video_error_codes_utils.h" | 
|  | #include "modules/video_coding/utility/simulcast_utility.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/experiments/field_trial_parser.h" | 
|  | #include "rtc_base/logging.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // If forced fallback is allowed, either: | 
|  | // | 
|  | // 1) The forced fallback is requested if the resolution is less than or equal | 
|  | //    to `max_pixels_`. The resolution is allowed to be scaled down to | 
|  | //    `min_pixels_`. | 
|  | // | 
|  | // 2) The forced fallback is requested if temporal support is preferred and the | 
|  | //    SW fallback supports temporal layers while the HW encoder does not. | 
|  |  | 
|  | struct ForcedFallbackParams { | 
|  | public: | 
|  | bool SupportsResolutionBasedSwitch(const VideoCodec& codec) const { | 
|  | if (!enable_resolution_based_switch || | 
|  | codec.width * codec.height > max_pixels) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (vp8_specific_resolution_switch && | 
|  | (codec.codecType != kVideoCodecVP8 || | 
|  | codec.numberOfSimulcastStreams > 1)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool SupportsTemporalBasedSwitch(const VideoCodec& codec) const { | 
|  | return enable_temporal_based_switch && | 
|  | SimulcastUtility::NumberOfTemporalLayers(codec, 0) != 1; | 
|  | } | 
|  |  | 
|  | bool enable_temporal_based_switch = false; | 
|  | bool enable_resolution_based_switch = false; | 
|  | bool vp8_specific_resolution_switch = false; | 
|  | int min_pixels = kDefaultMinPixelsPerFrame; | 
|  | int max_pixels = 320 * 240; | 
|  | }; | 
|  |  | 
|  | const char kVp8ForceFallbackEncoderFieldTrial[] = | 
|  | "WebRTC-VP8-Forced-Fallback-Encoder-v2"; | 
|  |  | 
|  | absl::optional<ForcedFallbackParams> ParseFallbackParamsFromFieldTrials( | 
|  | const FieldTrialsView& field_trials, | 
|  | const VideoEncoder& main_encoder) { | 
|  | // Ignore WebRTC-VP8-Forced-Fallback-Encoder-v2 if | 
|  | // WebRTC-Video-EncoderFallbackSettings is present. | 
|  | FieldTrialOptional<int> resolution_threshold_px("resolution_threshold_px"); | 
|  | ParseFieldTrial({&resolution_threshold_px}, | 
|  | field_trials.Lookup("WebRTC-Video-EncoderFallbackSettings")); | 
|  | if (resolution_threshold_px) { | 
|  | ForcedFallbackParams params; | 
|  | params.enable_resolution_based_switch = true; | 
|  | params.max_pixels = resolution_threshold_px.Value(); | 
|  | return params; | 
|  | } | 
|  |  | 
|  | const std::string field_trial = | 
|  | field_trials.Lookup(kVp8ForceFallbackEncoderFieldTrial); | 
|  | if (!absl::StartsWith(field_trial, "Enabled")) { | 
|  | return absl::nullopt; | 
|  | } | 
|  |  | 
|  | int max_pixels_lower_bound = | 
|  | main_encoder.GetEncoderInfo().scaling_settings.min_pixels_per_frame - 1; | 
|  |  | 
|  | ForcedFallbackParams params; | 
|  | params.enable_resolution_based_switch = true; | 
|  |  | 
|  | int min_bps = 0; | 
|  | if (sscanf(field_trial.c_str(), "Enabled-%d,%d,%d", ¶ms.min_pixels, | 
|  | ¶ms.max_pixels, &min_bps) != 3) { | 
|  | RTC_LOG(LS_WARNING) | 
|  | << "Invalid number of forced fallback parameters provided."; | 
|  | return absl::nullopt; | 
|  | } else if (params.min_pixels <= 0 || | 
|  | params.max_pixels < max_pixels_lower_bound || | 
|  | params.max_pixels < params.min_pixels || min_bps <= 0) { | 
|  | RTC_LOG(LS_WARNING) << "Invalid forced fallback parameter value provided."; | 
|  | return absl::nullopt; | 
|  | } | 
|  |  | 
|  | params.vp8_specific_resolution_switch = true; | 
|  | return params; | 
|  | } | 
|  |  | 
|  | absl::optional<ForcedFallbackParams> GetForcedFallbackParams( | 
|  | const FieldTrialsView& field_trials, | 
|  | bool prefer_temporal_support, | 
|  | const VideoEncoder& main_encoder) { | 
|  | absl::optional<ForcedFallbackParams> params = | 
|  | ParseFallbackParamsFromFieldTrials(field_trials, main_encoder); | 
|  | if (prefer_temporal_support) { | 
|  | if (!params.has_value()) { | 
|  | params.emplace(); | 
|  | } | 
|  | params->enable_temporal_based_switch = true; | 
|  | } | 
|  | return params; | 
|  | } | 
|  |  | 
|  | class VideoEncoderSoftwareFallbackWrapper final : public VideoEncoder { | 
|  | public: | 
|  | VideoEncoderSoftwareFallbackWrapper( | 
|  | const FieldTrialsView& field_trials, | 
|  | std::unique_ptr<webrtc::VideoEncoder> sw_encoder, | 
|  | std::unique_ptr<webrtc::VideoEncoder> hw_encoder, | 
|  | bool prefer_temporal_support); | 
|  | ~VideoEncoderSoftwareFallbackWrapper() override; | 
|  |  | 
|  | void SetFecControllerOverride( | 
|  | FecControllerOverride* fec_controller_override) override; | 
|  |  | 
|  | int32_t InitEncode(const VideoCodec* codec_settings, | 
|  | const VideoEncoder::Settings& settings) override; | 
|  |  | 
|  | int32_t RegisterEncodeCompleteCallback( | 
|  | EncodedImageCallback* callback) override; | 
|  |  | 
|  | int32_t Release() override; | 
|  |  | 
|  | int32_t Encode(const VideoFrame& frame, | 
|  | const std::vector<VideoFrameType>* frame_types) override; | 
|  |  | 
|  | void OnPacketLossRateUpdate(float packet_loss_rate) override; | 
|  |  | 
|  | void OnRttUpdate(int64_t rtt_ms) override; | 
|  |  | 
|  | void OnLossNotification(const LossNotification& loss_notification) override; | 
|  |  | 
|  | void SetRates(const RateControlParameters& parameters) override; | 
|  |  | 
|  | EncoderInfo GetEncoderInfo() const override; | 
|  |  | 
|  | private: | 
|  | bool InitFallbackEncoder(bool is_forced); | 
|  | bool TryInitForcedFallbackEncoder(); | 
|  | bool IsFallbackActive() const; | 
|  |  | 
|  | VideoEncoder* current_encoder() { | 
|  | switch (encoder_state_) { | 
|  | case EncoderState::kUninitialized: | 
|  | RTC_LOG(LS_WARNING) | 
|  | << "Trying to access encoder in uninitialized fallback wrapper."; | 
|  | // Return main encoder to preserve previous behavior. | 
|  | [[fallthrough]]; | 
|  | case EncoderState::kMainEncoderUsed: | 
|  | return encoder_.get(); | 
|  | case EncoderState::kFallbackDueToFailure: | 
|  | case EncoderState::kForcedFallback: | 
|  | return fallback_encoder_.get(); | 
|  | } | 
|  | RTC_CHECK_NOTREACHED(); | 
|  | } | 
|  |  | 
|  | // Updates encoder with last observed parameters, such as callbacks, rates, | 
|  | // etc. | 
|  | void PrimeEncoder(VideoEncoder* encoder) const; | 
|  |  | 
|  | // Settings used in the last InitEncode call and used if a dynamic fallback to | 
|  | // software is required. | 
|  | VideoCodec codec_settings_; | 
|  | absl::optional<VideoEncoder::Settings> encoder_settings_; | 
|  |  | 
|  | // The last rate control settings, if set. | 
|  | absl::optional<RateControlParameters> rate_control_parameters_; | 
|  |  | 
|  | // The last channel parameters set. | 
|  | absl::optional<float> packet_loss_; | 
|  | absl::optional<int64_t> rtt_; | 
|  | absl::optional<LossNotification> loss_notification_; | 
|  |  | 
|  | enum class EncoderState { | 
|  | kUninitialized, | 
|  | kMainEncoderUsed, | 
|  | kFallbackDueToFailure, | 
|  | kForcedFallback | 
|  | }; | 
|  |  | 
|  | EncoderState encoder_state_; | 
|  | const std::unique_ptr<webrtc::VideoEncoder> encoder_; | 
|  | const std::unique_ptr<webrtc::VideoEncoder> fallback_encoder_; | 
|  |  | 
|  | EncodedImageCallback* callback_; | 
|  |  | 
|  | const absl::optional<ForcedFallbackParams> fallback_params_; | 
|  | int32_t EncodeWithMainEncoder(const VideoFrame& frame, | 
|  | const std::vector<VideoFrameType>* frame_types); | 
|  | }; | 
|  |  | 
|  | VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper( | 
|  | const FieldTrialsView& field_trials, | 
|  | std::unique_ptr<webrtc::VideoEncoder> sw_encoder, | 
|  | std::unique_ptr<webrtc::VideoEncoder> hw_encoder, | 
|  | bool prefer_temporal_support) | 
|  | : encoder_state_(EncoderState::kUninitialized), | 
|  | encoder_(std::move(hw_encoder)), | 
|  | fallback_encoder_(std::move(sw_encoder)), | 
|  | callback_(nullptr), | 
|  | fallback_params_(GetForcedFallbackParams(field_trials, | 
|  | prefer_temporal_support, | 
|  | *encoder_)) { | 
|  | RTC_DCHECK(fallback_encoder_); | 
|  | } | 
|  |  | 
|  | VideoEncoderSoftwareFallbackWrapper::~VideoEncoderSoftwareFallbackWrapper() = | 
|  | default; | 
|  |  | 
|  | void VideoEncoderSoftwareFallbackWrapper::PrimeEncoder( | 
|  | VideoEncoder* encoder) const { | 
|  | RTC_DCHECK(encoder); | 
|  | // Replay callback, rates, and channel parameters. | 
|  | if (callback_) { | 
|  | encoder->RegisterEncodeCompleteCallback(callback_); | 
|  | } | 
|  | if (rate_control_parameters_) { | 
|  | encoder->SetRates(*rate_control_parameters_); | 
|  | } | 
|  | if (rtt_.has_value()) { | 
|  | encoder->OnRttUpdate(rtt_.value()); | 
|  | } | 
|  | if (packet_loss_.has_value()) { | 
|  | encoder->OnPacketLossRateUpdate(packet_loss_.value()); | 
|  | } | 
|  |  | 
|  | if (loss_notification_.has_value()) { | 
|  | encoder->OnLossNotification(loss_notification_.value()); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder(bool is_forced) { | 
|  | RTC_LOG(LS_WARNING) << "[VESFW] " << __func__ | 
|  | << "(is_forced=" << (is_forced ? "true" : "false") << ")"; | 
|  |  | 
|  | RTC_DCHECK(encoder_settings_.has_value()); | 
|  | const int ret = fallback_encoder_->InitEncode(&codec_settings_, | 
|  | encoder_settings_.value()); | 
|  |  | 
|  | if (ret != WEBRTC_VIDEO_CODEC_OK) { | 
|  | RTC_LOG(LS_ERROR) | 
|  | << "[VESFW] software-encoder fallback initialization failed with" | 
|  | << " error code: " << WebRtcVideoCodecErrorToString(ret); | 
|  | fallback_encoder_->Release(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (encoder_state_ == EncoderState::kMainEncoderUsed) { | 
|  | // Since we're switching to the fallback encoder, Release the real encoder. | 
|  | // It may be re-initialized via InitEncode later, and it will continue to | 
|  | // get Set calls for rates and channel parameters in the meantime. | 
|  | encoder_->Release(); | 
|  | } | 
|  |  | 
|  | if (is_forced) { | 
|  | encoder_state_ = EncoderState::kForcedFallback; | 
|  | } else { | 
|  | encoder_state_ = EncoderState::kFallbackDueToFailure; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void VideoEncoderSoftwareFallbackWrapper::SetFecControllerOverride( | 
|  | FecControllerOverride* fec_controller_override) { | 
|  | // It is important that only one of those would ever interact with the | 
|  | // `fec_controller_override` at a given time. This is the responsibility | 
|  | // of `this` to maintain. | 
|  |  | 
|  | encoder_->SetFecControllerOverride(fec_controller_override); | 
|  | fallback_encoder_->SetFecControllerOverride(fec_controller_override); | 
|  | } | 
|  |  | 
|  | int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode( | 
|  | const VideoCodec* codec_settings, | 
|  | const VideoEncoder::Settings& settings) { | 
|  | RTC_LOG(LS_INFO) << "[VESFW] " << __func__ | 
|  | << "(codec=" << codec_settings->ToString() | 
|  | << ", settings={number_of_cores: " | 
|  | << settings.number_of_cores | 
|  | << ", max_payload_size: " << settings.max_payload_size | 
|  | << "})"; | 
|  | // Store settings, in case we need to dynamically switch to the fallback | 
|  | // encoder after a failed Encode call. | 
|  | codec_settings_ = *codec_settings; | 
|  | encoder_settings_ = settings; | 
|  | // Clear stored rate/channel parameters. | 
|  | rate_control_parameters_ = absl::nullopt; | 
|  |  | 
|  | RTC_DCHECK_EQ(encoder_state_, EncoderState::kUninitialized) | 
|  | << "InitEncode() should never be called on an active instance!"; | 
|  |  | 
|  | // Try to init forced software codec if it should be used. | 
|  | if (TryInitForcedFallbackEncoder()) { | 
|  | PrimeEncoder(current_encoder()); | 
|  | return WEBRTC_VIDEO_CODEC_OK; | 
|  | } | 
|  |  | 
|  | int32_t ret = encoder_->InitEncode(codec_settings, settings); | 
|  | if (ret == WEBRTC_VIDEO_CODEC_OK) { | 
|  | encoder_state_ = EncoderState::kMainEncoderUsed; | 
|  | PrimeEncoder(current_encoder()); | 
|  | return ret; | 
|  | } | 
|  | RTC_LOG(LS_WARNING) << "[VESFW] Hardware encoder initialization failed with" | 
|  | << " error code: " << WebRtcVideoCodecErrorToString(ret); | 
|  |  | 
|  | // Try to instantiate software codec. | 
|  | if (InitFallbackEncoder(/*is_forced=*/false)) { | 
|  | PrimeEncoder(current_encoder()); | 
|  | return WEBRTC_VIDEO_CODEC_OK; | 
|  | } | 
|  |  | 
|  | // Software encoder failed too, use original return code. | 
|  | RTC_LOG(LS_WARNING) | 
|  | << "[VESFW] Software fallback encoder initialization also failed."; | 
|  | encoder_state_ = EncoderState::kUninitialized; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback( | 
|  | EncodedImageCallback* callback) { | 
|  | callback_ = callback; | 
|  | return current_encoder()->RegisterEncodeCompleteCallback(callback); | 
|  | } | 
|  |  | 
|  | int32_t VideoEncoderSoftwareFallbackWrapper::Release() { | 
|  | if (encoder_state_ == EncoderState::kUninitialized) { | 
|  | return WEBRTC_VIDEO_CODEC_OK; | 
|  | } | 
|  | int32_t ret = current_encoder()->Release(); | 
|  | encoder_state_ = EncoderState::kUninitialized; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int32_t VideoEncoderSoftwareFallbackWrapper::Encode( | 
|  | const VideoFrame& frame, | 
|  | const std::vector<VideoFrameType>* frame_types) { | 
|  | switch (encoder_state_) { | 
|  | case EncoderState::kUninitialized: | 
|  | return WEBRTC_VIDEO_CODEC_ERROR; | 
|  | case EncoderState::kMainEncoderUsed: { | 
|  | return EncodeWithMainEncoder(frame, frame_types); | 
|  | } | 
|  | case EncoderState::kFallbackDueToFailure: | 
|  | case EncoderState::kForcedFallback: | 
|  | return fallback_encoder_->Encode(frame, frame_types); | 
|  | } | 
|  | RTC_CHECK_NOTREACHED(); | 
|  | } | 
|  |  | 
|  | int32_t VideoEncoderSoftwareFallbackWrapper::EncodeWithMainEncoder( | 
|  | const VideoFrame& frame, | 
|  | const std::vector<VideoFrameType>* frame_types) { | 
|  | int32_t ret = encoder_->Encode(frame, frame_types); | 
|  | // If requested, try a software fallback. | 
|  | bool fallback_requested = (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE); | 
|  | if (fallback_requested && InitFallbackEncoder(/*is_forced=*/false)) { | 
|  | // Start using the fallback with this frame. | 
|  | PrimeEncoder(current_encoder()); | 
|  | if (frame.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative && | 
|  | fallback_encoder_->GetEncoderInfo().supports_native_handle) { | 
|  | return fallback_encoder_->Encode(frame, frame_types); | 
|  | } else { | 
|  | RTC_LOG(LS_INFO) << "Fallback encoder does not support native handle - " | 
|  | "converting frame to I420"; | 
|  | rtc::scoped_refptr<I420BufferInterface> src_buffer = | 
|  | frame.video_frame_buffer()->ToI420(); | 
|  | if (!src_buffer) { | 
|  | RTC_LOG(LS_ERROR) << "Failed to convert from to I420"; | 
|  | return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; | 
|  | } | 
|  | rtc::scoped_refptr<VideoFrameBuffer> dst_buffer = | 
|  | src_buffer->Scale(codec_settings_.width, codec_settings_.height); | 
|  | if (!dst_buffer) { | 
|  | RTC_LOG(LS_ERROR) << "Failed to scale video frame."; | 
|  | return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; | 
|  | } | 
|  | VideoFrame scaled_frame = frame; | 
|  | scaled_frame.set_video_frame_buffer(dst_buffer); | 
|  | scaled_frame.set_update_rect(VideoFrame::UpdateRect{ | 
|  | 0, 0, scaled_frame.width(), scaled_frame.height()}); | 
|  | return fallback_encoder_->Encode(scaled_frame, frame_types); | 
|  | } | 
|  | } | 
|  | // Fallback encoder failed too, return original error code. | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void VideoEncoderSoftwareFallbackWrapper::SetRates( | 
|  | const RateControlParameters& parameters) { | 
|  | rate_control_parameters_ = parameters; | 
|  | return current_encoder()->SetRates(parameters); | 
|  | } | 
|  |  | 
|  | void VideoEncoderSoftwareFallbackWrapper::OnPacketLossRateUpdate( | 
|  | float packet_loss_rate) { | 
|  | packet_loss_ = packet_loss_rate; | 
|  | current_encoder()->OnPacketLossRateUpdate(packet_loss_rate); | 
|  | } | 
|  |  | 
|  | void VideoEncoderSoftwareFallbackWrapper::OnRttUpdate(int64_t rtt_ms) { | 
|  | rtt_ = rtt_ms; | 
|  | current_encoder()->OnRttUpdate(rtt_ms); | 
|  | } | 
|  |  | 
|  | void VideoEncoderSoftwareFallbackWrapper::OnLossNotification( | 
|  | const LossNotification& loss_notification) { | 
|  | loss_notification_ = loss_notification; | 
|  | current_encoder()->OnLossNotification(loss_notification); | 
|  | } | 
|  |  | 
|  | VideoEncoder::EncoderInfo VideoEncoderSoftwareFallbackWrapper::GetEncoderInfo() | 
|  | const { | 
|  | EncoderInfo fallback_encoder_info = fallback_encoder_->GetEncoderInfo(); | 
|  | EncoderInfo default_encoder_info = encoder_->GetEncoderInfo(); | 
|  |  | 
|  | EncoderInfo info = | 
|  | IsFallbackActive() ? fallback_encoder_info : default_encoder_info; | 
|  |  | 
|  | info.requested_resolution_alignment = cricket::LeastCommonMultiple( | 
|  | fallback_encoder_info.requested_resolution_alignment, | 
|  | default_encoder_info.requested_resolution_alignment); | 
|  | info.apply_alignment_to_all_simulcast_layers = | 
|  | fallback_encoder_info.apply_alignment_to_all_simulcast_layers || | 
|  | default_encoder_info.apply_alignment_to_all_simulcast_layers; | 
|  |  | 
|  | if (fallback_params_ && fallback_params_->vp8_specific_resolution_switch) { | 
|  | info.scaling_settings.min_pixels_per_frame = fallback_params_->min_pixels; | 
|  | } | 
|  |  | 
|  | return info; | 
|  | } | 
|  |  | 
|  | bool VideoEncoderSoftwareFallbackWrapper::IsFallbackActive() const { | 
|  | return encoder_state_ == EncoderState::kForcedFallback || | 
|  | encoder_state_ == EncoderState::kFallbackDueToFailure; | 
|  | } | 
|  |  | 
|  | bool VideoEncoderSoftwareFallbackWrapper::TryInitForcedFallbackEncoder() { | 
|  | if (!fallback_params_) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | RTC_DCHECK_EQ(encoder_state_, EncoderState::kUninitialized); | 
|  |  | 
|  | if (fallback_params_->SupportsResolutionBasedSwitch(codec_settings_)) { | 
|  | // Settings valid, try to instantiate software codec. | 
|  | RTC_LOG(LS_INFO) << "Request forced SW encoder fallback: " | 
|  | << codec_settings_.width << "x" << codec_settings_.height; | 
|  | return InitFallbackEncoder(/*is_forced=*/true); | 
|  | } | 
|  |  | 
|  | if (fallback_params_->SupportsTemporalBasedSwitch(codec_settings_)) { | 
|  | // First init main encoder to see if that supports temporal layers. | 
|  | if (encoder_->InitEncode(&codec_settings_, encoder_settings_.value()) == | 
|  | WEBRTC_VIDEO_CODEC_OK) { | 
|  | encoder_state_ = EncoderState::kMainEncoderUsed; | 
|  | } | 
|  |  | 
|  | if (encoder_state_ == EncoderState::kMainEncoderUsed && | 
|  | encoder_->GetEncoderInfo().fps_allocation[0].size() != 1) { | 
|  | // Primary encoder already supports temporal layers, use that instead. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Try to initialize fallback and check if it supports temporal layers. | 
|  | if (fallback_encoder_->InitEncode(&codec_settings_, | 
|  | encoder_settings_.value()) == | 
|  | WEBRTC_VIDEO_CODEC_OK) { | 
|  | if (fallback_encoder_->GetEncoderInfo().fps_allocation[0].size() != 1) { | 
|  | // Fallback encoder available and supports temporal layers, use it! | 
|  | if (encoder_state_ == EncoderState::kMainEncoderUsed) { | 
|  | // Main encoder initialized but does not support temporal layers, | 
|  | // release it again. | 
|  | encoder_->Release(); | 
|  | } | 
|  | encoder_state_ = EncoderState::kForcedFallback; | 
|  | RTC_LOG(LS_INFO) | 
|  | << "Forced switch to SW encoder due to temporal support."; | 
|  | return true; | 
|  | } else { | 
|  | // Fallback encoder intialization succeeded, but it does not support | 
|  | // temporal layers either - release it. | 
|  | fallback_encoder_->Release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (encoder_state_ == EncoderState::kMainEncoderUsed) { | 
|  | // Main encoder already initialized - make use of it. | 
|  | RTC_LOG(LS_INFO) | 
|  | << "Cannot fall back for temporal support since fallback that " | 
|  | "supports is not available. Using main encoder instead."; | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Neither forced fallback mode supported. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | std::unique_ptr<VideoEncoder> CreateVideoEncoderSoftwareFallbackWrapper( | 
|  | const Environment& env, | 
|  | std::unique_ptr<VideoEncoder> sw_fallback_encoder, | 
|  | std::unique_ptr<VideoEncoder> hw_encoder, | 
|  | bool prefer_temporal_support) { | 
|  | return std::make_unique<VideoEncoderSoftwareFallbackWrapper>( | 
|  | env.field_trials(), std::move(sw_fallback_encoder), std::move(hw_encoder), | 
|  | prefer_temporal_support); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<VideoEncoder> CreateVideoEncoderSoftwareFallbackWrapper( | 
|  | std::unique_ptr<VideoEncoder> sw_fallback_encoder, | 
|  | std::unique_ptr<VideoEncoder> hw_encoder, | 
|  | bool prefer_temporal_support) { | 
|  | return std::make_unique<VideoEncoderSoftwareFallbackWrapper>( | 
|  | FieldTrialBasedConfig(), std::move(sw_fallback_encoder), | 
|  | std::move(hw_encoder), prefer_temporal_support); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |