magjed | f6acc2a | 2016-11-22 09:43:03 | [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_decoder_software_fallback_wrapper.h" |
magjed | f6acc2a | 2016-11-22 09:43:03 | [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 | |
Mirko Bonadei | 317a1f0 | 2019-09-17 15:06:18 | [diff] [blame] | 15 | #include <memory> |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 16 | #include <string> |
Steve Anton | e78bcb9 | 2017-10-31 16:53:08 | [diff] [blame] | 17 | #include <utility> |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 18 | |
Yves Gerey | 3e70781 | 2018-11-28 15:47:49 | [diff] [blame] | 19 | #include "api/video/encoded_image.h" |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 20 | #include "api/video_codecs/video_decoder.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 21 | #include "modules/video_coding/include/video_error_codes.h" |
| 22 | #include "rtc_base/checks.h" |
| 23 | #include "rtc_base/logging.h" |
| 24 | #include "rtc_base/trace_event.h" |
Åsa Persson | a8cb366 | 2019-02-07 14:31:18 | [diff] [blame] | 25 | #include "system_wrappers/include/field_trial.h" |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 26 | #include "system_wrappers/include/metrics.h" |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 27 | |
| 28 | namespace webrtc { |
| 29 | |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 30 | namespace { |
| 31 | |
Ilya Nikolaevskiy | 35fc153 | 2020-05-15 12:25:59 | [diff] [blame] | 32 | constexpr size_t kMaxConsequtiveHwErrors = 4; |
| 33 | |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 34 | class VideoDecoderSoftwareFallbackWrapper final : public VideoDecoder { |
| 35 | public: |
| 36 | VideoDecoderSoftwareFallbackWrapper( |
| 37 | std::unique_ptr<VideoDecoder> sw_fallback_decoder, |
| 38 | std::unique_ptr<VideoDecoder> hw_decoder); |
| 39 | ~VideoDecoderSoftwareFallbackWrapper() override; |
| 40 | |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 41 | bool Configure(const Settings& settings) override; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 42 | |
| 43 | int32_t Decode(const EncodedImage& input_image, |
| 44 | bool missing_frames, |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 45 | int64_t render_time_ms) override; |
| 46 | |
| 47 | int32_t RegisterDecodeCompleteCallback( |
| 48 | DecodedImageCallback* callback) override; |
| 49 | |
| 50 | int32_t Release() override; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 51 | |
Erik Språng | c12f625 | 2021-01-13 20:49:59 | [diff] [blame] | 52 | DecoderInfo GetDecoderInfo() const override; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 53 | const char* ImplementationName() const override; |
| 54 | |
| 55 | private: |
| 56 | bool InitFallbackDecoder(); |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 57 | void UpdateFallbackDecoderHistograms(); |
| 58 | |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 59 | bool InitHwDecoder(); |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 60 | |
| 61 | VideoDecoder& active_decoder() const; |
| 62 | |
| 63 | // Determines if we are trying to use the HW or SW decoder. |
| 64 | enum class DecoderType { |
| 65 | kNone, |
| 66 | kHardware, |
| 67 | kFallback, |
| 68 | } decoder_type_; |
| 69 | std::unique_ptr<VideoDecoder> hw_decoder_; |
| 70 | |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 71 | Settings decoder_settings_; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 72 | const std::unique_ptr<VideoDecoder> fallback_decoder_; |
| 73 | const std::string fallback_implementation_name_; |
| 74 | DecodedImageCallback* callback_; |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 75 | int32_t hw_decoded_frames_since_last_fallback_; |
Ilya Nikolaevskiy | 35fc153 | 2020-05-15 12:25:59 | [diff] [blame] | 76 | size_t hw_consequtive_generic_errors_; |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 77 | }; |
| 78 | |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 79 | VideoDecoderSoftwareFallbackWrapper::VideoDecoderSoftwareFallbackWrapper( |
Magnus Jedvert | 7501b1c | 2017-11-09 12:43:42 | [diff] [blame] | 80 | std::unique_ptr<VideoDecoder> sw_fallback_decoder, |
| 81 | std::unique_ptr<VideoDecoder> hw_decoder) |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 82 | : decoder_type_(DecoderType::kNone), |
Magnus Jedvert | 7501b1c | 2017-11-09 12:43:42 | [diff] [blame] | 83 | hw_decoder_(std::move(hw_decoder)), |
Magnus Jedvert | 7501b1c | 2017-11-09 12:43:42 | [diff] [blame] | 84 | fallback_decoder_(std::move(sw_fallback_decoder)), |
| 85 | fallback_implementation_name_( |
Johannes Kron | 96dbc60 | 2022-04-04 12:16:38 | [diff] [blame] | 86 | fallback_decoder_->GetDecoderInfo().implementation_name + |
| 87 | " (fallback from: " + |
| 88 | hw_decoder_->GetDecoderInfo().implementation_name + ")"), |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 89 | callback_(nullptr), |
Ilya Nikolaevskiy | 35fc153 | 2020-05-15 12:25:59 | [diff] [blame] | 90 | hw_decoded_frames_since_last_fallback_(0), |
| 91 | hw_consequtive_generic_errors_(0) {} |
Paulina Hensman | a680a6a | 2018-04-05 09:42:24 | [diff] [blame] | 92 | VideoDecoderSoftwareFallbackWrapper::~VideoDecoderSoftwareFallbackWrapper() = |
| 93 | default; |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 94 | |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 95 | bool VideoDecoderSoftwareFallbackWrapper::Configure(const Settings& settings) { |
| 96 | decoder_settings_ = settings; |
Peter Boström | f812e45 | 2017-02-13 22:11:08 | [diff] [blame] | 97 | |
Åsa Persson | a8cb366 | 2019-02-07 14:31:18 | [diff] [blame] | 98 | if (webrtc::field_trial::IsEnabled("WebRTC-Video-ForcedSwDecoderFallback")) { |
| 99 | RTC_LOG(LS_INFO) << "Forced software decoder fallback enabled."; |
| 100 | RTC_DCHECK(decoder_type_ == DecoderType::kNone); |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 101 | return InitFallbackDecoder(); |
Åsa Persson | a8cb366 | 2019-02-07 14:31:18 | [diff] [blame] | 102 | } |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 103 | if (InitHwDecoder()) { |
| 104 | return true; |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 105 | } |
Magnus Jedvert | 7501b1c | 2017-11-09 12:43:42 | [diff] [blame] | 106 | |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 107 | RTC_DCHECK(decoder_type_ == DecoderType::kNone); |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 108 | return InitFallbackDecoder(); |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 109 | } |
| 110 | |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 111 | bool VideoDecoderSoftwareFallbackWrapper::InitHwDecoder() { |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 112 | RTC_DCHECK(decoder_type_ == DecoderType::kNone); |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 113 | if (!hw_decoder_->Configure(decoder_settings_)) { |
| 114 | return false; |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | decoder_type_ = DecoderType::kHardware; |
| 118 | if (callback_) |
| 119 | hw_decoder_->RegisterDecodeCompleteCallback(callback_); |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 120 | return true; |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | bool VideoDecoderSoftwareFallbackWrapper::InitFallbackDecoder() { |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 124 | RTC_DCHECK(decoder_type_ == DecoderType::kNone || |
| 125 | decoder_type_ == DecoderType::kHardware); |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 126 | RTC_LOG(LS_WARNING) << "Decoder falling back to software decoding."; |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 127 | if (!fallback_decoder_->Configure(decoder_settings_)) { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 128 | RTC_LOG(LS_ERROR) << "Failed to initialize software-decoder fallback."; |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 129 | return false; |
| 130 | } |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 131 | |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 132 | UpdateFallbackDecoderHistograms(); |
| 133 | |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 134 | if (decoder_type_ == DecoderType::kHardware) { |
| 135 | hw_decoder_->Release(); |
| 136 | } |
| 137 | decoder_type_ = DecoderType::kFallback; |
| 138 | |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 139 | if (callback_) |
| 140 | fallback_decoder_->RegisterDecodeCompleteCallback(callback_); |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 141 | return true; |
| 142 | } |
| 143 | |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 144 | void VideoDecoderSoftwareFallbackWrapper::UpdateFallbackDecoderHistograms() { |
| 145 | const std::string kFallbackHistogramsUmaPrefix = |
| 146 | "WebRTC.Video.HardwareDecodedFramesBetweenSoftwareFallbacks."; |
| 147 | // Each histogram needs its own code path for this to work otherwise the |
| 148 | // histogram names will be mixed up by the optimization that takes place. |
Danil Chapovalov | 7fa3f40 | 2021-08-12 09:04:45 | [diff] [blame] | 149 | switch (decoder_settings_.codec_type()) { |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 150 | case kVideoCodecGeneric: |
| 151 | RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Generic", |
| 152 | hw_decoded_frames_since_last_fallback_); |
| 153 | break; |
| 154 | case kVideoCodecVP8: |
| 155 | RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Vp8", |
| 156 | hw_decoded_frames_since_last_fallback_); |
| 157 | break; |
| 158 | case kVideoCodecVP9: |
| 159 | RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Vp9", |
| 160 | hw_decoded_frames_since_last_fallback_); |
| 161 | break; |
| 162 | case kVideoCodecAV1: |
| 163 | RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Av1", |
| 164 | hw_decoded_frames_since_last_fallback_); |
| 165 | break; |
| 166 | case kVideoCodecH264: |
| 167 | RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "H264", |
| 168 | hw_decoded_frames_since_last_fallback_); |
| 169 | break; |
| 170 | case kVideoCodecMultiplex: |
| 171 | RTC_HISTOGRAM_COUNTS_100000(kFallbackHistogramsUmaPrefix + "Multiplex", |
| 172 | hw_decoded_frames_since_last_fallback_); |
| 173 | break; |
| 174 | } |
| 175 | } |
| 176 | |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 177 | int32_t VideoDecoderSoftwareFallbackWrapper::Decode( |
| 178 | const EncodedImage& input_image, |
| 179 | bool missing_frames, |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 180 | int64_t render_time_ms) { |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 181 | TRACE_EVENT0("webrtc", "VideoDecoderSoftwareFallbackWrapper::Decode"); |
| 182 | switch (decoder_type_) { |
| 183 | case DecoderType::kNone: |
| 184 | return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| 185 | case DecoderType::kHardware: { |
| 186 | int32_t ret = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
Niels Möller | 7aacdd9 | 2019-03-25 08:11:40 | [diff] [blame] | 187 | ret = hw_decoder_->Decode(input_image, missing_frames, render_time_ms); |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 188 | if (ret != WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) { |
Ilya Nikolaevskiy | 35fc153 | 2020-05-15 12:25:59 | [diff] [blame] | 189 | if (ret != WEBRTC_VIDEO_CODEC_ERROR) { |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 190 | ++hw_decoded_frames_since_last_fallback_; |
Ilya Nikolaevskiy | 35fc153 | 2020-05-15 12:25:59 | [diff] [blame] | 191 | hw_consequtive_generic_errors_ = 0; |
| 192 | return ret; |
Johannes Kron | 5703303 | 2020-03-17 09:01:43 | [diff] [blame] | 193 | } |
Ilya Nikolaevskiy | 35fc153 | 2020-05-15 12:25:59 | [diff] [blame] | 194 | if (input_image._frameType == VideoFrameType::kVideoFrameKey) { |
| 195 | // Only count errors on key-frames, since generic errors can happen |
| 196 | // with hw decoder due to many arbitrary reasons. |
| 197 | // However, requesting a key-frame is supposed to fix the issue. |
| 198 | ++hw_consequtive_generic_errors_; |
| 199 | } |
| 200 | if (hw_consequtive_generic_errors_ < kMaxConsequtiveHwErrors) { |
| 201 | return ret; |
| 202 | } |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | // HW decoder returned WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE or |
Ilya Nikolaevskiy | 35fc153 | 2020-05-15 12:25:59 | [diff] [blame] | 206 | // too many generic errors on key-frames encountered. |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 207 | if (!InitFallbackDecoder()) { |
| 208 | return ret; |
| 209 | } |
| 210 | |
| 211 | // Fallback decoder initialized, fall-through. |
Danil Chapovalov | 46cc32d | 2022-01-17 13:41:13 | [diff] [blame] | 212 | [[fallthrough]]; |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 213 | } |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 214 | case DecoderType::kFallback: |
| 215 | return fallback_decoder_->Decode(input_image, missing_frames, |
Niels Möller | 7aacdd9 | 2019-03-25 08:11:40 | [diff] [blame] | 216 | render_time_ms); |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 217 | default: |
Artem Titov | d325196 | 2021-11-15 15:57:07 | [diff] [blame] | 218 | RTC_DCHECK_NOTREACHED(); |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 219 | return WEBRTC_VIDEO_CODEC_ERROR; |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 220 | } |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 221 | } |
| 222 | |
| 223 | int32_t VideoDecoderSoftwareFallbackWrapper::RegisterDecodeCompleteCallback( |
| 224 | DecodedImageCallback* callback) { |
| 225 | callback_ = callback; |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 226 | return active_decoder().RegisterDecodeCompleteCallback(callback); |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 227 | } |
| 228 | |
| 229 | int32_t VideoDecoderSoftwareFallbackWrapper::Release() { |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 230 | int32_t status; |
| 231 | switch (decoder_type_) { |
| 232 | case DecoderType::kHardware: |
| 233 | status = hw_decoder_->Release(); |
| 234 | break; |
| 235 | case DecoderType::kFallback: |
| 236 | RTC_LOG(LS_INFO) << "Releasing software fallback decoder."; |
| 237 | status = fallback_decoder_->Release(); |
| 238 | break; |
| 239 | case DecoderType::kNone: |
| 240 | status = WEBRTC_VIDEO_CODEC_OK; |
| 241 | break; |
| 242 | default: |
Artem Titov | d325196 | 2021-11-15 15:57:07 | [diff] [blame] | 243 | RTC_DCHECK_NOTREACHED(); |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 244 | status = WEBRTC_VIDEO_CODEC_ERROR; |
Peter Boström | f812e45 | 2017-02-13 22:11:08 | [diff] [blame] | 245 | } |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 246 | |
| 247 | decoder_type_ = DecoderType::kNone; |
| 248 | return status; |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 249 | } |
| 250 | |
Erik Språng | c12f625 | 2021-01-13 20:49:59 | [diff] [blame] | 251 | VideoDecoder::DecoderInfo VideoDecoderSoftwareFallbackWrapper::GetDecoderInfo() |
| 252 | const { |
| 253 | DecoderInfo info = active_decoder().GetDecoderInfo(); |
| 254 | if (decoder_type_ == DecoderType::kFallback) { |
| 255 | // Cached "A (fallback from B)" string. |
| 256 | info.implementation_name = fallback_implementation_name_; |
| 257 | } |
| 258 | return info; |
| 259 | } |
| 260 | |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 261 | const char* VideoDecoderSoftwareFallbackWrapper::ImplementationName() const { |
Erik Språng | c12f625 | 2021-01-13 20:49:59 | [diff] [blame] | 262 | if (decoder_type_ == DecoderType::kFallback) { |
| 263 | // Cached "A (fallback from B)" string. |
| 264 | return fallback_implementation_name_.c_str(); |
| 265 | } else { |
| 266 | return hw_decoder_->ImplementationName(); |
| 267 | } |
Sami Kalliomäki | 99f52f8 | 2018-02-23 08:05:49 | [diff] [blame] | 268 | } |
| 269 | |
| 270 | VideoDecoder& VideoDecoderSoftwareFallbackWrapper::active_decoder() const { |
| 271 | return decoder_type_ == DecoderType::kFallback ? *fallback_decoder_ |
| 272 | : *hw_decoder_; |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 273 | } |
| 274 | |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 275 | } // namespace |
| 276 | |
| 277 | std::unique_ptr<VideoDecoder> CreateVideoDecoderSoftwareFallbackWrapper( |
| 278 | std::unique_ptr<VideoDecoder> sw_fallback_decoder, |
| 279 | std::unique_ptr<VideoDecoder> hw_decoder) { |
Mirko Bonadei | 317a1f0 | 2019-09-17 15:06:18 | [diff] [blame] | 280 | return std::make_unique<VideoDecoderSoftwareFallbackWrapper>( |
Anders Carlsson | dd3e0ab | 2018-06-12 09:15:56 | [diff] [blame] | 281 | std::move(sw_fallback_decoder), std::move(hw_decoder)); |
| 282 | } |
| 283 | |
magjed | f6acc2a | 2016-11-22 09:43:03 | [diff] [blame] | 284 | } // namespace webrtc |