blob: 90d3c05b88da3950cdf767ec024a42ab6ea23c21 [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:
30 FakeEncodedImageCallback() : last_frame_was_timing_(false) {}
31 Result OnEncodedImage(const EncodedImage& encoded_image,
32 const CodecSpecificInfo* codec_specific_info,
33 const RTPFragmentationHeader* fragmentation) override {
sprangba050a62017-08-18 02:51:12 -070034 last_frame_was_timing_ =
35 encoded_image.timing_.flags != TimingFrameFlags::kInvalid;
mflodman351424e2017-08-10 02:43:14 -070036 return Result(Result::OK);
ilnik04f4d122017-06-19 07:18:55 -070037 };
38
39 bool WasTimingFrame() { return last_frame_was_timing_; }
40
41 private:
42 bool last_frame_was_timing_;
43};
44
45enum class FrameType {
46 kNormal,
47 kTiming,
48 kDropped,
49};
50
51// Emulates |num_frames| on |num_streams| frames with capture timestamps
52// increased by 1 from 0. Size of each frame is between
53// |min_frame_size| and |max_frame_size|, outliers are counted relatevely to
54// |average_frame_sizes[]| for each stream.
55std::vector<std::vector<FrameType>> GetTimingFrames(
56 const int64_t delay_ms,
57 const size_t min_frame_size,
58 const size_t max_frame_size,
59 std::vector<size_t> average_frame_sizes,
60 const int num_streams,
61 const int num_frames) {
62 FakeEncodedImageCallback sink;
63 VCMEncodedFrameCallback callback(&sink, nullptr);
64 const size_t kFramerate = 30;
65 callback.SetTimingFramesThresholds(
66 {delay_ms, kDefaultOutlierFrameSizePercent});
67 callback.OnFrameRateChanged(kFramerate);
68 int s, i;
69 std::vector<std::vector<FrameType>> result(num_streams);
70 for (s = 0; s < num_streams; ++s)
71 callback.OnTargetBitrateChanged(average_frame_sizes[s] * kFramerate, s);
72 int64_t current_timestamp = 0;
73 for (i = 0; i < num_frames; ++i) {
74 current_timestamp += 1;
75 for (s = 0; s < num_streams; ++s) {
76 // every (5+s)-th frame is dropped on s-th stream by design.
77 bool dropped = i % (5 + s) == 0;
78
79 EncodedImage image;
80 CodecSpecificInfo codec_specific;
81 image._length = FrameSize(min_frame_size, max_frame_size, s, i);
82 image.capture_time_ms_ = current_timestamp;
83 codec_specific.codecType = kVideoCodecGeneric;
84 codec_specific.codecSpecific.generic.simulcast_idx = s;
85 callback.OnEncodeStarted(current_timestamp, s);
86 if (dropped) {
87 result[s].push_back(FrameType::kDropped);
88 continue;
89 }
90 callback.OnEncodedImage(image, &codec_specific, nullptr);
91 if (sink.WasTimingFrame()) {
92 result[s].push_back(FrameType::kTiming);
93 } else {
94 result[s].push_back(FrameType::kNormal);
95 }
96 }
97 }
98 return result;
99}
100} // namespace
101
102TEST(TestVCMEncodedFrameCallback, MarksTimingFramesPeriodicallyTogether) {
103 const int64_t kDelayMs = 29;
104 const size_t kMinFrameSize = 10;
105 const size_t kMaxFrameSize = 20;
106 const int kNumFrames = 1000;
107 const int kNumStreams = 3;
108 // No outliers as 1000 is larger than anything from range [10,20].
109 const std::vector<size_t> kAverageSize = {1000, 1000, 1000};
110 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
111 kAverageSize, kNumStreams, kNumFrames);
112 // Timing frames should be tirggered every delayMs.
113 // As no outliers are expected, frames on all streams have to be
114 // marked together.
115 int last_timing_frame = -1;
116 for (int i = 0; i < kNumFrames; ++i) {
117 int num_normal = 0;
118 int num_timing = 0;
119 int num_dropped = 0;
120 for (int s = 0; s < kNumStreams; ++s) {
121 if (frames[s][i] == FrameType::kTiming) {
122 ++num_timing;
123 } else if (frames[s][i] == FrameType::kNormal) {
124 ++num_normal;
125 } else {
126 ++num_dropped;
127 }
128 }
129 // Can't have both normal and timing frames at the same timstamp.
130 EXPECT_TRUE(num_timing == 0 || num_normal == 0);
131 if (num_dropped < kNumStreams) {
132 if (last_timing_frame == -1 || i >= last_timing_frame + kDelayMs) {
133 // If didn't have timing frames for a period, current sent frame has to
134 // be one. No normal frames should be sent.
135 EXPECT_EQ(num_normal, 0);
136 } else {
137 // No unneeded timing frames should be sent.
138 EXPECT_EQ(num_timing, 0);
139 }
140 }
141 if (num_timing > 0)
142 last_timing_frame = i;
143 }
144}
145
146TEST(TestVCMEncodedFrameCallback, MarksOutliers) {
147 const int64_t kDelayMs = 29;
148 const size_t kMinFrameSize = 2495;
149 const size_t kMaxFrameSize = 2505;
150 const int kNumFrames = 1000;
151 const int kNumStreams = 3;
152 // Possible outliers as 1000 lies in range [995, 1005].
153 const std::vector<size_t> kAverageSize = {998, 1000, 1004};
154 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
155 kAverageSize, kNumStreams, kNumFrames);
156 // All outliers should be marked.
157 for (int i = 0; i < kNumFrames; ++i) {
158 for (int s = 0; s < kNumStreams; ++s) {
159 if (FrameSize(kMinFrameSize, kMaxFrameSize, s, i) >=
160 kAverageSize[s] * kDefaultOutlierFrameSizePercent / 100) {
161 // Too big frame. May be dropped or timing, but not normal.
162 EXPECT_NE(frames[s][i], FrameType::kNormal);
163 }
164 }
165 }
166}
167
sprangba050a62017-08-18 02:51:12 -0700168TEST(TestVCMEncodedFrameCallback, NoTimingFrameIfNoEncodeStartTime) {
169 EncodedImage image;
170 CodecSpecificInfo codec_specific;
171 int64_t timestamp = 1;
172 image._length = 500;
173 image.capture_time_ms_ = timestamp;
174 codec_specific.codecType = kVideoCodecGeneric;
175 codec_specific.codecSpecific.generic.simulcast_idx = 0;
176 FakeEncodedImageCallback sink;
177 VCMEncodedFrameCallback callback(&sink, nullptr);
178 VideoCodec::TimingFrameTriggerThresholds thresholds;
179 thresholds.delay_ms = 1; // Make all frames timing frames.
180 callback.SetTimingFramesThresholds(thresholds);
181
182 // Verify a single frame works with encode start time set.
183 callback.OnEncodeStarted(timestamp, 0);
184 callback.OnEncodedImage(image, &codec_specific, nullptr);
185 EXPECT_TRUE(sink.WasTimingFrame());
186
187 // New frame, now skip OnEncodeStarted. Should not result in timing frame.
188 image.capture_time_ms_ = ++timestamp;
189 callback.OnEncodedImage(image, &codec_specific, nullptr);
190 EXPECT_FALSE(sink.WasTimingFrame());
191}
192
ilnik04f4d122017-06-19 07:18:55 -0700193} // namespace test
194} // namespace webrtc