v4l2_stateful_decoder: Add HEVC mini parser

This adds an HEVC bitstream parser for use with v4l2_stateful_decoder.
The parser is used to split an HEVC Annex-B bitstream into frames. If
there are metadata NALU's before the next frame is completed, they will
be included in the next frame's buffer. If the next frame is made up of
multiple slices, all slices will be included with the next frame's
buffer.

BUG=b:227248846
TEST=Manually tested with HEVC sources on Trogdor

Change-Id: Ib51d23f75f6ce67bafc54c401116a927d41b521c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/drm-tests/+/3567602
Tested-by: Nathan Hebert <nhebert@chromium.org>
Reviewed-by: Fritz Koenig <frkoenig@chromium.org>
Commit-Queue: Nathan Hebert <nhebert@chromium.org>
diff --git a/bitstreams/bitstream_helper.c b/bitstreams/bitstream_helper.c
index ae0f431..fcddf0e 100644
--- a/bitstreams/bitstream_helper.c
+++ b/bitstreams/bitstream_helper.c
@@ -10,6 +10,7 @@
 #include <sys/mman.h>
 
 #include "bitstreams/bitstream_helper_h264.h"
+#include "bitstreams/bitstream_helper_h265.h"
 #include "bitstreams/bitstream_helper_ivf.h"
 #include "v4l2_macros.h"
 
@@ -47,6 +48,14 @@
     fill_compressed_buffer = fill_compressed_buffer_h264;
 
     return true;
+  } else if (init_bitstream_h265()) {
+    LOG_INFO("Initializing H265 bitstream");
+
+    get_fourcc = get_fourcc_h265;
+    is_end_of_stream = is_end_of_stream_h265;
+    fill_compressed_buffer = fill_compressed_buffer_h265;
+
+    return true;
   } else if (init_bitstream_ivf()) {
     LOG_INFO("Initializing VP8/VP9 bitstream");
     get_fourcc = get_fourcc_ivf;
diff --git a/bitstreams/bitstream_helper_h265.c b/bitstreams/bitstream_helper_h265.c
new file mode 100644
index 0000000..ba42b5b
--- /dev/null
+++ b/bitstreams/bitstream_helper_h265.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <linux/videodev2.h>
+#include <string.h>
+
+#include "bitstreams/bitstream_helper.h"
+#include "bitstreams/bitstream_helper_h265.h"
+#include "bitstreams/h265_partial_parser.h"
+#include "bitstreams/nalu_parser.h"
+#include "v4l2_macros.h"
+
+static const uint32_t kH265Fourcc = v4l2_fourcc('H', 'E', 'V', 'C');
+
+bool init_bitstream_h265(void) {
+  // Sometimes H.265 NALU streams are padded with a leading zero, sometimes
+  // they are not.
+  assert(filesize > 5);
+  uint8_t* first_nalu = find_next_nalu(file_buf, file_buf + 3);
+  if (NULL == first_nalu)
+    return false;
+
+  curr_pos = first_nalu - file_buf;
+
+  // Parse the first NALU to determine the type
+  const H265Nalu nalu = parse_h265_nalu(file_buf + curr_pos,
+                                        file_buf + filesize);
+  // TODO: Make a better "is H.265" test. All tast test vectors start with one
+  // of the following NALU types: VPS_NUT, AUD_NUT.
+  if (nalu.nal_unit_type != kVpsNut && nalu.nal_unit_type != kAudNut)
+    return false;
+
+  return true;
+}
+
+uint32_t get_fourcc_h265(void) {
+  return kH265Fourcc;
+}
+
+bool is_end_of_stream_h265(void) {
+  return curr_pos >= filesize;
+}
+
+// V4L2 drivers expect all NALUs associated with a given frame to appear in the
+// same buffer. This function helps group NALUs together into a frame by parsing
+// them individually and then checking for new pictures.
+size_t fill_compressed_buffer_h265(uint8_t* dest, size_t max_len) {
+  assert(dest);
+
+  size_t num_bytes_filled = 0;
+  bool found_first_slice = false;
+
+  while (curr_pos + num_bytes_filled < filesize) {
+    // Parse the next NALU
+    const H265Nalu nalu = parse_h265_nalu(
+        file_buf + curr_pos + num_bytes_filled, file_buf + filesize);
+
+    // Frame boundaries can only be detected with slice headers.
+    if (is_h265_nalu_slice_type(nalu.nal_unit_type)) {
+      if (found_first_slice && is_h265_slice_first_in_picture(&nalu))
+        break;
+
+      found_first_slice = true;
+    }
+
+    num_bytes_filled += nalu.size;
+  }
+
+  // Fill the buffer with all data we've parsed
+  assert(num_bytes_filled < max_len);
+  if (num_bytes_filled) {
+    memcpy(dest, file_buf + curr_pos, num_bytes_filled);
+  }
+  curr_pos += num_bytes_filled;
+  return num_bytes_filled;
+}
diff --git a/bitstreams/bitstream_helper_h265.h b/bitstreams/bitstream_helper_h265.h
new file mode 100644
index 0000000..b9e53af
--- /dev/null
+++ b/bitstreams/bitstream_helper_h265.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __BITSTREAM_HELPER_H265_H__
+#define __BITSTREAM_HELPER_H265_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+bool init_bitstream_h265(void);
+
+uint32_t get_fourcc_h265(void);
+
+bool is_end_of_stream_h265(void);
+
+size_t fill_compressed_buffer_h265(uint8_t* dest, size_t max_len);
+
+#endif  // BITSTREAM_HELPER_H265_H
diff --git a/bitstreams/h265_partial_parser.c b/bitstreams/h265_partial_parser.c
new file mode 100644
index 0000000..dd79d30
--- /dev/null
+++ b/bitstreams/h265_partial_parser.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "bitstreams/bitfield_stream.h"
+#include "bitstreams/h265_partial_parser.h"
+#include "bitstreams/nalu_parser.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "bitstream_helper.h"
+#include "v4l2_macros.h"
+
+bool is_h265_nalu_slice_type(enum H265NaluType type) {
+  switch (type) {
+    case kTrailN:
+    case kTrailR:
+    case kTsaN:
+    case kTsaR:
+    case kStsaN:
+    case kStsaR:
+    case kRadlN:
+    case kRadlR:
+    case kRaslN:
+    case kRaslR:
+    case kBlaWLp:
+    case kBlaWRadl:
+    case kBlaNLp:
+    case kIdrWRadl:
+    case kIdrNLp:
+    case kCraNut:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool is_h265_slice_first_in_picture(const H265Nalu *nalu) {
+  bool first_slice_segment_in_pic_flag = false;
+  BitfieldStream bfs = bitfield_stream_init(nalu->data + NALU_SIGNATURE_SIZE,
+                                            nalu->data + nalu->size);
+  bitfield_stream_skip_bits(&bfs, 16);
+  bitfield_stream_read_bits(&bfs, 1, &first_slice_segment_in_pic_flag);
+  return first_slice_segment_in_pic_flag;
+}
+
+H265Nalu parse_h265_nalu(uint8_t* curr_pos, uint8_t* end_pos) {
+  const Nalu base_nalu = tokenize_nalu(curr_pos, end_pos);
+
+  H265Nalu nalu = {.data=base_nalu.data,
+                   .size=base_nalu.size};
+
+  BitfieldStream bfs = bitfield_stream_init(nalu.data + NALU_SIGNATURE_SIZE,
+                                            nalu.data + nalu.size);
+
+  uint8_t forbidden_bit = 0;
+  bitfield_stream_read_bits(&bfs, 1, &forbidden_bit);
+  assert(forbidden_bit == 0);
+  bitfield_stream_read_bits(&bfs, 6, &nalu.nal_unit_type);
+  bitfield_stream_read_bits(&bfs, 6, &nalu.nuh_layer_id);
+  bitfield_stream_read_bits(&bfs, 3, &nalu.nuh_temporal_id_plus1);
+
+  return nalu;
+}
diff --git a/bitstreams/h265_partial_parser.h b/bitstreams/h265_partial_parser.h
new file mode 100644
index 0000000..92ef9df
--- /dev/null
+++ b/bitstreams/h265_partial_parser.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef __H265_PARTIAL_PARSER_H__
+#define __H265_PARTIAL_PARSER_H__
+
+// NAL Unit types are taken from Table 7-1 of HEVC/H265 standard
+// http://www.itu.int/rec/T-REC-H.265-201410-I/en
+enum H265NaluType {
+  kTrailN = 0,
+  kTrailR = 1,
+  kTsaN = 2,
+  kTsaR = 3,
+  kStsaN = 4,
+  kStsaR = 5,
+  kRadlN = 6,
+  kRadlR = 7,
+  kRaslN = 8,
+  kRaslR = 9,
+  kRsvVclN10 = 10,
+  kRsvVclR11 = 11,
+  kRsvVclN12 = 12,
+  kRsvVclR13 = 13,
+  kRsvVclN14 = 14,
+  kRsvVclR15 = 15,
+  kBlaWLp = 16,
+  kBlaWRadl = 17,
+  kBlaNLp = 18,
+  kIdrWRadl = 19,
+  kIdrNLp = 20,
+  kCraNut = 21,
+  kRsvIrapVcl22 = 22,
+  kRsvIrapVcl23 = 23,
+  kRsvVcl24 = 24,
+  kRsvVcl25 = 25,
+  kRsvVcl26 = 26,
+  kRsvVcl27 = 27,
+  kRsvVcl28 = 28,
+  kRsvVcl29 = 29,
+  kRsvVcl30 = 30,
+  kRsvVcl31 = 31,
+  kVpsNut = 32,
+  kSpsNut = 33,
+  kPpsNut = 34,
+  kAudNut = 35,
+  kEosNut = 36,
+  kEobNut = 37,
+  kFdNut = 38,
+  kPrefixSeiNut = 39,
+  kSuffixSeiNut = 40,
+  kRsvNvcl41 = 41,
+  kRsvNvcl42 = 42,
+  kRsvNvcl43 = 43,
+  kRsvNvcl44 = 44,
+  kRsvNvcl45 = 45,
+  kRsvNvcl46 = 46,
+  kRsvNvcl47 = 47,
+  kUnspec48 = 48,
+  kUnspec49 = 49,
+  kUnspec50 = 50,
+  kUnspec51 = 51,
+  kUnspec52 = 52,
+  kUnspec53 = 53,
+  kUnspec54 = 54,
+  kUnspec55 = 55,
+  kUnspec56 = 56,
+  kUnspec57 = 57,
+  kUnspec58 = 58,
+  kUnspec59 = 59,
+  kUnspec60 = 60,
+  kUnspec61 = 61,
+  kUnspec62 = 62,
+  kUnspec63 = 63,
+};
+
+bool is_h265_nalu_slice_type(enum H265NaluType type);
+
+typedef struct H265Nalu {
+  const uint8_t* data;
+  size_t size;
+
+  uint8_t nal_unit_type;
+  uint8_t nuh_layer_id;
+  uint8_t nuh_temporal_id_plus1;
+} H265Nalu;
+
+H265Nalu parse_h265_nalu(uint8_t* curr_pos, uint8_t* end_pos);
+
+bool is_h265_slice_first_in_picture(const H265Nalu *nalu);
+
+#endif // __H265_PARTIAL_PARSER_H__
diff --git a/bitstreams/module.mk b/bitstreams/module.mk
index 90db883..84dcd74 100644
--- a/bitstreams/module.mk
+++ b/bitstreams/module.mk
@@ -11,5 +11,7 @@
   bitstreams/bitstream_helper.o \
   bitstreams/bitstream_helper_ivf.o \
   bitstreams/bitstream_helper_h264.o \
+  bitstreams/bitstream_helper_h265.o \
   bitstreams/h264_partial_parser.o \
+  bitstreams/h265_partial_parser.o \
   bitstreams/nalu_parser.o \