v4l2codecs: Allow output caps to be updated
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Mon, 14 Nov 2022 20:53:00 +0000 (15:53 -0500)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Wed, 16 Nov 2022 13:12:38 +0000 (13:12 +0000)
This change allow output caps to be updated even though we stay in
streaming state. This is needed so that any upstream updated to fields
like framerate, hdr data, etc. can result in a downstream caps event
being pushed.

Previously, any of these changes was being ignored and the downstream
caps would not reflect it.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3328>

subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codech264dec.c
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codech265dec.c
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codecmpeg2dec.c
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codecvp8dec.c
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codecvp9dec.c

index f2657c0..4ae62e9 100644 (file)
@@ -75,7 +75,7 @@ struct _GstV4l2CodecH264Dec
   GstV4l2CodecPool *src_pool;
   gint min_pool_size;
   gboolean has_videometa;
-  gboolean need_negotiation;
+  gboolean streaming;
   gboolean interlaced;
   gboolean need_sequence;
   gboolean copy_frames;
@@ -234,6 +234,16 @@ gst_v4l2_codec_h264_dec_close (GstVideoDecoder * decoder)
 }
 
 static void
+gst_v4l2_codec_h264_dec_streamoff (GstV4l2CodecH264Dec * self)
+{
+  if (self->streaming) {
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
+    self->streaming = FALSE;
+  }
+}
+
+static void
 gst_v4l2_codec_h264_dec_reset_allocation (GstV4l2CodecH264Dec * self)
 {
   if (self->sink_allocator) {
@@ -253,9 +263,7 @@ gst_v4l2_codec_h264_dec_stop (GstVideoDecoder * decoder)
 {
   GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder);
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
+  gst_v4l2_codec_h264_dec_streamoff (self);
   gst_v4l2_codec_h264_dec_reset_allocation (self);
 
   if (self->output_state)
@@ -314,15 +322,11 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
   GstCaps *filter, *caps;
 
   /* Ignore downstream renegotiation request. */
-  if (!self->need_negotiation)
-    return TRUE;
-  self->need_negotiation = FALSE;
+  if (self->streaming)
+    goto done;
 
   GST_DEBUG_OBJECT (self, "Negotiate");
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
   gst_v4l2_codec_h264_dec_reset_allocation (self);
 
   if (!gst_v4l2_decoder_set_sink_fmt (self->decoder, V4L2_PIX_FMT_H264_SLICE,
@@ -366,6 +370,7 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
   if (self->output_state)
     gst_video_codec_state_unref (self->output_state);
 
+done:
   self->output_state =
       gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
       self->vinfo.finfo->format, self->display_width,
@@ -377,6 +382,9 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
   self->output_state->caps = gst_video_info_to_caps (&self->output_state->info);
 
   if (GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder)) {
+    if (self->streaming)
+      return TRUE;
+
     if (!gst_v4l2_decoder_streamon (self->decoder, GST_PAD_SINK)) {
       GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
           ("Could not enable the decoder driver."),
@@ -391,6 +399,8 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
       return FALSE;
     }
 
+    self->streaming = TRUE;
+
     return TRUE;
   }
 
@@ -404,6 +414,11 @@ gst_v4l2_codec_h264_dec_decide_allocation (GstVideoDecoder * decoder,
   GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder);
   guint min = 0, num_bitstream;
 
+  /* If we are streaming here, then it means there is nothing allocation
+   * related in the new state and allocation can be ignored */
+  if (self->streaming)
+    return TRUE;
+
   self->has_videometa = gst_query_find_allocation_meta (query,
       GST_VIDEO_META_API_TYPE, NULL);
 
@@ -894,7 +909,7 @@ gst_v4l2_codec_h264_dec_new_sequence (GstH264Decoder * decoder,
   self->need_sequence = TRUE;
 
   if (negotiation_needed) {
-    self->need_negotiation = TRUE;
+    gst_v4l2_codec_h264_dec_streamoff (self);
     if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
       GST_ERROR_OBJECT (self, "Failed to negotiate with downstream");
       return GST_FLOW_NOT_NEGOTIATED;
@@ -1046,6 +1061,13 @@ gst_v4l2_codec_h264_dec_output_picture (GstH264Decoder * decoder,
   GstV4l2Request *request = gst_h264_picture_get_user_data (picture);
   gint ret;
 
+  if (picture->discont_state) {
+    if (!gst_video_decoder_negotiate (vdec)) {
+      GST_ERROR_OBJECT (vdec, "Could not re-negotiate with updated state");
+      return FALSE;
+    }
+  }
+
   GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number);
 
   ret = gst_v4l2_request_set_done (request);
index 45c3e72..242c855 100644 (file)
@@ -76,7 +76,7 @@ struct _GstV4l2CodecH265Dec
   GstV4l2CodecPool *src_pool;
   gint min_pool_size;
   gboolean has_videometa;
-  gboolean need_negotiation;
+  gboolean streaming;
   gboolean copy_frames;
   gboolean need_sequence;
 
@@ -268,6 +268,16 @@ gst_v4l2_codec_h265_dec_close (GstVideoDecoder * decoder)
 }
 
 static void
+gst_v4l2_codec_h265_dec_streamoff (GstV4l2CodecH265Dec * self)
+{
+  if (self->streaming) {
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
+    self->streaming = FALSE;
+  }
+}
+
+static void
 gst_v4l2_codec_h265_dec_reset_allocation (GstV4l2CodecH265Dec * self)
 {
   if (self->sink_allocator) {
@@ -287,9 +297,7 @@ gst_v4l2_codec_h265_dec_stop (GstVideoDecoder * decoder)
 {
   GstV4l2CodecH265Dec *self = GST_V4L2_CODEC_H265_DEC (decoder);
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
+  gst_v4l2_codec_h265_dec_streamoff (self);
   gst_v4l2_codec_h265_dec_reset_allocation (self);
 
   if (self->output_state)
@@ -348,15 +356,11 @@ gst_v4l2_codec_h265_dec_negotiate (GstVideoDecoder * decoder)
   GstCaps *filter, *caps;
 
   /* Ignore downstream renegotiation request. */
-  if (!self->need_negotiation)
-    return TRUE;
-  self->need_negotiation = FALSE;
+  if (self->streaming)
+    goto done;
 
   GST_DEBUG_OBJECT (self, "Negotiate");
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
   gst_v4l2_codec_h265_dec_reset_allocation (self);
 
   if (!gst_v4l2_decoder_set_sink_fmt (self->decoder, V4L2_PIX_FMT_HEVC_SLICE,
@@ -397,6 +401,7 @@ gst_v4l2_codec_h265_dec_negotiate (GstVideoDecoder * decoder)
   }
   gst_caps_unref (caps);
 
+done:
   if (self->output_state)
     gst_video_codec_state_unref (self->output_state);
 
@@ -408,6 +413,9 @@ gst_v4l2_codec_h265_dec_negotiate (GstVideoDecoder * decoder)
   self->output_state->caps = gst_video_info_to_caps (&self->output_state->info);
 
   if (GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder)) {
+    if (self->streaming)
+      return TRUE;
+
     if (!gst_v4l2_decoder_streamon (self->decoder, GST_PAD_SINK)) {
       GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
           ("Could not enable the decoder driver."),
@@ -422,6 +430,8 @@ gst_v4l2_codec_h265_dec_negotiate (GstVideoDecoder * decoder)
       return FALSE;
     }
 
+    self->streaming = TRUE;
+
     return TRUE;
   }
 
@@ -435,6 +445,9 @@ gst_v4l2_codec_h265_dec_decide_allocation (GstVideoDecoder * decoder,
   GstV4l2CodecH265Dec *self = GST_V4L2_CODEC_H265_DEC (decoder);
   guint min = 0;
 
+  if (self->streaming)
+    return TRUE;
+
   self->has_videometa = gst_query_find_allocation_meta (query,
       GST_VIDEO_META_API_TYPE, NULL);
 
@@ -902,7 +915,7 @@ gst_v4l2_codec_h265_dec_new_sequence (GstH265Decoder * decoder,
   gst_v4l2_codec_h265_dec_fill_sequence (self, sps);
 
   if (negotiation_needed) {
-    self->need_negotiation = TRUE;
+    gst_v4l2_codec_h265_dec_streamoff (self);
     if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
       GST_ERROR_OBJECT (self, "Failed to negotiate with downstream");
       return GST_FLOW_NOT_NEGOTIATED;
@@ -1174,9 +1187,17 @@ gst_v4l2_codec_h265_dec_output_picture (GstH265Decoder * decoder,
     GstVideoCodecFrame * frame, GstH265Picture * picture)
 {
   GstV4l2CodecH265Dec *self = GST_V4L2_CODEC_H265_DEC (decoder);
+  GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder);
   GstV4l2Request *request = gst_h265_picture_get_user_data (picture);
   gint ret;
 
+  if (picture->discont_state) {
+    if (!gst_video_decoder_negotiate (vdec)) {
+      GST_ERROR_OBJECT (vdec, "Could not re-negotiate with updated state");
+      return FALSE;
+    }
+  }
+
   GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number);
 
   ret = gst_v4l2_request_set_done (request);
@@ -1207,10 +1228,10 @@ gst_v4l2_codec_h265_dec_output_picture (GstH265Decoder * decoder,
 
   gst_h265_picture_unref (picture);
 
-  return gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
+  return gst_video_decoder_finish_frame (vdec, frame);
 
 error:
-  gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame);
+  gst_video_decoder_drop_frame (vdec, frame);
   gst_h265_picture_unref (picture);
 
   return GST_FLOW_ERROR;
index d7982c8..00dd161 100644 (file)
@@ -84,7 +84,7 @@ struct _GstV4l2CodecMpeg2Dec
   GstV4l2CodecPool *src_pool;
   gint min_pool_size;
   gboolean has_videometa;
-  gboolean need_negotiation;
+  gboolean streaming;
 
   GstMemory *bitstream;
   GstMapInfo bitstream_map;
@@ -150,6 +150,16 @@ gst_v4l2_codec_mpeg2_dec_close (GstVideoDecoder * decoder)
 }
 
 static void
+gst_v4l2_codec_mpeg2_dec_streamoff (GstV4l2CodecMpeg2Dec * self)
+{
+  if (self->streaming) {
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
+    self->streaming = FALSE;
+  }
+}
+
+static void
 gst_v4l2_codec_mpeg2_dec_reset_allocation (GstV4l2CodecMpeg2Dec * self)
 {
   if (self->sink_allocator) {
@@ -169,9 +179,7 @@ gst_v4l2_codec_mpeg2_dec_stop (GstVideoDecoder * decoder)
 {
   GstV4l2CodecMpeg2Dec *self = GST_V4L2_CODEC_MPEG2_DEC (decoder);
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
+  gst_v4l2_codec_mpeg2_dec_streamoff (self);
   gst_v4l2_codec_mpeg2_dec_reset_allocation (self);
 
   if (self->output_state)
@@ -236,15 +244,11 @@ gst_v4l2_codec_mpeg2_dec_negotiate (GstVideoDecoder * decoder)
   GstCaps *filter, *caps;
 
   /* Ignore downstream renegotiation request. */
-  if (!self->need_negotiation)
-    return TRUE;
-  self->need_negotiation = FALSE;
+  if (self->streaming)
+    goto done;
 
   GST_DEBUG_OBJECT (self, "Negotiate");
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
   gst_v4l2_codec_mpeg2_dec_reset_allocation (self);
 
   if (!gst_v4l2_decoder_set_sink_fmt (self->decoder, V4L2_PIX_FMT_MPEG2_SLICE,
@@ -285,6 +289,7 @@ gst_v4l2_codec_mpeg2_dec_negotiate (GstVideoDecoder * decoder)
   }
   gst_caps_unref (caps);
 
+done:
   if (self->output_state)
     gst_video_codec_state_unref (self->output_state);
 
@@ -300,6 +305,9 @@ gst_v4l2_codec_mpeg2_dec_negotiate (GstVideoDecoder * decoder)
   self->output_state->caps = gst_video_info_to_caps (&self->output_state->info);
 
   if (GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder)) {
+    if (self->streaming)
+      return TRUE;
+
     if (!gst_v4l2_decoder_streamon (self->decoder, GST_PAD_SINK)) {
       GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
           ("Could not enable the decoder driver."),
@@ -314,6 +322,8 @@ gst_v4l2_codec_mpeg2_dec_negotiate (GstVideoDecoder * decoder)
       return FALSE;
     }
 
+    self->streaming = TRUE;
+
     return TRUE;
   }
 
@@ -418,7 +428,7 @@ gst_v4l2_codec_mpeg2_dec_new_sequence (GstMpeg2Decoder * decoder,
     GST_INFO_OBJECT (self, "Profile change %d -> %d",
         self->profile, mpeg_profile);
     self->profile = mpeg_profile;
-    self->need_negotiation = TRUE;
+    self->streaming = TRUE;
   }
 
   if (self->vinfo.finfo->format == GST_VIDEO_FORMAT_UNKNOWN)
@@ -449,7 +459,7 @@ gst_v4l2_codec_mpeg2_dec_new_sequence (GstMpeg2Decoder * decoder,
   /* *INDENT-ON* */
 
   if (negotiation_needed) {
-    self->need_negotiation = TRUE;
+    gst_v4l2_codec_mpeg2_dec_streamoff (self);
     if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
       GST_ERROR_OBJECT (self, "Failed to negotiate with downstream");
       return GST_FLOW_ERROR;
@@ -672,6 +682,13 @@ gst_v4l2_codec_mpeg2_dec_output_picture (GstMpeg2Decoder * decoder,
   GstV4l2Request *request = gst_mpeg2_picture_get_user_data (picture);
   gint ret;
 
+  if (picture->discont_state) {
+    if (!gst_video_decoder_negotiate (vdec)) {
+      GST_ERROR_OBJECT (vdec, "Could not re-negotiate with updated state");
+      return FALSE;
+    }
+  }
+
   GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number);
 
   ret = gst_v4l2_request_set_done (request);
index 95a6df1..2169c96 100644 (file)
@@ -73,7 +73,7 @@ struct _GstV4l2CodecVp8Dec
   GstV4l2CodecPool *src_pool;
   gint min_pool_size;
   gboolean has_videometa;
-  gboolean need_negotiation;
+  gboolean streaming;
   gboolean copy_frames;
 
   struct v4l2_ctrl_vp8_frame frame_header;
@@ -137,6 +137,16 @@ gst_v4l2_codec_vp8_dec_close (GstVideoDecoder * decoder)
 }
 
 static void
+gst_v4l2_codec_vp8_dec_streamoff (GstV4l2CodecVp8Dec * self)
+{
+  if (self->streaming) {
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
+    self->streaming = FALSE;
+  }
+}
+
+static void
 gst_v4l2_codec_vp8_dec_reset_allocation (GstV4l2CodecVp8Dec * self)
 {
   if (self->sink_allocator) {
@@ -156,9 +166,7 @@ gst_v4l2_codec_vp8_dec_stop (GstVideoDecoder * decoder)
 {
   GstV4l2CodecVp8Dec *self = GST_V4L2_CODEC_VP8_DEC (decoder);
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
+  gst_v4l2_codec_vp8_dec_streamoff (self);
   gst_v4l2_codec_vp8_dec_reset_allocation (self);
 
   if (self->output_state)
@@ -185,15 +193,11 @@ gst_v4l2_codec_vp8_dec_negotiate (GstVideoDecoder * decoder)
   GstCaps *filter, *caps;
 
   /* Ignore downstream renegotiation request. */
-  if (!self->need_negotiation)
-    return TRUE;
-  self->need_negotiation = FALSE;
+  if (self->streaming)
+    goto done;
 
   GST_DEBUG_OBJECT (self, "Negotiate");
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
   gst_v4l2_codec_vp8_dec_reset_allocation (self);
 
   if (!gst_v4l2_decoder_set_sink_fmt (self->decoder, V4L2_PIX_FMT_VP8_FRAME,
@@ -234,6 +238,7 @@ gst_v4l2_codec_vp8_dec_negotiate (GstVideoDecoder * decoder)
   }
   gst_caps_unref (caps);
 
+done:
   if (self->output_state)
     gst_video_codec_state_unref (self->output_state);
 
@@ -245,6 +250,9 @@ gst_v4l2_codec_vp8_dec_negotiate (GstVideoDecoder * decoder)
   self->output_state->caps = gst_video_info_to_caps (&self->output_state->info);
 
   if (GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder)) {
+    if (self->streaming)
+      return TRUE;
+
     if (!gst_v4l2_decoder_streamon (self->decoder, GST_PAD_SINK)) {
       GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
           ("Could not enable the decoder driver."),
@@ -259,6 +267,8 @@ gst_v4l2_codec_vp8_dec_negotiate (GstVideoDecoder * decoder)
       return FALSE;
     }
 
+    self->streaming = TRUE;
+
     return TRUE;
   }
 
@@ -273,6 +283,9 @@ gst_v4l2_codec_vp8_dec_decide_allocation (GstVideoDecoder * decoder,
   guint min = 0;
   guint num_bitstream;
 
+  if (self->streaming)
+    return TRUE;
+
   self->has_videometa = gst_query_find_allocation_meta (query,
       GST_VIDEO_META_API_TYPE, NULL);
 
@@ -470,7 +483,7 @@ gst_v4l2_codec_vp8_dec_new_sequence (GstVp8Decoder * decoder,
   gst_v4l2_codec_vp8_dec_fill_frame_header (self, frame_hdr);
 
   if (negotiation_needed) {
-    self->need_negotiation = TRUE;
+    gst_v4l2_codec_vp8_dec_streamoff (self);
     if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
       GST_ERROR_OBJECT (self, "Failed to negotiate with downstream");
       return GST_FLOW_NOT_NEGOTIATED;
@@ -710,6 +723,13 @@ gst_v4l2_codec_vp8_dec_output_picture (GstVp8Decoder * decoder,
   GstV4l2Request *request = gst_vp8_picture_get_user_data (picture);
   gint ret;
 
+  if (picture->discont_state) {
+    if (!gst_video_decoder_negotiate (vdec)) {
+      GST_ERROR_OBJECT (vdec, "Could not re-negotiate with updated state");
+      return FALSE;
+    }
+  }
+
   GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number);
 
   ret = gst_v4l2_request_set_done (request);
index 5902c1b..1640155 100644 (file)
@@ -73,7 +73,7 @@ struct _GstV4l2CodecVp9Dec
   GstV4l2CodecAllocator *src_allocator;
   GstV4l2CodecPool *src_pool;
   gboolean has_videometa;
-  gboolean need_negotiation;
+  gboolean streaming;
   gboolean copy_frames;
 
   struct v4l2_ctrl_vp9_frame v4l2_vp9_frame;
@@ -402,6 +402,16 @@ gst_v4l2_codec_vp9_dec_close (GstVideoDecoder * decoder)
 }
 
 static void
+gst_v4l2_codec_vp9_dec_streamoff (GstV4l2CodecVp9Dec * self)
+{
+  if (self->streaming) {
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
+    gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
+    self->streaming = FALSE;
+  }
+}
+
+static void
 gst_v4l2_codec_vp9_dec_reset_allocation (GstV4l2CodecVp9Dec * self)
 {
   if (self->sink_allocator) {
@@ -451,15 +461,11 @@ gst_v4l2_codec_vp9_dec_negotiate (GstVideoDecoder * decoder)
 
   GstCaps *filter, *caps;
   /* Ignore downstream renegotiation request. */
-  if (!self->need_negotiation)
-    return TRUE;
-  self->need_negotiation = FALSE;
+  if (self->streaming)
+    goto done;
 
   GST_DEBUG_OBJECT (self, "Negotiate");
 
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
-  gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
-
   gst_v4l2_codec_vp9_dec_reset_allocation (self);
 
   if (!gst_v4l2_decoder_set_sink_fmt (self->decoder, V4L2_PIX_FMT_VP9_FRAME,
@@ -499,6 +505,7 @@ gst_v4l2_codec_vp9_dec_negotiate (GstVideoDecoder * decoder)
   }
   gst_caps_unref (caps);
 
+done:
   if (self->output_state)
     gst_video_codec_state_unref (self->output_state);
 
@@ -510,6 +517,9 @@ gst_v4l2_codec_vp9_dec_negotiate (GstVideoDecoder * decoder)
   self->output_state->caps = gst_video_info_to_caps (&self->output_state->info);
 
   if (GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder)) {
+    if (self->streaming)
+      return TRUE;
+
     if (!gst_v4l2_decoder_streamon (self->decoder, GST_PAD_SINK)) {
       GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
           ("Could not enable the decoder driver."),
@@ -524,6 +534,8 @@ gst_v4l2_codec_vp9_dec_negotiate (GstVideoDecoder * decoder)
       return FALSE;
     }
 
+    self->streaming = TRUE;
+
     return TRUE;
   }
 
@@ -645,7 +657,7 @@ gst_v4l2_codec_vp9_dec_new_sequence (GstVp9Decoder * decoder,
     gst_v4l2_codec_vp9_dec_fill_prob_updates (self, frame_hdr);
 
   if (negotiation_needed) {
-    self->need_negotiation = TRUE;
+    gst_v4l2_codec_vp9_dec_streamoff (self);
     if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
       GST_ERROR_OBJECT (self, "Failed to negotiate with downstream");
       return GST_FLOW_ERROR;
@@ -932,6 +944,13 @@ gst_v4l2_codec_vp9_dec_output_picture (GstVp9Decoder * decoder,
   GstV4l2Request *request = NULL;
   gint ret;
 
+  if (picture->discont_state) {
+    if (!gst_video_decoder_negotiate (vdec)) {
+      GST_ERROR_OBJECT (vdec, "Could not re-negotiate with updated state");
+      return FALSE;
+    }
+  }
+
   GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number);
 
   if (!GST_MINI_OBJECT_FLAG_IS_SET (picture, FLAG_PICTURE_HOLDS_BUFFER))