v4l2videodec: Add resolution change support

Support resolution change event.

https://bugzilla.gnome.org/show_bug.cgi?id=752962

UpStream Status: Pending
diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c
index 5a92dbb..2653331 100644
--- a/sys/v4l2/gstv4l2bufferpool.c
+++ b/sys/v4l2/gstv4l2bufferpool.c
@@ -1120,6 +1120,37 @@
 }
 
 static GstFlowReturn
+gst_v4l2_buffer_pool_dqevent (GstV4l2BufferPool * pool)
+{
+  GstV4l2Object *obj = pool->obj;
+  struct v4l2_event evt;
+
+  memset (&evt, 0x00, sizeof (struct v4l2_event));
+  if (obj->ioctl (pool->video_fd, VIDIOC_DQEVENT, &evt) < 0)
+    goto dqevent_failed;
+
+  switch (evt.type)
+  {
+    case V4L2_EVENT_SOURCE_CHANGE:
+      return GST_V4L2_FLOW_SOURCE_CHANGE;
+      break;
+    case V4L2_EVENT_EOS:
+      return GST_V4L2_FLOW_LAST_BUFFER;
+      break;
+    default:
+      break;
+  }
+
+  return GST_FLOW_OK;
+
+  /* ERRORS */
+dqevent_failed:
+  {
+    return GST_FLOW_ERROR;
+  }
+}
+
+static GstFlowReturn
 gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer)
 {
   GstFlowReturn res;
@@ -1131,11 +1162,6 @@
   gsize size;
   gint i;
 
-  if ((res = gst_v4l2_buffer_pool_poll (pool)) != GST_FLOW_OK)
-    goto poll_failed;
-
-  GST_LOG_OBJECT (pool, "dequeueing a buffer");
-
   res = gst_v4l2_allocator_dqbuf (pool->vallocator, &group);
   if (res == GST_FLOW_EOS)
     goto eos;
@@ -1263,11 +1289,6 @@
   return GST_FLOW_OK;
 
   /* ERRORS */
-poll_failed:
-  {
-    GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res));
-    return res;
-  }
 eos:
   {
     return GST_FLOW_EOS;
@@ -1285,6 +1306,30 @@
 }
 
 static GstFlowReturn
+gst_v4l2_buffer_pool_dequeue (GstV4l2BufferPool * pool, GstBuffer ** buffer)
+{
+  GstFlowReturn res;
+  GstV4l2Object *obj = pool->obj;
+
+  if ((res = gst_v4l2_buffer_pool_poll (pool)) != GST_FLOW_OK)
+    goto poll_failed;
+
+  if (obj->can_wait_event && gst_poll_fd_can_read_pri (pool->poll, &pool->pollfd)) {
+    return gst_v4l2_buffer_pool_dqevent (pool);
+  }
+
+  GST_LOG_OBJECT (pool, "dequeueing a buffer");
+  return gst_v4l2_buffer_pool_dqbuf (pool, buffer);
+
+  /* ERRORS */
+poll_failed:
+  {
+    GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res));
+    return res;
+  }
+}
+
+static GstFlowReturn
 gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
     GstBufferPoolAcquireParams * params)
 {
@@ -1320,7 +1365,7 @@
           /* just dequeue a buffer, we basically use the queue of v4l2 as the
            * storage for our buffers. This function does poll first so we can
            * interrupt it fine. */
-          ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer);
+          ret = gst_v4l2_buffer_pool_dequeue (pool, buffer);
           break;
         }
         default:
@@ -1767,7 +1812,7 @@
           }
 
           /* buffer not from our pool, grab a frame and copy it into the target */
-          if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp)) != GST_FLOW_OK)
+          if ((ret = gst_v4l2_buffer_pool_dequeue (pool, &tmp)) != GST_FLOW_OK)
             goto done;
 
           /* An empty buffer on capture indicates the end of stream */
@@ -1920,7 +1965,7 @@
             GstBuffer *out;
             /* all buffers are queued, try to dequeue one and release it back
              * into the pool so that _acquire can get to it again. */
-            ret = gst_v4l2_buffer_pool_dqbuf (pool, &out);
+            ret = gst_v4l2_buffer_pool_dequeue (pool, &out);
             if (ret == GST_FLOW_OK && out->pool == NULL)
               /* release the rendered buffer back into the pool. This wakes up any
                * thread waiting for a buffer in _acquire(). */
diff --git a/sys/v4l2/gstv4l2bufferpool.h b/sys/v4l2/gstv4l2bufferpool.h
index 0fffc71..da23405 100644
--- a/sys/v4l2/gstv4l2bufferpool.h
+++ b/sys/v4l2/gstv4l2bufferpool.h
@@ -42,8 +42,8 @@
 #define GST_V4L2_BUFFER_POOL_CAST(obj) ((GstV4l2BufferPool*)(obj))
 
 /* This flow return is used to indicated that the last buffer of a
- * drain or a resoltuion change has been found. This should normally
- * only occure for mem-2-mem devices. */
+ * drain has been found. This should normally only occure for
+ * mem-2-mem devices. */
 #define GST_V4L2_FLOW_LAST_BUFFER GST_FLOW_CUSTOM_SUCCESS
 
 /* This flow return is used to indicated that the returned buffer was marked
@@ -51,6 +51,11 @@
  * simply waiting for next buffer. */
 #define GST_V4L2_FLOW_CORRUPTED_BUFFER GST_FLOW_CUSTOM_SUCCESS_1
 
+/* This flow return is used to indicated that the last buffer of a
+ * resoltuion change has been found. This should normally only
+ * occure for mem-2-mem devices. */
+#define GST_V4L2_FLOW_SOURCE_CHANGE GST_FLOW_CUSTOM_SUCCESS_2
+
 struct _GstV4l2BufferPool
 {
   GstBufferPool parent;
diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c
index 124c778..9e1fe90 100644
--- a/sys/v4l2/gstv4l2object.c
+++ b/sys/v4l2/gstv4l2object.c
@@ -532,6 +532,10 @@
     v4l2object->munmap = munmap;
   }
 
+  v4l2object->poll = gst_poll_new (TRUE);
+  v4l2object->can_wait_event = FALSE;
+  v4l2object->can_poll_device = TRUE;
+
   return v4l2object;
 }
 
@@ -903,6 +907,14 @@
 
   ret = gst_v4l2_dup (v4l2object, other);
 
+  if (ret && !V4L2_TYPE_IS_OUTPUT (v4l2object->type)) {
+    gst_poll_fd_init (&v4l2object->pollfd);
+    v4l2object->pollfd.fd = v4l2object->video_fd;
+    gst_poll_add_fd (v4l2object->poll, &v4l2object->pollfd);
+    /* used for dequeue event */
+    gst_poll_fd_ctl_read (v4l2object->poll, &v4l2object->pollfd, TRUE);
+  }
+
   return ret;
 }
 
@@ -3869,6 +3881,99 @@
   return gst_v4l2_object_set_format_full (v4l2object, caps, TRUE, error);
 }
 
+GstFlowReturn
+gst_v4l2_object_poll (GstV4l2Object * v4l2object)
+{
+  gint ret;
+
+  if (!v4l2object->can_poll_device)
+    goto done;
+
+  GST_LOG_OBJECT (v4l2object, "polling device");
+
+again:
+  ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
+  if (G_UNLIKELY (ret < 0)) {
+    switch (errno) {
+      case EBUSY:
+        goto stopped;
+      case EAGAIN:
+      case EINTR:
+        goto again;
+      case ENXIO:
+        GST_WARNING_OBJECT (v4l2object,
+            "v4l2 device doesn't support polling. Disabling"
+            " using libv4l2 in this case may cause deadlocks");
+        v4l2object->can_poll_device = FALSE;
+        goto done;
+      default:
+        goto select_error;
+    }
+  }
+
+  if (gst_poll_fd_has_error (v4l2object->poll, &v4l2object->pollfd))
+    goto select_error;
+
+done:
+  return GST_FLOW_OK;
+
+  /* ERRORS */
+stopped:
+  {
+    GST_DEBUG_OBJECT (v4l2object, "stop called");
+    return GST_FLOW_FLUSHING;
+  }
+select_error:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
+        ("poll error %d: %s (%d)", ret, g_strerror (errno), errno));
+    return GST_FLOW_ERROR;
+  }
+}
+
+GstFlowReturn
+gst_v4l2_object_dqevent (GstV4l2Object * v4l2object)
+{
+  GstFlowReturn res;
+  struct v4l2_event evt;
+
+  GST_ERROR_OBJECT (v4l2object, "dequeueing a event");
+  if ((res = gst_v4l2_object_poll (v4l2object)) != GST_FLOW_OK)
+    goto poll_failed;
+
+  GST_ERROR_OBJECT (v4l2object, "1equeueing a event");
+
+  memset (&evt, 0x00, sizeof (struct v4l2_event));
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_DQEVENT, &evt) < 0)
+    goto dqevent_failed;
+
+  GST_ERROR_OBJECT (v4l2object, "2equeueing a event: %d", evt.type);
+  switch (evt.type)
+  {
+    case V4L2_EVENT_SOURCE_CHANGE:
+      return GST_V4L2_FLOW_SOURCE_CHANGE;
+      break;
+    case V4L2_EVENT_EOS:
+      return GST_V4L2_FLOW_LAST_BUFFER;
+      break;
+    default:
+      break;
+  }
+
+  return GST_FLOW_OK;
+
+  /* ERRORS */
+poll_failed:
+  {
+    GST_DEBUG_OBJECT (v4l2object, "poll error %s", gst_flow_get_name (res));
+    return res;
+  }
+dqevent_failed:
+  {
+    return GST_FLOW_ERROR;
+  }
+}
+
 /**
  * gst_v4l2_object_acquire_format:
  * @v4l2object the object
@@ -4116,6 +4221,8 @@
 
   GST_LOG_OBJECT (v4l2object->dbg_obj, "start flushing");
 
+  gst_poll_set_flushing (v4l2object->poll, TRUE);
+
   if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool))
     gst_buffer_pool_set_flushing (v4l2object->pool, TRUE);
 
@@ -4132,6 +4239,8 @@
   if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool))
     gst_buffer_pool_set_flushing (v4l2object->pool, FALSE);
 
+  gst_poll_set_flushing (v4l2object->poll, FALSE);
+
   return ret;
 }
 
@@ -4152,6 +4261,8 @@
     v4l2object->pool = NULL;
   }
 
+  gst_poll_free (v4l2object->poll);
+
   GST_V4L2_SET_INACTIVE (v4l2object);
 
 done:
diff --git a/sys/v4l2/gstv4l2object.h b/sys/v4l2/gstv4l2object.h
index 7871eaf..11e8057 100644
--- a/sys/v4l2/gstv4l2object.h
+++ b/sys/v4l2/gstv4l2object.h
@@ -122,6 +122,9 @@
   /* the video-device's file descriptor */
   gint video_fd;
   GstV4l2IOMode mode;
+  GstPoll *poll;             /* a poll for video_fd */
+  GstPollFD pollfd;
+  gboolean can_poll_device;
 
   gboolean active;
   gboolean streaming;
@@ -208,6 +211,8 @@
    * on slow USB firmwares. When this is set, gst_v4l2_set_format() will modify
    * the caps to reflect what was negotiated during fixation */
   gboolean skip_try_fmt_probes;
+  gboolean can_wait_event;
+  gboolean need_wait_event;
 };
 
 struct _GstV4l2ObjectClassHelper {
@@ -290,6 +295,7 @@
 GstCaps *    gst_v4l2_object_probe_caps  (GstV4l2Object * v4l2object, GstCaps * filter);
 GstCaps *    gst_v4l2_object_get_caps    (GstV4l2Object * v4l2object, GstCaps * filter);
 
+GstFlowReturn gst_v4l2_object_dqevent     (GstV4l2Object * v4l2object);
 gboolean     gst_v4l2_object_acquire_format (GstV4l2Object * v4l2object, GstVideoInfo * info);
 
 gboolean     gst_v4l2_object_set_crop    (GstV4l2Object * obj);
diff --git a/sys/v4l2/gstv4l2videodec.c b/sys/v4l2/gstv4l2videodec.c
index 838ebff..0da7210 100644
--- a/sys/v4l2/gstv4l2videodec.c
+++ b/sys/v4l2/gstv4l2videodec.c
@@ -457,72 +457,6 @@
   return frame;
 }
 
-static void
-gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
-{
-  GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
-  GstV4l2BufferPool *v4l2_pool = GST_V4L2_BUFFER_POOL (self->v4l2capture->pool);
-  GstBufferPool *pool;
-  GstVideoCodecFrame *frame;
-  GstBuffer *buffer = NULL;
-  GstFlowReturn ret;
-
-  GST_LOG_OBJECT (decoder, "Allocate output buffer");
-
-  self->output_flow = GST_FLOW_OK;
-  do {
-    /* We cannot use the base class allotate helper since it taking the internal
-     * stream lock. we know that the acquire may need to poll until more frames
-     * comes in and holding this lock would prevent that.
-     */
-    pool = gst_video_decoder_get_buffer_pool (decoder);
-
-    /* Pool may be NULL if we started going to READY state */
-    if (pool == NULL) {
-      ret = GST_FLOW_FLUSHING;
-      goto beach;
-    }
-
-    ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
-    g_object_unref (pool);
-
-    if (ret != GST_FLOW_OK)
-      goto beach;
-
-    GST_LOG_OBJECT (decoder, "Process output buffer");
-    ret = gst_v4l2_buffer_pool_process (v4l2_pool, &buffer);
-
-  } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER);
-
-  if (ret != GST_FLOW_OK)
-    goto beach;
-
-  frame = gst_v4l2_video_dec_get_oldest_frame (decoder);
-
-  if (frame) {
-    frame->output_buffer = buffer;
-    buffer = NULL;
-    ret = gst_video_decoder_finish_frame (decoder, frame);
-
-    if (ret != GST_FLOW_OK)
-      goto beach;
-  } else {
-    GST_WARNING_OBJECT (decoder, "Decoder is producing too many buffers");
-    gst_buffer_unref (buffer);
-  }
-
-  return;
-
-beach:
-  GST_DEBUG_OBJECT (decoder, "Leaving output thread: %s",
-      gst_flow_get_name (ret));
-
-  gst_buffer_replace (&buffer, NULL);
-  self->output_flow = ret;
-  gst_v4l2_object_unlock (self->v4l2output);
-  gst_pad_pause_task (decoder->srcpad);
-}
-
 static gboolean
 gst_v4l2_video_remove_padding (GstCapsFeatures * features,
     GstStructure * structure, gpointer user_data)
@@ -557,74 +491,39 @@
   return TRUE;
 }
 
-static GstFlowReturn
-gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
-    GstVideoCodecFrame * frame)
+static void
+gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
 {
-  GstV4l2Error error = GST_V4L2_ERROR_INIT;
   GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
-  GstFlowReturn ret = GST_FLOW_OK;
-  gboolean processed = FALSE;
-  GstBuffer *tmp;
-  GstTaskState task_state;
-
-  GST_DEBUG_OBJECT (self, "Handling frame %d", frame->system_frame_number);
-
-  if (G_UNLIKELY (!g_atomic_int_get (&self->active)))
-    goto flushing;
-
-  if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2output))) {
-    if (!self->input_state)
-      goto not_negotiated;
-    if (!gst_v4l2_object_set_format (self->v4l2output, self->input_state->caps,
-            &error))
-      goto not_negotiated;
-  }
+  GstV4l2BufferPool *v4l2_pool;
+  GstBufferPool *pool;
+  GstVideoCodecFrame *frame;
+  GstBuffer *buffer = NULL;
+  GstFlowReturn ret;
 
   if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) {
-    GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool);
+    GstV4l2Error error = GST_V4L2_ERROR_INIT;
     GstVideoInfo info;
     GstVideoCodecState *output_state;
-    GstBuffer *codec_data;
     GstCaps *acquired_caps, *available_caps, *caps, *filter;
     GstStructure *st;
 
-    GST_DEBUG_OBJECT (self, "Sending header");
-
-    codec_data = self->input_state->codec_data;
-
-    /* We are running in byte-stream mode, so we don't know the headers, but
-     * we need to send something, otherwise the decoder will refuse to
-     * intialize.
-     */
-    if (codec_data) {
-      gst_buffer_ref (codec_data);
-    } else {
-      codec_data = gst_buffer_ref (frame->input_buffer);
-      processed = TRUE;
+    /* Wait until received SOURCE_CHANGE event to get right video format */
+    while (self->v4l2capture->can_wait_event
+        && self->v4l2capture->need_wait_event) {
+      ret = gst_v4l2_object_dqevent (self->v4l2capture);
+      if (ret == GST_V4L2_FLOW_SOURCE_CHANGE) {
+        GST_DEBUG_OBJECT (self, "Received source change event");
+        break;
+      } else if (ret == GST_V4L2_FLOW_LAST_BUFFER) {
+        GST_DEBUG_OBJECT (self, "Received eos event");
+        goto beach;
+      } else if (ret != GST_FLOW_OK) {
+        GST_ERROR_OBJECT (self, "dqevent error");
+        goto beach;
+      }
     }
-
-    /* Ensure input internal pool is active */
-    if (!gst_buffer_pool_is_active (pool)) {
-      GstStructure *config = gst_buffer_pool_get_config (pool);
-      gst_buffer_pool_config_set_params (config, self->input_state->caps,
-          self->v4l2output->info.size, 2, 2);
-
-      /* There is no reason to refuse this config */
-      if (!gst_buffer_pool_set_config (pool, config))
-        goto activate_failed;
-
-      if (!gst_buffer_pool_set_active (pool, TRUE))
-        goto activate_failed;
-    }
-
-    GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
-    ret =
-        gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self->
-            v4l2output->pool), &codec_data);
-    GST_VIDEO_DECODER_STREAM_LOCK (decoder);
-
-    gst_buffer_unref (codec_data);
+    self->v4l2capture->need_wait_event = FALSE;
 
     /* For decoders G_FMT returns coded size, G_SELECTION returns visible size
      * in the compose rectangle. gst_v4l2_object_acquire_format() checks both
@@ -694,6 +593,155 @@
       goto activate_failed;
   }
 
+  GST_LOG_OBJECT (decoder, "Allocate output buffer");
+
+  v4l2_pool = GST_V4L2_BUFFER_POOL (self->v4l2capture->pool);
+
+  self->output_flow = GST_FLOW_OK;
+  do {
+    /* We cannot use the base class allotate helper since it taking the internal
+     * stream lock. we know that the acquire may need to poll until more frames
+     * comes in and holding this lock would prevent that.
+     */
+    pool = gst_video_decoder_get_buffer_pool (decoder);
+
+    /* Pool may be NULL if we started going to READY state */
+    if (pool == NULL) {
+      ret = GST_FLOW_FLUSHING;
+      goto beach;
+    }
+
+    ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
+    g_object_unref (pool);
+
+    if (ret != GST_FLOW_OK)
+      goto beach;
+
+    GST_LOG_OBJECT (decoder, "Process output buffer");
+    ret = gst_v4l2_buffer_pool_process (v4l2_pool, &buffer);
+    if (ret == GST_V4L2_FLOW_SOURCE_CHANGE) {
+      gst_v4l2_object_stop (self->v4l2capture);
+      return;
+    }
+  } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER);
+
+  if (ret != GST_FLOW_OK)
+    goto beach;
+
+  frame = gst_v4l2_video_dec_get_oldest_frame (decoder);
+
+  if (frame) {
+    frame->output_buffer = buffer;
+    buffer = NULL;
+    ret = gst_video_decoder_finish_frame (decoder, frame);
+
+    if (ret != GST_FLOW_OK)
+      goto beach;
+  } else {
+    GST_WARNING_OBJECT (decoder, "Decoder is producing too many buffers");
+    gst_buffer_unref (buffer);
+  }
+
+  return;
+
+/* ERRORS */
+not_negotiated:
+  {
+    GST_ERROR_OBJECT (self, "not negotiated");
+    ret = GST_FLOW_NOT_NEGOTIATED;
+    goto beach;
+  }
+activate_failed:
+  {
+    GST_ERROR_OBJECT (self, "Buffer pool activation failed");
+    GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
+        (_("Failed to allocate required memory.")),
+        ("Buffer pool activation failed"));
+    ret = GST_FLOW_ERROR;
+    goto beach;
+  }
+flushing:
+  {
+    ret = GST_FLOW_FLUSHING;
+    goto beach;
+  }
+beach:
+  GST_DEBUG_OBJECT (decoder, "Leaving output thread: %s",
+      gst_flow_get_name (ret));
+
+  gst_buffer_replace (&buffer, NULL);
+  self->output_flow = ret;
+  gst_v4l2_object_unlock (self->v4l2output);
+  gst_pad_pause_task (decoder->srcpad);
+}
+
+static GstFlowReturn
+gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
+    GstVideoCodecFrame * frame)
+{
+  GstV4l2Error error = GST_V4L2_ERROR_INIT;
+  GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
+  GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool);
+  GstFlowReturn ret = GST_FLOW_OK;
+  gboolean processed = FALSE;
+  GstBuffer *tmp;
+  GstTaskState task_state;
+
+  GST_DEBUG_OBJECT (self, "Handling frame %d", frame->system_frame_number);
+
+  if (G_UNLIKELY (!g_atomic_int_get (&self->active)))
+    goto flushing;
+
+  if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2output))) {
+    if (!self->input_state)
+      goto not_negotiated;
+    if (!gst_v4l2_object_set_format (self->v4l2output, self->input_state->caps,
+            &error))
+      goto not_negotiated;
+  }
+
+  if (G_UNLIKELY (!gst_buffer_pool_is_active (pool))) {
+    GstBuffer *codec_data;
+
+    GST_DEBUG_OBJECT (self, "Sending header");
+
+    codec_data = self->input_state->codec_data;
+
+    /* We are running in byte-stream mode, so we don't know the headers, but
+     * we need to send something, otherwise the decoder will refuse to
+     * intialize.
+     */
+    if (codec_data) {
+      gst_buffer_ref (codec_data);
+    } else {
+      codec_data = gst_buffer_ref (frame->input_buffer);
+      processed = TRUE;
+    }
+
+    /* Ensure input internal pool is active */
+    if (!gst_buffer_pool_is_active (pool)) {
+      GstStructure *config = gst_buffer_pool_get_config (pool);
+      gst_buffer_pool_config_set_params (config, self->input_state->caps,
+          self->v4l2output->info.size, 2, 2);
+
+      /* There is no reason to refuse this config */
+      if (!gst_buffer_pool_set_config (pool, config))
+        goto activate_failed;
+
+      if (!gst_buffer_pool_set_active (pool, TRUE))
+        goto activate_failed;
+    }
+
+    GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
+    ret =
+        gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self->
+            v4l2output->pool), &codec_data);
+    GST_VIDEO_DECODER_STREAM_LOCK (decoder);
+
+    gst_buffer_unref (codec_data);
+
+  }
+
   task_state = gst_pad_get_task_state (GST_VIDEO_DECODER_SRC_PAD (self));
   if (task_state == GST_TASK_STOPPED || task_state == GST_TASK_PAUSED) {
     /* It's possible that the processing thread stopped due to an error */
@@ -961,6 +1009,9 @@
       GST_OBJECT (GST_VIDEO_DECODER_SRC_PAD (self)),
       V4L2_BUF_TYPE_VIDEO_CAPTURE, klass->default_device,
       gst_v4l2_get_input, gst_v4l2_set_input, NULL);
+  self->v4l2capture->no_initial_format = TRUE;
+  self->v4l2capture->need_wait_event = TRUE;
+  self->v4l2output->keep_aspect = FALSE;
 }
 
 static void
diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c
index d3dbd42..51467fc 100644
--- a/sys/v4l2/v4l2_calls.c
+++ b/sys/v4l2/v4l2_calls.c
@@ -509,6 +509,42 @@
   }
 }
 
+gboolean
+gst_v4l2_subscribe_event (GstV4l2Object * v4l2object)
+{
+  GstElement *e;
+  struct v4l2_event_subscription  sub;
+
+  e = v4l2object->element;
+
+  GST_DEBUG_OBJECT (e, "subscribe event");
+
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
+
+  memset(&sub, 0, sizeof(struct v4l2_event_subscription));
+  sub.type = V4L2_EVENT_SOURCE_CHANGE;
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_SUBSCRIBE_EVENT, &sub) < 0)
+    goto failed;
+
+  sub.type = V4L2_EVENT_EOS;
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_SUBSCRIBE_EVENT, &sub) < 0)
+    goto failed;
+
+  v4l2object->can_wait_event = TRUE;
+
+  return TRUE;
+
+  /* ERRORS */
+failed:
+  {
+    GST_WARNING_OBJECT (e, "Cannot subscribe V4L2_EVENT_SOURCE_CHANGE or "
+        "V4L2_EVENT_EOS event for device '%s'.",
+        v4l2object->videodev);
+    return TRUE;
+  }
+}
+
 /******************************************************
  * gst_v4l2_open():
  *   open the video device (v4l2object->videodev)
@@ -591,6 +627,10 @@
   if (v4l2object->extra_controls)
     gst_v4l2_set_controls (v4l2object, v4l2object->extra_controls);
 
+  if (GST_IS_V4L2_VIDEO_DEC (v4l2object->element)) {
+    gst_v4l2_subscribe_event (v4l2object);
+  }
+
   /* UVC devices are never interlaced, and doing VIDIOC_TRY_FMT on them
    * causes expensive and slow USB IO, so don't probe them for interlaced
    */
@@ -689,6 +729,7 @@
 
   v4l2object->never_interlaced = other->never_interlaced;
   v4l2object->no_initial_format = other->no_initial_format;
+  v4l2object->can_wait_event = other->can_wait_event;
 
   return TRUE;