/*
 *  Copyright (c) 2020 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/rtp_rtcp/source/video_rtp_depacketizer_h264.h"

#include <cstdint>
#include <optional>
#include <vector>

#include "api/array_view.h"
#include "common_video/h264/h264_common.h"
#include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtp_format_h264.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "test/gmock.h"
#include "test/gtest.h"

namespace webrtc {
namespace {

using ::testing::Each;
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::SizeIs;

// clang-format off: split example data on NAL unit boundaries.
constexpr uint8_t kOriginalSps[] = {
    H264::kSps, 0x00, 0x00, 0x03, 0x03,
    0xF4, 0x05, 0x03, 0xC7, 0xC0
};
constexpr uint8_t kRewrittenSps[] = {
    H264::kSps, 0x00, 0x00, 0x03, 0x03,
    0xF4, 0x05, 0x03, 0xC7, 0xE0,
    0x1B, 0x41, 0x10, 0x8D, 0x00
};
constexpr uint8_t kIdrOne[] = {H264::kIdr, 0xFF, 0x00, 0x00, 0x04};
constexpr uint8_t kIdrTwo[] = {H264::kIdr, 0xFF, 0x00, 0x11};
// clang-format on

TEST(VideoRtpDepacketizerH264Test, SingleNalu) {
  const uint8_t kPayload[] = {H264::kIdr, 0xFF};  // F=0, NRI=0, Type=5 (IDR).
  rtc::CopyOnWriteBuffer rtp_payload(kPayload);

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtp_payload);
  ASSERT_TRUE(parsed);

  EXPECT_EQ(parsed->video_payload, rtp_payload);
  EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
  EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
  const RTPVideoHeaderH264& h264 =
      absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
  EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
  EXPECT_EQ(h264.nalu_type, H264::kIdr);
}

TEST(VideoRtpDepacketizerH264Test, SingleNaluSpsWithResolution) {
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      H264::kSps, 0x7A, 0x00, 0x1F, 0xBC, 0xD9,
      0x40, 0x50, 0x05, 0xBA, 0x10, 0x00,
      0x00, 0x03, 0x00, 0xC0, 0x00, 0x00,
      0x03, 0x2A, 0xE0, 0xF1, 0x83, 0x25
  };
  // clang-format on
  rtc::CopyOnWriteBuffer rtp_payload(kPayload);

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtp_payload);
  ASSERT_TRUE(parsed);

  EXPECT_EQ(parsed->video_payload, rtp_payload);
  EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
  EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
  EXPECT_EQ(parsed->video_header.width, 1280u);
  EXPECT_EQ(parsed->video_header.height, 720u);
  const auto& h264 =
      absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
  EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
}

TEST(VideoRtpDepacketizerH264Test, StapAKey) {
  const NaluInfo kExpectedNalus[] = {
      {H264::kSps, 0, -1}, {H264::kPps, 1, 2}, {H264::kIdr, -1, 0}};
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      H264::kStapA,  // F=0, NRI=0, Type=24.
      // Length (2 bytes), nal header, payload.
      0x00, 0x18,
      kExpectedNalus[0].type, 0x7A, 0x00, 0x1F, 0xBC, 0xD9, 0x40, 0x50, 0x05,
      0xBA, 0x10, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x2A, 0xE0,
      0xF1, 0x83, 0x25,
      0x00, 0xD,
      kExpectedNalus[1].type, 0x69, 0xFC, 0x0, 0x0, 0x3, 0x0, 0x7, 0xFF, 0xFF,
      0xFF, 0xF6, 0x40,
      0x00, 0xB,
      kExpectedNalus[2].type, 0x85, 0xB8, 0x0, 0x4, 0x0, 0x0, 0x13, 0x93, 0x12,
      0x0
  };
  // clang-format on
  rtc::CopyOnWriteBuffer rtp_payload(kPayload);

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtp_payload);
  ASSERT_TRUE(parsed);

  EXPECT_EQ(parsed->video_payload, rtp_payload);
  EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
  EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
  const auto& h264 =
      absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
  EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264StapA);
  // NALU type for aggregated packets is the type of the first packet only.
  EXPECT_EQ(h264.nalu_type, H264::kSps);
  EXPECT_THAT(h264.nalus, ElementsAreArray(kExpectedNalus));
}

TEST(VideoRtpDepacketizerH264Test, StapANaluSpsWithResolution) {
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      H264::kStapA,  // F=0, NRI=0, Type=24.
      // Length (2 bytes), nal header, payload.
      0x00, 0x19,
      H264::kSps, 0x7A, 0x00, 0x1F, 0xBC, 0xD9, 0x40, 0x50, 0x05, 0xBA, 0x10,
      0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x2A, 0xE0, 0xF1, 0x83,
      0x25, 0x80, 0x00, 0x03,
      H264::kIdr, 0xFF, 0x00,
      0x00, 0x04,
      H264::kIdr, 0xFF, 0x00, 0x11
  };
  // clang-format on
  rtc::CopyOnWriteBuffer rtp_payload(kPayload);

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtp_payload);
  ASSERT_TRUE(parsed);

  EXPECT_EQ(parsed->video_payload, rtp_payload);
  EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
  EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
  EXPECT_EQ(parsed->video_header.width, 1280u);
  EXPECT_EQ(parsed->video_header.height, 720u);
  const auto& h264 =
      absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
  EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264StapA);
}

TEST(VideoRtpDepacketizerH264Test, EmptyStapARejected) {
  // clang-format off: split example data on NAL unit boundaries.
  uint8_t lone_empty_packet[] = {
      H264::kStapA, 0x00, 0x00
  };
  uint8_t leading_empty_packet[] = {
      H264::kStapA,
      0x00, 0x00,  // Empty STAP-A is invalid.
      0x00, 0x04,
      H264::kIdr, 0xFF, 0x00, 0x11
  };
  uint8_t middle_empty_packet[] = {
      H264::kStapA,
      0x00, 0x03,
      H264::kIdr, 0xFF, 0x00, 0x00, 0x00,
      0x00, 0x04,
      H264::kIdr, 0xFF, 0x00, 0x11
  };
  uint8_t trailing_empty_packet[] = {
      H264::kStapA,
      0x00, 0x03,
      H264::kIdr, 0xFF, 0x00, 0x00, 0x00
  };
  // clang-format on

  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(lone_empty_packet)));
  EXPECT_FALSE(
      depacketizer.Parse(rtc::CopyOnWriteBuffer(leading_empty_packet)));
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(middle_empty_packet)));
  EXPECT_FALSE(
      depacketizer.Parse(rtc::CopyOnWriteBuffer(trailing_empty_packet)));
}

TEST(VideoRtpDepacketizerH264Test, DepacketizeWithRewriting) {
  rtc::CopyOnWriteBuffer in_buffer;
  rtc::Buffer out_buffer;

  uint8_t kHeader[] = {H264::kStapA, 0x00};
  in_buffer.AppendData(kHeader, 1);
  out_buffer.AppendData(kHeader, 1);

  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kOriginalSps));
  in_buffer.AppendData(kHeader, 2);
  in_buffer.AppendData(kOriginalSps);
  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kRewrittenSps));
  out_buffer.AppendData(kHeader, 2);
  out_buffer.AppendData(kRewrittenSps);

  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kIdrOne));
  in_buffer.AppendData(kHeader, 2);
  in_buffer.AppendData(kIdrOne);
  out_buffer.AppendData(kHeader, 2);
  out_buffer.AppendData(kIdrOne);

  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kIdrTwo));
  in_buffer.AppendData(kHeader, 2);
  in_buffer.AppendData(kIdrTwo);
  out_buffer.AppendData(kHeader, 2);
  out_buffer.AppendData(kIdrTwo);

  VideoRtpDepacketizerH264 depacketizer;
  auto parsed = depacketizer.Parse(in_buffer);
  ASSERT_TRUE(parsed);
  EXPECT_THAT(rtc::MakeArrayView(parsed->video_payload.cdata(),
                                 parsed->video_payload.size()),
              ElementsAreArray(out_buffer));
}

TEST(VideoRtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) {
  rtc::CopyOnWriteBuffer in_buffer;
  rtc::Buffer out_buffer;

  uint8_t kHeader[] = {H264::kStapA, 0x00};
  in_buffer.AppendData(kHeader, 1);
  out_buffer.AppendData(kHeader, 1);

  // First SPS will be kept...
  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kOriginalSps));
  in_buffer.AppendData(kHeader, 2);
  in_buffer.AppendData(kOriginalSps);
  out_buffer.AppendData(kHeader, 2);
  out_buffer.AppendData(kOriginalSps);

  // ...only the second one will be rewritten.
  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kOriginalSps));
  in_buffer.AppendData(kHeader, 2);
  in_buffer.AppendData(kOriginalSps);
  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kRewrittenSps));
  out_buffer.AppendData(kHeader, 2);
  out_buffer.AppendData(kRewrittenSps);

  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kIdrOne));
  in_buffer.AppendData(kHeader, 2);
  in_buffer.AppendData(kIdrOne);
  out_buffer.AppendData(kHeader, 2);
  out_buffer.AppendData(kIdrOne);

  ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kIdrTwo));
  in_buffer.AppendData(kHeader, 2);
  in_buffer.AppendData(kIdrTwo);
  out_buffer.AppendData(kHeader, 2);
  out_buffer.AppendData(kIdrTwo);

  VideoRtpDepacketizerH264 depacketizer;
  auto parsed = depacketizer.Parse(in_buffer);
  ASSERT_TRUE(parsed);
  std::vector<uint8_t> expected_packet_payload(
      out_buffer.data(), &out_buffer.data()[out_buffer.size()]);
  EXPECT_THAT(rtc::MakeArrayView(parsed->video_payload.cdata(),
                                 parsed->video_payload.size()),
              ElementsAreArray(out_buffer));
}

TEST(VideoRtpDepacketizerH264Test, StapADelta) {
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      H264::kStapA,  // F=0, NRI=0, Type=24.
      // Length (2 bytes), nal header, payload.
      0x00, 0x02,
      H264::kSlice, 0xFF,
      0x00, 0x03,
      H264::kSlice, 0xFF, 0x00,
      0x00, 0x04,
      H264::kSlice, 0xFF, 0x00, 0x11
  };
  // clang-format on
  rtc::CopyOnWriteBuffer rtp_payload(kPayload);

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtp_payload);
  ASSERT_TRUE(parsed);

  EXPECT_EQ(parsed->video_payload.size(), rtp_payload.size());
  EXPECT_EQ(parsed->video_payload.cdata(), rtp_payload.cdata());

  EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta);
  EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
  const RTPVideoHeaderH264& h264 =
      absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
  EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264StapA);
  // NALU type for aggregated packets is the type of the first packet only.
  EXPECT_EQ(h264.nalu_type, H264::kSlice);
}

TEST(VideoRtpDepacketizerH264Test, FuA) {
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload1[] = {
      H264::kFuA,              // F=0, NRI=0, Type=28.
      kH264SBit | H264::kIdr,  // FU header.
      0x85, 0xB8, 0x00, 0x04, 0x00, 0x00, 0x13, 0x93, 0x12, 0x00  // Payload.
  };
  const uint8_t kExpected1[] = {
      H264::kIdr, 0x85, 0xB8, 0x00,  0x04, 0x00,
      0x00, 0x13, 0x93, 0x12, 0x00};

  const uint8_t kPayload2[] = {
      H264::kFuA,  // F=0, NRI=0, Type=28.
      H264::kIdr,  // FU header.
      0x02         // Payload.
  };
  const uint8_t kExpected2[] = {0x02};

  const uint8_t kPayload3[] = {
      H264::kFuA,              // F=0, NRI=0, Type=28.
      kH264EBit | H264::kIdr,  // FU header.
      0x03                     // Payload.
  };
  const uint8_t kExpected3[] = {0x03};
  // clang-format on

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed1 =
      depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload1));
  ASSERT_TRUE(parsed1);
  // We expect that the first packet is one byte shorter since the FU-A header
  // has been replaced by the original nal header.
  EXPECT_THAT(rtc::MakeArrayView(parsed1->video_payload.cdata(),
                                 parsed1->video_payload.size()),
              ElementsAreArray(kExpected1));
  EXPECT_EQ(parsed1->video_header.frame_type, VideoFrameType::kVideoFrameKey);
  EXPECT_EQ(parsed1->video_header.codec, kVideoCodecH264);
  EXPECT_TRUE(parsed1->video_header.is_first_packet_in_frame);
  {
    const RTPVideoHeaderH264& h264 =
        absl::get<RTPVideoHeaderH264>(parsed1->video_header.video_type_header);
    EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264FuA);
    EXPECT_EQ(h264.nalu_type, H264::kIdr);
    ASSERT_THAT(h264.nalus, SizeIs(1));
    EXPECT_EQ(h264.nalus[0].type, H264::kIdr);
    EXPECT_EQ(h264.nalus[0].sps_id, -1);
    EXPECT_EQ(h264.nalus[0].pps_id, 0);
  }

  // Following packets will be 2 bytes shorter since they will only be appended
  // onto the first packet.
  auto parsed2 = depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload2));
  EXPECT_THAT(rtc::MakeArrayView(parsed2->video_payload.cdata(),
                                 parsed2->video_payload.size()),
              ElementsAreArray(kExpected2));
  EXPECT_FALSE(parsed2->video_header.is_first_packet_in_frame);
  EXPECT_EQ(parsed2->video_header.codec, kVideoCodecH264);
  {
    const RTPVideoHeaderH264& h264 =
        absl::get<RTPVideoHeaderH264>(parsed2->video_header.video_type_header);
    EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264FuA);
    EXPECT_EQ(h264.nalu_type, H264::kIdr);
    // NALU info is only expected for the first FU-A packet.
    EXPECT_THAT(h264.nalus, IsEmpty());
  }

  auto parsed3 = depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload3));
  EXPECT_THAT(rtc::MakeArrayView(parsed3->video_payload.cdata(),
                                 parsed3->video_payload.size()),
              ElementsAreArray(kExpected3));
  EXPECT_FALSE(parsed3->video_header.is_first_packet_in_frame);
  EXPECT_EQ(parsed3->video_header.codec, kVideoCodecH264);
  {
    const RTPVideoHeaderH264& h264 =
        absl::get<RTPVideoHeaderH264>(parsed3->video_header.video_type_header);
    EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264FuA);
    EXPECT_EQ(h264.nalu_type, H264::kIdr);
    // NALU info is only expected for the first FU-A packet.
    EXPECT_THAT(h264.nalus, IsEmpty());
  }
}

TEST(VideoRtpDepacketizerH264Test, EmptyPayload) {
  rtc::CopyOnWriteBuffer empty;
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(empty));
}

TEST(VideoRtpDepacketizerH264Test, TruncatedFuaNalu) {
  const uint8_t kPayload[] = {0x9c};
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}

TEST(VideoRtpDepacketizerH264Test, TruncatedSingleStapANalu) {
  const uint8_t kPayload[] = {0xd8, 0x27};
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}

TEST(VideoRtpDepacketizerH264Test, StapAPacketWithTruncatedNalUnits) {
  const uint8_t kPayload[] = {0x58, 0xCB, 0xED, 0xDF};
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}

TEST(VideoRtpDepacketizerH264Test, TruncationJustAfterSingleStapANalu) {
  const uint8_t kPayload[] = {0x38, 0x27, 0x27};
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}

TEST(VideoRtpDepacketizerH264Test, SeiPacket) {
  const uint8_t kPayload[] = {
      H264::kSei,             // F=0, NRI=0, Type=6.
      0x03, 0x03, 0x03, 0x03  // Payload.
  };
  VideoRtpDepacketizerH264 depacketizer;
  auto parsed = depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
  ASSERT_TRUE(parsed);
  const RTPVideoHeaderH264& h264 =
      absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
  EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta);
  EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
  EXPECT_EQ(h264.nalu_type, H264::kSei);
  ASSERT_THAT(h264.nalus, SizeIs(1));
  EXPECT_EQ(h264.nalus[0].type, H264::kSei);
  EXPECT_EQ(h264.nalus[0].sps_id, -1);
  EXPECT_EQ(h264.nalus[0].pps_id, -1);
}

TEST(VideoRtpDepacketizerH264Test, ShortSpsPacket) {
  const uint8_t kPayload[] = {0x27, 0x80, 0x00};
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}

TEST(VideoRtpDepacketizerH264Test, BadSps) {
  const uint8_t kPayload[] = {
      H264::kSps, 0x42, 0x41, 0x2a, 0xd3, 0x93, 0xd3, 0x3b  // Payload.
  };
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}

TEST(VideoRtpDepacketizerH264Test, BadPps) {
  const uint8_t kPayload[] = {
      H264::kPps,
      0x00  // Payload.
  };
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}

TEST(VideoRtpDepacketizerH264Test, BadSlice) {
  const uint8_t kPayload[] = {
      H264::kIdr,
      0xc0  // Payload.
  };
  VideoRtpDepacketizerH264 depacketizer;
  EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}

TEST(VideoRtpDepacketizerH264Test, StapASpsPpsMultiSlice) {
  // A STAP-A containing a black 320x192 key frame with multiple slices.
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      H264::kStapA,  // F=0, NRI=0, Type=24.
      0x00, 0x10,
      0x67, 0x42, 0xc0, 0x15, 0x8c, 0x68, 0x14, 0x19,  // SPS.
      0x79, 0xe0, 0x1e, 0x11, 0x08, 0xd4, 0x00, 0x04,
      0x00, 0x06,
      0x68, 0xce, 0x3c, 0x80, 0x00, 0x2e,  // PPS.
      // Slices.
      0x00, 0x30,
      0x65, 0xb8, 0x00, 0x04, 0x08, 0x79, 0x31, 0x40, 0x00, 0x42, 0xae, 0x4d,
      0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
      0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xd6, 0xeb, 0xae, 0xba, 0xeb, 0xae,
      0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xbc, 0x00, 0x2f,
      0x00, 0x2f,
      0x65, 0x05, 0x2e, 0x00, 0x01, 0x02, 0x1e, 0x4c, 0x50, 0x00, 0x10, 0xab,
      0x93, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
      0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x75, 0xba, 0xeb, 0xae, 0xba,
      0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xaf,
      0x00, 0x30,
      0x65, 0x02, 0x8b, 0x80, 0x00, 0x40, 0x87, 0x93, 0x14, 0x00, 0x04, 0x2a,
      0xe4, 0xdc, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
      0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x6e, 0xba, 0xeb, 0xae,
      0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xc0,
      0x00, 0x30,
      0x65, 0x03, 0xcb, 0x80, 0x00, 0x40, 0x87, 0x93, 0x14, 0x00, 0x04, 0x2a,
      0xe4, 0xdc, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
      0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x6e, 0xba, 0xeb, 0xae,
      0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xc0,
      0x00, 0x30,
      0x65, 0x01, 0x42, 0xe0, 0x00, 0x10, 0x21, 0xe4, 0xc5, 0x00, 0x01, 0x0a,
      0xb9, 0x37, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
      0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x5b, 0xae, 0xba, 0xeb,
      0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xf0,
      0x00, 0x30,
      0x65, 0x01, 0x92, 0xe0, 0x00, 0x10, 0x21, 0xe4, 0xc5, 0x00, 0x01, 0x0a,
      0xb9, 0x37, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
      0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x5b, 0xae, 0xba, 0xeb,
      0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xf0
  };
  // clang-format on

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
  ASSERT_TRUE(parsed);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
}

TEST(VideoRtpDepacketizerH264Test, SecondSliceIdrNalu) {
  // First few bytes of a second slice of an IDR nalu with
  // first_mb_in_slice = 480.
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      0x65, 0x00, 0xf0, 0x88, 0x82, 0x01, 0x3b, 0xff, 0xdf, 0xfe, 0x0b, 0xbb,
      0xfc, 0xb4, 0x30, 0xd1, 0x00, 0xef, 0xfd, 0xef, 0x0e, 0x79, 0x8b, 0x74,
      0x9b, 0x44, 0xf3, 0xb8, 0x65, 0x8f, 0xa1, 0x92, 0x30, 0xf9, 0x40, 0x06,
      0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
      0x03, 0x00, 0x18, 0x87, 0x4f, 0x6a, 0xfe, 0x60, 0x03, 0x9f, 0xfe, 0xd8,
      0x8b, 0xa6, 0x67, 0x31
  };
  // clang-format on

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
  ASSERT_TRUE(parsed);
  EXPECT_FALSE(parsed->video_header.is_first_packet_in_frame);
}

TEST(VideoRtpDepacketizerH264Test, AudSetsFirstPacketInFrame) {
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      H264::kAud, 0x10
  };
  // clang-format on

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
  ASSERT_TRUE(parsed);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
}

TEST(VideoRtpDepacketizerH264Test, PpsSetsFirstPacketInFrame) {
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      H264::kPps, 0x69, 0xFC, 0x00, 0x00, 0x03, 0x00,
      0x07, 0xFF, 0xFF, 0xFF, 0xF6, 0x40
  };
  // clang-format on

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
  ASSERT_TRUE(parsed);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
}

TEST(VideoRtpDepacketizerH264Test, SeiSetsFirstPacketInFrame) {
  // clang-format off: split example data on NAL unit boundaries.
  const uint8_t kPayload[] = {
      H264::kSei, 0x05, 0x04, 0xDE, 0xAD, 0xBE, 0xEF, 0x80
  };
  // clang-format on

  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
  ASSERT_TRUE(parsed);
  EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
}

TEST(VideoRtpDepacketizerH264Test, EmptyNaluPayload) {
  const uint8_t kPayload[] = {H264::kEndOfSequence};
  VideoRtpDepacketizerH264 depacketizer;
  std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
      depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
  ASSERT_TRUE(parsed);
}

}  // namespace
}  // namespace webrtc