Include H.265 support in RTP video frame assembler.

This adds support of H.265 into the RTP video frame assembler, which is
now a public interface.

Bug: chromium:41480904
Change-Id: I74fd761949d0b095ba4526d2fa887e963f48abcb
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/367603
Commit-Queue: Jianlin Qiu <jianlin.qiu@intel.com>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43374}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 35c5926..935cb12 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -1606,6 +1606,10 @@
       "//third_party/abseil-cpp/absl/strings",
       "//third_party/abseil-cpp/absl/strings:string_view",
     ]
+
+    if (rtc_use_h265) {
+      deps += [ "video:rtp_video_frame_h265_assembler_unittests" ]
+    }
   }
 
   rtc_library("compile_all_headers") {
diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn
index 106140e..fd6cbd2 100644
--- a/api/video/BUILD.gn
+++ b/api/video/BUILD.gn
@@ -233,6 +233,27 @@
   ]
 }
 
+if (rtc_use_h265) {
+  rtc_library("rtp_video_frame_h265_assembler_unittests") {
+    testonly = true
+    sources = [ "rtp_video_frame_h265_assembler_unittests.cc" ]
+
+    deps = [
+      ":encoded_frame",
+      ":rtp_video_frame_assembler",
+      ":video_frame",
+      ":video_frame_type",
+      "..:array_view",
+      "../../modules/rtp_rtcp:rtp_rtcp",
+      "../../modules/rtp_rtcp:rtp_rtcp_format",
+      "../../modules/rtp_rtcp:rtp_video_header",
+      "../../modules/video_coding:codec_globals_headers",
+      "../../rtc_base:checks",
+      "../../test:test_support",
+    ]
+  }
+}
+
 rtc_source_set("video_codec_constants") {
   visibility = [ "*" ]
   sources = [ "video_codec_constants.h" ]
diff --git a/api/video/rtp_video_frame_assembler.cc b/api/video/rtp_video_frame_assembler.cc
index 2485eb3..ac71105 100644
--- a/api/video/rtp_video_frame_assembler.cc
+++ b/api/video/rtp_video_frame_assembler.cc
@@ -43,6 +43,10 @@
 #include "rtc_base/logging.h"
 #include "rtc_base/numerics/sequence_number_unwrapper.h"
 
+#ifdef RTC_ENABLE_H265
+#include "modules/rtp_rtcp/source/video_rtp_depacketizer_h265.h"
+#endif
+
 namespace webrtc {
 namespace {
 std::unique_ptr<VideoRtpDepacketizer> CreateDepacketizer(
@@ -61,9 +65,11 @@
     case RtpVideoFrameAssembler::kGeneric:
       return std::make_unique<VideoRtpDepacketizerGeneric>();
     case RtpVideoFrameAssembler::kH265:
-      // TODO(bugs.webrtc.org/13485): Implement VideoRtpDepacketizerH265
-      RTC_DCHECK_NOTREACHED();
+#ifdef RTC_ENABLE_H265
+      return std::make_unique<VideoRtpDepacketizerH265>();
+#else
       return nullptr;
+#endif
   }
   RTC_DCHECK_NOTREACHED();
   return nullptr;
diff --git a/api/video/rtp_video_frame_h265_assembler_unittests.cc b/api/video/rtp_video_frame_h265_assembler_unittests.cc
new file mode 100644
index 0000000..3c9d020
--- /dev/null
+++ b/api/video/rtp_video_frame_h265_assembler_unittests.cc
@@ -0,0 +1,156 @@
+/*
+ *  Copyright (c) 2024 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 <cstdint>
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include "api/array_view.h"
+#include "api/video/encoded_frame.h"
+#include "api/video/rtp_video_frame_assembler.h"
+#include "api/video/video_codec_type.h"
+#include "api/video/video_frame_type.h"
+#include "modules/rtp_rtcp/source/rtp_format.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 "rtc_base/checks.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::SizeIs;
+using ::testing::UnorderedElementsAre;
+using PayloadFormat = RtpVideoFrameAssembler::PayloadFormat;
+
+class PacketBuilder {
+ public:
+  explicit PacketBuilder(PayloadFormat format)
+      : format_(format), packet_to_send_(&extension_manager_) {}
+
+  PacketBuilder& WithSeqNum(uint16_t seq_num) {
+    seq_num_ = seq_num;
+    return *this;
+  }
+
+  PacketBuilder& WithPayload(rtc::ArrayView<const uint8_t> payload) {
+    payload_.assign(payload.begin(), payload.end());
+    return *this;
+  }
+
+  PacketBuilder& WithVideoHeader(const RTPVideoHeader& video_header) {
+    video_header_ = video_header;
+    return *this;
+  }
+
+  template <typename T, typename... Args>
+  PacketBuilder& WithExtension(int id, const Args&... args) {
+    extension_manager_.Register<T>(id);
+    packet_to_send_.IdentifyExtensions(extension_manager_);
+    packet_to_send_.SetExtension<T>(std::forward<const Args>(args)...);
+    return *this;
+  }
+
+  RtpPacketReceived Build() {
+    auto packetizer =
+        RtpPacketizer::Create(GetVideoCodecType(), payload_, {}, video_header_);
+    packetizer->NextPacket(&packet_to_send_);
+    packet_to_send_.SetSequenceNumber(seq_num_);
+
+    RtpPacketReceived received(&extension_manager_);
+    received.Parse(packet_to_send_.Buffer());
+    return received;
+  }
+
+ private:
+  std::optional<VideoCodecType> GetVideoCodecType() {
+    switch (format_) {
+      case PayloadFormat::kH265: {
+        return kVideoCodecH265;
+      }
+      default:
+        RTC_DCHECK_NOTREACHED();
+        return std::nullopt;
+    }
+  }
+
+  const RtpVideoFrameAssembler::PayloadFormat format_;
+  uint16_t seq_num_ = 0;
+  std::vector<uint8_t> payload_;
+  RTPVideoHeader video_header_;
+  RtpPacketReceived::ExtensionManager extension_manager_;
+  RtpPacketToSend packet_to_send_;
+};
+
+void AppendFrames(RtpVideoFrameAssembler::FrameVector&& from,
+                  RtpVideoFrameAssembler::FrameVector& to) {
+  to.insert(to.end(), std::make_move_iterator(from.begin()),
+            std::make_move_iterator(from.end()));
+}
+
+rtc::ArrayView<int64_t> References(const std::unique_ptr<EncodedFrame>& frame) {
+  return rtc::MakeArrayView(frame->references, frame->num_references);
+}
+
+rtc::ArrayView<uint8_t> Payload(const std::unique_ptr<EncodedFrame>& frame) {
+  return rtc::ArrayView<uint8_t>(*frame->GetEncodedData());
+}
+
+TEST(RtpVideoFrameH265Assembler, H265Packetization) {
+  RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kH265);
+  RtpVideoFrameAssembler::FrameVector frames;
+
+  // Key and delta frames generated on linux with ffmpeg command:
+  // `ffmpeg -i /dev/video0 -r 30 -c:v libx265 -s 1280x720 camera.h265`,
+  // truncated for test.
+  // IDR_N_LP(key) frame with start code included.
+  uint8_t kIdrPayload[] = {0x00, 0x00, 0x00, 0x01, 0x28, 0x01, 0xaf,
+                           0x08, 0x4a, 0x31, 0x11, 0x15, 0xe5, 0xc0};
+  // TRAIL_R(delta) frame with start code included.
+  uint8_t kDeltaPayload[] = {0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0xd0,
+                             0x09, 0x7e, 0x10, 0xc6, 0x1c, 0x8c, 0x17};
+
+  RTPVideoHeader video_header;
+  video_header.frame_type = VideoFrameType::kVideoFrameKey;
+  RtpVideoFrameAssembler::FrameVector idr_frames =
+      assembler.InsertPacket(PacketBuilder(PayloadFormat::kH265)
+                                 .WithPayload(kIdrPayload)
+                                 .WithVideoHeader(video_header)
+                                 .WithSeqNum(10)
+                                 .Build());
+  AppendFrames(std::move(idr_frames), frames);
+
+  RtpVideoFrameAssembler::FrameVector delta_frames =
+      assembler.InsertPacket(PacketBuilder(PayloadFormat::kH265)
+                                 .WithPayload(kDeltaPayload)
+                                 .WithSeqNum(11)
+                                 .Build());
+  AppendFrames(std::move(delta_frames), frames);
+  ASSERT_THAT(frames, SizeIs(2));
+
+  auto first_frame = frames[0].ExtractFrame();
+  EXPECT_THAT(first_frame->Id(), Eq(10));
+  EXPECT_THAT(Payload(first_frame), ElementsAreArray(kIdrPayload));
+  EXPECT_THAT(References(first_frame), IsEmpty());
+
+  auto second_frame = frames[1].ExtractFrame();
+  EXPECT_THAT(second_frame->Id(), Eq(11));
+  EXPECT_THAT(Payload(second_frame), ElementsAreArray(kDeltaPayload));
+  EXPECT_THAT(References(second_frame), UnorderedElementsAre(10));
+}
+
+}  // namespace
+}  // namespace webrtc