/* * Copyright (c) 2021 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 "modules/video_coding/frame_buffer3.h" #include #include "api/video/encoded_frame.h" #include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { using ::testing::ElementsAre; using ::testing::Eq; using ::testing::IsEmpty; using ::testing::Matches; MATCHER_P(FrameWithId, id, "") { return Matches(Eq(id))(arg->Id()); } class FakeEncodedFrame : public EncodedFrame { public: int64_t ReceivedTime() const override { return 0; } int64_t RenderTime() const override { return 0; } }; class Builder { public: Builder& Time(uint32_t rtp_timestamp) { rtp_timestamp_ = rtp_timestamp; return *this; } Builder& Id(int64_t frame_id) { frame_id_ = frame_id; return *this; } Builder& AsLast() { last_spatial_layer_ = true; return *this; } Builder& Refs(const std::vector& references) { references_ = references; return *this; } std::unique_ptr Build() { RTC_CHECK_LE(references_.size(), EncodedFrame::kMaxFrameReferences); RTC_CHECK(rtp_timestamp_.has_value()); RTC_CHECK(frame_id_.has_value()); auto frame = std::make_unique(); frame->SetTimestamp(*rtp_timestamp_); frame->SetId(*frame_id_); frame->is_last_spatial_layer = last_spatial_layer_; for (int64_t ref : references_) { frame->references[frame->num_references] = ref; frame->num_references++; } return frame; } private: absl::optional rtp_timestamp_; absl::optional frame_id_; bool last_spatial_layer_ = false; std::vector references_; }; TEST(FrameBuffer3Test, RejectInvalidRefs) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); // Ref must be less than the id of this frame. buffer.InsertFrame(Builder().Time(0).Id(0).Refs({0}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); // Duplicate ids are also invalid. buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1, 1}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); } TEST(FrameBuffer3Test, LastContinuousUpdatesOnInsertedFrames) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); buffer.InsertFrame(Builder().Time(10).Id(1).Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(2)); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); } TEST(FrameBuffer3Test, LastContinuousFrameReordering) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(3)); } TEST(FrameBuffer3Test, LastContinuousTemporalUnit) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).Build()); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); } TEST(FrameBuffer3Test, LastContinuousTemporalUnitReordering) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).Build()); buffer.InsertFrame(Builder().Time(20).Id(3).Refs({1}).Build()); buffer.InsertFrame(Builder().Time(20).Id(4).Refs({2, 3}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(4)); } TEST(FrameBuffer3Test, NextDecodable) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(absl::nullopt)); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); } TEST(FrameBuffer3Test, AdvanceNextDecodableOnExtraction) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).AsLast().Build()); buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(1))); EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(20U)); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(2))); EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(30U)); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(3))); } TEST(FrameBuffer3Test, AdvanceLastDecodableOnExtraction) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); EXPECT_THAT(buffer.LastDecodableTemporalUnitRtpTimestamp(), Eq(10U)); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(1))); EXPECT_THAT(buffer.LastDecodableTemporalUnitRtpTimestamp(), Eq(30U)); } TEST(FrameBuffer3Test, FrameUpdatesNextDecodable) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(20).Id(2).AsLast().Build()); EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(20U)); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); } TEST(FrameBuffer3Test, KeyframeClearsFullBuffer) { FrameBuffer buffer(/*max_frame_slots=*/5, /*max_decode_history=*/10); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); buffer.InsertFrame(Builder().Time(40).Id(4).Refs({3}).AsLast().Build()); buffer.InsertFrame(Builder().Time(50).Id(5).Refs({4}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(5)); // Frame buffer is full buffer.InsertFrame(Builder().Time(60).Id(6).Refs({5}).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(5)); buffer.InsertFrame(Builder().Time(70).Id(7).AsLast().Build()); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(7)); } TEST(FrameBuffer3Test, DropNextDecodableTemporalUnit) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); buffer.ExtractNextDecodableTemporalUnit(); buffer.DropNextDecodableTemporalUnit(); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(3))); } TEST(FrameBuffer3Test, OldFramesAreIgnored) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); buffer.ExtractNextDecodableTemporalUnit(); buffer.ExtractNextDecodableTemporalUnit(); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(3))); } TEST(FrameBuffer3Test, ReturnFullTemporalUnitKSVC) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).Build()); buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).Build()); buffer.InsertFrame(Builder().Time(10).Id(3).Refs({2}).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(1), FrameWithId(2), FrameWithId(3))); buffer.InsertFrame(Builder().Time(20).Id(4).Refs({3}).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(4))); } TEST(FrameBuffer3Test, InterleavedStream) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); buffer.InsertFrame(Builder().Time(40).Id(4).Refs({2}).AsLast().Build()); buffer.InsertFrame(Builder().Time(50).Id(5).Refs({3}).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(1))); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(2))); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(3))); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(4))); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(5))); buffer.InsertFrame(Builder().Time(70).Id(7).Refs({5}).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(7))); buffer.InsertFrame(Builder().Time(60).Id(6).Refs({4}).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); buffer.InsertFrame(Builder().Time(90).Id(9).Refs({7}).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(9))); } TEST(FrameBuffer3Test, LegacyFrameIdJumpBehavior) { { test::ScopedFieldTrials field_trial( "WebRTC-LegacyFrameIdJumpBehavior/Disabled/"); FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(20).Id(3).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(3))); buffer.InsertFrame(Builder().Time(30).Id(2).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); } { // WebRTC-LegacyFrameIdJumpBehavior is disabled by default. FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); buffer.InsertFrame(Builder().Time(20).Id(3).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(3))); buffer.InsertFrame(Builder().Time(30).Id(2).Refs({1}).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); buffer.InsertFrame(Builder().Time(40).Id(1).AsLast().Build()); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(1))); } } TEST(FrameBuffer3Test, TotalNumberOfContinuousTemporalUnits) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(0)); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).Build()); EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); buffer.InsertFrame(Builder().Time(40).Id(4).Refs({2}).Build()); buffer.InsertFrame(Builder().Time(40).Id(5).Refs({3, 4}).AsLast().Build()); EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); // Reordered buffer.InsertFrame(Builder().Time(20).Id(3).Refs({2}).AsLast().Build()); EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(3)); } TEST(FrameBuffer3Test, TotalNumberOfDroppedFrames) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).Build()); buffer.InsertFrame(Builder().Time(20).Id(3).Refs({2}).AsLast().Build()); buffer.InsertFrame(Builder().Time(40).Id(4).Refs({1}).Build()); buffer.InsertFrame(Builder().Time(40).Id(5).Refs({4}).AsLast().Build()); buffer.ExtractNextDecodableTemporalUnit(); EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); buffer.DropNextDecodableTemporalUnit(); EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(2)); buffer.ExtractNextDecodableTemporalUnit(); EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(2)); } } // namespace } // namespace webrtc