blob: 4ea250635544b1d485e101677227412361ceed2b [file] [log] [blame]
ilnik04f4d122017-06-19 07:18:55 -07001/*
2 * Copyright (c) 2017 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
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/generic_encoder.h"
16#include "modules/video_coding/include/video_coding_defines.h"
Steve Anton10542f22019-01-11 09:11:00 -080017#include "rtc_base/fake_clock.h"
18#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "test/gtest.h"
ilnik04f4d122017-06-19 07:18:55 -070020
21namespace webrtc {
22namespace test {
23namespace {
24inline size_t FrameSize(const size_t& min_frame_size,
25 const size_t& max_frame_size,
26 const int& s,
27 const int& i) {
28 return min_frame_size + (s + 1) * i % (max_frame_size - min_frame_size);
29}
30
31class FakeEncodedImageCallback : public EncodedImageCallback {
32 public:
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020033 FakeEncodedImageCallback()
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010034 : last_frame_was_timing_(false),
35 num_frames_dropped_(0),
36 last_capture_timestamp_(-1) {}
ilnik04f4d122017-06-19 07:18:55 -070037 Result OnEncodedImage(const EncodedImage& encoded_image,
38 const CodecSpecificInfo* codec_specific_info,
39 const RTPFragmentationHeader* fragmentation) override {
sprangba050a62017-08-18 02:51:12 -070040 last_frame_was_timing_ =
Ilya Nikolaevskiyb6c462d2018-06-05 15:21:32 +020041 encoded_image.timing_.flags != VideoSendTiming::kInvalid &&
42 encoded_image.timing_.flags != VideoSendTiming::kNotTriggered;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010043 last_capture_timestamp_ = encoded_image.capture_time_ms_;
mflodman351424e2017-08-10 02:43:14 -070044 return Result(Result::OK);
ilnik04f4d122017-06-19 07:18:55 -070045 };
46
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020047 void OnDroppedFrame(DropReason reason) override { ++num_frames_dropped_; }
48
ilnik04f4d122017-06-19 07:18:55 -070049 bool WasTimingFrame() { return last_frame_was_timing_; }
50
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020051 size_t GetNumFramesDropped() { return num_frames_dropped_; }
52
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010053 int64_t GetLastCaptureTimestamp() { return last_capture_timestamp_; }
54
ilnik04f4d122017-06-19 07:18:55 -070055 private:
56 bool last_frame_was_timing_;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020057 size_t num_frames_dropped_;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010058 int64_t last_capture_timestamp_;
ilnik04f4d122017-06-19 07:18:55 -070059};
60
61enum class FrameType {
62 kNormal,
63 kTiming,
64 kDropped,
65};
66
67// Emulates |num_frames| on |num_streams| frames with capture timestamps
68// increased by 1 from 0. Size of each frame is between
69// |min_frame_size| and |max_frame_size|, outliers are counted relatevely to
70// |average_frame_sizes[]| for each stream.
71std::vector<std::vector<FrameType>> GetTimingFrames(
72 const int64_t delay_ms,
73 const size_t min_frame_size,
74 const size_t max_frame_size,
75 std::vector<size_t> average_frame_sizes,
76 const int num_streams,
77 const int num_frames) {
78 FakeEncodedImageCallback sink;
Niels Möller6bb5ab92019-01-11 11:11:10 +010079 VCMEncodedFrameCallback callback(&sink);
ilnik04f4d122017-06-19 07:18:55 -070080 const size_t kFramerate = 30;
81 callback.SetTimingFramesThresholds(
82 {delay_ms, kDefaultOutlierFrameSizePercent});
83 callback.OnFrameRateChanged(kFramerate);
84 int s, i;
Niels Möller77536a22019-01-15 08:50:01 +010085 std::vector<uint8_t> frame_data(max_frame_size);
ilnik04f4d122017-06-19 07:18:55 -070086 std::vector<std::vector<FrameType>> result(num_streams);
87 for (s = 0; s < num_streams; ++s)
88 callback.OnTargetBitrateChanged(average_frame_sizes[s] * kFramerate, s);
89 int64_t current_timestamp = 0;
90 for (i = 0; i < num_frames; ++i) {
91 current_timestamp += 1;
92 for (s = 0; s < num_streams; ++s) {
93 // every (5+s)-th frame is dropped on s-th stream by design.
94 bool dropped = i % (5 + s) == 0;
95
96 EncodedImage image;
97 CodecSpecificInfo codec_specific;
Niels Möller77536a22019-01-15 08:50:01 +010098 image.set_buffer(frame_data.data(), frame_data.size());
99 image.set_size(FrameSize(min_frame_size, max_frame_size, s, i));
ilnik04f4d122017-06-19 07:18:55 -0700100 image.capture_time_ms_ = current_timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200101 image.SetTimestamp(static_cast<uint32_t>(current_timestamp * 90));
Niels Möllerd3b8c632018-08-27 15:33:42 +0200102 image.SetSpatialIndex(s);
ilnik04f4d122017-06-19 07:18:55 -0700103 codec_specific.codecType = kVideoCodecGeneric;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100104 callback.OnEncodeStarted(static_cast<uint32_t>(current_timestamp * 90),
105 current_timestamp, s);
ilnik04f4d122017-06-19 07:18:55 -0700106 if (dropped) {
107 result[s].push_back(FrameType::kDropped);
108 continue;
109 }
110 callback.OnEncodedImage(image, &codec_specific, nullptr);
111 if (sink.WasTimingFrame()) {
112 result[s].push_back(FrameType::kTiming);
113 } else {
114 result[s].push_back(FrameType::kNormal);
115 }
116 }
117 }
118 return result;
119}
120} // namespace
121
122TEST(TestVCMEncodedFrameCallback, MarksTimingFramesPeriodicallyTogether) {
123 const int64_t kDelayMs = 29;
124 const size_t kMinFrameSize = 10;
125 const size_t kMaxFrameSize = 20;
126 const int kNumFrames = 1000;
127 const int kNumStreams = 3;
128 // No outliers as 1000 is larger than anything from range [10,20].
129 const std::vector<size_t> kAverageSize = {1000, 1000, 1000};
130 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
131 kAverageSize, kNumStreams, kNumFrames);
132 // Timing frames should be tirggered every delayMs.
133 // As no outliers are expected, frames on all streams have to be
134 // marked together.
135 int last_timing_frame = -1;
136 for (int i = 0; i < kNumFrames; ++i) {
137 int num_normal = 0;
138 int num_timing = 0;
139 int num_dropped = 0;
140 for (int s = 0; s < kNumStreams; ++s) {
141 if (frames[s][i] == FrameType::kTiming) {
142 ++num_timing;
143 } else if (frames[s][i] == FrameType::kNormal) {
144 ++num_normal;
145 } else {
146 ++num_dropped;
147 }
148 }
149 // Can't have both normal and timing frames at the same timstamp.
150 EXPECT_TRUE(num_timing == 0 || num_normal == 0);
151 if (num_dropped < kNumStreams) {
152 if (last_timing_frame == -1 || i >= last_timing_frame + kDelayMs) {
153 // If didn't have timing frames for a period, current sent frame has to
154 // be one. No normal frames should be sent.
155 EXPECT_EQ(num_normal, 0);
156 } else {
157 // No unneeded timing frames should be sent.
158 EXPECT_EQ(num_timing, 0);
159 }
160 }
161 if (num_timing > 0)
162 last_timing_frame = i;
163 }
164}
165
166TEST(TestVCMEncodedFrameCallback, MarksOutliers) {
167 const int64_t kDelayMs = 29;
168 const size_t kMinFrameSize = 2495;
169 const size_t kMaxFrameSize = 2505;
170 const int kNumFrames = 1000;
171 const int kNumStreams = 3;
172 // Possible outliers as 1000 lies in range [995, 1005].
173 const std::vector<size_t> kAverageSize = {998, 1000, 1004};
174 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
175 kAverageSize, kNumStreams, kNumFrames);
176 // All outliers should be marked.
177 for (int i = 0; i < kNumFrames; ++i) {
178 for (int s = 0; s < kNumStreams; ++s) {
179 if (FrameSize(kMinFrameSize, kMaxFrameSize, s, i) >=
180 kAverageSize[s] * kDefaultOutlierFrameSizePercent / 100) {
181 // Too big frame. May be dropped or timing, but not normal.
182 EXPECT_NE(frames[s][i], FrameType::kNormal);
183 }
184 }
185 }
186}
187
sprangba050a62017-08-18 02:51:12 -0700188TEST(TestVCMEncodedFrameCallback, NoTimingFrameIfNoEncodeStartTime) {
189 EncodedImage image;
190 CodecSpecificInfo codec_specific;
191 int64_t timestamp = 1;
Niels Möller77536a22019-01-15 08:50:01 +0100192 uint8_t frame_data[500];
193 image.set_buffer(frame_data, sizeof(frame_data));
194 image.set_size(sizeof(frame_data));
sprangba050a62017-08-18 02:51:12 -0700195 image.capture_time_ms_ = timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200196 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
sprangba050a62017-08-18 02:51:12 -0700197 codec_specific.codecType = kVideoCodecGeneric;
sprangba050a62017-08-18 02:51:12 -0700198 FakeEncodedImageCallback sink;
Niels Möller6bb5ab92019-01-11 11:11:10 +0100199 VCMEncodedFrameCallback callback(&sink);
sprangba050a62017-08-18 02:51:12 -0700200 VideoCodec::TimingFrameTriggerThresholds thresholds;
201 thresholds.delay_ms = 1; // Make all frames timing frames.
202 callback.SetTimingFramesThresholds(thresholds);
Ilya Nikolaevskiye0da9ea2017-11-08 14:39:02 +0100203 callback.OnTargetBitrateChanged(500, 0);
sprangba050a62017-08-18 02:51:12 -0700204
205 // Verify a single frame works with encode start time set.
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100206 callback.OnEncodeStarted(static_cast<uint32_t>(timestamp * 90), timestamp, 0);
sprangba050a62017-08-18 02:51:12 -0700207 callback.OnEncodedImage(image, &codec_specific, nullptr);
208 EXPECT_TRUE(sink.WasTimingFrame());
209
210 // New frame, now skip OnEncodeStarted. Should not result in timing frame.
211 image.capture_time_ms_ = ++timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200212 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
sprangba050a62017-08-18 02:51:12 -0700213 callback.OnEncodedImage(image, &codec_specific, nullptr);
214 EXPECT_FALSE(sink.WasTimingFrame());
215}
216
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200217TEST(TestVCMEncodedFrameCallback, AdjustsCaptureTimeForInternalSourceEncoder) {
218 rtc::ScopedFakeClock clock;
219 clock.SetTimeMicros(1234567);
220 EncodedImage image;
221 CodecSpecificInfo codec_specific;
222 const int64_t kEncodeStartDelayMs = 2;
223 const int64_t kEncodeFinishDelayMs = 10;
224 int64_t timestamp = 1;
Niels Möller77536a22019-01-15 08:50:01 +0100225 uint8_t frame_data[500];
226 image.set_buffer(frame_data, sizeof(frame_data));
227 image.set_size(sizeof(frame_data));
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200228 image.capture_time_ms_ = timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200229 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200230 codec_specific.codecType = kVideoCodecGeneric;
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200231 FakeEncodedImageCallback sink;
Niels Möller6bb5ab92019-01-11 11:11:10 +0100232 VCMEncodedFrameCallback callback(&sink);
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200233 callback.SetInternalSource(true);
234 VideoCodec::TimingFrameTriggerThresholds thresholds;
235 thresholds.delay_ms = 1; // Make all frames timing frames.
236 callback.SetTimingFramesThresholds(thresholds);
237 callback.OnTargetBitrateChanged(500, 0);
238
239 // Verify a single frame without encode timestamps isn't a timing frame.
240 callback.OnEncodedImage(image, &codec_specific, nullptr);
241 EXPECT_FALSE(sink.WasTimingFrame());
242
243 // New frame, but this time with encode timestamps set in timing_.
244 // This should be a timing frame.
245 image.capture_time_ms_ = ++timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200246 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200247 image.timing_.encode_start_ms = timestamp + kEncodeStartDelayMs;
248 image.timing_.encode_finish_ms = timestamp + kEncodeFinishDelayMs;
249 callback.OnEncodedImage(image, &codec_specific, nullptr);
250 EXPECT_TRUE(sink.WasTimingFrame());
251 // Frame is captured kEncodeFinishDelayMs before it's encoded, so restored
252 // capture timestamp should be kEncodeFinishDelayMs in the past.
253 EXPECT_EQ(
254 sink.GetLastCaptureTimestamp(),
255 clock.TimeNanos() / rtc::kNumNanosecsPerMillisec - kEncodeFinishDelayMs);
256}
257
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200258TEST(TestVCMEncodedFrameCallback, NotifiesAboutDroppedFrames) {
259 EncodedImage image;
260 CodecSpecificInfo codec_specific;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100261 const int64_t kTimestampMs1 = 47721840;
262 const int64_t kTimestampMs2 = 47721850;
263 const int64_t kTimestampMs3 = 47721860;
264 const int64_t kTimestampMs4 = 47721870;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200265 codec_specific.codecType = kVideoCodecGeneric;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200266 FakeEncodedImageCallback sink;
Niels Möller6bb5ab92019-01-11 11:11:10 +0100267 VCMEncodedFrameCallback callback(&sink);
Ilya Nikolaevskiye0da9ea2017-11-08 14:39:02 +0100268 // Any non-zero bitrate needed to be set before the first frame.
269 callback.OnTargetBitrateChanged(500, 0);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100270 image.capture_time_ms_ = kTimestampMs1;
Niels Möller23775882018-08-16 10:24:12 +0200271 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
272 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200273 EXPECT_EQ(0u, sink.GetNumFramesDropped());
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200274 callback.OnEncodedImage(image, &codec_specific, nullptr);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100275
276 image.capture_time_ms_ = kTimestampMs2;
Niels Möller23775882018-08-16 10:24:12 +0200277 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
278 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200279 // No OnEncodedImageCall for timestamp2. Yet, at this moment it's not known
280 // that frame with timestamp2 was dropped.
281 EXPECT_EQ(0u, sink.GetNumFramesDropped());
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100282
283 image.capture_time_ms_ = kTimestampMs3;
Niels Möller23775882018-08-16 10:24:12 +0200284 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
285 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200286 callback.OnEncodedImage(image, &codec_specific, nullptr);
287 EXPECT_EQ(1u, sink.GetNumFramesDropped());
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100288
289 image.capture_time_ms_ = kTimestampMs4;
Niels Möller23775882018-08-16 10:24:12 +0200290 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
291 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200292 callback.OnEncodedImage(image, &codec_specific, nullptr);
293 EXPECT_EQ(1u, sink.GetNumFramesDropped());
294}
295
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100296TEST(TestVCMEncodedFrameCallback, RestoresCaptureTimestamps) {
297 EncodedImage image;
298 CodecSpecificInfo codec_specific;
299 const int64_t kTimestampMs = 123456;
300 codec_specific.codecType = kVideoCodecGeneric;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100301 FakeEncodedImageCallback sink;
Niels Möller6bb5ab92019-01-11 11:11:10 +0100302 VCMEncodedFrameCallback callback(&sink);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100303 // Any non-zero bitrate needed to be set before the first frame.
304 callback.OnTargetBitrateChanged(500, 0);
305 image.capture_time_ms_ = kTimestampMs; // Incorrect timesetamp.
Niels Möller23775882018-08-16 10:24:12 +0200306 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
307 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100308 image.capture_time_ms_ = 0; // Incorrect timesetamp.
309 callback.OnEncodedImage(image, &codec_specific, nullptr);
310 EXPECT_EQ(kTimestampMs, sink.GetLastCaptureTimestamp());
311}
312
ilnik04f4d122017-06-19 07:18:55 -0700313} // namespace test
314} // namespace webrtc