xref: /aosp_15_r20/external/webrtc/test/pc/e2e/analyzer/video/quality_analyzing_video_encoder.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2019 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 "test/pc/e2e/analyzer/video/quality_analyzing_video_encoder.h"
12 
13 #include <cmath>
14 #include <memory>
15 #include <utility>
16 
17 #include "absl/strings/string_view.h"
18 #include "api/video/video_codec_type.h"
19 #include "api/video_codecs/video_encoder.h"
20 #include "modules/video_coding/include/video_error_codes.h"
21 #include "modules/video_coding/svc/scalability_mode_util.h"
22 #include "rtc_base/logging.h"
23 
24 namespace webrtc {
25 namespace webrtc_pc_e2e {
26 namespace {
27 
28 using EmulatedSFUConfigMap =
29     ::webrtc::webrtc_pc_e2e::QualityAnalyzingVideoEncoder::EmulatedSFUConfigMap;
30 
31 constexpr size_t kMaxFrameInPipelineCount = 1000;
32 constexpr double kNoMultiplier = 1.0;
33 constexpr double kEps = 1e-6;
34 
GetMinMaxBitratesBps(const VideoCodec & codec,size_t spatial_idx)35 std::pair<uint32_t, uint32_t> GetMinMaxBitratesBps(const VideoCodec& codec,
36                                                    size_t spatial_idx) {
37   uint32_t min_bitrate = codec.minBitrate;
38   uint32_t max_bitrate = codec.maxBitrate;
39   if (spatial_idx < codec.numberOfSimulcastStreams &&
40       codec.codecType != VideoCodecType::kVideoCodecVP9) {
41     min_bitrate =
42         std::max(min_bitrate, codec.simulcastStream[spatial_idx].minBitrate);
43     max_bitrate =
44         std::min(max_bitrate, codec.simulcastStream[spatial_idx].maxBitrate);
45   }
46   if (codec.codecType == VideoCodecType::kVideoCodecVP9 &&
47       spatial_idx < codec.VP9().numberOfSpatialLayers) {
48     min_bitrate =
49         std::max(min_bitrate, codec.spatialLayers[spatial_idx].minBitrate);
50     max_bitrate =
51         std::min(max_bitrate, codec.spatialLayers[spatial_idx].maxBitrate);
52   }
53   RTC_DCHECK_GT(max_bitrate, min_bitrate);
54   return {min_bitrate * 1000, max_bitrate * 1000};
55 }
56 
57 }  // namespace
58 
QualityAnalyzingVideoEncoder(absl::string_view peer_name,std::unique_ptr<VideoEncoder> delegate,double bitrate_multiplier,EmulatedSFUConfigMap stream_to_sfu_config,EncodedImageDataInjector * injector,VideoQualityAnalyzerInterface * analyzer)59 QualityAnalyzingVideoEncoder::QualityAnalyzingVideoEncoder(
60     absl::string_view peer_name,
61     std::unique_ptr<VideoEncoder> delegate,
62     double bitrate_multiplier,
63     EmulatedSFUConfigMap stream_to_sfu_config,
64     EncodedImageDataInjector* injector,
65     VideoQualityAnalyzerInterface* analyzer)
66     : peer_name_(peer_name),
67       delegate_(std::move(delegate)),
68       bitrate_multiplier_(bitrate_multiplier),
69       stream_to_sfu_config_(std::move(stream_to_sfu_config)),
70       injector_(injector),
71       analyzer_(analyzer),
72       mode_(SimulcastMode::kNormal),
73       delegate_callback_(nullptr) {}
74 QualityAnalyzingVideoEncoder::~QualityAnalyzingVideoEncoder() = default;
75 
SetFecControllerOverride(FecControllerOverride * fec_controller_override)76 void QualityAnalyzingVideoEncoder::SetFecControllerOverride(
77     FecControllerOverride* fec_controller_override) {
78   // Ignored.
79 }
80 
InitEncode(const VideoCodec * codec_settings,const Settings & settings)81 int32_t QualityAnalyzingVideoEncoder::InitEncode(
82     const VideoCodec* codec_settings,
83     const Settings& settings) {
84   MutexLock lock(&mutex_);
85   codec_settings_ = *codec_settings;
86   mode_ = SimulcastMode::kNormal;
87   absl::optional<InterLayerPredMode> inter_layer_pred_mode;
88   if (codec_settings->GetScalabilityMode().has_value()) {
89     inter_layer_pred_mode = ScalabilityModeToInterLayerPredMode(
90         *codec_settings->GetScalabilityMode());
91   } else if (codec_settings->codecType == kVideoCodecVP9) {
92     if (codec_settings->VP9().numberOfSpatialLayers > 1) {
93       inter_layer_pred_mode = codec_settings->VP9().interLayerPred;
94     }
95   }
96   if (inter_layer_pred_mode.has_value()) {
97     switch (*inter_layer_pred_mode) {
98       case InterLayerPredMode::kOn:
99         mode_ = SimulcastMode::kSVC;
100         break;
101       case InterLayerPredMode::kOnKeyPic:
102         mode_ = SimulcastMode::kKSVC;
103         break;
104       case InterLayerPredMode::kOff:
105         mode_ = SimulcastMode::kSimulcast;
106         break;
107       default:
108         RTC_DCHECK_NOTREACHED()
109             << "Unknown InterLayerPredMode value " << *inter_layer_pred_mode;
110         break;
111     }
112   }
113   if (codec_settings->numberOfSimulcastStreams > 1) {
114     mode_ = SimulcastMode::kSimulcast;
115   }
116   return delegate_->InitEncode(codec_settings, settings);
117 }
118 
RegisterEncodeCompleteCallback(EncodedImageCallback * callback)119 int32_t QualityAnalyzingVideoEncoder::RegisterEncodeCompleteCallback(
120     EncodedImageCallback* callback) {
121   // We need to get a lock here because delegate_callback can be hypothetically
122   // accessed from different thread (encoder one) concurrently.
123   MutexLock lock(&mutex_);
124   delegate_callback_ = callback;
125   return delegate_->RegisterEncodeCompleteCallback(this);
126 }
127 
Release()128 int32_t QualityAnalyzingVideoEncoder::Release() {
129   // Release encoder first. During release process it can still encode some
130   // frames, so we don't take a lock to prevent deadlock.
131   int32_t result = delegate_->Release();
132 
133   MutexLock lock(&mutex_);
134   delegate_callback_ = nullptr;
135   return result;
136 }
137 
Encode(const VideoFrame & frame,const std::vector<VideoFrameType> * frame_types)138 int32_t QualityAnalyzingVideoEncoder::Encode(
139     const VideoFrame& frame,
140     const std::vector<VideoFrameType>* frame_types) {
141   {
142     MutexLock lock(&mutex_);
143     // Store id to be able to retrieve it in analyzing callback.
144     timestamp_to_frame_id_list_.push_back({frame.timestamp(), frame.id()});
145     // If this list is growing, it means that we are not receiving new encoded
146     // images from encoder. So it should be a bug in setup on in the encoder.
147     RTC_DCHECK_LT(timestamp_to_frame_id_list_.size(), kMaxFrameInPipelineCount);
148   }
149   analyzer_->OnFramePreEncode(peer_name_, frame);
150   int32_t result = delegate_->Encode(frame, frame_types);
151   if (result != WEBRTC_VIDEO_CODEC_OK) {
152     // If origin encoder failed, then cleanup data for this frame.
153     {
154       MutexLock lock(&mutex_);
155       // The timestamp-frame_id pair can be not the last one, so we need to
156       // find it first and then remove. We will search from the end, because
157       // usually it will be the last or close to the last one.
158       auto it = timestamp_to_frame_id_list_.end();
159       while (it != timestamp_to_frame_id_list_.begin()) {
160         --it;
161         if (it->first == frame.timestamp()) {
162           timestamp_to_frame_id_list_.erase(it);
163           break;
164         }
165       }
166     }
167     analyzer_->OnEncoderError(peer_name_, frame, result);
168   }
169   return result;
170 }
171 
SetRates(const VideoEncoder::RateControlParameters & parameters)172 void QualityAnalyzingVideoEncoder::SetRates(
173     const VideoEncoder::RateControlParameters& parameters) {
174   RTC_DCHECK_GT(bitrate_multiplier_, 0.0);
175   if (fabs(bitrate_multiplier_ - kNoMultiplier) < kEps) {
176     {
177       MutexLock lock(&mutex_);
178       bitrate_allocation_ = parameters.bitrate;
179     }
180     return delegate_->SetRates(parameters);
181   }
182 
183   RateControlParameters adjusted_params = parameters;
184   {
185     MutexLock lock(&mutex_);
186     // Simulating encoder overshooting target bitrate, by configuring actual
187     // encoder too high. Take care not to adjust past limits of config,
188     // otherwise encoders may crash on DCHECK.
189     VideoBitrateAllocation multiplied_allocation;
190     for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
191       const uint32_t spatial_layer_bitrate_bps =
192           parameters.bitrate.GetSpatialLayerSum(si);
193       if (spatial_layer_bitrate_bps == 0) {
194         continue;
195       }
196 
197       uint32_t min_bitrate_bps;
198       uint32_t max_bitrate_bps;
199       std::tie(min_bitrate_bps, max_bitrate_bps) =
200           GetMinMaxBitratesBps(codec_settings_, si);
201       double bitrate_multiplier = bitrate_multiplier_;
202       const uint32_t corrected_bitrate = rtc::checked_cast<uint32_t>(
203           bitrate_multiplier * spatial_layer_bitrate_bps);
204       if (corrected_bitrate < min_bitrate_bps) {
205         bitrate_multiplier = min_bitrate_bps / spatial_layer_bitrate_bps;
206       } else if (corrected_bitrate > max_bitrate_bps) {
207         bitrate_multiplier = max_bitrate_bps / spatial_layer_bitrate_bps;
208       }
209 
210       for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
211         if (parameters.bitrate.HasBitrate(si, ti)) {
212           multiplied_allocation.SetBitrate(
213               si, ti,
214               rtc::checked_cast<uint32_t>(
215                   bitrate_multiplier * parameters.bitrate.GetBitrate(si, ti)));
216         }
217       }
218     }
219 
220     adjusted_params.bitrate = multiplied_allocation;
221     bitrate_allocation_ = adjusted_params.bitrate;
222   }
223   return delegate_->SetRates(adjusted_params);
224 }
225 
GetEncoderInfo() const226 VideoEncoder::EncoderInfo QualityAnalyzingVideoEncoder::GetEncoderInfo() const {
227   return delegate_->GetEncoderInfo();
228 }
229 
230 // It is assumed, that encoded callback will be always invoked with encoded
231 // images that correspond to the frames in the same sequence, that frames
232 // arrived. In other words, assume we have frames F1, F2 and F3 and they have
233 // corresponding encoded images I1, I2 and I3. In such case if we will call
234 // encode first with F1, then with F2 and then with F3, then encoder callback
235 // will be called first with all spatial layers for F1 (I1), then F2 (I2) and
236 // then F3 (I3).
237 //
238 // Basing on it we will use a list of timestamp-frame_id pairs like this:
239 //  1. If current encoded image timestamp is equals to timestamp in the front
240 //     pair - pick frame id from that pair
241 //  2. If current encoded image timestamp isn't equals to timestamp in the front
242 //     pair - remove the front pair and got to the step 1.
OnEncodedImage(const EncodedImage & encoded_image,const CodecSpecificInfo * codec_specific_info)243 EncodedImageCallback::Result QualityAnalyzingVideoEncoder::OnEncodedImage(
244     const EncodedImage& encoded_image,
245     const CodecSpecificInfo* codec_specific_info) {
246   uint16_t frame_id;
247   bool discard = false;
248   uint32_t target_encode_bitrate = 0;
249   std::string codec_name;
250   {
251     MutexLock lock(&mutex_);
252     std::pair<uint32_t, uint16_t> timestamp_frame_id;
253     while (!timestamp_to_frame_id_list_.empty()) {
254       timestamp_frame_id = timestamp_to_frame_id_list_.front();
255       if (timestamp_frame_id.first == encoded_image.Timestamp()) {
256         break;
257       }
258       timestamp_to_frame_id_list_.pop_front();
259     }
260 
261     // After the loop the first element should point to current `encoded_image`
262     // frame id. We don't remove it from the list, because there may be
263     // multiple spatial layers for this frame, so encoder can produce more
264     // encoded images with this timestamp. The first element will be removed
265     // when the next frame would be encoded and EncodedImageCallback would be
266     // called with the next timestamp.
267 
268     if (timestamp_to_frame_id_list_.empty()) {
269       // Ensure, that we have info about this frame. It can happen that for some
270       // reasons encoder response, that he failed to decode, when we were
271       // posting frame to it, but then call the callback for this frame.
272       RTC_LOG(LS_ERROR) << "QualityAnalyzingVideoEncoder::OnEncodedImage: No "
273                            "frame id for encoded_image.Timestamp()="
274                         << encoded_image.Timestamp();
275       return EncodedImageCallback::Result(
276           EncodedImageCallback::Result::Error::OK);
277     }
278     frame_id = timestamp_frame_id.second;
279 
280     discard = ShouldDiscard(frame_id, encoded_image);
281     if (!discard) {
282       target_encode_bitrate = bitrate_allocation_.GetSpatialLayerSum(
283           encoded_image.SpatialIndex().value_or(0));
284     }
285     codec_name =
286         std::string(CodecTypeToPayloadString(codec_settings_.codecType)) + "_" +
287         delegate_->GetEncoderInfo().implementation_name;
288   }
289 
290   VideoQualityAnalyzerInterface::EncoderStats stats;
291   stats.encoder_name = codec_name;
292   stats.target_encode_bitrate = target_encode_bitrate;
293   stats.qp = encoded_image.qp_;
294   analyzer_->OnFrameEncoded(peer_name_, frame_id, encoded_image, stats,
295                             discard);
296 
297   // Image data injector injects frame id and discard flag into provided
298   // EncodedImage and returns the image with a) modified original buffer (in
299   // such case the current owner of the buffer will be responsible for deleting
300   // it) or b) a new buffer (in such case injector will be responsible for
301   // deleting it).
302   const EncodedImage& image =
303       injector_->InjectData(frame_id, discard, encoded_image);
304   {
305     MutexLock lock(&mutex_);
306     RTC_DCHECK(delegate_callback_);
307     return delegate_callback_->OnEncodedImage(image, codec_specific_info);
308   }
309 }
310 
OnDroppedFrame(EncodedImageCallback::DropReason reason)311 void QualityAnalyzingVideoEncoder::OnDroppedFrame(
312     EncodedImageCallback::DropReason reason) {
313   MutexLock lock(&mutex_);
314   analyzer_->OnFrameDropped(peer_name_, reason);
315   RTC_DCHECK(delegate_callback_);
316   delegate_callback_->OnDroppedFrame(reason);
317 }
318 
ShouldDiscard(uint16_t frame_id,const EncodedImage & encoded_image)319 bool QualityAnalyzingVideoEncoder::ShouldDiscard(
320     uint16_t frame_id,
321     const EncodedImage& encoded_image) {
322   std::string stream_label = analyzer_->GetStreamLabel(frame_id);
323   EmulatedSFUConfigMap::mapped_type emulated_sfu_config =
324       stream_to_sfu_config_[stream_label];
325 
326   if (!emulated_sfu_config)
327     return false;
328 
329   int cur_spatial_index = encoded_image.SpatialIndex().value_or(0);
330   int cur_temporal_index = encoded_image.TemporalIndex().value_or(0);
331 
332   if (emulated_sfu_config->target_temporal_index &&
333       cur_temporal_index > *emulated_sfu_config->target_temporal_index)
334     return true;
335 
336   if (emulated_sfu_config->target_layer_index) {
337     switch (mode_) {
338       case SimulcastMode::kSimulcast:
339         // In simulcast mode only encoded images with required spatial index are
340         // interested, so all others have to be discarded.
341         return cur_spatial_index != *emulated_sfu_config->target_layer_index;
342       case SimulcastMode::kSVC:
343         // In SVC mode encoded images with spatial indexes that are equal or
344         // less than required one are interesting, so all above have to be
345         // discarded.
346         return cur_spatial_index > *emulated_sfu_config->target_layer_index;
347       case SimulcastMode::kKSVC:
348         // In KSVC mode for key frame encoded images with spatial indexes that
349         // are equal or less than required one are interesting, so all above
350         // have to be discarded. For other frames only required spatial index
351         // is interesting, so all others except the ones depending on the
352         // keyframes can be discarded. There's no good test for that, so we keep
353         // all of temporal layer 0 for now.
354         if (encoded_image._frameType == VideoFrameType::kVideoFrameKey ||
355             cur_temporal_index == 0)
356           return cur_spatial_index > *emulated_sfu_config->target_layer_index;
357         return cur_spatial_index != *emulated_sfu_config->target_layer_index;
358       case SimulcastMode::kNormal:
359         RTC_DCHECK_NOTREACHED() << "Analyzing encoder is in kNormal mode, but "
360                                    "target_layer_index is set";
361     }
362   }
363   return false;
364 }
365 
QualityAnalyzingVideoEncoderFactory(absl::string_view peer_name,std::unique_ptr<VideoEncoderFactory> delegate,double bitrate_multiplier,EmulatedSFUConfigMap stream_to_sfu_config,EncodedImageDataInjector * injector,VideoQualityAnalyzerInterface * analyzer)366 QualityAnalyzingVideoEncoderFactory::QualityAnalyzingVideoEncoderFactory(
367     absl::string_view peer_name,
368     std::unique_ptr<VideoEncoderFactory> delegate,
369     double bitrate_multiplier,
370     EmulatedSFUConfigMap stream_to_sfu_config,
371     EncodedImageDataInjector* injector,
372     VideoQualityAnalyzerInterface* analyzer)
373     : peer_name_(peer_name),
374       delegate_(std::move(delegate)),
375       bitrate_multiplier_(bitrate_multiplier),
376       stream_to_sfu_config_(std::move(stream_to_sfu_config)),
377       injector_(injector),
378       analyzer_(analyzer) {}
379 QualityAnalyzingVideoEncoderFactory::~QualityAnalyzingVideoEncoderFactory() =
380     default;
381 
382 std::vector<SdpVideoFormat>
GetSupportedFormats() const383 QualityAnalyzingVideoEncoderFactory::GetSupportedFormats() const {
384   return delegate_->GetSupportedFormats();
385 }
386 
387 std::unique_ptr<VideoEncoder>
CreateVideoEncoder(const SdpVideoFormat & format)388 QualityAnalyzingVideoEncoderFactory::CreateVideoEncoder(
389     const SdpVideoFormat& format) {
390   return std::make_unique<QualityAnalyzingVideoEncoder>(
391       peer_name_, delegate_->CreateVideoEncoder(format), bitrate_multiplier_,
392       stream_to_sfu_config_, injector_, analyzer_);
393 }
394 
395 }  // namespace webrtc_pc_e2e
396 }  // namespace webrtc
397