1 /*
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
11 #include "media/engine/simulcast_encoder_adapter.h"
12
13 #include <stdio.h>
14 #include <string.h>
15
16 #include <algorithm>
17 #include <cstdint>
18 #include <string>
19 #include <utility>
20
21 #include "api/scoped_refptr.h"
22 #include "api/video/i420_buffer.h"
23 #include "api/video/video_codec_constants.h"
24 #include "api/video/video_frame_buffer.h"
25 #include "api/video/video_rotation.h"
26 #include "api/video_codecs/video_encoder.h"
27 #include "api/video_codecs/video_encoder_factory.h"
28 #include "api/video_codecs/video_encoder_software_fallback_wrapper.h"
29 #include "media/base/video_common.h"
30 #include "modules/video_coding/include/video_error_codes.h"
31 #include "modules/video_coding/utility/simulcast_rate_allocator.h"
32 #include "rtc_base/atomic_ops.h"
33 #include "rtc_base/checks.h"
34 #include "rtc_base/experiments/rate_control_settings.h"
35 #include "rtc_base/logging.h"
36 #include "system_wrappers/include/field_trial.h"
37
38 namespace {
39
40 const unsigned int kDefaultMinQp = 2;
41 const unsigned int kDefaultMaxQp = 56;
42 // Max qp for lowest spatial resolution when doing simulcast.
43 const unsigned int kLowestResMaxQp = 45;
44
GetScreenshareBoostedQpValue()45 absl::optional<unsigned int> GetScreenshareBoostedQpValue() {
46 std::string experiment_group =
47 webrtc::field_trial::FindFullName("WebRTC-BoostedScreenshareQp");
48 unsigned int qp;
49 if (sscanf(experiment_group.c_str(), "%u", &qp) != 1)
50 return absl::nullopt;
51 qp = std::min(qp, 63u);
52 qp = std::max(qp, 1u);
53 return qp;
54 }
55
SumStreamMaxBitrate(int streams,const webrtc::VideoCodec & codec)56 uint32_t SumStreamMaxBitrate(int streams, const webrtc::VideoCodec& codec) {
57 uint32_t bitrate_sum = 0;
58 for (int i = 0; i < streams; ++i) {
59 bitrate_sum += codec.simulcastStream[i].maxBitrate;
60 }
61 return bitrate_sum;
62 }
63
NumberOfStreams(const webrtc::VideoCodec & codec)64 int NumberOfStreams(const webrtc::VideoCodec& codec) {
65 int streams =
66 codec.numberOfSimulcastStreams < 1 ? 1 : codec.numberOfSimulcastStreams;
67 uint32_t simulcast_max_bitrate = SumStreamMaxBitrate(streams, codec);
68 if (simulcast_max_bitrate == 0) {
69 streams = 1;
70 }
71 return streams;
72 }
73
NumActiveStreams(const webrtc::VideoCodec & codec)74 int NumActiveStreams(const webrtc::VideoCodec& codec) {
75 int num_configured_streams = NumberOfStreams(codec);
76 int num_active_streams = 0;
77 for (int i = 0; i < num_configured_streams; ++i) {
78 if (codec.simulcastStream[i].active) {
79 ++num_active_streams;
80 }
81 }
82 return num_active_streams;
83 }
84
VerifyCodec(const webrtc::VideoCodec * inst)85 int VerifyCodec(const webrtc::VideoCodec* inst) {
86 if (inst == nullptr) {
87 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
88 }
89 if (inst->maxFramerate < 1) {
90 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
91 }
92 // allow zero to represent an unspecified maxBitRate
93 if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {
94 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
95 }
96 if (inst->width <= 1 || inst->height <= 1) {
97 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
98 }
99 if (inst->codecType == webrtc::kVideoCodecVP8 &&
100 inst->VP8().automaticResizeOn && NumActiveStreams(*inst) > 1) {
101 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
102 }
103 return WEBRTC_VIDEO_CODEC_OK;
104 }
105
StreamResolutionCompare(const webrtc::SimulcastStream & a,const webrtc::SimulcastStream & b)106 bool StreamResolutionCompare(const webrtc::SimulcastStream& a,
107 const webrtc::SimulcastStream& b) {
108 return std::tie(a.height, a.width, a.maxBitrate, a.maxFramerate) <
109 std::tie(b.height, b.width, b.maxBitrate, b.maxFramerate);
110 }
111
112 // An EncodedImageCallback implementation that forwards on calls to a
113 // SimulcastEncoderAdapter, but with the stream index it's registered with as
114 // the first parameter to Encoded.
115 class AdapterEncodedImageCallback : public webrtc::EncodedImageCallback {
116 public:
AdapterEncodedImageCallback(webrtc::SimulcastEncoderAdapter * adapter,size_t stream_idx)117 AdapterEncodedImageCallback(webrtc::SimulcastEncoderAdapter* adapter,
118 size_t stream_idx)
119 : adapter_(adapter), stream_idx_(stream_idx) {}
120
OnEncodedImage(const webrtc::EncodedImage & encoded_image,const webrtc::CodecSpecificInfo * codec_specific_info,const webrtc::RTPFragmentationHeader * fragmentation)121 EncodedImageCallback::Result OnEncodedImage(
122 const webrtc::EncodedImage& encoded_image,
123 const webrtc::CodecSpecificInfo* codec_specific_info,
124 const webrtc::RTPFragmentationHeader* fragmentation) override {
125 return adapter_->OnEncodedImage(stream_idx_, encoded_image,
126 codec_specific_info, fragmentation);
127 }
128
129 private:
130 webrtc::SimulcastEncoderAdapter* const adapter_;
131 const size_t stream_idx_;
132 };
133 } // namespace
134
135 namespace webrtc {
136
SimulcastEncoderAdapter(VideoEncoderFactory * factory,const SdpVideoFormat & format)137 SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory,
138 const SdpVideoFormat& format)
139 : SimulcastEncoderAdapter(factory, nullptr, format) {}
140
SimulcastEncoderAdapter(VideoEncoderFactory * primary_factory,VideoEncoderFactory * fallback_factory,const SdpVideoFormat & format)141 SimulcastEncoderAdapter::SimulcastEncoderAdapter(
142 VideoEncoderFactory* primary_factory,
143 VideoEncoderFactory* fallback_factory,
144 const SdpVideoFormat& format)
145 : inited_(0),
146 primary_encoder_factory_(primary_factory),
147 fallback_encoder_factory_(fallback_factory),
148 video_format_(format),
149 encoded_complete_callback_(nullptr),
150 experimental_boosted_screenshare_qp_(GetScreenshareBoostedQpValue()),
151 boost_base_layer_quality_(RateControlSettings::ParseFromFieldTrials()
152 .Vp8BoostBaseLayerQuality()),
153 prefer_temporal_support_on_base_layer_(field_trial::IsEnabled(
154 "WebRTC-Video-PreferTemporalSupportOnBaseLayer")) {
155 RTC_DCHECK(primary_factory);
156
157 // The adapter is typically created on the worker thread, but operated on
158 // the encoder task queue.
159 encoder_queue_.Detach();
160
161 memset(&codec_, 0, sizeof(webrtc::VideoCodec));
162 }
163
~SimulcastEncoderAdapter()164 SimulcastEncoderAdapter::~SimulcastEncoderAdapter() {
165 RTC_DCHECK(!Initialized());
166 DestroyStoredEncoders();
167 }
168
SetFecControllerOverride(FecControllerOverride * fec_controller_override)169 void SimulcastEncoderAdapter::SetFecControllerOverride(
170 FecControllerOverride* fec_controller_override) {
171 // Ignored.
172 }
173
Release()174 int SimulcastEncoderAdapter::Release() {
175 RTC_DCHECK_RUN_ON(&encoder_queue_);
176
177 while (!streaminfos_.empty()) {
178 std::unique_ptr<VideoEncoder> encoder =
179 std::move(streaminfos_.back().encoder);
180 // Even though it seems very unlikely, there are no guarantees that the
181 // encoder will not call back after being Release()'d. Therefore, we first
182 // disable the callbacks here.
183 encoder->RegisterEncodeCompleteCallback(nullptr);
184 encoder->Release();
185 streaminfos_.pop_back(); // Deletes callback adapter.
186 stored_encoders_.push(std::move(encoder));
187 }
188
189 // It's legal to move the encoder to another queue now.
190 encoder_queue_.Detach();
191
192 rtc::AtomicOps::ReleaseStore(&inited_, 0);
193
194 return WEBRTC_VIDEO_CODEC_OK;
195 }
196
197 // TODO(eladalon): s/inst/codec_settings/g.
InitEncode(const VideoCodec * inst,const VideoEncoder::Settings & settings)198 int SimulcastEncoderAdapter::InitEncode(
199 const VideoCodec* inst,
200 const VideoEncoder::Settings& settings) {
201 RTC_DCHECK_RUN_ON(&encoder_queue_);
202
203 if (settings.number_of_cores < 1) {
204 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
205 }
206
207 int ret = VerifyCodec(inst);
208 if (ret < 0) {
209 return ret;
210 }
211
212 ret = Release();
213 if (ret < 0) {
214 return ret;
215 }
216
217 int number_of_streams = NumberOfStreams(*inst);
218 RTC_DCHECK_LE(number_of_streams, kMaxSimulcastStreams);
219 bool doing_simulcast_using_adapter = (number_of_streams > 1);
220 int num_active_streams = NumActiveStreams(*inst);
221
222 codec_ = *inst;
223 SimulcastRateAllocator rate_allocator(codec_);
224 VideoBitrateAllocation allocation =
225 rate_allocator.Allocate(VideoBitrateAllocationParameters(
226 codec_.startBitrate * 1000, codec_.maxFramerate));
227 std::vector<uint32_t> start_bitrates;
228 for (int i = 0; i < kMaxSimulcastStreams; ++i) {
229 uint32_t stream_bitrate = allocation.GetSpatialLayerSum(i) / 1000;
230 start_bitrates.push_back(stream_bitrate);
231 }
232
233 // Create |number_of_streams| of encoder instances and init them.
234 const auto minmax = std::minmax_element(
235 std::begin(codec_.simulcastStream),
236 std::begin(codec_.simulcastStream) + number_of_streams,
237 StreamResolutionCompare);
238 const auto lowest_resolution_stream_index =
239 std::distance(std::begin(codec_.simulcastStream), minmax.first);
240 const auto highest_resolution_stream_index =
241 std::distance(std::begin(codec_.simulcastStream), minmax.second);
242
243 RTC_DCHECK_LT(lowest_resolution_stream_index, number_of_streams);
244 RTC_DCHECK_LT(highest_resolution_stream_index, number_of_streams);
245
246 const SdpVideoFormat format(
247 codec_.codecType == webrtc::kVideoCodecVP8 ? "VP8" : "H264",
248 video_format_.parameters);
249
250 for (int i = 0; i < number_of_streams; ++i) {
251 // If an existing encoder instance exists, reuse it.
252 // TODO(brandtr): Set initial RTP state (e.g., picture_id/tl0_pic_idx) here,
253 // when we start storing that state outside the encoder wrappers.
254 std::unique_ptr<VideoEncoder> encoder;
255 if (!stored_encoders_.empty()) {
256 encoder = std::move(stored_encoders_.top());
257 stored_encoders_.pop();
258 } else {
259 encoder = primary_encoder_factory_->CreateVideoEncoder(format);
260 if (fallback_encoder_factory_ != nullptr) {
261 encoder = CreateVideoEncoderSoftwareFallbackWrapper(
262 fallback_encoder_factory_->CreateVideoEncoder(format),
263 std::move(encoder),
264 i == lowest_resolution_stream_index &&
265 prefer_temporal_support_on_base_layer_);
266 }
267 }
268
269 bool encoder_initialized = false;
270 if (doing_simulcast_using_adapter && i == 0 &&
271 encoder->GetEncoderInfo().supports_simulcast) {
272 ret = encoder->InitEncode(&codec_, settings);
273 if (ret < 0) {
274 encoder->Release();
275 } else {
276 doing_simulcast_using_adapter = false;
277 number_of_streams = 1;
278 encoder_initialized = true;
279 }
280 }
281
282 VideoCodec stream_codec;
283 uint32_t start_bitrate_kbps = start_bitrates[i];
284 const bool send_stream = doing_simulcast_using_adapter
285 ? start_bitrate_kbps > 0
286 : num_active_streams > 0;
287 if (!doing_simulcast_using_adapter) {
288 stream_codec = codec_;
289 stream_codec.numberOfSimulcastStreams =
290 std::max<uint8_t>(1, stream_codec.numberOfSimulcastStreams);
291 } else {
292 // Cap start bitrate to the min bitrate in order to avoid strange codec
293 // behavior. Since sending will be false, this should not matter.
294 StreamResolution stream_resolution =
295 i == highest_resolution_stream_index
296 ? StreamResolution::HIGHEST
297 : i == lowest_resolution_stream_index ? StreamResolution::LOWEST
298 : StreamResolution::OTHER;
299
300 start_bitrate_kbps =
301 std::max(codec_.simulcastStream[i].minBitrate, start_bitrate_kbps);
302 PopulateStreamCodec(codec_, i, start_bitrate_kbps, stream_resolution,
303 &stream_codec);
304 }
305
306 // TODO(ronghuawu): Remove once this is handled in LibvpxVp8Encoder.
307 if (stream_codec.qpMax < kDefaultMinQp) {
308 stream_codec.qpMax = kDefaultMaxQp;
309 }
310
311 if (!encoder_initialized) {
312 ret = encoder->InitEncode(&stream_codec, settings);
313 if (ret < 0) {
314 // Explicitly destroy the current encoder; because we haven't registered
315 // a StreamInfo for it yet, Release won't do anything about it.
316 encoder.reset();
317 Release();
318 return ret;
319 }
320 }
321
322 if (!doing_simulcast_using_adapter) {
323 // Without simulcast, just pass through the encoder info from the one
324 // active encoder.
325 encoder->RegisterEncodeCompleteCallback(encoded_complete_callback_);
326 streaminfos_.emplace_back(
327 std::move(encoder), nullptr,
328 std::make_unique<FramerateController>(stream_codec.maxFramerate),
329 stream_codec.width, stream_codec.height, send_stream);
330 } else {
331 std::unique_ptr<EncodedImageCallback> callback(
332 new AdapterEncodedImageCallback(this, i));
333 encoder->RegisterEncodeCompleteCallback(callback.get());
334 streaminfos_.emplace_back(
335 std::move(encoder), std::move(callback),
336 std::make_unique<FramerateController>(stream_codec.maxFramerate),
337 stream_codec.width, stream_codec.height, send_stream);
338 }
339 }
340
341 // To save memory, don't store encoders that we don't use.
342 DestroyStoredEncoders();
343
344 rtc::AtomicOps::ReleaseStore(&inited_, 1);
345
346 return WEBRTC_VIDEO_CODEC_OK;
347 }
348
Encode(const VideoFrame & input_image,const std::vector<VideoFrameType> * frame_types)349 int SimulcastEncoderAdapter::Encode(
350 const VideoFrame& input_image,
351 const std::vector<VideoFrameType>* frame_types) {
352 RTC_DCHECK_RUN_ON(&encoder_queue_);
353
354 if (!Initialized()) {
355 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
356 }
357 if (encoded_complete_callback_ == nullptr) {
358 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
359 }
360
361 // All active streams should generate a key frame if
362 // a key frame is requested by any stream.
363 bool send_key_frame = false;
364 if (frame_types) {
365 for (size_t i = 0; i < frame_types->size(); ++i) {
366 if (frame_types->at(i) == VideoFrameType::kVideoFrameKey) {
367 send_key_frame = true;
368 break;
369 }
370 }
371 }
372 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
373 if (streaminfos_[stream_idx].key_frame_request &&
374 streaminfos_[stream_idx].send_stream) {
375 send_key_frame = true;
376 break;
377 }
378 }
379
380 // Temporary thay may hold the result of texture to i420 buffer conversion.
381 rtc::scoped_refptr<I420BufferInterface> src_buffer;
382 int src_width = input_image.width();
383 int src_height = input_image.height();
384 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
385 // Don't encode frames in resolutions that we don't intend to send.
386 if (!streaminfos_[stream_idx].send_stream) {
387 continue;
388 }
389
390 const uint32_t frame_timestamp_ms =
391 1000 * input_image.timestamp() / 90000; // kVideoPayloadTypeFrequency;
392
393 // If adapter is passed through and only one sw encoder does simulcast,
394 // frame types for all streams should be passed to the encoder unchanged.
395 // Otherwise a single per-encoder frame type is passed.
396 std::vector<VideoFrameType> stream_frame_types(
397 streaminfos_.size() == 1 ? NumberOfStreams(codec_) : 1);
398 if (send_key_frame) {
399 std::fill(stream_frame_types.begin(), stream_frame_types.end(),
400 VideoFrameType::kVideoFrameKey);
401 streaminfos_[stream_idx].key_frame_request = false;
402 } else {
403 if (streaminfos_[stream_idx].framerate_controller->DropFrame(
404 frame_timestamp_ms)) {
405 continue;
406 }
407 std::fill(stream_frame_types.begin(), stream_frame_types.end(),
408 VideoFrameType::kVideoFrameDelta);
409 }
410 streaminfos_[stream_idx].framerate_controller->AddFrame(frame_timestamp_ms);
411
412 int dst_width = streaminfos_[stream_idx].width;
413 int dst_height = streaminfos_[stream_idx].height;
414 // If scaling isn't required, because the input resolution
415 // matches the destination or the input image is empty (e.g.
416 // a keyframe request for encoders with internal camera
417 // sources) or the source image has a native handle, pass the image on
418 // directly. Otherwise, we'll scale it to match what the encoder expects
419 // (below).
420 // For texture frames, the underlying encoder is expected to be able to
421 // correctly sample/scale the source texture.
422 // TODO(perkj): ensure that works going forward, and figure out how this
423 // affects webrtc:5683.
424 if ((dst_width == src_width && dst_height == src_height) ||
425 (input_image.video_frame_buffer()->type() ==
426 VideoFrameBuffer::Type::kNative &&
427 streaminfos_[stream_idx]
428 .encoder->GetEncoderInfo()
429 .supports_native_handle)) {
430 int ret = streaminfos_[stream_idx].encoder->Encode(input_image,
431 &stream_frame_types);
432 if (ret != WEBRTC_VIDEO_CODEC_OK) {
433 return ret;
434 }
435 } else {
436 if (src_buffer == nullptr) {
437 src_buffer = input_image.video_frame_buffer()->ToI420();
438 }
439 rtc::scoped_refptr<I420Buffer> dst_buffer =
440 I420Buffer::Create(dst_width, dst_height);
441
442 dst_buffer->ScaleFrom(*src_buffer);
443
444 // UpdateRect is not propagated to lower simulcast layers currently.
445 // TODO(ilnik): Consider scaling UpdateRect together with the buffer.
446 VideoFrame frame(input_image);
447 frame.set_video_frame_buffer(dst_buffer);
448 frame.set_rotation(webrtc::kVideoRotation_0);
449 frame.set_update_rect(
450 VideoFrame::UpdateRect{0, 0, frame.width(), frame.height()});
451 int ret =
452 streaminfos_[stream_idx].encoder->Encode(frame, &stream_frame_types);
453 if (ret != WEBRTC_VIDEO_CODEC_OK) {
454 return ret;
455 }
456 }
457 }
458
459 return WEBRTC_VIDEO_CODEC_OK;
460 }
461
RegisterEncodeCompleteCallback(EncodedImageCallback * callback)462 int SimulcastEncoderAdapter::RegisterEncodeCompleteCallback(
463 EncodedImageCallback* callback) {
464 RTC_DCHECK_RUN_ON(&encoder_queue_);
465 encoded_complete_callback_ = callback;
466 if (streaminfos_.size() == 1) {
467 streaminfos_[0].encoder->RegisterEncodeCompleteCallback(callback);
468 }
469 return WEBRTC_VIDEO_CODEC_OK;
470 }
471
SetRates(const RateControlParameters & parameters)472 void SimulcastEncoderAdapter::SetRates(
473 const RateControlParameters& parameters) {
474 RTC_DCHECK_RUN_ON(&encoder_queue_);
475
476 if (!Initialized()) {
477 RTC_LOG(LS_WARNING) << "SetRates while not initialized";
478 return;
479 }
480
481 if (parameters.framerate_fps < 1.0) {
482 RTC_LOG(LS_WARNING) << "Invalid framerate: " << parameters.framerate_fps;
483 return;
484 }
485
486 codec_.maxFramerate = static_cast<uint32_t>(parameters.framerate_fps + 0.5);
487
488 if (streaminfos_.size() == 1) {
489 // Not doing simulcast.
490 streaminfos_[0].encoder->SetRates(parameters);
491 return;
492 }
493
494 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
495 uint32_t stream_bitrate_kbps =
496 parameters.bitrate.GetSpatialLayerSum(stream_idx) / 1000;
497
498 // Need a key frame if we have not sent this stream before.
499 if (stream_bitrate_kbps > 0 && !streaminfos_[stream_idx].send_stream) {
500 streaminfos_[stream_idx].key_frame_request = true;
501 }
502 streaminfos_[stream_idx].send_stream = stream_bitrate_kbps > 0;
503
504 // Slice the temporal layers out of the full allocation and pass it on to
505 // the encoder handling the current simulcast stream.
506 RateControlParameters stream_parameters = parameters;
507 stream_parameters.bitrate = VideoBitrateAllocation();
508 for (int i = 0; i < kMaxTemporalStreams; ++i) {
509 if (parameters.bitrate.HasBitrate(stream_idx, i)) {
510 stream_parameters.bitrate.SetBitrate(
511 0, i, parameters.bitrate.GetBitrate(stream_idx, i));
512 }
513 }
514
515 // Assign link allocation proportionally to spatial layer allocation.
516 if (!parameters.bandwidth_allocation.IsZero() &&
517 parameters.bitrate.get_sum_bps() > 0) {
518 stream_parameters.bandwidth_allocation =
519 DataRate::BitsPerSec((parameters.bandwidth_allocation.bps() *
520 stream_parameters.bitrate.get_sum_bps()) /
521 parameters.bitrate.get_sum_bps());
522 // Make sure we don't allocate bandwidth lower than target bitrate.
523 if (stream_parameters.bandwidth_allocation.bps() <
524 stream_parameters.bitrate.get_sum_bps()) {
525 stream_parameters.bandwidth_allocation =
526 DataRate::BitsPerSec(stream_parameters.bitrate.get_sum_bps());
527 }
528 }
529
530 stream_parameters.framerate_fps = std::min<double>(
531 parameters.framerate_fps,
532 streaminfos_[stream_idx].framerate_controller->GetTargetRate());
533
534 streaminfos_[stream_idx].encoder->SetRates(stream_parameters);
535 }
536 }
537
OnPacketLossRateUpdate(float packet_loss_rate)538 void SimulcastEncoderAdapter::OnPacketLossRateUpdate(float packet_loss_rate) {
539 for (StreamInfo& info : streaminfos_) {
540 info.encoder->OnPacketLossRateUpdate(packet_loss_rate);
541 }
542 }
543
OnRttUpdate(int64_t rtt_ms)544 void SimulcastEncoderAdapter::OnRttUpdate(int64_t rtt_ms) {
545 for (StreamInfo& info : streaminfos_) {
546 info.encoder->OnRttUpdate(rtt_ms);
547 }
548 }
549
OnLossNotification(const LossNotification & loss_notification)550 void SimulcastEncoderAdapter::OnLossNotification(
551 const LossNotification& loss_notification) {
552 for (StreamInfo& info : streaminfos_) {
553 info.encoder->OnLossNotification(loss_notification);
554 }
555 }
556
557 // TODO(brandtr): Add task checker to this member function, when all encoder
558 // callbacks are coming in on the encoder queue.
OnEncodedImage(size_t stream_idx,const EncodedImage & encodedImage,const CodecSpecificInfo * codecSpecificInfo,const RTPFragmentationHeader * fragmentation)559 EncodedImageCallback::Result SimulcastEncoderAdapter::OnEncodedImage(
560 size_t stream_idx,
561 const EncodedImage& encodedImage,
562 const CodecSpecificInfo* codecSpecificInfo,
563 const RTPFragmentationHeader* fragmentation) {
564 EncodedImage stream_image(encodedImage);
565 CodecSpecificInfo stream_codec_specific = *codecSpecificInfo;
566
567 stream_image.SetSpatialIndex(stream_idx);
568
569 return encoded_complete_callback_->OnEncodedImage(
570 stream_image, &stream_codec_specific, fragmentation);
571 }
572
PopulateStreamCodec(const webrtc::VideoCodec & inst,int stream_index,uint32_t start_bitrate_kbps,StreamResolution stream_resolution,webrtc::VideoCodec * stream_codec)573 void SimulcastEncoderAdapter::PopulateStreamCodec(
574 const webrtc::VideoCodec& inst,
575 int stream_index,
576 uint32_t start_bitrate_kbps,
577 StreamResolution stream_resolution,
578 webrtc::VideoCodec* stream_codec) {
579 *stream_codec = inst;
580
581 // Stream specific settings.
582 stream_codec->numberOfSimulcastStreams = 0;
583 stream_codec->width = inst.simulcastStream[stream_index].width;
584 stream_codec->height = inst.simulcastStream[stream_index].height;
585 stream_codec->maxBitrate = inst.simulcastStream[stream_index].maxBitrate;
586 stream_codec->minBitrate = inst.simulcastStream[stream_index].minBitrate;
587 stream_codec->maxFramerate = inst.simulcastStream[stream_index].maxFramerate;
588 stream_codec->qpMax = inst.simulcastStream[stream_index].qpMax;
589 stream_codec->active = inst.simulcastStream[stream_index].active;
590 // Settings that are based on stream/resolution.
591 if (stream_resolution == StreamResolution::LOWEST) {
592 // Settings for lowest spatial resolutions.
593 if (inst.mode == VideoCodecMode::kScreensharing) {
594 if (experimental_boosted_screenshare_qp_) {
595 stream_codec->qpMax = *experimental_boosted_screenshare_qp_;
596 }
597 } else if (boost_base_layer_quality_) {
598 stream_codec->qpMax = kLowestResMaxQp;
599 }
600 }
601 if (inst.codecType == webrtc::kVideoCodecVP8) {
602 stream_codec->VP8()->numberOfTemporalLayers =
603 inst.simulcastStream[stream_index].numberOfTemporalLayers;
604 if (stream_resolution != StreamResolution::HIGHEST) {
605 // For resolutions below CIF, set the codec |complexity| parameter to
606 // kComplexityHigher, which maps to cpu_used = -4.
607 int pixels_per_frame = stream_codec->width * stream_codec->height;
608 if (pixels_per_frame < 352 * 288) {
609 stream_codec->VP8()->complexity =
610 webrtc::VideoCodecComplexity::kComplexityHigher;
611 }
612 // Turn off denoising for all streams but the highest resolution.
613 stream_codec->VP8()->denoisingOn = false;
614 }
615 } else if (inst.codecType == webrtc::kVideoCodecH264) {
616 stream_codec->H264()->numberOfTemporalLayers =
617 inst.simulcastStream[stream_index].numberOfTemporalLayers;
618 }
619 // TODO(ronghuawu): what to do with targetBitrate.
620
621 stream_codec->startBitrate = start_bitrate_kbps;
622 }
623
Initialized() const624 bool SimulcastEncoderAdapter::Initialized() const {
625 return rtc::AtomicOps::AcquireLoad(&inited_) == 1;
626 }
627
DestroyStoredEncoders()628 void SimulcastEncoderAdapter::DestroyStoredEncoders() {
629 while (!stored_encoders_.empty()) {
630 stored_encoders_.pop();
631 }
632 }
633
GetEncoderInfo() const634 VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const {
635 if (streaminfos_.size() == 1) {
636 // Not using simulcast adapting functionality, just pass through.
637 return streaminfos_[0].encoder->GetEncoderInfo();
638 }
639
640 VideoEncoder::EncoderInfo encoder_info;
641 encoder_info.implementation_name = "SimulcastEncoderAdapter";
642 encoder_info.requested_resolution_alignment = 1;
643 encoder_info.supports_native_handle = true;
644 encoder_info.scaling_settings.thresholds = absl::nullopt;
645 if (streaminfos_.empty()) {
646 return encoder_info;
647 }
648
649 encoder_info.scaling_settings = VideoEncoder::ScalingSettings::kOff;
650 int num_active_streams = NumActiveStreams(codec_);
651
652 for (size_t i = 0; i < streaminfos_.size(); ++i) {
653 VideoEncoder::EncoderInfo encoder_impl_info =
654 streaminfos_[i].encoder->GetEncoderInfo();
655
656 if (i == 0) {
657 // Encoder name indicates names of all sub-encoders.
658 encoder_info.implementation_name += " (";
659 encoder_info.implementation_name += encoder_impl_info.implementation_name;
660
661 encoder_info.supports_native_handle =
662 encoder_impl_info.supports_native_handle;
663 encoder_info.has_trusted_rate_controller =
664 encoder_impl_info.has_trusted_rate_controller;
665 encoder_info.is_hardware_accelerated =
666 encoder_impl_info.is_hardware_accelerated;
667 encoder_info.has_internal_source = encoder_impl_info.has_internal_source;
668 } else {
669 encoder_info.implementation_name += ", ";
670 encoder_info.implementation_name += encoder_impl_info.implementation_name;
671
672 // Native handle supported if any encoder supports it.
673 encoder_info.supports_native_handle |=
674 encoder_impl_info.supports_native_handle;
675
676 // Trusted rate controller only if all encoders have it.
677 encoder_info.has_trusted_rate_controller &=
678 encoder_impl_info.has_trusted_rate_controller;
679
680 // Uses hardware support if any of the encoders uses it.
681 // For example, if we are having issues with down-scaling due to
682 // pipelining delay in HW encoders we need higher encoder usage
683 // thresholds in CPU adaptation.
684 encoder_info.is_hardware_accelerated |=
685 encoder_impl_info.is_hardware_accelerated;
686
687 // Has internal source only if all encoders have it.
688 encoder_info.has_internal_source &= encoder_impl_info.has_internal_source;
689 }
690 encoder_info.fps_allocation[i] = encoder_impl_info.fps_allocation[0];
691 encoder_info.requested_resolution_alignment = cricket::LeastCommonMultiple(
692 encoder_info.requested_resolution_alignment,
693 encoder_impl_info.requested_resolution_alignment);
694 if (num_active_streams == 1 && codec_.simulcastStream[i].active) {
695 encoder_info.scaling_settings = encoder_impl_info.scaling_settings;
696 }
697 }
698 encoder_info.implementation_name += ")";
699
700 return encoder_info;
701 }
702
703 } // namespace webrtc
704