blob: e4d0b0bbe9d57ec218d57c66d4d2fc509644ec59 [file] [log] [blame]
pbos@webrtc.org9115cde2014-12-09 10:36:40 +00001/*
2 * Copyright (c) 2014 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/simulcast_encoder_adapter.h"
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000012
13#include <algorithm>
Ilya Nikolaevskiyc30a1312018-08-27 11:07:48 +020014#include <string>
15#include <utility>
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000016
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "api/video/i420_buffer.h"
Erik Språng7fd0a282018-05-22 15:37:23 +020018#include "api/video/video_bitrate_allocation.h"
Magnus Jedvertdf4883d2017-11-17 14:44:55 +010019#include "api/video_codecs/video_encoder_factory.h"
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +020020#include "modules/video_coding/utility/simulcast_rate_allocator.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "rtc_base/checks.h"
Erik Språng75de46a2018-11-07 14:53:32 +010022#include "rtc_base/logging.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "system_wrappers/include/clock.h"
Ilya Nikolaevskiyc30a1312018-08-27 11:07:48 +020024#include "system_wrappers/include/field_trial.h"
Mirko Bonadei65432062017-12-11 09:32:13 +010025#include "third_party/libyuv/include/libyuv/scale.h"
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000026
27namespace {
28
29const unsigned int kDefaultMinQp = 2;
30const unsigned int kDefaultMaxQp = 56;
31// Max qp for lowest spatial resolution when doing simulcast.
32const unsigned int kLowestResMaxQp = 45;
33
Ilya Nikolaevskiyc30a1312018-08-27 11:07:48 +020034absl::optional<unsigned int> GetScreenshareBoostedQpValue() {
35 std::string experiment_group =
36 webrtc::field_trial::FindFullName("WebRTC-BoostedScreenshareQp");
37 unsigned int qp;
38 if (sscanf(experiment_group.c_str(), "%u", &qp) != 1)
39 return absl::nullopt;
40 qp = std::min(qp, 63u);
41 qp = std::max(qp, 1u);
42 return qp;
43}
44
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000045uint32_t SumStreamMaxBitrate(int streams, const webrtc::VideoCodec& codec) {
46 uint32_t bitrate_sum = 0;
47 for (int i = 0; i < streams; ++i) {
48 bitrate_sum += codec.simulcastStream[i].maxBitrate;
49 }
50 return bitrate_sum;
51}
52
53int NumberOfStreams(const webrtc::VideoCodec& codec) {
54 int streams =
55 codec.numberOfSimulcastStreams < 1 ? 1 : codec.numberOfSimulcastStreams;
56 uint32_t simulcast_max_bitrate = SumStreamMaxBitrate(streams, codec);
57 if (simulcast_max_bitrate == 0) {
58 streams = 1;
59 }
60 return streams;
61}
62
63bool ValidSimulcastResolutions(const webrtc::VideoCodec& codec,
64 int num_streams) {
65 if (codec.width != codec.simulcastStream[num_streams - 1].width ||
66 codec.height != codec.simulcastStream[num_streams - 1].height) {
67 return false;
68 }
69 for (int i = 0; i < num_streams; ++i) {
70 if (codec.width * codec.simulcastStream[i].height !=
71 codec.height * codec.simulcastStream[i].width) {
72 return false;
73 }
74 }
75 return true;
76}
77
78int VerifyCodec(const webrtc::VideoCodec* inst) {
brandtr5e171752017-05-23 03:32:16 -070079 if (inst == nullptr) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000080 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
81 }
82 if (inst->maxFramerate < 1) {
83 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
84 }
85 // allow zero to represent an unspecified maxBitRate
86 if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {
87 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
88 }
89 if (inst->width <= 1 || inst->height <= 1) {
90 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
91 }
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +020092 if (inst->codecType == webrtc::kVideoCodecVP8 &&
93 inst->VP8().automaticResizeOn && inst->numberOfSimulcastStreams > 1) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000094 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
95 }
96 return WEBRTC_VIDEO_CODEC_OK;
97}
98
Noah Richards41ee1ea2015-04-15 09:24:26 -070099// An EncodedImageCallback implementation that forwards on calls to a
100// SimulcastEncoderAdapter, but with the stream index it's registered with as
101// the first parameter to Encoded.
102class AdapterEncodedImageCallback : public webrtc::EncodedImageCallback {
103 public:
104 AdapterEncodedImageCallback(webrtc::SimulcastEncoderAdapter* adapter,
105 size_t stream_idx)
106 : adapter_(adapter), stream_idx_(stream_idx) {}
107
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700108 EncodedImageCallback::Result OnEncodedImage(
109 const webrtc::EncodedImage& encoded_image,
110 const webrtc::CodecSpecificInfo* codec_specific_info,
111 const webrtc::RTPFragmentationHeader* fragmentation) override {
112 return adapter_->OnEncodedImage(stream_idx_, encoded_image,
113 codec_specific_info, fragmentation);
Noah Richards41ee1ea2015-04-15 09:24:26 -0700114 }
115
116 private:
117 webrtc::SimulcastEncoderAdapter* const adapter_;
118 const size_t stream_idx_;
119};
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000120} // namespace
121
122namespace webrtc {
123
Ilya Nikolaevskiy97b4ee52018-05-28 10:24:22 +0200124SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory,
125 const SdpVideoFormat& format)
Magnus Jedvertdf4883d2017-11-17 14:44:55 +0100126 : inited_(0),
127 factory_(factory),
Ilya Nikolaevskiy97b4ee52018-05-28 10:24:22 +0200128 video_format_(format),
Magnus Jedvertdf4883d2017-11-17 14:44:55 +0100129 encoded_complete_callback_(nullptr),
Oleh Prypina1d9ca42018-10-11 14:33:39 +0000130 experimental_boosted_screenshare_qp_(GetScreenshareBoostedQpValue()) {
Rasmus Brandtc334ce92018-02-05 13:03:25 +0100131 RTC_DCHECK(factory_);
Erik Språng75de46a2018-11-07 14:53:32 +0100132 encoder_info_.implementation_name = "SimulcastEncoderAdapter";
Magnus Jedvertdf4883d2017-11-17 14:44:55 +0100133
brandtr5e171752017-05-23 03:32:16 -0700134 // The adapter is typically created on the worker thread, but operated on
135 // the encoder task queue.
136 encoder_queue_.Detach();
137
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000138 memset(&codec_, 0, sizeof(webrtc::VideoCodec));
139}
140
141SimulcastEncoderAdapter::~SimulcastEncoderAdapter() {
brandtr5e171752017-05-23 03:32:16 -0700142 RTC_DCHECK(!Initialized());
143 DestroyStoredEncoders();
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000144}
145
146int SimulcastEncoderAdapter::Release() {
brandtr5e171752017-05-23 03:32:16 -0700147 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
148
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000149 while (!streaminfos_.empty()) {
magjed3f897582017-08-28 08:05:42 -0700150 std::unique_ptr<VideoEncoder> encoder =
151 std::move(streaminfos_.back().encoder);
brandtr5e171752017-05-23 03:32:16 -0700152 // Even though it seems very unlikely, there are no guarantees that the
Rasmus Brandt9cf9f752017-09-28 13:02:58 +0200153 // encoder will not call back after being Release()'d. Therefore, we first
154 // disable the callbacks here.
brandtr5e171752017-05-23 03:32:16 -0700155 encoder->RegisterEncodeCompleteCallback(nullptr);
Rasmus Brandt9cf9f752017-09-28 13:02:58 +0200156 encoder->Release();
brandtr5e171752017-05-23 03:32:16 -0700157 streaminfos_.pop_back(); // Deletes callback adapter.
magjed3f897582017-08-28 08:05:42 -0700158 stored_encoders_.push(std::move(encoder));
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000159 }
brandtr5e171752017-05-23 03:32:16 -0700160
161 // It's legal to move the encoder to another queue now.
162 encoder_queue_.Detach();
163
164 rtc::AtomicOps::ReleaseStore(&inited_, 0);
165
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000166 return WEBRTC_VIDEO_CODEC_OK;
167}
168
169int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
170 int number_of_cores,
171 size_t max_payload_size) {
brandtr5e171752017-05-23 03:32:16 -0700172 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
173
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000174 if (number_of_cores < 1) {
175 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
176 }
177
178 int ret = VerifyCodec(inst);
179 if (ret < 0) {
180 return ret;
181 }
182
183 ret = Release();
184 if (ret < 0) {
185 return ret;
186 }
187
188 int number_of_streams = NumberOfStreams(*inst);
brandtr5e171752017-05-23 03:32:16 -0700189 RTC_DCHECK_LE(number_of_streams, kMaxSimulcastStreams);
Peter Boströmd53c3892016-03-30 17:03:52 +0200190 const bool doing_simulcast = (number_of_streams > 1);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000191
192 if (doing_simulcast && !ValidSimulcastResolutions(*inst, number_of_streams)) {
193 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
194 }
195
196 codec_ = *inst;
Erik Språng82fad3d2018-03-21 09:57:23 +0100197 SimulcastRateAllocator rate_allocator(codec_);
Erik Språng566124a2018-04-23 12:32:22 +0200198 VideoBitrateAllocation allocation = rate_allocator.GetAllocation(
Erik Språng08127a92016-11-16 16:41:30 +0100199 codec_.startBitrate * 1000, codec_.maxFramerate);
200 std::vector<uint32_t> start_bitrates;
201 for (int i = 0; i < kMaxSimulcastStreams; ++i) {
202 uint32_t stream_bitrate = allocation.GetSpatialLayerSum(i) / 1000;
203 start_bitrates.push_back(stream_bitrate);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000204 }
205
Erik Språng75de46a2018-11-07 14:53:32 +0100206 encoder_info_.supports_native_handle = true;
207 encoder_info_.scaling_settings.thresholds = absl::nullopt;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000208 // Create |number_of_streams| of encoder instances and init them.
209 for (int i = 0; i < number_of_streams; ++i) {
210 VideoCodec stream_codec;
Erik Språng78ce6192016-09-12 16:04:43 +0200211 uint32_t start_bitrate_kbps = start_bitrates[i];
Erik Språng7d687b12018-09-12 17:04:10 +0200212 const bool send_stream = start_bitrate_kbps > 0;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000213 if (!doing_simulcast) {
214 stream_codec = codec_;
215 stream_codec.numberOfSimulcastStreams = 1;
Erik Språng75de46a2018-11-07 14:53:32 +0100216
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000217 } else {
Erik Språng78ce6192016-09-12 16:04:43 +0200218 // Cap start bitrate to the min bitrate in order to avoid strange codec
Erik Språng7d687b12018-09-12 17:04:10 +0200219 // behavior. Since sending will be false, this should not matter.
Erik Språng78ce6192016-09-12 16:04:43 +0200220 start_bitrate_kbps =
221 std::max(codec_.simulcastStream[i].minBitrate, start_bitrate_kbps);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000222 bool highest_resolution_stream = (i == (number_of_streams - 1));
brandtr5e171752017-05-23 03:32:16 -0700223 PopulateStreamCodec(codec_, i, start_bitrate_kbps,
Erik Språng78ce6192016-09-12 16:04:43 +0200224 highest_resolution_stream, &stream_codec);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000225 }
226
Erik Språngcc681cc2018-03-14 17:52:55 +0100227 // TODO(ronghuawu): Remove once this is handled in LibvpxVp8Encoder.
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000228 if (stream_codec.qpMax < kDefaultMinQp) {
229 stream_codec.qpMax = kDefaultMaxQp;
230 }
231
brandtr5e171752017-05-23 03:32:16 -0700232 // If an existing encoder instance exists, reuse it.
233 // TODO(brandtr): Set initial RTP state (e.g., picture_id/tl0_pic_idx) here,
234 // when we start storing that state outside the encoder wrappers.
magjed3f897582017-08-28 08:05:42 -0700235 std::unique_ptr<VideoEncoder> encoder;
brandtr5e171752017-05-23 03:32:16 -0700236 if (!stored_encoders_.empty()) {
magjed3f897582017-08-28 08:05:42 -0700237 encoder = std::move(stored_encoders_.top());
brandtr5e171752017-05-23 03:32:16 -0700238 stored_encoders_.pop();
239 } else {
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +0200240 encoder = factory_->CreateVideoEncoder(SdpVideoFormat(
241 codec_.codecType == webrtc::kVideoCodecVP8 ? "VP8" : "H264"));
brandtr5e171752017-05-23 03:32:16 -0700242 }
243
philipelcce46fc2015-12-21 03:04:49 -0800244 ret = encoder->InitEncode(&stream_codec, number_of_cores, max_payload_size);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000245 if (ret < 0) {
noahrice5ba75a2016-12-12 13:08:27 -0800246 // Explicitly destroy the current encoder; because we haven't registered a
247 // StreamInfo for it yet, Release won't do anything about it.
magjed3f897582017-08-28 08:05:42 -0700248 encoder.reset();
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000249 Release();
250 return ret;
251 }
brandtr5e171752017-05-23 03:32:16 -0700252 std::unique_ptr<EncodedImageCallback> callback(
253 new AdapterEncodedImageCallback(this, i));
254 encoder->RegisterEncodeCompleteCallback(callback.get());
magjed3f897582017-08-28 08:05:42 -0700255 streaminfos_.emplace_back(std::move(encoder), std::move(callback),
256 stream_codec.width, stream_codec.height,
Erik Språng7d687b12018-09-12 17:04:10 +0200257 send_stream);
brandtr5e171752017-05-23 03:32:16 -0700258
Erik Språng75de46a2018-11-07 14:53:32 +0100259 if (!doing_simulcast) {
260 // Without simulcast, just pass through the encoder info from the one
261 // active encoder.
262 encoder_info_ = streaminfos_[0].encoder->GetEncoderInfo();
263 } else {
264 const EncoderInfo encoder_impl_info =
265 streaminfos_[i].encoder->GetEncoderInfo();
266
267 if (i == 0) {
268 // Quality scaling not enabled for simulcast.
269 encoder_info_.scaling_settings = VideoEncoder::ScalingSettings::kOff;
270
271 // Encoder name indicates names of all sub-encoders.
272 encoder_info_.implementation_name = "SimulcastEncoderAdapter (";
273 encoder_info_.implementation_name +=
274 encoder_impl_info.implementation_name;
275
276 encoder_info_.supports_native_handle =
277 encoder_impl_info.supports_native_handle;
278 } else {
279 encoder_info_.implementation_name += ", ";
280 encoder_info_.implementation_name +=
281 encoder_impl_info.implementation_name;
282
283 // Native handle supported only if all encoders supports it.
284 encoder_info_.supports_native_handle &=
285 encoder_impl_info.supports_native_handle;
286 }
brandtr5e171752017-05-23 03:32:16 -0700287 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000288 }
brandtr5e171752017-05-23 03:32:16 -0700289
Peter Boströmd53c3892016-03-30 17:03:52 +0200290 if (doing_simulcast) {
Erik Språng75de46a2018-11-07 14:53:32 +0100291 encoder_info_.implementation_name += ")";
Peter Boströmd53c3892016-03-30 17:03:52 +0200292 }
brandtr5e171752017-05-23 03:32:16 -0700293
294 // To save memory, don't store encoders that we don't use.
295 DestroyStoredEncoders();
296
297 rtc::AtomicOps::ReleaseStore(&inited_, 1);
298
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000299 return WEBRTC_VIDEO_CODEC_OK;
300}
301
302int SimulcastEncoderAdapter::Encode(
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700303 const VideoFrame& input_image,
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000304 const CodecSpecificInfo* codec_specific_info,
pbos22993e12015-10-19 02:39:06 -0700305 const std::vector<FrameType>* frame_types) {
brandtr5e171752017-05-23 03:32:16 -0700306 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
307
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000308 if (!Initialized()) {
309 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
310 }
brandtr5e171752017-05-23 03:32:16 -0700311 if (encoded_complete_callback_ == nullptr) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000312 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
313 }
314
315 // All active streams should generate a key frame if
316 // a key frame is requested by any stream.
317 bool send_key_frame = false;
318 if (frame_types) {
319 for (size_t i = 0; i < frame_types->size(); ++i) {
Peter Boström49e196a2015-10-23 15:58:18 +0200320 if (frame_types->at(i) == kVideoFrameKey) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000321 send_key_frame = true;
322 break;
323 }
324 }
325 }
326 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
327 if (streaminfos_[stream_idx].key_frame_request &&
328 streaminfos_[stream_idx].send_stream) {
329 send_key_frame = true;
330 break;
331 }
332 }
333
334 int src_width = input_image.width();
335 int src_height = input_image.height();
336 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
Peter Boström5d0379d2015-10-06 14:04:51 +0200337 // Don't encode frames in resolutions that we don't intend to send.
brandtr5e171752017-05-23 03:32:16 -0700338 if (!streaminfos_[stream_idx].send_stream) {
Peter Boström5d0379d2015-10-06 14:04:51 +0200339 continue;
brandtr5e171752017-05-23 03:32:16 -0700340 }
Peter Boström5d0379d2015-10-06 14:04:51 +0200341
pbos22993e12015-10-19 02:39:06 -0700342 std::vector<FrameType> stream_frame_types;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000343 if (send_key_frame) {
Peter Boström49e196a2015-10-23 15:58:18 +0200344 stream_frame_types.push_back(kVideoFrameKey);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000345 streaminfos_[stream_idx].key_frame_request = false;
346 } else {
Peter Boström49e196a2015-10-23 15:58:18 +0200347 stream_frame_types.push_back(kVideoFrameDelta);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000348 }
349
350 int dst_width = streaminfos_[stream_idx].width;
351 int dst_height = streaminfos_[stream_idx].height;
352 // If scaling isn't required, because the input resolution
353 // matches the destination or the input image is empty (e.g.
354 // a keyframe request for encoders with internal camera
noahricfe3654d2016-07-01 09:05:54 -0700355 // sources) or the source image has a native handle, pass the image on
356 // directly. Otherwise, we'll scale it to match what the encoder expects
357 // (below).
358 // For texture frames, the underlying encoder is expected to be able to
359 // correctly sample/scale the source texture.
360 // TODO(perkj): ensure that works going forward, and figure out how this
361 // affects webrtc:5683.
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000362 if ((dst_width == src_width && dst_height == src_height) ||
Magnus Jedvert72dbe2a2017-06-10 17:03:37 +0000363 input_image.video_frame_buffer()->type() ==
364 VideoFrameBuffer::Type::kNative) {
noahric57779102016-05-25 06:48:46 -0700365 int ret = streaminfos_[stream_idx].encoder->Encode(
366 input_image, codec_specific_info, &stream_frame_types);
367 if (ret != WEBRTC_VIDEO_CODEC_OK) {
368 return ret;
369 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000370 } else {
nisse64ec8f82016-09-27 00:17:25 -0700371 rtc::scoped_refptr<I420Buffer> dst_buffer =
Magnus Jedvert72dbe2a2017-06-10 17:03:37 +0000372 I420Buffer::Create(dst_width, dst_height);
373 rtc::scoped_refptr<I420BufferInterface> src_buffer =
374 input_image.video_frame_buffer()->ToI420();
375 libyuv::I420Scale(src_buffer->DataY(), src_buffer->StrideY(),
376 src_buffer->DataU(), src_buffer->StrideU(),
377 src_buffer->DataV(), src_buffer->StrideV(), src_width,
378 src_height, dst_buffer->MutableDataY(),
379 dst_buffer->StrideY(), dst_buffer->MutableDataU(),
380 dst_buffer->StrideU(), dst_buffer->MutableDataV(),
381 dst_buffer->StrideV(), dst_width, dst_height,
nissec9c142f2016-05-17 04:05:47 -0700382 libyuv::kFilterBilinear);
nisse64ec8f82016-09-27 00:17:25 -0700383
noahric57779102016-05-25 06:48:46 -0700384 int ret = streaminfos_[stream_idx].encoder->Encode(
nisse64ec8f82016-09-27 00:17:25 -0700385 VideoFrame(dst_buffer, input_image.timestamp(),
386 input_image.render_time_ms(), webrtc::kVideoRotation_0),
387 codec_specific_info, &stream_frame_types);
noahric57779102016-05-25 06:48:46 -0700388 if (ret != WEBRTC_VIDEO_CODEC_OK) {
389 return ret;
390 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000391 }
392 }
393
394 return WEBRTC_VIDEO_CODEC_OK;
395}
396
397int SimulcastEncoderAdapter::RegisterEncodeCompleteCallback(
398 EncodedImageCallback* callback) {
brandtr5e171752017-05-23 03:32:16 -0700399 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000400 encoded_complete_callback_ = callback;
401 return WEBRTC_VIDEO_CODEC_OK;
402}
403
Erik Språng566124a2018-04-23 12:32:22 +0200404int SimulcastEncoderAdapter::SetRateAllocation(
405 const VideoBitrateAllocation& bitrate,
406 uint32_t new_framerate) {
brandtr5e171752017-05-23 03:32:16 -0700407 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
408
409 if (!Initialized()) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000410 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
brandtr5e171752017-05-23 03:32:16 -0700411 }
sprang647bf432016-11-10 06:46:20 -0800412
brandtr5e171752017-05-23 03:32:16 -0700413 if (new_framerate < 1) {
Erik Språng08127a92016-11-16 16:41:30 +0100414 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
brandtr5e171752017-05-23 03:32:16 -0700415 }
Erik Språng08127a92016-11-16 16:41:30 +0100416
brandtr5e171752017-05-23 03:32:16 -0700417 if (codec_.maxBitrate > 0 && bitrate.get_sum_kbps() > codec_.maxBitrate) {
Erik Språng08127a92016-11-16 16:41:30 +0100418 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
brandtr5e171752017-05-23 03:32:16 -0700419 }
Erik Språng08127a92016-11-16 16:41:30 +0100420
421 if (bitrate.get_sum_bps() > 0) {
sprang1369c832016-11-10 08:30:33 -0800422 // Make sure the bitrate fits the configured min bitrates. 0 is a special
423 // value that means paused, though, so leave it alone.
brandtr5e171752017-05-23 03:32:16 -0700424 if (bitrate.get_sum_kbps() < codec_.minBitrate) {
Erik Språng08127a92016-11-16 16:41:30 +0100425 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
brandtr5e171752017-05-23 03:32:16 -0700426 }
Erik Språng08127a92016-11-16 16:41:30 +0100427
sprang1369c832016-11-10 08:30:33 -0800428 if (codec_.numberOfSimulcastStreams > 0 &&
Erik Språng08127a92016-11-16 16:41:30 +0100429 bitrate.get_sum_kbps() < codec_.simulcastStream[0].minBitrate) {
430 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
sprang1369c832016-11-10 08:30:33 -0800431 }
sprang1369c832016-11-10 08:30:33 -0800432 }
Erik Språng08127a92016-11-16 16:41:30 +0100433
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000434 codec_.maxFramerate = new_framerate;
435
sprang1369c832016-11-10 08:30:33 -0800436 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
Erik Språng08127a92016-11-16 16:41:30 +0100437 uint32_t stream_bitrate_kbps =
438 bitrate.GetSpatialLayerSum(stream_idx) / 1000;
439
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000440 // Need a key frame if we have not sent this stream before.
Erik Språng78ce6192016-09-12 16:04:43 +0200441 if (stream_bitrate_kbps > 0 && !streaminfos_[stream_idx].send_stream) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000442 streaminfos_[stream_idx].key_frame_request = true;
443 }
Erik Språng78ce6192016-09-12 16:04:43 +0200444 streaminfos_[stream_idx].send_stream = stream_bitrate_kbps > 0;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000445
Erik Språng08127a92016-11-16 16:41:30 +0100446 // Slice the temporal layers out of the full allocation and pass it on to
447 // the encoder handling the current simulcast stream.
Erik Språng566124a2018-04-23 12:32:22 +0200448 VideoBitrateAllocation stream_allocation;
brandtr5e171752017-05-23 03:32:16 -0700449 for (int i = 0; i < kMaxTemporalStreams; ++i) {
erikvarga@webrtc.org01f2ec32017-11-15 14:58:23 +0100450 if (bitrate.HasBitrate(stream_idx, i)) {
451 stream_allocation.SetBitrate(0, i, bitrate.GetBitrate(stream_idx, i));
452 }
brandtr5e171752017-05-23 03:32:16 -0700453 }
Erik Språng08127a92016-11-16 16:41:30 +0100454 streaminfos_[stream_idx].encoder->SetRateAllocation(stream_allocation,
455 new_framerate);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000456 }
457
458 return WEBRTC_VIDEO_CODEC_OK;
459}
460
brandtr5e171752017-05-23 03:32:16 -0700461// TODO(brandtr): Add task checker to this member function, when all encoder
462// callbacks are coming in on the encoder queue.
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700463EncodedImageCallback::Result SimulcastEncoderAdapter::OnEncodedImage(
Noah Richards41ee1ea2015-04-15 09:24:26 -0700464 size_t stream_idx,
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000465 const EncodedImage& encodedImage,
466 const CodecSpecificInfo* codecSpecificInfo,
467 const RTPFragmentationHeader* fragmentation) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200468 EncodedImage stream_image(encodedImage);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000469 CodecSpecificInfo stream_codec_specific = *codecSpecificInfo;
Niels Möllerd3b8c632018-08-27 15:33:42 +0200470
471 stream_image.SetSpatialIndex(stream_idx);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000472
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700473 return encoded_complete_callback_->OnEncodedImage(
Niels Möllerd3b8c632018-08-27 15:33:42 +0200474 stream_image, &stream_codec_specific, fragmentation);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000475}
476
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000477void SimulcastEncoderAdapter::PopulateStreamCodec(
brandtr5e171752017-05-23 03:32:16 -0700478 const webrtc::VideoCodec& inst,
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000479 int stream_index,
Erik Språng78ce6192016-09-12 16:04:43 +0200480 uint32_t start_bitrate_kbps,
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000481 bool highest_resolution_stream,
Erik Språng78ce6192016-09-12 16:04:43 +0200482 webrtc::VideoCodec* stream_codec) {
brandtr5e171752017-05-23 03:32:16 -0700483 *stream_codec = inst;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000484
485 // Stream specific settings.
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000486 stream_codec->numberOfSimulcastStreams = 0;
brandtr5e171752017-05-23 03:32:16 -0700487 stream_codec->width = inst.simulcastStream[stream_index].width;
488 stream_codec->height = inst.simulcastStream[stream_index].height;
489 stream_codec->maxBitrate = inst.simulcastStream[stream_index].maxBitrate;
490 stream_codec->minBitrate = inst.simulcastStream[stream_index].minBitrate;
491 stream_codec->qpMax = inst.simulcastStream[stream_index].qpMax;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000492 // Settings that are based on stream/resolution.
brandtr5e171752017-05-23 03:32:16 -0700493 const bool lowest_resolution_stream = (stream_index == 0);
Ilya Nikolaevskiyc30a1312018-08-27 11:07:48 +0200494 if (lowest_resolution_stream) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000495 // Settings for lowest spatial resolutions.
Ilya Nikolaevskiyc30a1312018-08-27 11:07:48 +0200496 if (inst.mode == VideoCodecMode::kScreensharing) {
497 if (experimental_boosted_screenshare_qp_) {
498 stream_codec->qpMax = *experimental_boosted_screenshare_qp_;
499 }
500 } else {
501 stream_codec->qpMax = kLowestResMaxQp;
502 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000503 }
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +0200504 if (inst.codecType == webrtc::kVideoCodecVP8) {
505 stream_codec->VP8()->numberOfTemporalLayers =
506 inst.simulcastStream[stream_index].numberOfTemporalLayers;
507 if (!highest_resolution_stream) {
508 // For resolutions below CIF, set the codec |complexity| parameter to
509 // kComplexityHigher, which maps to cpu_used = -4.
510 int pixels_per_frame = stream_codec->width * stream_codec->height;
511 if (pixels_per_frame < 352 * 288) {
512 stream_codec->VP8()->complexity =
513 webrtc::VideoCodecComplexity::kComplexityHigher;
514 }
515 // Turn off denoising for all streams but the highest resolution.
516 stream_codec->VP8()->denoisingOn = false;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000517 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000518 }
519 // TODO(ronghuawu): what to do with targetBitrate.
520
Erik Språng78ce6192016-09-12 16:04:43 +0200521 stream_codec->startBitrate = start_bitrate_kbps;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000522}
523
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000524bool SimulcastEncoderAdapter::Initialized() const {
brandtr5e171752017-05-23 03:32:16 -0700525 return rtc::AtomicOps::AcquireLoad(&inited_) == 1;
526}
527
528void SimulcastEncoderAdapter::DestroyStoredEncoders() {
529 while (!stored_encoders_.empty()) {
brandtr5e171752017-05-23 03:32:16 -0700530 stored_encoders_.pop();
531 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000532}
533
Erik Språng9b5b0702018-11-01 14:52:30 +0100534VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const {
Erik Språng75de46a2018-11-07 14:53:32 +0100535 return encoder_info_;
pbosecd21b42016-01-07 08:03:05 -0800536}
537
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000538} // namespace webrtc