blob: 8dbf5d18372f7ae80e8c57a9f2f3d4a3d3e94db5 [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
kthelgason1cdddc92017-08-24 03:52:48 -070030 return (PayloadStringToCodecType(codec.name) == kVideoCodecVP8);
asapersson22c76c42017-08-16 00:53:59 -070031}
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() {
emircan82fac892017-08-23 14:19:50 -070092 MaybeModifyCodecForFallback();
magjed509e4fe2016-11-18 01:34:11 -080093 cricket::InternalEncoderFactory internal_factory;
94 if (!FindMatchingCodec(internal_factory.supported_codecs(), codec_)) {
magjed614d5b72016-11-15 06:30:54 -080095 LOG(LS_WARNING)
96 << "Encoder requesting fallback to codec not supported in software.";
97 return false;
98 }
magjed509e4fe2016-11-18 01:34:11 -080099 fallback_encoder_.reset(internal_factory.CreateVideoEncoder(codec_));
magjed614d5b72016-11-15 06:30:54 -0800100 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
101 max_payload_size_) !=
102 WEBRTC_VIDEO_CODEC_OK) {
103 LOG(LS_ERROR) << "Failed to initialize software-encoder fallback.";
104 fallback_encoder_->Release();
105 fallback_encoder_.reset();
106 return false;
107 }
108 // Replay callback, rates, and channel parameters.
109 if (callback_)
110 fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
111 if (rates_set_)
Erik Språng08127a92016-11-16 16:41:30 +0100112 fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate_);
magjed614d5b72016-11-15 06:30:54 -0800113 if (channel_parameters_set_)
114 fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
115
116 fallback_implementation_name_ =
117 std::string(fallback_encoder_->ImplementationName()) +
118 " (fallback from: " + encoder_->ImplementationName() + ")";
119 // Since we're switching to the fallback encoder, Release the real encoder. It
120 // may be re-initialized via InitEncode later, and it will continue to get
121 // Set calls for rates and channel parameters in the meantime.
122 encoder_->Release();
123 return true;
124}
125
126int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
127 const VideoCodec* codec_settings,
128 int32_t number_of_cores,
129 size_t max_payload_size) {
130 // Store settings, in case we need to dynamically switch to the fallback
131 // encoder after a failed Encode call.
132 codec_settings_ = *codec_settings;
133 number_of_cores_ = number_of_cores;
134 max_payload_size_ = max_payload_size;
135 // Clear stored rate/channel parameters.
136 rates_set_ = false;
137 channel_parameters_set_ = false;
asapersson22c76c42017-08-16 00:53:59 -0700138 ValidateSettingsForForcedFallback();
139
140 // Try to reinit forced software codec if it is in use.
141 if (TryReInitForcedFallbackEncoder()) {
142 return WEBRTC_VIDEO_CODEC_OK;
143 }
144 forced_fallback_.Reset();
magjed614d5b72016-11-15 06:30:54 -0800145
146 int32_t ret =
147 encoder_->InitEncode(codec_settings, number_of_cores, max_payload_size);
magjed509e4fe2016-11-18 01:34:11 -0800148 if (ret == WEBRTC_VIDEO_CODEC_OK || codec_.name.empty()) {
magjed614d5b72016-11-15 06:30:54 -0800149 if (fallback_encoder_)
150 fallback_encoder_->Release();
151 fallback_encoder_.reset();
152 if (callback_)
153 encoder_->RegisterEncodeCompleteCallback(callback_);
154 return ret;
155 }
156 // Try to instantiate software codec.
157 if (InitFallbackEncoder()) {
158 return WEBRTC_VIDEO_CODEC_OK;
159 }
160 // Software encoder failed, use original return code.
161 return ret;
162}
163
164int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback(
165 EncodedImageCallback* callback) {
166 callback_ = callback;
167 int32_t ret = encoder_->RegisterEncodeCompleteCallback(callback);
168 if (fallback_encoder_)
169 return fallback_encoder_->RegisterEncodeCompleteCallback(callback);
170 return ret;
171}
172
173int32_t VideoEncoderSoftwareFallbackWrapper::Release() {
174 // If the fallback_encoder_ is non-null, it means it was created via
175 // InitFallbackEncoder which has Release()d encoder_, so we should only ever
176 // need to Release() whichever one is active.
177 if (fallback_encoder_)
178 return fallback_encoder_->Release();
179 return encoder_->Release();
180}
181
182int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
183 const VideoFrame& frame,
184 const CodecSpecificInfo* codec_specific_info,
185 const std::vector<FrameType>* frame_types) {
asapersson22c76c42017-08-16 00:53:59 -0700186 if (TryReleaseForcedFallbackEncoder()) {
187 // Frame may have been converted from kNative to kI420 during fallback.
188 if (encoder_->SupportsNativeHandle() &&
189 frame.video_frame_buffer()->type() != VideoFrameBuffer::Type::kNative) {
190 LOG(LS_WARNING) << "Encoder supports native frames, dropping one frame "
191 << "to avoid possible reconfig due to format change.";
192 return WEBRTC_VIDEO_CODEC_ERROR;
193 }
194 }
magjed614d5b72016-11-15 06:30:54 -0800195 if (fallback_encoder_)
196 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
197 int32_t ret = encoder_->Encode(frame, codec_specific_info, frame_types);
198 // If requested, try a software fallback.
asapersson22c76c42017-08-16 00:53:59 -0700199 bool fallback_requested =
200 (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) ||
201 (ret == WEBRTC_VIDEO_CODEC_OK && RequestForcedFallback());
202 if (fallback_requested && InitFallbackEncoder()) {
203 // Fallback was successful.
204 if (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE)
205 forced_fallback_.Reset(); // Not a forced fallback.
Magnus Jedvert7a721e82017-06-14 11:28:08 +0200206 if (frame.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative &&
magjed614d5b72016-11-15 06:30:54 -0800207 !fallback_encoder_->SupportsNativeHandle()) {
208 LOG(LS_WARNING) << "Fallback encoder doesn't support native frames, "
209 << "dropping one frame.";
210 return WEBRTC_VIDEO_CODEC_ERROR;
211 }
212
asapersson22c76c42017-08-16 00:53:59 -0700213 // Start using the fallback with this frame.
magjed614d5b72016-11-15 06:30:54 -0800214 return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
215 }
216 return ret;
217}
218
219int32_t VideoEncoderSoftwareFallbackWrapper::SetChannelParameters(
220 uint32_t packet_loss,
221 int64_t rtt) {
222 channel_parameters_set_ = true;
223 packet_loss_ = packet_loss;
224 rtt_ = rtt;
225 int32_t ret = encoder_->SetChannelParameters(packet_loss, rtt);
226 if (fallback_encoder_)
227 return fallback_encoder_->SetChannelParameters(packet_loss, rtt);
228 return ret;
229}
230
Erik Språng08127a92016-11-16 16:41:30 +0100231int32_t VideoEncoderSoftwareFallbackWrapper::SetRateAllocation(
232 const BitrateAllocation& bitrate_allocation,
233 uint32_t framerate) {
magjed614d5b72016-11-15 06:30:54 -0800234 rates_set_ = true;
Erik Språng08127a92016-11-16 16:41:30 +0100235 bitrate_allocation_ = bitrate_allocation;
magjed614d5b72016-11-15 06:30:54 -0800236 framerate_ = framerate;
Erik Språng08127a92016-11-16 16:41:30 +0100237 int32_t ret = encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800238 if (fallback_encoder_)
Erik Språng08127a92016-11-16 16:41:30 +0100239 return fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate);
magjed614d5b72016-11-15 06:30:54 -0800240 return ret;
241}
242
magjed614d5b72016-11-15 06:30:54 -0800243bool VideoEncoderSoftwareFallbackWrapper::SupportsNativeHandle() const {
244 if (fallback_encoder_)
245 return fallback_encoder_->SupportsNativeHandle();
246 return encoder_->SupportsNativeHandle();
247}
248
kthelgason876222f2016-11-29 01:44:11 -0800249VideoEncoder::ScalingSettings
250VideoEncoderSoftwareFallbackWrapper::GetScalingSettings() const {
asapersson142fcc92017-08-17 08:58:54 -0700251 if (forced_fallback_possible_ && fallback_encoder_)
252 return fallback_encoder_->GetScalingSettings();
kthelgason876222f2016-11-29 01:44:11 -0800253 return encoder_->GetScalingSettings();
254}
255
kthelgason535dbd32017-01-26 00:36:31 -0800256const char *VideoEncoderSoftwareFallbackWrapper::ImplementationName() const {
257 if (fallback_encoder_)
258 return fallback_encoder_->ImplementationName();
259 return encoder_->ImplementationName();
260}
261
asapersson22c76c42017-08-16 00:53:59 -0700262bool VideoEncoderSoftwareFallbackWrapper::IsForcedFallbackActive() const {
263 return (forced_fallback_possible_ && fallback_encoder_ &&
264 forced_fallback_.start_ms);
265}
266
267bool VideoEncoderSoftwareFallbackWrapper::RequestForcedFallback() {
268 if (!forced_fallback_possible_ || fallback_encoder_ || !rates_set_)
269 return false;
270
271 // No fallback encoder.
272 return forced_fallback_.ShouldStart(bitrate_allocation_.get_sum_kbps(),
273 codec_settings_);
274}
275
276bool VideoEncoderSoftwareFallbackWrapper::TryReleaseForcedFallbackEncoder() {
277 if (!IsForcedFallbackActive())
278 return false;
279
asapersson142fcc92017-08-17 08:58:54 -0700280 if (!forced_fallback_.ShouldStop(bitrate_allocation_.get_sum_kbps(),
281 codec_settings_)) {
asapersson22c76c42017-08-16 00:53:59 -0700282 return false;
asapersson142fcc92017-08-17 08:58:54 -0700283 }
asapersson22c76c42017-08-16 00:53:59 -0700284
285 // Release the forced fallback encoder.
286 if (encoder_->InitEncode(&codec_settings_, number_of_cores_,
287 max_payload_size_) == WEBRTC_VIDEO_CODEC_OK) {
288 LOG(LS_INFO) << "Stop forced SW encoder fallback, max bitrate exceeded.";
289 fallback_encoder_->Release();
290 fallback_encoder_.reset();
291 forced_fallback_.Reset();
292 return true;
293 }
294 return false;
295}
296
297bool VideoEncoderSoftwareFallbackWrapper::TryReInitForcedFallbackEncoder() {
298 if (!IsForcedFallbackActive())
299 return false;
300
301 // Encoder reconfigured.
302 if (!forced_fallback_.IsValid(codec_settings_)) {
303 LOG(LS_INFO) << "Stop forced SW encoder fallback, max pixels exceeded.";
304 return false;
305 }
306 // Settings valid, reinitialize the forced fallback encoder.
307 if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
308 max_payload_size_) !=
309 WEBRTC_VIDEO_CODEC_OK) {
310 LOG(LS_ERROR) << "Failed to init forced SW encoder fallback.";
311 return false;
312 }
313 return true;
314}
315
316void VideoEncoderSoftwareFallbackWrapper::ValidateSettingsForForcedFallback() {
317 if (!forced_fallback_possible_)
318 return;
319
320 if (!IsForcedFallbackPossible(codec_settings_)) {
321 if (IsForcedFallbackActive()) {
322 fallback_encoder_->Release();
323 fallback_encoder_.reset();
324 }
325 LOG(LS_INFO) << "Disable forced_fallback_possible_ due to settings.";
326 forced_fallback_possible_ = false;
327 }
328}
329
330bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStart(
331 uint32_t bitrate_kbps,
332 const VideoCodec& codec) {
333 if (bitrate_kbps > low_kbps || !IsValid(codec)) {
334 start_ms.reset();
335 return false;
336 }
337
338 // Has bitrate been below |low_kbps| for long enough duration.
339 int64_t now_ms = rtc::TimeMillis();
340 if (!start_ms)
341 start_ms.emplace(now_ms);
342
343 if ((now_ms - *start_ms) >= min_low_ms) {
344 LOG(LS_INFO) << "Request forced SW encoder fallback.";
345 // In case the request fails, update time to avoid too frequent requests.
346 start_ms.emplace(now_ms);
347 return true;
348 }
349 return false;
350}
351
352bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStop(
asapersson142fcc92017-08-17 08:58:54 -0700353 uint32_t bitrate_kbps,
354 const VideoCodec& codec) const {
355 return bitrate_kbps >= high_kbps &&
356 (codec.width * codec.height >= kMinPixelsStop);
asapersson22c76c42017-08-16 00:53:59 -0700357}
358
emircan82fac892017-08-23 14:19:50 -0700359void VideoEncoderSoftwareFallbackWrapper::MaybeModifyCodecForFallback() {
360 // We have a specific case for H264 ConstrainedBaseline because that is the
361 // only supported profile in Sw fallback.
362 if (!cricket::CodecNamesEq(codec_.name.c_str(), cricket::kH264CodecName))
363 return;
364 codec_.SetParam(cricket::kH264FmtpProfileLevelId,
365 cricket::kH264ProfileLevelConstrainedBaseline);
366}
367
magjed614d5b72016-11-15 06:30:54 -0800368} // namespace webrtc