[DVQA] Allow processing of frames dropped by decoder
Bug: b/257402861 Change-Id: I4d495c33c162c4e3a0afef5b83adf19b6d79dfce Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/284160 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Artem Titov <titovartem@webrtc.org> Cr-Commit-Position: refs/heads/main@{#38693}
This commit is contained in:
parent
bbdb768989
commit
6d91a718c8
@ -126,16 +126,6 @@ FrameComparison ValidateFrameComparison(FrameComparison comparison) {
|
|||||||
RTC_DCHECK(comparison.frame_stats.decoded_frame_height.has_value())
|
RTC_DCHECK(comparison.frame_stats.decoded_frame_height.has_value())
|
||||||
<< "Dropped frame comparison has to have decoded_frame_height when "
|
<< "Dropped frame comparison has to have decoded_frame_height when "
|
||||||
<< "decode_end_time is set";
|
<< "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())
|
RTC_DCHECK(!comparison.frame_stats.rendered_time.IsFinite())
|
||||||
<< "Dropped frame comparison can't have rendered_time";
|
<< "Dropped frame comparison can't have rendered_time";
|
||||||
@ -448,8 +438,7 @@ void DefaultVideoQualityAnalyzerFramesComparator::ProcessComparison(
|
|||||||
FrameDropPhase dropped_phase;
|
FrameDropPhase dropped_phase;
|
||||||
if (frame_stats.decode_end_time.IsFinite()) {
|
if (frame_stats.decode_end_time.IsFinite()) {
|
||||||
dropped_phase = FrameDropPhase::kAfterDecoder;
|
dropped_phase = FrameDropPhase::kAfterDecoder;
|
||||||
} else if (frame_stats.decode_start_time.IsFinite() &&
|
} else if (frame_stats.decode_start_time.IsFinite()) {
|
||||||
frame_stats.decoder_failed) {
|
|
||||||
dropped_phase = FrameDropPhase::kByDecoder;
|
dropped_phase = FrameDropPhase::kByDecoder;
|
||||||
} else if (frame_stats.encoded_time.IsFinite()) {
|
} else if (frame_stats.encoded_time.IsFinite()) {
|
||||||
dropped_phase = FrameDropPhase::kTransport;
|
dropped_phase = FrameDropPhase::kTransport;
|
||||||
|
|||||||
@ -1092,8 +1092,78 @@ TEST(DefaultVideoQualityAnalyzerFramesComparatorTest,
|
|||||||
EXPECT_THAT(stats.decoders, IsEmpty());
|
EXPECT_THAT(stats.decoders, IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(titovartem): add test that just pre decoded frame can't be received as
|
TEST(DefaultVideoQualityAnalyzerFramesComparatorTest,
|
||||||
// dropped one because decoder always returns either decoded frame or error.
|
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, int64_t>{
|
||||||
|
{FrameDropPhase::kBeforeEncoder, 0},
|
||||||
|
{FrameDropPhase::kByEncoder, 0},
|
||||||
|
{FrameDropPhase::kTransport, 0},
|
||||||
|
{FrameDropPhase::kByDecoder, 1},
|
||||||
|
{FrameDropPhase::kAfterDecoder, 0}}));
|
||||||
|
EXPECT_EQ(stats.encoders,
|
||||||
|
std::vector<StreamCodecInfo>{*frame_stats.used_encoder});
|
||||||
|
EXPECT_THAT(stats.decoders, IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(DefaultVideoQualityAnalyzerFramesComparatorTest,
|
TEST(DefaultVideoQualityAnalyzerFramesComparatorTest,
|
||||||
DecodedDroppedKeyFrameAccountedInStats) {
|
DecodedDroppedKeyFrameAccountedInStats) {
|
||||||
|
|||||||
@ -2123,6 +2123,45 @@ TEST(DefaultVideoQualityAnalyzerTest, InfraMetricsNotCollectedByDefault) {
|
|||||||
EXPECT_EQ(stats.on_decoder_error_processing_time_ms.NumSamples(), 0);
|
EXPECT_EQ(stats.on_decoder_error_processing_time_ms.NumSamples(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(DefaultVideoQualityAnalyzerTest,
|
||||||
|
FrameDroppedByDecoderIsAccountedCorrectly) {
|
||||||
|
std::unique_ptr<test::FrameGeneratorInterface> 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<std::string>{"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
|
class DefaultVideoQualityAnalyzerTimeBetweenFreezesTest
|
||||||
: public TestWithParam<bool> {};
|
: public TestWithParam<bool> {};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user