v4l2codecs: Wait for buffers to come back
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Fri, 20 Mar 2020 16:37:41 +0000 (12:37 -0400)
committerNicolas Dufresne <nicolas.dufresne@collabora.com>
Tue, 31 Mar 2020 13:34:05 +0000 (09:34 -0400)
This code add required mechanism to try and allocate (not implemented yet)
otherwise wait for more buffers. This also comes with mechanism to terminate
the wait on flush or PAUSED_TO_READY transitions.

sys/v4l2codecs/gstv4l2codech264dec.c
sys/v4l2codecs/gstv4l2codecpool.c
sys/v4l2codecs/gstv4l2decoder.c

index db088a9..7ac430c 100644 (file)
@@ -107,14 +107,9 @@ gst_v4l2_codec_h264_dec_close (GstVideoDecoder * decoder)
   return TRUE;
 }
 
-static gboolean
-gst_v4l2_codec_h264_dec_stop (GstVideoDecoder * decoder)
+static void
+gst_v4l2_codec_h264_dec_reset_allocation (GstV4l2CodecH264Dec * self)
 {
-  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);
-
   if (self->sink_allocator) {
     gst_v4l2_codec_allocator_detach (self->sink_allocator);
     g_clear_object (&self->sink_allocator);
@@ -125,6 +120,17 @@ gst_v4l2_codec_h264_dec_stop (GstVideoDecoder * decoder)
     g_clear_object (&self->src_allocator);
     g_clear_object (&self->src_pool);
   }
+}
+
+static gboolean
+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_reset_allocation (self);
 
   if (self->output_state)
     gst_video_codec_state_unref (self->output_state);
@@ -156,11 +162,7 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
 
   GST_DEBUG_OBJECT (self, "Negotiate");
 
-  if (self->sink_allocator)
-    gst_v4l2_codec_allocator_detach (self->sink_allocator);
-
-  if (self->src_allocator)
-    gst_v4l2_codec_allocator_detach (self->src_allocator);
+  gst_v4l2_codec_h264_dec_reset_allocation (self);
 
   gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SINK);
   gst_v4l2_decoder_streamoff (self->decoder, GST_PAD_SRC);
@@ -729,9 +731,11 @@ gst_v4l2_codec_h264_dec_end_picture (GstH264Decoder * decoder,
   flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->src_pool),
       &buffer, NULL);
   if (flow_ret != GST_FLOW_OK) {
-    /* FIXME our pool does not wait */
-    GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE,
-        ("No more picture buffer available."), (NULL));
+    if (flow_ret == GST_FLOW_FLUSHING)
+      GST_DEBUG_OBJECT (self, "Frame decoding aborted, we are flushing.");
+    else
+      GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE,
+          ("No more picture buffer available."), (NULL));
     goto fail;
   }
 
@@ -808,6 +812,16 @@ gst_v4l2_codec_h264_dec_decode_slice (GstH264Decoder * decoder,
   return TRUE;
 }
 
+static void
+gst_v4l2_codec_h264_dec_set_flushing (GstV4l2CodecH264Dec * self,
+    gboolean flushing)
+{
+  if (self->sink_allocator)
+    gst_v4l2_codec_allocator_set_flushing (self->sink_allocator, flushing);
+  if (self->src_allocator)
+    gst_v4l2_codec_allocator_set_flushing (self->src_allocator, flushing);
+}
+
 static gboolean
 gst_v4l2_codec_h264_dec_flush (GstVideoDecoder * decoder)
 {
@@ -816,10 +830,40 @@ gst_v4l2_codec_h264_dec_flush (GstVideoDecoder * decoder)
   GST_DEBUG_OBJECT (self, "Flushing decoder state.");
 
   gst_v4l2_decoder_flush (self->decoder);
+  gst_v4l2_codec_h264_dec_set_flushing (self, FALSE);
 
   return GST_VIDEO_DECODER_CLASS (parent_class)->flush (decoder);
 }
 
+static gboolean
+gst_v4l2_codec_h264_dec_sink_event (GstVideoDecoder * decoder, GstEvent * event)
+{
+  GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_START:
+      GST_DEBUG_OBJECT (self, "flush start");
+      gst_v4l2_codec_h264_dec_set_flushing (self, TRUE);
+      break;
+    default:
+      break;
+  }
+
+  return GST_VIDEO_DECODER_CLASS (parent_class)->sink_event (decoder, event);
+}
+
+static GstStateChangeReturn
+gst_v4l2_codec_h264_dec_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (element);
+
+  if (transition == GST_STATE_CHANGE_PAUSED_TO_READY)
+    gst_v4l2_codec_h264_dec_set_flushing (self, TRUE);
+
+  return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+}
+
 static void
 gst_v4l2_codec_h264_dec_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
@@ -900,6 +944,8 @@ gst_v4l2_codec_h264_dec_subclass_init (GstV4l2CodecH264DecClass * klass,
 
   gst_element_class_add_static_pad_template (element_class, &sink_template);
   gst_element_class_add_static_pad_template (element_class, &src_template);
+  element_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_change_state);
 
   decoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_open);
   decoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_close);
@@ -909,6 +955,8 @@ gst_v4l2_codec_h264_dec_subclass_init (GstV4l2CodecH264DecClass * klass,
   decoder_class->decide_allocation =
       GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_decide_allocation);
   decoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_flush);
+  decoder_class->sink_event =
+      GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_sink_event);
 
   h264decoder_class->new_sequence =
       GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_new_sequence);
index 69a01d4..9f6d696 100644 (file)
@@ -60,9 +60,28 @@ gst_v4l2_codec_pool_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer,
   if (!buf)
     buf = gst_v4l2_codec_pool_create_empty_buffer ();
 
+  /* First, just try and obtain a buffer. */
   if (!gst_v4l2_codec_allocator_prepare_buffer (self->allocator, buf)) {
-    gst_atomic_queue_push (self->queue, buf);
-    return GST_FLOW_ERROR;
+    GstFlowReturn flow_ret = GST_FLOW_OK;
+
+    /* If none were available, try and allocate one. */
+    if (!gst_v4l2_codec_allocator_create_buffer (self->allocator)) {
+      /* Otherwise, wait if this is allowed. */
+      if (params && params->flags & GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT) {
+        flow_ret = GST_FLOW_EOS;
+      } else {
+        if (!gst_v4l2_codec_allocator_wait_for_buffer (self->allocator))
+          flow_ret = GST_FLOW_FLUSHING;
+      }
+    }
+
+    if (flow_ret != GST_FLOW_OK) {
+      gst_atomic_queue_push (self->queue, buf);
+      return flow_ret;
+    }
+
+    /* Finally, pop the buffer we created or waited for. */
+    gst_v4l2_codec_allocator_prepare_buffer (self->allocator, buf);
   }
 
   vmeta = gst_buffer_get_video_meta (buf);
index beb2e56..d23dd5a 100644 (file)
@@ -688,11 +688,14 @@ gst_v4l2_request_free (GstV4l2Request * request)
   request->decoder = NULL;
 
   if (request->pending) {
+    GST_DEBUG_OBJECT (decoder, "Freeing pending request %p.", request);
     gst_v4l2_request_free (request);
     g_object_unref (decoder);
     return;
   }
 
+  GST_DEBUG_OBJECT (decoder, "Recycling request %p.", request);
+
   ret = ioctl (request->fd, MEDIA_REQUEST_IOC_REINIT, NULL);
   if (ret < 0) {
     GST_ERROR_OBJECT (request->decoder, "MEDIA_REQUEST_IOC_REINIT failed: %s",
@@ -711,6 +714,8 @@ gst_v4l2_request_queue (GstV4l2Request * request)
 {
   gint ret;
 
+  GST_DEBUG_OBJECT (request->decoder, "Queuing request %p.", request);
+
   ret = ioctl (request->fd, MEDIA_REQUEST_IOC_QUEUE, NULL);
   if (ret < 0) {
     GST_ERROR_OBJECT (request->decoder, "MEDIA_REQUEST_IOC_QUEUE, failed: %s",