blob: 9f5dedabc0451820b3990362720c508c2d6ecf3a [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
magjed509e4fe2016-11-18 01:34:11 -080013#include "webrtc/media/engine/internalencoderfactory.h"
magjed614d5b72016-11-15 06:30:54 -080014#include "webrtc/modules/video_coding/include/video_error_codes.h"
asapersson22c76c42017-08-16 00:53:59 -070015#include "webrtc/rtc_base/checks.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020016#include "webrtc/rtc_base/logging.h"
asapersson22c76c42017-08-16 00:53:59 -070017#include "webrtc/rtc_base/timeutils.h"
18#include "webrtc/system_wrappers/include/field_trial.h"
magjed614d5b72016-11-15 06:30:54 -080019
20namespace webrtc {
asapersson22c76c42017-08-16 00:53:59 -070021namespace {
22const char kVp8ForceFallbackEncoderFieldTrial[] =
23 "WebRTC-VP8-Forced-Fallback-Encoder";
24
25bool EnableForcedFallback(const cricket::VideoCodec& codec) {
26 if (!webrtc::field_trial::IsEnabled(kVp8ForceFallbackEncoderFieldTrial))
27 return false;
28
29 return (PayloadNameToCodecType(codec.name).value_or(kVideoCodecUnknown) ==
30 kVideoCodecVP8);
31}
32
33bool IsForcedFallbackPossible(const VideoCodec& codec_settings) {
34 return codec_settings.codecType == kVideoCodecVP8 &&
35 codec_settings.numberOfSimulcastStreams <= 1 &&
36 codec_settings.VP8().numberOfTemporalLayers == 1;
37}
38
39void GetForcedFallbackParamsFromFieldTrialGroup(uint32_t* param_low_kbps,
40 uint32_t* param_high_kbps,
41 int64_t* param_min_low_ms) {
42 RTC_DCHECK(param_low_kbps);
43 RTC_DCHECK(param_high_kbps);
44 RTC_DCHECK(param_min_low_ms);
45 std::string group =
46 webrtc::field_trial::FindFullName(kVp8ForceFallbackEncoderFieldTrial);
47 if (group.empty())
48 return;
49
50 int low_kbps;
51 int high_kbps;
52 int min_low_ms;
53 if (sscanf(group.c_str(), "Enabled-%d,%d,%d", &low_kbps, &high_kbps,
54 &min_low_ms) != 3) {
55 LOG(LS_WARNING) << "Invalid number of forced fallback parameters provided.";
56 return;
57 }
58 if (min_low_ms <= 0 || low_kbps <= 0 || high_kbps <= low_kbps) {
59 LOG(LS_WARNING) << "Invalid forced fallback parameter value provided.";
60 return;
61 }
62 *param_low_kbps = low_kbps;
63 *param_high_kbps = high_kbps;
64 *param_min_low_ms = min_low_ms;
65}
66} // namespace
magjed614d5b72016-11-15 06:30:54 -080067
68VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
magjed509e4fe2016-11-18 01:34:11 -080069 const cricket::VideoCodec& codec,
magjed614d5b72016-11-15 06:30:54 -080070 webrtc::VideoEncoder* encoder)
Erik Språng08127a92016-11-16 16:41:30 +010071 : number_of_cores_(0),
72 max_payload_size_(0),
73 rates_set_(false),
74 framerate_(0),
magjed614d5b72016-11-15 06:30:54 -080075 channel_parameters_set_(false),
Erik Språng08127a92016-11-16 16:41:30 +010076 packet_loss_(0),
77 rtt_(0),
magjed509e4fe2016-11-18 01:34:11 -080078 codec_(codec),
magjed614d5b72016-11-15 06:30:54 -080079 encoder_(encoder),
asapersson22c76c42017-08-16 00:53:59 -070080 callback_(nullptr),
81 forced_fallback_possible_(EnableForcedFallback(codec)) {
82 if (forced_fallback_possible_) {
83 GetForcedFallbackParamsFromFieldTrialGroup(&forced_fallback_.low_kbps,
84 &forced_fallback_.high_kbps,
85 &forced_fallback_.min_low_ms);
86 }
87}
magjed614d5b72016-11-15 06:30:54 -080088
89bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder() {
magjed509e4fe2016-11-18 01:34:11 -080090 cricket::InternalEncoderFactory internal_factory;
91 if (!FindMatchingCodec(internal_factory.supported_codecs(), codec_)) {
magjed614d5b72016-11-15 06:30:54 -080092 LOG(LS_WARNING)
93 << "Encoder requesting fallback to codec not supported in software.";
94 return false;
95 }
magjed509e4fe2016-11-18 01:34:11 -080096 fallback_encoder_.reset(internal_factory.CreateVideoEncoder(codec_));
magjed614d5b72016-11-15 06:30:54 -080097 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
98 max_payload_size_) !=
99 WEBRTC_VIDEO_CODEC_OK) {
100 LOG(LS_ERROR) << "Failed to initialize software-encoder fallback.";
101 fallback_encoder_->Release();
102 fallback_encoder_.reset();
103 return false;
104 }
105 // Replay callback, rates, and channel parameters.
106 if (callback_)
107 fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
108 if (rates_set_)
Erik Språng08127a92016-11-16 16:41:30 +0100109 fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate_);
magjed614d5b72016-11-15 06:30:54 -0800110 if (channel_parameters_set_)
111 fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
112
113 fallback_implementation_name_ =
114 std::string(fallback_encoder_->ImplementationName()) +
115 " (fallback from: " + encoder_->ImplementationName() + ")";
116 // Since we're switching to the fallback encoder, Release the real encoder. It
117 // may be re-initialized via InitEncode later, and it will continue to get
118 // Set calls for rates and channel parameters in the meantime.
119 encoder_->Release();
120 return true;
121}
122
123int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
124 const VideoCodec* codec_settings,
125 int32_t number_of_cores,
126 size_t max_payload_size) {
127 // Store settings, in case we need to dynamically switch to the fallback
128 // encoder after a failed Encode call.
129 codec_settings_ = *codec_settings;
130 number_of_cores_ = number_of_cores;
131 max_payload_size_ = max_payload_size;
132 // Clear stored rate/channel parameters.
133 rates_set_ = false;
134 channel_parameters_set_ = false;
asapersson22c76c42017-08-16 00:53:59 -0700135 ValidateSettingsForForcedFallback();
136
137 // Try to reinit forced software codec if it is in use.
138 if (TryReInitForcedFallbackEncoder()) {
139 return WEBRTC_VIDEO_CODEC_OK;
140 }
141 forced_fallback_.Reset();
magjed614d5b72016-11-15 06:30:54 -0800142
143 int32_t ret =
144 encoder_->InitEncode(codec_settings, number_of_cores, max_payload_size);
magjed509e4fe2016-11-18 01:34:11 -0800145 if (ret == WEBRTC_VIDEO_CODEC_OK || codec_.name.empty()) {
magjed614d5b72016-11-15 06:30:54 -0800146 if (fallback_encoder_)
147 fallback_encoder_->Release();
148 fallback_encoder_.reset();
149 if (callback_)
150 encoder_->RegisterEncodeCompleteCallback(callback_);
151 return ret;
152 }
153 // Try to instantiate software codec.
154 if (InitFallbackEncoder()) {
155 return WEBRTC_VIDEO_CODEC_OK;
156 }
157 // Software encoder failed, use original return code.
158 return ret;
159}
160
161int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback(
162 EncodedImageCallback* callback) {
163 callback_ = callback;
164 int32_t ret = encoder_->RegisterEncodeCompleteCallback(callback);
165 if (fallback_encoder_)
166 return fallback_encoder_->RegisterEncodeCompleteCallback(callback);
167 return ret;
168}
169
170int32_t VideoEncoderSoftwareFallbackWrapper::Release() {
171 // If the fallback_encoder_ is non-null, it means it was created via
172 // InitFallbackEncoder which has Release()d encoder_, so we should only ever
173 // need to Release() whichever one is active.
174 if (fallback_encoder_)
175 return fallback_encoder_->Release();
176 return encoder_->Release();
177}
178
179int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
180 const VideoFrame& frame,
181 const CodecSpecificInfo* codec_specific_info,
182 const std::vector<FrameType>* frame_types) {
asapersson22c76c42017-08-16 00:53:59 -0700183 if (TryReleaseForcedFallbackEncoder()) {
184 // Frame may have been converted from kNative to kI420 during fallback.
185 if (encoder_->SupportsNativeHandle() &&
186 frame.video_frame_buffer()->type() != VideoFrameBuffer::Type::kNative) {
187 LOG(LS_WARNING) << "Encoder supports native frames, dropping one frame "
188 << "to avoid possible reconfig due to format change.";
189 return WEBRTC_VIDEO_CODEC_ERROR;
190 }
191 }
magjed614d5b72016-11-15 06:30:54 -0800192 if (fallback_encoder_)
193 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
194 int32_t ret = encoder_->Encode(frame, codec_specific_info, frame_types);
195 // If requested, try a software fallback.
asapersson22c76c42017-08-16 00:53:59 -0700196 bool fallback_requested =
197 (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) ||
198 (ret == WEBRTC_VIDEO_CODEC_OK && RequestForcedFallback());
199 if (fallback_requested && InitFallbackEncoder()) {
200 // Fallback was successful.
201 if (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE)
202 forced_fallback_.Reset(); // Not a forced fallback.
Magnus Jedvert7a721e82017-06-14 11:28:08 +0200203 if (frame.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative &&
magjed614d5b72016-11-15 06:30:54 -0800204 !fallback_encoder_->SupportsNativeHandle()) {
205 LOG(LS_WARNING) << "Fallback encoder doesn't support native frames, "
206 << "dropping one frame.";
207 return WEBRTC_VIDEO_CODEC_ERROR;
208 }
209
asapersson22c76c42017-08-16 00:53:59 -0700210 // Start using the fallback with this frame.
magjed614d5b72016-11-15 06:30:54 -0800211 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
212 }
213 return ret;
214}
215
216int32_t VideoEncoderSoftwareFallbackWrapper::SetChannelParameters(
217 uint32_t packet_loss,
218 int64_t rtt) {
219 channel_parameters_set_ = true;
220 packet_loss_ = packet_loss;
221 rtt_ = rtt;
222 int32_t ret = encoder_->SetChannelParameters(packet_loss, rtt);
223 if (fallback_encoder_)
224 return fallback_encoder_->SetChannelParameters(packet_loss, rtt);
225 return ret;
226}
227
Erik Språng08127a92016-11-16 16:41:30 +0100228int32_t VideoEncoderSoftwareFallbackWrapper::SetRateAllocation(
229 const BitrateAllocation& bitrate_allocation,
230 uint32_t framerate) {
magjed614d5b72016-11-15 06:30:54 -0800231 rates_set_ = true;
Erik Språng08127a92016-11-16 16:41:30 +0100232 bitrate_allocation_ = bitrate_allocation;
magjed614d5b72016-11-15 06:30:54 -0800233 framerate_ = framerate;
Erik Språng08127a92016-11-16 16:41:30 +0100234 int32_t ret = encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800235 if (fallback_encoder_)
Erik Språng08127a92016-11-16 16:41:30 +0100236 return fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800237 return ret;
238}
239
magjed614d5b72016-11-15 06:30:54 -0800240bool VideoEncoderSoftwareFallbackWrapper::SupportsNativeHandle() const {
241 if (fallback_encoder_)
242 return fallback_encoder_->SupportsNativeHandle();
243 return encoder_->SupportsNativeHandle();
244}
245
kthelgason876222f2016-11-29 01:44:11 -0800246VideoEncoder::ScalingSettings
247VideoEncoderSoftwareFallbackWrapper::GetScalingSettings() const {
248 return encoder_->GetScalingSettings();
249}
250
kthelgason535dbd32017-01-26 00:36:31 -0800251const char *VideoEncoderSoftwareFallbackWrapper::ImplementationName() const {
252 if (fallback_encoder_)
253 return fallback_encoder_->ImplementationName();
254 return encoder_->ImplementationName();
255}
256
asapersson22c76c42017-08-16 00:53:59 -0700257bool VideoEncoderSoftwareFallbackWrapper::IsForcedFallbackActive() const {
258 return (forced_fallback_possible_ && fallback_encoder_ &&
259 forced_fallback_.start_ms);
260}
261
262bool VideoEncoderSoftwareFallbackWrapper::RequestForcedFallback() {
263 if (!forced_fallback_possible_ || fallback_encoder_ || !rates_set_)
264 return false;
265
266 // No fallback encoder.
267 return forced_fallback_.ShouldStart(bitrate_allocation_.get_sum_kbps(),
268 codec_settings_);
269}
270
271bool VideoEncoderSoftwareFallbackWrapper::TryReleaseForcedFallbackEncoder() {
272 if (!IsForcedFallbackActive())
273 return false;
274
275 if (!forced_fallback_.ShouldStop(bitrate_allocation_.get_sum_kbps()))
276 return false;
277
278 // Release the forced fallback encoder.
279 if (encoder_->InitEncode(&codec_settings_, number_of_cores_,
280 max_payload_size_) == WEBRTC_VIDEO_CODEC_OK) {
281 LOG(LS_INFO) << "Stop forced SW encoder fallback, max bitrate exceeded.";
282 fallback_encoder_->Release();
283 fallback_encoder_.reset();
284 forced_fallback_.Reset();
285 return true;
286 }
287 return false;
288}
289
290bool VideoEncoderSoftwareFallbackWrapper::TryReInitForcedFallbackEncoder() {
291 if (!IsForcedFallbackActive())
292 return false;
293
294 // Encoder reconfigured.
295 if (!forced_fallback_.IsValid(codec_settings_)) {
296 LOG(LS_INFO) << "Stop forced SW encoder fallback, max pixels exceeded.";
297 return false;
298 }
299 // Settings valid, reinitialize the forced fallback encoder.
300 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
301 max_payload_size_) !=
302 WEBRTC_VIDEO_CODEC_OK) {
303 LOG(LS_ERROR) << "Failed to init forced SW encoder fallback.";
304 return false;
305 }
306 return true;
307}
308
309void VideoEncoderSoftwareFallbackWrapper::ValidateSettingsForForcedFallback() {
310 if (!forced_fallback_possible_)
311 return;
312
313 if (!IsForcedFallbackPossible(codec_settings_)) {
314 if (IsForcedFallbackActive()) {
315 fallback_encoder_->Release();
316 fallback_encoder_.reset();
317 }
318 LOG(LS_INFO) << "Disable forced_fallback_possible_ due to settings.";
319 forced_fallback_possible_ = false;
320 }
321}
322
323bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStart(
324 uint32_t bitrate_kbps,
325 const VideoCodec& codec) {
326 if (bitrate_kbps > low_kbps || !IsValid(codec)) {
327 start_ms.reset();
328 return false;
329 }
330
331 // Has bitrate been below |low_kbps| for long enough duration.
332 int64_t now_ms = rtc::TimeMillis();
333 if (!start_ms)
334 start_ms.emplace(now_ms);
335
336 if ((now_ms - *start_ms) >= min_low_ms) {
337 LOG(LS_INFO) << "Request forced SW encoder fallback.";
338 // In case the request fails, update time to avoid too frequent requests.
339 start_ms.emplace(now_ms);
340 return true;
341 }
342 return false;
343}
344
345bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStop(
346 uint32_t bitrate_kbps) const {
347 return bitrate_kbps >= high_kbps;
348}
349
magjed614d5b72016-11-15 06:30:54 -0800350} // namespace webrtc