summaryrefslogtreecommitdiffstats
path: root/media/webrtc/signaling/gtest/rtpsources_unittests.cpp
blob: 2590b8596d5c881737530c88f45c6c6ff98a8e24 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#include <RtpSourceObserver.h>
#include "RTCStatsReport.h"
#include "webrtc/modules/include/module_common_types.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"

using namespace mozilla;

namespace test {
class RtpSourcesTest : public ::testing::Test {
  using RtpSourceHistory = RtpSourceObserver::RtpSourceHistory;
  using RtpSourceEntry = mozilla::RtpSourceObserver::RtpSourceEntry;

 public:
  // History Tests

  // Test init happens as expected
  void TestInitState() {
    RtpSourceHistory history;
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
    const auto& e = history.mLatestEviction;
    EXPECT_FALSE(history.mHasEvictedEntry);
    EXPECT_EQ(e.predictedPlayoutTime, 0);
    EXPECT_FALSE(e.hasAudioLevel);
    EXPECT_EQ(e.audioLevel, 0);
  }

  // Test adding into the jitter window
  void TestInsertIntoJitterWindow() {
    const bool hasAudioLevel = true;
    const uint8_t audioLevel = 10;
    const int64_t jitter = 10;
    RtpSourceHistory history;
    const int64_t times[] = {100, 120, 140, 160, 180, 200, 220};
    const size_t numEntries = sizeof(times) / sizeof(times[0]);
    for (auto i : times) {
      history.Insert(i, i + jitter, i, hasAudioLevel, audioLevel);
    }
    ASSERT_EQ(history.mDetailedHistory.size(), numEntries);
    for (auto i : times) {
      auto entry = history.FindClosestNotAfter(i + jitter);
      ASSERT_NE(entry, nullptr);
      if (entry) {
        EXPECT_EQ(entry->predictedPlayoutTime, i + jitter);
        EXPECT_EQ(entry->hasAudioLevel, hasAudioLevel);
        EXPECT_EQ(entry->audioLevel, audioLevel);
      }
    }
  }

  // Tests inserting before the jitter window, in long term history.
  void TestInsertIntoLongTerm() {
    RtpSourceHistory history;

    constexpr int64_t timeNow = 100000;
    // Should be discarded as too old
    constexpr int64_t time0 = timeNow - (10 * 1000) - 1;
    // Shold be commited
    constexpr int64_t time1 = timeNow - (10 * 1000);
    // Should be commited because it is newer
    constexpr int64_t time2 = timeNow - (5 * 1000);
    // Should be discarded because it is older
    constexpr int64_t time3 = timeNow - (7 * 1000);
    // Prune time0 should not prune time2
    constexpr int64_t pruneTime0 = time2 + (10 * 1000);
    // Prune time1 should prune time2
    constexpr int64_t pruneTime1 = time2 + (10 * 1000) + 1;

    // time0
    history.Insert(timeNow, time0, 0, true, 0);
    EXPECT_TRUE(history.Empty());
    EXPECT_FALSE(history.mHasEvictedEntry);

    // time1
    history.Insert(timeNow, time1, 1, true, 0);
    // Check that the jitter window buffer hasn't been used
    EXPECT_TRUE(history.Empty());
    ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time1);
    EXPECT_TRUE(history.mHasEvictedEntry);

    // time2
    history.Insert(timeNow, time2, 2, true, 0);
    EXPECT_TRUE(history.Empty());
    ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time2);
    EXPECT_TRUE(history.mHasEvictedEntry);

    // time3
    history.Insert(timeNow, time3, 3, true, 0);
    EXPECT_TRUE(history.Empty());
    ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time2);
    EXPECT_TRUE(history.mHasEvictedEntry);

    // pruneTime0
    history.Prune(pruneTime0);
    EXPECT_TRUE(history.Empty());
    ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time2);
    EXPECT_TRUE(history.mHasEvictedEntry);

    // pruneTime1
    history.Prune(pruneTime1);
    EXPECT_TRUE(history.Empty());
    EXPECT_FALSE(history.mHasEvictedEntry);
  }

  // Tests that a value inserted into the jitter window will age into long term
  // storage
  void TestAgeIntoLongTerm() {
    RtpSourceHistory history;
    constexpr int64_t jitterWindow = RtpSourceHistory::kMinJitterWindow;
    constexpr int64_t jitter = jitterWindow / 2;
    constexpr int64_t timeNow0 = 100000;
    constexpr int64_t time0 = timeNow0;
    constexpr int64_t time1 = timeNow0 + jitter;
    // Prune at timeNow1 should evict time0
    constexpr int64_t timeNow1 = time0 + jitterWindow + 1;
    // Prune at timeNow2 should evict time1
    constexpr int64_t timeNow2 = time1 + jitterWindow + 1;

    // time0
    history.Insert(timeNow0, time0, 0, false, 1);
    EXPECT_FALSE(history.Empty());
    // Jitter window should not have grown
    ASSERT_EQ(history.mMaxJitterWindow, jitterWindow);
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
    EXPECT_FALSE(history.mHasEvictedEntry);

    // time1
    history.Insert(timeNow0, time1, 1, true, 2);
    ASSERT_EQ(history.mMaxJitterWindow, jitterWindow);
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(2));
    EXPECT_FALSE(history.mHasEvictedEntry);

    // Prune at timeNow1
    history.Prune(timeNow1);
    ASSERT_EQ(history.mMaxJitterWindow, jitterWindow);
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
    EXPECT_TRUE(history.mHasEvictedEntry);
    ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time0);
    ASSERT_EQ(history.mLatestEviction.hasAudioLevel, false);
    ASSERT_EQ(history.mLatestEviction.audioLevel, 1);

    // Prune at timeNow1
    history.Prune(timeNow2);
    EXPECT_EQ(history.mMaxJitterWindow, jitterWindow);
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
    EXPECT_TRUE(history.mHasEvictedEntry);
    EXPECT_EQ(history.mLatestEviction.predictedPlayoutTime, time1);
    EXPECT_EQ(history.mLatestEviction.hasAudioLevel, true);
    EXPECT_EQ(history.mLatestEviction.audioLevel, 2);
  }

  // Packets have a maximum audio level of 127
  void TestMaximumAudioLevel() {
    RtpSourceHistory history;
    constexpr int64_t timeNow = 0;
    const int64_t jitterAdjusted = timeNow + 10;
    const uint32_t ntpTimestamp = 0;
    const bool hasAudioLevel = true;
    const uint8_t audioLevel0 = 127;
    // should result in in hasAudioLevel = false
    const uint8_t audioLevel1 = 255;
    // should result in in hasAudioLevel = false
    const uint8_t audioLevel2 = 128;

    // audio level 0
    history.Insert(timeNow, jitterAdjusted, ntpTimestamp, hasAudioLevel,
                   audioLevel0);
    ASSERT_FALSE(history.mHasEvictedEntry);
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
    {
      auto* entry = history.FindClosestNotAfter(jitterAdjusted);
      ASSERT_NE(entry, nullptr);
      EXPECT_TRUE(entry->hasAudioLevel);
      EXPECT_EQ(entry->audioLevel, audioLevel0);
    }
    // audio level 1
    history.Insert(timeNow, jitterAdjusted, ntpTimestamp, hasAudioLevel,
                   audioLevel1);
    ASSERT_FALSE(history.mHasEvictedEntry);
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
    {
      auto* entry = history.FindClosestNotAfter(jitterAdjusted);
      ASSERT_NE(entry, nullptr);
      EXPECT_FALSE(entry->hasAudioLevel);
      EXPECT_EQ(entry->audioLevel, audioLevel1);
    }
    // audio level 2
    history.Insert(timeNow, jitterAdjusted, ntpTimestamp, hasAudioLevel,
                   audioLevel2);
    ASSERT_FALSE(history.mHasEvictedEntry);
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
    {
      auto* entry = history.FindClosestNotAfter(jitterAdjusted);
      ASSERT_NE(entry, nullptr);
      EXPECT_FALSE(entry->hasAudioLevel);
      EXPECT_EQ(entry->audioLevel, audioLevel2);
    }
  }

  void TestEmptyPrune() {
    // Empty Prune
    RtpSourceHistory history;
    const int64_t timeNow = 0;
    history.Prune(timeNow);
    EXPECT_TRUE(history.Empty());
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
    EXPECT_FALSE(history.mHasEvictedEntry);
  }

  void TestSinglePrune() {
    RtpSourceHistory history;
    constexpr int64_t timeNow = 10000;
    constexpr int64_t jitter = RtpSourceHistory::kMinJitterWindow / 2;
    const int64_t jitterAdjusted = timeNow + jitter;
    const uint32_t ntpTimestamp = 0;

    history.Insert(timeNow, jitterAdjusted, ntpTimestamp, false, 0);
    history.Prune(timeNow + (jitter * 3) + 1);
    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
    EXPECT_TRUE(history.mHasEvictedEntry);
    EXPECT_EQ(jitterAdjusted, history.mLatestEviction.predictedPlayoutTime);
  }

  // Observer tests that keys are properly handled
  void TestKeyManipulation() {
    constexpr unsigned int ssrc = 4682;
    constexpr unsigned int csrc = 4682;
    auto ssrcKey = RtpSourceObserver::GetKey(
        ssrc, dom::RTCRtpSourceEntryType::Synchronization);
    auto csrcKey = RtpSourceObserver::GetKey(
        csrc, dom::RTCRtpSourceEntryType::Contributing);
    // SSRC and CSRC keys are not the same
    EXPECT_NE(ssrcKey, csrcKey);
    // Keys can be reversed back to source
    EXPECT_EQ(RtpSourceObserver::GetSourceFromKey(ssrcKey), ssrc);
    EXPECT_EQ(RtpSourceObserver::GetSourceFromKey(csrcKey), csrc);
    // Keys can be reversed back to type
    EXPECT_EQ(RtpSourceObserver::GetTypeFromKey(ssrcKey),
              dom::RTCRtpSourceEntryType::Synchronization);
    EXPECT_EQ(RtpSourceObserver::GetTypeFromKey(csrcKey),
              dom::RTCRtpSourceEntryType::Contributing);
  }

  // Observer a header with a single Csrc
  void TestObserveOneCsrc() {
    RefPtr<RtpSourceObserver> observer =
        new RtpSourceObserver(dom::RTCStatsTimestampMaker());
    webrtc::RTPHeader header;
    constexpr unsigned int ssrc = 857265;
    constexpr unsigned int csrc = 3268365;
    constexpr int64_t jitter = 0;

    header.ssrc = ssrc;
    header.numCSRCs = 1;
    header.arrOfCSRCs[0] = csrc;
    observer->OnRtpPacket(header, jitter);

    // One for the SSRC, one for the CSRC
    EXPECT_EQ(observer->mRtpSources.size(), static_cast<size_t>(2));
    nsTArray<dom::RTCRtpSourceEntry> outLevels;
    observer->GetRtpSources(outLevels);
    EXPECT_EQ(outLevels.Length(), static_cast<size_t>(2));
    bool ssrcFound = false;
    bool csrcFound = true;
    for (auto& entry : outLevels) {
      if (entry.mSource == ssrc) {
        ssrcFound = true;
        EXPECT_EQ(entry.mSourceType,
                  dom::RTCRtpSourceEntryType::Synchronization);
      }
      if (entry.mSource == csrc) {
        csrcFound = true;
        EXPECT_EQ(entry.mSourceType, dom::RTCRtpSourceEntryType::Contributing);
      }
    }
    EXPECT_TRUE(ssrcFound);
    EXPECT_TRUE(csrcFound);
  }

  // Observer a header with two CSRCs
  void TestObserveTwoCsrcs() {
    RefPtr<RtpSourceObserver> observer =
        new RtpSourceObserver(dom::RTCStatsTimestampMaker());
    webrtc::RTPHeader header;
    constexpr unsigned int ssrc = 239485;
    constexpr unsigned int csrc0 = 3425;
    constexpr unsigned int csrc1 = 36457;
    constexpr int64_t jitter = 0;

    header.ssrc = ssrc;
    header.numCSRCs = 2;
    header.arrOfCSRCs[0] = csrc0;
    header.arrOfCSRCs[1] = csrc1;
    observer->OnRtpPacket(header, jitter);

    // One for the SSRC, two for the CSRCs
    EXPECT_EQ(observer->mRtpSources.size(), static_cast<size_t>(3));
    nsTArray<dom::RTCRtpSourceEntry> outLevels;
    observer->GetRtpSources(outLevels);
    EXPECT_EQ(outLevels.Length(), static_cast<size_t>(3));
    bool ssrcFound = false;
    bool csrc0Found = true;
    bool csrc1Found = true;
    for (auto& entry : outLevels) {
      if (entry.mSource == ssrc) {
        ssrcFound = true;
        EXPECT_EQ(entry.mSourceType,
                  dom::RTCRtpSourceEntryType::Synchronization);
      }
      if (entry.mSource == csrc0) {
        csrc0Found = true;
        EXPECT_EQ(entry.mSourceType, dom::RTCRtpSourceEntryType::Contributing);
      }
      if (entry.mSource == csrc1) {
        csrc1Found = true;
        EXPECT_EQ(entry.mSourceType, dom::RTCRtpSourceEntryType::Contributing);
      }
    }
    EXPECT_TRUE(ssrcFound);
    EXPECT_TRUE(csrc0Found);
    EXPECT_TRUE(csrc1Found);
  }

  // Observer a header with a CSRC with audio level extension
  void TestObserveCsrcWithAudioLevel() {
    RefPtr<RtpSourceObserver> observer =
        new RtpSourceObserver(dom::RTCStatsTimestampMaker());
    webrtc::RTPHeader header;
  }
};

TEST_F(RtpSourcesTest, TestInitState) { TestInitState(); }
TEST_F(RtpSourcesTest, TestInsertIntoJitterWindow) {
  TestInsertIntoJitterWindow();
}
TEST_F(RtpSourcesTest, TestAgeIntoLongTerm) { TestAgeIntoLongTerm(); }
TEST_F(RtpSourcesTest, TestInsertIntoLongTerm) { TestInsertIntoLongTerm(); }
TEST_F(RtpSourcesTest, TestMaximumAudioLevel) { TestMaximumAudioLevel(); }
TEST_F(RtpSourcesTest, TestEmptyPrune) { TestEmptyPrune(); }
TEST_F(RtpSourcesTest, TestSinglePrune) { TestSinglePrune(); }
TEST_F(RtpSourcesTest, TestKeyManipulation) { TestKeyManipulation(); }
TEST_F(RtpSourcesTest, TestObserveOneCsrc) { TestObserveOneCsrc(); }
TEST_F(RtpSourcesTest, TestObserveTwoCsrcs) { TestObserveTwoCsrcs(); }
TEST_F(RtpSourcesTest, TestObserveCsrcWithAudioLevel) {
  TestObserveCsrcWithAudioLevel();
}
}  // namespace test