| /* | |
| * Copyright (c) 2025 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/decodability_simulator.h" | |
| #include <memory> | |
| #include "absl/algorithm/container.h" | |
| #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 "logging/rtc_event_log/rtc_event_log_parser.h" | |
| #include "test/gmock.h" | |
| #include "test/gtest.h" | |
| #include "video/timing/simulator/frame_base.h" | |
| #include "video/timing/simulator/test/parsed_rtc_event_log_from_resources.h" | |
| namespace webrtc::video_timing_simulator { | |
| namespace { | |
| using ::testing::AllOf; | |
| using ::testing::ElementsAre; | |
| using ::testing::Eq; | |
| using ::testing::Field; | |
| using ::testing::Matcher; | |
| using ::testing::Ne; | |
| using ::testing::SizeIs; | |
| using Frame = DecodabilitySimulator::Frame; | |
| using Stream = DecodabilitySimulator::Stream; | |
| TEST(DecodabilitySimulatorFrameTest, UndecodableDuration) { | |
| DecodabilitySimulator::Frame frame{ | |
| .assembled_timestamp = Timestamp::Millis(0), | |
| .decodable_timestamp = Timestamp::Millis(5)}; | |
| EXPECT_EQ(frame.UndecodableDuration(), TimeDelta::Millis(5)); | |
| } | |
| TEST(DecodabilitySimulatorStreamTest, NumDecodableFrames) { | |
| DecodabilitySimulator::Stream stream{ | |
| .frames = {{}, {.decodable_timestamp = Timestamp::Millis(10)}}}; | |
| EXPECT_EQ(stream.NumDecodableFrames(), 1); | |
| } | |
| TEST(DecodabilitySimulatorStreamTest, UndecodableDurationMs) { | |
| DecodabilitySimulator::Stream stream{ | |
| .frames = {{.assembled_timestamp = Timestamp::Millis(0)}, | |
| {.assembled_timestamp = Timestamp::Millis(1), | |
| .decodable_timestamp = Timestamp::Millis(5)}}}; | |
| EXPECT_THAT( | |
| stream.UndecodableDurationMs().GetTimedSamples(), | |
| ElementsAre(Field(&SamplesStatsCounter::StatsSample::value, Eq(4)))); | |
| } | |
| Matcher<const Frame&> EqualsFrame(const Frame& expected) { | |
| return AllOf( | |
| Field("num_packets", &Frame::num_packets, Eq(expected.num_packets)), | |
| Field("size", &Frame::size, Eq(expected.size)), | |
| Field("unwrapped_rtp_timestamp", &Frame::unwrapped_rtp_timestamp, | |
| Eq(expected.unwrapped_rtp_timestamp)), | |
| Field("assembled_timestamp", &Frame::assembled_timestamp, | |
| Eq(expected.assembled_timestamp)), | |
| Field("decodable_timestamp", &Frame::decodable_timestamp, | |
| Eq(expected.decodable_timestamp))); | |
| } | |
| TEST(DecodabilitySimulatorTest, VideoRecvVp8) { | |
| std::unique_ptr<ParsedRtcEventLog> parsed_log = | |
| ParsedRtcEventLogFromResources("video_recv_vp8_pt96"); | |
| DecodabilitySimulator::Config config; | |
| DecodabilitySimulator simulator(config); | |
| DecodabilitySimulator::Results results = simulator.Simulate(*parsed_log); | |
| ASSERT_THAT(results.streams, SizeIs(1)); | |
| const auto& stream = results.streams.front(); | |
| EXPECT_THAT(stream.creation_timestamp, Eq(Timestamp::Millis(100942625))); | |
| EXPECT_THAT(stream.ssrc, Eq(3965119250)); | |
| EXPECT_THAT(stream.frames, SizeIs(650)); | |
| // Spot check the last frame. | |
| EXPECT_THAT( | |
| stream.frames.back(), | |
| EqualsFrame({.num_packets = 5, | |
| .size = DataSize::Bytes(5582), | |
| .unwrapped_rtp_timestamp = 2498236561, | |
| .assembled_timestamp = Timestamp::Millis(100964194), | |
| .decodable_timestamp = Timestamp::Millis(100964194)})); | |
| } | |
| TEST(DecodabilitySimulatorTest, VideoRecvVp9) { | |
| std::unique_ptr<ParsedRtcEventLog> parsed_log = | |
| ParsedRtcEventLogFromResources("video_recv_vp9_pt98"); | |
| DecodabilitySimulator::Config config; | |
| DecodabilitySimulator simulator(config); | |
| DecodabilitySimulator::Results results = simulator.Simulate(*parsed_log); | |
| ASSERT_THAT(results.streams, SizeIs(1)); | |
| const auto& stream = results.streams.front(); | |
| EXPECT_THAT(stream.creation_timestamp, Eq(Timestamp::Millis(98718560))); | |
| EXPECT_THAT(stream.ssrc, Eq(2849747025)); | |
| EXPECT_THAT(stream.frames, SizeIs(1493)); | |
| // Spot check the last frame. | |
| EXPECT_THAT( | |
| stream.frames.back(), | |
| EqualsFrame({.num_packets = 6, | |
| .size = DataSize::Bytes(6265), | |
| .unwrapped_rtp_timestamp = 2236817278, | |
| .assembled_timestamp = Timestamp::Millis(98768284), | |
| .decodable_timestamp = Timestamp::Millis(98768284)})); | |
| } | |
| TEST(DecodabilitySimulatorTest, VideoRecvAv1) { | |
| std::unique_ptr<ParsedRtcEventLog> parsed_log = | |
| ParsedRtcEventLogFromResources("video_recv_av1_pt45"); | |
| DecodabilitySimulator::Config config; | |
| DecodabilitySimulator simulator(config); | |
| DecodabilitySimulator::Results results = simulator.Simulate(*parsed_log); | |
| ASSERT_THAT(results.streams, SizeIs(1)); | |
| const auto& stream = results.streams.front(); | |
| EXPECT_THAT(stream.creation_timestamp, Eq(Timestamp::Millis(98821855))); | |
| EXPECT_THAT(stream.ssrc, Eq(2805827407)); | |
| EXPECT_THAT(stream.frames, SizeIs(1412)); | |
| // Spot check the last frame. | |
| // TODO: b/423646186 - The size values here seem unreasonable, look into this. | |
| EXPECT_THAT( | |
| stream.frames.back(), | |
| EqualsFrame({.num_packets = 16, | |
| .size = DataSize::Bytes(17559), | |
| .unwrapped_rtp_timestamp = 2213216087, | |
| .assembled_timestamp = Timestamp::Millis(98868830), | |
| .decodable_timestamp = Timestamp::Millis(98868830)})); | |
| } | |
| TEST(DecodabilitySimulatorTest, VideoRecvSequentialJoinVp8Vp9Av1) { | |
| std::unique_ptr<ParsedRtcEventLog> parsed_log = | |
| ParsedRtcEventLogFromResources("video_recv_sequential_join_vp8_vp9_av1"); | |
| DecodabilitySimulator::Config config; | |
| DecodabilitySimulator simulator(config); | |
| DecodabilitySimulator::Results results = simulator.Simulate(*parsed_log); | |
| EXPECT_THAT(results.streams, | |
| ElementsAre(AllOf(Field(&Stream::ssrc, Eq(2827012235)), | |
| Field(&Stream::frames, SizeIs(1746))), | |
| AllOf(Field(&Stream::ssrc, Eq(1651489786)), | |
| Field(&Stream::frames, SizeIs(1157))), | |
| AllOf(Field(&Stream::ssrc, Eq(1934275846)), | |
| Field(&Stream::frames, SizeIs(361))))); | |
| } | |
| // This log starts experiencing packet losses after half the duration. | |
| TEST(DecodabilitySimulatorTest, VideoRecvVp8Lossy) { | |
| std::unique_ptr<ParsedRtcEventLog> parsed_log = | |
| ParsedRtcEventLogFromResources("video_recv_vp8_pt96_lossy"); | |
| DecodabilitySimulator::Config config; | |
| DecodabilitySimulator simulator(config); | |
| DecodabilitySimulator::Results results = simulator.Simulate(*parsed_log); | |
| ASSERT_THAT(results.streams, SizeIs(1)); | |
| const auto& stream = results.streams.front(); | |
| EXPECT_THAT(stream.creation_timestamp, Eq(Timestamp::Millis(821417933))); | |
| EXPECT_THAT(stream.ssrc, Eq(4096673911)); | |
| EXPECT_THAT(stream.frames, SizeIs(1145)); | |
| // Number of decodable frames. | |
| EXPECT_THAT(absl::c_count_if(stream.frames, | |
| [](const auto& e) { | |
| return e.ArrivalTimestamp().IsFinite(); | |
| }), | |
| Eq(1117)); | |
| // Find the last decodable frame. | |
| EXPECT_TRUE(absl::c_is_sorted(stream.frames, | |
| ArrivalOrder<DecodabilitySimulator::Frame>)); | |
| auto it = stream.frames.rbegin(); | |
| while (it != stream.frames.rend() && !it->ArrivalTimestamp().IsFinite()) { | |
| ++it; | |
| } | |
| ASSERT_THAT(it, Ne(stream.frames.rend())); | |
| // Spot check the last decodable frame. | |
| EXPECT_THAT( | |
| *it, EqualsFrame({.num_packets = 4, | |
| .size = DataSize::Bytes(3902), | |
| .unwrapped_rtp_timestamp = 2607363343, | |
| .assembled_timestamp = Timestamp::Millis(821457158), | |
| .decodable_timestamp = Timestamp::Millis(821457158)})); | |
| } | |
| } // namespace | |
| } // namespace webrtc::video_timing_simulator |