From 34e05a6d769f33781dc00873515e901b9c976d95 Mon Sep 17 00:00:00 2001
From: Dan Sanders <sandersd@chromium.org>
Date: Thu, 31 Oct 2024 19:51:35 +0000
Subject: [PATCH] [media] Workaround for HEVC L1T2 with
 sps_max_sub_layers_minus1 = 0

When sps_max_sub_layers_minus1 = 0 but there are temporal layers, we
find that sps_max_dec_pic_buffering_minus1[temporal_id] is not present.
The specification does not clarify what the value should be, which
implies that it is not valid.

Previously, we would infer the value to be zero, which requires that
temporal layers must consist only of IRAP pictures. This is not
compatible with streams produced by Apple's hardware encoder.

This change instead infers the value to be
sps_max_dec_pic_buffering_minus1[sps_max_sub_layers_minus1].

Bug: 369963046
Change-Id: I1e2c508fccdb749753e9ddc1ab656ca89e4a6914
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5980228
Commit-Queue: Dan Sanders <sandersd@chromium.org>
Reviewed-by: Sida Zhu <zhusida@bytedance.com>
Cr-Commit-Position: refs/heads/main@{#1376532}
---
 media/parsers/h265_parser.cc | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/media/parsers/h265_parser.cc b/media/parsers/h265_parser.cc
index f91ea30968a34..31ce200eb8a63 100644
--- a/media/parsers/h265_parser.cc
+++ b/media/parsers/h265_parser.cc
@@ -1164,6 +1164,11 @@ H265Parser::Result H265Parser::ParseSliceHeader(const H265NALU& nalu,
   sps = GetSPS(pps->pps_seq_parameter_set_id);
   DCHECK(sps);  // We already validated this when we parsed the PPS.
 
+  // Workaround for crbug.com/369963046; Apple encoder produces L1T2 streams
+  // with sps_max_sub_layers_minus1 = 0.
+  int clamped_temporal_id =
+      std::min(shdr->temporal_id, sps->sps_max_sub_layers_minus1);
+
   if (!shdr->first_slice_segment_in_pic_flag) {
     if (pps->dependent_slice_segments_enabled_flag)
       READ_BOOL_OR_RETURN(&shdr->dependent_slice_segment_flag);
@@ -1187,7 +1192,7 @@ H265Parser::Result H265Parser::ParseSliceHeader(const H265NALU& nalu,
     // We also need to validate the fields that have conditions that depend on
     // anything unique in this slice (i.e. anything already parsed).
     if ((shdr->irap_pic ||
-         sps->sps_max_dec_pic_buffering_minus1[shdr->temporal_id] == 0) &&
+         sps->sps_max_dec_pic_buffering_minus1[clamped_temporal_id] == 0) &&
         nalu.nuh_layer_id == 0) {
       TRUE_OR_RETURN(shdr->slice_type == 2);
     }
@@ -1211,7 +1216,7 @@ H265Parser::Result H265Parser::ParseSliceHeader(const H265NALU& nalu,
     SKIP_BITS_OR_RETURN(pps->num_extra_slice_header_bits);
     READ_UE_OR_RETURN(&shdr->slice_type);
     if ((shdr->irap_pic ||
-         sps->sps_max_dec_pic_buffering_minus1[shdr->temporal_id] == 0) &&
+         sps->sps_max_dec_pic_buffering_minus1[clamped_temporal_id] == 0) &&
         nalu.nuh_layer_id == 0) {
       TRUE_OR_RETURN(shdr->slice_type == 2);
     }
@@ -1261,7 +1266,7 @@ H265Parser::Result H265Parser::ParseSliceHeader(const H265NALU& nalu,
         if (nalu.nuh_layer_id == 0) {
           TRUE_OR_RETURN(
               shdr->num_long_term_pics <=
-              (sps->sps_max_dec_pic_buffering_minus1[shdr->temporal_id] -
+              (sps->sps_max_dec_pic_buffering_minus1[clamped_temporal_id] -
                shdr->GetStRefPicSet(sps).num_negative_pics -
                shdr->GetStRefPicSet(sps).num_positive_pics -
                shdr->num_long_term_sps));