| /* |
| * Copyright (c) 2026 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 "video/timing/simulator/stream_base.h" |
| |
| #include <cstdint> |
| #include <vector> |
| |
| #include "api/numerics/samples_stats_counter.h" |
| #include "api/units/data_size.h" |
| #include "api/units/time_delta.h" |
| #include "api/units/timestamp.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| #include "video/timing/simulator/frame_base.h" |
| |
| namespace webrtc::video_timing_simulator { |
| namespace { |
| |
| using ::testing::ElementsAre; |
| using ::testing::Eq; |
| using ::testing::Field; |
| |
| struct TestFrame : public FrameBase<TestFrame> { |
| int num_packets = -1; |
| DataSize size = DataSize::Zero(); |
| int64_t unwrapped_rtp_timestamp = -1; |
| Timestamp assembled_timestamp = Timestamp::PlusInfinity(); |
| Timestamp ArrivalTimestampInternal() const { return assembled_timestamp; } |
| TimeDelta frame_delay_variation = TimeDelta::PlusInfinity(); |
| }; |
| |
| struct TestStream : public StreamBase<TestStream, TestFrame> { |
| Timestamp creation_timestamp = Timestamp::PlusInfinity(); |
| uint32_t ssrc = 0; |
| std::vector<TestFrame> frames; |
| }; |
| |
| TEST(StreamBaseTest, IsEmpty) { |
| TestStream stream; |
| EXPECT_TRUE(stream.IsEmpty()); |
| } |
| |
| TEST(StreamBaseTest, PopulateFrameDelayVariations) { |
| // Four frames at 30fps => 3000 RTP ticks between sent frames. |
| // Nominal inter-arrival-time is 33333us. |
| |
| // First frame becomes the initial baseline. |
| TestFrame frame1{.unwrapped_rtp_timestamp = 3000, |
| .assembled_timestamp = Timestamp::Micros(33333)}; |
| // Second frame is delayed 1000us. |
| TestFrame frame2{.unwrapped_rtp_timestamp = 6000, |
| .assembled_timestamp = Timestamp::Micros(66666 + 1000)}; |
| // Third frame is severely delayed, arriving back-to-back with the 4th frame. |
| TestFrame frame3{.unwrapped_rtp_timestamp = 9000, |
| .assembled_timestamp = Timestamp::Micros(99999 + 33333)}; |
| // The 4th frame arrives on time. |
| TestFrame frame4{.unwrapped_rtp_timestamp = 12000, |
| .assembled_timestamp = Timestamp::Micros(133332)}; |
| |
| TestStream stream{.frames = {frame1, frame2, frame3, frame4}}; |
| stream.PopulateFrameDelayVariations(); |
| |
| EXPECT_THAT( |
| stream.frames, |
| ElementsAre( |
| Field(&TestFrame::frame_delay_variation, Eq(TimeDelta::Zero())), |
| Field(&TestFrame::frame_delay_variation, Eq(TimeDelta::Micros(1000))), |
| // Due to the non-integer 1000/90 factor in the timestamp |
| // translation, we get a 33332us here instead of 33333us. |
| Field(&TestFrame::frame_delay_variation, |
| Eq(TimeDelta::Micros(33332))), |
| Field(&TestFrame::frame_delay_variation, Eq(TimeDelta::Zero())))); |
| } |
| |
| TEST(StreamBaseTest, DepartureDuration) { |
| TestStream stream{.frames = {{.unwrapped_rtp_timestamp = 3000}, |
| {.unwrapped_rtp_timestamp = 6000}}}; |
| |
| EXPECT_EQ(stream.DepartureDuration(), TimeDelta::Micros(33333)); |
| } |
| |
| TEST(StreamBaseTest, ArrivalDuration) { |
| TestStream stream{ |
| .frames = {{.assembled_timestamp = Timestamp::Micros(33333)}, |
| {.assembled_timestamp = Timestamp::Micros(66666 + 1000)}}}; |
| |
| EXPECT_EQ(stream.ArrivalDuration(), TimeDelta::Micros(34333)); |
| } |
| |
| TEST(StreamBaseTest, NumAssembledFrames) { |
| TestStream stream{ |
| .frames = {{.assembled_timestamp = Timestamp::Micros(33333)}, |
| {.assembled_timestamp = Timestamp::Micros(66666 + 1000)}}}; |
| |
| EXPECT_EQ(stream.NumAssembledFrames(), 2); |
| } |
| |
| TEST(StreamBaseTest, NumPackets) { |
| TestStream stream{.frames = {{.num_packets = 1}, {.num_packets = 2}}}; |
| |
| EXPECT_THAT( |
| stream.NumPackets().GetTimedSamples(), |
| ElementsAre(Field(&SamplesStatsCounter::StatsSample::value, Eq(1)), |
| Field(&SamplesStatsCounter::StatsSample::value, Eq(2)))); |
| } |
| |
| TEST(StreamBaseTest, SizeBytes) { |
| TestStream stream{ |
| .frames = {{.size = DataSize::Bytes(10)}, {.size = DataSize::Bytes(20)}}}; |
| |
| EXPECT_THAT( |
| stream.SizeBytes().GetTimedSamples(), |
| ElementsAre(Field(&SamplesStatsCounter::StatsSample::value, Eq(10)), |
| Field(&SamplesStatsCounter::StatsSample::value, Eq(20)))); |
| } |
| |
| TEST(StreamBaseTest, FrameDelayVariationMs) { |
| TestStream stream{ |
| .frames = {{.frame_delay_variation = TimeDelta::Millis(10)}, |
| {.frame_delay_variation = TimeDelta::Millis(20)}}}; |
| |
| EXPECT_THAT( |
| stream.FrameDelayVariationMs().GetTimedSamples(), |
| ElementsAre(Field(&SamplesStatsCounter::StatsSample::value, Eq(10)), |
| Field(&SamplesStatsCounter::StatsSample::value, Eq(20)))); |
| } |
| |
| TEST(StreamBaseTest, InterDepartureTimeMs) { |
| TestStream stream{.frames = {{.unwrapped_rtp_timestamp = 3000}, |
| {.unwrapped_rtp_timestamp = 6000}}}; |
| |
| EXPECT_THAT( |
| stream.InterDepartureTimeMs().GetTimedSamples(), |
| ElementsAre(Field(&SamplesStatsCounter::StatsSample::value, Eq(33.333)))); |
| } |
| |
| TEST(StreamBaseTest, InterArrivalTimeMs) { |
| TestStream stream{ |
| .frames = {{.assembled_timestamp = Timestamp::Micros(33333)}, |
| {.assembled_timestamp = Timestamp::Micros(66666 + 1000)}}}; |
| |
| EXPECT_THAT( |
| stream.InterArrivalTimeMs().GetTimedSamples(), |
| ElementsAre(Field(&SamplesStatsCounter::StatsSample::value, Eq(34.333)))); |
| } |
| |
| TEST(StreamBaseTest, InterFrameDelayVariationMs) { |
| TestStream stream{ |
| .frames = {{.unwrapped_rtp_timestamp = 3000, |
| .assembled_timestamp = Timestamp::Micros(33333)}, |
| {.unwrapped_rtp_timestamp = 6000, |
| .assembled_timestamp = Timestamp::Micros(66666 + 1000)}}}; |
| |
| EXPECT_THAT( |
| stream.InterFrameDelayVariationMs().GetTimedSamples(), |
| ElementsAre(Field(&SamplesStatsCounter::StatsSample::value, Eq(1.0)))); |
| } |
| |
| } // namespace |
| } // namespace webrtc::video_timing_simulator |