blob: 4b5fabb6f552f62ac29e477fb32488b688b84667 [file] [log] [blame]
Erik Språng6a7baa72019-02-26 18:31:00 +01001/*
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
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020011#include "video/frame_encode_metadata_writer.h"
Erik Språng6a7baa72019-02-26 18:31:00 +010012
13#include <algorithm>
14
15#include "modules/include/module_common_types_public.h"
16#include "modules/video_coding/include/video_coding_defines.h"
17#include "rtc_base/logging.h"
18#include "rtc_base/time_utils.h"
19
20namespace webrtc {
21namespace {
22const int kMessagesThrottlingThreshold = 2;
23const int kThrottleRatio = 100000;
24} // namespace
25
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020026FrameEncodeMetadataWriter::TimingFramesLayerInfo::TimingFramesLayerInfo() =
27 default;
28FrameEncodeMetadataWriter::TimingFramesLayerInfo::~TimingFramesLayerInfo() =
29 default;
Erik Språng6a7baa72019-02-26 18:31:00 +010030
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020031FrameEncodeMetadataWriter::FrameEncodeMetadataWriter(
32 EncodedImageCallback* frame_drop_callback)
Erik Språng6a7baa72019-02-26 18:31:00 +010033 : frame_drop_callback_(frame_drop_callback),
34 internal_source_(false),
35 framerate_fps_(0),
36 last_timing_frame_time_ms_(-1),
Erik Språng6a7baa72019-02-26 18:31:00 +010037 reordered_frames_logged_messages_(0),
38 stalled_encoder_logged_messages_(0) {
39 codec_settings_.timing_frame_thresholds = {-1, 0};
40}
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020041FrameEncodeMetadataWriter::~FrameEncodeMetadataWriter() {}
Erik Språng6a7baa72019-02-26 18:31:00 +010042
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020043void FrameEncodeMetadataWriter::OnEncoderInit(const VideoCodec& codec,
44 bool internal_source) {
Erik Språng6a7baa72019-02-26 18:31:00 +010045 rtc::CritScope cs(&lock_);
46 codec_settings_ = codec;
47 internal_source_ = internal_source;
48}
49
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020050void FrameEncodeMetadataWriter::OnSetRates(
Erik Språng6a7baa72019-02-26 18:31:00 +010051 const VideoBitrateAllocation& bitrate_allocation,
52 uint32_t framerate_fps) {
53 rtc::CritScope cs(&lock_);
54 framerate_fps_ = framerate_fps;
55 const size_t num_spatial_layers = NumSpatialLayers();
56 if (timing_frames_info_.size() < num_spatial_layers) {
57 timing_frames_info_.resize(num_spatial_layers);
58 }
59 for (size_t i = 0; i < num_spatial_layers; ++i) {
60 timing_frames_info_[i].target_bitrate_bytes_per_sec =
61 bitrate_allocation.GetSpatialLayerSum(i) / 8;
62 }
63}
64
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020065void FrameEncodeMetadataWriter::OnEncodeStarted(const VideoFrame& frame) {
Erik Språng6a7baa72019-02-26 18:31:00 +010066 rtc::CritScope cs(&lock_);
67 if (internal_source_) {
68 return;
69 }
70
71 const size_t num_spatial_layers = NumSpatialLayers();
72 timing_frames_info_.resize(num_spatial_layers);
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020073 FrameMetadata metadata;
74 metadata.rtp_timestamp = frame.timestamp();
75 metadata.encode_start_time_ms = rtc::TimeMillis();
76 metadata.ntp_time_ms = frame.ntp_time_ms();
77 metadata.timestamp_us = frame.timestamp_us();
78 metadata.rotation = frame.rotation();
79 metadata.color_space = frame.color_space();
Erik Språng6a7baa72019-02-26 18:31:00 +010080 for (size_t si = 0; si < num_spatial_layers; ++si) {
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020081 RTC_DCHECK(timing_frames_info_[si].frames.empty() ||
82 rtc::TimeDiff(
83 frame.render_time_ms(),
84 timing_frames_info_[si].frames.back().timestamp_us / 1000) >=
85 0);
Erik Språng6a7baa72019-02-26 18:31:00 +010086 // If stream is disabled due to low bandwidth OnEncodeStarted still will be
87 // called and have to be ignored.
88 if (timing_frames_info_[si].target_bitrate_bytes_per_sec == 0)
89 return;
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +020090 if (timing_frames_info_[si].frames.size() == kMaxEncodeStartTimeListSize) {
Erik Språng6a7baa72019-02-26 18:31:00 +010091 ++stalled_encoder_logged_messages_;
92 if (stalled_encoder_logged_messages_ <= kMessagesThrottlingThreshold ||
93 stalled_encoder_logged_messages_ % kThrottleRatio == 0) {
94 RTC_LOG(LS_WARNING) << "Too many frames in the encode_start_list."
95 " Did encoder stall?";
96 if (stalled_encoder_logged_messages_ == kMessagesThrottlingThreshold) {
97 RTC_LOG(LS_WARNING)
98 << "Too many log messages. Further stalled encoder"
99 "warnings will be throttled.";
100 }
101 }
102 frame_drop_callback_->OnDroppedFrame(
103 EncodedImageCallback::DropReason::kDroppedByEncoder);
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200104 timing_frames_info_[si].frames.pop_front();
Erik Språng6a7baa72019-02-26 18:31:00 +0100105 }
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200106 timing_frames_info_[si].frames.emplace_back(metadata);
Erik Språng6a7baa72019-02-26 18:31:00 +0100107 }
108}
109
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200110void FrameEncodeMetadataWriter::FillTimingInfo(size_t simulcast_svc_idx,
111 EncodedImage* encoded_image) {
Erik Språng6a7baa72019-02-26 18:31:00 +0100112 rtc::CritScope cs(&lock_);
113 absl::optional<size_t> outlier_frame_size;
114 absl::optional<int64_t> encode_start_ms;
115 uint8_t timing_flags = VideoSendTiming::kNotTriggered;
116
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200117 int64_t encode_done_ms = rtc::TimeMillis();
118
Erik Språng6a7baa72019-02-26 18:31:00 +0100119 // Encoders with internal sources do not call OnEncodeStarted
120 // |timing_frames_info_| may be not filled here.
121 if (!internal_source_) {
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200122 encode_start_ms =
123 ExtractEncodeStartTimeAndFillMetadata(simulcast_svc_idx, encoded_image);
Erik Språng6a7baa72019-02-26 18:31:00 +0100124 }
125
126 if (timing_frames_info_.size() > simulcast_svc_idx) {
127 size_t target_bitrate =
128 timing_frames_info_[simulcast_svc_idx].target_bitrate_bytes_per_sec;
129 if (framerate_fps_ > 0 && target_bitrate > 0) {
130 // framerate and target bitrate were reported by encoder.
131 size_t average_frame_size = target_bitrate / framerate_fps_;
132 outlier_frame_size.emplace(
133 average_frame_size *
134 codec_settings_.timing_frame_thresholds.outlier_ratio_percent / 100);
135 }
136 }
137
138 // Outliers trigger timing frames, but do not affect scheduled timing
139 // frames.
140 if (outlier_frame_size && encoded_image->size() >= *outlier_frame_size) {
141 timing_flags |= VideoSendTiming::kTriggeredBySize;
142 }
143
144 // Check if it's time to send a timing frame.
145 int64_t timing_frame_delay_ms =
146 encoded_image->capture_time_ms_ - last_timing_frame_time_ms_;
147 // Trigger threshold if it's a first frame, too long passed since the last
148 // timing frame, or we already sent timing frame on a different simulcast
149 // stream with the same capture time.
150 if (last_timing_frame_time_ms_ == -1 ||
151 timing_frame_delay_ms >=
152 codec_settings_.timing_frame_thresholds.delay_ms ||
153 timing_frame_delay_ms == 0) {
154 timing_flags |= VideoSendTiming::kTriggeredByTimer;
155 last_timing_frame_time_ms_ = encoded_image->capture_time_ms_;
156 }
157
158 // Workaround for chromoting encoder: it passes encode start and finished
159 // timestamps in |timing_| field, but they (together with capture timestamp)
160 // are not in the WebRTC clock.
161 if (internal_source_ && encoded_image->timing_.encode_finish_ms > 0 &&
162 encoded_image->timing_.encode_start_ms > 0) {
163 int64_t clock_offset_ms =
164 encode_done_ms - encoded_image->timing_.encode_finish_ms;
165 // Translate capture timestamp to local WebRTC clock.
166 encoded_image->capture_time_ms_ += clock_offset_ms;
167 encoded_image->SetTimestamp(
168 static_cast<uint32_t>(encoded_image->capture_time_ms_ * 90));
169 encode_start_ms.emplace(encoded_image->timing_.encode_start_ms +
170 clock_offset_ms);
171 }
172
173 // If encode start is not available that means that encoder uses internal
174 // source. In that case capture timestamp may be from a different clock with a
175 // drift relative to rtc::TimeMillis(). We can't use it for Timing frames,
176 // because to being sent in the network capture time required to be less than
177 // all the other timestamps.
178 if (encode_start_ms) {
179 encoded_image->SetEncodeTime(*encode_start_ms, encode_done_ms);
180 encoded_image->timing_.flags = timing_flags;
181 } else {
182 encoded_image->timing_.flags = VideoSendTiming::kInvalid;
183 }
184}
185
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200186void FrameEncodeMetadataWriter::Reset() {
Erik Språng6a7baa72019-02-26 18:31:00 +0100187 rtc::CritScope cs(&lock_);
188 timing_frames_info_.clear();
189 last_timing_frame_time_ms_ = -1;
190 reordered_frames_logged_messages_ = 0;
191 stalled_encoder_logged_messages_ = 0;
192}
193
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200194absl::optional<int64_t>
195FrameEncodeMetadataWriter::ExtractEncodeStartTimeAndFillMetadata(
Erik Språng6a7baa72019-02-26 18:31:00 +0100196 size_t simulcast_svc_idx,
197 EncodedImage* encoded_image) {
198 absl::optional<int64_t> result;
199 size_t num_simulcast_svc_streams = timing_frames_info_.size();
200 if (simulcast_svc_idx < num_simulcast_svc_streams) {
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200201 auto metadata_list = &timing_frames_info_[simulcast_svc_idx].frames;
Erik Språng6a7baa72019-02-26 18:31:00 +0100202 // Skip frames for which there was OnEncodeStarted but no OnEncodedImage
203 // call. These are dropped by encoder internally.
204 // Because some hardware encoders don't preserve capture timestamp we
205 // use RTP timestamps here.
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200206 while (!metadata_list->empty() &&
Erik Språng6a7baa72019-02-26 18:31:00 +0100207 IsNewerTimestamp(encoded_image->Timestamp(),
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200208 metadata_list->front().rtp_timestamp)) {
Erik Språng6a7baa72019-02-26 18:31:00 +0100209 frame_drop_callback_->OnDroppedFrame(
210 EncodedImageCallback::DropReason::kDroppedByEncoder);
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200211 metadata_list->pop_front();
Erik Språng6a7baa72019-02-26 18:31:00 +0100212 }
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200213 if (!metadata_list->empty() &&
214 metadata_list->front().rtp_timestamp == encoded_image->Timestamp()) {
215 result.emplace(metadata_list->front().encode_start_time_ms);
216
217 encoded_image->capture_time_ms_ =
218 metadata_list->front().timestamp_us / 1000;
219 encoded_image->ntp_time_ms_ = metadata_list->front().ntp_time_ms;
220 encoded_image->rotation_ = metadata_list->front().rotation;
221 encoded_image->SetColorSpace(metadata_list->front().color_space);
222 encoded_image->content_type_ =
223 (codec_settings_.mode == VideoCodecMode::kScreensharing)
224 ? VideoContentType::SCREENSHARE
225 : VideoContentType::UNSPECIFIED;
226
227 metadata_list->pop_front();
Erik Språng6a7baa72019-02-26 18:31:00 +0100228 } else {
229 ++reordered_frames_logged_messages_;
230 if (reordered_frames_logged_messages_ <= kMessagesThrottlingThreshold ||
231 reordered_frames_logged_messages_ % kThrottleRatio == 0) {
232 RTC_LOG(LS_WARNING) << "Frame with no encode started time recordings. "
233 "Encoder may be reordering frames "
234 "or not preserving RTP timestamps.";
235 if (reordered_frames_logged_messages_ == kMessagesThrottlingThreshold) {
236 RTC_LOG(LS_WARNING) << "Too many log messages. Further frames "
237 "reordering warnings will be throttled.";
238 }
239 }
240 }
241 }
242 return result;
243}
244
Ilya Nikolaevskiy2ebf5232019-05-13 16:13:36 +0200245size_t FrameEncodeMetadataWriter::NumSpatialLayers() const {
Erik Språng6a7baa72019-02-26 18:31:00 +0100246 size_t num_spatial_layers = codec_settings_.numberOfSimulcastStreams;
247 if (codec_settings_.codecType == kVideoCodecVP9) {
248 num_spatial_layers = std::max(
249 num_spatial_layers,
250 static_cast<size_t>(codec_settings_.VP9().numberOfSpatialLayers));
251 }
252 return std::max(num_spatial_layers, size_t{1});
253}
254
255} // namespace webrtc