video_decoder_software_fallback_wrapper.cc (9846B)
1 /* 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "api/video_codecs/video_decoder_software_fallback_wrapper.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <memory> 16 #include <string> 17 #include <utility> 18 19 #include "api/environment/environment.h" 20 #include "api/field_trials_view.h" 21 #include "api/video/encoded_image.h" 22 #include "api/video/video_codec_type.h" 23 #include "api/video/video_frame_type.h" 24 #include "api/video_codecs/video_decoder.h" 25 #include "modules/video_coding/include/video_error_codes.h" 26 #include "rtc_base/checks.h" 27 #include "rtc_base/logging.h" 28 #include "rtc_base/trace_event.h" 29 #include "system_wrappers/include/metrics.h" 30 31 namespace webrtc { 32 33 namespace { 34 35 constexpr size_t kMaxConsequtiveHwErrors = 4; 36 37 class VideoDecoderSoftwareFallbackWrapper final : public VideoDecoder { 38 public: 39 VideoDecoderSoftwareFallbackWrapper( 40 const Environment& env, 41 std::unique_ptr<VideoDecoder> sw_fallback_decoder, 42 std::unique_ptr<VideoDecoder> hw_decoder); 43 ~VideoDecoderSoftwareFallbackWrapper() override; 44 45 bool Configure(const Settings& settings) override; 46 47 int32_t Decode(const EncodedImage& input_image, 48 int64_t render_time_ms) override; 49 50 int32_t RegisterDecodeCompleteCallback( 51 DecodedImageCallback* callback) override; 52 53 int32_t Release() override; 54 55 DecoderInfo GetDecoderInfo() const override; 56 const char* ImplementationName() const override; 57 58 private: 59 bool InitFallbackDecoder(); 60 void UpdateFallbackDecoderHistograms(); 61 62 bool InitHwDecoder(); 63 64 VideoDecoder& active_decoder() const; 65 66 // Determines if we are trying to use the HW or SW decoder. 67 enum class DecoderType { 68 kNone, 69 kHardware, 70 kFallback, 71 } decoder_type_; 72 std::unique_ptr<VideoDecoder> hw_decoder_; 73 74 const bool force_sw_decoder_fallback_; 75 Settings decoder_settings_; 76 const std::unique_ptr<VideoDecoder> fallback_decoder_; 77 const std::string fallback_implementation_name_; 78 DecodedImageCallback* callback_; 79 int32_t hw_decoded_frames_since_last_fallback_; 80 size_t hw_consequtive_generic_errors_; 81 }; 82 83 VideoDecoderSoftwareFallbackWrapper::VideoDecoderSoftwareFallbackWrapper( 84 const Environment& env, 85 std::unique_ptr<VideoDecoder> sw_fallback_decoder, 86 std::unique_ptr<VideoDecoder> hw_decoder) 87 : decoder_type_(DecoderType::kNone), 88 hw_decoder_(std::move(hw_decoder)), 89 force_sw_decoder_fallback_( 90 env.field_trials().IsEnabled("WebRTC-Video-ForcedSwDecoderFallback")), 91 fallback_decoder_(std::move(sw_fallback_decoder)), 92 fallback_implementation_name_( 93 fallback_decoder_->GetDecoderInfo().implementation_name + 94 " (fallback from: " + 95 hw_decoder_->GetDecoderInfo().implementation_name + ")"), 96 callback_(nullptr), 97 hw_decoded_frames_since_last_fallback_(0), 98 hw_consequtive_generic_errors_(0) {} 99 VideoDecoderSoftwareFallbackWrapper::~VideoDecoderSoftwareFallbackWrapper() = 100 default; 101 102 bool VideoDecoderSoftwareFallbackWrapper::Configure(const Settings& settings) { 103 decoder_settings_ = settings; 104 105 if (force_sw_decoder_fallback_) { 106 RTC_LOG(LS_INFO) << "Forced software decoder fallback enabled."; 107 RTC_DCHECK(decoder_type_ == DecoderType::kNone); 108 return InitFallbackDecoder(); 109 } 110 if (InitHwDecoder()) { 111 return true; 112 } 113 114 RTC_DCHECK(decoder_type_ == DecoderType::kNone); 115 return InitFallbackDecoder(); 116 } 117 118 bool VideoDecoderSoftwareFallbackWrapper::InitHwDecoder() { 119 RTC_DCHECK(decoder_type_ == DecoderType::kNone); 120 if (!hw_decoder_->Configure(decoder_settings_)) { 121 return false; 122 } 123 124 decoder_type_ = DecoderType::kHardware; 125 if (callback_) 126 hw_decoder_->RegisterDecodeCompleteCallback(callback_); 127 return true; 128 } 129 130 bool VideoDecoderSoftwareFallbackWrapper::InitFallbackDecoder() { 131 RTC_DCHECK(decoder_type_ == DecoderType::kNone || 132 decoder_type_ == DecoderType::kHardware); 133 RTC_LOG(LS_WARNING) << "Decoder falling back to software decoding."; 134 if (!fallback_decoder_->Configure(decoder_settings_)) { 135 RTC_LOG(LS_ERROR) << "Failed to initialize software-decoder fallback."; 136 return false; 137 } 138 139 UpdateFallbackDecoderHistograms(); 140 141 if (decoder_type_ == DecoderType::kHardware) { 142 hw_decoder_->Release(); 143 } 144 decoder_type_ = DecoderType::kFallback; 145 146 if (callback_) 147 fallback_decoder_->RegisterDecodeCompleteCallback(callback_); 148 return true; 149 } 150 151 void VideoDecoderSoftwareFallbackWrapper::UpdateFallbackDecoderHistograms() { 152 const std::string kFallbackHistogramsUmaPrefix = 153 "WebRTC.Video.HardwareDecodedFramesBetweenSoftwareFallbacks."; 154 // Each histogram needs its own code path for this to work otherwise the 155 // histogram names will be mixed up by the optimization that takes place. 156 switch (decoder_settings_.codec_type()) { 157 case kVideoCodecGeneric: 158 RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Generic", 159 hw_decoded_frames_since_last_fallback_); 160 break; 161 case kVideoCodecVP8: 162 RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Vp8", 163 hw_decoded_frames_since_last_fallback_); 164 break; 165 case kVideoCodecVP9: 166 RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Vp9", 167 hw_decoded_frames_since_last_fallback_); 168 break; 169 case kVideoCodecAV1: 170 RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Av1", 171 hw_decoded_frames_since_last_fallback_); 172 break; 173 case kVideoCodecH264: 174 RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "H264", 175 hw_decoded_frames_since_last_fallback_); 176 break; 177 case kVideoCodecH265: 178 RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "H265", 179 hw_decoded_frames_since_last_fallback_); 180 break; 181 } 182 } 183 184 int32_t VideoDecoderSoftwareFallbackWrapper::Decode( 185 const EncodedImage& input_image, 186 int64_t render_time_ms) { 187 TRACE_EVENT0("webrtc", "VideoDecoderSoftwareFallbackWrapper::Decode"); 188 switch (decoder_type_) { 189 case DecoderType::kNone: 190 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; 191 case DecoderType::kHardware: { 192 int32_t ret = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; 193 ret = hw_decoder_->Decode(input_image, render_time_ms); 194 if (ret != WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) { 195 if (ret != WEBRTC_VIDEO_CODEC_ERROR) { 196 ++hw_decoded_frames_since_last_fallback_; 197 hw_consequtive_generic_errors_ = 0; 198 return ret; 199 } 200 if (input_image._frameType == VideoFrameType::kVideoFrameKey) { 201 // Only count errors on key-frames, since generic errors can happen 202 // with hw decoder due to many arbitrary reasons. 203 // However, requesting a key-frame is supposed to fix the issue. 204 ++hw_consequtive_generic_errors_; 205 } 206 if (hw_consequtive_generic_errors_ < kMaxConsequtiveHwErrors) { 207 return ret; 208 } 209 } 210 211 // HW decoder returned WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE or 212 // too many generic errors on key-frames encountered. 213 if (!InitFallbackDecoder()) { 214 return ret; 215 } 216 217 // Fallback decoder initialized, fall-through. 218 [[fallthrough]]; 219 } 220 case DecoderType::kFallback: 221 return fallback_decoder_->Decode(input_image, render_time_ms); 222 default: 223 RTC_DCHECK_NOTREACHED(); 224 return WEBRTC_VIDEO_CODEC_ERROR; 225 } 226 } 227 228 int32_t VideoDecoderSoftwareFallbackWrapper::RegisterDecodeCompleteCallback( 229 DecodedImageCallback* callback) { 230 callback_ = callback; 231 return active_decoder().RegisterDecodeCompleteCallback(callback); 232 } 233 234 int32_t VideoDecoderSoftwareFallbackWrapper::Release() { 235 int32_t status; 236 switch (decoder_type_) { 237 case DecoderType::kHardware: 238 status = hw_decoder_->Release(); 239 break; 240 case DecoderType::kFallback: 241 RTC_LOG(LS_INFO) << "Releasing software fallback decoder."; 242 status = fallback_decoder_->Release(); 243 break; 244 case DecoderType::kNone: 245 status = WEBRTC_VIDEO_CODEC_OK; 246 break; 247 default: 248 RTC_DCHECK_NOTREACHED(); 249 status = WEBRTC_VIDEO_CODEC_ERROR; 250 } 251 252 decoder_type_ = DecoderType::kNone; 253 return status; 254 } 255 256 VideoDecoder::DecoderInfo VideoDecoderSoftwareFallbackWrapper::GetDecoderInfo() 257 const { 258 DecoderInfo info = active_decoder().GetDecoderInfo(); 259 if (decoder_type_ == DecoderType::kFallback) { 260 // Cached "A (fallback from B)" string. 261 info.implementation_name = fallback_implementation_name_; 262 } 263 return info; 264 } 265 266 const char* VideoDecoderSoftwareFallbackWrapper::ImplementationName() const { 267 if (decoder_type_ == DecoderType::kFallback) { 268 // Cached "A (fallback from B)" string. 269 return fallback_implementation_name_.c_str(); 270 } else { 271 return hw_decoder_->ImplementationName(); 272 } 273 } 274 275 VideoDecoder& VideoDecoderSoftwareFallbackWrapper::active_decoder() const { 276 return decoder_type_ == DecoderType::kFallback ? *fallback_decoder_ 277 : *hw_decoder_; 278 } 279 280 } // namespace 281 282 std::unique_ptr<VideoDecoder> CreateVideoDecoderSoftwareFallbackWrapper( 283 const Environment& env, 284 std::unique_ptr<VideoDecoder> sw_fallback_decoder, 285 std::unique_ptr<VideoDecoder> hw_decoder) { 286 return std::make_unique<VideoDecoderSoftwareFallbackWrapper>( 287 env, std::move(sw_fallback_decoder), std::move(hw_decoder)); 288 } 289 290 } // namespace webrtc