h265decoder: Drain decoder on new_sequence()

Holding previously decoded but not outputted pictures even after
new_sequence is not a safe approach in various aspect.
However, we cannot drain out DPB on new_sequence() unconditionally,
because there is a case where decoder should drop decoded pictures
if NoOutputOfPriorPicsFlag is set.

To detect NoOutputOfPriorPicsFlag before the new_sequence() call,
this patch splits decoding process into two path, one for nal unit parsing
in order to detect NoOutputOfPriorPicsFlag and then each nal unit
will be decoded.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1937>
This commit is contained in:
Seungha Yang 2022-03-12 04:45:22 +09:00 committed by GStreamer Marge Bot
parent 9494509ee0
commit 3e49ff0ff5
2 changed files with 184 additions and 140 deletions

View file

@ -82,6 +82,8 @@ struct _GstH265DecoderPrivate
guint8 cur_source_scan_type;
guint8 cur_duplicate_flag;
gboolean no_output_of_prior_pics_flag;
/* vps/sps/pps of the current slice */
const GstH265VPS *active_vps;
const GstH265SPS *active_sps;
@ -123,8 +125,20 @@ struct _GstH265DecoderPrivate
GArray *ref_pic_list_tmp;
GArray *ref_pic_list0;
GArray *ref_pic_list1;
GArray *nalu;
};
typedef struct
{
union
{
GstH265SPS sps;
GstH265Slice slice;
} unit;
gboolean is_slice;
} GstH265DecoderNalUnit;
#define UPDATE_FLOW_RETURN(ret,new_ret) G_STMT_START { \
if (*(ret) == GST_FLOW_OK) \
*(ret) = new_ret; \
@ -156,6 +170,7 @@ static void gst_h265_decoder_clear_dpb (GstH265Decoder * self, gboolean flush);
static GstFlowReturn gst_h265_decoder_drain_internal (GstH265Decoder * self);
static GstFlowReturn
gst_h265_decoder_start_current_picture (GstH265Decoder * self);
static void gst_h265_decoder_clear_nalu (GstH265DecoderNalUnit * nalu);
static void
gst_h265_decoder_class_init (GstH265DecoderClass * klass)
@ -192,6 +207,10 @@ gst_h265_decoder_init (GstH265Decoder * self)
sizeof (GstH265Picture *), 32);
priv->ref_pic_list1 = g_array_sized_new (FALSE, TRUE,
sizeof (GstH265Picture *), 32);
priv->nalu = g_array_sized_new (FALSE, TRUE, sizeof (GstH265DecoderNalUnit),
8);
g_array_set_clear_func (priv->nalu,
(GDestroyNotify) gst_h265_decoder_clear_nalu);
}
static void
@ -203,6 +222,7 @@ gst_h265_decoder_finalize (GObject * object)
g_array_unref (priv->ref_pic_list_tmp);
g_array_unref (priv->ref_pic_list0);
g_array_unref (priv->ref_pic_list1);
g_array_unref (priv->nalu);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -249,24 +269,6 @@ gst_h265_decoder_stop (GstVideoDecoder * decoder)
return TRUE;
}
static GstFlowReturn
gst_h265_decoder_parse_vps (GstH265Decoder * self, GstH265NalUnit * nalu)
{
GstH265DecoderPrivate *priv = self->priv;
GstH265VPS vps;
GstH265ParserResult pres;
pres = gst_h265_parser_parse_vps (priv->parser, nalu, &vps);
if (pres != GST_H265_PARSER_OK) {
GST_WARNING_OBJECT (self, "Failed to parse VPS, result %d", pres);
return GST_FLOW_ERROR;
}
GST_LOG_OBJECT (self, "VPS parsed");
return GST_FLOW_OK;
}
static gboolean
gst_h265_decoder_is_crop_rect_changed (GstH265Decoder * self, GstH265SPS * sps)
{
@ -340,6 +342,14 @@ gst_h265_decoder_process_sps (GstH265Decoder * self, GstH265SPS * sps)
g_assert (klass->new_sequence);
if (priv->no_output_of_prior_pics_flag) {
gst_h265_decoder_clear_dpb (self, FALSE);
} else {
ret = gst_h265_decoder_drain_internal (self);
if (ret != GST_FLOW_OK)
return ret;
}
ret = klass->new_sequence (self, sps, max_dpb_size);
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (self, "subclass does not want accept new sequence");
@ -371,53 +381,7 @@ gst_h265_decoder_process_sps (GstH265Decoder * self, GstH265SPS * sps)
return GST_FLOW_OK;
}
static GstFlowReturn
gst_h265_decoder_parse_sps (GstH265Decoder * self, GstH265NalUnit * nalu)
{
GstH265DecoderPrivate *priv = self->priv;
GstH265SPS sps;
GstH265ParserResult pres;
GstFlowReturn ret = GST_FLOW_OK;
pres = gst_h265_parse_sps (priv->parser, nalu, &sps, TRUE);
if (pres != GST_H265_PARSER_OK) {
GST_WARNING_OBJECT (self, "Failed to parse SPS, result %d", pres);
return GST_FLOW_ERROR;
}
GST_LOG_OBJECT (self, "SPS parsed");
ret = gst_h265_decoder_process_sps (self, &sps);
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (self, "Failed to process SPS");
} else if (gst_h265_parser_update_sps (priv->parser,
&sps) != GST_H265_PARSER_OK) {
GST_WARNING_OBJECT (self, "Failed to update SPS");
ret = GST_FLOW_ERROR;
}
return ret;
}
static GstFlowReturn
gst_h265_decoder_parse_pps (GstH265Decoder * self, GstH265NalUnit * nalu)
{
GstH265DecoderPrivate *priv = self->priv;
GstH265PPS pps;
GstH265ParserResult pres;
pres = gst_h265_parser_parse_pps (priv->parser, nalu, &pps);
if (pres != GST_H265_PARSER_OK) {
GST_WARNING_OBJECT (self, "Failed to parse PPS, result %d", pres);
return GST_FLOW_ERROR;
}
GST_LOG_OBJECT (self, "PPS parsed");
return GST_FLOW_OK;
}
static GstFlowReturn
static GstH265ParserResult
gst_h265_decoder_parse_sei (GstH265Decoder * self, GstH265NalUnit * nalu)
{
GstH265DecoderPrivate *priv = self->priv;
@ -432,7 +396,7 @@ gst_h265_decoder_parse_sei (GstH265Decoder * self, GstH265NalUnit * nalu)
/* XXX: Ignore error from SEI parsing, it might be malformed bitstream,
* or our fault. But shouldn't be critical */
g_clear_pointer (&messages, g_array_unref);
return GST_FLOW_OK;
return GST_H265_PARSER_OK;
}
for (i = 0; i < messages->len; i++) {
@ -457,7 +421,7 @@ gst_h265_decoder_parse_sei (GstH265Decoder * self, GstH265NalUnit * nalu)
g_array_free (messages, TRUE);
GST_LOG_OBJECT (self, "SEI parsed");
return GST_FLOW_OK;
return GST_H265_PARSER_OK;
}
static void
@ -631,31 +595,12 @@ gst_h265_decoder_preprocess_slice (GstH265Decoder * self, GstH265Slice * slice)
}
static GstFlowReturn
gst_h265_decoder_parse_slice (GstH265Decoder * self, GstH265NalUnit * nalu)
gst_h265_decoder_process_slice (GstH265Decoder * self, GstH265Slice * slice)
{
GstH265DecoderPrivate *priv = self->priv;
GstH265ParserResult pres = GST_H265_PARSER_OK;
GstFlowReturn ret = GST_FLOW_OK;
memset (&priv->current_slice, 0, sizeof (GstH265Slice));
pres = gst_h265_parser_parse_slice_hdr (priv->parser, nalu,
&priv->current_slice.header);
if (pres != GST_H265_PARSER_OK) {
GST_ERROR_OBJECT (self, "Failed to parse slice header, ret %d", pres);
memset (&priv->current_slice, 0, sizeof (GstH265Slice));
return GST_FLOW_ERROR;
}
/* NOTE: gst_h265_parser_parse_slice_hdr() allocates array
* GstH265SliceHdr::entry_point_offset_minus1 but we don't use it
* in this h265decoder baseclass at the moment
*/
gst_h265_slice_hdr_free (&priv->current_slice.header);
priv->current_slice.nalu = *nalu;
priv->current_slice = *slice;
if (priv->current_slice.header.dependent_slice_segment_flag) {
GstH265SliceHdr *slice_hdr = &priv->current_slice.header;
@ -713,24 +658,99 @@ gst_h265_decoder_parse_slice (GstH265Decoder * self, GstH265NalUnit * nalu)
return gst_h265_decoder_decode_slice (self);
}
static GstFlowReturn
gst_h265_decoder_decode_nal (GstH265Decoder * self, GstH265NalUnit * nalu)
static GstH265ParserResult
gst_h265_decoder_parse_slice (GstH265Decoder * self, GstH265NalUnit * nalu)
{
GstH265DecoderPrivate *priv = self->priv;
GstFlowReturn ret = GST_FLOW_OK;
GstH265ParserResult pres;
GstH265Slice slice;
GstH265DecoderNalUnit decoder_nalu;
memset (&slice, 0, sizeof (GstH265Slice));
pres = gst_h265_parser_parse_slice_hdr (priv->parser, nalu, &slice.header);
if (pres != GST_H265_PARSER_OK)
return pres;
/* NOTE: gst_h265_parser_parse_slice_hdr() allocates array
* GstH265SliceHdr::entry_point_offset_minus1 but we don't use it
* in this h265decoder baseclass at the moment
*/
gst_h265_slice_hdr_free (&priv->current_slice.header);
slice.nalu = *nalu;
if (nalu->type >= GST_H265_NAL_SLICE_BLA_W_LP &&
nalu->type <= GST_H265_NAL_SLICE_CRA_NUT) {
slice.rap_pic_flag = TRUE;
}
/* NoRaslOutputFlag == 1 if the current picture is
* 1) an IDR picture
* 2) a BLA picture
* 3) a CRA picture that is the first access unit in the bitstream
* 4) first picture that follows an end of sequence NAL unit in decoding order
* 5) has HandleCraAsBlaFlag == 1 (set by external means, so not considering )
*/
if (GST_H265_IS_NAL_TYPE_IDR (nalu->type) ||
GST_H265_IS_NAL_TYPE_BLA (nalu->type) ||
(GST_H265_IS_NAL_TYPE_CRA (nalu->type) && priv->new_bitstream) ||
priv->prev_nal_is_eos) {
slice.no_rasl_output_flag = TRUE;
}
if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type)) {
slice.intra_pic_flag = TRUE;
if (slice.no_rasl_output_flag && !priv->new_bitstream) {
/* C 3.2 */
slice.clear_dpb = TRUE;
if (nalu->type == GST_H265_NAL_SLICE_CRA_NUT) {
slice.no_output_of_prior_pics_flag = TRUE;
} else {
slice.no_output_of_prior_pics_flag =
slice.header.no_output_of_prior_pics_flag;
}
}
}
if (slice.no_output_of_prior_pics_flag)
priv->no_output_of_prior_pics_flag = TRUE;
decoder_nalu.unit.slice = slice;
decoder_nalu.is_slice = TRUE;
g_array_append_val (priv->nalu, decoder_nalu);
return GST_H265_PARSER_OK;
}
static GstH265ParserResult
gst_h265_decoder_parse_nalu (GstH265Decoder * self, GstH265NalUnit * nalu)
{
GstH265DecoderPrivate *priv = self->priv;
GstH265VPS vps;
GstH265SPS sps;
GstH265PPS pps;
GstH265ParserResult ret = GST_H265_PARSER_OK;
GstH265DecoderNalUnit decoder_nalu;
GST_LOG_OBJECT (self, "Parsed nal type: %d, offset %d, size %d",
nalu->type, nalu->offset, nalu->size);
switch (nalu->type) {
case GST_H265_NAL_VPS:
ret = gst_h265_decoder_parse_vps (self, nalu);
ret = gst_h265_parser_parse_vps (priv->parser, nalu, &vps);
break;
case GST_H265_NAL_SPS:
ret = gst_h265_decoder_parse_sps (self, nalu);
ret = gst_h265_parser_parse_sps (priv->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_decoder_parse_pps (self, nalu);
ret = gst_h265_parser_parse_pps (priv->parser, nalu, &pps);
break;
case GST_H265_NAL_PREFIX_SEI:
case GST_H265_NAL_SUFFIX_SEI:
@ -769,6 +789,16 @@ gst_h265_decoder_decode_nal (GstH265Decoder * self, GstH265NalUnit * nalu)
return ret;
}
static GstFlowReturn
gst_h265_decoder_decode_nalu (GstH265Decoder * self,
GstH265DecoderNalUnit * nalu)
{
if (!nalu->is_slice)
return gst_h265_decoder_process_sps (self, &nalu->unit.sps);
return gst_h265_decoder_process_slice (self, &nalu->unit.slice);
}
static void
gst_h265_decoder_format_from_caps (GstH265Decoder * self, GstCaps * caps,
GstH265DecoderFormat * format, GstH265DecoderAlign * align)
@ -823,6 +853,9 @@ gst_h265_decoder_parse_codec_data (GstH265Decoder * self, const guint8 * data,
GstH265ParserResult pres;
GstH265NalUnit nalu;
GstFlowReturn ret = GST_FLOW_OK;
GstH265VPS vps;
GstH265SPS sps;
GstH265PPS pps;
/* parse the hvcC data */
if (size < 23) {
@ -860,24 +893,30 @@ gst_h265_decoder_parse_codec_data (GstH265Decoder * self, const guint8 * data,
switch (nalu.type) {
case GST_H265_NAL_VPS:
ret = gst_h265_decoder_parse_vps (self, &nalu);
if (ret != GST_FLOW_OK) {
pres = gst_h265_parser_parse_vps (priv->parser, &nalu, &vps);
if (pres != GST_H265_PARSER_OK) {
GST_WARNING_OBJECT (self, "Failed to parse VPS");
return ret;
return GST_FLOW_ERROR;
}
break;
case GST_H265_NAL_SPS:
ret = gst_h265_decoder_parse_sps (self, &nalu);
if (ret != GST_FLOW_OK) {
pres = gst_h265_parser_parse_sps (priv->parser, &nalu, &sps, TRUE);
if (pres != GST_H265_PARSER_OK) {
GST_WARNING_OBJECT (self, "Failed to parse SPS");
return GST_FLOW_ERROR;
}
ret = gst_h265_decoder_process_sps (self, &sps);
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (self, "Failed to process SPS");
return ret;
}
break;
case GST_H265_NAL_PPS:
ret = gst_h265_decoder_parse_pps (self, &nalu);
if (ret != GST_FLOW_OK) {
pres = gst_h265_parser_parse_pps (priv->parser, &nalu, &pps);
if (pres != GST_H265_PARSER_OK) {
GST_WARNING_OBJECT (self, "Failed to parse PPS");
return ret;
return GST_FLOW_ERROR;
}
break;
default:
@ -1010,26 +1049,11 @@ gst_h265_decoder_fill_picture_from_slice (GstH265Decoder * self,
const GstH265SliceHdr *slice_hdr = &slice->header;
const GstH265NalUnit *nalu = &slice->nalu;
if (nalu->type >= GST_H265_NAL_SLICE_BLA_W_LP &&
nalu->type <= GST_H265_NAL_SLICE_CRA_NUT)
picture->RapPicFlag = TRUE;
/* NoRaslOutputFlag == 1 if the current picture is
* 1) an IDR picture
* 2) a BLA picture
* 3) a CRA picture that is the first access unit in the bitstream
* 4) first picture that follows an end of sequence NAL unit in decoding order
* 5) has HandleCraAsBlaFlag == 1 (set by external means, so not considering )
*/
if (GST_H265_IS_NAL_TYPE_IDR (nalu->type) ||
GST_H265_IS_NAL_TYPE_BLA (nalu->type) ||
(GST_H265_IS_NAL_TYPE_CRA (nalu->type) && priv->new_bitstream) ||
priv->prev_nal_is_eos) {
picture->NoRaslOutputFlag = TRUE;
}
if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type)) {
picture->IntraPicFlag = TRUE;
picture->RapPicFlag = slice->rap_pic_flag;
picture->NoRaslOutputFlag = slice->no_rasl_output_flag;
picture->IntraPicFlag = slice->intra_pic_flag;
picture->NoOutputOfPriorPicsFlag = slice->no_output_of_prior_pics_flag;
if (picture->IntraPicFlag) {
priv->associated_irap_NoRaslOutputFlag = picture->NoRaslOutputFlag;
}
@ -1555,21 +1579,12 @@ gst_h265_decoder_dpb_init (GstH265Decoder * self, const GstH265Slice * slice,
GstH265Picture * picture)
{
GstH265DecoderPrivate *priv = self->priv;
const GstH265SliceHdr *slice_hdr = &slice->header;
const GstH265NalUnit *nalu = &slice->nalu;
const GstH265SPS *sps = priv->active_sps;
GstH265Picture *to_output;
GstFlowReturn ret = GST_FLOW_OK;
/* C 3.2 */
if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type) && picture->NoRaslOutputFlag
&& !priv->new_bitstream) {
if (nalu->type == GST_H265_NAL_SLICE_CRA_NUT)
picture->NoOutputOfPriorPicsFlag = TRUE;
else
picture->NoOutputOfPriorPicsFlag =
slice_hdr->no_output_of_prior_pics_flag;
if (slice->clear_dpb) {
if (picture->NoOutputOfPriorPicsFlag) {
GST_DEBUG_OBJECT (self, "Clear dpb");
gst_h265_decoder_clear_dpb (self, FALSE);
@ -1742,6 +1757,9 @@ gst_h265_decoder_reset_frame_state (GstH265Decoder * self)
priv->cur_pic_struct = GST_H265_SEI_PIC_STRUCT_FRAME;
priv->cur_source_scan_type = 2;
priv->cur_duplicate_flag = 0;
priv->no_output_of_prior_pics_flag = FALSE;
priv->current_frame = NULL;
g_array_set_size (priv->nalu, 0);
}
static GstFlowReturn
@ -1755,16 +1773,17 @@ gst_h265_decoder_handle_frame (GstVideoDecoder * decoder,
GstH265ParserResult pres;
GstMapInfo map;
GstFlowReturn decode_ret = GST_FLOW_OK;
guint i;
GST_LOG_OBJECT (self,
"handle frame, PTS: %" GST_TIME_FORMAT ", DTS: %"
GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (in_buf)),
GST_TIME_ARGS (GST_BUFFER_DTS (in_buf)));
priv->current_frame = frame;
gst_h265_decoder_reset_frame_state (self);
priv->current_frame = frame;
if (!gst_buffer_map (in_buf, &map, GST_MAP_READ)) {
GST_ELEMENT_ERROR (self, RESOURCE, READ,
("Failed to map memory for reading"), (NULL));
@ -1776,8 +1795,10 @@ gst_h265_decoder_handle_frame (GstVideoDecoder * decoder,
pres = gst_h265_parser_identify_nalu_hevc (priv->parser,
map.data, 0, map.size, priv->nal_length_size, &nalu);
while (pres == GST_H265_PARSER_OK && decode_ret == GST_FLOW_OK) {
decode_ret = gst_h265_decoder_decode_nal (self, &nalu);
while (pres == GST_H265_PARSER_OK) {
pres = gst_h265_decoder_parse_nalu (self, &nalu);
if (pres != GST_H265_PARSER_OK)
break;
pres = gst_h265_parser_identify_nalu_hevc (priv->parser,
map.data, nalu.offset + nalu.size, map.size, priv->nal_length_size,
@ -1790,19 +1811,26 @@ gst_h265_decoder_handle_frame (GstVideoDecoder * decoder,
if (pres == GST_H265_PARSER_NO_NAL_END)
pres = GST_H265_PARSER_OK;
while (pres == GST_H265_PARSER_OK && decode_ret == GST_FLOW_OK) {
decode_ret = gst_h265_decoder_decode_nal (self, &nalu);
while (pres == GST_H265_PARSER_OK) {
pres = gst_h265_decoder_parse_nalu (self, &nalu);
if (pres != GST_H265_PARSER_OK)
break;
pres = gst_h265_parser_identify_nalu (priv->parser,
map.data, nalu.offset + nalu.size, map.size, &nalu);
if (pres == GST_H265_PARSER_NO_NAL_END)
pres = GST_H265_PARSER_OK;
}
}
for (i = 0; i < priv->nalu->len && decode_ret == GST_FLOW_OK; i++) {
GstH265DecoderNalUnit *decoder_nalu =
&g_array_index (priv->nalu, GstH265DecoderNalUnit, i);
decode_ret = gst_h265_decoder_decode_nalu (self, decoder_nalu);
}
gst_buffer_unmap (in_buf, &map);
priv->current_frame = NULL;
gst_h265_decoder_reset_frame_state (self);
if (decode_ret != GST_FLOW_OK) {
if (decode_ret == GST_FLOW_ERROR) {
@ -1832,6 +1860,15 @@ gst_h265_decoder_handle_frame (GstVideoDecoder * decoder,
return decode_ret;
}
static void
gst_h265_decoder_clear_nalu (GstH265DecoderNalUnit * nalu)
{
if (!nalu)
return;
memset (nalu, 0, sizeof (GstH265DecoderNalUnit));
}
/**
* gst_h265_decoder_set_process_ref_pic_lists:
* @decoder: a #GstH265Decoder

View file

@ -48,6 +48,13 @@ struct _GstH265Slice
/* parsed nal unit (doesn't take ownership of raw data) */
GstH265NalUnit nalu;
/*< private >*/
gboolean rap_pic_flag;
gboolean no_rasl_output_flag;
gboolean no_output_of_prior_pics_flag;
gboolean clear_dpb;
gboolean intra_pic_flag;
};
struct _GstH265Picture