blob: 38d84bf08575c6fc584735135822cc3f8c8f3cb4 [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>
14
15// NOTE(ajm): Path provided by gyp.
16#include "libyuv/scale.h" // NOLINT
17
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "api/video/i420_buffer.h"
Magnus Jedvertdf4883d2017-11-17 14:44:55 +010019#include "api/video_codecs/video_encoder_factory.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "media/engine/scopedvideoencoder.h"
21#include "modules/video_coding/codecs/vp8/screenshare_layers.h"
22#include "modules/video_coding/codecs/vp8/simulcast_rate_allocator.h"
23#include "rtc_base/checks.h"
24#include "system_wrappers/include/clock.h"
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000025
26namespace {
27
28const unsigned int kDefaultMinQp = 2;
29const unsigned int kDefaultMaxQp = 56;
30// Max qp for lowest spatial resolution when doing simulcast.
31const unsigned int kLowestResMaxQp = 45;
32
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000033uint32_t SumStreamMaxBitrate(int streams, const webrtc::VideoCodec& codec) {
34 uint32_t bitrate_sum = 0;
35 for (int i = 0; i < streams; ++i) {
36 bitrate_sum += codec.simulcastStream[i].maxBitrate;
37 }
38 return bitrate_sum;
39}
40
41int NumberOfStreams(const webrtc::VideoCodec& codec) {
42 int streams =
43 codec.numberOfSimulcastStreams < 1 ? 1 : codec.numberOfSimulcastStreams;
44 uint32_t simulcast_max_bitrate = SumStreamMaxBitrate(streams, codec);
45 if (simulcast_max_bitrate == 0) {
46 streams = 1;
47 }
48 return streams;
49}
50
51bool ValidSimulcastResolutions(const webrtc::VideoCodec& codec,
52 int num_streams) {
53 if (codec.width != codec.simulcastStream[num_streams - 1].width ||
54 codec.height != codec.simulcastStream[num_streams - 1].height) {
55 return false;
56 }
57 for (int i = 0; i < num_streams; ++i) {
58 if (codec.width * codec.simulcastStream[i].height !=
59 codec.height * codec.simulcastStream[i].width) {
60 return false;
61 }
62 }
63 return true;
64}
65
66int VerifyCodec(const webrtc::VideoCodec* inst) {
brandtr5e171752017-05-23 03:32:16 -070067 if (inst == nullptr) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000068 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
69 }
70 if (inst->maxFramerate < 1) {
71 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
72 }
73 // allow zero to represent an unspecified maxBitRate
74 if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {
75 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
76 }
77 if (inst->width <= 1 || inst->height <= 1) {
78 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
79 }
hta257dc392016-10-25 09:05:06 -070080 if (inst->VP8().automaticResizeOn && inst->numberOfSimulcastStreams > 1) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +000081 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
82 }
83 return WEBRTC_VIDEO_CODEC_OK;
84}
85
Noah Richards41ee1ea2015-04-15 09:24:26 -070086// An EncodedImageCallback implementation that forwards on calls to a
87// SimulcastEncoderAdapter, but with the stream index it's registered with as
88// the first parameter to Encoded.
89class AdapterEncodedImageCallback : public webrtc::EncodedImageCallback {
90 public:
91 AdapterEncodedImageCallback(webrtc::SimulcastEncoderAdapter* adapter,
92 size_t stream_idx)
93 : adapter_(adapter), stream_idx_(stream_idx) {}
94
Sergey Ulanov525df3f2016-08-02 17:46:41 -070095 EncodedImageCallback::Result OnEncodedImage(
96 const webrtc::EncodedImage& encoded_image,
97 const webrtc::CodecSpecificInfo* codec_specific_info,
98 const webrtc::RTPFragmentationHeader* fragmentation) override {
99 return adapter_->OnEncodedImage(stream_idx_, encoded_image,
100 codec_specific_info, fragmentation);
Noah Richards41ee1ea2015-04-15 09:24:26 -0700101 }
102
103 private:
104 webrtc::SimulcastEncoderAdapter* const adapter_;
105 const size_t stream_idx_;
106};
107
Erik Språng08127a92016-11-16 16:41:30 +0100108// Utility class used to adapt the simulcast id as reported by the temporal
109// layers factory, since each sub-encoder will report stream 0.
110class TemporalLayersFactoryAdapter : public webrtc::TemporalLayersFactory {
111 public:
112 TemporalLayersFactoryAdapter(int adapted_simulcast_id,
113 const TemporalLayersFactory& tl_factory)
114 : adapted_simulcast_id_(adapted_simulcast_id), tl_factory_(tl_factory) {}
115 ~TemporalLayersFactoryAdapter() override {}
116 webrtc::TemporalLayers* Create(int simulcast_id,
117 int temporal_layers,
118 uint8_t initial_tl0_pic_idx) const override {
119 return tl_factory_.Create(adapted_simulcast_id_, temporal_layers,
120 initial_tl0_pic_idx);
121 }
Ilya Nikolaevskiybf352982017-10-02 10:08:25 +0200122 std::unique_ptr<webrtc::TemporalLayersChecker> CreateChecker(
123 int simulcast_id,
124 int temporal_layers,
125 uint8_t initial_tl0_pic_idx) const override {
126 return tl_factory_.CreateChecker(adapted_simulcast_id_, temporal_layers,
127 initial_tl0_pic_idx);
128 }
Erik Språng08127a92016-11-16 16:41:30 +0100129
130 const int adapted_simulcast_id_;
131 const TemporalLayersFactory& tl_factory_;
132};
133
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000134} // namespace
135
136namespace webrtc {
137
Magnus Jedvertdf4883d2017-11-17 14:44:55 +0100138SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory)
139 : inited_(0),
140 factory_(factory),
141 cricket_factory_(nullptr),
142 encoded_complete_callback_(nullptr),
143 implementation_name_("SimulcastEncoderAdapter") {
144 // The adapter is typically created on the worker thread, but operated on
145 // the encoder task queue.
146 encoder_queue_.Detach();
147
148 memset(&codec_, 0, sizeof(webrtc::VideoCodec));
149}
150
Zhi Huangaea84f52017-11-16 18:46:27 +0000151SimulcastEncoderAdapter::SimulcastEncoderAdapter(
152 cricket::WebRtcVideoEncoderFactory* factory)
brandtr5e171752017-05-23 03:32:16 -0700153 : inited_(0),
Magnus Jedvertdf4883d2017-11-17 14:44:55 +0100154 factory_(nullptr),
155 cricket_factory_(factory),
Erik Språng78ce6192016-09-12 16:04:43 +0200156 encoded_complete_callback_(nullptr),
Peter Boströma5dec162016-01-20 15:53:55 +0100157 implementation_name_("SimulcastEncoderAdapter") {
brandtr5e171752017-05-23 03:32:16 -0700158 // The adapter is typically created on the worker thread, but operated on
159 // the encoder task queue.
160 encoder_queue_.Detach();
161
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000162 memset(&codec_, 0, sizeof(webrtc::VideoCodec));
163}
164
165SimulcastEncoderAdapter::~SimulcastEncoderAdapter() {
brandtr5e171752017-05-23 03:32:16 -0700166 RTC_DCHECK(!Initialized());
167 DestroyStoredEncoders();
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000168}
169
170int SimulcastEncoderAdapter::Release() {
brandtr5e171752017-05-23 03:32:16 -0700171 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
172
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000173 while (!streaminfos_.empty()) {
magjed3f897582017-08-28 08:05:42 -0700174 std::unique_ptr<VideoEncoder> encoder =
175 std::move(streaminfos_.back().encoder);
brandtr5e171752017-05-23 03:32:16 -0700176 // Even though it seems very unlikely, there are no guarantees that the
Rasmus Brandt9cf9f752017-09-28 13:02:58 +0200177 // encoder will not call back after being Release()'d. Therefore, we first
178 // disable the callbacks here.
brandtr5e171752017-05-23 03:32:16 -0700179 encoder->RegisterEncodeCompleteCallback(nullptr);
Rasmus Brandt9cf9f752017-09-28 13:02:58 +0200180 encoder->Release();
brandtr5e171752017-05-23 03:32:16 -0700181 streaminfos_.pop_back(); // Deletes callback adapter.
magjed3f897582017-08-28 08:05:42 -0700182 stored_encoders_.push(std::move(encoder));
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000183 }
brandtr5e171752017-05-23 03:32:16 -0700184
185 // It's legal to move the encoder to another queue now.
186 encoder_queue_.Detach();
187
188 rtc::AtomicOps::ReleaseStore(&inited_, 0);
189
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000190 return WEBRTC_VIDEO_CODEC_OK;
191}
192
193int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
194 int number_of_cores,
195 size_t max_payload_size) {
brandtr5e171752017-05-23 03:32:16 -0700196 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
197
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000198 if (number_of_cores < 1) {
199 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
200 }
201
202 int ret = VerifyCodec(inst);
203 if (ret < 0) {
204 return ret;
205 }
206
207 ret = Release();
208 if (ret < 0) {
209 return ret;
210 }
211
212 int number_of_streams = NumberOfStreams(*inst);
brandtr5e171752017-05-23 03:32:16 -0700213 RTC_DCHECK_LE(number_of_streams, kMaxSimulcastStreams);
Peter Boströmd53c3892016-03-30 17:03:52 +0200214 const bool doing_simulcast = (number_of_streams > 1);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000215
216 if (doing_simulcast && !ValidSimulcastResolutions(*inst, number_of_streams)) {
217 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
218 }
219
220 codec_ = *inst;
Erik Språng08127a92016-11-16 16:41:30 +0100221 SimulcastRateAllocator rate_allocator(codec_, nullptr);
222 BitrateAllocation allocation = rate_allocator.GetAllocation(
223 codec_.startBitrate * 1000, codec_.maxFramerate);
224 std::vector<uint32_t> start_bitrates;
225 for (int i = 0; i < kMaxSimulcastStreams; ++i) {
226 uint32_t stream_bitrate = allocation.GetSpatialLayerSum(i) / 1000;
227 start_bitrates.push_back(stream_bitrate);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000228 }
229
Peter Boströma5dec162016-01-20 15:53:55 +0100230 std::string implementation_name;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000231 // Create |number_of_streams| of encoder instances and init them.
232 for (int i = 0; i < number_of_streams; ++i) {
233 VideoCodec stream_codec;
Erik Språng78ce6192016-09-12 16:04:43 +0200234 uint32_t start_bitrate_kbps = start_bitrates[i];
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000235 if (!doing_simulcast) {
236 stream_codec = codec_;
237 stream_codec.numberOfSimulcastStreams = 1;
238 } else {
Erik Språng78ce6192016-09-12 16:04:43 +0200239 // Cap start bitrate to the min bitrate in order to avoid strange codec
240 // behavior. Since sending sending will be false, this should not matter.
241 start_bitrate_kbps =
242 std::max(codec_.simulcastStream[i].minBitrate, start_bitrate_kbps);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000243 bool highest_resolution_stream = (i == (number_of_streams - 1));
brandtr5e171752017-05-23 03:32:16 -0700244 PopulateStreamCodec(codec_, i, start_bitrate_kbps,
Erik Språng78ce6192016-09-12 16:04:43 +0200245 highest_resolution_stream, &stream_codec);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000246 }
Erik Språng08127a92016-11-16 16:41:30 +0100247 TemporalLayersFactoryAdapter tl_factory_adapter(i,
248 *codec_.VP8()->tl_factory);
249 stream_codec.VP8()->tl_factory = &tl_factory_adapter;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000250
251 // TODO(ronghuawu): Remove once this is handled in VP8EncoderImpl.
252 if (stream_codec.qpMax < kDefaultMinQp) {
253 stream_codec.qpMax = kDefaultMaxQp;
254 }
255
brandtr5e171752017-05-23 03:32:16 -0700256 // If an existing encoder instance exists, reuse it.
257 // TODO(brandtr): Set initial RTP state (e.g., picture_id/tl0_pic_idx) here,
258 // when we start storing that state outside the encoder wrappers.
magjed3f897582017-08-28 08:05:42 -0700259 std::unique_ptr<VideoEncoder> encoder;
brandtr5e171752017-05-23 03:32:16 -0700260 if (!stored_encoders_.empty()) {
magjed3f897582017-08-28 08:05:42 -0700261 encoder = std::move(stored_encoders_.top());
brandtr5e171752017-05-23 03:32:16 -0700262 stored_encoders_.pop();
263 } else {
Magnus Jedvertdf4883d2017-11-17 14:44:55 +0100264 encoder = factory_ ? factory_->CreateVideoEncoder(SdpVideoFormat("VP8"))
265 : CreateScopedVideoEncoder(cricket_factory_,
266 cricket::VideoCodec("VP8"));
brandtr5e171752017-05-23 03:32:16 -0700267 }
268
philipelcce46fc2015-12-21 03:04:49 -0800269 ret = encoder->InitEncode(&stream_codec, number_of_cores, max_payload_size);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000270 if (ret < 0) {
noahrice5ba75a2016-12-12 13:08:27 -0800271 // Explicitly destroy the current encoder; because we haven't registered a
272 // StreamInfo for it yet, Release won't do anything about it.
magjed3f897582017-08-28 08:05:42 -0700273 encoder.reset();
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000274 Release();
275 return ret;
276 }
brandtr5e171752017-05-23 03:32:16 -0700277 std::unique_ptr<EncodedImageCallback> callback(
278 new AdapterEncodedImageCallback(this, i));
279 encoder->RegisterEncodeCompleteCallback(callback.get());
magjed3f897582017-08-28 08:05:42 -0700280 streaminfos_.emplace_back(std::move(encoder), std::move(callback),
281 stream_codec.width, stream_codec.height,
282 start_bitrate_kbps > 0);
brandtr5e171752017-05-23 03:32:16 -0700283
284 if (i != 0) {
Peter Boströma5dec162016-01-20 15:53:55 +0100285 implementation_name += ", ";
brandtr5e171752017-05-23 03:32:16 -0700286 }
Peter Boströma5dec162016-01-20 15:53:55 +0100287 implementation_name += streaminfos_[i].encoder->ImplementationName();
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) {
291 implementation_name_ =
292 "SimulcastEncoderAdapter (" + implementation_name + ")";
293 } else {
294 implementation_name_ = implementation_name;
295 }
brandtr5e171752017-05-23 03:32:16 -0700296
297 // To save memory, don't store encoders that we don't use.
298 DestroyStoredEncoders();
299
300 rtc::AtomicOps::ReleaseStore(&inited_, 1);
301
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000302 return WEBRTC_VIDEO_CODEC_OK;
303}
304
305int SimulcastEncoderAdapter::Encode(
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700306 const VideoFrame& input_image,
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000307 const CodecSpecificInfo* codec_specific_info,
pbos22993e12015-10-19 02:39:06 -0700308 const std::vector<FrameType>* frame_types) {
brandtr5e171752017-05-23 03:32:16 -0700309 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
310
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000311 if (!Initialized()) {
312 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
313 }
brandtr5e171752017-05-23 03:32:16 -0700314 if (encoded_complete_callback_ == nullptr) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000315 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
316 }
317
318 // All active streams should generate a key frame if
319 // a key frame is requested by any stream.
320 bool send_key_frame = false;
321 if (frame_types) {
322 for (size_t i = 0; i < frame_types->size(); ++i) {
Peter Boström49e196a2015-10-23 15:58:18 +0200323 if (frame_types->at(i) == kVideoFrameKey) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000324 send_key_frame = true;
325 break;
326 }
327 }
328 }
329 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
330 if (streaminfos_[stream_idx].key_frame_request &&
331 streaminfos_[stream_idx].send_stream) {
332 send_key_frame = true;
333 break;
334 }
335 }
336
337 int src_width = input_image.width();
338 int src_height = input_image.height();
339 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
Peter Boström5d0379d2015-10-06 14:04:51 +0200340 // Don't encode frames in resolutions that we don't intend to send.
brandtr5e171752017-05-23 03:32:16 -0700341 if (!streaminfos_[stream_idx].send_stream) {
Peter Boström5d0379d2015-10-06 14:04:51 +0200342 continue;
brandtr5e171752017-05-23 03:32:16 -0700343 }
Peter Boström5d0379d2015-10-06 14:04:51 +0200344
pbos22993e12015-10-19 02:39:06 -0700345 std::vector<FrameType> stream_frame_types;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000346 if (send_key_frame) {
Peter Boström49e196a2015-10-23 15:58:18 +0200347 stream_frame_types.push_back(kVideoFrameKey);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000348 streaminfos_[stream_idx].key_frame_request = false;
349 } else {
Peter Boström49e196a2015-10-23 15:58:18 +0200350 stream_frame_types.push_back(kVideoFrameDelta);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000351 }
352
353 int dst_width = streaminfos_[stream_idx].width;
354 int dst_height = streaminfos_[stream_idx].height;
355 // If scaling isn't required, because the input resolution
356 // matches the destination or the input image is empty (e.g.
357 // a keyframe request for encoders with internal camera
noahricfe3654d2016-07-01 09:05:54 -0700358 // sources) or the source image has a native handle, pass the image on
359 // directly. Otherwise, we'll scale it to match what the encoder expects
360 // (below).
361 // For texture frames, the underlying encoder is expected to be able to
362 // correctly sample/scale the source texture.
363 // TODO(perkj): ensure that works going forward, and figure out how this
364 // affects webrtc:5683.
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000365 if ((dst_width == src_width && dst_height == src_height) ||
Magnus Jedvert72dbe2a2017-06-10 17:03:37 +0000366 input_image.video_frame_buffer()->type() ==
367 VideoFrameBuffer::Type::kNative) {
noahric57779102016-05-25 06:48:46 -0700368 int ret = streaminfos_[stream_idx].encoder->Encode(
369 input_image, codec_specific_info, &stream_frame_types);
370 if (ret != WEBRTC_VIDEO_CODEC_OK) {
371 return ret;
372 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000373 } else {
nisse64ec8f82016-09-27 00:17:25 -0700374 rtc::scoped_refptr<I420Buffer> dst_buffer =
Magnus Jedvert72dbe2a2017-06-10 17:03:37 +0000375 I420Buffer::Create(dst_width, dst_height);
376 rtc::scoped_refptr<I420BufferInterface> src_buffer =
377 input_image.video_frame_buffer()->ToI420();
378 libyuv::I420Scale(src_buffer->DataY(), src_buffer->StrideY(),
379 src_buffer->DataU(), src_buffer->StrideU(),
380 src_buffer->DataV(), src_buffer->StrideV(), src_width,
381 src_height, dst_buffer->MutableDataY(),
382 dst_buffer->StrideY(), dst_buffer->MutableDataU(),
383 dst_buffer->StrideU(), dst_buffer->MutableDataV(),
384 dst_buffer->StrideV(), dst_width, dst_height,
nissec9c142f2016-05-17 04:05:47 -0700385 libyuv::kFilterBilinear);
nisse64ec8f82016-09-27 00:17:25 -0700386
noahric57779102016-05-25 06:48:46 -0700387 int ret = streaminfos_[stream_idx].encoder->Encode(
nisse64ec8f82016-09-27 00:17:25 -0700388 VideoFrame(dst_buffer, input_image.timestamp(),
389 input_image.render_time_ms(), webrtc::kVideoRotation_0),
390 codec_specific_info, &stream_frame_types);
noahric57779102016-05-25 06:48:46 -0700391 if (ret != WEBRTC_VIDEO_CODEC_OK) {
392 return ret;
393 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000394 }
395 }
396
397 return WEBRTC_VIDEO_CODEC_OK;
398}
399
400int SimulcastEncoderAdapter::RegisterEncodeCompleteCallback(
401 EncodedImageCallback* callback) {
brandtr5e171752017-05-23 03:32:16 -0700402 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000403 encoded_complete_callback_ = callback;
404 return WEBRTC_VIDEO_CODEC_OK;
405}
406
407int SimulcastEncoderAdapter::SetChannelParameters(uint32_t packet_loss,
pkasting@chromium.org16825b12015-01-12 21:51:21 +0000408 int64_t rtt) {
brandtr5e171752017-05-23 03:32:16 -0700409 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000410 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
411 streaminfos_[stream_idx].encoder->SetChannelParameters(packet_loss, rtt);
412 }
413 return WEBRTC_VIDEO_CODEC_OK;
414}
415
Erik Språng08127a92016-11-16 16:41:30 +0100416int SimulcastEncoderAdapter::SetRateAllocation(const BitrateAllocation& bitrate,
417 uint32_t new_framerate) {
brandtr5e171752017-05-23 03:32:16 -0700418 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
419
420 if (!Initialized()) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000421 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
brandtr5e171752017-05-23 03:32:16 -0700422 }
sprang647bf432016-11-10 06:46:20 -0800423
brandtr5e171752017-05-23 03:32:16 -0700424 if (new_framerate < 1) {
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
brandtr5e171752017-05-23 03:32:16 -0700428 if (codec_.maxBitrate > 0 && bitrate.get_sum_kbps() > codec_.maxBitrate) {
Erik Språng08127a92016-11-16 16:41:30 +0100429 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
brandtr5e171752017-05-23 03:32:16 -0700430 }
Erik Språng08127a92016-11-16 16:41:30 +0100431
432 if (bitrate.get_sum_bps() > 0) {
sprang1369c832016-11-10 08:30:33 -0800433 // Make sure the bitrate fits the configured min bitrates. 0 is a special
434 // value that means paused, though, so leave it alone.
brandtr5e171752017-05-23 03:32:16 -0700435 if (bitrate.get_sum_kbps() < codec_.minBitrate) {
Erik Språng08127a92016-11-16 16:41:30 +0100436 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
brandtr5e171752017-05-23 03:32:16 -0700437 }
Erik Språng08127a92016-11-16 16:41:30 +0100438
sprang1369c832016-11-10 08:30:33 -0800439 if (codec_.numberOfSimulcastStreams > 0 &&
Erik Språng08127a92016-11-16 16:41:30 +0100440 bitrate.get_sum_kbps() < codec_.simulcastStream[0].minBitrate) {
441 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
sprang1369c832016-11-10 08:30:33 -0800442 }
sprang1369c832016-11-10 08:30:33 -0800443 }
Erik Språng08127a92016-11-16 16:41:30 +0100444
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000445 codec_.maxFramerate = new_framerate;
446
sprang1369c832016-11-10 08:30:33 -0800447 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
Erik Språng08127a92016-11-16 16:41:30 +0100448 uint32_t stream_bitrate_kbps =
449 bitrate.GetSpatialLayerSum(stream_idx) / 1000;
450
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000451 // Need a key frame if we have not sent this stream before.
Erik Språng78ce6192016-09-12 16:04:43 +0200452 if (stream_bitrate_kbps > 0 && !streaminfos_[stream_idx].send_stream) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000453 streaminfos_[stream_idx].key_frame_request = true;
454 }
Erik Språng78ce6192016-09-12 16:04:43 +0200455 streaminfos_[stream_idx].send_stream = stream_bitrate_kbps > 0;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000456
Erik Språng08127a92016-11-16 16:41:30 +0100457 // Slice the temporal layers out of the full allocation and pass it on to
458 // the encoder handling the current simulcast stream.
459 BitrateAllocation stream_allocation;
brandtr5e171752017-05-23 03:32:16 -0700460 for (int i = 0; i < kMaxTemporalStreams; ++i) {
erikvarga@webrtc.org01f2ec32017-11-15 14:58:23 +0100461 if (bitrate.HasBitrate(stream_idx, i)) {
462 stream_allocation.SetBitrate(0, i, bitrate.GetBitrate(stream_idx, i));
463 }
brandtr5e171752017-05-23 03:32:16 -0700464 }
Erik Språng08127a92016-11-16 16:41:30 +0100465 streaminfos_[stream_idx].encoder->SetRateAllocation(stream_allocation,
466 new_framerate);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000467 }
468
469 return WEBRTC_VIDEO_CODEC_OK;
470}
471
brandtr5e171752017-05-23 03:32:16 -0700472// TODO(brandtr): Add task checker to this member function, when all encoder
473// callbacks are coming in on the encoder queue.
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700474EncodedImageCallback::Result SimulcastEncoderAdapter::OnEncodedImage(
Noah Richards41ee1ea2015-04-15 09:24:26 -0700475 size_t stream_idx,
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000476 const EncodedImage& encodedImage,
477 const CodecSpecificInfo* codecSpecificInfo,
478 const RTPFragmentationHeader* fragmentation) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000479 CodecSpecificInfo stream_codec_specific = *codecSpecificInfo;
perkj275afc52016-09-01 00:21:16 -0700480 stream_codec_specific.codec_name = implementation_name_.c_str();
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000481 CodecSpecificInfoVP8* vp8Info = &(stream_codec_specific.codecSpecific.VP8);
482 vp8Info->simulcastIdx = stream_idx;
483
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700484 return encoded_complete_callback_->OnEncodedImage(
Peter Boström5d0379d2015-10-06 14:04:51 +0200485 encodedImage, &stream_codec_specific, fragmentation);
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000486}
487
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000488void SimulcastEncoderAdapter::PopulateStreamCodec(
brandtr5e171752017-05-23 03:32:16 -0700489 const webrtc::VideoCodec& inst,
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000490 int stream_index,
Erik Språng78ce6192016-09-12 16:04:43 +0200491 uint32_t start_bitrate_kbps,
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000492 bool highest_resolution_stream,
Erik Språng78ce6192016-09-12 16:04:43 +0200493 webrtc::VideoCodec* stream_codec) {
brandtr5e171752017-05-23 03:32:16 -0700494 *stream_codec = inst;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000495
496 // Stream specific settings.
hta257dc392016-10-25 09:05:06 -0700497 stream_codec->VP8()->numberOfTemporalLayers =
brandtr5e171752017-05-23 03:32:16 -0700498 inst.simulcastStream[stream_index].numberOfTemporalLayers;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000499 stream_codec->numberOfSimulcastStreams = 0;
brandtr5e171752017-05-23 03:32:16 -0700500 stream_codec->width = inst.simulcastStream[stream_index].width;
501 stream_codec->height = inst.simulcastStream[stream_index].height;
502 stream_codec->maxBitrate = inst.simulcastStream[stream_index].maxBitrate;
503 stream_codec->minBitrate = inst.simulcastStream[stream_index].minBitrate;
504 stream_codec->qpMax = inst.simulcastStream[stream_index].qpMax;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000505 // Settings that are based on stream/resolution.
brandtr5e171752017-05-23 03:32:16 -0700506 const bool lowest_resolution_stream = (stream_index == 0);
507 if (lowest_resolution_stream) {
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000508 // Settings for lowest spatial resolutions.
509 stream_codec->qpMax = kLowestResMaxQp;
510 }
511 if (!highest_resolution_stream) {
512 // For resolutions below CIF, set the codec |complexity| parameter to
513 // kComplexityHigher, which maps to cpu_used = -4.
514 int pixels_per_frame = stream_codec->width * stream_codec->height;
515 if (pixels_per_frame < 352 * 288) {
hta257dc392016-10-25 09:05:06 -0700516 stream_codec->VP8()->complexity = webrtc::kComplexityHigher;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000517 }
518 // Turn off denoising for all streams but the highest resolution.
hta257dc392016-10-25 09:05:06 -0700519 stream_codec->VP8()->denoisingOn = false;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000520 }
521 // TODO(ronghuawu): what to do with targetBitrate.
522
Erik Språng78ce6192016-09-12 16:04:43 +0200523 stream_codec->startBitrate = start_bitrate_kbps;
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000524}
525
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000526bool SimulcastEncoderAdapter::Initialized() const {
brandtr5e171752017-05-23 03:32:16 -0700527 return rtc::AtomicOps::AcquireLoad(&inited_) == 1;
528}
529
530void SimulcastEncoderAdapter::DestroyStoredEncoders() {
531 while (!stored_encoders_.empty()) {
brandtr5e171752017-05-23 03:32:16 -0700532 stored_encoders_.pop();
533 }
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000534}
535
pbos65e15ba2015-10-15 10:52:15 -0700536bool SimulcastEncoderAdapter::SupportsNativeHandle() const {
brandtr5e171752017-05-23 03:32:16 -0700537 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
pbos65e15ba2015-10-15 10:52:15 -0700538 // We should not be calling this method before streaminfos_ are configured.
539 RTC_DCHECK(!streaminfos_.empty());
noahricfe3654d2016-07-01 09:05:54 -0700540 for (const auto& streaminfo : streaminfos_) {
brandtr5e171752017-05-23 03:32:16 -0700541 if (!streaminfo.encoder->SupportsNativeHandle()) {
noahricfe3654d2016-07-01 09:05:54 -0700542 return false;
brandtr5e171752017-05-23 03:32:16 -0700543 }
noahricfe3654d2016-07-01 09:05:54 -0700544 }
545 return true;
pbos65e15ba2015-10-15 10:52:15 -0700546}
547
kthelgason876222f2016-11-29 01:44:11 -0800548VideoEncoder::ScalingSettings SimulcastEncoderAdapter::GetScalingSettings()
549 const {
brandtr5e171752017-05-23 03:32:16 -0700550 // TODO(brandtr): Investigate why the sequence checker below fails on mac.
551 // RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
kthelgason876222f2016-11-29 01:44:11 -0800552 // Turn off quality scaling for simulcast.
brandtr5e171752017-05-23 03:32:16 -0700553 if (!Initialized() || NumberOfStreams(codec_) != 1) {
kthelgason876222f2016-11-29 01:44:11 -0800554 return VideoEncoder::ScalingSettings(false);
brandtr5e171752017-05-23 03:32:16 -0700555 }
kthelgason876222f2016-11-29 01:44:11 -0800556 return streaminfos_[0].encoder->GetScalingSettings();
557}
558
pbosecd21b42016-01-07 08:03:05 -0800559const char* SimulcastEncoderAdapter::ImplementationName() const {
brandtr5e171752017-05-23 03:32:16 -0700560 RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_);
Peter Boströma5dec162016-01-20 15:53:55 +0100561 return implementation_name_.c_str();
pbosecd21b42016-01-07 08:03:05 -0800562}
563
pbos@webrtc.org9115cde2014-12-09 10:36:40 +0000564} // namespace webrtc