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

View File

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