diff --git a/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator.cc b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator.cc index 199cae3538..400309653a 100644 --- a/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator.cc +++ b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator.cc @@ -126,16 +126,6 @@ FrameComparison ValidateFrameComparison(FrameComparison comparison) { RTC_DCHECK(comparison.frame_stats.decoded_frame_height.has_value()) << "Dropped frame comparison has to have decoded_frame_height when " << "decode_end_time is set"; - } else { - RTC_DCHECK(!comparison.frame_stats.received_time.IsFinite()) - << "Dropped frame comparison can't have received_time when " - << "decode_end_time is not set and there were no decoder failures"; - RTC_DCHECK(!comparison.frame_stats.decode_start_time.IsFinite()) - << "Dropped frame comparison can't have decode_start_time when " - << "decode_end_time is not set and there were no decoder failures"; - RTC_DCHECK(!comparison.frame_stats.used_decoder.has_value()) - << "Dropped frame comparison can't have used_decoder when " - << "decode_end_time is not set and there were no decoder failures"; } RTC_DCHECK(!comparison.frame_stats.rendered_time.IsFinite()) << "Dropped frame comparison can't have rendered_time"; @@ -448,8 +438,7 @@ void DefaultVideoQualityAnalyzerFramesComparator::ProcessComparison( FrameDropPhase dropped_phase; if (frame_stats.decode_end_time.IsFinite()) { dropped_phase = FrameDropPhase::kAfterDecoder; - } else if (frame_stats.decode_start_time.IsFinite() && - frame_stats.decoder_failed) { + } else if (frame_stats.decode_start_time.IsFinite()) { dropped_phase = FrameDropPhase::kByDecoder; } else if (frame_stats.encoded_time.IsFinite()) { dropped_phase = FrameDropPhase::kTransport; diff --git a/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator_test.cc b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator_test.cc index c2450d148c..519eee6d07 100644 --- a/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator_test.cc +++ b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator_test.cc @@ -1092,8 +1092,78 @@ TEST(DefaultVideoQualityAnalyzerFramesComparatorTest, EXPECT_THAT(stats.decoders, IsEmpty()); } -// TODO(titovartem): add test that just pre decoded frame can't be received as -// dropped one because decoder always returns either decoded frame or error. +TEST(DefaultVideoQualityAnalyzerFramesComparatorTest, + PreDecodedDroppedKeyFrameAccountedInStats) { + DefaultVideoQualityAnalyzerCpuMeasurer cpu_measurer; + DefaultVideoQualityAnalyzerFramesComparator comparator( + Clock::GetRealTimeClock(), cpu_measurer, + DefaultVideoQualityAnalyzerOptions()); + + Timestamp captured_time = Clock::GetRealTimeClock()->CurrentTime(); + uint16_t frame_id = 1; + size_t stream = 0; + size_t sender = 0; + size_t receiver = 1; + InternalStatsKey stats_key(stream, sender, receiver); + + // Frame captured + FrameStats frame_stats(/*frame_id=*/1, captured_time); + // Frame pre encoded + frame_stats.pre_encode_time = captured_time + TimeDelta::Millis(10); + // Frame encoded + frame_stats.encoded_time = captured_time + TimeDelta::Millis(20); + frame_stats.used_encoder = + Vp8CodecForOneFrame(frame_id, frame_stats.encoded_time); + frame_stats.encoded_frame_type = VideoFrameType::kVideoFrameKey; + frame_stats.encoded_image_size = DataSize::Bytes(1000); + frame_stats.target_encode_bitrate = 2000; + // Frame pre decoded + frame_stats.pre_decoded_frame_type = VideoFrameType::kVideoFrameKey; + frame_stats.pre_decoded_image_size = DataSize::Bytes(500); + frame_stats.received_time = captured_time + TimeDelta::Millis(30); + frame_stats.decode_start_time = captured_time + TimeDelta::Millis(40); + + comparator.Start(/*max_threads_count=*/1); + comparator.EnsureStatsForStream(stream, sender, /*peers_count=*/2, + captured_time, captured_time); + comparator.AddComparison(stats_key, + /*captured=*/absl::nullopt, + /*rendered=*/absl::nullopt, + FrameComparisonType::kDroppedFrame, frame_stats); + comparator.Stop(/*last_rendered_frame_times=*/{}); + + EXPECT_EQ(comparator.stream_stats().size(), 1lu); + StreamStats stats = comparator.stream_stats().at(stats_key); + EXPECT_EQ(stats.stream_started_time, captured_time); + expectEmpty(stats.psnr); + expectEmpty(stats.ssim); + expectEmpty(stats.transport_time_ms); + expectEmpty(stats.total_delay_incl_transport_ms); + expectEmpty(stats.time_between_rendered_frames_ms); + expectEmpty(stats.encode_frame_rate); + EXPECT_DOUBLE_EQ(GetFirstOrDie(stats.encode_time_ms), 10.0); + expectEmpty(stats.decode_time_ms); + expectEmpty(stats.receive_to_render_time_ms); + expectEmpty(stats.skipped_between_rendered); + expectEmpty(stats.freeze_time_ms); + expectEmpty(stats.time_between_freezes_ms); + expectEmpty(stats.resolution_of_decoded_frame); + EXPECT_DOUBLE_EQ(GetFirstOrDie(stats.target_encode_bitrate), 2000.0); + expectEmpty(stats.recv_key_frame_size_bytes); + expectEmpty(stats.recv_delta_frame_size_bytes); + EXPECT_EQ(stats.total_encoded_images_payload, 1000); + EXPECT_EQ(stats.num_send_key_frames, 1); + EXPECT_EQ(stats.num_recv_key_frames, 0); + EXPECT_THAT(stats.dropped_by_phase, Eq(std::map{ + {FrameDropPhase::kBeforeEncoder, 0}, + {FrameDropPhase::kByEncoder, 0}, + {FrameDropPhase::kTransport, 0}, + {FrameDropPhase::kByDecoder, 1}, + {FrameDropPhase::kAfterDecoder, 0}})); + EXPECT_EQ(stats.encoders, + std::vector{*frame_stats.used_encoder}); + EXPECT_THAT(stats.decoders, IsEmpty()); +} TEST(DefaultVideoQualityAnalyzerFramesComparatorTest, DecodedDroppedKeyFrameAccountedInStats) { diff --git a/test/pc/e2e/analyzer/video/default_video_quality_analyzer_test.cc b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_test.cc index 7d39238f8a..fc970e1ea2 100644 --- a/test/pc/e2e/analyzer/video/default_video_quality_analyzer_test.cc +++ b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_test.cc @@ -2123,6 +2123,45 @@ TEST(DefaultVideoQualityAnalyzerTest, InfraMetricsNotCollectedByDefault) { EXPECT_EQ(stats.on_decoder_error_processing_time_ms.NumSamples(), 0); } +TEST(DefaultVideoQualityAnalyzerTest, + FrameDroppedByDecoderIsAccountedCorrectly) { + std::unique_ptr frame_generator = + test::CreateSquareFrameGenerator(kFrameWidth, kFrameHeight, + /*type=*/absl::nullopt, + /*num_squares=*/absl::nullopt); + + DefaultVideoQualityAnalyzerOptions options = AnalyzerOptionsForTest(); + options.report_infra_metrics = false; + DefaultVideoQualityAnalyzer analyzer(Clock::GetRealTimeClock(), + test::GetGlobalMetricsLogger(), options); + analyzer.Start("test_case", std::vector{"alice", "bob"}, + kAnalyzerMaxThreadsCount); + + VideoFrame to_be_dropped_frame = + NextFrame(frame_generator.get(), /*timestamp_us=*/1); + uint16_t frame_id = + analyzer.OnFrameCaptured("alice", "alice_video", to_be_dropped_frame); + to_be_dropped_frame.set_id(frame_id); + analyzer.OnFramePreEncode("alice", to_be_dropped_frame); + analyzer.OnFrameEncoded("alice", to_be_dropped_frame.id(), + FakeEncode(to_be_dropped_frame), + VideoQualityAnalyzerInterface::EncoderStats(), false); + VideoFrame received_to_be_dropped_frame = DeepCopy(to_be_dropped_frame); + analyzer.OnFramePreDecode("bob", received_to_be_dropped_frame.id(), + FakeEncode(received_to_be_dropped_frame)); + PassFramesThroughAnalyzer(analyzer, "alice", "alice_video", {"bob"}, + /*frames_count=*/1, *frame_generator); + + // Give analyzer some time to process frames on async thread. The computations + // have to be fast (heavy metrics are disabled!), so if doesn't fit 100ms it + // means we have an issue! + SleepMs(100); + analyzer.Stop(); + + StreamStats stats = analyzer.GetStats().at(StatsKey("alice_video", "bob")); + ASSERT_EQ(stats.dropped_by_phase[FrameDropPhase::kByDecoder], 1); +} + class DefaultVideoQualityAnalyzerTimeBetweenFreezesTest : public TestWithParam {};