h265decoder: Fix broken SPS/PPS link

Because of 2 pass nalu handling in decoder, link between
slice header and SPS/PPS can be broken at the second pass
if SPS/PPS got updated after slice header in the same AU

Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4323
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8673>
This commit is contained in:
Seungha Yang 2025-03-25 02:09:49 +09:00 committed by GStreamer Marge Bot
parent 4898020c28
commit a014556713

@ -32,6 +32,7 @@
#include <gst/base/base.h>
#include "gsth265decoder.h"
#include <gst/codecparsers/gsth265parser-private.h>
GST_DEBUG_CATEGORY (gst_h265_decoder_debug);
#define GST_CAT_DEFAULT gst_h265_decoder_debug
@ -67,6 +68,7 @@ struct _GstH265DecoderPrivate
GstH265DecoderFormat in_format;
GstH265DecoderAlign align;
GstH265Parser *parser;
GstH265Parser *preproc_parser;
GstH265Dpb *dpb;
/* 0: frame or field-pair interlaced stream
@ -144,10 +146,13 @@ typedef struct
{
union
{
GstH265VPS vps;
GstH265SPS sps;
GstH265PPS pps;
GstH265Slice slice;
} unit;
gboolean is_slice;
GstH265NalUnitType nalu_type;
guint pps_id;
} GstH265DecoderNalUnit;
typedef struct
@ -266,6 +271,7 @@ gst_h265_decoder_start (GstVideoDecoder * decoder)
GstH265DecoderPrivate *priv = self->priv;
priv->parser = gst_h265_parser_new ();
priv->preproc_parser = gst_h265_parser_new ();
priv->dpb = gst_h265_dpb_new ();
priv->new_bitstream = TRUE;
priv->prev_nal_is_eos = FALSE;
@ -290,6 +296,11 @@ gst_h265_decoder_stop (GstVideoDecoder * decoder)
priv->parser = NULL;
}
if (priv->preproc_parser) {
gst_h265_parser_free (priv->preproc_parser);
priv->preproc_parser = NULL;
}
if (priv->dpb) {
gst_h265_dpb_free (priv->dpb);
priv->dpb = NULL;
@ -626,7 +637,7 @@ gst_h265_decoder_parse_sei (GstH265Decoder * self, GstH265NalUnit * nalu)
GArray *messages = NULL;
guint i;
pres = gst_h265_parser_parse_sei (priv->parser, nalu, &messages);
pres = gst_h265_parser_parse_sei (priv->preproc_parser, nalu, &messages);
if (pres != GST_H265_PARSER_OK) {
GST_WARNING_OBJECT (self, "Failed to parse SEI, result %d", pres);
@ -904,7 +915,8 @@ gst_h265_decoder_parse_slice (GstH265Decoder * self, GstH265NalUnit * nalu)
memset (&slice, 0, sizeof (GstH265Slice));
pres = gst_h265_parser_parse_slice_hdr (priv->parser, nalu, &slice.header);
pres = gst_h265_parser_parse_slice_hdr (priv->preproc_parser,
nalu, &slice.header);
if (pres != GST_H265_PARSER_OK)
return pres;
@ -948,7 +960,9 @@ gst_h265_decoder_parse_slice (GstH265Decoder * self, GstH265NalUnit * nalu)
priv->no_output_of_prior_pics_flag = TRUE;
decoder_nalu.unit.slice = slice;
decoder_nalu.is_slice = TRUE;
decoder_nalu.nalu_type = nalu->type;
decoder_nalu.pps_id = slice.header.pps->id;
g_array_append_val (priv->nalu, decoder_nalu);
return GST_H265_PARSER_OK;
@ -967,21 +981,33 @@ gst_h265_decoder_parse_nalu (GstH265Decoder * self, GstH265NalUnit * nalu)
GST_LOG_OBJECT (self, "Parsed nal type: %d, offset %d, size %d",
nalu->type, nalu->offset, nalu->size);
memset (&decoder_nalu, 0, sizeof (GstH265DecoderNalUnit));
decoder_nalu.nalu_type = nalu->type;
switch (nalu->type) {
case GST_H265_NAL_VPS:
ret = gst_h265_parser_parse_vps (priv->parser, nalu, &vps);
break;
case GST_H265_NAL_SPS:
ret = gst_h265_parser_parse_sps (priv->parser, nalu, &sps, TRUE);
ret = gst_h265_parser_parse_vps (priv->preproc_parser, nalu, &vps);
if (ret != GST_H265_PARSER_OK)
break;
decoder_nalu.unit.vps = vps;
g_array_append_val (priv->nalu, decoder_nalu);
break;
case GST_H265_NAL_SPS:
ret = gst_h265_parser_parse_sps (priv->preproc_parser, nalu, &sps, TRUE);
if (ret != GST_H265_PARSER_OK)
break;
memset (&decoder_nalu, 0, sizeof (GstH265DecoderNalUnit));
decoder_nalu.unit.sps = sps;
g_array_append_val (priv->nalu, decoder_nalu);
break;
case GST_H265_NAL_PPS:
ret = gst_h265_parser_parse_pps (priv->parser, nalu, &pps);
ret = gst_h265_parser_parse_pps (priv->preproc_parser, nalu, &pps);
if (ret != GST_H265_PARSER_OK)
break;
decoder_nalu.unit.pps = pps;
g_array_append_val (priv->nalu, decoder_nalu);
break;
case GST_H265_NAL_PREFIX_SEI:
case GST_H265_NAL_SUFFIX_SEI:
@ -1020,14 +1046,53 @@ gst_h265_decoder_parse_nalu (GstH265Decoder * self, GstH265NalUnit * nalu)
return ret;
}
static inline gboolean
is_slice_nalu (GstH265NalUnitType type)
{
if ((type >= GST_H265_NAL_SLICE_TRAIL_N &&
type <= GST_H265_NAL_SLICE_RASL_R) ||
(type >= GST_H265_NAL_SLICE_BLA_W_LP &&
type <= GST_H265_NAL_SLICE_CRA_NUT)) {
return TRUE;
}
return FALSE;
}
static GstFlowReturn
gst_h265_decoder_decode_nalu (GstH265Decoder * self,
GstH265DecoderNalUnit * nalu)
{
if (nalu->is_slice)
return gst_h265_decoder_process_slice (self, &nalu->unit.slice);
GstH265DecoderPrivate *priv = self->priv;
GstH265ParserResult rst;
return GST_FLOW_OK;
switch (nalu->nalu_type) {
case GST_H265_NAL_VPS:
gst_h265_parser_update_vps (priv->parser, &nalu->unit.vps);
return GST_FLOW_OK;
case GST_H265_NAL_SPS:
gst_h265_parser_update_sps (priv->parser, &nalu->unit.sps);
return GST_FLOW_OK;
case GST_H265_NAL_PPS:
gst_h265_parser_update_pps (priv->parser, &nalu->unit.pps);
return GST_FLOW_OK;
default:
if (!is_slice_nalu (nalu->nalu_type)) {
GST_WARNING_OBJECT (self, "Unexpected nal type %d", nalu->nalu_type);
return GST_FLOW_OK;
}
break;
}
rst = gst_h265_parser_link_slice_hdr (priv->parser,
&nalu->unit.slice.header, nalu->pps_id);
if (rst != GST_H265_PARSER_OK) {
GST_ERROR_OBJECT (self, "Couldn't update slice header");
return GST_FLOW_ERROR;
}
return gst_h265_decoder_process_slice (self, &nalu->unit.slice);
}
static void
@ -1112,6 +1177,7 @@ gst_h265_decoder_parse_codec_data (GstH265Decoder * self, const guint8 * data,
GST_WARNING_OBJECT (self, "Failed to parse VPS");
goto out;
}
gst_h265_parser_update_vps (priv->preproc_parser, &vps);
break;
case GST_H265_NAL_SPS:
pres = gst_h265_parser_parse_sps (parser, nalu, &sps, TRUE);
@ -1119,6 +1185,7 @@ gst_h265_decoder_parse_codec_data (GstH265Decoder * self, const guint8 * data,
GST_WARNING_OBJECT (self, "Failed to parse SPS");
goto out;
}
gst_h265_parser_update_sps (priv->preproc_parser, &sps);
break;
case GST_H265_NAL_PPS:
pres = gst_h265_parser_parse_pps (parser, nalu, &pps);
@ -1126,6 +1193,7 @@ gst_h265_decoder_parse_codec_data (GstH265Decoder * self, const guint8 * data,
GST_WARNING_OBJECT (self, "Failed to parse PPS");
goto out;
}
gst_h265_parser_update_pps (priv->preproc_parser, &pps);
break;
default:
break;
@ -2055,7 +2123,7 @@ gst_h265_decoder_handle_frame (GstVideoDecoder * decoder,
gsize consumed;
do {
pres = gst_h265_parser_identify_and_split_nalu_hevc (priv->parser,
pres = gst_h265_parser_identify_and_split_nalu_hevc (priv->preproc_parser,
map.data, offset, map.size, priv->nal_length_size, priv->split_nalu,
&consumed);
if (pres != GST_H265_PARSER_OK)
@ -2075,7 +2143,7 @@ gst_h265_decoder_handle_frame (GstVideoDecoder * decoder,
offset += consumed;
} while (pres == GST_H265_PARSER_OK);
} else {
pres = gst_h265_parser_identify_nalu (priv->parser,
pres = gst_h265_parser_identify_nalu (priv->preproc_parser,
map.data, 0, map.size, &nalu);
if (pres == GST_H265_PARSER_NO_NAL_END)
@ -2086,7 +2154,7 @@ gst_h265_decoder_handle_frame (GstVideoDecoder * decoder,
if (pres != GST_H265_PARSER_OK)
break;
pres = gst_h265_parser_identify_nalu (priv->parser,
pres = gst_h265_parser_identify_nalu (priv->preproc_parser,
map.data, nalu.offset + nalu.size, map.size, &nalu);
if (pres == GST_H265_PARSER_NO_NAL_END)
pres = GST_H265_PARSER_OK;
@ -2142,7 +2210,7 @@ gst_h265_decoder_clear_nalu (GstH265DecoderNalUnit * nalu)
if (!nalu)
return;
if (nalu->is_slice)
if (is_slice_nalu (nalu->nalu_type))
gst_h265_slice_hdr_free (&nalu->unit.slice.header);
memset (nalu, 0, sizeof (GstH265DecoderNalUnit));