blob: 2b788c55710ced7542b749befdbff74c998c0062 [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"
16#include "test/gtest.h"
ilnik04f4d122017-06-19 07:18:55 -070017
18namespace webrtc {
19namespace test {
20namespace {
21inline size_t FrameSize(const size_t& min_frame_size,
22 const size_t& max_frame_size,
23 const int& s,
24 const int& i) {
25 return min_frame_size + (s + 1) * i % (max_frame_size - min_frame_size);
26}
27
28class FakeEncodedImageCallback : public EncodedImageCallback {
29 public:
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020030 FakeEncodedImageCallback()
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010031 : last_frame_was_timing_(false),
32 num_frames_dropped_(0),
33 last_capture_timestamp_(-1) {}
ilnik04f4d122017-06-19 07:18:55 -070034 Result OnEncodedImage(const EncodedImage& encoded_image,
35 const CodecSpecificInfo* codec_specific_info,
36 const RTPFragmentationHeader* fragmentation) override {
sprangba050a62017-08-18 02:51:12 -070037 last_frame_was_timing_ =
Niels Möllerc241af92017-11-10 08:51:29 +010038 encoded_image.timing_.flags != TimingFrameFlags::kInvalid &&
39 encoded_image.timing_.flags != TimingFrameFlags::kNotTriggered;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010040 last_capture_timestamp_ = encoded_image.capture_time_ms_;
mflodman351424e2017-08-10 02:43:14 -070041 return Result(Result::OK);
ilnik04f4d122017-06-19 07:18:55 -070042 };
43
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020044 void OnDroppedFrame(DropReason reason) override { ++num_frames_dropped_; }
45
ilnik04f4d122017-06-19 07:18:55 -070046 bool WasTimingFrame() { return last_frame_was_timing_; }
47
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020048 size_t GetNumFramesDropped() { return num_frames_dropped_; }
49
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010050 int64_t GetLastCaptureTimestamp() { return last_capture_timestamp_; }
51
ilnik04f4d122017-06-19 07:18:55 -070052 private:
53 bool last_frame_was_timing_;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +020054 size_t num_frames_dropped_;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010055 int64_t last_capture_timestamp_;
ilnik04f4d122017-06-19 07:18:55 -070056};
57
58enum class FrameType {
59 kNormal,
60 kTiming,
61 kDropped,
62};
63
64// Emulates |num_frames| on |num_streams| frames with capture timestamps
65// increased by 1 from 0. Size of each frame is between
66// |min_frame_size| and |max_frame_size|, outliers are counted relatevely to
67// |average_frame_sizes[]| for each stream.
68std::vector<std::vector<FrameType>> GetTimingFrames(
69 const int64_t delay_ms,
70 const size_t min_frame_size,
71 const size_t max_frame_size,
72 std::vector<size_t> average_frame_sizes,
73 const int num_streams,
74 const int num_frames) {
75 FakeEncodedImageCallback sink;
76 VCMEncodedFrameCallback callback(&sink, nullptr);
77 const size_t kFramerate = 30;
78 callback.SetTimingFramesThresholds(
79 {delay_ms, kDefaultOutlierFrameSizePercent});
80 callback.OnFrameRateChanged(kFramerate);
81 int s, i;
82 std::vector<std::vector<FrameType>> result(num_streams);
83 for (s = 0; s < num_streams; ++s)
84 callback.OnTargetBitrateChanged(average_frame_sizes[s] * kFramerate, s);
85 int64_t current_timestamp = 0;
86 for (i = 0; i < num_frames; ++i) {
87 current_timestamp += 1;
88 for (s = 0; s < num_streams; ++s) {
89 // every (5+s)-th frame is dropped on s-th stream by design.
90 bool dropped = i % (5 + s) == 0;
91
92 EncodedImage image;
93 CodecSpecificInfo codec_specific;
94 image._length = FrameSize(min_frame_size, max_frame_size, s, i);
95 image.capture_time_ms_ = current_timestamp;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010096 image._timeStamp = static_cast<uint32_t>(current_timestamp * 90);
ilnik04f4d122017-06-19 07:18:55 -070097 codec_specific.codecType = kVideoCodecGeneric;
98 codec_specific.codecSpecific.generic.simulcast_idx = s;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +010099 callback.OnEncodeStarted(static_cast<uint32_t>(current_timestamp * 90),
100 current_timestamp, s);
ilnik04f4d122017-06-19 07:18:55 -0700101 if (dropped) {
102 result[s].push_back(FrameType::kDropped);
103 continue;
104 }
105 callback.OnEncodedImage(image, &codec_specific, nullptr);
106 if (sink.WasTimingFrame()) {
107 result[s].push_back(FrameType::kTiming);
108 } else {
109 result[s].push_back(FrameType::kNormal);
110 }
111 }
112 }
113 return result;
114}
115} // namespace
116
117TEST(TestVCMEncodedFrameCallback, MarksTimingFramesPeriodicallyTogether) {
118 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
161TEST(TestVCMEncodedFrameCallback, MarksOutliers) {
162 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
sprangba050a62017-08-18 02:51:12 -0700183TEST(TestVCMEncodedFrameCallback, NoTimingFrameIfNoEncodeStartTime) {
184 EncodedImage image;
185 CodecSpecificInfo codec_specific;
186 int64_t timestamp = 1;
187 image._length = 500;
188 image.capture_time_ms_ = timestamp;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100189 image._timeStamp = static_cast<uint32_t>(timestamp * 90);
sprangba050a62017-08-18 02:51:12 -0700190 codec_specific.codecType = kVideoCodecGeneric;
191 codec_specific.codecSpecific.generic.simulcast_idx = 0;
192 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;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100206 image._timeStamp = 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 Nikolaevskiyd79314f2017-10-23 10:45:37 +0200211TEST(TestVCMEncodedFrameCallback, NotifiesAboutDroppedFrames) {
212 EncodedImage image;
213 CodecSpecificInfo codec_specific;
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100214 const int64_t kTimestampMs1 = 47721840;
215 const int64_t kTimestampMs2 = 47721850;
216 const int64_t kTimestampMs3 = 47721860;
217 const int64_t kTimestampMs4 = 47721870;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200218 codec_specific.codecType = kVideoCodecGeneric;
219 codec_specific.codecSpecific.generic.simulcast_idx = 0;
220 FakeEncodedImageCallback sink;
221 VCMEncodedFrameCallback callback(&sink, nullptr);
Ilya Nikolaevskiye0da9ea2017-11-08 14:39:02 +0100222 // Any non-zero bitrate needed to be set before the first frame.
223 callback.OnTargetBitrateChanged(500, 0);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100224 image.capture_time_ms_ = kTimestampMs1;
225 image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
226 callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200227 EXPECT_EQ(0u, sink.GetNumFramesDropped());
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200228 callback.OnEncodedImage(image, &codec_specific, nullptr);
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100229
230 image.capture_time_ms_ = kTimestampMs2;
231 image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
232 callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200233 // No OnEncodedImageCall for timestamp2. Yet, at this moment it's not known
234 // that frame with timestamp2 was dropped.
235 EXPECT_EQ(0u, sink.GetNumFramesDropped());
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100236
237 image.capture_time_ms_ = kTimestampMs3;
238 image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
239 callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200240 callback.OnEncodedImage(image, &codec_specific, nullptr);
241 EXPECT_EQ(1u, sink.GetNumFramesDropped());
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100242
243 image.capture_time_ms_ = kTimestampMs4;
244 image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
245 callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200246 callback.OnEncodedImage(image, &codec_specific, nullptr);
247 EXPECT_EQ(1u, sink.GetNumFramesDropped());
248}
249
Ilya Nikolaevskiy76f2a852017-11-16 14:33:53 +0100250TEST(TestVCMEncodedFrameCallback, RestoresCaptureTimestamps) {
251 EncodedImage image;
252 CodecSpecificInfo codec_specific;
253 const int64_t kTimestampMs = 123456;
254 codec_specific.codecType = kVideoCodecGeneric;
255 codec_specific.codecSpecific.generic.simulcast_idx = 0;
256 FakeEncodedImageCallback sink;
257 VCMEncodedFrameCallback callback(&sink, nullptr);
258 // Any non-zero bitrate needed to be set before the first frame.
259 callback.OnTargetBitrateChanged(500, 0);
260 image.capture_time_ms_ = kTimestampMs; // Incorrect timesetamp.
261 image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
262 callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
263 image.capture_time_ms_ = 0; // Incorrect timesetamp.
264 callback.OnEncodedImage(image, &codec_specific, nullptr);
265 EXPECT_EQ(kTimestampMs, sink.GetLastCaptureTimestamp());
266}
267
ilnik04f4d122017-06-19 07:18:55 -0700268} // namespace test
269} // namespace webrtc