xref: /aosp_15_r20/external/webrtc/video/frame_encode_metadata_writer.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 "video/frame_encode_metadata_writer.h"
12 
13 #include <algorithm>
14 #include <memory>
15 #include <utility>
16 
17 #include "common_video/h264/sps_vui_rewriter.h"
18 #include "modules/include/module_common_types_public.h"
19 #include "modules/video_coding/include/video_coding_defines.h"
20 #include "modules/video_coding/svc/create_scalability_structure.h"
21 #include "rtc_base/logging.h"
22 #include "rtc_base/time_utils.h"
23 
24 namespace webrtc {
25 namespace {
26 const int kMessagesThrottlingThreshold = 2;
27 const int kThrottleRatio = 100000;
28 
29 class EncodedImageBufferWrapper : public EncodedImageBufferInterface {
30  public:
EncodedImageBufferWrapper(rtc::Buffer && buffer)31   explicit EncodedImageBufferWrapper(rtc::Buffer&& buffer)
32       : buffer_(std::move(buffer)) {}
33 
data() const34   const uint8_t* data() const override { return buffer_.data(); }
data()35   uint8_t* data() override { return buffer_.data(); }
size() const36   size_t size() const override { return buffer_.size(); }
37 
38  private:
39   rtc::Buffer buffer_;
40 };
41 
42 }  // namespace
43 
44 FrameEncodeMetadataWriter::TimingFramesLayerInfo::TimingFramesLayerInfo() =
45     default;
46 FrameEncodeMetadataWriter::TimingFramesLayerInfo::~TimingFramesLayerInfo() =
47     default;
48 
FrameEncodeMetadataWriter(EncodedImageCallback * frame_drop_callback)49 FrameEncodeMetadataWriter::FrameEncodeMetadataWriter(
50     EncodedImageCallback* frame_drop_callback)
51     : frame_drop_callback_(frame_drop_callback),
52       framerate_fps_(0),
53       last_timing_frame_time_ms_(-1),
54       reordered_frames_logged_messages_(0),
55       stalled_encoder_logged_messages_(0) {
56   codec_settings_.timing_frame_thresholds = {-1, 0};
57 }
~FrameEncodeMetadataWriter()58 FrameEncodeMetadataWriter::~FrameEncodeMetadataWriter() {}
59 
OnEncoderInit(const VideoCodec & codec)60 void FrameEncodeMetadataWriter::OnEncoderInit(const VideoCodec& codec) {
61   MutexLock lock(&lock_);
62   codec_settings_ = codec;
63   size_t num_spatial_layers = codec_settings_.numberOfSimulcastStreams;
64   if (codec_settings_.codecType == kVideoCodecVP9) {
65     num_spatial_layers = std::max(
66         num_spatial_layers,
67         static_cast<size_t>(codec_settings_.VP9()->numberOfSpatialLayers));
68   } else if (codec_settings_.codecType == kVideoCodecAV1 &&
69              codec_settings_.GetScalabilityMode().has_value()) {
70     std::unique_ptr<ScalableVideoController> structure =
71         CreateScalabilityStructure(*codec_settings_.GetScalabilityMode());
72     if (structure) {
73       num_spatial_layers = structure->StreamConfig().num_spatial_layers;
74     } else {
75       // |structure| maybe nullptr if the scalability mode is invalid.
76       RTC_LOG(LS_WARNING) << "Cannot create ScalabilityStructure, since the "
77                              "scalability mode is invalid";
78     }
79   }
80   num_spatial_layers_ = std::max(num_spatial_layers, size_t{1});
81 }
82 
OnSetRates(const VideoBitrateAllocation & bitrate_allocation,uint32_t framerate_fps)83 void FrameEncodeMetadataWriter::OnSetRates(
84     const VideoBitrateAllocation& bitrate_allocation,
85     uint32_t framerate_fps) {
86   MutexLock lock(&lock_);
87   framerate_fps_ = framerate_fps;
88   if (timing_frames_info_.size() < num_spatial_layers_) {
89     timing_frames_info_.resize(num_spatial_layers_);
90   }
91   for (size_t i = 0; i < num_spatial_layers_; ++i) {
92     timing_frames_info_[i].target_bitrate_bytes_per_sec =
93         bitrate_allocation.GetSpatialLayerSum(i) / 8;
94   }
95 }
96 
OnEncodeStarted(const VideoFrame & frame)97 void FrameEncodeMetadataWriter::OnEncodeStarted(const VideoFrame& frame) {
98   MutexLock lock(&lock_);
99 
100   timing_frames_info_.resize(num_spatial_layers_);
101   FrameMetadata metadata;
102   metadata.rtp_timestamp = frame.timestamp();
103   metadata.encode_start_time_ms = rtc::TimeMillis();
104   metadata.ntp_time_ms = frame.ntp_time_ms();
105   metadata.timestamp_us = frame.timestamp_us();
106   metadata.rotation = frame.rotation();
107   metadata.color_space = frame.color_space();
108   metadata.packet_infos = frame.packet_infos();
109   for (size_t si = 0; si < num_spatial_layers_; ++si) {
110     RTC_DCHECK(timing_frames_info_[si].frames.empty() ||
111                rtc::TimeDiff(
112                    frame.render_time_ms(),
113                    timing_frames_info_[si].frames.back().timestamp_us / 1000) >=
114                    0);
115     // If stream is disabled due to low bandwidth OnEncodeStarted still will be
116     // called and have to be ignored.
117     if (timing_frames_info_[si].target_bitrate_bytes_per_sec == 0)
118       continue;
119     if (timing_frames_info_[si].frames.size() == kMaxEncodeStartTimeListSize) {
120       ++stalled_encoder_logged_messages_;
121       if (stalled_encoder_logged_messages_ <= kMessagesThrottlingThreshold ||
122           stalled_encoder_logged_messages_ % kThrottleRatio == 0) {
123         RTC_LOG(LS_WARNING) << "Too many frames in the encode_start_list."
124                                " Did encoder stall?";
125         if (stalled_encoder_logged_messages_ == kMessagesThrottlingThreshold) {
126           RTC_LOG(LS_WARNING)
127               << "Too many log messages. Further stalled encoder"
128                  "warnings will be throttled.";
129         }
130       }
131       frame_drop_callback_->OnDroppedFrame(
132           EncodedImageCallback::DropReason::kDroppedByEncoder);
133       timing_frames_info_[si].frames.pop_front();
134     }
135     timing_frames_info_[si].frames.emplace_back(metadata);
136   }
137 }
138 
FillTimingInfo(size_t simulcast_svc_idx,EncodedImage * encoded_image)139 void FrameEncodeMetadataWriter::FillTimingInfo(size_t simulcast_svc_idx,
140                                                EncodedImage* encoded_image) {
141   MutexLock lock(&lock_);
142   absl::optional<size_t> outlier_frame_size;
143   absl::optional<int64_t> encode_start_ms;
144   uint8_t timing_flags = VideoSendTiming::kNotTriggered;
145 
146   int64_t encode_done_ms = rtc::TimeMillis();
147 
148   encode_start_ms =
149       ExtractEncodeStartTimeAndFillMetadata(simulcast_svc_idx, encoded_image);
150 
151   if (timing_frames_info_.size() > simulcast_svc_idx) {
152     size_t target_bitrate =
153         timing_frames_info_[simulcast_svc_idx].target_bitrate_bytes_per_sec;
154     if (framerate_fps_ > 0 && target_bitrate > 0) {
155       // framerate and target bitrate were reported by encoder.
156       size_t average_frame_size = target_bitrate / framerate_fps_;
157       outlier_frame_size.emplace(
158           average_frame_size *
159           codec_settings_.timing_frame_thresholds.outlier_ratio_percent / 100);
160     }
161   }
162 
163   // Outliers trigger timing frames, but do not affect scheduled timing
164   // frames.
165   if (outlier_frame_size && encoded_image->size() >= *outlier_frame_size) {
166     timing_flags |= VideoSendTiming::kTriggeredBySize;
167   }
168 
169   // Check if it's time to send a timing frame.
170   int64_t timing_frame_delay_ms =
171       encoded_image->capture_time_ms_ - last_timing_frame_time_ms_;
172   // Trigger threshold if it's a first frame, too long passed since the last
173   // timing frame, or we already sent timing frame on a different simulcast
174   // stream with the same capture time.
175   if (last_timing_frame_time_ms_ == -1 ||
176       timing_frame_delay_ms >=
177           codec_settings_.timing_frame_thresholds.delay_ms ||
178       timing_frame_delay_ms == 0) {
179     timing_flags |= VideoSendTiming::kTriggeredByTimer;
180     last_timing_frame_time_ms_ = encoded_image->capture_time_ms_;
181   }
182 
183   // If encode start is not available that means that encoder uses internal
184   // source. In that case capture timestamp may be from a different clock with a
185   // drift relative to rtc::TimeMillis(). We can't use it for Timing frames,
186   // because to being sent in the network capture time required to be less than
187   // all the other timestamps.
188   if (encode_start_ms) {
189     encoded_image->SetEncodeTime(*encode_start_ms, encode_done_ms);
190     encoded_image->timing_.flags = timing_flags;
191   } else {
192     encoded_image->timing_.flags = VideoSendTiming::kInvalid;
193   }
194 }
195 
UpdateBitstream(const CodecSpecificInfo * codec_specific_info,EncodedImage * encoded_image)196 void FrameEncodeMetadataWriter::UpdateBitstream(
197     const CodecSpecificInfo* codec_specific_info,
198     EncodedImage* encoded_image) {
199   if (!codec_specific_info ||
200       codec_specific_info->codecType != kVideoCodecH264 ||
201       encoded_image->_frameType != VideoFrameType::kVideoFrameKey) {
202     return;
203   }
204 
205   // Make sure that the data is not copied if owned by EncodedImage.
206   const EncodedImage& buffer = *encoded_image;
207   rtc::Buffer modified_buffer =
208       SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite(
209           buffer, encoded_image->ColorSpace());
210 
211   encoded_image->SetEncodedData(
212       rtc::make_ref_counted<EncodedImageBufferWrapper>(
213           std::move(modified_buffer)));
214 }
215 
Reset()216 void FrameEncodeMetadataWriter::Reset() {
217   MutexLock lock(&lock_);
218   for (auto& info : timing_frames_info_) {
219     info.frames.clear();
220   }
221   last_timing_frame_time_ms_ = -1;
222   reordered_frames_logged_messages_ = 0;
223   stalled_encoder_logged_messages_ = 0;
224 }
225 
226 absl::optional<int64_t>
ExtractEncodeStartTimeAndFillMetadata(size_t simulcast_svc_idx,EncodedImage * encoded_image)227 FrameEncodeMetadataWriter::ExtractEncodeStartTimeAndFillMetadata(
228     size_t simulcast_svc_idx,
229     EncodedImage* encoded_image) {
230   absl::optional<int64_t> result;
231   size_t num_simulcast_svc_streams = timing_frames_info_.size();
232   if (simulcast_svc_idx < num_simulcast_svc_streams) {
233     auto metadata_list = &timing_frames_info_[simulcast_svc_idx].frames;
234     // Skip frames for which there was OnEncodeStarted but no OnEncodedImage
235     // call. These are dropped by encoder internally.
236     // Because some hardware encoders don't preserve capture timestamp we
237     // use RTP timestamps here.
238     while (!metadata_list->empty() &&
239            IsNewerTimestamp(encoded_image->Timestamp(),
240                             metadata_list->front().rtp_timestamp)) {
241       frame_drop_callback_->OnDroppedFrame(
242           EncodedImageCallback::DropReason::kDroppedByEncoder);
243       metadata_list->pop_front();
244     }
245 
246     encoded_image->content_type_ =
247         (codec_settings_.mode == VideoCodecMode::kScreensharing)
248             ? VideoContentType::SCREENSHARE
249             : VideoContentType::UNSPECIFIED;
250 
251     if (!metadata_list->empty() &&
252         metadata_list->front().rtp_timestamp == encoded_image->Timestamp()) {
253       result.emplace(metadata_list->front().encode_start_time_ms);
254       encoded_image->capture_time_ms_ =
255           metadata_list->front().timestamp_us / 1000;
256       encoded_image->ntp_time_ms_ = metadata_list->front().ntp_time_ms;
257       encoded_image->rotation_ = metadata_list->front().rotation;
258       encoded_image->SetColorSpace(metadata_list->front().color_space);
259       encoded_image->SetPacketInfos(metadata_list->front().packet_infos);
260       metadata_list->pop_front();
261     } else {
262       ++reordered_frames_logged_messages_;
263       if (reordered_frames_logged_messages_ <= kMessagesThrottlingThreshold ||
264           reordered_frames_logged_messages_ % kThrottleRatio == 0) {
265         RTC_LOG(LS_WARNING) << "Frame with no encode started time recordings. "
266                                "Encoder may be reordering frames "
267                                "or not preserving RTP timestamps.";
268         if (reordered_frames_logged_messages_ == kMessagesThrottlingThreshold) {
269           RTC_LOG(LS_WARNING) << "Too many log messages. Further frames "
270                                  "reordering warnings will be throttled.";
271         }
272       }
273     }
274   }
275   return result;
276 }
277 
278 }  // namespace webrtc
279