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