blob: 2be6856c406395577a7b49c8c313eb8cfcfe3d48 [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
11#include <vector>
12
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "modules/video_coding/encoded_frame.h"
14#include "modules/video_coding/generic_encoder.h"
15#include "modules/video_coding/include/video_coding_defines.h"
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +020016#include "rtc_base/fakeclock.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "test/gtest.h"
ilnik04f4d122017-06-19 07:18:55 -070018
19namespace webrtc {
20namespace test {
21namespace {
22inline size_t FrameSize(const size_t& min_frame_size,
23 const size_t& max_frame_size,
24 const int& s,
25 const int& i) {
26 return min_frame_size + (s + 1) * i % (max_frame_size - min_frame_size);
27}
28
29class FakeEncodedImageCallback : public EncodedImageCallback {
30 public:
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020031 FakeEncodedImageCallback()
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010032 : last_frame_was_timing_(false),
33 num_frames_dropped_(0),
34 last_capture_timestamp_(-1) {}
ilnik04f4d122017-06-19 07:18:55 -070035 Result OnEncodedImage(const EncodedImage& encoded_image,
36 const CodecSpecificInfo* codec_specific_info,
37 const RTPFragmentationHeader* fragmentation) override {
sprangba050a62017-08-18 02:51:12 -070038 last_frame_was_timing_ =
Ilya Nikolaevskiyb6c462d2018-06-05 15:21:32 +020039 encoded_image.timing_.flags != VideoSendTiming::kInvalid &&
40 encoded_image.timing_.flags != VideoSendTiming::kNotTriggered;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010041 last_capture_timestamp_ = encoded_image.capture_time_ms_;
mflodman351424e2017-08-10 02:43:14 -070042 return Result(Result::OK);
ilnik04f4d122017-06-19 07:18:55 -070043 };
44
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020045 void OnDroppedFrame(DropReason reason) override { ++num_frames_dropped_; }
46
ilnik04f4d122017-06-19 07:18:55 -070047 bool WasTimingFrame() { return last_frame_was_timing_; }
48
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020049 size_t GetNumFramesDropped() { return num_frames_dropped_; }
50
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010051 int64_t GetLastCaptureTimestamp() { return last_capture_timestamp_; }
52
ilnik04f4d122017-06-19 07:18:55 -070053 private:
54 bool last_frame_was_timing_;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020055 size_t num_frames_dropped_;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010056 int64_t last_capture_timestamp_;
ilnik04f4d122017-06-19 07:18:55 -070057};
58
59enum class FrameType {
60 kNormal,
61 kTiming,
62 kDropped,
63};
64
65// Emulates |num_frames| on |num_streams| frames with capture timestamps
66// increased by 1 from 0. Size of each frame is between
67// |min_frame_size| and |max_frame_size|, outliers are counted relatevely to
68// |average_frame_sizes[]| for each stream.
69std::vector<std::vector<FrameType>> GetTimingFrames(
70 const int64_t delay_ms,
71 const size_t min_frame_size,
72 const size_t max_frame_size,
73 std::vector<size_t> average_frame_sizes,
74 const int num_streams,
75 const int num_frames) {
76 FakeEncodedImageCallback sink;
77 VCMEncodedFrameCallback callback(&sink, nullptr);
78 const size_t kFramerate = 30;
79 callback.SetTimingFramesThresholds(
80 {delay_ms, kDefaultOutlierFrameSizePercent});
81 callback.OnFrameRateChanged(kFramerate);
82 int s, i;
83 std::vector<std::vector<FrameType>> result(num_streams);
84 for (s = 0; s < num_streams; ++s)
85 callback.OnTargetBitrateChanged(average_frame_sizes[s] * kFramerate, s);
86 int64_t current_timestamp = 0;
87 for (i = 0; i < num_frames; ++i) {
88 current_timestamp += 1;
89 for (s = 0; s < num_streams; ++s) {
90 // every (5+s)-th frame is dropped on s-th stream by design.
91 bool dropped = i % (5 + s) == 0;
92
93 EncodedImage image;
94 CodecSpecificInfo codec_specific;
95 image._length = FrameSize(min_frame_size, max_frame_size, s, i);
96 image.capture_time_ms_ = current_timestamp;
Niels Möller23775882018-08-16 10:24:12 +020097 image.SetTimestamp(static_cast<uint32_t>(current_timestamp * 90));
Niels Möllerda0898d2018-08-27 15:33:42 +020098 image.SetSpatialIndex(s);
ilnik04f4d122017-06-19 07:18:55 -070099 codec_specific.codecType = kVideoCodecGeneric;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100100 callback.OnEncodeStarted(static_cast<uint32_t>(current_timestamp * 90),
101 current_timestamp, s);
ilnik04f4d122017-06-19 07:18:55 -0700102 if (dropped) {
103 result[s].push_back(FrameType::kDropped);
104 continue;
105 }
106 callback.OnEncodedImage(image, &codec_specific, nullptr);
107 if (sink.WasTimingFrame()) {
108 result[s].push_back(FrameType::kTiming);
109 } else {
110 result[s].push_back(FrameType::kNormal);
111 }
112 }
113 }
114 return result;
115}
116} // namespace
117
118TEST(TestVCMEncodedFrameCallback, MarksTimingFramesPeriodicallyTogether) {
119 const int64_t kDelayMs = 29;
120 const size_t kMinFrameSize = 10;
121 const size_t kMaxFrameSize = 20;
122 const int kNumFrames = 1000;
123 const int kNumStreams = 3;
124 // No outliers as 1000 is larger than anything from range [10,20].
125 const std::vector<size_t> kAverageSize = {1000, 1000, 1000};
126 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
127 kAverageSize, kNumStreams, kNumFrames);
128 // Timing frames should be tirggered every delayMs.
129 // As no outliers are expected, frames on all streams have to be
130 // marked together.
131 int last_timing_frame = -1;
132 for (int i = 0; i < kNumFrames; ++i) {
133 int num_normal = 0;
134 int num_timing = 0;
135 int num_dropped = 0;
136 for (int s = 0; s < kNumStreams; ++s) {
137 if (frames[s][i] == FrameType::kTiming) {
138 ++num_timing;
139 } else if (frames[s][i] == FrameType::kNormal) {
140 ++num_normal;
141 } else {
142 ++num_dropped;
143 }
144 }
145 // Can't have both normal and timing frames at the same timstamp.
146 EXPECT_TRUE(num_timing == 0 || num_normal == 0);
147 if (num_dropped < kNumStreams) {
148 if (last_timing_frame == -1 || i >= last_timing_frame + kDelayMs) {
149 // If didn't have timing frames for a period, current sent frame has to
150 // be one. No normal frames should be sent.
151 EXPECT_EQ(num_normal, 0);
152 } else {
153 // No unneeded timing frames should be sent.
154 EXPECT_EQ(num_timing, 0);
155 }
156 }
157 if (num_timing > 0)
158 last_timing_frame = i;
159 }
160}
161
162TEST(TestVCMEncodedFrameCallback, MarksOutliers) {
163 const int64_t kDelayMs = 29;
164 const size_t kMinFrameSize = 2495;
165 const size_t kMaxFrameSize = 2505;
166 const int kNumFrames = 1000;
167 const int kNumStreams = 3;
168 // Possible outliers as 1000 lies in range [995, 1005].
169 const std::vector<size_t> kAverageSize = {998, 1000, 1004};
170 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
171 kAverageSize, kNumStreams, kNumFrames);
172 // All outliers should be marked.
173 for (int i = 0; i < kNumFrames; ++i) {
174 for (int s = 0; s < kNumStreams; ++s) {
175 if (FrameSize(kMinFrameSize, kMaxFrameSize, s, i) >=
176 kAverageSize[s] * kDefaultOutlierFrameSizePercent / 100) {
177 // Too big frame. May be dropped or timing, but not normal.
178 EXPECT_NE(frames[s][i], FrameType::kNormal);
179 }
180 }
181 }
182}
183
sprangba050a62017-08-18 02:51:12 -0700184TEST(TestVCMEncodedFrameCallback, NoTimingFrameIfNoEncodeStartTime) {
185 EncodedImage image;
186 CodecSpecificInfo codec_specific;
187 int64_t timestamp = 1;
188 image._length = 500;
189 image.capture_time_ms_ = timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200190 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
sprangba050a62017-08-18 02:51:12 -0700191 codec_specific.codecType = kVideoCodecGeneric;
sprangba050a62017-08-18 02:51:12 -0700192 FakeEncodedImageCallback sink;
193 VCMEncodedFrameCallback callback(&sink, nullptr);
194 VideoCodec::TimingFrameTriggerThresholds thresholds;
195 thresholds.delay_ms = 1; // Make all frames timing frames.
196 callback.SetTimingFramesThresholds(thresholds);
Ilya Nikolaevskiye0da9ea2017-11-08 14:39:02 +0100197 callback.OnTargetBitrateChanged(500, 0);
sprangba050a62017-08-18 02:51:12 -0700198
199 // Verify a single frame works with encode start time set.
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100200 callback.OnEncodeStarted(static_cast<uint32_t>(timestamp * 90), timestamp, 0);
sprangba050a62017-08-18 02:51:12 -0700201 callback.OnEncodedImage(image, &codec_specific, nullptr);
202 EXPECT_TRUE(sink.WasTimingFrame());
203
204 // New frame, now skip OnEncodeStarted. Should not result in timing frame.
205 image.capture_time_ms_ = ++timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200206 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
sprangba050a62017-08-18 02:51:12 -0700207 callback.OnEncodedImage(image, &codec_specific, nullptr);
208 EXPECT_FALSE(sink.WasTimingFrame());
209}
210
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200211TEST(TestVCMEncodedFrameCallback, AdjustsCaptureTimeForInternalSourceEncoder) {
212 rtc::ScopedFakeClock clock;
213 clock.SetTimeMicros(1234567);
214 EncodedImage image;
215 CodecSpecificInfo codec_specific;
216 const int64_t kEncodeStartDelayMs = 2;
217 const int64_t kEncodeFinishDelayMs = 10;
218 int64_t timestamp = 1;
219 image._length = 500;
220 image.capture_time_ms_ = timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200221 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200222 codec_specific.codecType = kVideoCodecGeneric;
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200223 FakeEncodedImageCallback sink;
224 VCMEncodedFrameCallback callback(&sink, nullptr);
225 callback.SetInternalSource(true);
226 VideoCodec::TimingFrameTriggerThresholds thresholds;
227 thresholds.delay_ms = 1; // Make all frames timing frames.
228 callback.SetTimingFramesThresholds(thresholds);
229 callback.OnTargetBitrateChanged(500, 0);
230
231 // Verify a single frame without encode timestamps isn't a timing frame.
232 callback.OnEncodedImage(image, &codec_specific, nullptr);
233 EXPECT_FALSE(sink.WasTimingFrame());
234
235 // New frame, but this time with encode timestamps set in timing_.
236 // This should be a timing frame.
237 image.capture_time_ms_ = ++timestamp;
Niels Möller23775882018-08-16 10:24:12 +0200238 image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
Ilya Nikolaevskiy764aeb72018-04-03 10:01:52 +0200239 image.timing_.encode_start_ms = timestamp + kEncodeStartDelayMs;
240 image.timing_.encode_finish_ms = timestamp + kEncodeFinishDelayMs;
241 callback.OnEncodedImage(image, &codec_specific, nullptr);
242 EXPECT_TRUE(sink.WasTimingFrame());
243 // Frame is captured kEncodeFinishDelayMs before it's encoded, so restored
244 // capture timestamp should be kEncodeFinishDelayMs in the past.
245 EXPECT_EQ(
246 sink.GetLastCaptureTimestamp(),
247 clock.TimeNanos() / rtc::kNumNanosecsPerMillisec - kEncodeFinishDelayMs);
248}
249
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200250TEST(TestVCMEncodedFrameCallback, NotifiesAboutDroppedFrames) {
251 EncodedImage image;
252 CodecSpecificInfo codec_specific;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100253 const int64_t kTimestampMs1 = 47721840;
254 const int64_t kTimestampMs2 = 47721850;
255 const int64_t kTimestampMs3 = 47721860;
256 const int64_t kTimestampMs4 = 47721870;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200257 codec_specific.codecType = kVideoCodecGeneric;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200258 FakeEncodedImageCallback sink;
259 VCMEncodedFrameCallback callback(&sink, nullptr);
Ilya Nikolaevskiye0da9ea2017-11-08 14:39:02 +0100260 // Any non-zero bitrate needed to be set before the first frame.
261 callback.OnTargetBitrateChanged(500, 0);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100262 image.capture_time_ms_ = kTimestampMs1;
Niels Möller23775882018-08-16 10:24:12 +0200263 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
264 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200265 EXPECT_EQ(0u, sink.GetNumFramesDropped());
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200266 callback.OnEncodedImage(image, &codec_specific, nullptr);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100267
268 image.capture_time_ms_ = kTimestampMs2;
Niels Möller23775882018-08-16 10:24:12 +0200269 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
270 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200271 // No OnEncodedImageCall for timestamp2. Yet, at this moment it's not known
272 // that frame with timestamp2 was dropped.
273 EXPECT_EQ(0u, sink.GetNumFramesDropped());
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100274
275 image.capture_time_ms_ = kTimestampMs3;
Niels Möller23775882018-08-16 10:24:12 +0200276 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
277 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200278 callback.OnEncodedImage(image, &codec_specific, nullptr);
279 EXPECT_EQ(1u, sink.GetNumFramesDropped());
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100280
281 image.capture_time_ms_ = kTimestampMs4;
Niels Möller23775882018-08-16 10:24:12 +0200282 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
283 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200284 callback.OnEncodedImage(image, &codec_specific, nullptr);
285 EXPECT_EQ(1u, sink.GetNumFramesDropped());
286}
287
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100288TEST(TestVCMEncodedFrameCallback, RestoresCaptureTimestamps) {
289 EncodedImage image;
290 CodecSpecificInfo codec_specific;
291 const int64_t kTimestampMs = 123456;
292 codec_specific.codecType = kVideoCodecGeneric;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100293 FakeEncodedImageCallback sink;
294 VCMEncodedFrameCallback callback(&sink, nullptr);
295 // Any non-zero bitrate needed to be set before the first frame.
296 callback.OnTargetBitrateChanged(500, 0);
297 image.capture_time_ms_ = kTimestampMs; // Incorrect timesetamp.
Niels Möller23775882018-08-16 10:24:12 +0200298 image.SetTimestamp(static_cast<uint32_t>(image.capture_time_ms_ * 90));
299 callback.OnEncodeStarted(image.Timestamp(), image.capture_time_ms_, 0);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100300 image.capture_time_ms_ = 0; // Incorrect timesetamp.
301 callback.OnEncodedImage(image, &codec_specific, nullptr);
302 EXPECT_EQ(kTimestampMs, sink.GetLastCaptureTimestamp());
303}
304
ilnik04f4d122017-06-19 07:18:55 -0700305} // namespace test
306} // namespace webrtc