blob: f42881392ef8be023d513481ee1654a47da6b916 [file] [log] [blame]
/*
* 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_tracker.h"
#include <cstdint>
#include <memory>
#include <utility>
#include "api/sequence_checker.h"
#include "api/video/encoded_frame.h"
#include "rtc_base/thread_annotations.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "video/timing/simulator/assembler.h"
#include "video/timing/simulator/test/encoded_frame_generators.h"
#include "video/timing/simulator/test/matchers.h"
#include "video/timing/simulator/test/simulated_time_test_fixture.h"
namespace webrtc::video_timing_simulator {
namespace {
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::NiceMock;
class MockDecodabilityTrackerEvents : public DecodabilityTrackerEvents {
public:
MOCK_METHOD(void,
OnDecodableFrame,
(const EncodedFrame& decodable_frame),
(override));
};
class MockDecodedFrameIdCallback : public DecodedFrameIdCallback {
public:
MOCK_METHOD(void, OnDecodedFrameId, (int64_t frame_id), (override));
};
class DecodabilityTrackerTest : public SimulatedTimeTestFixture {
protected:
DecodabilityTrackerTest() {
SendTask([this]() {
RTC_DCHECK_RUN_ON(queue_ptr_);
decodability_tracker_ = std::make_unique<DecodabilityTracker>(
env_,
DecodabilityTracker::Config{.ssrc =
EncodedFrameBuilderGenerator::kSsrc},
&decodability_tracker_events_);
decodability_tracker_->SetDecodedFrameIdCallback(&decoded_frame_id_cb_);
});
}
~DecodabilityTrackerTest() {
SendTask([this]() {
RTC_DCHECK_RUN_ON(queue_ptr_);
decodability_tracker_.reset();
});
}
void OnAssembledFrame(std::unique_ptr<EncodedFrame> assembled_frame) {
SendTask([this, assembled_frame = std::move(assembled_frame)]() mutable {
RTC_DCHECK_RUN_ON(queue_ptr_);
decodability_tracker_->OnAssembledFrame(std::move(assembled_frame));
});
}
// Expectations.
MockDecodabilityTrackerEvents decodability_tracker_events_;
NiceMock<MockDecodedFrameIdCallback> decoded_frame_id_cb_;
// Object under test.
std::unique_ptr<DecodabilityTracker> decodability_tracker_
RTC_PT_GUARDED_BY(queue_ptr_);
};
TEST_F(DecodabilityTrackerTest, KeyframeIsDecodable) {
SingleLayerEncodedFrameGenerator generator(env_);
auto keyframe = generator.NextEncodedFrame();
EXPECT_THAT(keyframe->num_references, Eq(0));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(0)));
EXPECT_CALL(decoded_frame_id_cb_, OnDecodedFrameId(0));
OnAssembledFrame(std::move(keyframe));
}
TEST_F(DecodabilityTrackerTest, DeltaFrameIsNotDecodable) {
SingleLayerEncodedFrameGenerator generator(env_);
auto keyframe = generator.NextEncodedFrame();
auto delta_frame = generator.NextEncodedFrame();
EXPECT_THAT(delta_frame->num_references, Eq(1));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(0)))
.Times(0);
EXPECT_CALL(decoded_frame_id_cb_, OnDecodedFrameId(0)).Times(0);
OnAssembledFrame(std::move(delta_frame));
}
TEST_F(DecodabilityTrackerTest, KeyframeAndDeltaFrameAreDecodable) {
SingleLayerEncodedFrameGenerator generator(env_);
auto keyframe = generator.NextEncodedFrame();
auto delta_frame = generator.NextEncodedFrame();
{
InSequence seq;
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(0)));
EXPECT_CALL(decoded_frame_id_cb_, OnDecodedFrameId(0));
OnAssembledFrame(std::move(keyframe));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(1)));
EXPECT_CALL(decoded_frame_id_cb_, OnDecodedFrameId(1));
OnAssembledFrame(std::move(delta_frame));
}
}
TEST_F(DecodabilityTrackerTest, ReorderedKeyframeAndDeltaFrameAreDecodable) {
SingleLayerEncodedFrameGenerator generator(env_);
auto keyframe = generator.NextEncodedFrame();
auto delta_frame = generator.NextEncodedFrame();
{
InSequence seq;
OnAssembledFrame(std::move(delta_frame));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(0)));
EXPECT_CALL(decoded_frame_id_cb_, OnDecodedFrameId(0));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(1)));
EXPECT_CALL(decoded_frame_id_cb_, OnDecodedFrameId(1));
OnAssembledFrame(std::move(keyframe));
}
}
TEST_F(DecodabilityTrackerTest, OneTemporalLayerGoPIsDecodable) {
TemporalLayersEncodedFrameGenerator generator(env_);
auto keyframe = generator.NextEncodedFrame();
auto tl2a = generator.NextEncodedFrame();
auto tl1 = generator.NextEncodedFrame();
auto tl2b = generator.NextEncodedFrame();
{
InSequence seq;
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(0)));
OnAssembledFrame(std::move(keyframe));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(1)));
OnAssembledFrame(std::move(tl2a));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(2)));
OnAssembledFrame(std::move(tl1));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(3)));
OnAssembledFrame(std::move(tl2b));
}
}
TEST_F(DecodabilityTrackerTest, TwoTemporalLayerGoPsAreDecodable) {
TemporalLayersEncodedFrameGenerator generator(env_);
{
InSequence seq;
for (int i = 0; i < 8; ++i) {
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(i)));
OnAssembledFrame(generator.NextEncodedFrame());
}
}
}
// TODO: b/423646186 - Update this test when we handle reordered frames better.
TEST_F(DecodabilityTrackerTest, SkipsOverReorderedTl2A) {
TemporalLayersEncodedFrameGenerator generator(env_);
auto keyframe = generator.NextEncodedFrame();
auto tl2a = generator.NextEncodedFrame();
auto tl1 = generator.NextEncodedFrame();
auto tl2b = generator.NextEncodedFrame();
{
InSequence seq;
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(0)));
OnAssembledFrame(std::move(keyframe));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(2)));
OnAssembledFrame(std::move(tl1));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(1)))
.Times(0);
OnAssembledFrame(std::move(tl2a));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(3)));
OnAssembledFrame(std::move(tl2b));
}
}
TEST_F(DecodabilityTrackerTest, DoesNotSkipOverReorderedTl1) {
TemporalLayersEncodedFrameGenerator generator(env_);
auto keyframe = generator.NextEncodedFrame();
auto tl2a = generator.NextEncodedFrame();
auto tl1 = generator.NextEncodedFrame();
auto tl2b = generator.NextEncodedFrame();
{
InSequence seq;
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(0)));
OnAssembledFrame(std::move(keyframe));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(1)));
OnAssembledFrame(std::move(tl2a));
OnAssembledFrame(std::move(tl2b));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(2)));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(3)));
OnAssembledFrame(std::move(tl1));
}
}
// TODO: b/423646186 - Update this test when we handle reordered frames better.
TEST_F(DecodabilityTrackerTest, SkipsOverReorderedTl2B) {
TemporalLayersEncodedFrameGenerator generator(env_);
auto keyframe = generator.NextEncodedFrame();
auto tl2a = generator.NextEncodedFrame();
auto tl1 = generator.NextEncodedFrame();
auto tl2b = generator.NextEncodedFrame();
auto tl0_next_gop = generator.NextEncodedFrame();
{
InSequence seq;
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(0)));
OnAssembledFrame(std::move(keyframe));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(1)));
OnAssembledFrame(std::move(tl2a));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(2)));
OnAssembledFrame(std::move(tl1));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(4)));
OnAssembledFrame(std::move(tl0_next_gop));
EXPECT_CALL(decodability_tracker_events_,
OnDecodableFrame(EncodedFrameWithId(3)))
.Times(0);
OnAssembledFrame(std::move(tl2b));
}
}
} // namespace
} // namespace webrtc::video_timing_simulator