codecs: Keep track of non-decoding-essential input state change

In theory, input caps can be updated anytime at non-keyframe or
sequence boundary, such as HDR10 metadata, framerate, aspect-ratio
or so. Those information update might not trigger ::new_sequence()
or subclass may ignore the changes.

By this commit, input state change will be tracked by baseclass
and subclass will be able to know the non-decoding-essential
update by checking the codec specific picture struct
on ::output_picture()

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3328>
This commit is contained in:
Seungha Yang 2022-11-05 01:07:02 +09:00 committed by GStreamer Marge Bot
parent a40842545f
commit 2ede4011bf
18 changed files with 188 additions and 0 deletions

View file

@ -48,6 +48,8 @@ struct _GstAV1DecoderPrivate
guint preferred_output_delay;
GstQueueArray *output_queue;
gboolean is_live;
gboolean input_state_changed;
};
typedef struct
@ -83,6 +85,7 @@ static gboolean gst_av1_decoder_start (GstVideoDecoder * decoder);
static gboolean gst_av1_decoder_stop (GstVideoDecoder * decoder);
static gboolean gst_av1_decoder_set_format (GstVideoDecoder * decoder,
GstVideoCodecState * state);
static gboolean gst_av1_decoder_negotiate (GstVideoDecoder * decoder);
static GstFlowReturn gst_av1_decoder_finish (GstVideoDecoder * decoder);
static gboolean gst_av1_decoder_flush (GstVideoDecoder * decoder);
static GstFlowReturn gst_av1_decoder_drain (GstVideoDecoder * decoder);
@ -102,6 +105,7 @@ gst_av1_decoder_class_init (GstAV1DecoderClass * klass)
decoder_class->start = GST_DEBUG_FUNCPTR (gst_av1_decoder_start);
decoder_class->stop = GST_DEBUG_FUNCPTR (gst_av1_decoder_stop);
decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_av1_decoder_set_format);
decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_av1_decoder_negotiate);
decoder_class->finish = GST_DEBUG_FUNCPTR (gst_av1_decoder_finish);
decoder_class->flush = GST_DEBUG_FUNCPTR (gst_av1_decoder_flush);
decoder_class->drain = GST_DEBUG_FUNCPTR (gst_av1_decoder_drain);
@ -210,6 +214,8 @@ gst_av1_decoder_set_format (GstVideoDecoder * decoder,
GST_DEBUG_OBJECT (decoder, "Set format");
priv->input_state_changed = TRUE;
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
@ -227,6 +233,17 @@ gst_av1_decoder_set_format (GstVideoDecoder * decoder,
return TRUE;
}
static gboolean
gst_av1_decoder_negotiate (GstVideoDecoder * decoder)
{
GstAV1Decoder *self = GST_AV1_DECODER (decoder);
/* output state must be updated by subclass using new input state already */
self->priv->input_state_changed = FALSE;
return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
}
static void
gst_av1_decoder_drain_output_queue (GstAV1Decoder * self,
guint num, GstFlowReturn * ret)
@ -731,6 +748,14 @@ out:
} else {
GstAV1DecoderOutputFrame output_frame;
/* If subclass didn't update output state at this point,
* marking this picture as a discont and stores current input state */
if (priv->input_state_changed) {
priv->current_picture->discont_state =
gst_video_codec_state_ref (self->input_state);
priv->input_state_changed = FALSE;
}
output_frame.frame = frame;
output_frame.picture = priv->current_picture;
output_frame.self = self;

View file

@ -36,6 +36,9 @@ _gst_av1_picture_free (GstAV1Picture * picture)
if (picture->notify)
picture->notify (picture->user_data);
if (picture->discont_state)
gst_video_codec_state_unref (picture->discont_state);
g_free (picture);
}

View file

@ -22,6 +22,7 @@
#include <gst/codecs/codecs-prelude.h>
#include <gst/codecparsers/gstav1parser.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
@ -84,6 +85,9 @@ struct _GstAV1Picture
gboolean showable_frame;
gboolean apply_grain;
/* decoder input state if this picture is discont point */
GstVideoCodecState *discont_state;
gpointer user_data;
GDestroyNotify notify;
};

View file

@ -150,6 +150,8 @@ struct _GstH264DecoderPrivate
/* For delayed output */
GstQueueArray *output_queue;
gboolean input_state_changed;
};
typedef struct
@ -179,6 +181,7 @@ static gboolean gst_h264_decoder_start (GstVideoDecoder * decoder);
static gboolean gst_h264_decoder_stop (GstVideoDecoder * decoder);
static gboolean gst_h264_decoder_set_format (GstVideoDecoder * decoder,
GstVideoCodecState * state);
static gboolean gst_h264_decoder_negotiate (GstVideoDecoder * decoder);
static GstFlowReturn gst_h264_decoder_finish (GstVideoDecoder * decoder);
static gboolean gst_h264_decoder_flush (GstVideoDecoder * decoder);
static GstFlowReturn gst_h264_decoder_drain (GstVideoDecoder * decoder);
@ -307,6 +310,7 @@ gst_h264_decoder_class_init (GstH264DecoderClass * klass)
decoder_class->start = GST_DEBUG_FUNCPTR (gst_h264_decoder_start);
decoder_class->stop = GST_DEBUG_FUNCPTR (gst_h264_decoder_stop);
decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_h264_decoder_set_format);
decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_h264_decoder_negotiate);
decoder_class->finish = GST_DEBUG_FUNCPTR (gst_h264_decoder_finish);
decoder_class->flush = GST_DEBUG_FUNCPTR (gst_h264_decoder_flush);
decoder_class->drain = GST_DEBUG_FUNCPTR (gst_h264_decoder_drain);
@ -1023,6 +1027,14 @@ gst_h264_decoder_start_current_picture (GstH264Decoder * self)
g_assert (priv->active_sps != NULL);
g_assert (priv->active_pps != NULL);
/* If subclass didn't update output state at this point,
* marking this picture as a discont and stores current input state */
if (priv->input_state_changed) {
priv->current_picture->discont_state =
gst_video_codec_state_ref (self->input_state);
priv->input_state_changed = FALSE;
}
sps = priv->active_sps;
priv->max_frame_num = sps->max_frame_num;
@ -1369,6 +1381,8 @@ gst_h264_decoder_set_format (GstVideoDecoder * decoder,
GST_DEBUG_OBJECT (decoder, "Set format");
priv->input_state_changed = TRUE;
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
@ -1441,6 +1455,17 @@ gst_h264_decoder_set_format (GstVideoDecoder * decoder,
return TRUE;
}
static gboolean
gst_h264_decoder_negotiate (GstVideoDecoder * decoder)
{
GstH264Decoder *self = GST_H264_DECODER (decoder);
/* output state must be updated by subclass using new input state already */
self->priv->input_state_changed = FALSE;
return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
}
static gboolean
gst_h264_decoder_fill_picture_from_slice (GstH264Decoder * self,
const GstH264Slice * slice, GstH264Picture * picture)

View file

@ -35,6 +35,9 @@ _gst_h264_picture_free (GstH264Picture * picture)
if (picture->notify)
picture->notify (picture->user_data);
if (picture->discont_state)
gst_video_codec_state_unref (picture->discont_state);
g_free (picture);
}

View file

@ -161,6 +161,9 @@ struct _GstH264Picture
GstVideoBufferFlags buffer_flags;
/* decoder input state if this picture is discont point */
GstVideoCodecState *discont_state;
gpointer user_data;
GDestroyNotify notify;
};

View file

@ -134,6 +134,8 @@ struct _GstH265DecoderPrivate
guint preferred_output_delay;
gboolean is_live;
GstQueueArray *output_queue;
gboolean input_state_changed;
};
typedef struct
@ -173,6 +175,7 @@ static gboolean gst_h265_decoder_start (GstVideoDecoder * decoder);
static gboolean gst_h265_decoder_stop (GstVideoDecoder * decoder);
static gboolean gst_h265_decoder_set_format (GstVideoDecoder * decoder,
GstVideoCodecState * state);
static gboolean gst_h265_decoder_negotiate (GstVideoDecoder * decoder);
static GstFlowReturn gst_h265_decoder_finish (GstVideoDecoder * decoder);
static gboolean gst_h265_decoder_flush (GstVideoDecoder * decoder);
static GstFlowReturn gst_h265_decoder_drain (GstVideoDecoder * decoder);
@ -201,6 +204,7 @@ gst_h265_decoder_class_init (GstH265DecoderClass * klass)
decoder_class->start = GST_DEBUG_FUNCPTR (gst_h265_decoder_start);
decoder_class->stop = GST_DEBUG_FUNCPTR (gst_h265_decoder_stop);
decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_h265_decoder_set_format);
decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_h265_decoder_negotiate);
decoder_class->finish = GST_DEBUG_FUNCPTR (gst_h265_decoder_finish);
decoder_class->flush = GST_DEBUG_FUNCPTR (gst_h265_decoder_flush);
decoder_class->drain = GST_DEBUG_FUNCPTR (gst_h265_decoder_drain);
@ -1083,6 +1087,8 @@ gst_h265_decoder_set_format (GstVideoDecoder * decoder,
GST_DEBUG_OBJECT (decoder, "Set format");
priv->input_state_changed = TRUE;
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
@ -1151,6 +1157,17 @@ gst_h265_decoder_set_format (GstVideoDecoder * decoder,
return TRUE;
}
static gboolean
gst_h265_decoder_negotiate (GstVideoDecoder * decoder)
{
GstH265Decoder *self = GST_H265_DECODER (decoder);
/* output state must be updated by subclass using new input state already */
self->priv->input_state_changed = FALSE;
return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
}
static gboolean
gst_h265_decoder_flush (GstVideoDecoder * decoder)
{
@ -1785,6 +1802,14 @@ gst_h265_decoder_start_current_picture (GstH265Decoder * self)
return GST_FLOW_OK;
}
/* If subclass didn't update output state at this point,
* marking this picture as a discont and stores current input state */
if (priv->input_state_changed) {
priv->current_picture->discont_state =
gst_video_codec_state_ref (self->input_state);
priv->input_state_changed = FALSE;
}
gst_h265_decoder_prepare_rps (self, &priv->current_slice,
priv->current_picture);

View file

@ -34,6 +34,9 @@ _gst_h265_picture_free (GstH265Picture * picture)
if (picture->notify)
picture->notify (picture->user_data);
if (picture->discont_state)
gst_video_codec_state_unref (picture->discont_state);
g_free (picture);
}

View file

@ -90,6 +90,9 @@ struct _GstH265Picture
GstVideoBufferFlags buffer_flags;
/* decoder input state if this picture is discont point */
GstVideoCodecState *discont_state;
gpointer user_data;
GDestroyNotify notify;
};

View file

@ -267,6 +267,8 @@ struct _GstMpeg2DecoderPrivate
GstQueueArray *output_queue;
/* used for low-latency vs. high throughput mode decision */
gboolean is_live;
gboolean input_state_changed;
};
#define UPDATE_FLOW_RETURN(ret,new_ret) G_STMT_START { \
@ -293,6 +295,7 @@ static gboolean gst_mpeg2_decoder_start (GstVideoDecoder * decoder);
static gboolean gst_mpeg2_decoder_stop (GstVideoDecoder * decoder);
static gboolean gst_mpeg2_decoder_set_format (GstVideoDecoder * decoder,
GstVideoCodecState * state);
static gboolean gst_mpeg2_decoder_negotiate (GstVideoDecoder * decoder);
static GstFlowReturn gst_mpeg2_decoder_finish (GstVideoDecoder * decoder);
static gboolean gst_mpeg2_decoder_flush (GstVideoDecoder * decoder);
static GstFlowReturn gst_mpeg2_decoder_drain (GstVideoDecoder * decoder);
@ -314,6 +317,7 @@ gst_mpeg2_decoder_class_init (GstMpeg2DecoderClass * klass)
decoder_class->start = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_start);
decoder_class->stop = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_stop);
decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_set_format);
decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_negotiate);
decoder_class->finish = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_finish);
decoder_class->flush = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_flush);
decoder_class->drain = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_drain);
@ -379,6 +383,8 @@ gst_mpeg2_decoder_set_format (GstVideoDecoder * decoder,
GST_DEBUG_OBJECT (decoder, "Set format");
priv->input_state_changed = TRUE;
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
@ -395,6 +401,17 @@ gst_mpeg2_decoder_set_format (GstVideoDecoder * decoder,
return TRUE;
}
static gboolean
gst_mpeg2_decoder_negotiate (GstVideoDecoder * decoder)
{
GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder);
/* output state must be updated by subclass using new input state already */
self->priv->input_state_changed = FALSE;
return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
}
static GstFlowReturn
gst_mpeg2_decoder_drain (GstVideoDecoder * decoder)
{
@ -817,6 +834,14 @@ gst_mpeg2_decoder_start_current_picture (GstMpeg2Decoder * decoder,
GstMpeg2Picture *prev_picture, *next_picture;
GstFlowReturn ret;
/* If subclass didn't update output state at this point,
* marking this picture as a discont and stores current input state */
if (priv->input_state_changed) {
priv->current_picture->discont_state =
gst_video_codec_state_ref (decoder->input_state);
priv->input_state_changed = FALSE;
}
if (!klass->start_picture)
return GST_FLOW_OK;

View file

@ -40,6 +40,9 @@ _gst_mpeg2_picture_free (GstMpeg2Picture * picture)
if (picture->notify)
picture->notify (picture->user_data);
if (picture->discont_state)
gst_video_codec_state_unref (picture->discont_state);
g_free (picture);
}

View file

@ -94,6 +94,9 @@ struct _GstMpeg2Picture
GstMpegVideoPictureStructure structure;
GstMpegVideoPictureType type;
/* decoder input state if this picture is discont point */
GstVideoCodecState *discont_state;
gpointer user_data;
GDestroyNotify notify;
};

View file

@ -47,6 +47,8 @@ struct _GstVp8DecoderPrivate
/* for delayed output */
GstQueueArray *output_queue;
gboolean is_live;
gboolean input_state_changed;
};
typedef struct
@ -67,6 +69,7 @@ static gboolean gst_vp8_decoder_start (GstVideoDecoder * decoder);
static gboolean gst_vp8_decoder_stop (GstVideoDecoder * decoder);
static gboolean gst_vp8_decoder_set_format (GstVideoDecoder * decoder,
GstVideoCodecState * state);
static gboolean gst_vp8_decoder_negotiate (GstVideoDecoder * decoder);
static GstFlowReturn gst_vp8_decoder_finish (GstVideoDecoder * decoder);
static gboolean gst_vp8_decoder_flush (GstVideoDecoder * decoder);
static GstFlowReturn gst_vp8_decoder_drain (GstVideoDecoder * decoder);
@ -87,6 +90,7 @@ gst_vp8_decoder_class_init (GstVp8DecoderClass * klass)
decoder_class->start = GST_DEBUG_FUNCPTR (gst_vp8_decoder_start);
decoder_class->stop = GST_DEBUG_FUNCPTR (gst_vp8_decoder_stop);
decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_vp8_decoder_set_format);
decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_vp8_decoder_negotiate);
decoder_class->finish = GST_DEBUG_FUNCPTR (gst_vp8_decoder_finish);
decoder_class->flush = GST_DEBUG_FUNCPTR (gst_vp8_decoder_flush);
decoder_class->drain = GST_DEBUG_FUNCPTR (gst_vp8_decoder_drain);
@ -205,6 +209,8 @@ gst_vp8_decoder_set_format (GstVideoDecoder * decoder,
GST_DEBUG_OBJECT (decoder, "Set format");
priv->input_state_changed = TRUE;
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
@ -221,6 +227,17 @@ gst_vp8_decoder_set_format (GstVideoDecoder * decoder,
return TRUE;
}
static gboolean
gst_vp8_decoder_negotiate (GstVideoDecoder * decoder)
{
GstVp8Decoder *self = GST_VP8_DECODER (decoder);
/* output state must be updated by subclass using new input state already */
self->priv->input_state_changed = FALSE;
return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
}
static gboolean
gst_vp8_decoder_update_reference (GstVp8Decoder * self, GstVp8Picture * picture)
{
@ -452,6 +469,13 @@ gst_vp8_decoder_handle_frame (GstVideoDecoder * decoder,
ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
} else {
/* If subclass didn't update output state at this point,
* marking this picture as a discont and stores current input state */
if (priv->input_state_changed) {
picture->discont_state = gst_video_codec_state_ref (self->input_state);
priv->input_state_changed = FALSE;
}
output_frame.frame = frame;
output_frame.picture = picture;
output_frame.self = self;

View file

@ -36,6 +36,9 @@ _gst_vp8_picture_free (GstVp8Picture * picture)
if (picture->notify)
picture->notify (picture->user_data);
if (picture->discont_state)
gst_video_codec_state_unref (picture->discont_state);
g_free (picture);
}

View file

@ -22,6 +22,7 @@
#include <gst/codecs/codecs-prelude.h>
#include <gst/codecparsers/gstvp8parser.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
@ -34,6 +35,7 @@ typedef struct _GstVp8Picture GstVp8Picture;
struct _GstVp8Picture
{
/*< private >*/
GstMiniObject parent;
GstClockTime pts;
@ -46,6 +48,9 @@ struct _GstVp8Picture
const guint8 * data;
gsize size;
/* decoder input state if this picture is discont point */
GstVideoCodecState *discont_state;
gpointer user_data;
GDestroyNotify notify;
};

View file

@ -84,6 +84,8 @@ struct _GstVp9DecoderPrivate
guint preferred_output_delay;
GstQueueArray *output_queue;
gboolean is_live;
gboolean input_state_changed;
};
typedef struct
@ -104,6 +106,7 @@ static gboolean gst_vp9_decoder_start (GstVideoDecoder * decoder);
static gboolean gst_vp9_decoder_stop (GstVideoDecoder * decoder);
static gboolean gst_vp9_decoder_set_format (GstVideoDecoder * decoder,
GstVideoCodecState * state);
static gboolean gst_vp9_decoder_negotiate (GstVideoDecoder * decoder);
static GstFlowReturn gst_vp9_decoder_finish (GstVideoDecoder * decoder);
static gboolean gst_vp9_decoder_flush (GstVideoDecoder * decoder);
static GstFlowReturn gst_vp9_decoder_drain (GstVideoDecoder * decoder);
@ -125,6 +128,7 @@ gst_vp9_decoder_class_init (GstVp9DecoderClass * klass)
decoder_class->start = GST_DEBUG_FUNCPTR (gst_vp9_decoder_start);
decoder_class->stop = GST_DEBUG_FUNCPTR (gst_vp9_decoder_stop);
decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_vp9_decoder_set_format);
decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_vp9_decoder_negotiate);
decoder_class->finish = GST_DEBUG_FUNCPTR (gst_vp9_decoder_finish);
decoder_class->flush = GST_DEBUG_FUNCPTR (gst_vp9_decoder_flush);
decoder_class->drain = GST_DEBUG_FUNCPTR (gst_vp9_decoder_drain);
@ -264,6 +268,8 @@ gst_vp9_decoder_set_format (GstVideoDecoder * decoder,
GST_DEBUG_OBJECT (decoder, "Set format");
priv->input_state_changed = TRUE;
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
@ -277,6 +283,17 @@ gst_vp9_decoder_set_format (GstVideoDecoder * decoder,
return TRUE;
}
static gboolean
gst_vp9_decoder_negotiate (GstVideoDecoder * decoder)
{
GstVp9Decoder *self = GST_VP9_DECODER (decoder);
/* output state must be updated by subclass using new input state already */
self->priv->input_state_changed = FALSE;
return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
}
static void
gst_vp9_decoder_reset (GstVp9Decoder * self)
{
@ -529,6 +546,13 @@ gst_vp9_decoder_handle_frame (GstVideoDecoder * decoder,
ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
} else {
/* If subclass didn't update output state at this point,
* marking this picture as a discont and stores current input state */
if (priv->input_state_changed) {
picture->discont_state = gst_video_codec_state_ref (self->input_state);
priv->input_state_changed = FALSE;
}
output_frame.frame = frame;
output_frame.picture = picture;
output_frame.self = self;

View file

@ -36,6 +36,9 @@ _gst_vp9_picture_free (GstVp9Picture * picture)
if (picture->notify)
picture->notify (picture->user_data);
if (picture->discont_state)
gst_video_codec_state_unref (picture->discont_state);
g_free (picture);
}

View file

@ -22,6 +22,7 @@
#include <gst/codecs/codecs-prelude.h>
#include <gst/codecs/gstvp9statefulparser.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
@ -46,6 +47,9 @@ struct _GstVp9Picture
const guint8 * data;
gsize size;
/* decoder input state if this picture is discont point */
GstVideoCodecState *discont_state;
gpointer user_data;
GDestroyNotify notify;
};