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:
}
static GstFlowReturn
gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
gboolean wait)
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;
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;
GstBuffer *outbuf = NULL;
......@@ -1213,16 +1240,6 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
gsize size;
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);
if (res == GST_FLOW_EOS)
goto eos;
......@@ -1350,11 +1367,6 @@ done:
return res;
/* ERRORS */
poll_failed:
{
GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res));
return res;
}
eos:
{
return GST_FLOW_EOS;
......@@ -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
gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
GstBufferPoolAcquireParams * params)
......@@ -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
* storage for our buffers. This function does poll first so we can
* interrupt it fine. */
ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer, TRUE);
ret = gst_v4l2_buffer_pool_dequeue (pool, buffer, TRUE);
break;
}
default:
......@@ -1680,8 +1726,10 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps)
gst_poll_add_fd (pool->poll, &pool->pollfd);
if (V4L2_TYPE_IS_OUTPUT (obj->type))
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_pri (pool->poll, &pool->pollfd, TRUE);
}
pool->video_fd = fd;
pool->obj = obj;
......@@ -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 */
if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp, TRUE))
!= GST_FLOW_OK)
if ((ret =
gst_v4l2_buffer_pool_dequeue (pool, &tmp,
TRUE)) != GST_FLOW_OK)
goto done;
/* An empty buffer on capture indicates the end of stream */
......@@ -2022,7 +2071,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf)
gst_buffer_unref (to_queue);
/* 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) {
if (buffer->pool == NULL)
gst_v4l2_buffer_pool_release_buffer (bpool, buffer);
......@@ -2031,7 +2080,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf)
if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) {
/* 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, &buffer, TRUE);
ret = gst_v4l2_buffer_pool_dequeue (pool, &buffer, TRUE);
if (ret == GST_FLOW_OK && buffer->pool == NULL)
/* release the rendered buffer back into the pool. This wakes up any
* thread waiting for a buffer in _acquire(). */
......
......@@ -42,8 +42,8 @@ G_BEGIN_DECLS
#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 @@ G_BEGIN_DECLS
* 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;
......
......@@ -535,6 +535,10 @@ gst_v4l2_object_new (GstElement * element,
v4l2object->munmap = munmap;
}
v4l2object->poll = gst_poll_new (TRUE);
v4l2object->can_wait_event = FALSE;
v4l2object->can_poll_device = TRUE;
return v4l2object;
}
......@@ -546,6 +550,8 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
{
g_return_if_fail (v4l2object != NULL);
gst_poll_free (v4l2object->poll);
g_free (v4l2object->videodev);
g_free (v4l2object->channel);
......@@ -906,6 +912,15 @@ gst_v4l2_object_open_shared (GstV4l2Object * v4l2object, GstV4l2Object * 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;
}
......@@ -3881,6 +3896,93 @@ gst_v4l2_object_try_format (GstV4l2Object * v4l2object, GstCaps * caps,
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:
* @v4l2object: the object
......@@ -4128,6 +4230,8 @@ gst_v4l2_object_unlock (GstV4l2Object * v4l2object)
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);
......@@ -4144,6 +4248,8 @@ gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object)
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;
}
......
......@@ -130,6 +130,9 @@ struct _GstV4l2Object {
/* 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;
......@@ -216,6 +219,8 @@ struct _GstV4l2Object {
* 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 {
......@@ -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_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);
......
......@@ -466,18 +466,146 @@ gst_v4l2_video_dec_get_oldest_frame (GstVideoDecoder * decoder)
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
gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
{
GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
GstV4l2BufferPool *v4l2_pool = GST_V4L2_BUFFER_POOL (self->v4l2capture->pool);
GstV4l2BufferPool *v4l2_pool;
GstBufferPool *pool;
GstVideoCodecFrame *frame;
GstBuffer *buffer = NULL;
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");
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
......@@ -495,12 +623,17 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
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)
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)
......@@ -522,6 +655,27 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
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));
......@@ -532,46 +686,13 @@ beach:
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
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;
......@@ -590,13 +711,8 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
goto not_negotiated;
}
if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) {
GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool);
GstVideoInfo info;
GstVideoCodecState *output_state;
if (G_UNLIKELY (!gst_buffer_pool_is_active (pool))) {
GstBuffer *codec_data;
GstCaps *acquired_caps, *available_caps, *caps, *filter;
GstStructure *st;
GST_DEBUG_OBJECT (self, "Sending header");
......@@ -635,72 +751,6 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
gst_buffer_unref (codec_data);
/* 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_fields (st, "format", "colorimetry", "chroma-site",
NULL);
/* Probe currently available pixel formats */
available_caps = gst_caps_copy (self->probed_srccaps);
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;
}
task_state = gst_pad_get_task_state (GST_VIDEO_DECODER_SRC_PAD (self));
......@@ -971,6 +1021,9 @@ gst_v4l2_video_dec_subinstance_init (GTypeInstance * instance, gpointer g_class)
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
......
......@@ -509,6 +509,43 @@ gst_v4l2_adjust_buf_type (GstV4l2Object * v4l2object)
}
}
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():