blob: 2bc74a7e724578392b6bf4f544bdc2c1c76da0dd [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;
asapersson142fcc92017-08-17 08:58:54 -070053 int min_pixels;
54 if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d", &low_kbps, &high_kbps,
55 &min_low_ms, &min_pixels) != 4) {
asapersson22c76c42017-08-16 00:53:59 -070056 LOG(LS_WARNING) << "Invalid number of forced fallback parameters provided.";
57 return;
58 }
asapersson142fcc92017-08-17 08:58:54 -070059 if (min_low_ms <= 0 || min_pixels <= 0 || low_kbps <= 0 ||
60 high_kbps <= low_kbps) {
asapersson22c76c42017-08-16 00:53:59 -070061 LOG(LS_WARNING) << "Invalid forced fallback parameter value provided.";
62 return;
63 }
64 *param_low_kbps = low_kbps;
65 *param_high_kbps = high_kbps;
66 *param_min_low_ms = min_low_ms;
67}
68} // namespace
magjed614d5b72016-11-15 06:30:54 -080069
70VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
magjed509e4fe2016-11-18 01:34:11 -080071 const cricket::VideoCodec& codec,
magjed614d5b72016-11-15 06:30:54 -080072 webrtc::VideoEncoder* encoder)
Erik Språng08127a92016-11-16 16:41:30 +010073 : number_of_cores_(0),
74 max_payload_size_(0),
75 rates_set_(false),
76 framerate_(0),
magjed614d5b72016-11-15 06:30:54 -080077 channel_parameters_set_(false),
Erik Språng08127a92016-11-16 16:41:30 +010078 packet_loss_(0),
79 rtt_(0),
magjed509e4fe2016-11-18 01:34:11 -080080 codec_(codec),
magjed614d5b72016-11-15 06:30:54 -080081 encoder_(encoder),
asapersson22c76c42017-08-16 00:53:59 -070082 callback_(nullptr),
83 forced_fallback_possible_(EnableForcedFallback(codec)) {
84 if (forced_fallback_possible_) {
85 GetForcedFallbackParamsFromFieldTrialGroup(&forced_fallback_.low_kbps,
86 &forced_fallback_.high_kbps,
87 &forced_fallback_.min_low_ms);
88 }
89}
magjed614d5b72016-11-15 06:30:54 -080090
91bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder() {
magjed509e4fe2016-11-18 01:34:11 -080092 cricket::InternalEncoderFactory internal_factory;
93 if (!FindMatchingCodec(internal_factory.supported_codecs(), codec_)) {
magjed614d5b72016-11-15 06:30:54 -080094 LOG(LS_WARNING)
95 << "Encoder requesting fallback to codec not supported in software.";
96 return false;
97 }
magjed509e4fe2016-11-18 01:34:11 -080098 fallback_encoder_.reset(internal_factory.CreateVideoEncoder(codec_));
magjed614d5b72016-11-15 06:30:54 -080099 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
100 max_payload_size_) !=
101 WEBRTC_VIDEO_CODEC_OK) {
102 LOG(LS_ERROR) << "Failed to initialize software-encoder fallback.";
103 fallback_encoder_->Release();
104 fallback_encoder_.reset();
105 return false;
106 }
107 // Replay callback, rates, and channel parameters.
108 if (callback_)
109 fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
110 if (rates_set_)
Erik Språng08127a92016-11-16 16:41:30 +0100111 fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate_);
magjed614d5b72016-11-15 06:30:54 -0800112 if (channel_parameters_set_)
113 fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
114
115 fallback_implementation_name_ =
116 std::string(fallback_encoder_->ImplementationName()) +
117 " (fallback from: " + encoder_->ImplementationName() + ")";
118 // Since we're switching to the fallback encoder, Release the real encoder. It
119 // may be re-initialized via InitEncode later, and it will continue to get
120 // Set calls for rates and channel parameters in the meantime.
121 encoder_->Release();
122 return true;
123}
124
125int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
126 const VideoCodec* codec_settings,
127 int32_t number_of_cores,
128 size_t max_payload_size) {
129 // Store settings, in case we need to dynamically switch to the fallback
130 // encoder after a failed Encode call.
131 codec_settings_ = *codec_settings;
132 number_of_cores_ = number_of_cores;
133 max_payload_size_ = max_payload_size;
134 // Clear stored rate/channel parameters.
135 rates_set_ = false;
136 channel_parameters_set_ = false;
asapersson22c76c42017-08-16 00:53:59 -0700137 ValidateSettingsForForcedFallback();
138
139 // Try to reinit forced software codec if it is in use.
140 if (TryReInitForcedFallbackEncoder()) {
141 return WEBRTC_VIDEO_CODEC_OK;
142 }
143 forced_fallback_.Reset();
magjed614d5b72016-11-15 06:30:54 -0800144
145 int32_t ret =
146 encoder_->InitEncode(codec_settings, number_of_cores, max_payload_size);
magjed509e4fe2016-11-18 01:34:11 -0800147 if (ret == WEBRTC_VIDEO_CODEC_OK || codec_.name.empty()) {
magjed614d5b72016-11-15 06:30:54 -0800148 if (fallback_encoder_)
149 fallback_encoder_->Release();
150 fallback_encoder_.reset();
151 if (callback_)
152 encoder_->RegisterEncodeCompleteCallback(callback_);
153 return ret;
154 }
155 // Try to instantiate software codec.
156 if (InitFallbackEncoder()) {
157 return WEBRTC_VIDEO_CODEC_OK;
158 }
159 // Software encoder failed, use original return code.
160 return ret;
161}
162
163int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback(
164 EncodedImageCallback* callback) {
165 callback_ = callback;
166 int32_t ret = encoder_->RegisterEncodeCompleteCallback(callback);
167 if (fallback_encoder_)
168 return fallback_encoder_->RegisterEncodeCompleteCallback(callback);
169 return ret;
170}
171
172int32_t VideoEncoderSoftwareFallbackWrapper::Release() {
173 // If the fallback_encoder_ is non-null, it means it was created via
174 // InitFallbackEncoder which has Release()d encoder_, so we should only ever
175 // need to Release() whichever one is active.
176 if (fallback_encoder_)
177 return fallback_encoder_->Release();
178 return encoder_->Release();
179}
180
181int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
182 const VideoFrame& frame,
183 const CodecSpecificInfo* codec_specific_info,
184 const std::vector<FrameType>* frame_types) {
asapersson22c76c42017-08-16 00:53:59 -0700185 if (TryReleaseForcedFallbackEncoder()) {
186 // Frame may have been converted from kNative to kI420 during fallback.
187 if (encoder_->SupportsNativeHandle() &&
188 frame.video_frame_buffer()->type() != VideoFrameBuffer::Type::kNative) {
189 LOG(LS_WARNING) << "Encoder supports native frames, dropping one frame "
190 << "to avoid possible reconfig due to format change.";
191 return WEBRTC_VIDEO_CODEC_ERROR;
192 }
193 }
magjed614d5b72016-11-15 06:30:54 -0800194 if (fallback_encoder_)
195 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
196 int32_t ret = encoder_->Encode(frame, codec_specific_info, frame_types);
197 // If requested, try a software fallback.
asapersson22c76c42017-08-16 00:53:59 -0700198 bool fallback_requested =
199 (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) ||
200 (ret == WEBRTC_VIDEO_CODEC_OK && RequestForcedFallback());
201 if (fallback_requested && InitFallbackEncoder()) {
202 // Fallback was successful.
203 if (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE)
204 forced_fallback_.Reset(); // Not a forced fallback.
Magnus Jedvert7a721e82017-06-14 11:28:08 +0200205 if (frame.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative &&
magjed614d5b72016-11-15 06:30:54 -0800206 !fallback_encoder_->SupportsNativeHandle()) {
207 LOG(LS_WARNING) << "Fallback encoder doesn't support native frames, "
208 << "dropping one frame.";
209 return WEBRTC_VIDEO_CODEC_ERROR;
210 }
211
asapersson22c76c42017-08-16 00:53:59 -0700212 // Start using the fallback with this frame.
magjed614d5b72016-11-15 06:30:54 -0800213 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
214 }
215 return ret;
216}
217
218int32_t VideoEncoderSoftwareFallbackWrapper::SetChannelParameters(
219 uint32_t packet_loss,
220 int64_t rtt) {
221 channel_parameters_set_ = true;
222 packet_loss_ = packet_loss;
223 rtt_ = rtt;
224 int32_t ret = encoder_->SetChannelParameters(packet_loss, rtt);
225 if (fallback_encoder_)
226 return fallback_encoder_->SetChannelParameters(packet_loss, rtt);
227 return ret;
228}
229
Erik Språng08127a92016-11-16 16:41:30 +0100230int32_t VideoEncoderSoftwareFallbackWrapper::SetRateAllocation(
231 const BitrateAllocation& bitrate_allocation,
232 uint32_t framerate) {
magjed614d5b72016-11-15 06:30:54 -0800233 rates_set_ = true;
Erik Språng08127a92016-11-16 16:41:30 +0100234 bitrate_allocation_ = bitrate_allocation;
magjed614d5b72016-11-15 06:30:54 -0800235 framerate_ = framerate;
Erik Språng08127a92016-11-16 16:41:30 +0100236 int32_t ret = encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800237 if (fallback_encoder_)
Erik Språng08127a92016-11-16 16:41:30 +0100238 return fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800239 return ret;
240}
241
magjed614d5b72016-11-15 06:30:54 -0800242bool VideoEncoderSoftwareFallbackWrapper::SupportsNativeHandle() const {
243 if (fallback_encoder_)
244 return fallback_encoder_->SupportsNativeHandle();
245 return encoder_->SupportsNativeHandle();
246}
247
kthelgason876222f2016-11-29 01:44:11 -0800248VideoEncoder::ScalingSettings
249VideoEncoderSoftwareFallbackWrapper::GetScalingSettings() const {
asapersson142fcc92017-08-17 08:58:54 -0700250 if (forced_fallback_possible_ && fallback_encoder_)
251 return fallback_encoder_->GetScalingSettings();
kthelgason876222f2016-11-29 01:44:11 -0800252 return encoder_->GetScalingSettings();
253}
254
kthelgason535dbd32017-01-26 00:36:31 -0800255const char *VideoEncoderSoftwareFallbackWrapper::ImplementationName() const {
256 if (fallback_encoder_)
257 return fallback_encoder_->ImplementationName();
258 return encoder_->ImplementationName();
259}
260
asapersson22c76c42017-08-16 00:53:59 -0700261bool VideoEncoderSoftwareFallbackWrapper::IsForcedFallbackActive() const {
262 return (forced_fallback_possible_ && fallback_encoder_ &&
263 forced_fallback_.start_ms);
264}
265
266bool VideoEncoderSoftwareFallbackWrapper::RequestForcedFallback() {
267 if (!forced_fallback_possible_ || fallback_encoder_ || !rates_set_)
268 return false;
269
270 // No fallback encoder.
271 return forced_fallback_.ShouldStart(bitrate_allocation_.get_sum_kbps(),
272 codec_settings_);
273}
274
275bool VideoEncoderSoftwareFallbackWrapper::TryReleaseForcedFallbackEncoder() {
276 if (!IsForcedFallbackActive())
277 return false;
278
asapersson142fcc92017-08-17 08:58:54 -0700279 if (!forced_fallback_.ShouldStop(bitrate_allocation_.get_sum_kbps(),
280 codec_settings_)) {
asapersson22c76c42017-08-16 00:53:59 -0700281 return false;
asapersson142fcc92017-08-17 08:58:54 -0700282 }
asapersson22c76c42017-08-16 00:53:59 -0700283
284 // Release the forced fallback encoder.
285 if (encoder_->InitEncode(&codec_settings_, number_of_cores_,
286 max_payload_size_) == WEBRTC_VIDEO_CODEC_OK) {
287 LOG(LS_INFO) << "Stop forced SW encoder fallback, max bitrate exceeded.";
288 fallback_encoder_->Release();
289 fallback_encoder_.reset();
290 forced_fallback_.Reset();
291 return true;
292 }
293 return false;
294}
295
296bool VideoEncoderSoftwareFallbackWrapper::TryReInitForcedFallbackEncoder() {
297 if (!IsForcedFallbackActive())
298 return false;
299
300 // Encoder reconfigured.
301 if (!forced_fallback_.IsValid(codec_settings_)) {
302 LOG(LS_INFO) << "Stop forced SW encoder fallback, max pixels exceeded.";
303 return false;
304 }
305 // Settings valid, reinitialize the forced fallback encoder.
306 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
307 max_payload_size_) !=
308 WEBRTC_VIDEO_CODEC_OK) {
309 LOG(LS_ERROR) << "Failed to init forced SW encoder fallback.";
310 return false;
311 }
312 return true;
313}
314
315void VideoEncoderSoftwareFallbackWrapper::ValidateSettingsForForcedFallback() {
316 if (!forced_fallback_possible_)
317 return;
318
319 if (!IsForcedFallbackPossible(codec_settings_)) {
320 if (IsForcedFallbackActive()) {
321 fallback_encoder_->Release();
322 fallback_encoder_.reset();
323 }
324 LOG(LS_INFO) << "Disable forced_fallback_possible_ due to settings.";
325 forced_fallback_possible_ = false;
326 }
327}
328
329bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStart(
330 uint32_t bitrate_kbps,
331 const VideoCodec& codec) {
332 if (bitrate_kbps > low_kbps || !IsValid(codec)) {
333 start_ms.reset();
334 return false;
335 }
336
337 // Has bitrate been below |low_kbps| for long enough duration.
338 int64_t now_ms = rtc::TimeMillis();
339 if (!start_ms)
340 start_ms.emplace(now_ms);
341
342 if ((now_ms - *start_ms) >= min_low_ms) {
343 LOG(LS_INFO) << "Request forced SW encoder fallback.";
344 // In case the request fails, update time to avoid too frequent requests.
345 start_ms.emplace(now_ms);
346 return true;
347 }
348 return false;
349}
350
351bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStop(
asapersson142fcc92017-08-17 08:58:54 -0700352 uint32_t bitrate_kbps,
353 const VideoCodec& codec) const {
354 return bitrate_kbps >= high_kbps &&
355 (codec.width * codec.height >= kMinPixelsStop);
asapersson22c76c42017-08-16 00:53:59 -0700356}
357
magjed614d5b72016-11-15 06:30:54 -0800358} // namespace webrtc