/*
 *  Copyright 2017 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/rtp_video_stream_receiver2.h"

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <vector>

#include "api/environment/environment.h"
#include "api/environment/environment_factory.h"
#include "api/frame_transformer_interface.h"
#include "api/make_ref_counted.h"
#include "api/rtp_header_extension_id.h"
#include "api/rtp_headers.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/task_queue/task_queue_base.h"
#include "api/task_queue/task_queue_factory.h"
#include "api/test/mock_frame_transformer.h"
#include "api/transport/rtp/corruption_detection_message.h"
#include "api/transport/rtp/dependency_descriptor.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "api/video/color_space.h"
#include "api/video/corruption_detection/frame_instrumentation_data.h"
#include "api/video/encoded_frame.h"
#include "api/video/video_codec_type.h"
#include "api/video/video_frame_type.h"
#include "api/video/video_timing.h"
#include "call/rtp_packet_sink_interface.h"
#include "call/test/mock_rtp_packet_sink_interface.h"
#include "call/video_receive_stream.h"
#include "common_video/h264/h264_common.h"
#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
#include "media/base/media_constants.h"
#include "modules/include/module_common_types.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/source/corruption_detection_extension.h"
#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h"
#include "modules/rtp_rtcp/source/rtp_format.h"
#include "modules/rtp_rtcp/source/rtp_format_vp9.h"
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h"
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "modules/rtp_rtcp/source/rtp_video_header.h"
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
#include "modules/video_coding/codecs/vp8/include/vp8_globals.h"
#include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
#include "modules/video_coding/nack_requester.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "system_wrappers/include/ntp_time.h"
#include "test/create_test_field_trials.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/mock_transport.h"
#include "test/rtcp_packet_parser.h"
#include "test/time_controller/simulated_time_controller.h"
#include "test/time_controller/simulated_time_controller_impl.h"

namespace webrtc {

namespace {

using ::testing::_;
using ::testing::DoubleNear;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::SizeIs;
using ::testing::Values;

constexpr uint8_t kH264StartCode[] = {0x00, 0x00, 0x00, 0x01};

// Corruption detection metrics for testing.
constexpr double kStd = 1.0;
constexpr int kLumaThreshold = 5;
constexpr int kChormaThreshold = 3;
constexpr int kVp9PayloadType = 99;
constexpr int kNumSamples = 13;
// 8 bits.
constexpr int kMaxSequenceIdx = 127;

std::vector<uint64_t> GetAbsoluteCaptureTimestamps(const EncodedFrame* frame) {
  std::vector<uint64_t> result;
  for (const auto& packet_info : frame->PacketInfos()) {
    if (packet_info.absolute_capture_time()) {
      result.push_back(
          packet_info.absolute_capture_time()->absolute_capture_timestamp);
    }
  }
  return result;
}

RTPVideoHeader GetGenericVideoHeader(VideoFrameType frame_type) {
  RTPVideoHeader video_header;
  video_header.is_first_packet_in_frame = true;
  video_header.is_last_packet_in_frame = true;
  video_header.codec = kVideoCodecGeneric;
  video_header.frame_type = frame_type;
  return video_header;
}

class MockNackSender : public NackSender {
 public:
  MOCK_METHOD(void,
              SendNack,
              (const std::vector<uint16_t>& sequence_numbers,
               bool buffering_allowed),
              (override));
};

class MockKeyFrameRequestSender : public KeyFrameRequestSender {
 public:
  MOCK_METHOD(void, RequestKeyFrame, (), (override));
};

class MockOnCompleteFrameCallback
    : public RtpVideoStreamReceiver2::OnCompleteFrameCallback {
 public:
  MOCK_METHOD(void, DoOnCompleteFrame, (EncodedFrame*), ());
  MOCK_METHOD(void, DoOnCompleteFrameFailNullptr, (EncodedFrame*), ());
  MOCK_METHOD(void, DoOnCompleteFrameFailLength, (EncodedFrame*), ());
  MOCK_METHOD(void, DoOnCompleteFrameFailBitstream, (EncodedFrame*), ());
  void OnCompleteFrame(std::unique_ptr<EncodedFrame> frame) override {
    if (frame == nullptr) {
      DoOnCompleteFrameFailNullptr(nullptr);
      return;
    }
    EXPECT_EQ(buffer_.size(), frame->size());
    if (buffer_.size() != frame->size()) {
      DoOnCompleteFrameFailLength(frame.get());
      return;
    }
    if (memcmp(buffer_.data(), frame->data(), buffer_.size()) != 0) {
      DoOnCompleteFrameFailBitstream(frame.get());
      return;
    }
    DoOnCompleteFrame(frame.get());
  }

  void ClearExpectedBitstream() { buffer_.clear(); }

  void AppendExpectedBitstream(std::span<const uint8_t> data) {
    buffer_.insert(buffer_.end(), data.begin(), data.end());
  }
  std::vector<uint8_t> buffer_;
};

constexpr uint32_t kSsrc = 111;
constexpr int kPayloadType = 100;
constexpr int kRedPayloadType = 125;

std::unique_ptr<RtpPacketReceived> CreateRtpPacketReceived() {
  constexpr uint16_t kSequenceNumber = 222;
  auto packet = std::make_unique<RtpPacketReceived>();
  packet->SetSsrc(kSsrc);
  packet->SetSequenceNumber(kSequenceNumber);
  packet->SetPayloadType(kPayloadType);
  return packet;
}

MATCHER_P(SamePacketAs, other, "") {
  return arg.Ssrc() == other.Ssrc() &&
         arg.SequenceNumber() == other.SequenceNumber();
}

}  // namespace

class RtpVideoStreamReceiver2Test : public ::testing::Test,
                                    public RtpPacketSinkInterface {
 public:
  RtpVideoStreamReceiver2Test() : RtpVideoStreamReceiver2Test("") {}
  explicit RtpVideoStreamReceiver2Test(std::string field_trials)
      : time_controller_(Timestamp::Millis(100)),
        env_(CreateEnvironment(CreateTestFieldTrialsPtr(field_trials),
                               time_controller_.GetClock(),
                               time_controller_.GetTaskQueueFactory(),
                               &log_)),
        task_queue_(time_controller_.GetTaskQueueFactory()->CreateTaskQueue(
            "RtpVideoStreamReceiver2Test",
            TaskQueueFactory::Priority::kNormal)),
        task_queue_setter_(task_queue_.get()),
        config_(CreateConfig()) {
    rtp_receive_statistics_ = ReceiveStatistics::Create(&env_.clock());
    rtp_video_stream_receiver_ = std::make_unique<RtpVideoStreamReceiver2>(
        env_, TaskQueueBase::Current(), &mock_transport_, nullptr, nullptr,
        &config_, rtp_receive_statistics_.get(), nullptr, nullptr,
        &nack_periodic_processor_, &mock_on_complete_frame_callback_, nullptr,
        nullptr);
    rtp_video_stream_receiver_->AddReceiveCodec(kPayloadType,
                                                kVideoCodecGeneric, {},
                                                /*raw_payload=*/false);
    ON_CALL(mock_transport_, SendRtcp)
        .WillByDefault(
            [this](std::span<const uint8_t> packet, ::testing::Unused) {
              return rtcp_packet_parser_.Parse(packet);
            });
  }

  RTPVideoHeader GetDefaultH264VideoHeader() {
    RTPVideoHeader video_header;
    video_header.codec = kVideoCodecH264;
    video_header.video_type_header.emplace<RTPVideoHeaderH264>();
    return video_header;
  }

  // TODO(Johan): refactor h264_sps_pps_tracker_unittests.cc to avoid duplicate
  // code.
  void AddSps(RTPVideoHeader* video_header,
              uint8_t sps_id,
              CopyOnWriteBuffer* data) {
    NaluInfo info;
    info.type = H264::NaluType::kSps;
    info.sps_id = sps_id;
    info.pps_id = -1;
    data->AppendData<uint8_t, 2>({H264::NaluType::kSps, sps_id});
    auto& h264 = std::get<RTPVideoHeaderH264>(video_header->video_type_header);
    h264.nalus.push_back(info);
  }

  void AddPps(RTPVideoHeader* video_header,
              uint8_t sps_id,
              uint8_t pps_id,
              CopyOnWriteBuffer* data) {
    NaluInfo info;
    info.type = H264::NaluType::kPps;
    info.sps_id = sps_id;
    info.pps_id = pps_id;
    data->AppendData<uint8_t, 2>({H264::NaluType::kPps, pps_id});
    auto& h264 = std::get<RTPVideoHeaderH264>(video_header->video_type_header);
    h264.nalus.push_back(info);
  }

  void AddIdr(RTPVideoHeader* video_header, int pps_id) {
    NaluInfo info;
    info.type = H264::NaluType::kIdr;
    info.sps_id = -1;
    info.pps_id = pps_id;
    auto& h264 = std::get<RTPVideoHeaderH264>(video_header->video_type_header);
    h264.nalus.push_back(info);
  }

  void OnRtpPacket(const RtpPacketReceived& packet) override {
    if (test_packet_sink_)
      test_packet_sink_->OnRtpPacket(packet);
  }

 protected:
  VideoReceiveStreamInterface::Config CreateConfig() {
    VideoReceiveStreamInterface::Config config(nullptr);
    config.rtp.remote_ssrc = 1111;
    config.rtp.red_payload_type = kRedPayloadType;
    config.rtp.packet_sink_ = this;
    return config;
  }

  GlobalSimulatedTimeController time_controller_;
  MockRtcEventLog log_;
  Environment env_;
  std::unique_ptr<TaskQueueBase, TaskQueueDeleter> task_queue_;
  TokenTaskQueue::CurrentTaskQueueSetter task_queue_setter_;

  VideoReceiveStreamInterface::Config config_;
  NackPeriodicProcessor nack_periodic_processor_;
  test::RtcpPacketParser rtcp_packet_parser_;
  MockTransport mock_transport_;
  MockOnCompleteFrameCallback mock_on_complete_frame_callback_;
  std::unique_ptr<ReceiveStatistics> rtp_receive_statistics_;
  std::unique_ptr<RtpVideoStreamReceiver2> rtp_video_stream_receiver_;
  RtpPacketSinkInterface* test_packet_sink_ = nullptr;
};

TEST_F(RtpVideoStreamReceiver2Test, CacheColorSpaceFromLastPacketOfKeyframe) {
  // Test that color space is cached from the last packet of a key frame and
  // that it's not reset by padding packets without color space.
  const ColorSpace kColorSpace(
      ColorSpace::PrimaryID::kFILM, ColorSpace::TransferID::kBT2020_12,
      ColorSpace::MatrixID::kBT2020_NCL, ColorSpace::RangeID::kFull);
  const std::vector<uint8_t> kKeyFramePayload = {0, 1, 2, 3, 4, 5,
                                                 6, 7, 8, 9, 10};
  const std::vector<uint8_t> kDeltaFramePayload = {0, 1, 2, 3, 4};

  // Anonymous helper class that generates received packets.
  class {
   public:
    void SetPayload(const std::vector<uint8_t>& payload,
                    VideoFrameType video_frame_type) {
      video_frame_type_ = video_frame_type;
      RtpPacketizer::PayloadSizeLimits pay_load_size_limits;
      // Reduce max payload length to make sure the key frame generates two
      // packets.
      pay_load_size_limits.max_payload_len = 8;
      RTPVideoHeaderVP9 rtp_video_header_vp9;
      rtp_video_header_vp9.InitRTPVideoHeaderVP9();
      rtp_video_header_vp9.inter_pic_predicted =
          (video_frame_type == VideoFrameType::kVideoFrameDelta);
      rtp_packetizer_ = std::make_unique<RtpPacketizerVp9>(
          payload, pay_load_size_limits, rtp_video_header_vp9);
    }

    size_t NumPackets() { return rtp_packetizer_->NumPackets(); }
    void SetColorSpace(const ColorSpace& color_space) {
      color_space_ = color_space;
    }

    RtpPacketReceived NextPacket() {
      RtpHeaderExtensionMap extension_map;
      extension_map.Register<ColorSpaceExtension>(RtpHeaderExtensionId(1));
      RtpPacketToSend packet_to_send(&extension_map);
      packet_to_send.SetSequenceNumber(sequence_number_++);
      packet_to_send.SetSsrc(kSsrc);
      packet_to_send.SetPayloadType(kVp9PayloadType);
      bool include_color_space =
          (rtp_packetizer_->NumPackets() == 1u &&
           video_frame_type_ == VideoFrameType::kVideoFrameKey);
      if (include_color_space) {
        EXPECT_TRUE(
            packet_to_send.SetExtension<ColorSpaceExtension>(color_space_));
      }
      rtp_packetizer_->NextPacket(&packet_to_send);

      RtpPacketReceived received_packet(&extension_map);
      received_packet.Parse(packet_to_send.data(), packet_to_send.size());
      return received_packet;
    }

   private:
    uint16_t sequence_number_ = 0;
    VideoFrameType video_frame_type_;
    ColorSpace color_space_;
    std::unique_ptr<RtpPacketizer> rtp_packetizer_;
  } received_packet_generator;
  received_packet_generator.SetColorSpace(kColorSpace);

  // Prepare the receiver for VP9.
  CodecParameterMap codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kVp9PayloadType, kVideoCodecVP9,
                                              codec_params,
                                              /*raw_payload=*/false);

  // Generate key frame packets.
  received_packet_generator.SetPayload(kKeyFramePayload,
                                       VideoFrameType::kVideoFrameKey);
  EXPECT_EQ(received_packet_generator.NumPackets(), 2u);
  RtpPacketReceived key_frame_packet1 = received_packet_generator.NextPacket();
  RtpPacketReceived key_frame_packet2 = received_packet_generator.NextPacket();

  // Generate delta frame packet.
  received_packet_generator.SetPayload(kDeltaFramePayload,
                                       VideoFrameType::kVideoFrameDelta);
  EXPECT_EQ(received_packet_generator.NumPackets(), 1u);
  RtpPacketReceived delta_frame_packet = received_packet_generator.NextPacket();

  rtp_video_stream_receiver_->StartReceive();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kKeyFramePayload);

  // Send the key frame and expect a callback with color space information.
  EXPECT_FALSE(key_frame_packet1.GetExtension<ColorSpaceExtension>());
  EXPECT_TRUE(key_frame_packet2.GetExtension<ColorSpaceExtension>());
  rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet1);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
      .WillOnce([kColorSpace](EncodedFrame* frame) {
        ASSERT_TRUE(frame->EncodedImage().ColorSpace());
        EXPECT_EQ(*frame->EncodedImage().ColorSpace(), kColorSpace);
      });
  rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet2);
  // Resend the first key frame packet to simulate padding for example.
  rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet1);

  mock_on_complete_frame_callback_.ClearExpectedBitstream();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kDeltaFramePayload);

  // Expect delta frame to have color space set even though color space not
  // included in the RTP packet.
  EXPECT_FALSE(delta_frame_packet.GetExtension<ColorSpaceExtension>());
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
      .WillOnce([kColorSpace](EncodedFrame* frame) {
        ASSERT_TRUE(frame->EncodedImage().ColorSpace());
        EXPECT_EQ(*frame->EncodedImage().ColorSpace(), kColorSpace);
      });
  rtp_video_stream_receiver_->OnRtpPacket(delta_frame_packet);
}

class ReceivedPacketGenerator {
 public:
  ReceivedPacketGenerator() = default;

  void SetPayload(const std::vector<uint8_t>& payload,
                  VideoFrameType video_frame_type) {
    video_frame_type_ = video_frame_type;
    RtpPacketizer::PayloadSizeLimits pay_load_size_limits;
    RTPVideoHeaderVP9 rtp_video_header_vp9;
    rtp_video_header_vp9.InitRTPVideoHeaderVP9();
    rtp_video_header_vp9.inter_pic_predicted =
        (video_frame_type == VideoFrameType::kVideoFrameDelta);
    rtp_packetizer_ = std::make_unique<RtpPacketizerVp9>(
        payload, pay_load_size_limits, rtp_video_header_vp9);
  }

  size_t NumPackets() { return rtp_packetizer_->NumPackets(); }

  void SetCorruptionDetectionHeader(const CorruptionDetectionMessage& msg) {
    corruption_detection_msg_ = msg;
  }

  RtpPacketReceived NextPacket(bool include_corruption_header) {
    RtpHeaderExtensionMap extension_map;
    extension_map.Register<CorruptionDetectionExtension>(
        RtpHeaderExtensionId(/*id=*/1));
    RtpPacketToSend packet_to_send(&extension_map);
    packet_to_send.SetSequenceNumber(sequence_number_++);
    packet_to_send.SetSsrc(kSsrc);
    packet_to_send.SetPayloadType(kVp9PayloadType);
    packet_to_send.SetTimestamp(timestamp_++);
    if (include_corruption_header) {
      EXPECT_TRUE(packet_to_send.SetExtension<CorruptionDetectionExtension>(
          corruption_detection_msg_));
    }
    rtp_packetizer_->NextPacket(&packet_to_send);

    RtpPacketReceived received_packet(&extension_map);
    received_packet.Parse(packet_to_send.data(), packet_to_send.size());
    return received_packet;
  }

 private:
  uint16_t sequence_number_ = 0;
  uint32_t timestamp_ = 0;
  VideoFrameType video_frame_type_;
  CorruptionDetectionMessage corruption_detection_msg_;
  std::unique_ptr<RtpPacketizer> rtp_packetizer_;
};

std::optional<CorruptionDetectionMessage> GetCorruptionDetectionMessage(
    int sequence_idx,
    bool interpret_as_MSB) {
  CorruptionDetectionMessage::Builder builder;
  builder.WithSequenceIndex(sequence_idx);
  builder.WithInterpretSequenceIndexAsMostSignificantBits(interpret_as_MSB);
  builder.WithStdDev(kStd);
  builder.WithLumaErrorThreshold(kLumaThreshold);
  builder.WithChromaErrorThreshold(kChormaThreshold);

  double sample_value = 0.5;
  std::vector<double> sample_values;
  for (int i = 0; i < kNumSamples; i++) {
    sample_values.push_back(sample_value);
    sample_value += 0.5;
  }
  builder.WithSampleValues(sample_values);

  std::optional<CorruptionDetectionMessage> kCorruptionDetectionMsg =
      builder.Build();
  return kCorruptionDetectionMsg;
}

TEST_F(RtpVideoStreamReceiver2Test,
       FrameInstrumentationDataGetsPopulatedLSBIncreasedCorrectly) {
  const std::vector<uint8_t> kKeyFramePayload = {0, 1, 2, 3, 4};
  const std::vector<uint8_t> kDeltaFramePayload = {5, 6, 7, 8, 9};

  // Prepare the receiver for VP9.
  CodecParameterMap codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kVp9PayloadType, kVideoCodecVP9,
                                              codec_params,
                                              /*raw_payload=*/false);

  ReceivedPacketGenerator received_packet_generator;
  std::optional<CorruptionDetectionMessage> corruption_detection_msg =
      GetCorruptionDetectionMessage(
          /*sequence_idx=*/0, /*interpret_as_MSB*/ true);
  ASSERT_TRUE(corruption_detection_msg.has_value());
  received_packet_generator.SetCorruptionDetectionHeader(
      *corruption_detection_msg);

  // Generate key frame packets.
  received_packet_generator.SetPayload(kKeyFramePayload,
                                       VideoFrameType::kVideoFrameKey);
  // Have corruption header on the key frame.
  RtpPacketReceived key_frame_packet =
      received_packet_generator.NextPacket(/*include_corruption_header=*/true);

  // Generate delta frame packet.
  received_packet_generator.SetPayload(kDeltaFramePayload,
                                       VideoFrameType::kVideoFrameDelta);

  // Second message sets the LSB instead of MSB, increasing the counter by the
  // number of samples in the first message.
  corruption_detection_msg = GetCorruptionDetectionMessage(
      /*sequence_idx=*/kNumSamples, /*interpret_as_MSB*/ false);
  ASSERT_TRUE(corruption_detection_msg.has_value());
  received_packet_generator.SetCorruptionDetectionHeader(
      *corruption_detection_msg);
  RtpPacketReceived delta_frame_packet =
      received_packet_generator.NextPacket(/*include_corruption_header=*/true);

  rtp_video_stream_receiver_->StartReceive();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kKeyFramePayload);

  EXPECT_TRUE(key_frame_packet.GetExtension<CorruptionDetectionExtension>());
  std::unique_ptr<EncodedFrame> key_encoded_frame;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
      .WillOnce([&](EncodedFrame* encoded_frame) {
        key_encoded_frame = std::make_unique<EncodedFrame>(*encoded_frame);
      });
  rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet);
  ASSERT_TRUE(key_encoded_frame != nullptr);
  std::optional<FrameInstrumentationData> frame_inst_data_key_frame =
      key_encoded_frame->CodecSpecific()->frame_instrumentation_data;
  ASSERT_TRUE(frame_inst_data_key_frame.has_value());
  EXPECT_EQ(frame_inst_data_key_frame->sequence_index(), 0);
  EXPECT_THAT(frame_inst_data_key_frame->std_dev(), DoubleNear(kStd, 0.1));
  EXPECT_EQ(frame_inst_data_key_frame->luma_error_threshold(), kLumaThreshold);
  EXPECT_EQ(frame_inst_data_key_frame->chroma_error_threshold(),
            kChormaThreshold);

  mock_on_complete_frame_callback_.ClearExpectedBitstream();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kDeltaFramePayload);

  EXPECT_TRUE(delta_frame_packet.GetExtension<CorruptionDetectionExtension>());
  std::unique_ptr<EncodedFrame> delta_encoded_frame;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
      .WillOnce([&](EncodedFrame* encoded_frame) {
        delta_encoded_frame = std::make_unique<EncodedFrame>(*encoded_frame);
      });
  rtp_video_stream_receiver_->OnRtpPacket(delta_frame_packet);
  ASSERT_TRUE(delta_encoded_frame != nullptr);
  ASSERT_TRUE(delta_encoded_frame->CodecSpecific()
                  ->frame_instrumentation_data.has_value());

  std::optional<FrameInstrumentationData> frame_inst_data_delta_frame =
      delta_encoded_frame->CodecSpecific()->frame_instrumentation_data;
  ASSERT_TRUE(frame_inst_data_delta_frame.has_value());
  EXPECT_EQ(frame_inst_data_delta_frame->sequence_index(), kNumSamples);
  EXPECT_THAT(frame_inst_data_delta_frame->std_dev(), DoubleNear(kStd, 0.1));
  EXPECT_EQ(frame_inst_data_delta_frame->luma_error_threshold(),
            kLumaThreshold);
  EXPECT_EQ(frame_inst_data_delta_frame->chroma_error_threshold(),
            kChormaThreshold);
}

TEST_F(RtpVideoStreamReceiver2Test,
       FrameInstrumentationDataGetsPopulatedForSyncMessage) {
  const std::vector<uint8_t> kKeyFramePayload = {0, 1, 2, 3, 4};

  // Prepare the receiver for VP9.
  CodecParameterMap codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kVp9PayloadType, kVideoCodecVP9,
                                              codec_params,
                                              /*raw_payload=*/false);

  ReceivedPacketGenerator received_packet_generator;
  // Create a sync-only message.
  CorruptionDetectionMessage::Builder builder;
  builder.WithSequenceIndex(0).WithInterpretSequenceIndexAsMostSignificantBits(
      true);
  std::optional<CorruptionDetectionMessage> corruption_detection_msg =
      builder.Build();
  ASSERT_TRUE(corruption_detection_msg.has_value());
  received_packet_generator.SetCorruptionDetectionHeader(
      *corruption_detection_msg);

  // Generate key frame packets.
  received_packet_generator.SetPayload(kKeyFramePayload,
                                       VideoFrameType::kVideoFrameKey);
  // Have corruption header on the key frame.
  RtpPacketReceived key_frame_packet =
      received_packet_generator.NextPacket(/*include_corruption_header=*/true);

  rtp_video_stream_receiver_->StartReceive();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kKeyFramePayload);

  EXPECT_TRUE(key_frame_packet.GetExtension<CorruptionDetectionExtension>());
  std::unique_ptr<EncodedFrame> key_encoded_frame;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
      .WillOnce([&](EncodedFrame* encoded_frame) {
        key_encoded_frame = std::make_unique<EncodedFrame>(*encoded_frame);
      });
  rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet);
  ASSERT_TRUE(key_encoded_frame != nullptr);
  std::optional<FrameInstrumentationData> frame_inst_data_key_frame =
      key_encoded_frame->CodecSpecific()->frame_instrumentation_data;
  ASSERT_TRUE(frame_inst_data_key_frame.has_value());
  EXPECT_EQ(frame_inst_data_key_frame->sequence_index(), 0);
  EXPECT_TRUE(frame_inst_data_key_frame->is_sync_only());
  EXPECT_EQ(frame_inst_data_key_frame->std_dev(), 0.0);
  EXPECT_EQ(frame_inst_data_key_frame->luma_error_threshold(), 0);
  EXPECT_EQ(frame_inst_data_key_frame->chroma_error_threshold(), 0);
  EXPECT_TRUE(frame_inst_data_key_frame->sample_values().empty());
}

TEST_F(RtpVideoStreamReceiver2Test,
       FrameInstrumentationDataGetsPopulatedMSBIncreasedCorrectly) {
  const std::vector<uint8_t> kKeyFramePayload = {0, 1, 2, 3, 4};
  const std::vector<uint8_t> kDeltaFramePayload = {5, 6, 7, 8, 9};

  // Prepare the receiver for VP9.
  CodecParameterMap codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kVp9PayloadType, kVideoCodecVP9,
                                              codec_params,
                                              /*raw_payload=*/false);

  ReceivedPacketGenerator received_packet_generator;
  std::optional<CorruptionDetectionMessage> corruption_detection_msg =
      GetCorruptionDetectionMessage(
          /*sequence_idx=*/0, /*interpret_as_MSB*/ true);
  ASSERT_TRUE(corruption_detection_msg.has_value());
  received_packet_generator.SetCorruptionDetectionHeader(
      *corruption_detection_msg);

  // Generate key frame packets.
  received_packet_generator.SetPayload(kKeyFramePayload,
                                       VideoFrameType::kVideoFrameKey);
  // Have corruption header on the key frame.
  RtpPacketReceived key_frame_packet =
      received_packet_generator.NextPacket(/*include_corruption_header=*/true);
  rtp_video_stream_receiver_->StartReceive();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kKeyFramePayload);
  rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet);

  RtpPacketReceived delta_frame_packet;
  int sequence_idx = 0;
  for (int i = 0; i < 10; i++) {
    sequence_idx += kNumSamples;
    if (sequence_idx > kMaxSequenceIdx) {
      sequence_idx = sequence_idx - (kMaxSequenceIdx + 1);
    }
    corruption_detection_msg = GetCorruptionDetectionMessage(
        /*sequence_idx=*/sequence_idx, /*interpret_as_MSB*/ false);
    ASSERT_TRUE(corruption_detection_msg.has_value());
    received_packet_generator.SetCorruptionDetectionHeader(
        *corruption_detection_msg);

    // Generate delta frame packet.
    received_packet_generator.SetPayload(kDeltaFramePayload,
                                         VideoFrameType::kVideoFrameDelta);
    // Send corruption header with each frame.
    delta_frame_packet = received_packet_generator.NextPacket(
        /*include_corruption_header=*/true);

    mock_on_complete_frame_callback_.ClearExpectedBitstream();
    mock_on_complete_frame_callback_.AppendExpectedBitstream(
        kDeltaFramePayload);

    EXPECT_TRUE(
        delta_frame_packet.GetExtension<CorruptionDetectionExtension>());
    std::unique_ptr<EncodedFrame> delta_encoded_frame;
    EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
        .WillOnce([&](EncodedFrame* encoded_frame) {
          delta_encoded_frame = std::make_unique<EncodedFrame>(*encoded_frame);
        });
    rtp_video_stream_receiver_->OnRtpPacket(delta_frame_packet);
    ASSERT_TRUE(delta_encoded_frame != nullptr);
    std::optional<FrameInstrumentationData> frame_inst_data =
        delta_encoded_frame->CodecSpecific()->frame_instrumentation_data;
    ASSERT_TRUE(frame_inst_data.has_value());
    if (frame_inst_data->sequence_index() < (kMaxSequenceIdx + 1)) {
      EXPECT_EQ(frame_inst_data->sequence_index(), sequence_idx);
    } else {
      EXPECT_EQ(frame_inst_data->sequence_index(),
                sequence_idx + kMaxSequenceIdx + 1);
    }
  }
}

// TODO: bugs.webrtc.org/358039777 - Add tests for corruption detection when we
// have scalability.

TEST_F(RtpVideoStreamReceiver2Test, GenericKeyFrame) {
  RtpPacketReceived rtp_packet;
  CopyOnWriteBuffer data({'1', '2', '3', '4'});
  rtp_packet.SetPayloadType(kPayloadType);
  rtp_packet.SetSequenceNumber(1);
  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);
}

TEST_F(RtpVideoStreamReceiver2Test, SetProtectionPayloadTypes) {
  EXPECT_NE(rtp_video_stream_receiver_->red_payload_type(), 104);
  EXPECT_NE(rtp_video_stream_receiver_->ulpfec_payload_type(), 107);

  rtp_video_stream_receiver_->SetProtectionPayloadTypes(104, 107);

  EXPECT_EQ(rtp_video_stream_receiver_->red_payload_type(), 104);
  EXPECT_EQ(rtp_video_stream_receiver_->ulpfec_payload_type(), 107);
}

TEST_F(RtpVideoStreamReceiver2Test, PacketInfoIsPropagatedIntoVideoFrames) {
  constexpr uint64_t kAbsoluteCaptureTimestamp = 12;
  constexpr RtpHeaderExtensionId kId0(1);

  RtpHeaderExtensionMap extension_map;
  extension_map.Register<AbsoluteCaptureTimeExtension>(kId0);
  RtpPacketReceived rtp_packet(&extension_map);
  rtp_packet.SetPayloadType(kPayloadType);
  CopyOnWriteBuffer data({'1', '2', '3', '4'});
  rtp_packet.SetSequenceNumber(1);
  rtp_packet.SetTimestamp(1);
  rtp_packet.SetSsrc(kSsrc);
  rtp_packet.SetExtension<AbsoluteCaptureTimeExtension>(AbsoluteCaptureTime{
      .absolute_capture_timestamp = kAbsoluteCaptureTimestamp,
      .estimated_capture_clock_offset = std::nullopt});

  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
      .WillOnce([kAbsoluteCaptureTimestamp](EncodedFrame* frame) {
        EXPECT_THAT(GetAbsoluteCaptureTimestamps(frame),
                    ElementsAre(kAbsoluteCaptureTimestamp));
      });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);
}

TEST_F(RtpVideoStreamReceiver2Test,
       MissingAbsoluteCaptureTimeIsFilledWithExtrapolatedValue) {
  constexpr uint64_t kAbsoluteCaptureTimestamp = 12;
  constexpr RtpHeaderExtensionId kId0(1);

  RtpHeaderExtensionMap extension_map;
  extension_map.Register<AbsoluteCaptureTimeExtension>(kId0);
  RtpPacketReceived rtp_packet(&extension_map);
  rtp_packet.SetPayloadType(kPayloadType);

  CopyOnWriteBuffer data({'1', '2', '3', '4'});
  uint16_t sequence_number = 1;
  uint32_t rtp_timestamp = 1;
  rtp_packet.SetSequenceNumber(sequence_number);
  rtp_packet.SetTimestamp(rtp_timestamp);
  rtp_packet.SetSsrc(kSsrc);
  rtp_packet.SetExtension<AbsoluteCaptureTimeExtension>(AbsoluteCaptureTime{
      .absolute_capture_timestamp = kAbsoluteCaptureTimestamp,
      .estimated_capture_clock_offset = std::nullopt});

  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);

  // Rtp packet without absolute capture time.
  rtp_packet = RtpPacketReceived(&extension_map);
  rtp_packet.SetPayloadType(kPayloadType);
  rtp_packet.SetSequenceNumber(++sequence_number);
  rtp_packet.SetTimestamp(++rtp_timestamp);
  rtp_packet.SetSsrc(kSsrc);

  // There is no absolute capture time in the second packet.
  // Expect rtp video stream receiver to extrapolate it for the resulting video
  // frame using absolute capture time from the previous packet.
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
      .WillOnce([](EncodedFrame* frame) {
        EXPECT_THAT(GetAbsoluteCaptureTimestamps(frame), SizeIs(1));
      });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);
}

// Integration test (full receiver path): reproduces the post-congestion video
// recovery freeze and verifies recovery. After prolonged subscriber-side loss
// the publisher's RTP sequence number advances past the 16-bit half-range while
// the receiver is stalled. When media resumes, the fresh keyframe lands at a
// sequence number that appears "behind" the pre-stall GoP in modular
// arithmetic; the stale GoP then shadows reference lookups and every
// post-recovery delta frame is silently dropped ("has no GoP"), so video stays
// frozen for 60-150s even though complete frames are arriving. This drives
// packets through the whole receiver (packet buffer -> frame assembly ->
// reference finder) and asserts frames keep flowing after the jump.
// issues.webrtc.org/516639936.
TEST_F(RtpVideoStreamReceiver2Test, RecoversAfterLargeSequenceNumberJump) {
  const CopyOnWriteBuffer data("1234");
  // Every frame carries the same payload so the mock's bitstream check passes
  // for all of them and we can assert on the delivered-frame count.
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);

  auto inject = [&](uint16_t seq, uint32_t rtp_timestamp, bool keyframe) {
    RtpPacketReceived rtp_packet;
    rtp_packet.SetPayloadType(kPayloadType);
    rtp_packet.SetSequenceNumber(seq);
    rtp_packet.SetTimestamp(rtp_timestamp);
    rtp_packet.SetSsrc(kSsrc);
    RTPVideoHeader video_header = GetGenericVideoHeader(
        keyframe ? VideoFrameType::kVideoFrameKey
                 : VideoFrameType::kVideoFrameDelta);
    rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
        data, rtp_packet, video_header);
  };

  // 4 pre-stall frames (1 key + 3 delta) and 141 post-recovery frames
  // (1 key + 140 delta) must all be delivered. The freeze needs >100
  // post-recovery deltas to manifest: the finder's routine
  // (last_seq_num - 100) cleanup eventually erases the new keyframe's GoP,
  // leaving only the stale wrapped GoP, after which every further delta is
  // dropped ("has no GoP"). Before the fix the delivered count was ~105.
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(145);

  // Pre-stall stream: keyframe followed by a few deltas. Start high so the
  // post-recovery sequence number wraps past 0. This reproduces the real-world
  // condition: the stale pre-stall GoP then sorts numerically *after* the new
  // keyframe and survives the reference finder's routine lower_bound cleanup,
  // so only the stale-GoP removal can clear it.
  uint16_t seq = 60'000;
  uint32_t ts = 90'000;
  inject(seq, ts, /*keyframe=*/true);
  for (int i = 0; i < 3; ++i) {
    inject(++seq, ts += 3'000, /*keyframe=*/false);
  }

  // Prolonged congestion: when media resumes the sequence number has advanced
  // past the half-range (+40000, wrapping past 0 to ~34467) and the timestamp
  // has advanced with it. A fresh (PLI-triggered) keyframe arrives, followed
  // by delta frames.
  seq += 40'000;
  ts += 40'000u * 3'000u;
  inject(seq, ts, /*keyframe=*/true);
  for (int i = 0; i < 140; ++i) {
    inject(++seq, ts += 3'000, /*keyframe=*/false);
  }
}

TEST_F(RtpVideoStreamReceiver2Test,
       NoInfiniteRecursionOnEncapsulatedRedPacket) {
  const std::vector<uint8_t> data({
      0x80,              // RTP version.
      kRedPayloadType,   // Payload type.
      0, 0, 0, 0, 0, 0,  // Don't care.
      0, 0, 0x4, 0x57,   // SSRC
      kRedPayloadType,   // RED header.
      0, 0, 0, 0, 0      // Don't care.
  });
  RtpPacketReceived packet;
  EXPECT_TRUE(packet.Parse(data.data(), data.size()));
  rtp_video_stream_receiver_->StartReceive();
  rtp_video_stream_receiver_->OnRtpPacket(packet);
}

TEST_F(RtpVideoStreamReceiver2Test,
       DropsPacketWithRedPayloadTypeAndEmptyPayload) {
  config_.rtp.red_payload_type = kRedPayloadType;
  SetUp();  // re-create rtp_video_stream_receiver with red payload type.
  // clang-format off
  const uint8_t data[] = {
      0x80,              // RTP version.
      kRedPayloadType,   // Payload type.
      0, 0, 0, 0, 0, 0,  // Don't care.
      0, 0, 0x4, 0x57,   // SSRC
      // Empty rtp payload.
  };
  // clang-format on
  RtpPacketReceived packet;
  // Manually convert to CopyOnWriteBuffer to be sure capacity == size
  // and asan bot can catch read buffer overflow.
  EXPECT_TRUE(packet.Parse(CopyOnWriteBuffer(data)));
  rtp_video_stream_receiver_->StartReceive();
  rtp_video_stream_receiver_->OnRtpPacket(packet);
  // Expect asan doesn't find anything.
}

TEST_F(RtpVideoStreamReceiver2Test, GenericKeyFrameBitstreamError) {
  RtpPacketReceived rtp_packet;
  rtp_packet.SetPayloadType(kPayloadType);
  CopyOnWriteBuffer data({'1', '2', '3', '4'});
  rtp_packet.SetSequenceNumber(1);
  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
  constexpr uint8_t expected_bitsteam[] = {1, 2, 3, 0xff};
  mock_on_complete_frame_callback_.AppendExpectedBitstream(expected_bitsteam);
  EXPECT_CALL(mock_on_complete_frame_callback_,
              DoOnCompleteFrameFailBitstream(_));
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);
}

class RtpVideoStreamReceiver2TestH264
    : public RtpVideoStreamReceiver2Test,
      public ::testing::WithParamInterface<std::string> {
 protected:
  RtpVideoStreamReceiver2TestH264() : RtpVideoStreamReceiver2Test(GetParam()) {}
};

INSTANTIATE_TEST_SUITE_P(SpsPpsIdrIsKeyframeAndH26xPacketBuffer,
                         RtpVideoStreamReceiver2TestH264,
                         Values("",
                                "WebRTC-SpsPpsIdrIsH264Keyframe/Enabled/",
                                "WebRTC-Video-H26xPacketBuffer/Enabled/",
                                "WebRTC-SpsPpsIdrIsH264Keyframe/Enabled/"
                                "WebRTC-Video-H26xPacketBuffer/Enabled/"));

TEST_P(RtpVideoStreamReceiver2TestH264, InBandSpsPps) {
  constexpr int kH264PayloadType = 98;
  CodecParameterMap codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kH264PayloadType, kVideoCodecH264,
                                              codec_params,
                                              /*raw_payload=*/false);
  rtp_video_stream_receiver_->StartReceive();

  CopyOnWriteBuffer sps_data;
  RtpPacketReceived rtp_packet;
  RTPVideoHeader sps_video_header = GetDefaultH264VideoHeader();
  AddSps(&sps_video_header, 0, &sps_data);
  rtp_packet.SetSequenceNumber(0);
  rtp_packet.SetPayloadType(kH264PayloadType);
  sps_video_header.is_first_packet_in_frame = true;
  sps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(sps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      sps_data, rtp_packet, sps_video_header);

  CopyOnWriteBuffer pps_data;
  RTPVideoHeader pps_video_header = GetDefaultH264VideoHeader();
  AddPps(&pps_video_header, 0, 1, &pps_data);
  rtp_packet.SetSequenceNumber(1);
  rtp_packet.SetPayloadType(kH264PayloadType);
  pps_video_header.is_first_packet_in_frame = true;
  pps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(pps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      pps_data, rtp_packet, pps_video_header);

  RTPVideoHeader idr_video_header = GetDefaultH264VideoHeader();
  AddIdr(&idr_video_header, 1);
  rtp_packet.SetSequenceNumber(2);
  rtp_packet.SetPayloadType(kH264PayloadType);
  rtp_packet.SetMarker(true);
  idr_video_header.is_first_packet_in_frame = true;
  idr_video_header.is_last_packet_in_frame = true;
  idr_video_header.frame_type = VideoFrameType::kVideoFrameKey;
  const uint8_t idr[] = {0x65, 1, 2, 3};
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(idr);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(idr), rtp_packet, idr_video_header);
}

TEST_P(RtpVideoStreamReceiver2TestH264, OutOfBandFmtpSpsPps) {
  constexpr int kH264PayloadType = 99;
  CodecParameterMap codec_params;
  // Example parameter sets from https://tools.ietf.org/html/rfc3984#section-8.2
  // .
  codec_params.emplace(kH264FmtpSpropParameterSets, "Z0IACpZTBYmI,aMljiA==");
  rtp_video_stream_receiver_->AddReceiveCodec(kH264PayloadType, kVideoCodecH264,
                                              codec_params,
                                              /*raw_payload=*/false);
  rtp_video_stream_receiver_->StartReceive();
  const uint8_t binary_sps[] = {0x67, 0x42, 0x00, 0x0a, 0x96,
                                0x53, 0x05, 0x89, 0x88};
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(binary_sps);
  const uint8_t binary_pps[] = {0x68, 0xc9, 0x63, 0x88};
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(binary_pps);

  RtpPacketReceived rtp_packet;
  RTPVideoHeader video_header = GetDefaultH264VideoHeader();
  AddIdr(&video_header, 0);
  rtp_packet.SetPayloadType(kH264PayloadType);
  rtp_packet.SetSequenceNumber(2);
  rtp_packet.SetMarker(true);
  video_header.is_first_packet_in_frame = true;
  video_header.is_last_packet_in_frame = true;
  video_header.codec = kVideoCodecH264;
  video_header.frame_type = VideoFrameType::kVideoFrameKey;
  CopyOnWriteBuffer data({'1', '2', '3'});
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);
  // IDR frames without SPS/PPS are not returned by
  // |H26xPacketBuffer.InsertPacket| until SPS and PPS are received when
  // WebRTC-SpsPpsIdrIsH264Keyframe is enabled.
  if (!env_.field_trials().IsEnabled("WebRTC-SpsPpsIdrIsH264Keyframe") ||
      !env_.field_trials().IsEnabled("WebRTC-Video-H26xPacketBuffer")) {
    EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  }
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);
}

TEST_P(RtpVideoStreamReceiver2TestH264, ForceSpsPpsIdrIsKeyframe) {
  CodecParameterMap codec_params;
  // Forcing can be done either with field trial or codec_params.
  if (!env_.field_trials().IsEnabled("WebRTC-SpsPpsIdrIsH264Keyframe")) {
    codec_params.emplace(kH264FmtpSpsPpsIdrInKeyframe, "");
  }
  rtp_video_stream_receiver_->AddReceiveCodec(kPayloadType, kVideoCodecH264,
                                              codec_params,
                                              /*raw_payload=*/false);
  rtp_video_stream_receiver_->StartReceive();
  CopyOnWriteBuffer sps_data;
  RtpPacketReceived rtp_packet;
  RTPVideoHeader sps_video_header = GetDefaultH264VideoHeader();
  AddSps(&sps_video_header, 0, &sps_data);
  rtp_packet.SetSequenceNumber(0);
  rtp_packet.SetPayloadType(kPayloadType);
  sps_video_header.is_first_packet_in_frame = true;
  sps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(sps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      sps_data, rtp_packet, sps_video_header);

  CopyOnWriteBuffer pps_data;
  RTPVideoHeader pps_video_header = GetDefaultH264VideoHeader();
  AddPps(&pps_video_header, 0, 1, &pps_data);
  rtp_packet.SetSequenceNumber(1);
  pps_video_header.is_first_packet_in_frame = true;
  pps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(pps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      pps_data, rtp_packet, pps_video_header);

  RTPVideoHeader idr_video_header = GetDefaultH264VideoHeader();
  AddIdr(&idr_video_header, 1);
  rtp_packet.SetSequenceNumber(2);
  rtp_packet.SetMarker(true);
  idr_video_header.is_first_packet_in_frame = true;
  idr_video_header.is_last_packet_in_frame = true;
  idr_video_header.frame_type = VideoFrameType::kVideoFrameKey;
  const uint8_t idr[] = {0x65, 1, 2, 3};
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(idr);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_TRUE(frame->is_keyframe()); });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(idr), rtp_packet, idr_video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(idr);
  rtp_packet.SetSequenceNumber(3);
  // IDR frames without SPS/PPS are not returned by
  // |H26xPacketBuffer.InsertPacket| until SPS and PPS are received, while
  // |PacketBuffer| returns it as a delta frame.
  if (env_.field_trials().IsEnabled("WebRTC-Video-H26xPacketBuffer")) {
    EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(0);
  } else {
    EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
        .WillOnce(
            [&](EncodedFrame* frame) { EXPECT_FALSE(frame->is_keyframe()); });
  }
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(idr), rtp_packet, idr_video_header);
}

class RtpVideoStreamReceiver2TestPadding
    : public RtpVideoStreamReceiver2Test,
      public ::testing::WithParamInterface<std::string> {
 protected:
  RtpVideoStreamReceiver2TestPadding()
      : RtpVideoStreamReceiver2Test(GetParam()) {}
};

INSTANTIATE_TEST_SUITE_P(PaddingInMediaStreamAndH26xPacketBuffer,
                         RtpVideoStreamReceiver2TestPadding,
                         Values("", "WebRTC-Video-H26xPacketBuffer/Enabled/"));

TEST_P(RtpVideoStreamReceiver2TestPadding, PaddingInMediaStream) {
  RtpPacketReceived rtp_packet;
  RTPVideoHeader video_header = GetDefaultH264VideoHeader();
  CopyOnWriteBuffer data({'1', '2', '3'});
  rtp_packet.SetPayloadType(kPayloadType);
  rtp_packet.SetSequenceNumber(2);
  video_header.is_first_packet_in_frame = true;
  video_header.is_last_packet_in_frame = true;
  video_header.codec = kVideoCodecGeneric;
  video_header.frame_type = VideoFrameType::kVideoFrameKey;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);

  rtp_packet.SetSequenceNumber(3);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting({}, rtp_packet,
                                                              video_header);

  rtp_packet.SetSequenceNumber(4);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  video_header.frame_type = VideoFrameType::kVideoFrameDelta;
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);

  rtp_packet.SetSequenceNumber(6);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  rtp_packet.SetSequenceNumber(5);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting({}, rtp_packet,
                                                              video_header);
}

TEST_P(RtpVideoStreamReceiver2TestPadding, EmptyPaddingInMediaStream) {
  constexpr int kH264PayloadType = 98;
  RtpPacketReceived rtp_packet_idr, rtp_packet_padding, rtp_packet_slice;
  // Example Stap-A packet with SPS, PPS, and IDR.
  std::vector<uint8_t> raw_rtp_with_sps_pps_idr{
      0x80, 0xe2, 0x13, 0xba, 0x87, 0xa0, 0x0a, 0x8a, 0x00, 0x00, 0x6f,
      0x00, 0x78, 0x00, 0x19, 0x67, 0x42, 0x40, 0x29, 0x95, 0xb8, 0x78,
      0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00,
      0x03, 0x00, 0x78, 0x8d, 0xa1, 0xc3, 0x2e, 0x00, 0x04, 0x68, 0xce,
      0x3c, 0x80, 0x00, 0x07, 0x05, 0x88, 0x80, 0x03, 0x53, 0xff, 0xff};
  // Example Empty padding packet next Idr.
  std::vector<uint8_t> raw_rtp_empty_padding{
      0x80, 0x62, 0x13, 0xbb, 0x87, 0xa0, 0x21, 0x0a, 0x00, 0x00, 0x6f, 0x00};
  // Example Single NALU packet with slice.
  std::vector<uint8_t> raw_rtp_slice(
      {0x80, 0xE2, 0x13, 0xbc, 0x87, 0xa0, 0x21, 0x0a, 0x00, 0x00, 0x6f,
       0x00, 0x01, 0x9a, 0x02, 0x3f, 0xc1, 0x48, 0x9a, 0xeb, 0xea, 0xff});

  // Example EncodedFrame with SPS, PPS, and IDR.
  std::vector<uint8_t> expect_frame_with_sps_pps_idr{
      0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x40, 0x29, 0x95, 0xb8, 0x78, 0x2f,
      0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00,
      0x78, 0x8d, 0xa1, 0xc3, 0x2e, 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x3c,
      0x80, 0x00, 0x00, 0x00, 0x01, 0x05, 0x88, 0x80, 0x03, 0x53, 0xff, 0xff};
  // Example EncodedFrame with slice.
  std::vector<uint8_t> expect_frame_with_slice{0x00, 0x00, 0x00, 0x01, 0x01,
                                               0x9a, 0x02, 0x3f, 0xc1, 0x48,
                                               0x9a, 0xeb, 0xea, 0xff};
  rtp_packet_idr.Parse(raw_rtp_with_sps_pps_idr.data(),
                       raw_rtp_with_sps_pps_idr.size());
  rtp_packet_padding.Parse(raw_rtp_empty_padding.data(),
                           raw_rtp_empty_padding.size());
  rtp_packet_slice.Parse(raw_rtp_slice.data(), raw_rtp_slice.size());

  // Prepare the receiver for H264.
  CodecParameterMap codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kH264PayloadType, kVideoCodecH264,
                                              codec_params, false);
  rtp_video_stream_receiver_->StartReceive();

  // Expect IDR frame.
  mock_on_complete_frame_callback_.AppendExpectedBitstream(
      expect_frame_with_sps_pps_idr);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));

  rtp_video_stream_receiver_->OnRtpPacket(rtp_packet_idr);

  rtp_video_stream_receiver_->OnRtpPacket(rtp_packet_padding);

  // Expect single NALU frame.
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(
      expect_frame_with_slice);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  rtp_video_stream_receiver_->OnRtpPacket(rtp_packet_slice);
}

TEST_F(RtpVideoStreamReceiver2Test, RequestKeyframeIfFirstFrameIsDelta) {
  RtpPacketReceived rtp_packet;
  rtp_packet.SetPayloadType(kPayloadType);
  CopyOnWriteBuffer data({'1', '2', '3', '4'});
  rtp_packet.SetSequenceNumber(1);
  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameDelta);

  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);
  EXPECT_THAT(rtcp_packet_parser_.pli()->num_packets(), Eq(1));
}

TEST_F(RtpVideoStreamReceiver2Test, RequestKeyframeWhenPacketBufferGetsFull) {
  constexpr int kPacketBufferMaxSize = 2048;

  RtpPacketReceived rtp_packet;
  rtp_packet.SetPayloadType(kPayloadType);
  CopyOnWriteBuffer data({'1', '2', '3', '4'});
  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameDelta);
  // Incomplete frames so that the packet buffer is filling up.
  video_header.is_last_packet_in_frame = false;
  uint16_t start_sequence_number = 1234;
  rtp_packet.SetSequenceNumber(start_sequence_number);
  while (rtp_packet.SequenceNumber() - start_sequence_number <
         kPacketBufferMaxSize) {
    rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
        data, rtp_packet, video_header);
    rtp_packet.SetSequenceNumber(rtp_packet.SequenceNumber() + 2);
  }

  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(data, rtp_packet,
                                                              video_header);
  EXPECT_THAT(rtcp_packet_parser_.pli()->num_packets(), Eq(1));
}

TEST_F(RtpVideoStreamReceiver2Test, SinkGetsRtpNotifications) {
  rtp_video_stream_receiver_->StartReceive();

  MockRtpPacketSink test_sink;
  test_packet_sink_ = &test_sink;

  auto rtp_packet = CreateRtpPacketReceived();
  EXPECT_CALL(test_sink, OnRtpPacket(SamePacketAs(*rtp_packet)));

  rtp_video_stream_receiver_->OnRtpPacket(*rtp_packet);

  // Test tear-down.
  rtp_video_stream_receiver_->StopReceive();
  test_packet_sink_ = nullptr;
}

TEST_F(RtpVideoStreamReceiver2Test, NonStartedStreamGetsNoRtpCallbacks) {
  // Explicitly showing that the stream is not in the `started` state,
  // regardless of whether streams start out `started` or `stopped`.
  rtp_video_stream_receiver_->StopReceive();

  MockRtpPacketSink test_sink;
  test_packet_sink_ = &test_sink;

  auto rtp_packet = CreateRtpPacketReceived();
  EXPECT_CALL(test_sink, OnRtpPacket(_)).Times(0);

  rtp_video_stream_receiver_->OnRtpPacket(*rtp_packet);

  test_packet_sink_ = nullptr;
}

TEST_F(RtpVideoStreamReceiver2Test, ParseGenericDescriptorOnePacket) {
  const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
  const int kSpatialIndex = 1;

  rtp_video_stream_receiver_->StartReceive();

  RtpHeaderExtensionMap extension_map;
  extension_map.Register<RtpGenericFrameDescriptorExtension00>(
      RtpHeaderExtensionId(5));
  RtpPacketReceived rtp_packet(&extension_map);
  rtp_packet.SetPayloadType(kPayloadType);

  RtpGenericFrameDescriptor generic_descriptor;
  generic_descriptor.SetFirstPacketInSubFrame(true);
  generic_descriptor.SetLastPacketInSubFrame(true);
  generic_descriptor.SetFrameId(100);
  generic_descriptor.SetSpatialLayersBitmask(1 << kSpatialIndex);
  generic_descriptor.AddFrameDependencyDiff(90);
  generic_descriptor.AddFrameDependencyDiff(80);
  ASSERT_TRUE(rtp_packet.SetExtension<RtpGenericFrameDescriptorExtension00>(
      generic_descriptor));

  rtp_packet.SetPayload(data);
  // The first byte is the header, so we ignore the first byte of `data`.
  mock_on_complete_frame_callback_.AppendExpectedBitstream(
      std::span(data).subspan(1));

  rtp_packet.SetMarker(true);
  rtp_packet.SetPayloadType(kPayloadType);
  rtp_packet.SetSequenceNumber(1);

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce([kSpatialIndex](EncodedFrame* frame) {
        EXPECT_EQ(frame->num_references, 2U);
        EXPECT_EQ(frame->references[0], frame->Id() - 90);
        EXPECT_EQ(frame->references[1], frame->Id() - 80);
        EXPECT_EQ(frame->SpatialIndex(), kSpatialIndex);
        EXPECT_THAT(frame->PacketInfos(), SizeIs(1));
      });

  rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
}

TEST_F(RtpVideoStreamReceiver2Test, ParseGenericDescriptorTwoPackets) {
  const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
  const int kSpatialIndex = 1;

  rtp_video_stream_receiver_->StartReceive();

  RtpHeaderExtensionMap extension_map;
  extension_map.Register<RtpGenericFrameDescriptorExtension00>(
      RtpHeaderExtensionId(5));
  RtpPacketReceived first_packet(&extension_map);

  RtpGenericFrameDescriptor first_packet_descriptor;
  first_packet_descriptor.SetFirstPacketInSubFrame(true);
  first_packet_descriptor.SetLastPacketInSubFrame(false);
  first_packet_descriptor.SetFrameId(100);
  first_packet_descriptor.SetSpatialLayersBitmask(1 << kSpatialIndex);
  first_packet_descriptor.SetResolution(480, 360);
  ASSERT_TRUE(first_packet.SetExtension<RtpGenericFrameDescriptorExtension00>(
      first_packet_descriptor));

  first_packet.SetPayload(data);
  // The first byte is the header, so we ignore the first byte of `data`.
  mock_on_complete_frame_callback_.AppendExpectedBitstream(
      std::span(data).subspan(1));

  first_packet.SetPayloadType(kPayloadType);
  first_packet.SetSequenceNumber(1);
  rtp_video_stream_receiver_->OnRtpPacket(first_packet);

  RtpPacketReceived second_packet(&extension_map);
  RtpGenericFrameDescriptor second_packet_descriptor;
  second_packet_descriptor.SetFirstPacketInSubFrame(false);
  second_packet_descriptor.SetLastPacketInSubFrame(true);
  ASSERT_TRUE(second_packet.SetExtension<RtpGenericFrameDescriptorExtension00>(
      second_packet_descriptor));

  second_packet.SetMarker(true);
  second_packet.SetPayloadType(kPayloadType);
  second_packet.SetSequenceNumber(2);

  second_packet.SetPayload(data);
  // The first byte is the header, so we ignore the first byte of `data`.
  mock_on_complete_frame_callback_.AppendExpectedBitstream(
      std::span(data).subspan(1));

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce([kSpatialIndex](EncodedFrame* frame) {
        EXPECT_EQ(frame->num_references, 0U);
        EXPECT_EQ(frame->SpatialIndex(), kSpatialIndex);
        EXPECT_EQ(frame->EncodedImage()._encodedWidth, 480u);
        EXPECT_EQ(frame->EncodedImage()._encodedHeight, 360u);
        EXPECT_THAT(frame->PacketInfos(), SizeIs(2));
      });

  rtp_video_stream_receiver_->OnRtpPacket(second_packet);
}

TEST_F(RtpVideoStreamReceiver2Test, ParseGenericDescriptorRawPayload) {
  const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
  const int kRawPayloadType = 123;

  rtp_video_stream_receiver_->AddReceiveCodec(kRawPayloadType,
                                              kVideoCodecGeneric, {},
                                              /*raw_payload=*/true);
  rtp_video_stream_receiver_->StartReceive();

  RtpHeaderExtensionMap extension_map;
  extension_map.Register<RtpGenericFrameDescriptorExtension00>(
      RtpHeaderExtensionId(5));
  RtpPacketReceived rtp_packet(&extension_map);

  RtpGenericFrameDescriptor generic_descriptor;
  generic_descriptor.SetFirstPacketInSubFrame(true);
  generic_descriptor.SetLastPacketInSubFrame(true);
  ASSERT_TRUE(rtp_packet.SetExtension<RtpGenericFrameDescriptorExtension00>(
      generic_descriptor));

  rtp_packet.SetPayload(data);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);

  rtp_packet.SetMarker(true);
  rtp_packet.SetPayloadType(kRawPayloadType);
  rtp_packet.SetSequenceNumber(1);

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame);
  rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
}

TEST_F(RtpVideoStreamReceiver2Test, SetRawPayloadTypesUpdatesDepacketizer) {
  const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
  const int kTestRawPayloadType = 123;

  rtp_video_stream_receiver_->AddReceiveCodec(
      kTestRawPayloadType, kVideoCodecGeneric, {}, /*raw_payload=*/false);
  rtp_video_stream_receiver_->StartReceive();

  rtp_video_stream_receiver_->SetRawPayloadTypes({kTestRawPayloadType});

  RtpHeaderExtensionMap extension_map;
  extension_map.Register<RtpGenericFrameDescriptorExtension00>(
      RtpHeaderExtensionId(5));
  RtpPacketReceived rtp_packet(&extension_map);

  RtpGenericFrameDescriptor generic_descriptor;
  generic_descriptor.SetFirstPacketInSubFrame(true);
  generic_descriptor.SetLastPacketInSubFrame(true);
  ASSERT_TRUE(rtp_packet.SetExtension<RtpGenericFrameDescriptorExtension00>(
      generic_descriptor));

  rtp_packet.SetPayload(data);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);

  rtp_packet.SetMarker(true);
  rtp_packet.SetPayloadType(kTestRawPayloadType);
  rtp_packet.SetSequenceNumber(1);

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame);
  rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
}

TEST_F(RtpVideoStreamReceiver2Test, UnwrapsFrameId) {
  const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
  const int kPayloadTypeGeneric = 123;

  rtp_video_stream_receiver_->AddReceiveCodec(kPayloadTypeGeneric,
                                              kVideoCodecGeneric, {},
                                              /*raw_payload=*/true);
  rtp_video_stream_receiver_->StartReceive();
  RtpHeaderExtensionMap extension_map;
  extension_map.Register<RtpGenericFrameDescriptorExtension00>(
      RtpHeaderExtensionId(5));

  uint16_t rtp_sequence_number = 1;
  auto inject_packet = [&](uint16_t wrapped_frame_id) {
    RtpPacketReceived rtp_packet(&extension_map);

    RtpGenericFrameDescriptor generic_descriptor;
    generic_descriptor.SetFirstPacketInSubFrame(true);
    generic_descriptor.SetLastPacketInSubFrame(true);
    generic_descriptor.SetFrameId(wrapped_frame_id);
    ASSERT_TRUE(rtp_packet.SetExtension<RtpGenericFrameDescriptorExtension00>(
        generic_descriptor));

    rtp_packet.SetPayload(data);
    mock_on_complete_frame_callback_.ClearExpectedBitstream();
    mock_on_complete_frame_callback_.AppendExpectedBitstream(data);
    rtp_packet.SetMarker(true);
    rtp_packet.SetPayloadType(kPayloadTypeGeneric);
    rtp_packet.SetSequenceNumber(++rtp_sequence_number);
    rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
  };

  int64_t first_picture_id;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce([&](EncodedFrame* frame) { first_picture_id = frame->Id(); });
  inject_packet(/*wrapped_frame_id=*/0xffff);

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce([&](EncodedFrame* frame) {
        EXPECT_EQ(frame->Id() - first_picture_id, 3);
      });
  inject_packet(/*wrapped_frame_id=*/0x0002);
}

class RtpVideoStreamReceiver2DependencyDescriptorTest
    : public RtpVideoStreamReceiver2Test {
 public:
  RtpVideoStreamReceiver2DependencyDescriptorTest() {
    rtp_video_stream_receiver_->AddReceiveCodec(payload_type_,
                                                kVideoCodecGeneric, {},
                                                /*raw_payload=*/true);
    extension_map_.Register<RtpDependencyDescriptorExtension>(
        RtpHeaderExtensionId(7));
    rtp_video_stream_receiver_->StartReceive();
  }

  // Returns some valid structure for the DependencyDescriptors.
  // First template of that structure always fit for a key frame.
  static FrameDependencyStructure CreateStreamStructure() {
    FrameDependencyStructure stream_structure;
    stream_structure.num_decode_targets = 1;
    stream_structure.templates = {
        FrameDependencyTemplate().Dtis("S"),
        FrameDependencyTemplate().Dtis("S").FrameDiffs({1}),
    };
    return stream_structure;
  }

  void InjectPacketWith(const FrameDependencyStructure& stream_structure,
                        const DependencyDescriptor& dependency_descriptor) {
    const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
    RtpPacketReceived rtp_packet(&extension_map_);
    ASSERT_TRUE(rtp_packet.SetExtension<RtpDependencyDescriptorExtension>(
        stream_structure, dependency_descriptor));
    rtp_packet.SetPayload(data);
    mock_on_complete_frame_callback_.ClearExpectedBitstream();
    mock_on_complete_frame_callback_.AppendExpectedBitstream(data);
    rtp_packet.SetMarker(true);
    rtp_packet.SetPayloadType(payload_type_);
    rtp_packet.SetSequenceNumber(++rtp_sequence_number_);
    rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
  }

 private:
  const int payload_type_ = 123;
  RtpHeaderExtensionMap extension_map_;
  uint16_t rtp_sequence_number_ = 321;
};

TEST_F(RtpVideoStreamReceiver2DependencyDescriptorTest, UnwrapsFrameId) {
  FrameDependencyStructure stream_structure = CreateStreamStructure();

  DependencyDescriptor keyframe_descriptor;
  keyframe_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure);
  keyframe_descriptor.frame_dependencies = stream_structure.templates[0];
  keyframe_descriptor.frame_number = 0xfff0;
  // DependencyDescriptor doesn't support reordering delta frame before
  // keyframe. Thus feed a key frame first, then test reodered delta frames.
  int64_t first_picture_id;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce([&](EncodedFrame* frame) { first_picture_id = frame->Id(); });
  InjectPacketWith(stream_structure, keyframe_descriptor);

  DependencyDescriptor deltaframe1_descriptor;
  deltaframe1_descriptor.frame_dependencies = stream_structure.templates[1];
  deltaframe1_descriptor.frame_number = 0xfffe;

  DependencyDescriptor deltaframe2_descriptor;
  deltaframe2_descriptor.frame_dependencies = stream_structure.templates[1];
  deltaframe2_descriptor.frame_number = 0x0002;

  // Parser should unwrap frame ids correctly even if packets were reordered by
  // the network.
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce([&](EncodedFrame* frame) {
        // 0x0002 - 0xfff0
        EXPECT_EQ(frame->Id() - first_picture_id, 18);
      })
      .WillOnce([&](EncodedFrame* frame) {
        // 0xfffe - 0xfff0
        EXPECT_EQ(frame->Id() - first_picture_id, 14);
      });
  InjectPacketWith(stream_structure, deltaframe2_descriptor);
  InjectPacketWith(stream_structure, deltaframe1_descriptor);
}

TEST_F(RtpVideoStreamReceiver2DependencyDescriptorTest,
       DropsLateDeltaFramePacketWithDependencyDescriptorExtension) {
  FrameDependencyStructure stream_structure1 = CreateStreamStructure();
  FrameDependencyStructure stream_structure2 = CreateStreamStructure();
  // Make sure template ids for these two structures do not collide:
  // adjust structure_id (that is also used as template id offset).
  stream_structure1.structure_id = 13;
  stream_structure2.structure_id =
      stream_structure1.structure_id + stream_structure1.templates.size();

  DependencyDescriptor keyframe1_descriptor;
  keyframe1_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure1);
  keyframe1_descriptor.frame_dependencies = stream_structure1.templates[0];
  keyframe1_descriptor.frame_number = 1;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame);
  InjectPacketWith(stream_structure1, keyframe1_descriptor);

  // Pass in 2nd key frame with different structure.
  DependencyDescriptor keyframe2_descriptor;
  keyframe2_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure2);
  keyframe2_descriptor.frame_dependencies = stream_structure2.templates[0];
  keyframe2_descriptor.frame_number = 3;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame);
  InjectPacketWith(stream_structure2, keyframe2_descriptor);

  // Pass in late delta frame that uses structure of the 1st key frame.
  DependencyDescriptor deltaframe_descriptor;
  deltaframe_descriptor.frame_dependencies = stream_structure1.templates[0];
  deltaframe_descriptor.frame_number = 2;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(0);
  InjectPacketWith(stream_structure1, deltaframe_descriptor);
}

TEST_F(RtpVideoStreamReceiver2DependencyDescriptorTest,
       DropsLateKeyFramePacketWithDependencyDescriptorExtension) {
  FrameDependencyStructure stream_structure1 = CreateStreamStructure();
  FrameDependencyStructure stream_structure2 = CreateStreamStructure();
  // Make sure template ids for these two structures do not collide:
  // adjust structure_id (that is also used as template id offset).
  stream_structure1.structure_id = 13;
  stream_structure2.structure_id =
      stream_structure1.structure_id + stream_structure1.templates.size();

  DependencyDescriptor keyframe1_descriptor;
  keyframe1_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure1);
  keyframe1_descriptor.frame_dependencies = stream_structure1.templates[0];
  keyframe1_descriptor.frame_number = 1;

  DependencyDescriptor keyframe2_descriptor;
  keyframe2_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure2);
  keyframe2_descriptor.frame_dependencies = stream_structure2.templates[0];
  keyframe2_descriptor.frame_number = 3;

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 3); });
  InjectPacketWith(stream_structure2, keyframe2_descriptor);
  InjectPacketWith(stream_structure1, keyframe1_descriptor);

  // Pass in delta frame that uses structure of the 2nd key frame. Late key
  // frame shouldn't block it.
  DependencyDescriptor deltaframe_descriptor;
  deltaframe_descriptor.frame_dependencies = stream_structure2.templates[0];
  deltaframe_descriptor.frame_number = 4;
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 4); });
  InjectPacketWith(stream_structure2, deltaframe_descriptor);
}

TEST_F(RtpVideoStreamReceiver2DependencyDescriptorTest,
       RequestKeyframeIfInitialKeyframePacketIsLost) {
  FrameDependencyStructure stream_structure = CreateStreamStructure();

  DependencyDescriptor keyframe_descriptor_without_structure;
  keyframe_descriptor_without_structure.frame_dependencies =
      stream_structure.templates[0];
  keyframe_descriptor_without_structure.frame_number = 0;

  InjectPacketWith(stream_structure, keyframe_descriptor_without_structure);

  // Not enough time since last keyframe request
  time_controller_.AdvanceTime(TimeDelta::Millis(500));
  InjectPacketWith(stream_structure, keyframe_descriptor_without_structure);
  EXPECT_THAT(rtcp_packet_parser_.pli()->num_packets(), Eq(1));

  time_controller_.AdvanceTime(TimeDelta::Millis(501));
  InjectPacketWith(stream_structure, keyframe_descriptor_without_structure);
  EXPECT_THAT(rtcp_packet_parser_.pli()->num_packets(), Eq(2));
}

TEST_F(RtpVideoStreamReceiver2DependencyDescriptorTest,
       RetryStashedPacketsAfterReceivingScalabilityStructure) {
  FrameDependencyStructure stream_structure1 = CreateStreamStructure();
  FrameDependencyStructure stream_structure2 = CreateStreamStructure();
  // Make sure template ids for these two structures do not collide:
  // adjust structure_id (that is also used as template id offset).
  stream_structure1.structure_id = 13;
  stream_structure2.structure_id =
      stream_structure1.structure_id + stream_structure1.templates.size();

  DependencyDescriptor keyframe1_descriptor;
  keyframe1_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure1);
  keyframe1_descriptor.frame_dependencies = stream_structure1.templates[0];
  keyframe1_descriptor.frame_number = 1;

  DependencyDescriptor keyframe2_descriptor;
  keyframe2_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure2);
  keyframe2_descriptor.frame_dependencies = stream_structure2.templates[0];
  keyframe2_descriptor.frame_number = 2;

  DependencyDescriptor deltaframe_descriptor;
  deltaframe_descriptor.frame_dependencies = stream_structure2.templates[1];
  deltaframe_descriptor.frame_number = 3;

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 1); })
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 2); })
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 3); });

  InjectPacketWith(stream_structure1, keyframe1_descriptor);
  InjectPacketWith(stream_structure2, deltaframe_descriptor);
  InjectPacketWith(stream_structure2, keyframe2_descriptor);
}

TEST_F(RtpVideoStreamReceiver2DependencyDescriptorTest,
       RetryStashedPacketsAfterReceivingEarlierScalabilityStructure) {
  FrameDependencyStructure stream_structure1 = CreateStreamStructure();
  FrameDependencyStructure stream_structure2 = CreateStreamStructure();
  FrameDependencyStructure stream_structure3 = CreateStreamStructure();
  // Make sure template ids for these two structures do not collide:
  // adjust structure_id (that is also used as template id offset).
  stream_structure1.structure_id = 13;
  stream_structure2.structure_id =
      stream_structure1.structure_id + stream_structure1.templates.size();
  stream_structure3.structure_id =
      stream_structure2.structure_id + stream_structure2.templates.size();

  DependencyDescriptor keyframe1_descriptor;
  keyframe1_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure1);
  keyframe1_descriptor.frame_dependencies = stream_structure1.templates[0];
  keyframe1_descriptor.frame_number = 1;

  DependencyDescriptor keyframe2_descriptor;
  keyframe2_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure2);
  keyframe2_descriptor.frame_dependencies = stream_structure2.templates[0];
  keyframe2_descriptor.frame_number = 2;

  DependencyDescriptor deltaframe2_descriptor;
  deltaframe2_descriptor.frame_dependencies = stream_structure2.templates[1];
  deltaframe2_descriptor.frame_number = 3;

  DependencyDescriptor keyframe3_descriptor;
  keyframe3_descriptor.attached_structure =
      std::make_unique<FrameDependencyStructure>(stream_structure3);
  keyframe3_descriptor.frame_dependencies = stream_structure3.templates[0];
  keyframe3_descriptor.frame_number = 4;

  DependencyDescriptor deltaframe3_descriptor;
  deltaframe3_descriptor.frame_dependencies = stream_structure3.templates[1];
  deltaframe3_descriptor.frame_number = 5;

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 1); })
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 2); })
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 3); })
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 4); })
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_EQ(frame->Id() & 0xFFFF, 5); });

  InjectPacketWith(stream_structure1, keyframe1_descriptor);
  InjectPacketWith(stream_structure2, deltaframe2_descriptor);
  InjectPacketWith(stream_structure3, deltaframe3_descriptor);
  InjectPacketWith(stream_structure2, keyframe2_descriptor);
  InjectPacketWith(stream_structure3, keyframe3_descriptor);
}

TEST_F(RtpVideoStreamReceiver2Test, TransformFrame) {
  scoped_refptr<MockFrameTransformer> mock_frame_transformer =
      make_ref_counted<testing::NiceMock<MockFrameTransformer>>();
  EXPECT_CALL(*mock_frame_transformer,
              RegisterTransformedFrameSinkCallback(_, config_.rtp.remote_ssrc));
  auto receiver = std::make_unique<RtpVideoStreamReceiver2>(
      env_, TaskQueueBase::Current(), &mock_transport_, nullptr, nullptr,
      &config_, rtp_receive_statistics_.get(), nullptr, nullptr,
      &nack_periodic_processor_, &mock_on_complete_frame_callback_, nullptr,
      mock_frame_transformer);
  receiver->AddReceiveCodec(kPayloadType, kVideoCodecGeneric, {},
                            /*raw_payload=*/false);

  RtpPacketReceived rtp_packet;
  rtp_packet.SetPayloadType(kPayloadType);
  CopyOnWriteBuffer data({'1', '2', '3', '4'});
  rtp_packet.SetSequenceNumber(1);
  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);
  std::unique_ptr<TransformableFrameInterface> transformed_frame;
  EXPECT_CALL(*mock_frame_transformer, Transform(_))
      .WillOnce(testing::SaveArgByMove<0>(&transformed_frame));
  receiver->OnReceivedPayloadDataForTesting(data, rtp_packet, video_header);
  EXPECT_TRUE(transformed_frame->ReceiveTime().has_value());
  EXPECT_FALSE(transformed_frame->CaptureTime());
  EXPECT_FALSE(transformed_frame->SenderCaptureTimeOffset());

  EXPECT_CALL(*mock_frame_transformer,
              UnregisterTransformedFrameSinkCallback(config_.rtp.remote_ssrc));
  receiver = nullptr;
}

TEST_F(RtpVideoStreamReceiver2Test, TransformFrameWithAbsoluteCaptureTime) {
  scoped_refptr<MockFrameTransformer> mock_frame_transformer =
      make_ref_counted<testing::NiceMock<MockFrameTransformer>>();
  EXPECT_CALL(*mock_frame_transformer,
              RegisterTransformedFrameSinkCallback(_, config_.rtp.remote_ssrc));
  auto receiver = std::make_unique<RtpVideoStreamReceiver2>(
      env_, TaskQueueBase::Current(), &mock_transport_, nullptr, nullptr,
      &config_, rtp_receive_statistics_.get(), nullptr, nullptr,
      &nack_periodic_processor_, &mock_on_complete_frame_callback_, nullptr,
      mock_frame_transformer);
  receiver->AddReceiveCodec(kPayloadType, kVideoCodecGeneric, {},
                            /*raw_payload=*/false);

  constexpr RtpHeaderExtensionId kId0(1);
  RtpHeaderExtensionMap extension_map;
  extension_map.Register<AbsoluteCaptureTimeExtension>(kId0);
  RtpPacketReceived rtp_packet(&extension_map);
  rtp_packet.SetPayloadType(kPayloadType);
  CopyOnWriteBuffer data({'1', '2', '3', '4'});
  rtp_packet.SetSequenceNumber(1);

  Timestamp capture_time = Timestamp::Millis(1234);
  TimeDelta sender_capture_time_offset = TimeDelta::Millis(56);
  AbsoluteCaptureTime absolute_capture_time = {
      .absolute_capture_timestamp = Int64MsToUQ32x32(capture_time.ms()),
      .estimated_capture_clock_offset =
          Int64MsToUQ32x32(sender_capture_time_offset.ms())};
  rtp_packet.SetExtension<AbsoluteCaptureTimeExtension>(absolute_capture_time);

  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(data);

  std::unique_ptr<TransformableFrameInterface> transformed_frame;
  EXPECT_CALL(*mock_frame_transformer, Transform(_))
      .WillOnce(testing::SaveArgByMove<0>(&transformed_frame));
  receiver->OnReceivedPayloadDataForTesting(data, rtp_packet, video_header);
  EXPECT_TRUE(transformed_frame->ReceiveTime().has_value());
  EXPECT_EQ(transformed_frame->CaptureTime(), capture_time);
  EXPECT_EQ(transformed_frame->SenderCaptureTimeOffset(),
            sender_capture_time_offset);

  EXPECT_CALL(*mock_frame_transformer,
              UnregisterTransformedFrameSinkCallback(config_.rtp.remote_ssrc));
  receiver = nullptr;
}

// Test default behavior and when playout delay is overridden by field trial.
VideoPlayoutDelay TransmittedPlayoutDelay() {
  return {TimeDelta::Millis(100), TimeDelta::Millis(200)};
}
VideoPlayoutDelay ForcedPlayoutDelay() {
  return {TimeDelta::Millis(70), TimeDelta::Millis(90)};
}
struct PlayoutDelayOptions {
  std::string field_trial;
  VideoPlayoutDelay expected_delay;
};
PlayoutDelayOptions DefaultBehavior() {
  return {.field_trial = "", .expected_delay = TransmittedPlayoutDelay()};
}
PlayoutDelayOptions OverridePlayoutDelay() {
  return {.field_trial = "WebRTC-ForcePlayoutDelay/min_ms:70,max_ms:90/",
          .expected_delay = ForcedPlayoutDelay()};
}

class RtpVideoStreamReceiver2TestPlayoutDelay
    : public RtpVideoStreamReceiver2Test,
      public ::testing::WithParamInterface<PlayoutDelayOptions> {
 protected:
  RtpVideoStreamReceiver2TestPlayoutDelay()
      : RtpVideoStreamReceiver2Test(GetParam().field_trial) {}
};

INSTANTIATE_TEST_SUITE_P(PlayoutDelay,
                         RtpVideoStreamReceiver2TestPlayoutDelay,
                         Values(DefaultBehavior(), OverridePlayoutDelay()));

TEST_P(RtpVideoStreamReceiver2TestPlayoutDelay, PlayoutDelay) {
  CopyOnWriteBuffer payload_data({'1', '2', '3', '4'});
  RtpHeaderExtensionMap extension_map;
  extension_map.Register<PlayoutDelayLimits>(RtpHeaderExtensionId(1));
  RtpPacketToSend packet_to_send(&extension_map);
  packet_to_send.SetPayloadType(kPayloadType);
  packet_to_send.SetSequenceNumber(1);

  // Set playout delay on outgoing packet.
  EXPECT_TRUE(packet_to_send.SetExtension<PlayoutDelayLimits>(
      TransmittedPlayoutDelay()));
  packet_to_send.SetPayload(payload_data);

  RtpPacketReceived received_packet(&extension_map);
  received_packet.Parse(packet_to_send.data(), packet_to_send.size());

  RTPVideoHeader video_header =
      GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(payload_data);
  // Expect the playout delay of encoded frame to be the same as the transmitted
  // playout delay unless it was overridden by a field trial.
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
      .WillOnce([expected_playout_delay =
                     GetParam().expected_delay](EncodedFrame* frame) {
        EXPECT_EQ(frame->EncodedImage().PlayoutDelay(), expected_playout_delay);
      });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      received_packet.PayloadBuffer(), received_packet, video_header);
}

#ifdef RTC_ENABLE_H265
RTPVideoHeader GetDefaultH265VideoHeader() {
  RTPVideoHeader video_header;
  video_header.codec = kVideoCodecH265;
  return video_header;
}

class RtpVideoStreamReceiver2TestH265 : public RtpVideoStreamReceiver2Test {
 protected:
  RtpVideoStreamReceiver2TestH265() : RtpVideoStreamReceiver2Test("") {}
};

TEST_F(RtpVideoStreamReceiver2TestH265, H265Bitstream) {
  constexpr int kH265PayloadType = 98;
  CodecParameterMap codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kH265PayloadType, kVideoCodecH265,
                                              codec_params,
                                              /*raw_payload=*/false);
  rtp_video_stream_receiver_->StartReceive();

  // Data is generated by WebCodecs H265 encoder, with 720p fake media capturer.
  // IDR is not complete.
  constexpr uint8_t vps[] = {0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01,
                             0xff, 0xff, 0x21, 0x40, 0x00, 0x00, 0x03, 0x00,
                             0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
                             0x7b, 0x3c, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x04,
                             0x00, 0x00, 0x03, 0x00, 0x79, 0x40};
  constexpr uint8_t sps[] = {
      0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x21, 0x40, 0x00, 0x00, 0x03,
      0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7b, 0xa0, 0x02,
      0x80, 0x80, 0x2d, 0x16, 0x8f, 0x92, 0x46, 0xd9, 0x3f, 0xf6, 0x02, 0x80,
      0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x01, 0xe6, 0x45,
      0xde, 0xf7, 0xe0, 0x04, 0x65, 0x00, 0x23, 0x28, 0x80};
  constexpr uint8_t pps[] = {0x00, 0x00, 0x00, 0x01, 0x44, 0x01,
                             0xc0, 0x25, 0x64, 0xc0, 0xed, 0x90};
  constexpr uint8_t idr[] = {0x00, 0x00, 0x00, 0x01, 0x26, 0x01, 0xaf,
                             0xb0, 0x87, 0x11, 0x7a, 0xc1, 0x45, 0x57,
                             0x3f, 0xff, 0x57, 0x14, 0x5f, 0xf7, 0x7a,
                             0x37, 0xfd, 0xe3, 0xd9};

  RtpPacketReceived rtp_packet;
  rtp_packet.SetPayloadType(kPayloadType);
  rtp_packet.SetSequenceNumber(0);
  rtp_packet.SetPayloadType(kH265PayloadType);
  RTPVideoHeader video_header = GetDefaultH265VideoHeader();
  mock_on_complete_frame_callback_.AppendExpectedBitstream(vps);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(vps), rtp_packet, video_header);

  rtp_packet.SetSequenceNumber(1);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(sps);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(sps), rtp_packet, video_header);

  rtp_packet.SetSequenceNumber(2);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(pps);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(pps), rtp_packet, video_header);

  rtp_packet.SetSequenceNumber(3);
  rtp_packet.SetMarker(true);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(idr);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(idr), rtp_packet, video_header);
}

TEST_F(RtpVideoStreamReceiver2Test, AddReceiveCodecWhileReceivingH265) {
  // Start receiving before adding any H26x codecs.
  rtp_video_stream_receiver_->StartReceive();

  constexpr int kH265PayloadType = 98;
  constexpr uint8_t vps[] = {0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01,
                             0xff, 0xff, 0x21, 0x40, 0x00, 0x00, 0x03, 0x00,
                             0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
                             0x7b, 0x3c, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x04,
                             0x00, 0x00, 0x03, 0x00, 0x79, 0x40};

  RtpPacketReceived rtp_packet;
  rtp_packet.SetSequenceNumber(0);
  rtp_packet.SetPayloadType(kH265PayloadType);
  RTPVideoHeader video_header = GetDefaultH265VideoHeader();

  // Before adding H.265 receive codec, packet insertion does not trigger
  // complete frame.
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(0);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(vps), rtp_packet, video_header);

  // Add H.265 receive codec while receiving.
  CodecParameterMap codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kH265PayloadType, kVideoCodecH265,
                                              codec_params,
                                              /*raw_payload=*/false);

  // Now h26x_packet_buffer_ is initialized, so packet insertion succeeds and
  // frame callback is invoked.
  mock_on_complete_frame_callback_.AppendExpectedBitstream(vps);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      CopyOnWriteBuffer(vps), rtp_packet, video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}
#endif  // RTC_ENABLE_H265

TEST_F(RtpVideoStreamReceiver2Test, LogsReceivedPacketToEventLog) {
  RtpPacketReceived rtp_packet;

  EXPECT_CALL(log_, LogProxy(_));
  rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
}

TEST_F(RtpVideoStreamReceiver2Test, DoesNotLogRecoveredPacketToEventLog) {
  RtpPacketReceived recovered_packet;
  recovered_packet.set_recovered(true);

  EXPECT_CALL(log_, LogProxy(_)).Times(0);
  rtp_video_stream_receiver_->OnRtpPacket(recovered_packet);
}

TEST_F(RtpVideoStreamReceiver2Test, PrunesHistoryMapsOnUndecodableFrames) {
  const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
  rtp_video_stream_receiver_->StartReceive();

  RtpHeaderExtensionMap extension_map;
  extension_map.Register<RtpGenericFrameDescriptorExtension00>(
      RtpHeaderExtensionId(5));

  // Inject 2049 complete single-packet frames.
  // Each frame completes and inserts an entry into last_seq_num_for_pic_id_
  // and last_timestamp_for_pic_id_.
  // After 2048 frames (kMaxFrameHistorySize), the capacity limit is reached,
  // and oldest entries are evicted as new ones arrive.
  for (int i = 1; i <= 2049; ++i) {
    mock_on_complete_frame_callback_.ClearExpectedBitstream();
    mock_on_complete_frame_callback_.AppendExpectedBitstream(
        std::span(data).subspan(1));

    RtpPacketReceived rtp_packet(&extension_map);
    rtp_packet.SetPayloadType(kPayloadType);

    RtpGenericFrameDescriptor generic_descriptor;
    generic_descriptor.SetFirstPacketInSubFrame(true);
    generic_descriptor.SetLastPacketInSubFrame(true);
    generic_descriptor.SetFrameId(i);
    ASSERT_TRUE(rtp_packet.SetExtension<RtpGenericFrameDescriptorExtension00>(
        generic_descriptor));

    rtp_packet.SetPayload(data);
    rtp_packet.SetMarker(true);
    rtp_packet.SetSequenceNumber(static_cast<uint16_t>(i));
    rtp_packet.SetTimestamp(static_cast<uint32_t>(90000 + i * 3000));
    rtp_packet.SetSsrc(kSsrc);

    rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
  }

  // Verify that history maps are bounded by kMaxFrameHistorySize (2048).
  ASSERT_EQ(rtp_video_stream_receiver_->last_seq_num_for_pic_id().size(),
            2048u);
  ASSERT_EQ(rtp_video_stream_receiver_->last_timestamp_for_pic_id().size(),
            2048u);

  // Verify that the oldest frame (1) was evicted, while newer frames (2..2049)
  // are retained.
  EXPECT_FALSE(
      rtp_video_stream_receiver_->last_seq_num_for_pic_id().contains(1));
  EXPECT_FALSE(
      rtp_video_stream_receiver_->last_timestamp_for_pic_id().contains(1));
  EXPECT_EQ(
      rtp_video_stream_receiver_->last_seq_num_for_pic_id().begin()->first, 2);
  EXPECT_EQ(
      rtp_video_stream_receiver_->last_seq_num_for_pic_id().rbegin()->first,
      2049);

  // Calling FrameDecoded for the newest frame clears all remaining entries.
  rtp_video_stream_receiver_->FrameDecoded(2049);
  EXPECT_THAT(rtp_video_stream_receiver_->last_seq_num_for_pic_id(), IsEmpty());
  EXPECT_THAT(rtp_video_stream_receiver_->last_timestamp_for_pic_id(),
              IsEmpty());
}

TEST_F(RtpVideoStreamReceiver2Test, RemoveReceiveCodecsResetsState) {
  constexpr uint8_t kH264PayloadType = 99;

  // Configure H.264 with SPS/PPS/IDR forced keyframe mode.
  CodecParameterMap codec_params_keyframe;
  codec_params_keyframe.emplace(kH264FmtpSpsPpsIdrInKeyframe, "");
  rtp_video_stream_receiver_->AddReceiveCodec(kH264PayloadType, kVideoCodecH264,
                                              codec_params_keyframe,
                                              /*raw_payload=*/false);

  // Remove codecs. This must reset sps_pps_idr_is_h264_keyframe_ to false.
  rtp_video_stream_receiver_->RemoveReceiveCodecs();

  // Re-add H.264 without forcing SPS/PPS/IDR keyframe mode.
  CodecParameterMap empty_codec_params;
  rtp_video_stream_receiver_->AddReceiveCodec(kH264PayloadType, kVideoCodecH264,
                                              empty_codec_params,
                                              /*raw_payload=*/false);
  rtp_video_stream_receiver_->StartReceive();

  // Send SPS
  CopyOnWriteBuffer sps_data;
  RtpPacketReceived rtp_packet;
  RTPVideoHeader sps_video_header = GetDefaultH264VideoHeader();
  AddSps(&sps_video_header, 0, &sps_data);
  rtp_packet.SetSequenceNumber(0);
  rtp_packet.SetPayloadType(kH264PayloadType);
  sps_video_header.is_first_packet_in_frame = true;
  sps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(sps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      sps_data, rtp_packet, sps_video_header);

  // Send PPS
  CopyOnWriteBuffer pps_data;
  RTPVideoHeader pps_video_header = GetDefaultH264VideoHeader();
  AddPps(&pps_video_header, 0, 1, &pps_data);
  rtp_packet.SetSequenceNumber(1);
  rtp_packet.SetPayloadType(kH264PayloadType);
  pps_video_header.is_first_packet_in_frame = true;
  pps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(pps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      pps_data, rtp_packet, pps_video_header);

  // Send IDR packet
  CopyOnWriteBuffer idr_data;
  RTPVideoHeader idr_video_header = GetDefaultH264VideoHeader();
  AddIdr(&idr_video_header, 1);
  rtp_packet.SetSequenceNumber(2);
  rtp_packet.SetPayloadType(kH264PayloadType);
  rtp_packet.SetMarker(true);
  idr_video_header.is_first_packet_in_frame = true;
  idr_video_header.is_last_packet_in_frame = true;
  idr_video_header.frame_type = VideoFrameType::kVideoFrameKey;

  const uint8_t idr[] = {0x65, 1, 2, 3};
  idr_data.AppendData(idr);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(idr_data);
  // Because sps_pps_idr_is_h264_keyframe_ was reset to false, IDR-only
  // keyframes are allowed again. Therefore, the IDR frame completes as a
  // keyframe.
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_TRUE(frame->is_keyframe()); });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      idr_data, rtp_packet, idr_video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}

TEST_F(RtpVideoStreamReceiver2Test, SetReceiveCodecsUpdatesInPlace) {
  constexpr uint8_t kH264Pt = 99;
  constexpr uint8_t kVp8Pt = 100;

  RtpVideoStreamReceiver2::ReceiveCodec h264_codec{
      .payload_type = kH264Pt,
      .video_codec = kVideoCodecH264,
  };
  RtpVideoStreamReceiver2::ReceiveCodec vp8_codec{
      .payload_type = kVp8Pt,
      .video_codec = kVideoCodecVP8,
  };

  rtp_video_stream_receiver_->SetReceiveCodecs({h264_codec});
  rtp_video_stream_receiver_->StartReceive();

  // Update codecs in-place by adding VP8 while keeping H264.
  rtp_video_stream_receiver_->SetReceiveCodecs({h264_codec, vp8_codec});

  // Verify VP8 payload is accepted and produces a complete frame.
  const uint8_t vp8_payload[] = {1, 2, 3, 4};
  CopyOnWriteBuffer payload(vp8_payload);
  RtpPacketReceived rtp_packet;
  rtp_packet.SetPayloadType(kVp8Pt);
  rtp_packet.SetSequenceNumber(1);

  RTPVideoHeader video_header;
  video_header.codec = kVideoCodecVP8;
  video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
  video_header.is_first_packet_in_frame = true;
  video_header.is_last_packet_in_frame = true;
  video_header.frame_type = VideoFrameType::kVideoFrameKey;

  mock_on_complete_frame_callback_.AppendExpectedBitstream(payload);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_TRUE(frame->is_keyframe()); });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet, video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}

TEST_F(RtpVideoStreamReceiver2Test,
       SetReceiveCodecsPreservesPacketBufferAndFrameAssembly) {
  constexpr uint8_t kH264Pt = 99;
  constexpr uint8_t kVp8Pt = 100;

  RtpVideoStreamReceiver2::ReceiveCodec h264_codec{
      .payload_type = kH264Pt,
      .video_codec = kVideoCodecH264,
  };
  RtpVideoStreamReceiver2::ReceiveCodec vp8_codec{
      .payload_type = kVp8Pt,
      .video_codec = kVideoCodecVP8,
  };

  rtp_video_stream_receiver_->SetReceiveCodecs({h264_codec});
  rtp_video_stream_receiver_->StartReceive();

  // Send SPS packet of H.264 frame.
  CopyOnWriteBuffer sps_data;
  RtpPacketReceived rtp_packet;
  RTPVideoHeader sps_video_header = GetDefaultH264VideoHeader();
  AddSps(&sps_video_header, 0, &sps_data);
  rtp_packet.SetSequenceNumber(0);
  rtp_packet.SetPayloadType(kH264Pt);
  sps_video_header.is_first_packet_in_frame = true;
  sps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(sps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      sps_data, rtp_packet, sps_video_header);

  // Send PPS packet of H.264 frame.
  CopyOnWriteBuffer pps_data;
  RTPVideoHeader pps_video_header = GetDefaultH264VideoHeader();
  AddPps(&pps_video_header, 0, 1, &pps_data);
  rtp_packet.SetSequenceNumber(1);
  rtp_packet.SetPayloadType(kH264Pt);
  pps_video_header.is_first_packet_in_frame = true;
  pps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(pps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      pps_data, rtp_packet, pps_video_header);

  // Update receive codecs in-place mid-frame assembly (adding VP8).
  rtp_video_stream_receiver_->SetReceiveCodecs({h264_codec, vp8_codec});

  // Send final IDR packet. Packet buffer must be preserved so that the complete
  // frame (SPS + PPS + IDR) is assembled and delivered.
  CopyOnWriteBuffer idr_data;
  RTPVideoHeader idr_video_header = GetDefaultH264VideoHeader();
  AddIdr(&idr_video_header, 1);
  rtp_packet.SetSequenceNumber(2);
  rtp_packet.SetPayloadType(kH264Pt);
  rtp_packet.SetMarker(true);
  idr_video_header.is_first_packet_in_frame = true;
  idr_video_header.is_last_packet_in_frame = true;
  idr_video_header.frame_type = VideoFrameType::kVideoFrameKey;

  const uint8_t idr[] = {0x65, 1, 2, 3};
  idr_data.AppendData(idr);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(idr_data);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_TRUE(frame->is_keyframe()); });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      idr_data, rtp_packet, idr_video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}

TEST_F(RtpVideoStreamReceiver2Test,
       SetReceiveCodecsSelectivelyRemovesAndPreservesPayloadTypes) {
  constexpr uint8_t kH264Pt = 99;
  constexpr uint8_t kVp8Pt = 100;
  constexpr uint8_t kVp9Pt = 101;

  RtpVideoStreamReceiver2::ReceiveCodec h264_codec{
      .payload_type = kH264Pt,
      .video_codec = kVideoCodecH264,
  };
  RtpVideoStreamReceiver2::ReceiveCodec vp8_codec{
      .payload_type = kVp8Pt,
      .video_codec = kVideoCodecVP8,
  };
  RtpVideoStreamReceiver2::ReceiveCodec vp9_codec{
      .payload_type = kVp9Pt,
      .video_codec = kVideoCodecVP9,
  };

  // Initially configure H264, VP8, and VP9.
  rtp_video_stream_receiver_->SetReceiveCodecs(
      {h264_codec, vp8_codec, vp9_codec});
  rtp_video_stream_receiver_->StartReceive();

  // Reconfigure codecs to keep H264 and VP8, but remove VP9.
  rtp_video_stream_receiver_->SetReceiveCodecs({h264_codec, vp8_codec});

  const uint8_t dummy_payload[] = {1, 2, 3, 4};
  CopyOnWriteBuffer payload(dummy_payload);

  // 1. Packet for retained VP8 (PT 100) completes a frame.
  RtpPacketReceived vp8_packet;
  vp8_packet.SetPayloadType(kVp8Pt);
  vp8_packet.SetSequenceNumber(1);
  RTPVideoHeader vp8_header;
  vp8_header.codec = kVideoCodecVP8;
  vp8_header.video_type_header.emplace<RTPVideoHeaderVP8>();
  vp8_header.is_first_packet_in_frame = true;
  vp8_header.is_last_packet_in_frame = true;
  vp8_header.frame_type = VideoFrameType::kVideoFrameKey;

  mock_on_complete_frame_callback_.AppendExpectedBitstream(payload);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(1);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, vp8_packet, vp8_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();

  // 2. Packet for removed VP9 (PT 101) is dropped by OnRtpPacket.
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(0);
  RtpPacketReceived vp9_packet;
  vp9_packet.SetPayloadType(kVp9Pt);
  vp9_packet.SetSequenceNumber(2);
  rtp_video_stream_receiver_->OnRtpPacket(vp9_packet);
}

TEST_F(RtpVideoStreamReceiver2Test,
       BufferedFrameForRemovedCodecDoesNotCrashOnAssembly) {
  constexpr uint8_t kAv1Pt = 100;
  RtpVideoStreamReceiver2::ReceiveCodec av1_codec{
      .payload_type = kAv1Pt,
      .video_codec = kVideoCodecAV1,
  };

  rtp_video_stream_receiver_->SetReceiveCodecs({av1_codec});
  rtp_video_stream_receiver_->StartReceive();

  const uint8_t av1_payload[] = {1, 2, 3, 4};
  CopyOnWriteBuffer payload(av1_payload);

  // 1. Packet 1 (first in frame) arrives.
  RtpPacketReceived rtp_packet1;
  rtp_packet1.SetPayloadType(kAv1Pt);
  rtp_packet1.SetSequenceNumber(1);
  RTPVideoHeader header1;
  header1.codec = kVideoCodecAV1;
  header1.is_first_packet_in_frame = true;
  header1.is_last_packet_in_frame = false;
  header1.frame_type = VideoFrameType::kVideoFrameKey;

  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet1, header1);

  // 2. Packet 3 (last in frame) arrives. Packet 2 is missing.
  RtpPacketReceived rtp_packet3;
  rtp_packet3.SetPayloadType(kAv1Pt);
  rtp_packet3.SetSequenceNumber(3);
  RTPVideoHeader header3;
  header3.codec = kVideoCodecAV1;
  header3.is_first_packet_in_frame = false;
  header3.is_last_packet_in_frame = true;
  header3.frame_type = VideoFrameType::kVideoFrameKey;

  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet3, header3);

  // 3. AV1 codec is removed via SetReceiveCodecs.
  rtp_video_stream_receiver_->SetReceiveCodecs({});

  // 4. Packet 2 arrives to complete the frame in packet_buffer_.
  // Verify frame assembly is skipped and does not crash.
  RtpPacketReceived rtp_packet2;
  rtp_packet2.SetPayloadType(kAv1Pt);
  rtp_packet2.SetSequenceNumber(2);
  RTPVideoHeader header2;
  header2.codec = kVideoCodecAV1;
  header2.is_first_packet_in_frame = false;
  header2.is_last_packet_in_frame = false;
  header2.frame_type = VideoFrameType::kVideoFrameKey;

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(0);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet2, header2);
}

TEST_F(RtpVideoStreamReceiver2Test,
       BufferedFrameForReplacedCodecDoesNotCrashOnAssembly) {
  constexpr uint8_t kPt = 100;
  RtpVideoStreamReceiver2::ReceiveCodec av1_codec{
      .payload_type = kPt,
      .video_codec = kVideoCodecAV1,
  };

  rtp_video_stream_receiver_->SetReceiveCodecs({av1_codec});
  rtp_video_stream_receiver_->StartReceive();

  const uint8_t payload_bytes[] = {1, 2, 3, 4};
  CopyOnWriteBuffer payload(payload_bytes);

  // 1. Packet 1 (first in frame) and packet 3 (last in frame) for AV1 arrive.
  RtpPacketReceived rtp_packet1;
  rtp_packet1.SetPayloadType(kPt);
  rtp_packet1.SetSequenceNumber(1);
  RTPVideoHeader header1;
  header1.codec = kVideoCodecAV1;
  header1.is_first_packet_in_frame = true;
  header1.is_last_packet_in_frame = false;
  header1.frame_type = VideoFrameType::kVideoFrameKey;

  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet1, header1);

  RtpPacketReceived rtp_packet3;
  rtp_packet3.SetPayloadType(kPt);
  rtp_packet3.SetSequenceNumber(3);
  RTPVideoHeader header3;
  header3.codec = kVideoCodecAV1;
  header3.is_first_packet_in_frame = false;
  header3.is_last_packet_in_frame = true;
  header3.frame_type = VideoFrameType::kVideoFrameKey;

  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet3, header3);

  // 2. Reconfigure PT 100 to H264.
  RtpVideoStreamReceiver2::ReceiveCodec h264_codec{
      .payload_type = kPt,
      .video_codec = kVideoCodecH264,
  };
  rtp_video_stream_receiver_->SetReceiveCodecs({h264_codec});

  // 3. Packet 2 arrives to complete the AV1 frame in packet_buffer_.
  RtpPacketReceived rtp_packet2;
  rtp_packet2.SetPayloadType(kPt);
  rtp_packet2.SetSequenceNumber(2);
  RTPVideoHeader header2;
  header2.codec = kVideoCodecAV1;
  header2.is_first_packet_in_frame = false;
  header2.is_last_packet_in_frame = false;
  header2.frame_type = VideoFrameType::kVideoFrameKey;

  // H264 depacketizer concatenates payloads for the 3 packets.
  mock_on_complete_frame_callback_.AppendExpectedBitstream(payload);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(payload);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(payload);

  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet2, header2);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}

TEST_F(RtpVideoStreamReceiver2Test,
       SetReceiveCodecsResetsH264KeyframeModeInPlace) {
  constexpr uint8_t kH264Pt = 99;

  RtpVideoStreamReceiver2::ReceiveCodec h264_sps_pps_codec{
      .payload_type = kH264Pt,
      .video_codec = kVideoCodecH264,
      .codec_params = {{std::string(kH264FmtpSpsPpsIdrInKeyframe), "1"}},
  };

  rtp_video_stream_receiver_->SetReceiveCodecs({h264_sps_pps_codec});

  // Update codecs in-place to plain H264 without sps_pps_idr_is_h264_keyframe.
  RtpVideoStreamReceiver2::ReceiveCodec plain_h264_codec{
      .payload_type = kH264Pt,
      .video_codec = kVideoCodecH264,
  };
  rtp_video_stream_receiver_->SetReceiveCodecs({plain_h264_codec});
  rtp_video_stream_receiver_->StartReceive();

  // Send SPS, PPS, and IDR packet. Because sps_pps_idr_is_h264_keyframe_ was
  // reset to false, IDR-only keyframes are allowed and frame completes.
  CopyOnWriteBuffer sps_data;
  RtpPacketReceived rtp_packet;
  RTPVideoHeader sps_video_header = GetDefaultH264VideoHeader();
  AddSps(&sps_video_header, 0, &sps_data);
  rtp_packet.SetSequenceNumber(0);
  rtp_packet.SetPayloadType(kH264Pt);
  sps_video_header.is_first_packet_in_frame = true;
  sps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(sps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      sps_data, rtp_packet, sps_video_header);

  CopyOnWriteBuffer pps_data;
  RTPVideoHeader pps_video_header = GetDefaultH264VideoHeader();
  AddPps(&pps_video_header, 0, 1, &pps_data);
  rtp_packet.SetSequenceNumber(1);
  rtp_packet.SetPayloadType(kH264Pt);
  pps_video_header.is_first_packet_in_frame = true;
  pps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(pps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      pps_data, rtp_packet, pps_video_header);

  CopyOnWriteBuffer idr_data;
  RTPVideoHeader idr_video_header = GetDefaultH264VideoHeader();
  AddIdr(&idr_video_header, 1);
  rtp_packet.SetSequenceNumber(2);
  rtp_packet.SetPayloadType(kH264Pt);
  rtp_packet.SetMarker(true);
  idr_video_header.is_first_packet_in_frame = true;
  idr_video_header.is_last_packet_in_frame = true;
  idr_video_header.frame_type = VideoFrameType::kVideoFrameKey;

  const uint8_t idr[] = {0x65, 1, 2, 3};
  idr_data.AppendData(idr);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(idr_data);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_TRUE(frame->is_keyframe()); });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      idr_data, rtp_packet, idr_video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}

TEST_F(RtpVideoStreamReceiver2Test,
       SetReceiveCodecsTogglesH264KeyframeModeInPlace) {
  constexpr uint8_t kH264Pt = 99;

  RtpVideoStreamReceiver2::ReceiveCodec plain_h264_codec{
      .payload_type = kH264Pt,
      .video_codec = kVideoCodecH264,
  };
  rtp_video_stream_receiver_->SetReceiveCodecs({plain_h264_codec});
  rtp_video_stream_receiver_->StartReceive();

  // Toggle in-place to SPS/PPS IDR keyframe mode.
  RtpVideoStreamReceiver2::ReceiveCodec h264_sps_pps_codec{
      .payload_type = kH264Pt,
      .video_codec = kVideoCodecH264,
      .codec_params = {{std::string(kH264FmtpSpsPpsIdrInKeyframe), "1"}},
  };
  rtp_video_stream_receiver_->SetReceiveCodecs({h264_sps_pps_codec});

  // Toggle in-place back to plain H264 mode.
  rtp_video_stream_receiver_->SetReceiveCodecs({plain_h264_codec});

  // Verify IDR frame completes properly under plain H264 mode.
  CopyOnWriteBuffer sps_data;
  RtpPacketReceived rtp_packet;
  RTPVideoHeader sps_video_header = GetDefaultH264VideoHeader();
  AddSps(&sps_video_header, 0, &sps_data);
  rtp_packet.SetSequenceNumber(0);
  rtp_packet.SetPayloadType(kH264Pt);
  sps_video_header.is_first_packet_in_frame = true;
  sps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(sps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      sps_data, rtp_packet, sps_video_header);

  CopyOnWriteBuffer pps_data;
  RTPVideoHeader pps_video_header = GetDefaultH264VideoHeader();
  AddPps(&pps_video_header, 0, 1, &pps_data);
  rtp_packet.SetSequenceNumber(1);
  rtp_packet.SetPayloadType(kH264Pt);
  pps_video_header.is_first_packet_in_frame = true;
  pps_video_header.frame_type = VideoFrameType::kEmptyFrame;
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(pps_data);
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      pps_data, rtp_packet, pps_video_header);

  CopyOnWriteBuffer idr_data;
  RTPVideoHeader idr_video_header = GetDefaultH264VideoHeader();
  AddIdr(&idr_video_header, 1);
  rtp_packet.SetSequenceNumber(2);
  rtp_packet.SetPayloadType(kH264Pt);
  rtp_packet.SetMarker(true);
  idr_video_header.is_first_packet_in_frame = true;
  idr_video_header.is_last_packet_in_frame = true;
  idr_video_header.frame_type = VideoFrameType::kVideoFrameKey;

  const uint8_t idr[] = {0x65, 1, 2, 3};
  idr_data.AppendData(idr);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(kH264StartCode);
  mock_on_complete_frame_callback_.AppendExpectedBitstream(idr_data);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_TRUE(frame->is_keyframe()); });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      idr_data, rtp_packet, idr_video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}

TEST_F(RtpVideoStreamReceiver2Test,
       SetReceiveCodecsPreservesDepacketizerInstanceForUnchangedCodecs) {
  constexpr uint8_t kVp8Pt = 100;

  RtpVideoStreamReceiver2::ReceiveCodec vp8_codec{
      .payload_type = kVp8Pt,
      .video_codec = kVideoCodecVP8,
  };

  rtp_video_stream_receiver_->SetReceiveCodecs({vp8_codec});
  rtp_video_stream_receiver_->StartReceive();

  // Call SetReceiveCodecs with identical codec configuration.
  rtp_video_stream_receiver_->SetReceiveCodecs({vp8_codec});

  // Verify frame delivery completes as expected.
  const uint8_t vp8_payload[] = {1, 2, 3, 4};
  CopyOnWriteBuffer payload(vp8_payload);
  RtpPacketReceived rtp_packet;
  rtp_packet.SetPayloadType(kVp8Pt);
  rtp_packet.SetSequenceNumber(1);

  RTPVideoHeader video_header;
  video_header.codec = kVideoCodecVP8;
  video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
  video_header.is_first_packet_in_frame = true;
  video_header.is_last_packet_in_frame = true;
  video_header.frame_type = VideoFrameType::kVideoFrameKey;

  mock_on_complete_frame_callback_.AppendExpectedBitstream(payload);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_TRUE(frame->is_keyframe()); });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet, video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}

TEST_F(RtpVideoStreamReceiver2Test,
       SetReceiveCodecsUpdatesDepacketizerWhenRawPayloadToggles) {
  constexpr uint8_t kVp8Pt = 100;

  RtpVideoStreamReceiver2::ReceiveCodec vp8_normal{
      .payload_type = kVp8Pt,
      .video_codec = kVideoCodecVP8,
      .raw_payload = false,
  };
  rtp_video_stream_receiver_->SetReceiveCodecs({vp8_normal});

  // Reconfigure in-place with raw_payload = true.
  RtpVideoStreamReceiver2::ReceiveCodec vp8_raw{
      .payload_type = kVp8Pt,
      .video_codec = kVideoCodecVP8,
      .raw_payload = true,
  };
  rtp_video_stream_receiver_->SetReceiveCodecs({vp8_raw});
  rtp_video_stream_receiver_->StartReceive();

  // Verify raw payload is delivered properly.
  const uint8_t raw_payload_bytes[] = {1, 2, 3, 4};
  CopyOnWriteBuffer payload(raw_payload_bytes);
  RtpPacketReceived rtp_packet;
  rtp_packet.SetPayloadType(kVp8Pt);
  rtp_packet.SetSequenceNumber(1);

  RTPVideoHeader video_header;
  video_header.codec = kVideoCodecVP8;
  video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
  video_header.is_first_packet_in_frame = true;
  video_header.is_last_packet_in_frame = true;
  video_header.frame_type = VideoFrameType::kVideoFrameKey;

  mock_on_complete_frame_callback_.AppendExpectedBitstream(payload);
  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame)
      .WillOnce(
          [&](EncodedFrame* frame) { EXPECT_TRUE(frame->is_keyframe()); });
  rtp_video_stream_receiver_->OnReceivedPayloadDataForTesting(
      payload, rtp_packet, video_header);
  mock_on_complete_frame_callback_.ClearExpectedBitstream();
}

}  // namespace webrtc
