/*
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
#include "modules/video_coding/packet_buffer.h"

#include <cstring>
#include <limits>
#include <ostream>
#include <string>
#include <utility>

#include "api/array_view.h"
#include "common_video/h264/h264_common.h"
#include "modules/video_coding/frame_object.h"
#include "rtc_base/random.h"
#include "test/field_trial.h"
#include "test/gmock.h"
#include "test/gtest.h"

namespace webrtc {
namespace video_coding {
namespace {

using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::IsEmpty;
using ::testing::Matches;
using ::testing::Pointee;
using ::testing::SizeIs;

constexpr int kStartSize = 16;
constexpr int kMaxSize = 64;

void IgnoreResult(PacketBuffer::InsertResult /*result*/) {}

// Validates frame boundaries are valid and returns first sequence_number for
// each frame.
std::vector<uint16_t> StartSeqNums(
    rtc::ArrayView<const std::unique_ptr<PacketBuffer::Packet>> packets) {
  std::vector<uint16_t> result;
  bool frame_boundary = true;
  for (const auto& packet : packets) {
    EXPECT_EQ(frame_boundary, packet->is_first_packet_in_frame());
    if (packet->is_first_packet_in_frame()) {
      result.push_back(packet->seq_num);
    }
    frame_boundary = packet->is_last_packet_in_frame();
  }
  EXPECT_TRUE(frame_boundary);
  return result;
}

MATCHER_P(StartSeqNumsAre, seq_num, "") {
  return Matches(ElementsAre(seq_num))(StartSeqNums(arg.packets));
}

MATCHER_P2(StartSeqNumsAre, seq_num1, seq_num2, "") {
  return Matches(ElementsAre(seq_num1, seq_num2))(StartSeqNums(arg.packets));
}

MATCHER(KeyFrame, "") {
  return arg->is_first_packet_in_frame() &&
         arg->video_header.frame_type == VideoFrameType::kVideoFrameKey;
}

MATCHER(DeltaFrame, "") {
  return arg->is_first_packet_in_frame() &&
         arg->video_header.frame_type == VideoFrameType::kVideoFrameDelta;
}

struct PacketBufferInsertResult : public PacketBuffer::InsertResult {
  explicit PacketBufferInsertResult(PacketBuffer::InsertResult result)
      : InsertResult(std::move(result)) {}
};

void PrintTo(const PacketBufferInsertResult& result, std::ostream* os) {
  *os << "frames: { ";
  for (const auto& packet : result.packets) {
    if (packet->is_first_packet_in_frame() &&
        packet->is_last_packet_in_frame()) {
      *os << "{sn: " << packet->seq_num << " }";
    } else if (packet->is_first_packet_in_frame()) {
      *os << "{sn: [" << packet->seq_num << "-";
    } else if (packet->is_last_packet_in_frame()) {
      *os << packet->seq_num << "] }, ";
    }
  }
  *os << " }";
  if (result.buffer_cleared) {
    *os << ", buffer_cleared";
  }
}

class PacketBufferTest : public ::testing::Test {
 protected:
  PacketBufferTest() : rand_(0x7732213), packet_buffer_(kStartSize, kMaxSize) {}

  uint16_t Rand() { return rand_.Rand<uint16_t>(); }

  enum IsKeyFrame { kKeyFrame, kDeltaFrame };
  enum IsFirst { kFirst, kNotFirst };
  enum IsLast { kLast, kNotLast };

  PacketBufferInsertResult Insert(uint16_t seq_num,  // packet sequence number
                                  IsKeyFrame keyframe,  // is keyframe
                                  IsFirst first,  // is first packet of frame
                                  IsLast last,    // is last packet of frame
                                  rtc::ArrayView<const uint8_t> data = {},
                                  uint32_t timestamp = 123u) {  // rtp timestamp
    auto packet = std::make_unique<PacketBuffer::Packet>();
    packet->video_header.codec = kVideoCodecGeneric;
    packet->timestamp = timestamp;
    packet->seq_num = seq_num;
    packet->video_header.frame_type = keyframe == kKeyFrame
                                          ? VideoFrameType::kVideoFrameKey
                                          : VideoFrameType::kVideoFrameDelta;
    packet->video_header.is_first_packet_in_frame = first == kFirst;
    packet->video_header.is_last_packet_in_frame = last == kLast;
    packet->video_payload.SetData(data.data(), data.size());

    return PacketBufferInsertResult(
        packet_buffer_.InsertPacket(std::move(packet)));
  }

  Random rand_;
  PacketBuffer packet_buffer_;
};

TEST_F(PacketBufferTest, InsertOnePacket) {
  const uint16_t seq_num = Rand();
  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast).packets, SizeIs(1));
}

TEST_F(PacketBufferTest, InsertMultiplePackets) {
  const uint16_t seq_num = Rand();
  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast).packets, SizeIs(1));
  EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kFirst, kLast).packets, SizeIs(1));
  EXPECT_THAT(Insert(seq_num + 2, kKeyFrame, kFirst, kLast).packets, SizeIs(1));
  EXPECT_THAT(Insert(seq_num + 3, kKeyFrame, kFirst, kLast).packets, SizeIs(1));
}

TEST_F(PacketBufferTest, InsertDuplicatePacket) {
  const uint16_t seq_num = Rand();
  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).packets, IsEmpty());
  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).packets, IsEmpty());
  EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast).packets,
              SizeIs(2));
}

TEST_F(PacketBufferTest, SeqNumWrapOneFrame) {
  Insert(0xFFFF, kKeyFrame, kFirst, kNotLast);
  EXPECT_THAT(Insert(0x0, kKeyFrame, kNotFirst, kLast),
              StartSeqNumsAre(0xFFFF));
}

TEST_F(PacketBufferTest, SeqNumWrapTwoFrames) {
  EXPECT_THAT(Insert(0xFFFF, kKeyFrame, kFirst, kLast),
              StartSeqNumsAre(0xFFFF));
  EXPECT_THAT(Insert(0x0, kKeyFrame, kFirst, kLast), StartSeqNumsAre(0x0));
}

TEST_F(PacketBufferTest, InsertOldPackets) {
  EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).packets, IsEmpty());
  EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).packets, SizeIs(1));
  EXPECT_THAT(Insert(101, kKeyFrame, kNotFirst, kLast).packets, SizeIs(2));

  EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).packets, IsEmpty());
  EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).packets, IsEmpty());
  EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).packets, SizeIs(1));

  packet_buffer_.ClearTo(102);
  EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).packets, IsEmpty());
  EXPECT_THAT(Insert(103, kDeltaFrame, kFirst, kLast).packets, SizeIs(1));
}

TEST_F(PacketBufferTest, FrameSize) {
  const uint16_t seq_num = Rand();
  uint8_t data1[5] = {};
  uint8_t data2[5] = {};
  uint8_t data3[5] = {};
  uint8_t data4[5] = {};

  Insert(seq_num, kKeyFrame, kFirst, kNotLast, data1);
  Insert(seq_num + 1, kKeyFrame, kNotFirst, kNotLast, data2);
  Insert(seq_num + 2, kKeyFrame, kNotFirst, kNotLast, data3);
  auto packets =
      Insert(seq_num + 3, kKeyFrame, kNotFirst, kLast, data4).packets;
  // Expect one frame of 4 packets.
  EXPECT_THAT(StartSeqNums(packets), ElementsAre(seq_num));
  EXPECT_THAT(packets, SizeIs(4));
}

TEST_F(PacketBufferTest, ExpandBuffer) {
  const uint16_t seq_num = Rand();

  Insert(seq_num, kKeyFrame, kFirst, kNotLast);
  for (int i = 1; i < kStartSize; ++i)
    EXPECT_FALSE(
        Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast).buffer_cleared);

  // Already inserted kStartSize number of packets, inserting the last packet
  // should increase the buffer size and also result in an assembled frame.
  EXPECT_FALSE(
      Insert(seq_num + kStartSize, kKeyFrame, kNotFirst, kLast).buffer_cleared);
}

TEST_F(PacketBufferTest, SingleFrameExpandsBuffer) {
  const uint16_t seq_num = Rand();

  Insert(seq_num, kKeyFrame, kFirst, kNotLast);
  for (int i = 1; i < kStartSize; ++i)
    Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast);
  EXPECT_THAT(Insert(seq_num + kStartSize, kKeyFrame, kNotFirst, kLast),
              StartSeqNumsAre(seq_num));
}

TEST_F(PacketBufferTest, ExpandBufferOverflow) {
  const uint16_t seq_num = Rand();

  EXPECT_FALSE(Insert(seq_num, kKeyFrame, kFirst, kNotLast).buffer_cleared);
  for (int i = 1; i < kMaxSize; ++i)
    EXPECT_FALSE(
        Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast).buffer_cleared);

  // Already inserted kMaxSize number of packets, inserting the last packet
  // should overflow the buffer and result in false being returned.
  EXPECT_TRUE(
      Insert(seq_num + kMaxSize, kKeyFrame, kNotFirst, kLast).buffer_cleared);
}

TEST_F(PacketBufferTest, OnePacketOneFrame) {
  const uint16_t seq_num = Rand();
  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num));
}

TEST_F(PacketBufferTest, TwoPacketsTwoFrames) {
  const uint16_t seq_num = Rand();

  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num));
  EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num + 1));
}

TEST_F(PacketBufferTest, TwoPacketsOneFrames) {
  const uint16_t seq_num = Rand();

  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).packets, IsEmpty());
  EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast),
              StartSeqNumsAre(seq_num));
}

TEST_F(PacketBufferTest, ThreePacketReorderingOneFrame) {
  const uint16_t seq_num = Rand();

  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).packets, IsEmpty());
  EXPECT_THAT(Insert(seq_num + 2, kKeyFrame, kNotFirst, kLast).packets,
              IsEmpty());
  EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kNotLast),
              StartSeqNumsAre(seq_num));
}

TEST_F(PacketBufferTest, Frames) {
  const uint16_t seq_num = Rand();

  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num));
  EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num + 1));
  EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num + 2));
  EXPECT_THAT(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num + 3));
}

TEST_F(PacketBufferTest, ClearSinglePacket) {
  const uint16_t seq_num = Rand();

  for (int i = 0; i < kMaxSize; ++i)
    Insert(seq_num + i, kDeltaFrame, kFirst, kLast);

  packet_buffer_.ClearTo(seq_num);
  EXPECT_FALSE(
      Insert(seq_num + kMaxSize, kDeltaFrame, kFirst, kLast).buffer_cleared);
}

TEST_F(PacketBufferTest, ClearPacketBeforeFullyReceivedFrame) {
  Insert(0, kKeyFrame, kFirst, kNotLast);
  Insert(1, kKeyFrame, kNotFirst, kNotLast);
  packet_buffer_.ClearTo(0);
  EXPECT_THAT(Insert(2, kKeyFrame, kNotFirst, kLast).packets, IsEmpty());
}

TEST_F(PacketBufferTest, ClearFullBuffer) {
  for (int i = 0; i < kMaxSize; ++i)
    Insert(i, kDeltaFrame, kFirst, kLast);

  packet_buffer_.ClearTo(kMaxSize - 1);

  for (int i = kMaxSize; i < 2 * kMaxSize; ++i)
    EXPECT_FALSE(Insert(i, kDeltaFrame, kFirst, kLast).buffer_cleared);
}

TEST_F(PacketBufferTest, DontClearNewerPacket) {
  EXPECT_THAT(Insert(0, kKeyFrame, kFirst, kLast), StartSeqNumsAre(0));
  packet_buffer_.ClearTo(0);
  EXPECT_THAT(Insert(2 * kStartSize, kKeyFrame, kFirst, kLast),
              StartSeqNumsAre(2 * kStartSize));
  EXPECT_THAT(Insert(3 * kStartSize + 1, kKeyFrame, kFirst, kNotLast).packets,
              IsEmpty());
  packet_buffer_.ClearTo(2 * kStartSize);
  EXPECT_THAT(Insert(3 * kStartSize + 2, kKeyFrame, kNotFirst, kLast),
              StartSeqNumsAre(3 * kStartSize + 1));
}

TEST_F(PacketBufferTest, OneIncompleteFrame) {
  const uint16_t seq_num = Rand();

  EXPECT_THAT(Insert(seq_num, kDeltaFrame, kFirst, kNotLast).packets,
              IsEmpty());
  EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kNotFirst, kLast),
              StartSeqNumsAre(seq_num));
  EXPECT_THAT(Insert(seq_num - 1, kDeltaFrame, kNotFirst, kLast).packets,
              IsEmpty());
}

TEST_F(PacketBufferTest, TwoIncompleteFramesFullBuffer) {
  const uint16_t seq_num = Rand();

  for (int i = 1; i < kMaxSize - 1; ++i)
    Insert(seq_num + i, kDeltaFrame, kNotFirst, kNotLast);
  EXPECT_THAT(Insert(seq_num, kDeltaFrame, kFirst, kNotLast).packets,
              IsEmpty());
  EXPECT_THAT(Insert(seq_num - 1, kDeltaFrame, kNotFirst, kLast).packets,
              IsEmpty());
}

TEST_F(PacketBufferTest, FramesReordered) {
  const uint16_t seq_num = Rand();

  EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num + 1));
  EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num));
  EXPECT_THAT(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num + 3));
  EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast),
              StartSeqNumsAre(seq_num + 2));
}

TEST_F(PacketBufferTest, InsertPacketAfterSequenceNumberWrapAround) {
  uint16_t kFirstSeqNum = 0;
  uint32_t kTimestampDelta = 100;
  uint32_t timestamp = 10000;
  uint16_t seq_num = kFirstSeqNum;

  // Loop until seq_num wraps around.
  SeqNumUnwrapper<uint16_t> unwrapper;
  while (unwrapper.Unwrap(seq_num) < std::numeric_limits<uint16_t>::max()) {
    Insert(seq_num++, kKeyFrame, kFirst, kNotLast, {}, timestamp);
    for (int i = 0; i < 5; ++i) {
      Insert(seq_num++, kKeyFrame, kNotFirst, kNotLast, {}, timestamp);
    }
    Insert(seq_num++, kKeyFrame, kNotFirst, kLast, {}, timestamp);
    timestamp += kTimestampDelta;
  }

  // Receive frame with overlapping sequence numbers.
  Insert(seq_num++, kKeyFrame, kFirst, kNotLast, {}, timestamp);
  for (int i = 0; i < 5; ++i) {
    Insert(seq_num++, kKeyFrame, kNotFirst, kNotLast, {}, timestamp);
  }
  auto packets =
      Insert(seq_num++, kKeyFrame, kNotFirst, kLast, {}, timestamp).packets;
  // One frame of 7 packets.
  EXPECT_THAT(StartSeqNums(packets), SizeIs(1));
  EXPECT_THAT(packets, SizeIs(7));
}

// If `sps_pps_idr_is_keyframe` is true, we require keyframes to contain
// SPS/PPS/IDR and the keyframes we create as part of the test do contain
// SPS/PPS/IDR. If `sps_pps_idr_is_keyframe` is false, we only require and
// create keyframes containing only IDR.
class PacketBufferH264Test : public PacketBufferTest {
 protected:
  explicit PacketBufferH264Test(bool sps_pps_idr_is_keyframe)
      : PacketBufferTest(), sps_pps_idr_is_keyframe_(sps_pps_idr_is_keyframe) {
    if (sps_pps_idr_is_keyframe) {
      packet_buffer_.ForceSpsPpsIdrIsH264Keyframe();
    }
  }

  PacketBufferInsertResult InsertH264(
      uint16_t seq_num,     // packet sequence number
      IsKeyFrame keyframe,  // is keyframe
      IsFirst first,        // is first packet of frame
      IsLast last,          // is last packet of frame
      uint32_t timestamp,   // rtp timestamp
      rtc::ArrayView<const uint8_t> data = {},
      uint32_t width = 0,     // width of frame (SPS/IDR)
      uint32_t height = 0) {  // height of frame (SPS/IDR)
    auto packet = std::make_unique<PacketBuffer::Packet>();
    packet->video_header.codec = kVideoCodecH264;
    auto& h264_header =
        packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
    packet->seq_num = seq_num;
    packet->timestamp = timestamp;
    if (keyframe == kKeyFrame) {
      if (sps_pps_idr_is_keyframe_) {
        h264_header.nalus[0].type = H264::NaluType::kSps;
        h264_header.nalus[1].type = H264::NaluType::kPps;
        h264_header.nalus[2].type = H264::NaluType::kIdr;
        h264_header.nalus_length = 3;
      } else {
        h264_header.nalus[0].type = H264::NaluType::kIdr;
        h264_header.nalus_length = 1;
      }
    }
    packet->video_header.width = width;
    packet->video_header.height = height;
    packet->video_header.is_first_packet_in_frame = first == kFirst;
    packet->video_header.is_last_packet_in_frame = last == kLast;
    packet->video_payload.SetData(data.data(), data.size());

    return PacketBufferInsertResult(
        packet_buffer_.InsertPacket(std::move(packet)));
  }

  PacketBufferInsertResult InsertH264KeyFrameWithAud(
      uint16_t seq_num,     // packet sequence number
      IsKeyFrame keyframe,  // is keyframe
      IsFirst first,        // is first packet of frame
      IsLast last,          // is last packet of frame
      uint32_t timestamp,   // rtp timestamp
      rtc::ArrayView<const uint8_t> data = {},
      uint32_t width = 0,     // width of frame (SPS/IDR)
      uint32_t height = 0) {  // height of frame (SPS/IDR)
    auto packet = std::make_unique<PacketBuffer::Packet>();
    packet->video_header.codec = kVideoCodecH264;
    auto& h264_header =
        packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
    packet->seq_num = seq_num;
    packet->timestamp = timestamp;

    // this should be the start of frame.
    RTC_CHECK(first == kFirst);

    // Insert a AUD NALU / packet without width/height.
    h264_header.nalus[0].type = H264::NaluType::kAud;
    h264_header.nalus_length = 1;
    packet->video_header.is_first_packet_in_frame = true;
    packet->video_header.is_last_packet_in_frame = false;
    IgnoreResult(packet_buffer_.InsertPacket(std::move(packet)));
    // insert IDR
    return InsertH264(seq_num + 1, keyframe, kNotFirst, last, timestamp, data,
                      width, height);
  }

  const bool sps_pps_idr_is_keyframe_;
};

// This fixture is used to test the general behaviour of the packet buffer
// in both configurations.
class PacketBufferH264ParameterizedTest
    : public ::testing::WithParamInterface<bool>,
      public PacketBufferH264Test {
 protected:
  PacketBufferH264ParameterizedTest() : PacketBufferH264Test(GetParam()) {}
};

INSTANTIATE_TEST_SUITE_P(SpsPpsIdrIsKeyframe,
                         PacketBufferH264ParameterizedTest,
                         ::testing::Bool());

TEST_P(PacketBufferH264ParameterizedTest, DontRemoveMissingPacketOnClearTo) {
  InsertH264(0, kKeyFrame, kFirst, kLast, 0);
  InsertH264(2, kDeltaFrame, kFirst, kNotLast, 2);
  packet_buffer_.ClearTo(0);
  // Expect no frame because of missing of packet #1
  EXPECT_THAT(InsertH264(3, kDeltaFrame, kNotFirst, kLast, 2).packets,
              IsEmpty());
}

TEST_P(PacketBufferH264ParameterizedTest, GetBitstreamOneFrameFullBuffer) {
  uint8_t data_arr[kStartSize][1];
  uint8_t expected[kStartSize];

  for (uint8_t i = 0; i < kStartSize; ++i) {
    data_arr[i][0] = i;
    expected[i] = i;
  }

  InsertH264(0, kKeyFrame, kFirst, kNotLast, 1, data_arr[0]);
  for (uint8_t i = 1; i < kStartSize - 1; ++i) {
    InsertH264(i, kKeyFrame, kNotFirst, kNotLast, 1, data_arr[i]);
  }

  auto packets = InsertH264(kStartSize - 1, kKeyFrame, kNotFirst, kLast, 1,
                            data_arr[kStartSize - 1])
                     .packets;
  ASSERT_THAT(StartSeqNums(packets), ElementsAre(0));
  EXPECT_THAT(packets, SizeIs(kStartSize));
  for (size_t i = 0; i < packets.size(); ++i) {
    EXPECT_THAT(packets[i]->video_payload, SizeIs(1)) << "Packet #" << i;
  }
}

TEST_P(PacketBufferH264ParameterizedTest, GetBitstreamBufferPadding) {
  uint16_t seq_num = Rand();
  rtc::CopyOnWriteBuffer data = "some plain old data";

  auto packet = std::make_unique<PacketBuffer::Packet>();
  auto& h264_header =
      packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
  h264_header.nalus_length = 1;
  h264_header.nalus[0].type = H264::NaluType::kIdr;
  h264_header.packetization_type = kH264SingleNalu;
  packet->seq_num = seq_num;
  packet->video_header.codec = kVideoCodecH264;
  packet->video_payload = data;
  packet->video_header.is_first_packet_in_frame = true;
  packet->video_header.is_last_packet_in_frame = true;
  auto frames = packet_buffer_.InsertPacket(std::move(packet)).packets;

  ASSERT_THAT(frames, SizeIs(1));
  EXPECT_EQ(frames[0]->seq_num, seq_num);
  EXPECT_EQ(frames[0]->video_payload, data);
}

TEST_P(PacketBufferH264ParameterizedTest, FrameResolution) {
  uint16_t seq_num = 100;
  uint8_t data[] = "some plain old data";
  uint32_t width = 640;
  uint32_t height = 360;
  uint32_t timestamp = 1000;

  auto packets = InsertH264(seq_num, kKeyFrame, kFirst, kLast, timestamp, data,
                            width, height)
                     .packets;

  ASSERT_THAT(packets, SizeIs(1));
  EXPECT_EQ(packets[0]->video_header.width, width);
  EXPECT_EQ(packets[0]->video_header.height, height);
}

TEST_P(PacketBufferH264ParameterizedTest, FrameResolutionNaluBeforeSPS) {
  uint16_t seq_num = 100;
  uint8_t data[] = "some plain old data";
  uint32_t width = 640;
  uint32_t height = 360;
  uint32_t timestamp = 1000;

  auto packets = InsertH264KeyFrameWithAud(seq_num, kKeyFrame, kFirst, kLast,
                                           timestamp, data, width, height)
                     .packets;

  ASSERT_THAT(StartSeqNums(packets), ElementsAre(seq_num));
  EXPECT_EQ(packets[0]->video_header.width, width);
  EXPECT_EQ(packets[0]->video_header.height, height);
}

TEST_F(PacketBufferTest, FreeSlotsOnFrameCreation) {
  const uint16_t seq_num = Rand();

  Insert(seq_num, kKeyFrame, kFirst, kNotLast);
  Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast);
  EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kLast),
              StartSeqNumsAre(seq_num));

  // Insert frame that fills the whole buffer.
  Insert(seq_num + 3, kKeyFrame, kFirst, kNotLast);
  for (int i = 0; i < kMaxSize - 2; ++i)
    Insert(seq_num + i + 4, kDeltaFrame, kNotFirst, kNotLast);
  EXPECT_THAT(Insert(seq_num + kMaxSize + 2, kKeyFrame, kNotFirst, kLast),
              StartSeqNumsAre(seq_num + 3));
}

TEST_F(PacketBufferTest, Clear) {
  const uint16_t seq_num = Rand();

  Insert(seq_num, kKeyFrame, kFirst, kNotLast);
  Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast);
  EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kLast),
              StartSeqNumsAre(seq_num));

  packet_buffer_.Clear();

  Insert(seq_num + kStartSize, kKeyFrame, kFirst, kNotLast);
  Insert(seq_num + kStartSize + 1, kDeltaFrame, kNotFirst, kNotLast);
  EXPECT_THAT(Insert(seq_num + kStartSize + 2, kDeltaFrame, kNotFirst, kLast),
              StartSeqNumsAre(seq_num + kStartSize));
}

TEST_F(PacketBufferTest, FramesAfterClear) {
  Insert(9025, kDeltaFrame, kFirst, kLast);
  Insert(9024, kKeyFrame, kFirst, kLast);
  packet_buffer_.ClearTo(9025);
  EXPECT_THAT(Insert(9057, kDeltaFrame, kFirst, kLast).packets, SizeIs(1));
  EXPECT_THAT(Insert(9026, kDeltaFrame, kFirst, kLast).packets, SizeIs(1));
}

TEST_F(PacketBufferTest, SameFrameDifferentTimestamps) {
  Insert(0, kKeyFrame, kFirst, kNotLast, {}, 1000);
  EXPECT_THAT(Insert(1, kKeyFrame, kNotFirst, kLast, {}, 1001).packets,
              IsEmpty());
}

TEST_F(PacketBufferTest, ContinuousSeqNumDoubleMarkerBit) {
  Insert(2, kKeyFrame, kNotFirst, kNotLast);
  Insert(1, kKeyFrame, kFirst, kLast);
  EXPECT_THAT(Insert(3, kKeyFrame, kNotFirst, kLast).packets, IsEmpty());
}

TEST_F(PacketBufferTest, IncomingCodecChange) {
  auto packet = std::make_unique<PacketBuffer::Packet>();
  packet->video_header.is_first_packet_in_frame = true;
  packet->video_header.is_last_packet_in_frame = true;
  packet->video_header.codec = kVideoCodecVP8;
  packet->video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
  packet->timestamp = 1;
  packet->seq_num = 1;
  packet->video_header.frame_type = VideoFrameType::kVideoFrameKey;
  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              SizeIs(1));

  packet = std::make_unique<PacketBuffer::Packet>();
  packet->video_header.is_first_packet_in_frame = true;
  packet->video_header.is_last_packet_in_frame = true;
  packet->video_header.codec = kVideoCodecH264;
  auto& h264_header =
      packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
  h264_header.nalus_length = 1;
  packet->timestamp = 3;
  packet->seq_num = 3;
  packet->video_header.frame_type = VideoFrameType::kVideoFrameKey;
  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              IsEmpty());

  packet = std::make_unique<PacketBuffer::Packet>();
  packet->video_header.is_first_packet_in_frame = true;
  packet->video_header.is_last_packet_in_frame = true;
  packet->video_header.codec = kVideoCodecVP8;
  packet->video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
  packet->timestamp = 2;
  packet->seq_num = 2;
  packet->video_header.frame_type = VideoFrameType::kVideoFrameDelta;
  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              SizeIs(2));
}

TEST_F(PacketBufferTest, TooManyNalusInPacket) {
  auto packet = std::make_unique<PacketBuffer::Packet>();
  packet->video_header.codec = kVideoCodecH264;
  packet->timestamp = 1;
  packet->seq_num = 1;
  packet->video_header.frame_type = VideoFrameType::kVideoFrameKey;
  packet->video_header.is_first_packet_in_frame = true;
  packet->video_header.is_last_packet_in_frame = true;
  auto& h264_header =
      packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
  h264_header.nalus_length = kMaxNalusPerPacket;
  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              IsEmpty());
}

TEST_P(PacketBufferH264ParameterizedTest, OneFrameFillBuffer) {
  InsertH264(0, kKeyFrame, kFirst, kNotLast, 1000);
  for (int i = 1; i < kStartSize - 1; ++i)
    InsertH264(i, kKeyFrame, kNotFirst, kNotLast, 1000);
  EXPECT_THAT(InsertH264(kStartSize - 1, kKeyFrame, kNotFirst, kLast, 1000),
              StartSeqNumsAre(0));
}

TEST_P(PacketBufferH264ParameterizedTest, CreateFramesAfterFilledBuffer) {
  EXPECT_THAT(InsertH264(kStartSize - 2, kKeyFrame, kFirst, kLast, 0).packets,
              SizeIs(1));

  InsertH264(kStartSize, kDeltaFrame, kFirst, kNotLast, 2000);
  for (int i = 1; i < kStartSize; ++i)
    InsertH264(kStartSize + i, kDeltaFrame, kNotFirst, kNotLast, 2000);
  EXPECT_THAT(
      InsertH264(kStartSize + kStartSize, kDeltaFrame, kNotFirst, kLast, 2000)
          .packets,
      IsEmpty());

  EXPECT_THAT(InsertH264(kStartSize - 1, kKeyFrame, kFirst, kLast, 1000),
              StartSeqNumsAre(kStartSize - 1, kStartSize));
}

TEST_P(PacketBufferH264ParameterizedTest, OneFrameMaxSeqNum) {
  InsertH264(65534, kKeyFrame, kFirst, kNotLast, 1000);
  EXPECT_THAT(InsertH264(65535, kKeyFrame, kNotFirst, kLast, 1000),
              StartSeqNumsAre(65534));
}

TEST_P(PacketBufferH264ParameterizedTest, ClearMissingPacketsOnKeyframe) {
  InsertH264(0, kKeyFrame, kFirst, kLast, 1000);
  InsertH264(2, kKeyFrame, kFirst, kLast, 3000);
  InsertH264(3, kDeltaFrame, kFirst, kNotLast, 4000);
  InsertH264(4, kDeltaFrame, kNotFirst, kLast, 4000);

  EXPECT_THAT(InsertH264(kStartSize + 1, kKeyFrame, kFirst, kLast, 18000),
              StartSeqNumsAre(kStartSize + 1));
}

TEST_P(PacketBufferH264ParameterizedTest, FindFramesOnPadding) {
  EXPECT_THAT(InsertH264(0, kKeyFrame, kFirst, kLast, 1000),
              StartSeqNumsAre(0));
  EXPECT_THAT(InsertH264(2, kDeltaFrame, kFirst, kLast, 1000).packets,
              IsEmpty());

  EXPECT_THAT(packet_buffer_.InsertPadding(1), StartSeqNumsAre(2));
}

TEST_P(PacketBufferH264ParameterizedTest, FindFramesOnReorderedPadding) {
  EXPECT_THAT(InsertH264(0, kKeyFrame, kFirst, kLast, 1001),
              StartSeqNumsAre(0));
  EXPECT_THAT(InsertH264(1, kDeltaFrame, kFirst, kNotLast, 1002).packets,
              IsEmpty());
  EXPECT_THAT(packet_buffer_.InsertPadding(3).packets, IsEmpty());
  EXPECT_THAT(InsertH264(4, kDeltaFrame, kFirst, kLast, 1003).packets,
              IsEmpty());
  EXPECT_THAT(InsertH264(2, kDeltaFrame, kNotFirst, kLast, 1002),
              StartSeqNumsAre(1, 4));
}

class PacketBufferH264XIsKeyframeTest : public PacketBufferH264Test {
 protected:
  const uint16_t kSeqNum = 5;

  explicit PacketBufferH264XIsKeyframeTest(bool sps_pps_idr_is_keyframe)
      : PacketBufferH264Test(sps_pps_idr_is_keyframe) {}

  std::unique_ptr<PacketBuffer::Packet> CreatePacket() {
    auto packet = std::make_unique<PacketBuffer::Packet>();
    packet->video_header.codec = kVideoCodecH264;
    packet->seq_num = kSeqNum;

    packet->video_header.is_first_packet_in_frame = true;
    packet->video_header.is_last_packet_in_frame = true;
    return packet;
  }
};

class PacketBufferH264IdrIsKeyframeTest
    : public PacketBufferH264XIsKeyframeTest {
 protected:
  PacketBufferH264IdrIsKeyframeTest()
      : PacketBufferH264XIsKeyframeTest(false) {}
};

TEST_F(PacketBufferH264IdrIsKeyframeTest, IdrIsKeyframe) {
  auto packet = CreatePacket();
  auto& h264_header =
      packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
  h264_header.nalus[0].type = H264::NaluType::kIdr;
  h264_header.nalus_length = 1;
  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              ElementsAre(KeyFrame()));
}

TEST_F(PacketBufferH264IdrIsKeyframeTest, SpsPpsIdrIsKeyframe) {
  auto packet = CreatePacket();
  auto& h264_header =
      packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
  h264_header.nalus[0].type = H264::NaluType::kSps;
  h264_header.nalus[1].type = H264::NaluType::kPps;
  h264_header.nalus[2].type = H264::NaluType::kIdr;
  h264_header.nalus_length = 3;

  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              ElementsAre(KeyFrame()));
}

class PacketBufferH264SpsPpsIdrIsKeyframeTest
    : public PacketBufferH264XIsKeyframeTest {
 protected:
  PacketBufferH264SpsPpsIdrIsKeyframeTest()
      : PacketBufferH264XIsKeyframeTest(true) {}
};

TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, IdrIsNotKeyframe) {
  auto packet = CreatePacket();
  auto& h264_header =
      packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
  h264_header.nalus[0].type = H264::NaluType::kIdr;
  h264_header.nalus_length = 1;

  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              ElementsAre(DeltaFrame()));
}

TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, SpsPpsIsNotKeyframe) {
  auto packet = CreatePacket();
  auto& h264_header =
      packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
  h264_header.nalus[0].type = H264::NaluType::kSps;
  h264_header.nalus[1].type = H264::NaluType::kPps;
  h264_header.nalus_length = 2;

  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              ElementsAre(DeltaFrame()));
}

TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, SpsPpsIdrIsKeyframe) {
  auto packet = CreatePacket();
  auto& h264_header =
      packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>();
  h264_header.nalus[0].type = H264::NaluType::kSps;
  h264_header.nalus[1].type = H264::NaluType::kPps;
  h264_header.nalus[2].type = H264::NaluType::kIdr;
  h264_header.nalus_length = 3;

  EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets,
              ElementsAre(KeyFrame()));
}

}  // namespace
}  // namespace video_coding
}  // namespace webrtc
