blob: 12eb13678ad43ccc0e4f2f796f3a2cadbe168d2f [file] [log] [blame]
ilnik04f4d122017-06-19 07:18:55 -07001/*
Erik Språng6a7baa72019-02-26 18:31:00 +01002 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
ilnik04f4d122017-06-19 07:18:55 -07003 *
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
Yves Gerey3e707812018-11-28 16:47:49 +010011#include <cstddef>
ilnik04f4d122017-06-19 07:18:55 -070012#include <vector>
13
Yves Gerey3e707812018-11-28 16:47:49 +010014#include "api/video/video_timing.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "modules/video_coding/include/video_coding_defines.h"
Steve Anton10542f22019-01-11 09:11:00 -080016#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "test/gtest.h"
Erik Språng6a7baa72019-02-26 18:31:00 +010018#include "video/frame_encode_timer.h"
ilnik04f4d122017-06-19 07:18:55 -070019
20namespace webrtc {
21namespace test {
22namespace {
23inline size_t FrameSize(const size_t& min_frame_size,
24 const size_t& max_frame_size,
25 const int& s,
26 const int& i) {
27 return min_frame_size + (s + 1) * i % (max_frame_size - min_frame_size);
28}
29
30class FakeEncodedImageCallback : public EncodedImageCallback {
31 public:
Erik Språng6a7baa72019-02-26 18:31:00 +010032 FakeEncodedImageCallback() : num_frames_dropped_(0) {}
ilnik04f4d122017-06-19 07:18:55 -070033 Result OnEncodedImage(const EncodedImage& encoded_image,
34 const CodecSpecificInfo* codec_specific_info,
35 const RTPFragmentationHeader* fragmentation) override {
mflodman351424e2017-08-10 02:43:14 -070036 return Result(Result::OK);
Mirko Bonadeic4dd7302019-02-25 09:12:02 +010037 }
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020038 void OnDroppedFrame(DropReason reason) override { ++num_frames_dropped_; }
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020039 size_t GetNumFramesDropped() { return num_frames_dropped_; }
40
ilnik04f4d122017-06-19 07:18:55 -070041 private:
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020042 size_t num_frames_dropped_;
ilnik04f4d122017-06-19 07:18:55 -070043};
44
45enum class FrameType {
46 kNormal,
47 kTiming,
48 kDropped,
49};
50
Erik Språng6a7baa72019-02-26 18:31:00 +010051bool IsTimingFrame(const EncodedImage& image) {
52 return image.timing_.flags != VideoSendTiming::kInvalid &&
53 image.timing_.flags != VideoSendTiming::kNotTriggered;
54}
55
ilnik04f4d122017-06-19 07:18:55 -070056// Emulates |num_frames| on |num_streams| frames with capture timestamps
57// increased by 1 from 0. Size of each frame is between
58// |min_frame_size| and |max_frame_size|, outliers are counted relatevely to
59// |average_frame_sizes[]| for each stream.
60std::vector<std::vector<FrameType>> GetTimingFrames(
61 const int64_t delay_ms,
62 const size_t min_frame_size,
63 const size_t max_frame_size,
64 std::vector<size_t> average_frame_sizes,
65 const int num_streams,
66 const int num_frames) {
67 FakeEncodedImageCallback sink;
Erik Språng6a7baa72019-02-26 18:31:00 +010068 FrameEncodeTimer encode_timer(&sink);
69 VideoCodec codec_settings;
70 codec_settings.numberOfSimulcastStreams = num_streams;
71 codec_settings.timing_frame_thresholds = {delay_ms,
72 kDefaultOutlierFrameSizePercent};
73 encode_timer.OnEncoderInit(codec_settings, false);
ilnik04f4d122017-06-19 07:18:55 -070074 const size_t kFramerate = 30;
Erik Språng6a7baa72019-02-26 18:31:00 +010075 VideoBitrateAllocation bitrate_allocation;
76 for (int si = 0; si < num_streams; ++si) {
77 bitrate_allocation.SetBitrate(si, 0,
78 average_frame_sizes[si] * 8 * kFramerate);
79 }
80 encode_timer.OnSetRates(bitrate_allocation, kFramerate);
81
ilnik04f4d122017-06-19 07:18:55 -070082 std::vector<std::vector<FrameType>> result(num_streams);
ilnik04f4d122017-06-19 07:18:55 -070083 int64_t current_timestamp = 0;
Erik Språng6a7baa72019-02-26 18:31:00 +010084 for (int i = 0; i < num_frames; ++i) {
ilnik04f4d122017-06-19 07:18:55 -070085 current_timestamp += 1;
Erik Språng6a7baa72019-02-26 18:31:00 +010086 encode_timer.OnEncodeStarted(static_cast<uint32_t>(current_timestamp * 90),
87 current_timestamp);
88 for (int si = 0; si < num_streams; ++si) {
ilnik04f4d122017-06-19 07:18:55 -070089 // every (5+s)-th frame is dropped on s-th stream by design.
Erik Språng6a7baa72019-02-26 18:31:00 +010090 bool dropped = i % (5 + si) == 0;
ilnik04f4d122017-06-19 07:18:55 -070091
92 EncodedImage image;
Niels Möller663844d2019-02-14 16:15:54 +010093 image.Allocate(max_frame_size);
Erik Språng6a7baa72019-02-26 18:31:00 +010094 image.set_size(FrameSize(min_frame_size, max_frame_size, si, i));
ilnik04f4d122017-06-19 07:18:55 -070095 image.capture_time_ms_ = current_timestamp;
Niels Möller23775882018-08-16 10:24:12 +020096 image.SetTimestamp(static_cast<uint32_t>(current_timestamp * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +010097 image.SetSpatialIndex(si);
98
ilnik04f4d122017-06-19 07:18:55 -070099 if (dropped) {
Erik Språng6a7baa72019-02-26 18:31:00 +0100100 result[si].push_back(FrameType::kDropped);
ilnik04f4d122017-06-19 07:18:55 -0700101 continue;
102 }
Erik Språng6a7baa72019-02-26 18:31:00 +0100103
104 encode_timer.FillTimingInfo(si, &image, current_timestamp);
105
106 if (IsTimingFrame(image)) {
107 result[si].push_back(FrameType::kTiming);
ilnik04f4d122017-06-19 07:18:55 -0700108 } else {
Erik Språng6a7baa72019-02-26 18:31:00 +0100109 result[si].push_back(FrameType::kNormal);
ilnik04f4d122017-06-19 07:18:55 -0700110 }
111 }
112 }
113 return result;
114}
115} // namespace
116
Erik Språng6a7baa72019-02-26 18:31:00 +0100117TEST(FrameEncodeTimerTest, MarksTimingFramesPeriodicallyTogether) {
ilnik04f4d122017-06-19 07:18:55 -0700118 const int64_t kDelayMs = 29;
119 const size_t kMinFrameSize = 10;
120 const size_t kMaxFrameSize = 20;
121 const int kNumFrames = 1000;
122 const int kNumStreams = 3;
123 // No outliers as 1000 is larger than anything from range [10,20].
124 const std::vector<size_t> kAverageSize = {1000, 1000, 1000};
125 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
126 kAverageSize, kNumStreams, kNumFrames);
127 // Timing frames should be tirggered every delayMs.
128 // As no outliers are expected, frames on all streams have to be
129 // marked together.
130 int last_timing_frame = -1;
131 for (int i = 0; i < kNumFrames; ++i) {
132 int num_normal = 0;
133 int num_timing = 0;
134 int num_dropped = 0;
135 for (int s = 0; s < kNumStreams; ++s) {
136 if (frames[s][i] == FrameType::kTiming) {
137 ++num_timing;
138 } else if (frames[s][i] == FrameType::kNormal) {
139 ++num_normal;
140 } else {
141 ++num_dropped;
142 }
143 }
144 // Can't have both normal and timing frames at the same timstamp.
145 EXPECT_TRUE(num_timing == 0 || num_normal == 0);
146 if (num_dropped < kNumStreams) {
147 if (last_timing_frame == -1 || i >= last_timing_frame + kDelayMs) {
148 // If didn't have timing frames for a period, current sent frame has to
149 // be one. No normal frames should be sent.
150 EXPECT_EQ(num_normal, 0);
151 } else {
152 // No unneeded timing frames should be sent.
153 EXPECT_EQ(num_timing, 0);
154 }
155 }
156 if (num_timing > 0)
157 last_timing_frame = i;
158 }
159}
160
Erik Språng6a7baa72019-02-26 18:31:00 +0100161TEST(FrameEncodeTimerTest, MarksOutliers) {
ilnik04f4d122017-06-19 07:18:55 -0700162 const int64_t kDelayMs = 29;
163 const size_t kMinFrameSize = 2495;
164 const size_t kMaxFrameSize = 2505;
165 const int kNumFrames = 1000;
166 const int kNumStreams = 3;
167 // Possible outliers as 1000 lies in range [995, 1005].
168 const std::vector<size_t> kAverageSize = {998, 1000, 1004};
169 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
170 kAverageSize, kNumStreams, kNumFrames);
171 // All outliers should be marked.
172 for (int i = 0; i < kNumFrames; ++i) {
173 for (int s = 0; s < kNumStreams; ++s) {
174 if (FrameSize(kMinFrameSize, kMaxFrameSize, s, i) >=
175 kAverageSize[s] * kDefaultOutlierFrameSizePercent / 100) {
176 // Too big frame. May be dropped or timing, but not normal.
177 EXPECT_NE(frames[s][i], FrameType::kNormal);
178 }
179 }
180 }
181}
182
Erik Språng6a7baa72019-02-26 18:31:00 +0100183TEST(FrameEncodeTimerTest, NoTimingFrameIfNoEncodeStartTime) {
sprangba050a62017-08-18 02:51:12 -0700184 int64_t timestamp = 1;
Niels Möller663844d2019-02-14 16:15:54 +0100185 constexpr size_t kFrameSize = 500;
Erik Språng6a7baa72019-02-26 18:31:00 +0100186 EncodedImage image;
Niels Möller663844d2019-02-14 16:15:54 +0100187 image.Allocate(kFrameSize);
188 image.set_size(kFrameSize);
sprangba050a62017-08-18 02:51:12 -0700189 image.capture_time_ms_ = timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200190 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100191
sprangba050a62017-08-18 02:51:12 -0700192 FakeEncodedImageCallback sink;
Erik Språng6a7baa72019-02-26 18:31:00 +0100193 FrameEncodeTimer encode_timer(&sink);
194 VideoCodec codec_settings;
195 // Make all frames timing frames.
196 codec_settings.timing_frame_thresholds.delay_ms = 1;
197 encode_timer.OnEncoderInit(codec_settings, false);
198 VideoBitrateAllocation bitrate_allocation;
199 bitrate_allocation.SetBitrate(0, 0, 500000);
200 encode_timer.OnSetRates(bitrate_allocation, 30);
sprangba050a62017-08-18 02:51:12 -0700201
202 // Verify a single frame works with encode start time set.
Erik Språng6a7baa72019-02-26 18:31:00 +0100203 encode_timer.OnEncodeStarted(static_cast<uint32_t>(timestamp * 90),
204 timestamp);
205 encode_timer.FillTimingInfo(0, &image, timestamp);
206 EXPECT_TRUE(IsTimingFrame(image));
sprangba050a62017-08-18 02:51:12 -0700207
208 // New frame, now skip OnEncodeStarted. Should not result in timing frame.
209 image.capture_time_ms_ = ++timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200210 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100211 image.timing_ = EncodedImage::Timing();
212 encode_timer.FillTimingInfo(0, &image, timestamp);
213 EXPECT_FALSE(IsTimingFrame(image));
sprangba050a62017-08-18 02:51:12 -0700214}
215
Erik Språng6a7baa72019-02-26 18:31:00 +0100216TEST(FrameEncodeTimerTest, AdjustsCaptureTimeForInternalSourceEncoder) {
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200217 const int64_t kEncodeStartDelayMs = 2;
218 const int64_t kEncodeFinishDelayMs = 10;
Niels Möller663844d2019-02-14 16:15:54 +0100219 constexpr size_t kFrameSize = 500;
Erik Språng6a7baa72019-02-26 18:31:00 +0100220
221 int64_t timestamp = 1;
222 EncodedImage image;
Niels Möller663844d2019-02-14 16:15:54 +0100223 image.Allocate(kFrameSize);
224 image.set_size(kFrameSize);
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200225 image.capture_time_ms_ = timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200226 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100227
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200228 FakeEncodedImageCallback sink;
Erik Språng6a7baa72019-02-26 18:31:00 +0100229 FrameEncodeTimer encode_timer(&sink);
230
231 VideoCodec codec_settings;
232 // Make all frames timing frames.
233 codec_settings.timing_frame_thresholds.delay_ms = 1;
234 encode_timer.OnEncoderInit(codec_settings, true);
235
236 VideoBitrateAllocation bitrate_allocation;
237 bitrate_allocation.SetBitrate(0, 0, 500000);
238 encode_timer.OnSetRates(bitrate_allocation, 30);
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200239
240 // Verify a single frame without encode timestamps isn't a timing frame.
Erik Språng6a7baa72019-02-26 18:31:00 +0100241 encode_timer.FillTimingInfo(0, &image, timestamp);
242 EXPECT_FALSE(IsTimingFrame(image));
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200243
244 // New frame, but this time with encode timestamps set in timing_.
245 // This should be a timing frame.
246 image.capture_time_ms_ = ++timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200247 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100248 image.timing_ = EncodedImage::Timing();
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200249 image.timing_.encode_start_ms = timestamp + kEncodeStartDelayMs;
250 image.timing_.encode_finish_ms = timestamp + kEncodeFinishDelayMs;
Erik Språng6a7baa72019-02-26 18:31:00 +0100251 const int64_t kEncodeDoneTimestamp = 1234567;
252 encode_timer.FillTimingInfo(0, &image, kEncodeDoneTimestamp);
253 EXPECT_TRUE(IsTimingFrame(image));
254
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200255 // Frame is captured kEncodeFinishDelayMs before it's encoded, so restored
256 // capture timestamp should be kEncodeFinishDelayMs in the past.
Erik Språng6a7baa72019-02-26 18:31:00 +0100257 EXPECT_EQ(image.capture_time_ms_,
258 kEncodeDoneTimestamp - kEncodeFinishDelayMs);
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200259}
260
Erik Språng6a7baa72019-02-26 18:31:00 +0100261TEST(FrameEncodeTimerTest, NotifiesAboutDroppedFrames) {
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100262 const int64_t kTimestampMs1 = 47721840;
263 const int64_t kTimestampMs2 = 47721850;
264 const int64_t kTimestampMs3 = 47721860;
265 const int64_t kTimestampMs4 = 47721870;
Erik Språng6a7baa72019-02-26 18:31:00 +0100266
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200267 FakeEncodedImageCallback sink;
Erik Språng6a7baa72019-02-26 18:31:00 +0100268 FrameEncodeTimer encode_timer(&sink);
269 encode_timer.OnEncoderInit(VideoCodec(), false);
Ilya Nikolaevskiye0da9ea2017-11-08 14:39:02 +0100270 // Any non-zero bitrate needed to be set before the first frame.
Erik Språng6a7baa72019-02-26 18:31:00 +0100271 VideoBitrateAllocation bitrate_allocation;
272 bitrate_allocation.SetBitrate(0, 0, 500000);
273 encode_timer.OnSetRates(bitrate_allocation, 30);
274
275 EncodedImage image;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100276 image.capture_time_ms_ = kTimestampMs1;
Niels Möller23775882018-08-16 10:24:12 +0200277 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100278 encode_timer.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_);
279
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200280 EXPECT_EQ(0u, sink.GetNumFramesDropped());
Erik Språng6a7baa72019-02-26 18:31:00 +0100281 encode_timer.FillTimingInfo(0, &image, kTimestampMs1);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100282
283 image.capture_time_ms_ = kTimestampMs2;
Niels Möller23775882018-08-16 10:24:12 +0200284 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100285 image.timing_ = EncodedImage::Timing();
286 encode_timer.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200287 // No OnEncodedImageCall for timestamp2. Yet, at this moment it's not known
288 // that frame with timestamp2 was dropped.
289 EXPECT_EQ(0u, sink.GetNumFramesDropped());
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100290
291 image.capture_time_ms_ = kTimestampMs3;
Niels Möller23775882018-08-16 10:24:12 +0200292 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100293 image.timing_ = EncodedImage::Timing();
294 encode_timer.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_);
295 encode_timer.FillTimingInfo(0, &image, kTimestampMs3);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200296 EXPECT_EQ(1u, sink.GetNumFramesDropped());
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100297
298 image.capture_time_ms_ = kTimestampMs4;
Niels Möller23775882018-08-16 10:24:12 +0200299 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100300 image.timing_ = EncodedImage::Timing();
301 encode_timer.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_);
302 encode_timer.FillTimingInfo(0, &image, kTimestampMs4);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200303 EXPECT_EQ(1u, sink.GetNumFramesDropped());
304}
305
Erik Språng6a7baa72019-02-26 18:31:00 +0100306TEST(FrameEncodeTimerTest, RestoresCaptureTimestamps) {
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100307 EncodedImage image;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100308 const int64_t kTimestampMs = 123456;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100309 FakeEncodedImageCallback sink;
Erik Språng6a7baa72019-02-26 18:31:00 +0100310
311 FrameEncodeTimer encode_timer(&sink);
312 encode_timer.OnEncoderInit(VideoCodec(), false);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100313 // Any non-zero bitrate needed to be set before the first frame.
Erik Språng6a7baa72019-02-26 18:31:00 +0100314 VideoBitrateAllocation bitrate_allocation;
315 bitrate_allocation.SetBitrate(0, 0, 500000);
316 encode_timer.OnSetRates(bitrate_allocation, 30);
317
318 image.capture_time_ms_ = kTimestampMs; // Correct timestamp.
Niels Möller23775882018-08-16 10:24:12 +0200319 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
Erik Språng6a7baa72019-02-26 18:31:00 +0100320 encode_timer.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_);
321 image.capture_time_ms_ = 0; // Incorrect timestamp.
322 encode_timer.FillTimingInfo(0, &image, kTimestampMs);
323 EXPECT_EQ(kTimestampMs, image.capture_time_ms_);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100324}
325
ilnik04f4d122017-06-19 07:18:55 -0700326} // namespace test
327} // namespace webrtc