blob: daee90ef85031579cd60a747926879ebbef5ceed [file] [log] [blame]
magjed614d5b72016-11-15 06:30:54 -08001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "media/engine/videoencodersoftwarefallbackwrapper.h"
magjed614d5b72016-11-15 06:30:54 -080012
Steve Antone78bcb92017-10-31 09:53:08 -070013#include <utility>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "media/base/h264_profile_level_id.h"
16#include "media/engine/internalencoderfactory.h"
17#include "modules/video_coding/include/video_error_codes.h"
18#include "rtc_base/checks.h"
19#include "rtc_base/logging.h"
20#include "rtc_base/timeutils.h"
21#include "system_wrappers/include/field_trial.h"
magjed614d5b72016-11-15 06:30:54 -080022
23namespace webrtc {
asapersson22c76c42017-08-16 00:53:59 -070024namespace {
25const char kVp8ForceFallbackEncoderFieldTrial[] =
26 "WebRTC-VP8-Forced-Fallback-Encoder";
27
28bool EnableForcedFallback(const cricket::VideoCodec& codec) {
29 if (!webrtc::field_trial::IsEnabled(kVp8ForceFallbackEncoderFieldTrial))
30 return false;
31
kthelgason1cdddc92017-08-24 03:52:48 -070032 return (PayloadStringToCodecType(codec.name) == kVideoCodecVP8);
asapersson22c76c42017-08-16 00:53:59 -070033}
34
35bool IsForcedFallbackPossible(const VideoCodec& codec_settings) {
36 return codec_settings.codecType == kVideoCodecVP8 &&
37 codec_settings.numberOfSimulcastStreams <= 1 &&
38 codec_settings.VP8().numberOfTemporalLayers == 1;
39}
40
41void GetForcedFallbackParamsFromFieldTrialGroup(uint32_t* param_low_kbps,
42 uint32_t* param_high_kbps,
43 int64_t* param_min_low_ms) {
44 RTC_DCHECK(param_low_kbps);
45 RTC_DCHECK(param_high_kbps);
46 RTC_DCHECK(param_min_low_ms);
47 std::string group =
48 webrtc::field_trial::FindFullName(kVp8ForceFallbackEncoderFieldTrial);
49 if (group.empty())
50 return;
51
52 int low_kbps;
53 int high_kbps;
54 int min_low_ms;
asapersson142fcc92017-08-17 08:58:54 -070055 int min_pixels;
56 if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d", &low_kbps, &high_kbps,
57 &min_low_ms, &min_pixels) != 4) {
asapersson22c76c42017-08-16 00:53:59 -070058 LOG(LS_WARNING) << "Invalid number of forced fallback parameters provided.";
59 return;
60 }
asapersson142fcc92017-08-17 08:58:54 -070061 if (min_low_ms <= 0 || min_pixels <= 0 || low_kbps <= 0 ||
62 high_kbps <= low_kbps) {
asapersson22c76c42017-08-16 00:53:59 -070063 LOG(LS_WARNING) << "Invalid forced fallback parameter value provided.";
64 return;
65 }
66 *param_low_kbps = low_kbps;
67 *param_high_kbps = high_kbps;
68 *param_min_low_ms = min_low_ms;
69}
70} // namespace
magjed614d5b72016-11-15 06:30:54 -080071
72VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
magjed509e4fe2016-11-18 01:34:11 -080073 const cricket::VideoCodec& codec,
magjedf52d34d2017-08-29 00:58:52 -070074 std::unique_ptr<webrtc::VideoEncoder> encoder)
Erik Språng08127a92016-11-16 16:41:30 +010075 : number_of_cores_(0),
76 max_payload_size_(0),
77 rates_set_(false),
78 framerate_(0),
magjed614d5b72016-11-15 06:30:54 -080079 channel_parameters_set_(false),
Erik Språng08127a92016-11-16 16:41:30 +010080 packet_loss_(0),
81 rtt_(0),
magjed509e4fe2016-11-18 01:34:11 -080082 codec_(codec),
magjedf52d34d2017-08-29 00:58:52 -070083 encoder_(std::move(encoder)),
asapersson22c76c42017-08-16 00:53:59 -070084 callback_(nullptr),
85 forced_fallback_possible_(EnableForcedFallback(codec)) {
86 if (forced_fallback_possible_) {
87 GetForcedFallbackParamsFromFieldTrialGroup(&forced_fallback_.low_kbps,
88 &forced_fallback_.high_kbps,
89 &forced_fallback_.min_low_ms);
90 }
91}
magjed614d5b72016-11-15 06:30:54 -080092
93bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder() {
brandtr955d7f12017-08-24 05:19:57 -070094 LOG(LS_WARNING) << "Encoder falling back to software encoding.";
emircan82fac892017-08-23 14:19:50 -070095 MaybeModifyCodecForFallback();
magjed509e4fe2016-11-18 01:34:11 -080096 cricket::InternalEncoderFactory internal_factory;
97 if (!FindMatchingCodec(internal_factory.supported_codecs(), codec_)) {
magjed614d5b72016-11-15 06:30:54 -080098 LOG(LS_WARNING)
99 << "Encoder requesting fallback to codec not supported in software.";
100 return false;
101 }
magjed509e4fe2016-11-18 01:34:11 -0800102 fallback_encoder_.reset(internal_factory.CreateVideoEncoder(codec_));
magjed614d5b72016-11-15 06:30:54 -0800103 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
104 max_payload_size_) !=
105 WEBRTC_VIDEO_CODEC_OK) {
106 LOG(LS_ERROR) << "Failed to initialize software-encoder fallback.";
107 fallback_encoder_->Release();
108 fallback_encoder_.reset();
109 return false;
110 }
111 // Replay callback, rates, and channel parameters.
112 if (callback_)
113 fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
114 if (rates_set_)
Erik Språng08127a92016-11-16 16:41:30 +0100115 fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate_);
magjed614d5b72016-11-15 06:30:54 -0800116 if (channel_parameters_set_)
117 fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
118
119 fallback_implementation_name_ =
120 std::string(fallback_encoder_->ImplementationName()) +
121 " (fallback from: " + encoder_->ImplementationName() + ")";
122 // Since we're switching to the fallback encoder, Release the real encoder. It
123 // may be re-initialized via InitEncode later, and it will continue to get
124 // Set calls for rates and channel parameters in the meantime.
125 encoder_->Release();
126 return true;
127}
128
129int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
130 const VideoCodec* codec_settings,
131 int32_t number_of_cores,
132 size_t max_payload_size) {
133 // Store settings, in case we need to dynamically switch to the fallback
134 // encoder after a failed Encode call.
135 codec_settings_ = *codec_settings;
136 number_of_cores_ = number_of_cores;
137 max_payload_size_ = max_payload_size;
138 // Clear stored rate/channel parameters.
139 rates_set_ = false;
140 channel_parameters_set_ = false;
asapersson22c76c42017-08-16 00:53:59 -0700141 ValidateSettingsForForcedFallback();
142
143 // Try to reinit forced software codec if it is in use.
144 if (TryReInitForcedFallbackEncoder()) {
145 return WEBRTC_VIDEO_CODEC_OK;
146 }
147 forced_fallback_.Reset();
magjed614d5b72016-11-15 06:30:54 -0800148
149 int32_t ret =
150 encoder_->InitEncode(codec_settings, number_of_cores, max_payload_size);
magjed509e4fe2016-11-18 01:34:11 -0800151 if (ret == WEBRTC_VIDEO_CODEC_OK || codec_.name.empty()) {
brandtr955d7f12017-08-24 05:19:57 -0700152 if (fallback_encoder_) {
153 LOG(LS_WARNING)
154 << "InitEncode OK, no longer using the software fallback encoder.";
magjed614d5b72016-11-15 06:30:54 -0800155 fallback_encoder_->Release();
brandtr955d7f12017-08-24 05:19:57 -0700156 }
magjed614d5b72016-11-15 06:30:54 -0800157 fallback_encoder_.reset();
158 if (callback_)
159 encoder_->RegisterEncodeCompleteCallback(callback_);
160 return ret;
161 }
162 // Try to instantiate software codec.
163 if (InitFallbackEncoder()) {
164 return WEBRTC_VIDEO_CODEC_OK;
165 }
166 // Software encoder failed, use original return code.
167 return ret;
168}
169
170int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback(
171 EncodedImageCallback* callback) {
172 callback_ = callback;
173 int32_t ret = encoder_->RegisterEncodeCompleteCallback(callback);
174 if (fallback_encoder_)
175 return fallback_encoder_->RegisterEncodeCompleteCallback(callback);
176 return ret;
177}
178
179int32_t VideoEncoderSoftwareFallbackWrapper::Release() {
180 // If the fallback_encoder_ is non-null, it means it was created via
181 // InitFallbackEncoder which has Release()d encoder_, so we should only ever
182 // need to Release() whichever one is active.
183 if (fallback_encoder_)
184 return fallback_encoder_->Release();
185 return encoder_->Release();
186}
187
188int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
189 const VideoFrame& frame,
190 const CodecSpecificInfo* codec_specific_info,
191 const std::vector<FrameType>* frame_types) {
asapersson22c76c42017-08-16 00:53:59 -0700192 if (TryReleaseForcedFallbackEncoder()) {
193 // Frame may have been converted from kNative to kI420 during fallback.
194 if (encoder_->SupportsNativeHandle() &&
195 frame.video_frame_buffer()->type() != VideoFrameBuffer::Type::kNative) {
196 LOG(LS_WARNING) << "Encoder supports native frames, dropping one frame "
197 << "to avoid possible reconfig due to format change.";
198 return WEBRTC_VIDEO_CODEC_ERROR;
199 }
200 }
magjed614d5b72016-11-15 06:30:54 -0800201 if (fallback_encoder_)
202 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
203 int32_t ret = encoder_->Encode(frame, codec_specific_info, frame_types);
204 // If requested, try a software fallback.
asapersson22c76c42017-08-16 00:53:59 -0700205 bool fallback_requested =
206 (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) ||
207 (ret == WEBRTC_VIDEO_CODEC_OK && RequestForcedFallback());
208 if (fallback_requested && InitFallbackEncoder()) {
209 // Fallback was successful.
210 if (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE)
211 forced_fallback_.Reset(); // Not a forced fallback.
Magnus Jedvert7a721e82017-06-14 11:28:08 +0200212 if (frame.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative &&
magjed614d5b72016-11-15 06:30:54 -0800213 !fallback_encoder_->SupportsNativeHandle()) {
214 LOG(LS_WARNING) << "Fallback encoder doesn't support native frames, "
215 << "dropping one frame.";
216 return WEBRTC_VIDEO_CODEC_ERROR;
217 }
218
asapersson22c76c42017-08-16 00:53:59 -0700219 // Start using the fallback with this frame.
magjed614d5b72016-11-15 06:30:54 -0800220 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
221 }
222 return ret;
223}
224
225int32_t VideoEncoderSoftwareFallbackWrapper::SetChannelParameters(
226 uint32_t packet_loss,
227 int64_t rtt) {
228 channel_parameters_set_ = true;
229 packet_loss_ = packet_loss;
230 rtt_ = rtt;
231 int32_t ret = encoder_->SetChannelParameters(packet_loss, rtt);
232 if (fallback_encoder_)
233 return fallback_encoder_->SetChannelParameters(packet_loss, rtt);
234 return ret;
235}
236
Erik Språng08127a92016-11-16 16:41:30 +0100237int32_t VideoEncoderSoftwareFallbackWrapper::SetRateAllocation(
238 const BitrateAllocation& bitrate_allocation,
239 uint32_t framerate) {
magjed614d5b72016-11-15 06:30:54 -0800240 rates_set_ = true;
Erik Språng08127a92016-11-16 16:41:30 +0100241 bitrate_allocation_ = bitrate_allocation;
magjed614d5b72016-11-15 06:30:54 -0800242 framerate_ = framerate;
Erik Språng08127a92016-11-16 16:41:30 +0100243 int32_t ret = encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800244 if (fallback_encoder_)
Erik Språng08127a92016-11-16 16:41:30 +0100245 return fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800246 return ret;
247}
248
magjed614d5b72016-11-15 06:30:54 -0800249bool VideoEncoderSoftwareFallbackWrapper::SupportsNativeHandle() const {
250 if (fallback_encoder_)
251 return fallback_encoder_->SupportsNativeHandle();
252 return encoder_->SupportsNativeHandle();
253}
254
kthelgason876222f2016-11-29 01:44:11 -0800255VideoEncoder::ScalingSettings
256VideoEncoderSoftwareFallbackWrapper::GetScalingSettings() const {
asapersson142fcc92017-08-17 08:58:54 -0700257 if (forced_fallback_possible_ && fallback_encoder_)
258 return fallback_encoder_->GetScalingSettings();
kthelgason876222f2016-11-29 01:44:11 -0800259 return encoder_->GetScalingSettings();
260}
261
kthelgason535dbd32017-01-26 00:36:31 -0800262const char *VideoEncoderSoftwareFallbackWrapper::ImplementationName() const {
263 if (fallback_encoder_)
264 return fallback_encoder_->ImplementationName();
265 return encoder_->ImplementationName();
266}
267
asapersson22c76c42017-08-16 00:53:59 -0700268bool VideoEncoderSoftwareFallbackWrapper::IsForcedFallbackActive() const {
269 return (forced_fallback_possible_ && fallback_encoder_ &&
270 forced_fallback_.start_ms);
271}
272
273bool VideoEncoderSoftwareFallbackWrapper::RequestForcedFallback() {
274 if (!forced_fallback_possible_ || fallback_encoder_ || !rates_set_)
275 return false;
276
277 // No fallback encoder.
278 return forced_fallback_.ShouldStart(bitrate_allocation_.get_sum_kbps(),
279 codec_settings_);
280}
281
282bool VideoEncoderSoftwareFallbackWrapper::TryReleaseForcedFallbackEncoder() {
283 if (!IsForcedFallbackActive())
284 return false;
285
asapersson142fcc92017-08-17 08:58:54 -0700286 if (!forced_fallback_.ShouldStop(bitrate_allocation_.get_sum_kbps(),
287 codec_settings_)) {
asapersson22c76c42017-08-16 00:53:59 -0700288 return false;
asapersson142fcc92017-08-17 08:58:54 -0700289 }
asapersson22c76c42017-08-16 00:53:59 -0700290
291 // Release the forced fallback encoder.
292 if (encoder_->InitEncode(&codec_settings_, number_of_cores_,
293 max_payload_size_) == WEBRTC_VIDEO_CODEC_OK) {
294 LOG(LS_INFO) << "Stop forced SW encoder fallback, max bitrate exceeded.";
295 fallback_encoder_->Release();
296 fallback_encoder_.reset();
297 forced_fallback_.Reset();
298 return true;
299 }
300 return false;
301}
302
303bool VideoEncoderSoftwareFallbackWrapper::TryReInitForcedFallbackEncoder() {
304 if (!IsForcedFallbackActive())
305 return false;
306
307 // Encoder reconfigured.
308 if (!forced_fallback_.IsValid(codec_settings_)) {
309 LOG(LS_INFO) << "Stop forced SW encoder fallback, max pixels exceeded.";
310 return false;
311 }
312 // Settings valid, reinitialize the forced fallback encoder.
313 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
314 max_payload_size_) !=
315 WEBRTC_VIDEO_CODEC_OK) {
316 LOG(LS_ERROR) << "Failed to init forced SW encoder fallback.";
317 return false;
318 }
319 return true;
320}
321
322void VideoEncoderSoftwareFallbackWrapper::ValidateSettingsForForcedFallback() {
323 if (!forced_fallback_possible_)
324 return;
325
326 if (!IsForcedFallbackPossible(codec_settings_)) {
327 if (IsForcedFallbackActive()) {
328 fallback_encoder_->Release();
329 fallback_encoder_.reset();
330 }
331 LOG(LS_INFO) << "Disable forced_fallback_possible_ due to settings.";
332 forced_fallback_possible_ = false;
333 }
334}
335
336bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStart(
337 uint32_t bitrate_kbps,
338 const VideoCodec& codec) {
339 if (bitrate_kbps > low_kbps || !IsValid(codec)) {
340 start_ms.reset();
341 return false;
342 }
343
344 // Has bitrate been below |low_kbps| for long enough duration.
345 int64_t now_ms = rtc::TimeMillis();
346 if (!start_ms)
347 start_ms.emplace(now_ms);
348
349 if ((now_ms - *start_ms) >= min_low_ms) {
350 LOG(LS_INFO) << "Request forced SW encoder fallback.";
351 // In case the request fails, update time to avoid too frequent requests.
352 start_ms.emplace(now_ms);
353 return true;
354 }
355 return false;
356}
357
358bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStop(
asapersson142fcc92017-08-17 08:58:54 -0700359 uint32_t bitrate_kbps,
360 const VideoCodec& codec) const {
361 return bitrate_kbps >= high_kbps &&
362 (codec.width * codec.height >= kMinPixelsStop);
asapersson22c76c42017-08-16 00:53:59 -0700363}
364
emircan82fac892017-08-23 14:19:50 -0700365void VideoEncoderSoftwareFallbackWrapper::MaybeModifyCodecForFallback() {
366 // We have a specific case for H264 ConstrainedBaseline because that is the
367 // only supported profile in Sw fallback.
368 if (!cricket::CodecNamesEq(codec_.name.c_str(), cricket::kH264CodecName))
369 return;
370 codec_.SetParam(cricket::kH264FmtpProfileLevelId,
371 cricket::kH264ProfileLevelConstrainedBaseline);
372}
373
magjed614d5b72016-11-15 06:30:54 -0800374} // namespace webrtc