xref: /aosp_15_r20/external/webrtc/media/engine/simulcast_encoder_adapter.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "media/engine/simulcast_encoder_adapter.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <stdio.h>
14*d9f75844SAndroid Build Coastguard Worker #include <string.h>
15*d9f75844SAndroid Build Coastguard Worker 
16*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
17*d9f75844SAndroid Build Coastguard Worker #include <cstdint>
18*d9f75844SAndroid Build Coastguard Worker #include <string>
19*d9f75844SAndroid Build Coastguard Worker #include <utility>
20*d9f75844SAndroid Build Coastguard Worker 
21*d9f75844SAndroid Build Coastguard Worker #include "absl/algorithm/container.h"
22*d9f75844SAndroid Build Coastguard Worker #include "api/scoped_refptr.h"
23*d9f75844SAndroid Build Coastguard Worker #include "api/video/i420_buffer.h"
24*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_codec_constants.h"
25*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_frame_buffer.h"
26*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_rotation.h"
27*d9f75844SAndroid Build Coastguard Worker #include "api/video_codecs/video_encoder.h"
28*d9f75844SAndroid Build Coastguard Worker #include "api/video_codecs/video_encoder_factory.h"
29*d9f75844SAndroid Build Coastguard Worker #include "api/video_codecs/video_encoder_software_fallback_wrapper.h"
30*d9f75844SAndroid Build Coastguard Worker #include "media/base/video_common.h"
31*d9f75844SAndroid Build Coastguard Worker #include "modules/video_coding/include/video_error_codes.h"
32*d9f75844SAndroid Build Coastguard Worker #include "modules/video_coding/utility/simulcast_rate_allocator.h"
33*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
34*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/experiments/rate_control_settings.h"
35*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
36*d9f75844SAndroid Build Coastguard Worker #include "system_wrappers/include/field_trial.h"
37*d9f75844SAndroid Build Coastguard Worker 
38*d9f75844SAndroid Build Coastguard Worker namespace {
39*d9f75844SAndroid Build Coastguard Worker 
40*d9f75844SAndroid Build Coastguard Worker const unsigned int kDefaultMinQp = 2;
41*d9f75844SAndroid Build Coastguard Worker const unsigned int kDefaultMaxQp = 56;
42*d9f75844SAndroid Build Coastguard Worker // Max qp for lowest spatial resolution when doing simulcast.
43*d9f75844SAndroid Build Coastguard Worker const unsigned int kLowestResMaxQp = 45;
44*d9f75844SAndroid Build Coastguard Worker 
GetScreenshareBoostedQpValue()45*d9f75844SAndroid Build Coastguard Worker absl::optional<unsigned int> GetScreenshareBoostedQpValue() {
46*d9f75844SAndroid Build Coastguard Worker   std::string experiment_group =
47*d9f75844SAndroid Build Coastguard Worker       webrtc::field_trial::FindFullName("WebRTC-BoostedScreenshareQp");
48*d9f75844SAndroid Build Coastguard Worker   unsigned int qp;
49*d9f75844SAndroid Build Coastguard Worker   if (sscanf(experiment_group.c_str(), "%u", &qp) != 1)
50*d9f75844SAndroid Build Coastguard Worker     return absl::nullopt;
51*d9f75844SAndroid Build Coastguard Worker   qp = std::min(qp, 63u);
52*d9f75844SAndroid Build Coastguard Worker   qp = std::max(qp, 1u);
53*d9f75844SAndroid Build Coastguard Worker   return qp;
54*d9f75844SAndroid Build Coastguard Worker }
55*d9f75844SAndroid Build Coastguard Worker 
SumStreamMaxBitrate(int streams,const webrtc::VideoCodec & codec)56*d9f75844SAndroid Build Coastguard Worker uint32_t SumStreamMaxBitrate(int streams, const webrtc::VideoCodec& codec) {
57*d9f75844SAndroid Build Coastguard Worker   uint32_t bitrate_sum = 0;
58*d9f75844SAndroid Build Coastguard Worker   for (int i = 0; i < streams; ++i) {
59*d9f75844SAndroid Build Coastguard Worker     bitrate_sum += codec.simulcastStream[i].maxBitrate;
60*d9f75844SAndroid Build Coastguard Worker   }
61*d9f75844SAndroid Build Coastguard Worker   return bitrate_sum;
62*d9f75844SAndroid Build Coastguard Worker }
63*d9f75844SAndroid Build Coastguard Worker 
CountAllStreams(const webrtc::VideoCodec & codec)64*d9f75844SAndroid Build Coastguard Worker int CountAllStreams(const webrtc::VideoCodec& codec) {
65*d9f75844SAndroid Build Coastguard Worker   int total_streams_count =
66*d9f75844SAndroid Build Coastguard Worker       codec.numberOfSimulcastStreams < 1 ? 1 : codec.numberOfSimulcastStreams;
67*d9f75844SAndroid Build Coastguard Worker   uint32_t simulcast_max_bitrate =
68*d9f75844SAndroid Build Coastguard Worker       SumStreamMaxBitrate(total_streams_count, codec);
69*d9f75844SAndroid Build Coastguard Worker   if (simulcast_max_bitrate == 0) {
70*d9f75844SAndroid Build Coastguard Worker     total_streams_count = 1;
71*d9f75844SAndroid Build Coastguard Worker   }
72*d9f75844SAndroid Build Coastguard Worker   return total_streams_count;
73*d9f75844SAndroid Build Coastguard Worker }
74*d9f75844SAndroid Build Coastguard Worker 
CountActiveStreams(const webrtc::VideoCodec & codec)75*d9f75844SAndroid Build Coastguard Worker int CountActiveStreams(const webrtc::VideoCodec& codec) {
76*d9f75844SAndroid Build Coastguard Worker   if (codec.numberOfSimulcastStreams < 1) {
77*d9f75844SAndroid Build Coastguard Worker     return 1;
78*d9f75844SAndroid Build Coastguard Worker   }
79*d9f75844SAndroid Build Coastguard Worker   int total_streams_count = CountAllStreams(codec);
80*d9f75844SAndroid Build Coastguard Worker   int active_streams_count = 0;
81*d9f75844SAndroid Build Coastguard Worker   for (int i = 0; i < total_streams_count; ++i) {
82*d9f75844SAndroid Build Coastguard Worker     if (codec.simulcastStream[i].active) {
83*d9f75844SAndroid Build Coastguard Worker       ++active_streams_count;
84*d9f75844SAndroid Build Coastguard Worker     }
85*d9f75844SAndroid Build Coastguard Worker   }
86*d9f75844SAndroid Build Coastguard Worker   return active_streams_count;
87*d9f75844SAndroid Build Coastguard Worker }
88*d9f75844SAndroid Build Coastguard Worker 
VerifyCodec(const webrtc::VideoCodec * inst)89*d9f75844SAndroid Build Coastguard Worker int VerifyCodec(const webrtc::VideoCodec* inst) {
90*d9f75844SAndroid Build Coastguard Worker   if (inst == nullptr) {
91*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
92*d9f75844SAndroid Build Coastguard Worker   }
93*d9f75844SAndroid Build Coastguard Worker   if (inst->maxFramerate < 1) {
94*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
95*d9f75844SAndroid Build Coastguard Worker   }
96*d9f75844SAndroid Build Coastguard Worker   // allow zero to represent an unspecified maxBitRate
97*d9f75844SAndroid Build Coastguard Worker   if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {
98*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
99*d9f75844SAndroid Build Coastguard Worker   }
100*d9f75844SAndroid Build Coastguard Worker   if (inst->width <= 1 || inst->height <= 1) {
101*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
102*d9f75844SAndroid Build Coastguard Worker   }
103*d9f75844SAndroid Build Coastguard Worker   if (inst->codecType == webrtc::kVideoCodecVP8 &&
104*d9f75844SAndroid Build Coastguard Worker       inst->VP8().automaticResizeOn && CountActiveStreams(*inst) > 1) {
105*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
106*d9f75844SAndroid Build Coastguard Worker   }
107*d9f75844SAndroid Build Coastguard Worker   return WEBRTC_VIDEO_CODEC_OK;
108*d9f75844SAndroid Build Coastguard Worker }
109*d9f75844SAndroid Build Coastguard Worker 
StreamQualityCompare(const webrtc::SimulcastStream & a,const webrtc::SimulcastStream & b)110*d9f75844SAndroid Build Coastguard Worker bool StreamQualityCompare(const webrtc::SimulcastStream& a,
111*d9f75844SAndroid Build Coastguard Worker                           const webrtc::SimulcastStream& b) {
112*d9f75844SAndroid Build Coastguard Worker   return std::tie(a.height, a.width, a.maxBitrate, a.maxFramerate) <
113*d9f75844SAndroid Build Coastguard Worker          std::tie(b.height, b.width, b.maxBitrate, b.maxFramerate);
114*d9f75844SAndroid Build Coastguard Worker }
115*d9f75844SAndroid Build Coastguard Worker 
GetLowestAndHighestQualityStreamIndixes(rtc::ArrayView<webrtc::SimulcastStream> streams,int * lowest_quality_stream_idx,int * highest_quality_stream_idx)116*d9f75844SAndroid Build Coastguard Worker void GetLowestAndHighestQualityStreamIndixes(
117*d9f75844SAndroid Build Coastguard Worker     rtc::ArrayView<webrtc::SimulcastStream> streams,
118*d9f75844SAndroid Build Coastguard Worker     int* lowest_quality_stream_idx,
119*d9f75844SAndroid Build Coastguard Worker     int* highest_quality_stream_idx) {
120*d9f75844SAndroid Build Coastguard Worker   const auto lowest_highest_quality_streams =
121*d9f75844SAndroid Build Coastguard Worker       absl::c_minmax_element(streams, StreamQualityCompare);
122*d9f75844SAndroid Build Coastguard Worker   *lowest_quality_stream_idx =
123*d9f75844SAndroid Build Coastguard Worker       std::distance(streams.begin(), lowest_highest_quality_streams.first);
124*d9f75844SAndroid Build Coastguard Worker   *highest_quality_stream_idx =
125*d9f75844SAndroid Build Coastguard Worker       std::distance(streams.begin(), lowest_highest_quality_streams.second);
126*d9f75844SAndroid Build Coastguard Worker }
127*d9f75844SAndroid Build Coastguard Worker 
GetStreamStartBitratesKbps(const webrtc::VideoCodec & codec)128*d9f75844SAndroid Build Coastguard Worker std::vector<uint32_t> GetStreamStartBitratesKbps(
129*d9f75844SAndroid Build Coastguard Worker     const webrtc::VideoCodec& codec) {
130*d9f75844SAndroid Build Coastguard Worker   std::vector<uint32_t> start_bitrates;
131*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<webrtc::VideoBitrateAllocator> rate_allocator =
132*d9f75844SAndroid Build Coastguard Worker       std::make_unique<webrtc::SimulcastRateAllocator>(codec);
133*d9f75844SAndroid Build Coastguard Worker   webrtc::VideoBitrateAllocation allocation =
134*d9f75844SAndroid Build Coastguard Worker       rate_allocator->Allocate(webrtc::VideoBitrateAllocationParameters(
135*d9f75844SAndroid Build Coastguard Worker           codec.startBitrate * 1000, codec.maxFramerate));
136*d9f75844SAndroid Build Coastguard Worker 
137*d9f75844SAndroid Build Coastguard Worker   int total_streams_count = CountAllStreams(codec);
138*d9f75844SAndroid Build Coastguard Worker   for (int i = 0; i < total_streams_count; ++i) {
139*d9f75844SAndroid Build Coastguard Worker     uint32_t stream_bitrate = allocation.GetSpatialLayerSum(i) / 1000;
140*d9f75844SAndroid Build Coastguard Worker     start_bitrates.push_back(stream_bitrate);
141*d9f75844SAndroid Build Coastguard Worker   }
142*d9f75844SAndroid Build Coastguard Worker   return start_bitrates;
143*d9f75844SAndroid Build Coastguard Worker }
144*d9f75844SAndroid Build Coastguard Worker 
145*d9f75844SAndroid Build Coastguard Worker }  // namespace
146*d9f75844SAndroid Build Coastguard Worker 
147*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
148*d9f75844SAndroid Build Coastguard Worker 
EncoderContext(std::unique_ptr<VideoEncoder> encoder,bool prefer_temporal_support,VideoEncoder::EncoderInfo primary_info,VideoEncoder::EncoderInfo fallback_info)149*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::EncoderContext::EncoderContext(
150*d9f75844SAndroid Build Coastguard Worker     std::unique_ptr<VideoEncoder> encoder,
151*d9f75844SAndroid Build Coastguard Worker     bool prefer_temporal_support,
152*d9f75844SAndroid Build Coastguard Worker     VideoEncoder::EncoderInfo primary_info,
153*d9f75844SAndroid Build Coastguard Worker     VideoEncoder::EncoderInfo fallback_info)
154*d9f75844SAndroid Build Coastguard Worker     : encoder_(std::move(encoder)),
155*d9f75844SAndroid Build Coastguard Worker       prefer_temporal_support_(prefer_temporal_support),
156*d9f75844SAndroid Build Coastguard Worker       primary_info_(std::move(primary_info)),
157*d9f75844SAndroid Build Coastguard Worker       fallback_info_(std::move(fallback_info)) {}
158*d9f75844SAndroid Build Coastguard Worker 
Release()159*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::EncoderContext::Release() {
160*d9f75844SAndroid Build Coastguard Worker   if (encoder_) {
161*d9f75844SAndroid Build Coastguard Worker     encoder_->Release();
162*d9f75844SAndroid Build Coastguard Worker     encoder_->RegisterEncodeCompleteCallback(nullptr);
163*d9f75844SAndroid Build Coastguard Worker   }
164*d9f75844SAndroid Build Coastguard Worker }
165*d9f75844SAndroid Build Coastguard Worker 
StreamContext(SimulcastEncoderAdapter * parent,std::unique_ptr<EncoderContext> encoder_context,std::unique_ptr<FramerateController> framerate_controller,int stream_idx,uint16_t width,uint16_t height,bool is_paused)166*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::StreamContext::StreamContext(
167*d9f75844SAndroid Build Coastguard Worker     SimulcastEncoderAdapter* parent,
168*d9f75844SAndroid Build Coastguard Worker     std::unique_ptr<EncoderContext> encoder_context,
169*d9f75844SAndroid Build Coastguard Worker     std::unique_ptr<FramerateController> framerate_controller,
170*d9f75844SAndroid Build Coastguard Worker     int stream_idx,
171*d9f75844SAndroid Build Coastguard Worker     uint16_t width,
172*d9f75844SAndroid Build Coastguard Worker     uint16_t height,
173*d9f75844SAndroid Build Coastguard Worker     bool is_paused)
174*d9f75844SAndroid Build Coastguard Worker     : parent_(parent),
175*d9f75844SAndroid Build Coastguard Worker       encoder_context_(std::move(encoder_context)),
176*d9f75844SAndroid Build Coastguard Worker       framerate_controller_(std::move(framerate_controller)),
177*d9f75844SAndroid Build Coastguard Worker       stream_idx_(stream_idx),
178*d9f75844SAndroid Build Coastguard Worker       width_(width),
179*d9f75844SAndroid Build Coastguard Worker       height_(height),
180*d9f75844SAndroid Build Coastguard Worker       is_keyframe_needed_(false),
181*d9f75844SAndroid Build Coastguard Worker       is_paused_(is_paused) {
182*d9f75844SAndroid Build Coastguard Worker   if (parent_) {
183*d9f75844SAndroid Build Coastguard Worker     encoder_context_->encoder().RegisterEncodeCompleteCallback(this);
184*d9f75844SAndroid Build Coastguard Worker   }
185*d9f75844SAndroid Build Coastguard Worker }
186*d9f75844SAndroid Build Coastguard Worker 
StreamContext(StreamContext && rhs)187*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::StreamContext::StreamContext(StreamContext&& rhs)
188*d9f75844SAndroid Build Coastguard Worker     : parent_(rhs.parent_),
189*d9f75844SAndroid Build Coastguard Worker       encoder_context_(std::move(rhs.encoder_context_)),
190*d9f75844SAndroid Build Coastguard Worker       framerate_controller_(std::move(rhs.framerate_controller_)),
191*d9f75844SAndroid Build Coastguard Worker       stream_idx_(rhs.stream_idx_),
192*d9f75844SAndroid Build Coastguard Worker       width_(rhs.width_),
193*d9f75844SAndroid Build Coastguard Worker       height_(rhs.height_),
194*d9f75844SAndroid Build Coastguard Worker       is_keyframe_needed_(rhs.is_keyframe_needed_),
195*d9f75844SAndroid Build Coastguard Worker       is_paused_(rhs.is_paused_) {
196*d9f75844SAndroid Build Coastguard Worker   if (parent_) {
197*d9f75844SAndroid Build Coastguard Worker     encoder_context_->encoder().RegisterEncodeCompleteCallback(this);
198*d9f75844SAndroid Build Coastguard Worker   }
199*d9f75844SAndroid Build Coastguard Worker }
200*d9f75844SAndroid Build Coastguard Worker 
~StreamContext()201*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::StreamContext::~StreamContext() {
202*d9f75844SAndroid Build Coastguard Worker   if (encoder_context_) {
203*d9f75844SAndroid Build Coastguard Worker     encoder_context_->Release();
204*d9f75844SAndroid Build Coastguard Worker   }
205*d9f75844SAndroid Build Coastguard Worker }
206*d9f75844SAndroid Build Coastguard Worker 
207*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<SimulcastEncoderAdapter::EncoderContext>
ReleaseEncoderContext()208*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::StreamContext::ReleaseEncoderContext() && {
209*d9f75844SAndroid Build Coastguard Worker   encoder_context_->Release();
210*d9f75844SAndroid Build Coastguard Worker   return std::move(encoder_context_);
211*d9f75844SAndroid Build Coastguard Worker }
212*d9f75844SAndroid Build Coastguard Worker 
OnKeyframe(Timestamp timestamp)213*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::StreamContext::OnKeyframe(Timestamp timestamp) {
214*d9f75844SAndroid Build Coastguard Worker   is_keyframe_needed_ = false;
215*d9f75844SAndroid Build Coastguard Worker   if (framerate_controller_) {
216*d9f75844SAndroid Build Coastguard Worker     framerate_controller_->KeepFrame(timestamp.us() * 1000);
217*d9f75844SAndroid Build Coastguard Worker   }
218*d9f75844SAndroid Build Coastguard Worker }
219*d9f75844SAndroid Build Coastguard Worker 
ShouldDropFrame(Timestamp timestamp)220*d9f75844SAndroid Build Coastguard Worker bool SimulcastEncoderAdapter::StreamContext::ShouldDropFrame(
221*d9f75844SAndroid Build Coastguard Worker     Timestamp timestamp) {
222*d9f75844SAndroid Build Coastguard Worker   if (!framerate_controller_) {
223*d9f75844SAndroid Build Coastguard Worker     return false;
224*d9f75844SAndroid Build Coastguard Worker   }
225*d9f75844SAndroid Build Coastguard Worker   return framerate_controller_->ShouldDropFrame(timestamp.us() * 1000);
226*d9f75844SAndroid Build Coastguard Worker }
227*d9f75844SAndroid Build Coastguard Worker 
228*d9f75844SAndroid Build Coastguard Worker EncodedImageCallback::Result
OnEncodedImage(const EncodedImage & encoded_image,const CodecSpecificInfo * codec_specific_info)229*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::StreamContext::OnEncodedImage(
230*d9f75844SAndroid Build Coastguard Worker     const EncodedImage& encoded_image,
231*d9f75844SAndroid Build Coastguard Worker     const CodecSpecificInfo* codec_specific_info) {
232*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(parent_);  // If null, this method should never be called.
233*d9f75844SAndroid Build Coastguard Worker   return parent_->OnEncodedImage(stream_idx_, encoded_image,
234*d9f75844SAndroid Build Coastguard Worker                                  codec_specific_info);
235*d9f75844SAndroid Build Coastguard Worker }
236*d9f75844SAndroid Build Coastguard Worker 
OnDroppedFrame(DropReason)237*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::StreamContext::OnDroppedFrame(
238*d9f75844SAndroid Build Coastguard Worker     DropReason /*reason*/) {
239*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(parent_);  // If null, this method should never be called.
240*d9f75844SAndroid Build Coastguard Worker   parent_->OnDroppedFrame(stream_idx_);
241*d9f75844SAndroid Build Coastguard Worker }
242*d9f75844SAndroid Build Coastguard Worker 
SimulcastEncoderAdapter(VideoEncoderFactory * factory,const SdpVideoFormat & format)243*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory,
244*d9f75844SAndroid Build Coastguard Worker                                                  const SdpVideoFormat& format)
245*d9f75844SAndroid Build Coastguard Worker     : SimulcastEncoderAdapter(factory, nullptr, format) {}
246*d9f75844SAndroid Build Coastguard Worker 
SimulcastEncoderAdapter(VideoEncoderFactory * primary_factory,VideoEncoderFactory * fallback_factory,const SdpVideoFormat & format)247*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::SimulcastEncoderAdapter(
248*d9f75844SAndroid Build Coastguard Worker     VideoEncoderFactory* primary_factory,
249*d9f75844SAndroid Build Coastguard Worker     VideoEncoderFactory* fallback_factory,
250*d9f75844SAndroid Build Coastguard Worker     const SdpVideoFormat& format)
251*d9f75844SAndroid Build Coastguard Worker     : inited_(0),
252*d9f75844SAndroid Build Coastguard Worker       primary_encoder_factory_(primary_factory),
253*d9f75844SAndroid Build Coastguard Worker       fallback_encoder_factory_(fallback_factory),
254*d9f75844SAndroid Build Coastguard Worker       video_format_(format),
255*d9f75844SAndroid Build Coastguard Worker       total_streams_count_(0),
256*d9f75844SAndroid Build Coastguard Worker       bypass_mode_(false),
257*d9f75844SAndroid Build Coastguard Worker       encoded_complete_callback_(nullptr),
258*d9f75844SAndroid Build Coastguard Worker       experimental_boosted_screenshare_qp_(GetScreenshareBoostedQpValue()),
259*d9f75844SAndroid Build Coastguard Worker       boost_base_layer_quality_(RateControlSettings::ParseFromFieldTrials()
260*d9f75844SAndroid Build Coastguard Worker                                     .Vp8BoostBaseLayerQuality()),
261*d9f75844SAndroid Build Coastguard Worker       prefer_temporal_support_on_base_layer_(field_trial::IsEnabled(
262*d9f75844SAndroid Build Coastguard Worker           "WebRTC-Video-PreferTemporalSupportOnBaseLayer")) {
263*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(primary_factory);
264*d9f75844SAndroid Build Coastguard Worker 
265*d9f75844SAndroid Build Coastguard Worker   // The adapter is typically created on the worker thread, but operated on
266*d9f75844SAndroid Build Coastguard Worker   // the encoder task queue.
267*d9f75844SAndroid Build Coastguard Worker   encoder_queue_.Detach();
268*d9f75844SAndroid Build Coastguard Worker }
269*d9f75844SAndroid Build Coastguard Worker 
~SimulcastEncoderAdapter()270*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::~SimulcastEncoderAdapter() {
271*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(!Initialized());
272*d9f75844SAndroid Build Coastguard Worker   DestroyStoredEncoders();
273*d9f75844SAndroid Build Coastguard Worker }
274*d9f75844SAndroid Build Coastguard Worker 
SetFecControllerOverride(FecControllerOverride *)275*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::SetFecControllerOverride(
276*d9f75844SAndroid Build Coastguard Worker     FecControllerOverride* /*fec_controller_override*/) {
277*d9f75844SAndroid Build Coastguard Worker   // Ignored.
278*d9f75844SAndroid Build Coastguard Worker }
279*d9f75844SAndroid Build Coastguard Worker 
Release()280*d9f75844SAndroid Build Coastguard Worker int SimulcastEncoderAdapter::Release() {
281*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(&encoder_queue_);
282*d9f75844SAndroid Build Coastguard Worker 
283*d9f75844SAndroid Build Coastguard Worker   while (!stream_contexts_.empty()) {
284*d9f75844SAndroid Build Coastguard Worker     // Move the encoder instances and put it on the `cached_encoder_contexts_`
285*d9f75844SAndroid Build Coastguard Worker     // where it may possibly be reused from (ordering does not matter).
286*d9f75844SAndroid Build Coastguard Worker     cached_encoder_contexts_.push_front(
287*d9f75844SAndroid Build Coastguard Worker         std::move(stream_contexts_.back()).ReleaseEncoderContext());
288*d9f75844SAndroid Build Coastguard Worker     stream_contexts_.pop_back();
289*d9f75844SAndroid Build Coastguard Worker   }
290*d9f75844SAndroid Build Coastguard Worker 
291*d9f75844SAndroid Build Coastguard Worker   bypass_mode_ = false;
292*d9f75844SAndroid Build Coastguard Worker 
293*d9f75844SAndroid Build Coastguard Worker   // It's legal to move the encoder to another queue now.
294*d9f75844SAndroid Build Coastguard Worker   encoder_queue_.Detach();
295*d9f75844SAndroid Build Coastguard Worker 
296*d9f75844SAndroid Build Coastguard Worker   inited_.store(0);
297*d9f75844SAndroid Build Coastguard Worker 
298*d9f75844SAndroid Build Coastguard Worker   return WEBRTC_VIDEO_CODEC_OK;
299*d9f75844SAndroid Build Coastguard Worker }
300*d9f75844SAndroid Build Coastguard Worker 
InitEncode(const VideoCodec * inst,const VideoEncoder::Settings & settings)301*d9f75844SAndroid Build Coastguard Worker int SimulcastEncoderAdapter::InitEncode(
302*d9f75844SAndroid Build Coastguard Worker     const VideoCodec* inst,
303*d9f75844SAndroid Build Coastguard Worker     const VideoEncoder::Settings& settings) {
304*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(&encoder_queue_);
305*d9f75844SAndroid Build Coastguard Worker 
306*d9f75844SAndroid Build Coastguard Worker   if (settings.number_of_cores < 1) {
307*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
308*d9f75844SAndroid Build Coastguard Worker   }
309*d9f75844SAndroid Build Coastguard Worker 
310*d9f75844SAndroid Build Coastguard Worker   int ret = VerifyCodec(inst);
311*d9f75844SAndroid Build Coastguard Worker   if (ret < 0) {
312*d9f75844SAndroid Build Coastguard Worker     return ret;
313*d9f75844SAndroid Build Coastguard Worker   }
314*d9f75844SAndroid Build Coastguard Worker 
315*d9f75844SAndroid Build Coastguard Worker   Release();
316*d9f75844SAndroid Build Coastguard Worker 
317*d9f75844SAndroid Build Coastguard Worker   codec_ = *inst;
318*d9f75844SAndroid Build Coastguard Worker   total_streams_count_ = CountAllStreams(*inst);
319*d9f75844SAndroid Build Coastguard Worker 
320*d9f75844SAndroid Build Coastguard Worker   // TODO(ronghuawu): Remove once this is handled in LibvpxVp8Encoder.
321*d9f75844SAndroid Build Coastguard Worker   if (codec_.qpMax < kDefaultMinQp) {
322*d9f75844SAndroid Build Coastguard Worker     codec_.qpMax = kDefaultMaxQp;
323*d9f75844SAndroid Build Coastguard Worker   }
324*d9f75844SAndroid Build Coastguard Worker 
325*d9f75844SAndroid Build Coastguard Worker   bool is_legacy_singlecast = codec_.numberOfSimulcastStreams == 0;
326*d9f75844SAndroid Build Coastguard Worker   int lowest_quality_stream_idx = 0;
327*d9f75844SAndroid Build Coastguard Worker   int highest_quality_stream_idx = 0;
328*d9f75844SAndroid Build Coastguard Worker   if (!is_legacy_singlecast) {
329*d9f75844SAndroid Build Coastguard Worker     GetLowestAndHighestQualityStreamIndixes(
330*d9f75844SAndroid Build Coastguard Worker         rtc::ArrayView<SimulcastStream>(codec_.simulcastStream,
331*d9f75844SAndroid Build Coastguard Worker                                         total_streams_count_),
332*d9f75844SAndroid Build Coastguard Worker         &lowest_quality_stream_idx, &highest_quality_stream_idx);
333*d9f75844SAndroid Build Coastguard Worker   }
334*d9f75844SAndroid Build Coastguard Worker 
335*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<EncoderContext> encoder_context = FetchOrCreateEncoderContext(
336*d9f75844SAndroid Build Coastguard Worker       /*is_lowest_quality_stream=*/(
337*d9f75844SAndroid Build Coastguard Worker           is_legacy_singlecast ||
338*d9f75844SAndroid Build Coastguard Worker           codec_.simulcastStream[lowest_quality_stream_idx].active));
339*d9f75844SAndroid Build Coastguard Worker   if (encoder_context == nullptr) {
340*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_MEMORY;
341*d9f75844SAndroid Build Coastguard Worker   }
342*d9f75844SAndroid Build Coastguard Worker 
343*d9f75844SAndroid Build Coastguard Worker   // Two distinct scenarios:
344*d9f75844SAndroid Build Coastguard Worker   // * Singlecast (total_streams_count == 1) or simulcast with simulcast-capable
345*d9f75844SAndroid Build Coastguard Worker   //   underlaying encoder implementation if active_streams_count > 1. SEA
346*d9f75844SAndroid Build Coastguard Worker   //   operates in bypass mode: original settings are passed to the underlaying
347*d9f75844SAndroid Build Coastguard Worker   //   encoder, frame encode complete callback is not intercepted.
348*d9f75844SAndroid Build Coastguard Worker   // * Multi-encoder simulcast or singlecast if layers are deactivated
349*d9f75844SAndroid Build Coastguard Worker   //   (active_streams_count >= 1). SEA creates N=active_streams_count encoders
350*d9f75844SAndroid Build Coastguard Worker   //   and configures each to produce a single stream.
351*d9f75844SAndroid Build Coastguard Worker 
352*d9f75844SAndroid Build Coastguard Worker   int active_streams_count = CountActiveStreams(*inst);
353*d9f75844SAndroid Build Coastguard Worker   // If we only have a single active layer it is better to create an encoder
354*d9f75844SAndroid Build Coastguard Worker   // with only one configured layer than creating it with all-but-one disabled
355*d9f75844SAndroid Build Coastguard Worker   // layers because that way we control scaling.
356*d9f75844SAndroid Build Coastguard Worker   bool separate_encoders_needed =
357*d9f75844SAndroid Build Coastguard Worker       !encoder_context->encoder().GetEncoderInfo().supports_simulcast ||
358*d9f75844SAndroid Build Coastguard Worker       active_streams_count == 1;
359*d9f75844SAndroid Build Coastguard Worker   // Singlecast or simulcast with simulcast-capable underlaying encoder.
360*d9f75844SAndroid Build Coastguard Worker   if (total_streams_count_ == 1 || !separate_encoders_needed) {
361*d9f75844SAndroid Build Coastguard Worker     int ret = encoder_context->encoder().InitEncode(&codec_, settings);
362*d9f75844SAndroid Build Coastguard Worker     if (ret >= 0) {
363*d9f75844SAndroid Build Coastguard Worker       stream_contexts_.emplace_back(
364*d9f75844SAndroid Build Coastguard Worker           /*parent=*/nullptr, std::move(encoder_context),
365*d9f75844SAndroid Build Coastguard Worker           /*framerate_controller=*/nullptr, /*stream_idx=*/0, codec_.width,
366*d9f75844SAndroid Build Coastguard Worker           codec_.height, /*is_paused=*/active_streams_count == 0);
367*d9f75844SAndroid Build Coastguard Worker       bypass_mode_ = true;
368*d9f75844SAndroid Build Coastguard Worker 
369*d9f75844SAndroid Build Coastguard Worker       DestroyStoredEncoders();
370*d9f75844SAndroid Build Coastguard Worker       inited_.store(1);
371*d9f75844SAndroid Build Coastguard Worker       return WEBRTC_VIDEO_CODEC_OK;
372*d9f75844SAndroid Build Coastguard Worker     }
373*d9f75844SAndroid Build Coastguard Worker 
374*d9f75844SAndroid Build Coastguard Worker     encoder_context->Release();
375*d9f75844SAndroid Build Coastguard Worker     if (total_streams_count_ == 1) {
376*d9f75844SAndroid Build Coastguard Worker       // Failed to initialize singlecast encoder.
377*d9f75844SAndroid Build Coastguard Worker       return ret;
378*d9f75844SAndroid Build Coastguard Worker     }
379*d9f75844SAndroid Build Coastguard Worker   }
380*d9f75844SAndroid Build Coastguard Worker 
381*d9f75844SAndroid Build Coastguard Worker   // Multi-encoder simulcast or singlecast (deactivated layers).
382*d9f75844SAndroid Build Coastguard Worker   std::vector<uint32_t> stream_start_bitrate_kbps =
383*d9f75844SAndroid Build Coastguard Worker       GetStreamStartBitratesKbps(codec_);
384*d9f75844SAndroid Build Coastguard Worker 
385*d9f75844SAndroid Build Coastguard Worker   for (int stream_idx = 0; stream_idx < total_streams_count_; ++stream_idx) {
386*d9f75844SAndroid Build Coastguard Worker     if (!is_legacy_singlecast && !codec_.simulcastStream[stream_idx].active) {
387*d9f75844SAndroid Build Coastguard Worker       continue;
388*d9f75844SAndroid Build Coastguard Worker     }
389*d9f75844SAndroid Build Coastguard Worker 
390*d9f75844SAndroid Build Coastguard Worker     if (encoder_context == nullptr) {
391*d9f75844SAndroid Build Coastguard Worker       encoder_context = FetchOrCreateEncoderContext(
392*d9f75844SAndroid Build Coastguard Worker           /*is_lowest_quality_stream=*/stream_idx == lowest_quality_stream_idx);
393*d9f75844SAndroid Build Coastguard Worker     }
394*d9f75844SAndroid Build Coastguard Worker     if (encoder_context == nullptr) {
395*d9f75844SAndroid Build Coastguard Worker       Release();
396*d9f75844SAndroid Build Coastguard Worker       return WEBRTC_VIDEO_CODEC_MEMORY;
397*d9f75844SAndroid Build Coastguard Worker     }
398*d9f75844SAndroid Build Coastguard Worker 
399*d9f75844SAndroid Build Coastguard Worker     VideoCodec stream_codec = MakeStreamCodec(
400*d9f75844SAndroid Build Coastguard Worker         codec_, stream_idx, stream_start_bitrate_kbps[stream_idx],
401*d9f75844SAndroid Build Coastguard Worker         /*is_lowest_quality_stream=*/stream_idx == lowest_quality_stream_idx,
402*d9f75844SAndroid Build Coastguard Worker         /*is_highest_quality_stream=*/stream_idx == highest_quality_stream_idx);
403*d9f75844SAndroid Build Coastguard Worker 
404*d9f75844SAndroid Build Coastguard Worker     int ret = encoder_context->encoder().InitEncode(&stream_codec, settings);
405*d9f75844SAndroid Build Coastguard Worker     if (ret < 0) {
406*d9f75844SAndroid Build Coastguard Worker       encoder_context.reset();
407*d9f75844SAndroid Build Coastguard Worker       Release();
408*d9f75844SAndroid Build Coastguard Worker       return ret;
409*d9f75844SAndroid Build Coastguard Worker     }
410*d9f75844SAndroid Build Coastguard Worker 
411*d9f75844SAndroid Build Coastguard Worker     // Intercept frame encode complete callback only for upper streams, where
412*d9f75844SAndroid Build Coastguard Worker     // we need to set a correct stream index. Set `parent` to nullptr for the
413*d9f75844SAndroid Build Coastguard Worker     // lowest stream to bypass the callback.
414*d9f75844SAndroid Build Coastguard Worker     SimulcastEncoderAdapter* parent = stream_idx > 0 ? this : nullptr;
415*d9f75844SAndroid Build Coastguard Worker 
416*d9f75844SAndroid Build Coastguard Worker     bool is_paused = stream_start_bitrate_kbps[stream_idx] == 0;
417*d9f75844SAndroid Build Coastguard Worker     stream_contexts_.emplace_back(
418*d9f75844SAndroid Build Coastguard Worker         parent, std::move(encoder_context),
419*d9f75844SAndroid Build Coastguard Worker         std::make_unique<FramerateController>(stream_codec.maxFramerate),
420*d9f75844SAndroid Build Coastguard Worker         stream_idx, stream_codec.width, stream_codec.height, is_paused);
421*d9f75844SAndroid Build Coastguard Worker   }
422*d9f75844SAndroid Build Coastguard Worker 
423*d9f75844SAndroid Build Coastguard Worker   // To save memory, don't store encoders that we don't use.
424*d9f75844SAndroid Build Coastguard Worker   DestroyStoredEncoders();
425*d9f75844SAndroid Build Coastguard Worker 
426*d9f75844SAndroid Build Coastguard Worker   inited_.store(1);
427*d9f75844SAndroid Build Coastguard Worker   return WEBRTC_VIDEO_CODEC_OK;
428*d9f75844SAndroid Build Coastguard Worker }
429*d9f75844SAndroid Build Coastguard Worker 
Encode(const VideoFrame & input_image,const std::vector<VideoFrameType> * frame_types)430*d9f75844SAndroid Build Coastguard Worker int SimulcastEncoderAdapter::Encode(
431*d9f75844SAndroid Build Coastguard Worker     const VideoFrame& input_image,
432*d9f75844SAndroid Build Coastguard Worker     const std::vector<VideoFrameType>* frame_types) {
433*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(&encoder_queue_);
434*d9f75844SAndroid Build Coastguard Worker 
435*d9f75844SAndroid Build Coastguard Worker   if (!Initialized()) {
436*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
437*d9f75844SAndroid Build Coastguard Worker   }
438*d9f75844SAndroid Build Coastguard Worker   if (encoded_complete_callback_ == nullptr) {
439*d9f75844SAndroid Build Coastguard Worker     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
440*d9f75844SAndroid Build Coastguard Worker   }
441*d9f75844SAndroid Build Coastguard Worker 
442*d9f75844SAndroid Build Coastguard Worker   if (encoder_info_override_.requested_resolution_alignment()) {
443*d9f75844SAndroid Build Coastguard Worker     const int alignment =
444*d9f75844SAndroid Build Coastguard Worker         *encoder_info_override_.requested_resolution_alignment();
445*d9f75844SAndroid Build Coastguard Worker     if (input_image.width() % alignment != 0 ||
446*d9f75844SAndroid Build Coastguard Worker         input_image.height() % alignment != 0) {
447*d9f75844SAndroid Build Coastguard Worker       RTC_LOG(LS_WARNING) << "Frame " << input_image.width() << "x"
448*d9f75844SAndroid Build Coastguard Worker                           << input_image.height() << " not divisible by "
449*d9f75844SAndroid Build Coastguard Worker                           << alignment;
450*d9f75844SAndroid Build Coastguard Worker       return WEBRTC_VIDEO_CODEC_ERROR;
451*d9f75844SAndroid Build Coastguard Worker     }
452*d9f75844SAndroid Build Coastguard Worker     if (encoder_info_override_.apply_alignment_to_all_simulcast_layers()) {
453*d9f75844SAndroid Build Coastguard Worker       for (const auto& layer : stream_contexts_) {
454*d9f75844SAndroid Build Coastguard Worker         if (layer.width() % alignment != 0 || layer.height() % alignment != 0) {
455*d9f75844SAndroid Build Coastguard Worker           RTC_LOG(LS_WARNING)
456*d9f75844SAndroid Build Coastguard Worker               << "Codec " << layer.width() << "x" << layer.height()
457*d9f75844SAndroid Build Coastguard Worker               << " not divisible by " << alignment;
458*d9f75844SAndroid Build Coastguard Worker           return WEBRTC_VIDEO_CODEC_ERROR;
459*d9f75844SAndroid Build Coastguard Worker         }
460*d9f75844SAndroid Build Coastguard Worker       }
461*d9f75844SAndroid Build Coastguard Worker     }
462*d9f75844SAndroid Build Coastguard Worker   }
463*d9f75844SAndroid Build Coastguard Worker 
464*d9f75844SAndroid Build Coastguard Worker   bool is_keyframe_needed = false;
465*d9f75844SAndroid Build Coastguard Worker   for (const auto& layer : stream_contexts_) {
466*d9f75844SAndroid Build Coastguard Worker     if (layer.is_keyframe_needed()) {
467*d9f75844SAndroid Build Coastguard Worker       // This is legacy behavior, generating a keyframe on all layers
468*d9f75844SAndroid Build Coastguard Worker       // when generating one for a layer that became active for the first time
469*d9f75844SAndroid Build Coastguard Worker       // or after being disabled
470*d9f75844SAndroid Build Coastguard Worker       is_keyframe_needed = true;
471*d9f75844SAndroid Build Coastguard Worker       break;
472*d9f75844SAndroid Build Coastguard Worker     }
473*d9f75844SAndroid Build Coastguard Worker   }
474*d9f75844SAndroid Build Coastguard Worker 
475*d9f75844SAndroid Build Coastguard Worker   // Temporary thay may hold the result of texture to i420 buffer conversion.
476*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<VideoFrameBuffer> src_buffer;
477*d9f75844SAndroid Build Coastguard Worker   int src_width = input_image.width();
478*d9f75844SAndroid Build Coastguard Worker   int src_height = input_image.height();
479*d9f75844SAndroid Build Coastguard Worker 
480*d9f75844SAndroid Build Coastguard Worker   for (auto& layer : stream_contexts_) {
481*d9f75844SAndroid Build Coastguard Worker     // Don't encode frames in resolutions that we don't intend to send.
482*d9f75844SAndroid Build Coastguard Worker     if (layer.is_paused()) {
483*d9f75844SAndroid Build Coastguard Worker       continue;
484*d9f75844SAndroid Build Coastguard Worker     }
485*d9f75844SAndroid Build Coastguard Worker 
486*d9f75844SAndroid Build Coastguard Worker     // Convert timestamp from RTP 90kHz clock.
487*d9f75844SAndroid Build Coastguard Worker     const Timestamp frame_timestamp =
488*d9f75844SAndroid Build Coastguard Worker         Timestamp::Micros((1000 * input_image.timestamp()) / 90);
489*d9f75844SAndroid Build Coastguard Worker 
490*d9f75844SAndroid Build Coastguard Worker     // If adapter is passed through and only one sw encoder does simulcast,
491*d9f75844SAndroid Build Coastguard Worker     // frame types for all streams should be passed to the encoder unchanged.
492*d9f75844SAndroid Build Coastguard Worker     // Otherwise a single per-encoder frame type is passed.
493*d9f75844SAndroid Build Coastguard Worker     std::vector<VideoFrameType> stream_frame_types(
494*d9f75844SAndroid Build Coastguard Worker         bypass_mode_ ? total_streams_count_ : 1,
495*d9f75844SAndroid Build Coastguard Worker         VideoFrameType::kVideoFrameDelta);
496*d9f75844SAndroid Build Coastguard Worker     bool keyframe_requested = false;
497*d9f75844SAndroid Build Coastguard Worker     if (is_keyframe_needed) {
498*d9f75844SAndroid Build Coastguard Worker       std::fill(stream_frame_types.begin(), stream_frame_types.end(),
499*d9f75844SAndroid Build Coastguard Worker                 VideoFrameType::kVideoFrameKey);
500*d9f75844SAndroid Build Coastguard Worker       keyframe_requested = true;
501*d9f75844SAndroid Build Coastguard Worker     } else if (frame_types) {
502*d9f75844SAndroid Build Coastguard Worker       if (bypass_mode_) {
503*d9f75844SAndroid Build Coastguard Worker         // In bypass mode, requesting a key frame on any layer triggers a
504*d9f75844SAndroid Build Coastguard Worker         // key frame request on all layers.
505*d9f75844SAndroid Build Coastguard Worker         for (const auto& frame_type : *frame_types) {
506*d9f75844SAndroid Build Coastguard Worker           if (frame_type == VideoFrameType::kVideoFrameKey) {
507*d9f75844SAndroid Build Coastguard Worker             std::fill(stream_frame_types.begin(), stream_frame_types.end(),
508*d9f75844SAndroid Build Coastguard Worker                       VideoFrameType::kVideoFrameKey);
509*d9f75844SAndroid Build Coastguard Worker             keyframe_requested = true;
510*d9f75844SAndroid Build Coastguard Worker             break;
511*d9f75844SAndroid Build Coastguard Worker           }
512*d9f75844SAndroid Build Coastguard Worker         }
513*d9f75844SAndroid Build Coastguard Worker       } else {
514*d9f75844SAndroid Build Coastguard Worker         size_t stream_idx = static_cast<size_t>(layer.stream_idx());
515*d9f75844SAndroid Build Coastguard Worker         if (frame_types->size() >= stream_idx &&
516*d9f75844SAndroid Build Coastguard Worker             (*frame_types)[stream_idx] == VideoFrameType::kVideoFrameKey) {
517*d9f75844SAndroid Build Coastguard Worker           stream_frame_types[0] = VideoFrameType::kVideoFrameKey;
518*d9f75844SAndroid Build Coastguard Worker           keyframe_requested = true;
519*d9f75844SAndroid Build Coastguard Worker         }
520*d9f75844SAndroid Build Coastguard Worker       }
521*d9f75844SAndroid Build Coastguard Worker     }
522*d9f75844SAndroid Build Coastguard Worker     if (keyframe_requested) {
523*d9f75844SAndroid Build Coastguard Worker       layer.OnKeyframe(frame_timestamp);
524*d9f75844SAndroid Build Coastguard Worker     } else if (layer.ShouldDropFrame(frame_timestamp)) {
525*d9f75844SAndroid Build Coastguard Worker       continue;
526*d9f75844SAndroid Build Coastguard Worker     }
527*d9f75844SAndroid Build Coastguard Worker 
528*d9f75844SAndroid Build Coastguard Worker     // If scaling isn't required, because the input resolution
529*d9f75844SAndroid Build Coastguard Worker     // matches the destination or the input image is empty (e.g.
530*d9f75844SAndroid Build Coastguard Worker     // a keyframe request for encoders with internal camera
531*d9f75844SAndroid Build Coastguard Worker     // sources) or the source image has a native handle, pass the image on
532*d9f75844SAndroid Build Coastguard Worker     // directly. Otherwise, we'll scale it to match what the encoder expects
533*d9f75844SAndroid Build Coastguard Worker     // (below).
534*d9f75844SAndroid Build Coastguard Worker     // For texture frames, the underlying encoder is expected to be able to
535*d9f75844SAndroid Build Coastguard Worker     // correctly sample/scale the source texture.
536*d9f75844SAndroid Build Coastguard Worker     // TODO(perkj): ensure that works going forward, and figure out how this
537*d9f75844SAndroid Build Coastguard Worker     // affects webrtc:5683.
538*d9f75844SAndroid Build Coastguard Worker     if ((layer.width() == src_width && layer.height() == src_height) ||
539*d9f75844SAndroid Build Coastguard Worker         (input_image.video_frame_buffer()->type() ==
540*d9f75844SAndroid Build Coastguard Worker              VideoFrameBuffer::Type::kNative &&
541*d9f75844SAndroid Build Coastguard Worker          layer.encoder().GetEncoderInfo().supports_native_handle)) {
542*d9f75844SAndroid Build Coastguard Worker       int ret = layer.encoder().Encode(input_image, &stream_frame_types);
543*d9f75844SAndroid Build Coastguard Worker       if (ret != WEBRTC_VIDEO_CODEC_OK) {
544*d9f75844SAndroid Build Coastguard Worker         return ret;
545*d9f75844SAndroid Build Coastguard Worker       }
546*d9f75844SAndroid Build Coastguard Worker     } else {
547*d9f75844SAndroid Build Coastguard Worker       if (src_buffer == nullptr) {
548*d9f75844SAndroid Build Coastguard Worker         src_buffer = input_image.video_frame_buffer();
549*d9f75844SAndroid Build Coastguard Worker       }
550*d9f75844SAndroid Build Coastguard Worker       rtc::scoped_refptr<VideoFrameBuffer> dst_buffer =
551*d9f75844SAndroid Build Coastguard Worker           src_buffer->Scale(layer.width(), layer.height());
552*d9f75844SAndroid Build Coastguard Worker       if (!dst_buffer) {
553*d9f75844SAndroid Build Coastguard Worker         RTC_LOG(LS_ERROR) << "Failed to scale video frame";
554*d9f75844SAndroid Build Coastguard Worker         return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE;
555*d9f75844SAndroid Build Coastguard Worker       }
556*d9f75844SAndroid Build Coastguard Worker 
557*d9f75844SAndroid Build Coastguard Worker       // UpdateRect is not propagated to lower simulcast layers currently.
558*d9f75844SAndroid Build Coastguard Worker       // TODO(ilnik): Consider scaling UpdateRect together with the buffer.
559*d9f75844SAndroid Build Coastguard Worker       VideoFrame frame(input_image);
560*d9f75844SAndroid Build Coastguard Worker       frame.set_video_frame_buffer(dst_buffer);
561*d9f75844SAndroid Build Coastguard Worker       frame.set_rotation(webrtc::kVideoRotation_0);
562*d9f75844SAndroid Build Coastguard Worker       frame.set_update_rect(
563*d9f75844SAndroid Build Coastguard Worker           VideoFrame::UpdateRect{0, 0, frame.width(), frame.height()});
564*d9f75844SAndroid Build Coastguard Worker       int ret = layer.encoder().Encode(frame, &stream_frame_types);
565*d9f75844SAndroid Build Coastguard Worker       if (ret != WEBRTC_VIDEO_CODEC_OK) {
566*d9f75844SAndroid Build Coastguard Worker         return ret;
567*d9f75844SAndroid Build Coastguard Worker       }
568*d9f75844SAndroid Build Coastguard Worker     }
569*d9f75844SAndroid Build Coastguard Worker   }
570*d9f75844SAndroid Build Coastguard Worker 
571*d9f75844SAndroid Build Coastguard Worker   return WEBRTC_VIDEO_CODEC_OK;
572*d9f75844SAndroid Build Coastguard Worker }
573*d9f75844SAndroid Build Coastguard Worker 
RegisterEncodeCompleteCallback(EncodedImageCallback * callback)574*d9f75844SAndroid Build Coastguard Worker int SimulcastEncoderAdapter::RegisterEncodeCompleteCallback(
575*d9f75844SAndroid Build Coastguard Worker     EncodedImageCallback* callback) {
576*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(&encoder_queue_);
577*d9f75844SAndroid Build Coastguard Worker   encoded_complete_callback_ = callback;
578*d9f75844SAndroid Build Coastguard Worker   if (!stream_contexts_.empty() && stream_contexts_.front().stream_idx() == 0) {
579*d9f75844SAndroid Build Coastguard Worker     // Bypass frame encode complete callback for the lowest layer since there is
580*d9f75844SAndroid Build Coastguard Worker     // no need to override frame's spatial index.
581*d9f75844SAndroid Build Coastguard Worker     stream_contexts_.front().encoder().RegisterEncodeCompleteCallback(callback);
582*d9f75844SAndroid Build Coastguard Worker   }
583*d9f75844SAndroid Build Coastguard Worker   return WEBRTC_VIDEO_CODEC_OK;
584*d9f75844SAndroid Build Coastguard Worker }
585*d9f75844SAndroid Build Coastguard Worker 
SetRates(const RateControlParameters & parameters)586*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::SetRates(
587*d9f75844SAndroid Build Coastguard Worker     const RateControlParameters& parameters) {
588*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(&encoder_queue_);
589*d9f75844SAndroid Build Coastguard Worker 
590*d9f75844SAndroid Build Coastguard Worker   if (!Initialized()) {
591*d9f75844SAndroid Build Coastguard Worker     RTC_LOG(LS_WARNING) << "SetRates while not initialized";
592*d9f75844SAndroid Build Coastguard Worker     return;
593*d9f75844SAndroid Build Coastguard Worker   }
594*d9f75844SAndroid Build Coastguard Worker 
595*d9f75844SAndroid Build Coastguard Worker   if (parameters.framerate_fps < 1.0) {
596*d9f75844SAndroid Build Coastguard Worker     RTC_LOG(LS_WARNING) << "Invalid framerate: " << parameters.framerate_fps;
597*d9f75844SAndroid Build Coastguard Worker     return;
598*d9f75844SAndroid Build Coastguard Worker   }
599*d9f75844SAndroid Build Coastguard Worker 
600*d9f75844SAndroid Build Coastguard Worker   codec_.maxFramerate = static_cast<uint32_t>(parameters.framerate_fps + 0.5);
601*d9f75844SAndroid Build Coastguard Worker 
602*d9f75844SAndroid Build Coastguard Worker   if (bypass_mode_) {
603*d9f75844SAndroid Build Coastguard Worker     stream_contexts_.front().encoder().SetRates(parameters);
604*d9f75844SAndroid Build Coastguard Worker     return;
605*d9f75844SAndroid Build Coastguard Worker   }
606*d9f75844SAndroid Build Coastguard Worker 
607*d9f75844SAndroid Build Coastguard Worker   for (StreamContext& layer_context : stream_contexts_) {
608*d9f75844SAndroid Build Coastguard Worker     int stream_idx = layer_context.stream_idx();
609*d9f75844SAndroid Build Coastguard Worker     uint32_t stream_bitrate_kbps =
610*d9f75844SAndroid Build Coastguard Worker         parameters.bitrate.GetSpatialLayerSum(stream_idx) / 1000;
611*d9f75844SAndroid Build Coastguard Worker 
612*d9f75844SAndroid Build Coastguard Worker     // Need a key frame if we have not sent this stream before.
613*d9f75844SAndroid Build Coastguard Worker     if (stream_bitrate_kbps > 0 && layer_context.is_paused()) {
614*d9f75844SAndroid Build Coastguard Worker       layer_context.set_is_keyframe_needed();
615*d9f75844SAndroid Build Coastguard Worker     }
616*d9f75844SAndroid Build Coastguard Worker     layer_context.set_is_paused(stream_bitrate_kbps == 0);
617*d9f75844SAndroid Build Coastguard Worker 
618*d9f75844SAndroid Build Coastguard Worker     // Slice the temporal layers out of the full allocation and pass it on to
619*d9f75844SAndroid Build Coastguard Worker     // the encoder handling the current simulcast stream.
620*d9f75844SAndroid Build Coastguard Worker     RateControlParameters stream_parameters = parameters;
621*d9f75844SAndroid Build Coastguard Worker     stream_parameters.bitrate = VideoBitrateAllocation();
622*d9f75844SAndroid Build Coastguard Worker     for (int i = 0; i < kMaxTemporalStreams; ++i) {
623*d9f75844SAndroid Build Coastguard Worker       if (parameters.bitrate.HasBitrate(stream_idx, i)) {
624*d9f75844SAndroid Build Coastguard Worker         stream_parameters.bitrate.SetBitrate(
625*d9f75844SAndroid Build Coastguard Worker             0, i, parameters.bitrate.GetBitrate(stream_idx, i));
626*d9f75844SAndroid Build Coastguard Worker       }
627*d9f75844SAndroid Build Coastguard Worker     }
628*d9f75844SAndroid Build Coastguard Worker 
629*d9f75844SAndroid Build Coastguard Worker     // Assign link allocation proportionally to spatial layer allocation.
630*d9f75844SAndroid Build Coastguard Worker     if (!parameters.bandwidth_allocation.IsZero() &&
631*d9f75844SAndroid Build Coastguard Worker         parameters.bitrate.get_sum_bps() > 0) {
632*d9f75844SAndroid Build Coastguard Worker       stream_parameters.bandwidth_allocation =
633*d9f75844SAndroid Build Coastguard Worker           DataRate::BitsPerSec((parameters.bandwidth_allocation.bps() *
634*d9f75844SAndroid Build Coastguard Worker                                 stream_parameters.bitrate.get_sum_bps()) /
635*d9f75844SAndroid Build Coastguard Worker                                parameters.bitrate.get_sum_bps());
636*d9f75844SAndroid Build Coastguard Worker       // Make sure we don't allocate bandwidth lower than target bitrate.
637*d9f75844SAndroid Build Coastguard Worker       if (stream_parameters.bandwidth_allocation.bps() <
638*d9f75844SAndroid Build Coastguard Worker           stream_parameters.bitrate.get_sum_bps()) {
639*d9f75844SAndroid Build Coastguard Worker         stream_parameters.bandwidth_allocation =
640*d9f75844SAndroid Build Coastguard Worker             DataRate::BitsPerSec(stream_parameters.bitrate.get_sum_bps());
641*d9f75844SAndroid Build Coastguard Worker       }
642*d9f75844SAndroid Build Coastguard Worker     }
643*d9f75844SAndroid Build Coastguard Worker 
644*d9f75844SAndroid Build Coastguard Worker     stream_parameters.framerate_fps = std::min<double>(
645*d9f75844SAndroid Build Coastguard Worker         parameters.framerate_fps,
646*d9f75844SAndroid Build Coastguard Worker         layer_context.target_fps().value_or(parameters.framerate_fps));
647*d9f75844SAndroid Build Coastguard Worker 
648*d9f75844SAndroid Build Coastguard Worker     layer_context.encoder().SetRates(stream_parameters);
649*d9f75844SAndroid Build Coastguard Worker   }
650*d9f75844SAndroid Build Coastguard Worker }
651*d9f75844SAndroid Build Coastguard Worker 
OnPacketLossRateUpdate(float packet_loss_rate)652*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::OnPacketLossRateUpdate(float packet_loss_rate) {
653*d9f75844SAndroid Build Coastguard Worker   for (auto& c : stream_contexts_) {
654*d9f75844SAndroid Build Coastguard Worker     c.encoder().OnPacketLossRateUpdate(packet_loss_rate);
655*d9f75844SAndroid Build Coastguard Worker   }
656*d9f75844SAndroid Build Coastguard Worker }
657*d9f75844SAndroid Build Coastguard Worker 
OnRttUpdate(int64_t rtt_ms)658*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::OnRttUpdate(int64_t rtt_ms) {
659*d9f75844SAndroid Build Coastguard Worker   for (auto& c : stream_contexts_) {
660*d9f75844SAndroid Build Coastguard Worker     c.encoder().OnRttUpdate(rtt_ms);
661*d9f75844SAndroid Build Coastguard Worker   }
662*d9f75844SAndroid Build Coastguard Worker }
663*d9f75844SAndroid Build Coastguard Worker 
OnLossNotification(const LossNotification & loss_notification)664*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::OnLossNotification(
665*d9f75844SAndroid Build Coastguard Worker     const LossNotification& loss_notification) {
666*d9f75844SAndroid Build Coastguard Worker   for (auto& c : stream_contexts_) {
667*d9f75844SAndroid Build Coastguard Worker     c.encoder().OnLossNotification(loss_notification);
668*d9f75844SAndroid Build Coastguard Worker   }
669*d9f75844SAndroid Build Coastguard Worker }
670*d9f75844SAndroid Build Coastguard Worker 
671*d9f75844SAndroid Build Coastguard Worker // TODO(brandtr): Add task checker to this member function, when all encoder
672*d9f75844SAndroid Build Coastguard Worker // callbacks are coming in on the encoder queue.
OnEncodedImage(size_t stream_idx,const EncodedImage & encodedImage,const CodecSpecificInfo * codecSpecificInfo)673*d9f75844SAndroid Build Coastguard Worker EncodedImageCallback::Result SimulcastEncoderAdapter::OnEncodedImage(
674*d9f75844SAndroid Build Coastguard Worker     size_t stream_idx,
675*d9f75844SAndroid Build Coastguard Worker     const EncodedImage& encodedImage,
676*d9f75844SAndroid Build Coastguard Worker     const CodecSpecificInfo* codecSpecificInfo) {
677*d9f75844SAndroid Build Coastguard Worker   EncodedImage stream_image(encodedImage);
678*d9f75844SAndroid Build Coastguard Worker   CodecSpecificInfo stream_codec_specific = *codecSpecificInfo;
679*d9f75844SAndroid Build Coastguard Worker 
680*d9f75844SAndroid Build Coastguard Worker   stream_image.SetSpatialIndex(stream_idx);
681*d9f75844SAndroid Build Coastguard Worker 
682*d9f75844SAndroid Build Coastguard Worker   return encoded_complete_callback_->OnEncodedImage(stream_image,
683*d9f75844SAndroid Build Coastguard Worker                                                     &stream_codec_specific);
684*d9f75844SAndroid Build Coastguard Worker }
685*d9f75844SAndroid Build Coastguard Worker 
OnDroppedFrame(size_t stream_idx)686*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::OnDroppedFrame(size_t stream_idx) {
687*d9f75844SAndroid Build Coastguard Worker   // Not yet implemented.
688*d9f75844SAndroid Build Coastguard Worker }
689*d9f75844SAndroid Build Coastguard Worker 
Initialized() const690*d9f75844SAndroid Build Coastguard Worker bool SimulcastEncoderAdapter::Initialized() const {
691*d9f75844SAndroid Build Coastguard Worker   return inited_.load() == 1;
692*d9f75844SAndroid Build Coastguard Worker }
693*d9f75844SAndroid Build Coastguard Worker 
DestroyStoredEncoders()694*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::DestroyStoredEncoders() {
695*d9f75844SAndroid Build Coastguard Worker   while (!cached_encoder_contexts_.empty()) {
696*d9f75844SAndroid Build Coastguard Worker     cached_encoder_contexts_.pop_back();
697*d9f75844SAndroid Build Coastguard Worker   }
698*d9f75844SAndroid Build Coastguard Worker }
699*d9f75844SAndroid Build Coastguard Worker 
700*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<SimulcastEncoderAdapter::EncoderContext>
FetchOrCreateEncoderContext(bool is_lowest_quality_stream) const701*d9f75844SAndroid Build Coastguard Worker SimulcastEncoderAdapter::FetchOrCreateEncoderContext(
702*d9f75844SAndroid Build Coastguard Worker     bool is_lowest_quality_stream) const {
703*d9f75844SAndroid Build Coastguard Worker   bool prefer_temporal_support = fallback_encoder_factory_ != nullptr &&
704*d9f75844SAndroid Build Coastguard Worker                                  is_lowest_quality_stream &&
705*d9f75844SAndroid Build Coastguard Worker                                  prefer_temporal_support_on_base_layer_;
706*d9f75844SAndroid Build Coastguard Worker 
707*d9f75844SAndroid Build Coastguard Worker   // Toggling of `prefer_temporal_support` requires encoder recreation. Find
708*d9f75844SAndroid Build Coastguard Worker   // and reuse encoder with desired `prefer_temporal_support`. Otherwise, if
709*d9f75844SAndroid Build Coastguard Worker   // there is no such encoder in the cache, create a new instance.
710*d9f75844SAndroid Build Coastguard Worker   auto encoder_context_iter =
711*d9f75844SAndroid Build Coastguard Worker       std::find_if(cached_encoder_contexts_.begin(),
712*d9f75844SAndroid Build Coastguard Worker                    cached_encoder_contexts_.end(), [&](auto& encoder_context) {
713*d9f75844SAndroid Build Coastguard Worker                      return encoder_context->prefer_temporal_support() ==
714*d9f75844SAndroid Build Coastguard Worker                             prefer_temporal_support;
715*d9f75844SAndroid Build Coastguard Worker                    });
716*d9f75844SAndroid Build Coastguard Worker 
717*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<SimulcastEncoderAdapter::EncoderContext> encoder_context;
718*d9f75844SAndroid Build Coastguard Worker   if (encoder_context_iter != cached_encoder_contexts_.end()) {
719*d9f75844SAndroid Build Coastguard Worker     encoder_context = std::move(*encoder_context_iter);
720*d9f75844SAndroid Build Coastguard Worker     cached_encoder_contexts_.erase(encoder_context_iter);
721*d9f75844SAndroid Build Coastguard Worker   } else {
722*d9f75844SAndroid Build Coastguard Worker     std::unique_ptr<VideoEncoder> primary_encoder =
723*d9f75844SAndroid Build Coastguard Worker         primary_encoder_factory_->CreateVideoEncoder(video_format_);
724*d9f75844SAndroid Build Coastguard Worker 
725*d9f75844SAndroid Build Coastguard Worker     std::unique_ptr<VideoEncoder> fallback_encoder;
726*d9f75844SAndroid Build Coastguard Worker     if (fallback_encoder_factory_ != nullptr) {
727*d9f75844SAndroid Build Coastguard Worker       fallback_encoder =
728*d9f75844SAndroid Build Coastguard Worker           fallback_encoder_factory_->CreateVideoEncoder(video_format_);
729*d9f75844SAndroid Build Coastguard Worker     }
730*d9f75844SAndroid Build Coastguard Worker 
731*d9f75844SAndroid Build Coastguard Worker     std::unique_ptr<VideoEncoder> encoder;
732*d9f75844SAndroid Build Coastguard Worker     VideoEncoder::EncoderInfo primary_info;
733*d9f75844SAndroid Build Coastguard Worker     VideoEncoder::EncoderInfo fallback_info;
734*d9f75844SAndroid Build Coastguard Worker 
735*d9f75844SAndroid Build Coastguard Worker     if (primary_encoder != nullptr) {
736*d9f75844SAndroid Build Coastguard Worker       primary_info = primary_encoder->GetEncoderInfo();
737*d9f75844SAndroid Build Coastguard Worker       fallback_info = primary_info;
738*d9f75844SAndroid Build Coastguard Worker 
739*d9f75844SAndroid Build Coastguard Worker       if (fallback_encoder == nullptr) {
740*d9f75844SAndroid Build Coastguard Worker         encoder = std::move(primary_encoder);
741*d9f75844SAndroid Build Coastguard Worker       } else {
742*d9f75844SAndroid Build Coastguard Worker         encoder = CreateVideoEncoderSoftwareFallbackWrapper(
743*d9f75844SAndroid Build Coastguard Worker             std::move(fallback_encoder), std::move(primary_encoder),
744*d9f75844SAndroid Build Coastguard Worker             prefer_temporal_support);
745*d9f75844SAndroid Build Coastguard Worker       }
746*d9f75844SAndroid Build Coastguard Worker     } else if (fallback_encoder != nullptr) {
747*d9f75844SAndroid Build Coastguard Worker       RTC_LOG(LS_WARNING) << "Failed to create primary " << video_format_.name
748*d9f75844SAndroid Build Coastguard Worker                           << " encoder. Use fallback encoder.";
749*d9f75844SAndroid Build Coastguard Worker       fallback_info = fallback_encoder->GetEncoderInfo();
750*d9f75844SAndroid Build Coastguard Worker       primary_info = fallback_info;
751*d9f75844SAndroid Build Coastguard Worker       encoder = std::move(fallback_encoder);
752*d9f75844SAndroid Build Coastguard Worker     } else {
753*d9f75844SAndroid Build Coastguard Worker       RTC_LOG(LS_ERROR) << "Failed to create primary and fallback "
754*d9f75844SAndroid Build Coastguard Worker                         << video_format_.name << " encoders.";
755*d9f75844SAndroid Build Coastguard Worker       return nullptr;
756*d9f75844SAndroid Build Coastguard Worker     }
757*d9f75844SAndroid Build Coastguard Worker 
758*d9f75844SAndroid Build Coastguard Worker     encoder_context = std::make_unique<SimulcastEncoderAdapter::EncoderContext>(
759*d9f75844SAndroid Build Coastguard Worker         std::move(encoder), prefer_temporal_support, primary_info,
760*d9f75844SAndroid Build Coastguard Worker         fallback_info);
761*d9f75844SAndroid Build Coastguard Worker   }
762*d9f75844SAndroid Build Coastguard Worker 
763*d9f75844SAndroid Build Coastguard Worker   encoder_context->encoder().RegisterEncodeCompleteCallback(
764*d9f75844SAndroid Build Coastguard Worker       encoded_complete_callback_);
765*d9f75844SAndroid Build Coastguard Worker   return encoder_context;
766*d9f75844SAndroid Build Coastguard Worker }
767*d9f75844SAndroid Build Coastguard Worker 
MakeStreamCodec(const webrtc::VideoCodec & codec,int stream_idx,uint32_t start_bitrate_kbps,bool is_lowest_quality_stream,bool is_highest_quality_stream)768*d9f75844SAndroid Build Coastguard Worker webrtc::VideoCodec SimulcastEncoderAdapter::MakeStreamCodec(
769*d9f75844SAndroid Build Coastguard Worker     const webrtc::VideoCodec& codec,
770*d9f75844SAndroid Build Coastguard Worker     int stream_idx,
771*d9f75844SAndroid Build Coastguard Worker     uint32_t start_bitrate_kbps,
772*d9f75844SAndroid Build Coastguard Worker     bool is_lowest_quality_stream,
773*d9f75844SAndroid Build Coastguard Worker     bool is_highest_quality_stream) {
774*d9f75844SAndroid Build Coastguard Worker   webrtc::VideoCodec codec_params = codec;
775*d9f75844SAndroid Build Coastguard Worker   const SimulcastStream& stream_params = codec.simulcastStream[stream_idx];
776*d9f75844SAndroid Build Coastguard Worker 
777*d9f75844SAndroid Build Coastguard Worker   codec_params.numberOfSimulcastStreams = 0;
778*d9f75844SAndroid Build Coastguard Worker   codec_params.width = stream_params.width;
779*d9f75844SAndroid Build Coastguard Worker   codec_params.height = stream_params.height;
780*d9f75844SAndroid Build Coastguard Worker   codec_params.maxBitrate = stream_params.maxBitrate;
781*d9f75844SAndroid Build Coastguard Worker   codec_params.minBitrate = stream_params.minBitrate;
782*d9f75844SAndroid Build Coastguard Worker   codec_params.maxFramerate = stream_params.maxFramerate;
783*d9f75844SAndroid Build Coastguard Worker   codec_params.qpMax = stream_params.qpMax;
784*d9f75844SAndroid Build Coastguard Worker   codec_params.active = stream_params.active;
785*d9f75844SAndroid Build Coastguard Worker   codec_params.SetScalabilityMode(stream_params.GetScalabilityMode());
786*d9f75844SAndroid Build Coastguard Worker   // Settings that are based on stream/resolution.
787*d9f75844SAndroid Build Coastguard Worker   if (is_lowest_quality_stream) {
788*d9f75844SAndroid Build Coastguard Worker     // Settings for lowest spatial resolutions.
789*d9f75844SAndroid Build Coastguard Worker     if (codec.mode == VideoCodecMode::kScreensharing) {
790*d9f75844SAndroid Build Coastguard Worker       if (experimental_boosted_screenshare_qp_) {
791*d9f75844SAndroid Build Coastguard Worker         codec_params.qpMax = *experimental_boosted_screenshare_qp_;
792*d9f75844SAndroid Build Coastguard Worker       }
793*d9f75844SAndroid Build Coastguard Worker     } else if (boost_base_layer_quality_) {
794*d9f75844SAndroid Build Coastguard Worker       codec_params.qpMax = kLowestResMaxQp;
795*d9f75844SAndroid Build Coastguard Worker     }
796*d9f75844SAndroid Build Coastguard Worker   }
797*d9f75844SAndroid Build Coastguard Worker   if (codec.codecType == webrtc::kVideoCodecVP8) {
798*d9f75844SAndroid Build Coastguard Worker     codec_params.VP8()->numberOfTemporalLayers =
799*d9f75844SAndroid Build Coastguard Worker         stream_params.numberOfTemporalLayers;
800*d9f75844SAndroid Build Coastguard Worker     if (!is_highest_quality_stream) {
801*d9f75844SAndroid Build Coastguard Worker       // For resolutions below CIF, set the codec `complexity` parameter to
802*d9f75844SAndroid Build Coastguard Worker       // kComplexityHigher, which maps to cpu_used = -4.
803*d9f75844SAndroid Build Coastguard Worker       int pixels_per_frame = codec_params.width * codec_params.height;
804*d9f75844SAndroid Build Coastguard Worker       if (pixels_per_frame < 352 * 288) {
805*d9f75844SAndroid Build Coastguard Worker         codec_params.SetVideoEncoderComplexity(
806*d9f75844SAndroid Build Coastguard Worker             webrtc::VideoCodecComplexity::kComplexityHigher);
807*d9f75844SAndroid Build Coastguard Worker       }
808*d9f75844SAndroid Build Coastguard Worker       // Turn off denoising for all streams but the highest resolution.
809*d9f75844SAndroid Build Coastguard Worker       codec_params.VP8()->denoisingOn = false;
810*d9f75844SAndroid Build Coastguard Worker     }
811*d9f75844SAndroid Build Coastguard Worker   } else if (codec.codecType == webrtc::kVideoCodecH264) {
812*d9f75844SAndroid Build Coastguard Worker     codec_params.H264()->numberOfTemporalLayers =
813*d9f75844SAndroid Build Coastguard Worker         stream_params.numberOfTemporalLayers;
814*d9f75844SAndroid Build Coastguard Worker   }
815*d9f75844SAndroid Build Coastguard Worker 
816*d9f75844SAndroid Build Coastguard Worker   // Cap start bitrate to the min bitrate in order to avoid strange codec
817*d9f75844SAndroid Build Coastguard Worker   // behavior.
818*d9f75844SAndroid Build Coastguard Worker   codec_params.startBitrate =
819*d9f75844SAndroid Build Coastguard Worker       std::max(stream_params.minBitrate, start_bitrate_kbps);
820*d9f75844SAndroid Build Coastguard Worker 
821*d9f75844SAndroid Build Coastguard Worker   // Legacy screenshare mode is only enabled for the first simulcast layer
822*d9f75844SAndroid Build Coastguard Worker   codec_params.legacy_conference_mode =
823*d9f75844SAndroid Build Coastguard Worker       codec.legacy_conference_mode && stream_idx == 0;
824*d9f75844SAndroid Build Coastguard Worker 
825*d9f75844SAndroid Build Coastguard Worker   return codec_params;
826*d9f75844SAndroid Build Coastguard Worker }
827*d9f75844SAndroid Build Coastguard Worker 
OverrideFromFieldTrial(VideoEncoder::EncoderInfo * info) const828*d9f75844SAndroid Build Coastguard Worker void SimulcastEncoderAdapter::OverrideFromFieldTrial(
829*d9f75844SAndroid Build Coastguard Worker     VideoEncoder::EncoderInfo* info) const {
830*d9f75844SAndroid Build Coastguard Worker   if (encoder_info_override_.requested_resolution_alignment()) {
831*d9f75844SAndroid Build Coastguard Worker     info->requested_resolution_alignment = cricket::LeastCommonMultiple(
832*d9f75844SAndroid Build Coastguard Worker         info->requested_resolution_alignment,
833*d9f75844SAndroid Build Coastguard Worker         *encoder_info_override_.requested_resolution_alignment());
834*d9f75844SAndroid Build Coastguard Worker     info->apply_alignment_to_all_simulcast_layers =
835*d9f75844SAndroid Build Coastguard Worker         info->apply_alignment_to_all_simulcast_layers ||
836*d9f75844SAndroid Build Coastguard Worker         encoder_info_override_.apply_alignment_to_all_simulcast_layers();
837*d9f75844SAndroid Build Coastguard Worker   }
838*d9f75844SAndroid Build Coastguard Worker   // Override resolution bitrate limits unless they're set already.
839*d9f75844SAndroid Build Coastguard Worker   if (info->resolution_bitrate_limits.empty() &&
840*d9f75844SAndroid Build Coastguard Worker       !encoder_info_override_.resolution_bitrate_limits().empty()) {
841*d9f75844SAndroid Build Coastguard Worker     info->resolution_bitrate_limits =
842*d9f75844SAndroid Build Coastguard Worker         encoder_info_override_.resolution_bitrate_limits();
843*d9f75844SAndroid Build Coastguard Worker   }
844*d9f75844SAndroid Build Coastguard Worker }
845*d9f75844SAndroid Build Coastguard Worker 
GetEncoderInfo() const846*d9f75844SAndroid Build Coastguard Worker VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const {
847*d9f75844SAndroid Build Coastguard Worker   if (stream_contexts_.size() == 1) {
848*d9f75844SAndroid Build Coastguard Worker     // Not using simulcast adapting functionality, just pass through.
849*d9f75844SAndroid Build Coastguard Worker     VideoEncoder::EncoderInfo info =
850*d9f75844SAndroid Build Coastguard Worker         stream_contexts_.front().encoder().GetEncoderInfo();
851*d9f75844SAndroid Build Coastguard Worker     OverrideFromFieldTrial(&info);
852*d9f75844SAndroid Build Coastguard Worker     return info;
853*d9f75844SAndroid Build Coastguard Worker   }
854*d9f75844SAndroid Build Coastguard Worker 
855*d9f75844SAndroid Build Coastguard Worker   VideoEncoder::EncoderInfo encoder_info;
856*d9f75844SAndroid Build Coastguard Worker   encoder_info.implementation_name = "SimulcastEncoderAdapter";
857*d9f75844SAndroid Build Coastguard Worker   encoder_info.requested_resolution_alignment = 1;
858*d9f75844SAndroid Build Coastguard Worker   encoder_info.apply_alignment_to_all_simulcast_layers = false;
859*d9f75844SAndroid Build Coastguard Worker   encoder_info.supports_native_handle = true;
860*d9f75844SAndroid Build Coastguard Worker   encoder_info.scaling_settings.thresholds = absl::nullopt;
861*d9f75844SAndroid Build Coastguard Worker 
862*d9f75844SAndroid Build Coastguard Worker   if (stream_contexts_.empty()) {
863*d9f75844SAndroid Build Coastguard Worker     // GetEncoderInfo queried before InitEncode. Only alignment info is needed
864*d9f75844SAndroid Build Coastguard Worker     // to be filled.
865*d9f75844SAndroid Build Coastguard Worker     // Create one encoder and query it.
866*d9f75844SAndroid Build Coastguard Worker 
867*d9f75844SAndroid Build Coastguard Worker     std::unique_ptr<SimulcastEncoderAdapter::EncoderContext> encoder_context =
868*d9f75844SAndroid Build Coastguard Worker         FetchOrCreateEncoderContext(/*is_lowest_quality_stream=*/true);
869*d9f75844SAndroid Build Coastguard Worker     if (encoder_context == nullptr) {
870*d9f75844SAndroid Build Coastguard Worker       return encoder_info;
871*d9f75844SAndroid Build Coastguard Worker     }
872*d9f75844SAndroid Build Coastguard Worker 
873*d9f75844SAndroid Build Coastguard Worker     const VideoEncoder::EncoderInfo& primary_info =
874*d9f75844SAndroid Build Coastguard Worker         encoder_context->PrimaryInfo();
875*d9f75844SAndroid Build Coastguard Worker     const VideoEncoder::EncoderInfo& fallback_info =
876*d9f75844SAndroid Build Coastguard Worker         encoder_context->FallbackInfo();
877*d9f75844SAndroid Build Coastguard Worker 
878*d9f75844SAndroid Build Coastguard Worker     encoder_info.requested_resolution_alignment = cricket::LeastCommonMultiple(
879*d9f75844SAndroid Build Coastguard Worker         primary_info.requested_resolution_alignment,
880*d9f75844SAndroid Build Coastguard Worker         fallback_info.requested_resolution_alignment);
881*d9f75844SAndroid Build Coastguard Worker 
882*d9f75844SAndroid Build Coastguard Worker     encoder_info.apply_alignment_to_all_simulcast_layers =
883*d9f75844SAndroid Build Coastguard Worker         primary_info.apply_alignment_to_all_simulcast_layers ||
884*d9f75844SAndroid Build Coastguard Worker         fallback_info.apply_alignment_to_all_simulcast_layers;
885*d9f75844SAndroid Build Coastguard Worker 
886*d9f75844SAndroid Build Coastguard Worker     if (!primary_info.supports_simulcast || !fallback_info.supports_simulcast) {
887*d9f75844SAndroid Build Coastguard Worker       encoder_info.apply_alignment_to_all_simulcast_layers = true;
888*d9f75844SAndroid Build Coastguard Worker     }
889*d9f75844SAndroid Build Coastguard Worker 
890*d9f75844SAndroid Build Coastguard Worker     cached_encoder_contexts_.emplace_back(std::move(encoder_context));
891*d9f75844SAndroid Build Coastguard Worker 
892*d9f75844SAndroid Build Coastguard Worker     OverrideFromFieldTrial(&encoder_info);
893*d9f75844SAndroid Build Coastguard Worker     return encoder_info;
894*d9f75844SAndroid Build Coastguard Worker   }
895*d9f75844SAndroid Build Coastguard Worker 
896*d9f75844SAndroid Build Coastguard Worker   encoder_info.scaling_settings = VideoEncoder::ScalingSettings::kOff;
897*d9f75844SAndroid Build Coastguard Worker 
898*d9f75844SAndroid Build Coastguard Worker   for (size_t i = 0; i < stream_contexts_.size(); ++i) {
899*d9f75844SAndroid Build Coastguard Worker     VideoEncoder::EncoderInfo encoder_impl_info =
900*d9f75844SAndroid Build Coastguard Worker         stream_contexts_[i].encoder().GetEncoderInfo();
901*d9f75844SAndroid Build Coastguard Worker     if (i == 0) {
902*d9f75844SAndroid Build Coastguard Worker       // Encoder name indicates names of all sub-encoders.
903*d9f75844SAndroid Build Coastguard Worker       encoder_info.implementation_name += " (";
904*d9f75844SAndroid Build Coastguard Worker       encoder_info.implementation_name += encoder_impl_info.implementation_name;
905*d9f75844SAndroid Build Coastguard Worker 
906*d9f75844SAndroid Build Coastguard Worker       encoder_info.supports_native_handle =
907*d9f75844SAndroid Build Coastguard Worker           encoder_impl_info.supports_native_handle;
908*d9f75844SAndroid Build Coastguard Worker       encoder_info.has_trusted_rate_controller =
909*d9f75844SAndroid Build Coastguard Worker           encoder_impl_info.has_trusted_rate_controller;
910*d9f75844SAndroid Build Coastguard Worker       encoder_info.is_hardware_accelerated =
911*d9f75844SAndroid Build Coastguard Worker           encoder_impl_info.is_hardware_accelerated;
912*d9f75844SAndroid Build Coastguard Worker       encoder_info.is_qp_trusted = encoder_impl_info.is_qp_trusted;
913*d9f75844SAndroid Build Coastguard Worker     } else {
914*d9f75844SAndroid Build Coastguard Worker       encoder_info.implementation_name += ", ";
915*d9f75844SAndroid Build Coastguard Worker       encoder_info.implementation_name += encoder_impl_info.implementation_name;
916*d9f75844SAndroid Build Coastguard Worker 
917*d9f75844SAndroid Build Coastguard Worker       // Native handle supported if any encoder supports it.
918*d9f75844SAndroid Build Coastguard Worker       encoder_info.supports_native_handle |=
919*d9f75844SAndroid Build Coastguard Worker           encoder_impl_info.supports_native_handle;
920*d9f75844SAndroid Build Coastguard Worker 
921*d9f75844SAndroid Build Coastguard Worker       // Trusted rate controller only if all encoders have it.
922*d9f75844SAndroid Build Coastguard Worker       encoder_info.has_trusted_rate_controller &=
923*d9f75844SAndroid Build Coastguard Worker           encoder_impl_info.has_trusted_rate_controller;
924*d9f75844SAndroid Build Coastguard Worker 
925*d9f75844SAndroid Build Coastguard Worker       // Uses hardware support if any of the encoders uses it.
926*d9f75844SAndroid Build Coastguard Worker       // For example, if we are having issues with down-scaling due to
927*d9f75844SAndroid Build Coastguard Worker       // pipelining delay in HW encoders we need higher encoder usage
928*d9f75844SAndroid Build Coastguard Worker       // thresholds in CPU adaptation.
929*d9f75844SAndroid Build Coastguard Worker       encoder_info.is_hardware_accelerated |=
930*d9f75844SAndroid Build Coastguard Worker           encoder_impl_info.is_hardware_accelerated;
931*d9f75844SAndroid Build Coastguard Worker 
932*d9f75844SAndroid Build Coastguard Worker       // Treat QP from frame/slice/tile header as average QP only if all
933*d9f75844SAndroid Build Coastguard Worker       // encoders report it as average QP.
934*d9f75844SAndroid Build Coastguard Worker       encoder_info.is_qp_trusted =
935*d9f75844SAndroid Build Coastguard Worker           encoder_info.is_qp_trusted.value_or(true) &&
936*d9f75844SAndroid Build Coastguard Worker           encoder_impl_info.is_qp_trusted.value_or(true);
937*d9f75844SAndroid Build Coastguard Worker     }
938*d9f75844SAndroid Build Coastguard Worker     encoder_info.fps_allocation[i] = encoder_impl_info.fps_allocation[0];
939*d9f75844SAndroid Build Coastguard Worker     encoder_info.requested_resolution_alignment = cricket::LeastCommonMultiple(
940*d9f75844SAndroid Build Coastguard Worker         encoder_info.requested_resolution_alignment,
941*d9f75844SAndroid Build Coastguard Worker         encoder_impl_info.requested_resolution_alignment);
942*d9f75844SAndroid Build Coastguard Worker     // request alignment on all layers if any of the encoders may need it, or
943*d9f75844SAndroid Build Coastguard Worker     // if any non-top layer encoder requests a non-trivial alignment.
944*d9f75844SAndroid Build Coastguard Worker     if (encoder_impl_info.apply_alignment_to_all_simulcast_layers ||
945*d9f75844SAndroid Build Coastguard Worker         (encoder_impl_info.requested_resolution_alignment > 1 &&
946*d9f75844SAndroid Build Coastguard Worker          (codec_.simulcastStream[i].height < codec_.height ||
947*d9f75844SAndroid Build Coastguard Worker           codec_.simulcastStream[i].width < codec_.width))) {
948*d9f75844SAndroid Build Coastguard Worker       encoder_info.apply_alignment_to_all_simulcast_layers = true;
949*d9f75844SAndroid Build Coastguard Worker     }
950*d9f75844SAndroid Build Coastguard Worker   }
951*d9f75844SAndroid Build Coastguard Worker   encoder_info.implementation_name += ")";
952*d9f75844SAndroid Build Coastguard Worker 
953*d9f75844SAndroid Build Coastguard Worker   OverrideFromFieldTrial(&encoder_info);
954*d9f75844SAndroid Build Coastguard Worker 
955*d9f75844SAndroid Build Coastguard Worker   return encoder_info;
956*d9f75844SAndroid Build Coastguard Worker }
957*d9f75844SAndroid Build Coastguard Worker 
958*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
959