| /* | |
| * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #include "modules/remote_bitrate_estimator/inter_arrival.h" | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| namespace testing { | |
| enum { | |
| kTimestampGroupLengthUs = 5000, | |
| kMinStep = 20, | |
| kTriggerNewGroupUs = kTimestampGroupLengthUs + kMinStep, | |
| kBurstThresholdMs = 5, | |
| kAbsSendTimeFraction = 18, | |
| kAbsSendTimeInterArrivalUpshift = 8, | |
| kInterArrivalShift = kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift, | |
| }; | |
| constexpr double kRtpTimestampToMs = 1.0 / 90.0; | |
| constexpr double kAstToMs = | |
| 1000.0 / static_cast<double>(1 << kInterArrivalShift); | |
| class InterArrivalTest : public ::testing::Test { | |
| protected: | |
| void SetUp() override { | |
| inter_arrival_.reset(new InterArrival(kTimestampGroupLengthUs / 1000, 1.0)); | |
| inter_arrival_rtp_.reset(new InterArrival( | |
| MakeRtpTimestamp(kTimestampGroupLengthUs), kRtpTimestampToMs)); | |
| inter_arrival_ast_.reset( | |
| new InterArrival(MakeAbsSendTime(kTimestampGroupLengthUs), kAstToMs)); | |
| } | |
| // Test that neither inter_arrival instance complete the timestamp group from | |
| // the given data. | |
| void ExpectFalse(int64_t timestamp_us, | |
| int64_t arrival_time_ms, | |
| size_t packet_size) { | |
| InternalExpectFalse(inter_arrival_rtp_.get(), | |
| MakeRtpTimestamp(timestamp_us), arrival_time_ms, | |
| packet_size); | |
| InternalExpectFalse(inter_arrival_ast_.get(), MakeAbsSendTime(timestamp_us), | |
| arrival_time_ms, packet_size); | |
| } | |
| // Test that both inter_arrival instances complete the timestamp group from | |
| // the given data and that all returned deltas are as expected (except | |
| // timestamp delta, which is rounded from us to different ranges and must | |
| // match within an interval, given in |timestamp_near]. | |
| void ExpectTrue(int64_t timestamp_us, | |
| int64_t arrival_time_ms, | |
| size_t packet_size, | |
| int64_t expected_timestamp_delta_us, | |
| int64_t expected_arrival_time_delta_ms, | |
| int expected_packet_size_delta, | |
| uint32_t timestamp_near) { | |
| InternalExpectTrue(inter_arrival_rtp_.get(), MakeRtpTimestamp(timestamp_us), | |
| arrival_time_ms, packet_size, | |
| MakeRtpTimestamp(expected_timestamp_delta_us), | |
| expected_arrival_time_delta_ms, | |
| expected_packet_size_delta, timestamp_near); | |
| InternalExpectTrue(inter_arrival_ast_.get(), MakeAbsSendTime(timestamp_us), | |
| arrival_time_ms, packet_size, | |
| MakeAbsSendTime(expected_timestamp_delta_us), | |
| expected_arrival_time_delta_ms, | |
| expected_packet_size_delta, timestamp_near << 8); | |
| } | |
| void WrapTestHelper(int64_t wrap_start_us, | |
| uint32_t timestamp_near, | |
| bool unorderly_within_group) { | |
| // Step through the range of a 32 bit int, 1/4 at a time to not cause | |
| // packets close to wraparound to be judged as out of order. | |
| // G1 | |
| int64_t arrival_time = 17; | |
| ExpectFalse(0, arrival_time, 1); | |
| // G2 | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectFalse(wrap_start_us / 4, arrival_time, 1); | |
| // G3 | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectTrue(wrap_start_us / 2, arrival_time, 1, wrap_start_us / 4, 6, | |
| 0, // Delta G2-G1 | |
| 0); | |
| // G4 | |
| arrival_time += kBurstThresholdMs + 1; | |
| int64_t g4_arrival_time = arrival_time; | |
| ExpectTrue(wrap_start_us / 2 + wrap_start_us / 4, arrival_time, 1, | |
| wrap_start_us / 4, 6, 0, // Delta G3-G2 | |
| timestamp_near); | |
| // G5 | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectTrue(wrap_start_us, arrival_time, 2, wrap_start_us / 4, 6, | |
| 0, // Delta G4-G3 | |
| timestamp_near); | |
| for (int i = 0; i < 10; ++i) { | |
| // Slowly step across the wrap point. | |
| arrival_time += kBurstThresholdMs + 1; | |
| if (unorderly_within_group) { | |
| // These packets arrive with timestamps in decreasing order but are | |
| // nevertheless accumulated to group because their timestamps are higher | |
| // than the initial timestamp of the group. | |
| ExpectFalse(wrap_start_us + kMinStep * (9 - i), arrival_time, 1); | |
| } else { | |
| ExpectFalse(wrap_start_us + kMinStep * i, arrival_time, 1); | |
| } | |
| } | |
| int64_t g5_arrival_time = arrival_time; | |
| // This packet is out of order and should be dropped. | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectFalse(wrap_start_us - 100, arrival_time, 100); | |
| // G6 | |
| arrival_time += kBurstThresholdMs + 1; | |
| int64_t g6_arrival_time = arrival_time; | |
| ExpectTrue(wrap_start_us + kTriggerNewGroupUs, arrival_time, 10, | |
| wrap_start_us / 4 + 9 * kMinStep, | |
| g5_arrival_time - g4_arrival_time, | |
| (2 + 10) - 1, // Delta G5-G4 | |
| timestamp_near); | |
| // This packet is out of order and should be dropped. | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectFalse(wrap_start_us + kTimestampGroupLengthUs, arrival_time, 100); | |
| // G7 | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectTrue(wrap_start_us + 2 * kTriggerNewGroupUs, arrival_time, 100, | |
| // Delta G6-G5 | |
| kTriggerNewGroupUs - 9 * kMinStep, | |
| g6_arrival_time - g5_arrival_time, 10 - (2 + 10), | |
| timestamp_near); | |
| } | |
| std::unique_ptr<InterArrival> inter_arrival_; | |
| private: | |
| static uint32_t MakeRtpTimestamp(int64_t us) { | |
| return static_cast<uint32_t>(static_cast<uint64_t>(us * 90 + 500) / 1000); | |
| } | |
| static uint32_t MakeAbsSendTime(int64_t us) { | |
| uint32_t absolute_send_time = | |
| static_cast<uint32_t>(((static_cast<uint64_t>(us) << 18) + 500000) / | |
| 1000000) & | |
| 0x00FFFFFFul; | |
| return absolute_send_time << 8; | |
| } | |
| static void InternalExpectFalse(InterArrival* inter_arrival, | |
| uint32_t timestamp, | |
| int64_t arrival_time_ms, | |
| size_t packet_size) { | |
| uint32_t dummy_timestamp = 101; | |
| int64_t dummy_arrival_time_ms = 303; | |
| int dummy_packet_size = 909; | |
| bool computed = inter_arrival->ComputeDeltas( | |
| timestamp, arrival_time_ms, arrival_time_ms, packet_size, | |
| &dummy_timestamp, &dummy_arrival_time_ms, &dummy_packet_size); | |
| EXPECT_EQ(computed, false); | |
| EXPECT_EQ(101ul, dummy_timestamp); | |
| EXPECT_EQ(303, dummy_arrival_time_ms); | |
| EXPECT_EQ(909, dummy_packet_size); | |
| } | |
| static void InternalExpectTrue(InterArrival* inter_arrival, | |
| uint32_t timestamp, | |
| int64_t arrival_time_ms, | |
| size_t packet_size, | |
| uint32_t expected_timestamp_delta, | |
| int64_t expected_arrival_time_delta_ms, | |
| int expected_packet_size_delta, | |
| uint32_t timestamp_near) { | |
| uint32_t delta_timestamp = 101; | |
| int64_t delta_arrival_time_ms = 303; | |
| int delta_packet_size = 909; | |
| bool computed = inter_arrival->ComputeDeltas( | |
| timestamp, arrival_time_ms, arrival_time_ms, packet_size, | |
| &delta_timestamp, &delta_arrival_time_ms, &delta_packet_size); | |
| EXPECT_EQ(true, computed); | |
| EXPECT_NEAR(expected_timestamp_delta, delta_timestamp, timestamp_near); | |
| EXPECT_EQ(expected_arrival_time_delta_ms, delta_arrival_time_ms); | |
| EXPECT_EQ(expected_packet_size_delta, delta_packet_size); | |
| } | |
| std::unique_ptr<InterArrival> inter_arrival_rtp_; | |
| std::unique_ptr<InterArrival> inter_arrival_ast_; | |
| }; | |
| TEST_F(InterArrivalTest, FirstPacket) { | |
| ExpectFalse(0, 17, 1); | |
| } | |
| TEST_F(InterArrivalTest, FirstGroup) { | |
| // G1 | |
| int64_t arrival_time = 17; | |
| int64_t g1_arrival_time = arrival_time; | |
| ExpectFalse(0, arrival_time, 1); | |
| // G2 | |
| arrival_time += kBurstThresholdMs + 1; | |
| int64_t g2_arrival_time = arrival_time; | |
| ExpectFalse(kTriggerNewGroupUs, arrival_time, 2); | |
| // G3 | |
| // Only once the first packet of the third group arrives, do we see the deltas | |
| // between the first two. | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 1, | |
| // Delta G2-G1 | |
| kTriggerNewGroupUs, g2_arrival_time - g1_arrival_time, 1, 0); | |
| } | |
| TEST_F(InterArrivalTest, SecondGroup) { | |
| // G1 | |
| int64_t arrival_time = 17; | |
| int64_t g1_arrival_time = arrival_time; | |
| ExpectFalse(0, arrival_time, 1); | |
| // G2 | |
| arrival_time += kBurstThresholdMs + 1; | |
| int64_t g2_arrival_time = arrival_time; | |
| ExpectFalse(kTriggerNewGroupUs, arrival_time, 2); | |
| // G3 | |
| arrival_time += kBurstThresholdMs + 1; | |
| int64_t g3_arrival_time = arrival_time; | |
| ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 1, | |
| // Delta G2-G1 | |
| kTriggerNewGroupUs, g2_arrival_time - g1_arrival_time, 1, 0); | |
| // G4 | |
| // First packet of 4th group yields deltas between group 2 and 3. | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectTrue(3 * kTriggerNewGroupUs, arrival_time, 2, | |
| // Delta G3-G2 | |
| kTriggerNewGroupUs, g3_arrival_time - g2_arrival_time, -1, 0); | |
| } | |
| TEST_F(InterArrivalTest, AccumulatedGroup) { | |
| // G1 | |
| int64_t arrival_time = 17; | |
| int64_t g1_arrival_time = arrival_time; | |
| ExpectFalse(0, arrival_time, 1); | |
| // G2 | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectFalse(kTriggerNewGroupUs, 28, 2); | |
| int64_t timestamp = kTriggerNewGroupUs; | |
| for (int i = 0; i < 10; ++i) { | |
| // A bunch of packets arriving within the same group. | |
| arrival_time += kBurstThresholdMs + 1; | |
| timestamp += kMinStep; | |
| ExpectFalse(timestamp, arrival_time, 1); | |
| } | |
| int64_t g2_arrival_time = arrival_time; | |
| int64_t g2_timestamp = timestamp; | |
| // G3 | |
| arrival_time = 500; | |
| ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 100, g2_timestamp, | |
| g2_arrival_time - g1_arrival_time, | |
| (2 + 10) - 1, // Delta G2-G1 | |
| 0); | |
| } | |
| TEST_F(InterArrivalTest, OutOfOrderPacket) { | |
| // G1 | |
| int64_t arrival_time = 17; | |
| int64_t timestamp = 0; | |
| ExpectFalse(timestamp, arrival_time, 1); | |
| int64_t g1_timestamp = timestamp; | |
| int64_t g1_arrival_time = arrival_time; | |
| // G2 | |
| arrival_time += 11; | |
| timestamp += kTriggerNewGroupUs; | |
| ExpectFalse(timestamp, 28, 2); | |
| for (int i = 0; i < 10; ++i) { | |
| arrival_time += kBurstThresholdMs + 1; | |
| timestamp += kMinStep; | |
| ExpectFalse(timestamp, arrival_time, 1); | |
| } | |
| int64_t g2_timestamp = timestamp; | |
| int64_t g2_arrival_time = arrival_time; | |
| // This packet is out of order and should be dropped. | |
| arrival_time = 281; | |
| ExpectFalse(g1_timestamp, arrival_time, 100); | |
| // G3 | |
| arrival_time = 500; | |
| timestamp = 2 * kTriggerNewGroupUs; | |
| ExpectTrue(timestamp, arrival_time, 100, | |
| // Delta G2-G1 | |
| g2_timestamp - g1_timestamp, g2_arrival_time - g1_arrival_time, | |
| (2 + 10) - 1, 0); | |
| } | |
| TEST_F(InterArrivalTest, OutOfOrderWithinGroup) { | |
| // G1 | |
| int64_t arrival_time = 17; | |
| int64_t timestamp = 0; | |
| ExpectFalse(timestamp, arrival_time, 1); | |
| int64_t g1_timestamp = timestamp; | |
| int64_t g1_arrival_time = arrival_time; | |
| // G2 | |
| timestamp += kTriggerNewGroupUs; | |
| arrival_time += 11; | |
| ExpectFalse(kTriggerNewGroupUs, 28, 2); | |
| timestamp += 10 * kMinStep; | |
| int64_t g2_timestamp = timestamp; | |
| for (int i = 0; i < 10; ++i) { | |
| // These packets arrive with timestamps in decreasing order but are | |
| // nevertheless accumulated to group because their timestamps are higher | |
| // than the initial timestamp of the group. | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectFalse(timestamp, arrival_time, 1); | |
| timestamp -= kMinStep; | |
| } | |
| int64_t g2_arrival_time = arrival_time; | |
| // However, this packet is deemed out of order and should be dropped. | |
| arrival_time = 281; | |
| timestamp = g1_timestamp; | |
| ExpectFalse(timestamp, arrival_time, 100); | |
| // G3 | |
| timestamp = 2 * kTriggerNewGroupUs; | |
| arrival_time = 500; | |
| ExpectTrue(timestamp, arrival_time, 100, g2_timestamp - g1_timestamp, | |
| g2_arrival_time - g1_arrival_time, (2 + 10) - 1, 0); | |
| } | |
| TEST_F(InterArrivalTest, TwoBursts) { | |
| // G1 | |
| int64_t g1_arrival_time = 17; | |
| ExpectFalse(0, g1_arrival_time, 1); | |
| // G2 | |
| int64_t timestamp = kTriggerNewGroupUs; | |
| int64_t arrival_time = 100; // Simulate no packets arriving for 100 ms. | |
| for (int i = 0; i < 10; ++i) { | |
| // A bunch of packets arriving in one burst (within 5 ms apart). | |
| timestamp += 30000; | |
| arrival_time += kBurstThresholdMs; | |
| ExpectFalse(timestamp, arrival_time, 1); | |
| } | |
| int64_t g2_arrival_time = arrival_time; | |
| int64_t g2_timestamp = timestamp; | |
| // G3 | |
| timestamp += 30000; | |
| arrival_time += kBurstThresholdMs + 1; | |
| ExpectTrue(timestamp, arrival_time, 100, g2_timestamp, | |
| g2_arrival_time - g1_arrival_time, | |
| 10 - 1, // Delta G2-G1 | |
| 0); | |
| } | |
| TEST_F(InterArrivalTest, NoBursts) { | |
| // G1 | |
| ExpectFalse(0, 17, 1); | |
| // G2 | |
| int64_t timestamp = kTriggerNewGroupUs; | |
| int64_t arrival_time = 28; | |
| ExpectFalse(timestamp, arrival_time, 2); | |
| // G3 | |
| ExpectTrue(kTriggerNewGroupUs + 30000, arrival_time + kBurstThresholdMs + 1, | |
| 100, timestamp - 0, arrival_time - 17, | |
| 2 - 1, // Delta G2-G1 | |
| 0); | |
| } | |
| // Yields 0xfffffffe when converted to internal representation in | |
| // inter_arrival_rtp_ and inter_arrival_ast_ respectively. | |
| static const int64_t kStartRtpTimestampWrapUs = 47721858827; | |
| static const int64_t kStartAbsSendTimeWrapUs = 63999995; | |
| TEST_F(InterArrivalTest, RtpTimestampWrap) { | |
| WrapTestHelper(kStartRtpTimestampWrapUs, 1, false); | |
| } | |
| TEST_F(InterArrivalTest, AbsSendTimeWrap) { | |
| WrapTestHelper(kStartAbsSendTimeWrapUs, 1, false); | |
| } | |
| TEST_F(InterArrivalTest, RtpTimestampWrapOutOfOrderWithinGroup) { | |
| WrapTestHelper(kStartRtpTimestampWrapUs, 1, true); | |
| } | |
| TEST_F(InterArrivalTest, AbsSendTimeWrapOutOfOrderWithinGroup) { | |
| WrapTestHelper(kStartAbsSendTimeWrapUs, 1, true); | |
| } | |
| TEST_F(InterArrivalTest, PositiveArrivalTimeJump) { | |
| const size_t kPacketSize = 1000; | |
| uint32_t send_time_ms = 10000; | |
| int64_t arrival_time_ms = 20000; | |
| int64_t system_time_ms = 30000; | |
| uint32_t send_delta; | |
| int64_t arrival_delta; | |
| int size_delta; | |
| EXPECT_FALSE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| const int kTimeDeltaMs = 30; | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs; | |
| system_time_ms += kTimeDeltaMs; | |
| EXPECT_FALSE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs + InterArrival::kArrivalTimeOffsetThresholdMs; | |
| system_time_ms += kTimeDeltaMs; | |
| EXPECT_TRUE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| EXPECT_EQ(kTimeDeltaMs, static_cast<int>(send_delta)); | |
| EXPECT_EQ(kTimeDeltaMs, arrival_delta); | |
| EXPECT_EQ(size_delta, 0); | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs; | |
| system_time_ms += kTimeDeltaMs; | |
| // The previous arrival time jump should now be detected and cause a reset. | |
| EXPECT_FALSE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| // The two next packets will not give a valid delta since we're in the initial | |
| // state. | |
| for (int i = 0; i < 2; ++i) { | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs; | |
| system_time_ms += kTimeDeltaMs; | |
| EXPECT_FALSE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| } | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs; | |
| system_time_ms += kTimeDeltaMs; | |
| EXPECT_TRUE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| EXPECT_EQ(kTimeDeltaMs, static_cast<int>(send_delta)); | |
| EXPECT_EQ(kTimeDeltaMs, arrival_delta); | |
| EXPECT_EQ(size_delta, 0); | |
| } | |
| TEST_F(InterArrivalTest, NegativeArrivalTimeJump) { | |
| const size_t kPacketSize = 1000; | |
| uint32_t send_time_ms = 10000; | |
| int64_t arrival_time_ms = 20000; | |
| int64_t system_time_ms = 30000; | |
| uint32_t send_delta; | |
| int64_t arrival_delta; | |
| int size_delta; | |
| EXPECT_FALSE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| const int kTimeDeltaMs = 30; | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs; | |
| system_time_ms += kTimeDeltaMs; | |
| EXPECT_FALSE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs; | |
| system_time_ms += kTimeDeltaMs; | |
| EXPECT_TRUE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| EXPECT_EQ(kTimeDeltaMs, static_cast<int>(send_delta)); | |
| EXPECT_EQ(kTimeDeltaMs, arrival_delta); | |
| EXPECT_EQ(size_delta, 0); | |
| // Three out of order will fail, after that we will be reset and two more will | |
| // fail before we get our first valid delta after the reset. | |
| arrival_time_ms -= 1000; | |
| for (int i = 0; i < InterArrival::kReorderedResetThreshold + 3; ++i) { | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs; | |
| system_time_ms += kTimeDeltaMs; | |
| // The previous arrival time jump should now be detected and cause a reset. | |
| EXPECT_FALSE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| } | |
| send_time_ms += kTimeDeltaMs; | |
| arrival_time_ms += kTimeDeltaMs; | |
| system_time_ms += kTimeDeltaMs; | |
| EXPECT_TRUE(inter_arrival_->ComputeDeltas( | |
| send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, | |
| &arrival_delta, &size_delta)); | |
| EXPECT_EQ(kTimeDeltaMs, static_cast<int>(send_delta)); | |
| EXPECT_EQ(kTimeDeltaMs, arrival_delta); | |
| EXPECT_EQ(size_delta, 0); | |
| } | |
| } // namespace testing | |
| } // namespace webrtc |