Commit 1f99b152 authored by Song Bing's avatar Song Bing

v4l2videodec: Add resolution change support

Support resolution change event.

https://bugzilla.gnome.org/show_bug.cgi?id=752962
parent f6b91fe3
Pipeline #42255 failed with stages
in 7 minutes and 58 seconds
...@@ -1201,8 +1201,35 @@ queue_failed: ...@@ -1201,8 +1201,35 @@ queue_failed:
} }
static GstFlowReturn static GstFlowReturn
gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer, gst_v4l2_buffer_pool_dqevent (GstV4l2BufferPool * pool)
gboolean wait) {
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;
case V4L2_EVENT_EOS:
return GST_V4L2_FLOW_LAST_BUFFER;
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; GstFlowReturn res;
GstBuffer *outbuf = NULL; GstBuffer *outbuf = NULL;
...@@ -1213,16 +1240,6 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer, ...@@ -1213,16 +1240,6 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
gsize size; gsize size;
gint i; gint i;
if ((res = gst_v4l2_buffer_pool_poll (pool, wait)) < GST_FLOW_OK)
goto poll_failed;
if (res == GST_FLOW_CUSTOM_SUCCESS) {
GST_LOG_OBJECT (pool, "nothing to dequeue");
goto done;
}
GST_LOG_OBJECT (pool, "dequeueing a buffer");
res = gst_v4l2_allocator_dqbuf (pool->vallocator, &group); res = gst_v4l2_allocator_dqbuf (pool->vallocator, &group);
if (res == GST_FLOW_EOS) if (res == GST_FLOW_EOS)
goto eos; goto eos;
...@@ -1350,11 +1367,6 @@ done: ...@@ -1350,11 +1367,6 @@ done:
return res; return res;
/* ERRORS */ /* ERRORS */
poll_failed:
{
GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res));
return res;
}
eos: eos:
{ {
return GST_FLOW_EOS; return GST_FLOW_EOS;
...@@ -1371,6 +1383,40 @@ no_buffer: ...@@ -1371,6 +1383,40 @@ no_buffer:
} }
} }
static GstFlowReturn
gst_v4l2_buffer_pool_dequeue (GstV4l2BufferPool * pool, GstBuffer ** buffer,
gboolean wait)
{
GstFlowReturn res;
GstV4l2Object *obj = pool->obj;
if ((res = gst_v4l2_buffer_pool_poll (pool, wait)) < GST_FLOW_OK)
goto poll_failed;
if (res == GST_FLOW_CUSTOM_SUCCESS) {
GST_LOG_OBJECT (pool, "nothing to dequeue");
goto done;
}
GST_LOG_OBJECT (pool, "dequeueing a buffer");
if (obj->can_wait_event && gst_poll_fd_has_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;
}
done:
return res;
}
static GstFlowReturn static GstFlowReturn
gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
GstBufferPoolAcquireParams * params) GstBufferPoolAcquireParams * params)
...@@ -1407,7 +1453,7 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, ...@@ -1407,7 +1453,7 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
/* just dequeue a buffer, we basically use the queue of v4l2 as the /* 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 * storage for our buffers. This function does poll first so we can
* interrupt it fine. */ * interrupt it fine. */
ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer, TRUE); ret = gst_v4l2_buffer_pool_dequeue (pool, buffer, TRUE);
break; break;
} }
default: default:
...@@ -1680,8 +1726,10 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps) ...@@ -1680,8 +1726,10 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps)
gst_poll_add_fd (pool->poll, &pool->pollfd); gst_poll_add_fd (pool->poll, &pool->pollfd);
if (V4L2_TYPE_IS_OUTPUT (obj->type)) if (V4L2_TYPE_IS_OUTPUT (obj->type))
gst_poll_fd_ctl_write (pool->poll, &pool->pollfd, TRUE); gst_poll_fd_ctl_write (pool->poll, &pool->pollfd, TRUE);
else else {
gst_poll_fd_ctl_read (pool->poll, &pool->pollfd, TRUE); gst_poll_fd_ctl_read (pool->poll, &pool->pollfd, TRUE);
gst_poll_fd_ctl_pri (pool->poll, &pool->pollfd, TRUE);
}
pool->video_fd = fd; pool->video_fd = fd;
pool->obj = obj; pool->obj = obj;
...@@ -1866,8 +1914,9 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) ...@@ -1866,8 +1914,9 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf)
} }
/* buffer not from our pool, grab a frame and copy it into the target */ /* buffer not from our pool, grab a frame and copy it into the target */
if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp, TRUE)) if ((ret =
!= GST_FLOW_OK) gst_v4l2_buffer_pool_dequeue (pool, &tmp,
TRUE)) != GST_FLOW_OK)
goto done; goto done;
/* An empty buffer on capture indicates the end of stream */ /* An empty buffer on capture indicates the end of stream */
...@@ -2022,7 +2071,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) ...@@ -2022,7 +2071,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf)
gst_buffer_unref (to_queue); gst_buffer_unref (to_queue);
/* release as many buffer as possible */ /* release as many buffer as possible */
while (gst_v4l2_buffer_pool_dqbuf (pool, &buffer, FALSE) == while (gst_v4l2_buffer_pool_dequeue (pool, &buffer, FALSE) ==
GST_FLOW_OK) { GST_FLOW_OK) {
if (buffer->pool == NULL) if (buffer->pool == NULL)
gst_v4l2_buffer_pool_release_buffer (bpool, buffer); gst_v4l2_buffer_pool_release_buffer (bpool, buffer);
...@@ -2031,7 +2080,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) ...@@ -2031,7 +2080,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf)
if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) { if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) {
/* all buffers are queued, try to dequeue one and release it back /* all buffers are queued, try to dequeue one and release it back
* into the pool so that _acquire can get to it again. */ * into the pool so that _acquire can get to it again. */
ret = gst_v4l2_buffer_pool_dqbuf (pool, &buffer, TRUE); ret = gst_v4l2_buffer_pool_dequeue (pool, &buffer, TRUE);
if (ret == GST_FLOW_OK && buffer->pool == NULL) if (ret == GST_FLOW_OK && buffer->pool == NULL)
/* release the rendered buffer back into the pool. This wakes up any /* release the rendered buffer back into the pool. This wakes up any
* thread waiting for a buffer in _acquire(). */ * thread waiting for a buffer in _acquire(). */
......
...@@ -42,8 +42,8 @@ G_BEGIN_DECLS ...@@ -42,8 +42,8 @@ G_BEGIN_DECLS
#define GST_V4L2_BUFFER_POOL_CAST(obj) ((GstV4l2BufferPool*)(obj)) #define GST_V4L2_BUFFER_POOL_CAST(obj) ((GstV4l2BufferPool*)(obj))
/* This flow return is used to indicated that the last buffer of a /* This flow return is used to indicated that the last buffer of a
* drain or a resoltuion change has been found. This should normally * drain has been found. This should normally only occure for
* only occure for mem-2-mem devices. */ * mem-2-mem devices. */
#define GST_V4L2_FLOW_LAST_BUFFER GST_FLOW_CUSTOM_SUCCESS #define GST_V4L2_FLOW_LAST_BUFFER GST_FLOW_CUSTOM_SUCCESS
/* This flow return is used to indicated that the returned buffer was marked /* This flow return is used to indicated that the returned buffer was marked
...@@ -51,6 +51,11 @@ G_BEGIN_DECLS ...@@ -51,6 +51,11 @@ G_BEGIN_DECLS
* simply waiting for next buffer. */ * simply waiting for next buffer. */
#define GST_V4L2_FLOW_CORRUPTED_BUFFER GST_FLOW_CUSTOM_SUCCESS_1 #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 struct _GstV4l2BufferPool
{ {
GstBufferPool parent; GstBufferPool parent;
......
...@@ -535,6 +535,10 @@ gst_v4l2_object_new (GstElement * element, ...@@ -535,6 +535,10 @@ gst_v4l2_object_new (GstElement * element,
v4l2object->munmap = munmap; v4l2object->munmap = munmap;
} }
v4l2object->poll = gst_poll_new (TRUE);
v4l2object->can_wait_event = FALSE;
v4l2object->can_poll_device = TRUE;
return v4l2object; return v4l2object;
} }
...@@ -546,6 +550,8 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object) ...@@ -546,6 +550,8 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
{ {
g_return_if_fail (v4l2object != NULL); g_return_if_fail (v4l2object != NULL);
gst_poll_free (v4l2object->poll);
g_free (v4l2object->videodev); g_free (v4l2object->videodev);
g_free (v4l2object->channel); g_free (v4l2object->channel);
...@@ -906,6 +912,15 @@ gst_v4l2_object_open_shared (GstV4l2Object * v4l2object, GstV4l2Object * other) ...@@ -906,6 +912,15 @@ gst_v4l2_object_open_shared (GstV4l2Object * v4l2object, GstV4l2Object * other)
ret = gst_v4l2_dup (v4l2object, other); 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);
gst_poll_fd_ctl_pri (v4l2object->poll, &v4l2object->pollfd, TRUE);
}
return ret; return ret;
} }
...@@ -3881,6 +3896,93 @@ gst_v4l2_object_try_format (GstV4l2Object * v4l2object, GstCaps * caps, ...@@ -3881,6 +3896,93 @@ gst_v4l2_object_try_format (GstV4l2Object * v4l2object, GstCaps * caps,
return gst_v4l2_object_set_format_full (v4l2object, caps, TRUE, error); 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;
if ((res = gst_v4l2_object_poll (v4l2object)) != GST_FLOW_OK)
goto poll_failed;
memset (&evt, 0x00, sizeof (struct v4l2_event));
if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_DQEVENT, &evt) < 0)
goto dqevent_failed;
switch (evt.type) {
case V4L2_EVENT_SOURCE_CHANGE:
return GST_V4L2_FLOW_SOURCE_CHANGE;
case V4L2_EVENT_EOS:
return GST_V4L2_FLOW_LAST_BUFFER;
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: * gst_v4l2_object_acquire_format:
* @v4l2object: the object * @v4l2object: the object
...@@ -4128,6 +4230,8 @@ gst_v4l2_object_unlock (GstV4l2Object * v4l2object) ...@@ -4128,6 +4230,8 @@ gst_v4l2_object_unlock (GstV4l2Object * v4l2object)
GST_LOG_OBJECT (v4l2object->dbg_obj, "start flushing"); 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)) if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool))
gst_buffer_pool_set_flushing (v4l2object->pool, TRUE); gst_buffer_pool_set_flushing (v4l2object->pool, TRUE);
...@@ -4144,6 +4248,8 @@ gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object) ...@@ -4144,6 +4248,8 @@ gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object)
if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool)) if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool))
gst_buffer_pool_set_flushing (v4l2object->pool, FALSE); gst_buffer_pool_set_flushing (v4l2object->pool, FALSE);
gst_poll_set_flushing (v4l2object->poll, FALSE);
return ret; return ret;
} }
......
...@@ -130,6 +130,9 @@ struct _GstV4l2Object { ...@@ -130,6 +130,9 @@ struct _GstV4l2Object {
/* the video-device's file descriptor */ /* the video-device's file descriptor */
gint video_fd; gint video_fd;
GstV4l2IOMode mode; GstV4l2IOMode mode;
GstPoll *poll; /* a poll for video_fd */
GstPollFD pollfd;
gboolean can_poll_device;
gboolean active; gboolean active;
gboolean streaming; gboolean streaming;
...@@ -216,6 +219,8 @@ struct _GstV4l2Object { ...@@ -216,6 +219,8 @@ struct _GstV4l2Object {
* on slow USB firmwares. When this is set, gst_v4l2_set_format() will modify * on slow USB firmwares. When this is set, gst_v4l2_set_format() will modify
* the caps to reflect what was negotiated during fixation */ * the caps to reflect what was negotiated during fixation */
gboolean skip_try_fmt_probes; gboolean skip_try_fmt_probes;
gboolean can_wait_event;
gboolean need_wait_event;
}; };
struct _GstV4l2ObjectClassHelper { struct _GstV4l2ObjectClassHelper {
...@@ -299,6 +304,7 @@ gboolean gst_v4l2_object_stop (GstV4l2Object * v4l2object); ...@@ -299,6 +304,7 @@ gboolean gst_v4l2_object_stop (GstV4l2Object * v4l2object);
GstCaps * gst_v4l2_object_probe_caps (GstV4l2Object * v4l2object, GstCaps * filter); GstCaps * gst_v4l2_object_probe_caps (GstV4l2Object * v4l2object, GstCaps * filter);
GstCaps * gst_v4l2_object_get_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_acquire_format (GstV4l2Object * v4l2object, GstVideoInfo * info);
gboolean gst_v4l2_object_set_crop (GstV4l2Object * obj); gboolean gst_v4l2_object_set_crop (GstV4l2Object * obj);
......
...@@ -466,18 +466,146 @@ gst_v4l2_video_dec_get_oldest_frame (GstVideoDecoder * decoder) ...@@ -466,18 +466,146 @@ gst_v4l2_video_dec_get_oldest_frame (GstVideoDecoder * decoder)
return frame; return frame;
} }
static gboolean
gst_v4l2_video_remove_padding (GstCapsFeatures * features,
GstStructure * structure, gpointer user_data)
{
GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (user_data);
GstVideoAlignment *align = &self->v4l2capture->align;
GstVideoInfo *info = &self->v4l2capture->info;
int width, height;
if (!gst_structure_get_int (structure, "width", &width))
return TRUE;
if (!gst_structure_get_int (structure, "height", &height))
return TRUE;
if (align->padding_left != 0 || align->padding_top != 0 ||
height != info->height + align->padding_bottom)
return TRUE;
if (height == info->height + align->padding_bottom) {
/* Some drivers may round up width to the padded with */
if (width == info->width + align->padding_right)
gst_structure_set (structure,
"width", G_TYPE_INT, width - align->padding_right,
"height", G_TYPE_INT, height - align->padding_bottom, NULL);
/* Some drivers may keep visible width and only round up bytesperline */
else if (width == info->width)
gst_structure_set (structure,
"height", G_TYPE_INT, height - align->padding_bottom, NULL);
}
return TRUE;
}
static void static void
gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
{ {
GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
GstV4l2BufferPool *v4l2_pool = GST_V4L2_BUFFER_POOL (self->v4l2capture->pool); GstV4l2BufferPool *v4l2_pool;
GstBufferPool *pool; GstBufferPool *pool;
GstVideoCodecFrame *frame; GstVideoCodecFrame *frame;
GstBuffer *buffer = NULL; GstBuffer *buffer = NULL;
GstFlowReturn ret; GstFlowReturn ret;
if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) {
GstV4l2Error error = GST_V4L2_ERROR_INIT;
GstVideoInfo info;
GstVideoCodecState *output_state;
GstCaps *acquired_caps, *available_caps, *caps, *filter;
GstStructure *st;
/* 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;
}
}
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
* and returns the visible size as with/height and the coded size as
* padding. */
if (!gst_v4l2_object_acquire_format (self->v4l2capture, &info))
goto not_negotiated;
/* Create caps from the acquired format, remove the format field */
acquired_caps = gst_video_info_to_caps (&info);
GST_DEBUG_OBJECT (self, "Acquired caps: %" GST_PTR_FORMAT, acquired_caps);
st = gst_caps_get_structure (acquired_caps, 0);
gst_structure_remove_field (st, "format");
/* Probe currently available pixel formats */
available_caps = gst_v4l2_object_probe_caps (self->v4l2capture, NULL);
available_caps = gst_caps_make_writable (available_caps);
GST_DEBUG_OBJECT (self, "Available caps: %" GST_PTR_FORMAT, available_caps);
/* Replace coded size with visible size, we want to negotiate visible size
* with downstream, not coded size. */
gst_caps_map_in_place (available_caps, gst_v4l2_video_remove_padding, self);
filter = gst_caps_intersect_full (available_caps, acquired_caps,
GST_CAPS_INTERSECT_FIRST);
GST_DEBUG_OBJECT (self, "Filtered caps: %" GST_PTR_FORMAT, filter);
gst_caps_unref (acquired_caps);
gst_caps_unref (available_caps);
caps = gst_pad_peer_query_caps (decoder->srcpad, filter);
gst_caps_unref (filter);
GST_DEBUG_OBJECT (self, "Possible decoded caps: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_empty (caps)) {
gst_caps_unref (caps);
goto not_negotiated;
}
/* Fixate pixel format */
caps = gst_caps_fixate (caps);
GST_DEBUG_OBJECT (self, "Chosen decoded caps: %" GST_PTR_FORMAT, caps);
/* Try to set negotiated format, on success replace acquired format */
if (gst_v4l2_object_set_format (self->v4l2capture, caps, &error))
gst_video_info_from_caps (&info, caps);
else
gst_v4l2_clear_error (&error);
gst_caps_unref (caps);
output_state = gst_video_decoder_set_output_state (decoder,
info.finfo->format, info.width, info.height, self->input_state);
/* Copy the rest of the information, there might be more in the future */
output_state->info.interlace_mode = info.interlace_mode;
gst_video_codec_state_unref (output_state);
if (!gst_video_decoder_negotiate (decoder)) {
if (GST_PAD_IS_FLUSHING (decoder->srcpad))
goto flushing;
else
goto not_negotiated;
}
/* Ensure our internal pool is activated */
if (!gst_buffer_pool_set_active (GST_BUFFER_POOL (self->v4l2capture->pool),
TRUE))
goto activate_failed;
}
GST_LOG_OBJECT (decoder, "Allocate output buffer"); GST_LOG_OBJECT (decoder, "Allocate output buffer");
v4l2_pool = GST_V4L2_BUFFER_POOL (self->v4l2capture->pool);
self->output_flow = GST_FLOW_OK; self->output_flow = GST_FLOW_OK;
do { do {
/* We cannot use the base class allotate helper since it taking the internal /* We cannot use the base class allotate helper since it taking the internal
...@@ -495,12 +623,17 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) ...@@ -495,12 +623,17 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL); ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
g_object_unref (pool); g_object_unref (pool);
if (ret == GST_V4L2_FLOW_SOURCE_CHANGE) {
GST_DEBUG_OBJECT (decoder, "Received resolution change");
gst_v4l2_object_stop (self->v4l2capture);
return;
}
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
goto beach; goto beach;
GST_LOG_OBJECT (decoder, "Process output buffer"); GST_LOG_OBJECT (decoder, "Process output buffer");
ret = gst_v4l2_buffer_pool_process (v4l2_pool, &buffer); ret = gst_v4l2_buffer_pool_process (v4l2_pool, &buffer);
} while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER); } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER);
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
...@@ -522,6 +655,27 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) ...@@ -522,6 +655,27 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
return; 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: beach:
GST_DEBUG_OBJECT (decoder, "Leaving output thread: %s", GST_DEBUG_OBJECT (decoder, "Leaving output thread: %s",
gst_flow_get_name (ret)); gst_flow_get_name (ret));
...@@ -532,46 +686,13 @@ beach: ...@@ -532,46 +686,13 @@ beach:
gst_pad_pause_task (decoder->srcpad); gst_pad_pause_task (decoder->srcpad);
} }
static gboolean
gst_v4l2_video_remove_padding (GstCapsFeatures * features,
GstStructure * structure, gpointer user_data)
{
GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (user_data);
GstVideoAlignment *align = &self->v4l2capture->align;
GstVideoInfo *info = &self->v4l2capture->info;
int width, height;
if (!gst_structure_get_int (structure, "width", &width))
return TRUE;
if (!gst_structure_get_int (structure, "height", &height))
return TRUE;
if (align->padding_left != 0 || align->padding_top != 0 ||
height != info->height + align->padding_bottom)
return TRUE;
if (height == info->height + align->padding_bottom) {
/* Some drivers may round up width to the padded with */
if (width == info->width + align->padding_right)
gst_structure_set (structure,
"width", G_TYPE_INT, width - align->padding_right,
"height", G_TYPE_INT, height - align->padding_bottom, NULL);
/* Some drivers may keep visible width and only round up bytesperline */
else if (width == info->width)
gst_structure_set (structure,
"height", G_TYPE_INT, height - align->padding_bottom, NULL);
}
return TRUE;
}
static GstFlowReturn static GstFlowReturn
gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
GstVideoCodecFrame * frame) GstVideoCodecFrame * frame)
{ {
GstV4l2Error error = GST_V4L2_ERROR_INIT; GstV4l2Error error = GST_V4L2_ERROR_INIT;
GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool);
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
gboolean processed = FALSE; gboolean processed = FALSE;
GstBuffer *tmp; GstBuffer *tmp;
...@@ -590,13 +711,8 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, ...@@ -590,13 +711,8 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
goto not_negotiated; goto not_negotiated;
} }
if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) { if (G_UNLIKELY (!gst_buffer_pool_is_active (pool))) {
GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool);
GstVideoInfo info;
GstVideoCodecState *output_state;
GstBuffer *codec_data; GstBuffer *codec_data;
GstCaps *acquired_caps, *available_caps, *caps, *filter;
GstStructure *st;