blob: 540597838a9058bd187f76eac5bfc9d6baf9aafc [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
11#include "webrtc/media/engine/videoencodersoftwarefallbackwrapper.h"
12
emircan82fac892017-08-23 14:19:50 -070013#include "webrtc/media/base/h264_profile_level_id.h"
magjed509e4fe2016-11-18 01:34:11 -080014#include "webrtc/media/engine/internalencoderfactory.h"
magjed614d5b72016-11-15 06:30:54 -080015#include "webrtc/modules/video_coding/include/video_error_codes.h"
asapersson22c76c42017-08-16 00:53:59 -070016#include "webrtc/rtc_base/checks.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020017#include "webrtc/rtc_base/logging.h"
asapersson22c76c42017-08-16 00:53:59 -070018#include "webrtc/rtc_base/timeutils.h"
19#include "webrtc/system_wrappers/include/field_trial.h"
magjed614d5b72016-11-15 06:30:54 -080020
21namespace webrtc {
asapersson22c76c42017-08-16 00:53:59 -070022namespace {
23const char kVp8ForceFallbackEncoderFieldTrial[] =
24 "WebRTC-VP8-Forced-Fallback-Encoder";
25
26bool EnableForcedFallback(const cricket::VideoCodec& codec) {
27 if (!webrtc::field_trial::IsEnabled(kVp8ForceFallbackEncoderFieldTrial))
28 return false;
29
30 return (PayloadNameToCodecType(codec.name).value_or(kVideoCodecUnknown) ==
31 kVideoCodecVP8);
32}
33
34bool IsForcedFallbackPossible(const VideoCodec& codec_settings) {
35 return codec_settings.codecType == kVideoCodecVP8 &&
36 codec_settings.numberOfSimulcastStreams <= 1 &&
37 codec_settings.VP8().numberOfTemporalLayers == 1;
38}
39
40void GetForcedFallbackParamsFromFieldTrialGroup(uint32_t* param_low_kbps,
41 uint32_t* param_high_kbps,
42 int64_t* param_min_low_ms) {
43 RTC_DCHECK(param_low_kbps);
44 RTC_DCHECK(param_high_kbps);
45 RTC_DCHECK(param_min_low_ms);
46 std::string group =
47 webrtc::field_trial::FindFullName(kVp8ForceFallbackEncoderFieldTrial);
48 if (group.empty())
49 return;
50
51 int low_kbps;
52 int high_kbps;
53 int min_low_ms;
asapersson142fcc92017-08-17 08:58:54 -070054 int min_pixels;
55 if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d", &low_kbps, &high_kbps,
56 &min_low_ms, &min_pixels) != 4) {
asapersson22c76c42017-08-16 00:53:59 -070057 LOG(LS_WARNING) << "Invalid number of forced fallback parameters provided.";
58 return;
59 }
asapersson142fcc92017-08-17 08:58:54 -070060 if (min_low_ms <= 0 || min_pixels <= 0 || low_kbps <= 0 ||
61 high_kbps <= low_kbps) {
asapersson22c76c42017-08-16 00:53:59 -070062 LOG(LS_WARNING) << "Invalid forced fallback parameter value provided.";
63 return;
64 }
65 *param_low_kbps = low_kbps;
66 *param_high_kbps = high_kbps;
67 *param_min_low_ms = min_low_ms;
68}
69} // namespace
magjed614d5b72016-11-15 06:30:54 -080070
71VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
magjed509e4fe2016-11-18 01:34:11 -080072 const cricket::VideoCodec& codec,
magjed614d5b72016-11-15 06:30:54 -080073 webrtc::VideoEncoder* encoder)
Erik Språng08127a92016-11-16 16:41:30 +010074 : number_of_cores_(0),
75 max_payload_size_(0),
76 rates_set_(false),
77 framerate_(0),
magjed614d5b72016-11-15 06:30:54 -080078 channel_parameters_set_(false),
Erik Språng08127a92016-11-16 16:41:30 +010079 packet_loss_(0),
80 rtt_(0),
magjed509e4fe2016-11-18 01:34:11 -080081 codec_(codec),
magjed614d5b72016-11-15 06:30:54 -080082 encoder_(encoder),
asapersson22c76c42017-08-16 00:53:59 -070083 callback_(nullptr),
84 forced_fallback_possible_(EnableForcedFallback(codec)) {
85 if (forced_fallback_possible_) {
86 GetForcedFallbackParamsFromFieldTrialGroup(&forced_fallback_.low_kbps,
87 &forced_fallback_.high_kbps,
88 &forced_fallback_.min_low_ms);
89 }
90}
magjed614d5b72016-11-15 06:30:54 -080091
92bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder() {
emircan82fac892017-08-23 14:19:50 -070093 MaybeModifyCodecForFallback();
magjed509e4fe2016-11-18 01:34:11 -080094 cricket::InternalEncoderFactory internal_factory;
95 if (!FindMatchingCodec(internal_factory.supported_codecs(), codec_)) {
magjed614d5b72016-11-15 06:30:54 -080096 LOG(LS_WARNING)
97 << "Encoder requesting fallback to codec not supported in software.";
98 return false;
99 }
magjed509e4fe2016-11-18 01:34:11 -0800100 fallback_encoder_.reset(internal_factory.CreateVideoEncoder(codec_));
magjed614d5b72016-11-15 06:30:54 -0800101 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
102 max_payload_size_) !=
103 WEBRTC_VIDEO_CODEC_OK) {
104 LOG(LS_ERROR) << "Failed to initialize software-encoder fallback.";
105 fallback_encoder_->Release();
106 fallback_encoder_.reset();
107 return false;
108 }
109 // Replay callback, rates, and channel parameters.
110 if (callback_)
111 fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
112 if (rates_set_)
Erik Språng08127a92016-11-16 16:41:30 +0100113 fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate_);
magjed614d5b72016-11-15 06:30:54 -0800114 if (channel_parameters_set_)
115 fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
116
117 fallback_implementation_name_ =
118 std::string(fallback_encoder_->ImplementationName()) +
119 " (fallback from: " + encoder_->ImplementationName() + ")";
120 // Since we're switching to the fallback encoder, Release the real encoder. It
121 // may be re-initialized via InitEncode later, and it will continue to get
122 // Set calls for rates and channel parameters in the meantime.
123 encoder_->Release();
124 return true;
125}
126
127int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
128 const VideoCodec* codec_settings,
129 int32_t number_of_cores,
130 size_t max_payload_size) {
131 // Store settings, in case we need to dynamically switch to the fallback
132 // encoder after a failed Encode call.
133 codec_settings_ = *codec_settings;
134 number_of_cores_ = number_of_cores;
135 max_payload_size_ = max_payload_size;
136 // Clear stored rate/channel parameters.
137 rates_set_ = false;
138 channel_parameters_set_ = false;
asapersson22c76c42017-08-16 00:53:59 -0700139 ValidateSettingsForForcedFallback();
140
141 // Try to reinit forced software codec if it is in use.
142 if (TryReInitForcedFallbackEncoder()) {
143 return WEBRTC_VIDEO_CODEC_OK;
144 }
145 forced_fallback_.Reset();
magjed614d5b72016-11-15 06:30:54 -0800146
147 int32_t ret =
148 encoder_->InitEncode(codec_settings, number_of_cores, max_payload_size);
magjed509e4fe2016-11-18 01:34:11 -0800149 if (ret == WEBRTC_VIDEO_CODEC_OK || codec_.name.empty()) {
magjed614d5b72016-11-15 06:30:54 -0800150 if (fallback_encoder_)
151 fallback_encoder_->Release();
152 fallback_encoder_.reset();
153 if (callback_)
154 encoder_->RegisterEncodeCompleteCallback(callback_);
155 return ret;
156 }
157 // Try to instantiate software codec.
158 if (InitFallbackEncoder()) {
159 return WEBRTC_VIDEO_CODEC_OK;
160 }
161 // Software encoder failed, use original return code.
162 return ret;
163}
164
165int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback(
166 EncodedImageCallback* callback) {
167 callback_ = callback;
168 int32_t ret = encoder_->RegisterEncodeCompleteCallback(callback);
169 if (fallback_encoder_)
170 return fallback_encoder_->RegisterEncodeCompleteCallback(callback);
171 return ret;
172}
173
174int32_t VideoEncoderSoftwareFallbackWrapper::Release() {
175 // If the fallback_encoder_ is non-null, it means it was created via
176 // InitFallbackEncoder which has Release()d encoder_, so we should only ever
177 // need to Release() whichever one is active.
178 if (fallback_encoder_)
179 return fallback_encoder_->Release();
180 return encoder_->Release();
181}
182
183int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
184 const VideoFrame& frame,
185 const CodecSpecificInfo* codec_specific_info,
186 const std::vector<FrameType>* frame_types) {
asapersson22c76c42017-08-16 00:53:59 -0700187 if (TryReleaseForcedFallbackEncoder()) {
188 // Frame may have been converted from kNative to kI420 during fallback.
189 if (encoder_->SupportsNativeHandle() &&
190 frame.video_frame_buffer()->type() != VideoFrameBuffer::Type::kNative) {
191 LOG(LS_WARNING) << "Encoder supports native frames, dropping one frame "
192 << "to avoid possible reconfig due to format change.";
193 return WEBRTC_VIDEO_CODEC_ERROR;
194 }
195 }
magjed614d5b72016-11-15 06:30:54 -0800196 if (fallback_encoder_)
197 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
198 int32_t ret = encoder_->Encode(frame, codec_specific_info, frame_types);
199 // If requested, try a software fallback.
asapersson22c76c42017-08-16 00:53:59 -0700200 bool fallback_requested =
201 (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) ||
202 (ret == WEBRTC_VIDEO_CODEC_OK && RequestForcedFallback());
203 if (fallback_requested && InitFallbackEncoder()) {
204 // Fallback was successful.
205 if (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE)
206 forced_fallback_.Reset(); // Not a forced fallback.
Magnus Jedvert7a721e82017-06-14 11:28:08 +0200207 if (frame.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative &&
magjed614d5b72016-11-15 06:30:54 -0800208 !fallback_encoder_->SupportsNativeHandle()) {
209 LOG(LS_WARNING) << "Fallback encoder doesn't support native frames, "
210 << "dropping one frame.";
211 return WEBRTC_VIDEO_CODEC_ERROR;
212 }
213
asapersson22c76c42017-08-16 00:53:59 -0700214 // Start using the fallback with this frame.
magjed614d5b72016-11-15 06:30:54 -0800215 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
216 }
217 return ret;
218}
219
220int32_t VideoEncoderSoftwareFallbackWrapper::SetChannelParameters(
221 uint32_t packet_loss,
222 int64_t rtt) {
223 channel_parameters_set_ = true;
224 packet_loss_ = packet_loss;
225 rtt_ = rtt;
226 int32_t ret = encoder_->SetChannelParameters(packet_loss, rtt);
227 if (fallback_encoder_)
228 return fallback_encoder_->SetChannelParameters(packet_loss, rtt);
229 return ret;
230}
231
Erik Språng08127a92016-11-16 16:41:30 +0100232int32_t VideoEncoderSoftwareFallbackWrapper::SetRateAllocation(
233 const BitrateAllocation& bitrate_allocation,
234 uint32_t framerate) {
magjed614d5b72016-11-15 06:30:54 -0800235 rates_set_ = true;
Erik Språng08127a92016-11-16 16:41:30 +0100236 bitrate_allocation_ = bitrate_allocation;
magjed614d5b72016-11-15 06:30:54 -0800237 framerate_ = framerate;
Erik Språng08127a92016-11-16 16:41:30 +0100238 int32_t ret = encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800239 if (fallback_encoder_)
Erik Språng08127a92016-11-16 16:41:30 +0100240 return fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800241 return ret;
242}
243
magjed614d5b72016-11-15 06:30:54 -0800244bool VideoEncoderSoftwareFallbackWrapper::SupportsNativeHandle() const {
245 if (fallback_encoder_)
246 return fallback_encoder_->SupportsNativeHandle();
247 return encoder_->SupportsNativeHandle();
248}
249
kthelgason876222f2016-11-29 01:44:11 -0800250VideoEncoder::ScalingSettings
251VideoEncoderSoftwareFallbackWrapper::GetScalingSettings() const {
asapersson142fcc92017-08-17 08:58:54 -0700252 if (forced_fallback_possible_ && fallback_encoder_)
253 return fallback_encoder_->GetScalingSettings();
kthelgason876222f2016-11-29 01:44:11 -0800254 return encoder_->GetScalingSettings();
255}
256
kthelgason535dbd32017-01-26 00:36:31 -0800257const char *VideoEncoderSoftwareFallbackWrapper::ImplementationName() const {
258 if (fallback_encoder_)
259 return fallback_encoder_->ImplementationName();
260 return encoder_->ImplementationName();
261}
262
asapersson22c76c42017-08-16 00:53:59 -0700263bool VideoEncoderSoftwareFallbackWrapper::IsForcedFallbackActive() const {
264 return (forced_fallback_possible_ && fallback_encoder_ &&
265 forced_fallback_.start_ms);
266}
267
268bool VideoEncoderSoftwareFallbackWrapper::RequestForcedFallback() {
269 if (!forced_fallback_possible_ || fallback_encoder_ || !rates_set_)
270 return false;
271
272 // No fallback encoder.
273 return forced_fallback_.ShouldStart(bitrate_allocation_.get_sum_kbps(),
274 codec_settings_);
275}
276
277bool VideoEncoderSoftwareFallbackWrapper::TryReleaseForcedFallbackEncoder() {
278 if (!IsForcedFallbackActive())
279 return false;
280
asapersson142fcc92017-08-17 08:58:54 -0700281 if (!forced_fallback_.ShouldStop(bitrate_allocation_.get_sum_kbps(),
282 codec_settings_)) {
asapersson22c76c42017-08-16 00:53:59 -0700283 return false;
asapersson142fcc92017-08-17 08:58:54 -0700284 }
asapersson22c76c42017-08-16 00:53:59 -0700285
286 // Release the forced fallback encoder.
287 if (encoder_->InitEncode(&codec_settings_, number_of_cores_,
288 max_payload_size_) == WEBRTC_VIDEO_CODEC_OK) {
289 LOG(LS_INFO) << "Stop forced SW encoder fallback, max bitrate exceeded.";
290 fallback_encoder_->Release();
291 fallback_encoder_.reset();
292 forced_fallback_.Reset();
293 return true;
294 }
295 return false;
296}
297
298bool VideoEncoderSoftwareFallbackWrapper::TryReInitForcedFallbackEncoder() {
299 if (!IsForcedFallbackActive())
300 return false;
301
302 // Encoder reconfigured.
303 if (!forced_fallback_.IsValid(codec_settings_)) {
304 LOG(LS_INFO) << "Stop forced SW encoder fallback, max pixels exceeded.";
305 return false;
306 }
307 // Settings valid, reinitialize the forced fallback encoder.
308 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
309 max_payload_size_) !=
310 WEBRTC_VIDEO_CODEC_OK) {
311 LOG(LS_ERROR) << "Failed to init forced SW encoder fallback.";
312 return false;
313 }
314 return true;
315}
316
317void VideoEncoderSoftwareFallbackWrapper::ValidateSettingsForForcedFallback() {
318 if (!forced_fallback_possible_)
319 return;
320
321 if (!IsForcedFallbackPossible(codec_settings_)) {
322 if (IsForcedFallbackActive()) {
323 fallback_encoder_->Release();
324 fallback_encoder_.reset();
325 }
326 LOG(LS_INFO) << "Disable forced_fallback_possible_ due to settings.";
327 forced_fallback_possible_ = false;
328 }
329}
330
331bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStart(
332 uint32_t bitrate_kbps,
333 const VideoCodec& codec) {
334 if (bitrate_kbps > low_kbps || !IsValid(codec)) {
335 start_ms.reset();
336 return false;
337 }
338
339 // Has bitrate been below |low_kbps| for long enough duration.
340 int64_t now_ms = rtc::TimeMillis();
341 if (!start_ms)
342 start_ms.emplace(now_ms);
343
344 if ((now_ms - *start_ms) >= min_low_ms) {
345 LOG(LS_INFO) << "Request forced SW encoder fallback.";
346 // In case the request fails, update time to avoid too frequent requests.
347 start_ms.emplace(now_ms);
348 return true;
349 }
350 return false;
351}
352
353bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStop(
asapersson142fcc92017-08-17 08:58:54 -0700354 uint32_t bitrate_kbps,
355 const VideoCodec& codec) const {
356 return bitrate_kbps >= high_kbps &&
357 (codec.width * codec.height >= kMinPixelsStop);
asapersson22c76c42017-08-16 00:53:59 -0700358}
359
emircan82fac892017-08-23 14:19:50 -0700360void VideoEncoderSoftwareFallbackWrapper::MaybeModifyCodecForFallback() {
361 // We have a specific case for H264 ConstrainedBaseline because that is the
362 // only supported profile in Sw fallback.
363 if (!cricket::CodecNamesEq(codec_.name.c_str(), cricket::kH264CodecName))
364 return;
365 codec_.SetParam(cricket::kH264FmtpProfileLevelId,
366 cricket::kH264ProfileLevelConstrainedBaseline);
367}
368
magjed614d5b72016-11-15 06:30:54 -0800369} // namespace webrtc