summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Olbrich <m.olbrich@pengutronix.de>2013-09-13 16:30:57 +0200
committerMichael Olbrich <m.olbrich@pengutronix.de>2013-09-13 16:30:57 +0200
commitb5bd0ad35223b44bdbd5dc0f80c921ff60bce9eb (patch)
tree82e7f726af222ab4b7600c00b2503622cff1fb52
parentf631080120a597aee8f8b97621593fb54145ec41 (diff)
parente49c918fa3527405cf803bebc87e757993fc88e3 (diff)
downloadgst-plugins-good-b5bd0ad35223b44bdbd5dc0f80c921ff60bce9eb.tar.gz
gst-plugins-good-b5bd0ad35223b44bdbd5dc0f80c921ff60bce9eb.tar.xz
Merge '1.0/topic/upstream'
umpf-merge-topic: 1.0/topic/upstream
-rw-r--r--configure.ac18
-rw-r--r--gst/avi/gstavimux.c2
-rw-r--r--sys/v4l2/Makefile.am6
-rw-r--r--sys/v4l2/gstv4l2bufferpool.c201
-rw-r--r--sys/v4l2/gstv4l2bufferpool.h1
-rw-r--r--sys/v4l2/gstv4l2object.c233
-rw-r--r--sys/v4l2/gstv4l2object.h14
-rw-r--r--sys/v4l2/gstv4l2sink.c4
-rw-r--r--sys/v4l2/gstv4l2src.c5
-rw-r--r--sys/v4l2/v4l2_calls.c106
-rw-r--r--sys/v4l2/v4l2_calls.h3
11 files changed, 490 insertions, 103 deletions
diff --git a/configure.ac b/configure.ac
index be036640a..3a0e5564e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -611,6 +611,24 @@ if test x$HAVE_GST_V4L2 = xyes; then
fi
fi
+# Check if v4l2 support dmabuf
+if test x$HAVE_GST_V4L2 = xyes; then
+ AC_CHECK_DECLS(V4L2_MEMORY_DMABUF,,,[
+ #include <sys/types.h>
+ #ifdef __sun /* Solaris */
+ #include <sys/types.h>
+ #include <sys/videodev2.h>
+ #elif __FreeBSD__
+ #include <linux/videodev2.h>
+ #else /* Linux */
+ #include <linux/types.h>
+ #define _LINUX_TIME_H
+ #define __user
+ #include <linux/videodev2.h>
+ #endif
+ ])
+fi
+
dnl Check for X11
translit(dnm, m, l) AM_CONDITIONAL(USE_X, true)
AG_GST_CHECK_FEATURE(X, [X libraries and plugins],
diff --git a/gst/avi/gstavimux.c b/gst/avi/gstavimux.c
index 273955598..54d74c4ca 100644
--- a/gst/avi/gstavimux.c
+++ b/gst/avi/gstavimux.c
@@ -1291,7 +1291,7 @@ gst_avi_mux_riff_get_avi_header (GstAviMux * avimux)
if (audpad->auds_codec_data) {
gst_buffer_map (audpad->auds_codec_data, &map, GST_MAP_READ);
hdl &= gst_byte_writer_put_data (&bw, map.data, map.size);
- gst_buffer_unmap (vidpad->vids_codec_data, &map);
+ gst_buffer_unmap (audpad->auds_codec_data, &map);
}
gst_avi_mux_end_chunk (&bw, strf);
}
diff --git a/sys/v4l2/Makefile.am b/sys/v4l2/Makefile.am
index efdabeb6b..3709733c6 100644
--- a/sys/v4l2/Makefile.am
+++ b/sys/v4l2/Makefile.am
@@ -8,7 +8,6 @@ xv_source =
xv_libs =
endif
-
# variables used for enum/marshal generation
glib_enum_headers = tuner.h tunernorm.h tunerchannel.h
glib_enum_define = GST_INTERFACES
@@ -56,11 +55,10 @@ libgstvideo4linux2_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \
libgstvideo4linux2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstvideo4linux2_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
-
libgstvideo4linux2_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
- $(GST_BASE_LIBS) \
- $(GST_PLUGINS_BASE_LIBS) \
+ -lgstallocators-$(GST_API_VERSION) \
-lgstvideo-$(GST_API_VERSION) \
+ $(GST_BASE_LIBS) \
$(GST_LIBS) \
$(xv_libs) \
$(LIBV4L2_LIBS) \
diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c
index c43749e0f..a156ecea5 100644
--- a/sys/v4l2/gstv4l2bufferpool.c
+++ b/sys/v4l2/gstv4l2bufferpool.c
@@ -29,10 +29,14 @@
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
+#if HAVE_DECL_V4L2_MEMORY_DMABUF
+#include <fcntl.h>
+#endif
#include "gst/video/video.h"
#include "gst/video/gstvideometa.h"
#include "gst/video/gstvideopool.h"
+#include "gst/allocators/gstdmabuf.h"
#include <gstv4l2bufferpool.h>
@@ -104,6 +108,7 @@ gst_v4l2_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer)
switch (obj->mode) {
case GST_V4L2_IO_RW:
+ case GST_V4L2_IO_DMABUF:
break;
case GST_V4L2_IO_MMAP:
{
@@ -152,7 +157,30 @@ gst_v4l2_buffer_pool_alloc_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
break;
}
case GST_V4L2_IO_MMAP:
+ case GST_V4L2_IO_DMABUF:
{
+#ifdef VIDIOC_CREATE_BUFS
+ if (pool->num_allocated == pool->num_buffers) {
+ struct v4l2_create_buffers create_bufs;
+
+ memset (&create_bufs, 0, sizeof (struct v4l2_create_buffers));
+ create_bufs.count = 1;
+ create_bufs.memory = V4L2_MEMORY_MMAP;
+ create_bufs.format.type = obj->type;
+
+ if (v4l2_ioctl (pool->video_fd, VIDIOC_G_FMT, &create_bufs.format) < 0)
+ goto g_fmt_failed;
+
+ if (v4l2_ioctl (pool->video_fd, VIDIOC_CREATE_BUFS, &create_bufs) < 0)
+ goto create_bufs_failed;
+
+ GST_LOG_OBJECT (pool, "created buffer with index: %u",
+ create_bufs.index);
+ pool->num_buffers++;
+ pool->buffers = g_renew (GstBuffer *, pool->buffers, pool->num_buffers);
+ pool->buffers[pool->num_buffers - 1] = NULL;
+ }
+#endif
newbuf = gst_buffer_new ();
meta = GST_V4L2_META_ADD (newbuf);
@@ -175,19 +203,36 @@ gst_v4l2_buffer_pool_alloc_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
GST_LOG_OBJECT (pool, " memory: %d", meta->vbuffer.memory);
if (meta->vbuffer.memory == V4L2_MEMORY_MMAP)
GST_LOG_OBJECT (pool, " MMAP offset: %u", meta->vbuffer.m.offset);
- GST_LOG_OBJECT (pool, " length: %u", meta->vbuffer.length);
-
- meta->mem = v4l2_mmap (0, meta->vbuffer.length,
- PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd,
- meta->vbuffer.m.offset);
- if (meta->mem == MAP_FAILED)
- goto mmap_failed;
-
- gst_buffer_append_memory (newbuf,
- gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE,
- meta->mem, meta->vbuffer.length, 0, meta->vbuffer.length, NULL,
- NULL));
+ if (obj->mode == GST_V4L2_IO_MMAP) {
+ meta->mem = v4l2_mmap (0, meta->vbuffer.length,
+ PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd,
+ meta->vbuffer.m.offset);
+ if (meta->mem == MAP_FAILED)
+ goto mmap_failed;
+
+ gst_buffer_append_memory (newbuf,
+ gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE,
+ meta->mem, meta->vbuffer.length, 0, meta->vbuffer.length, NULL,
+ NULL));
+ }
+#if HAVE_DECL_V4L2_MEMORY_DMABUF
+ if (obj->mode == GST_V4L2_IO_DMABUF) {
+ struct v4l2_exportbuffer expbuf;
+
+ memset (&expbuf, 0, sizeof (struct v4l2_exportbuffer));
+ expbuf.type = meta->vbuffer.type;
+ expbuf.index = meta->vbuffer.index;
+ expbuf.flags = O_CLOEXEC;
+ if (v4l2_ioctl (pool->video_fd, VIDIOC_EXPBUF, &expbuf) < 0)
+ goto expbuf_failed;
+
+ meta->vbuffer.memory = V4L2_MEMORY_DMABUF;
+ gst_buffer_append_memory (newbuf,
+ gst_dmabuf_allocator_alloc (pool->allocator, expbuf.fd,
+ meta->vbuffer.length));
+ }
+#endif
/* add metadata to raw video buffers */
if (pool->add_videometa && info->finfo) {
const GstVideoFormatInfo *finfo = info->finfo;
@@ -229,6 +274,24 @@ gst_v4l2_buffer_pool_alloc_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
return GST_FLOW_OK;
/* ERRORS */
+#ifdef VIDIOC_CREATE_BUFS
+g_fmt_failed:
+ {
+ gint errnosave = errno;
+
+ GST_WARNING ("Failed G_FMT: %s", g_strerror (errnosave));
+ errno = errnosave;
+ return GST_FLOW_ERROR;
+ }
+create_bufs_failed:
+ {
+ gint errnosave = errno;
+
+ GST_WARNING ("Failed CREATE_BUFS: %s", g_strerror (errnosave));
+ errno = errnosave;
+ return GST_FLOW_ERROR;
+ }
+#endif
querybuf_failed:
{
gint errnosave = errno;
@@ -247,6 +310,17 @@ mmap_failed:
errno = errnosave;
return GST_FLOW_ERROR;
}
+#if HAVE_DECL_V4L2_MEMORY_DMABUF
+expbuf_failed:
+ {
+ gint errnosave = errno;
+
+ GST_WARNING ("Failed EXPBUF: %s", g_strerror (errnosave));
+ gst_buffer_unref (newbuf);
+ errno = errnosave;
+ return GST_FLOW_ERROR;
+ }
+#endif
}
static gboolean
@@ -298,6 +372,7 @@ gst_v4l2_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
num_buffers = 1;
copy_threshold = 0;
break;
+ case GST_V4L2_IO_DMABUF:
case GST_V4L2_IO_MMAP:
{
/* request a reasonable number of buffers when no max specified. We will
@@ -357,6 +432,10 @@ gst_v4l2_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
pool->size = size;
pool->num_buffers = num_buffers;
pool->copy_threshold = copy_threshold;
+
+ if (obj->mode == GST_V4L2_IO_DMABUF)
+ allocator = gst_dmabuf_allocator_obtain ();
+
if (pool->allocator)
gst_object_unref (pool->allocator);
if ((pool->allocator = allocator))
@@ -406,6 +485,7 @@ start_streaming (GstV4l2BufferPool * pool)
break;
case GST_V4L2_IO_MMAP:
case GST_V4L2_IO_USERPTR:
+ case GST_V4L2_IO_DMABUF:
GST_DEBUG_OBJECT (pool, "STREAMON");
if (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0)
goto start_failed;
@@ -460,6 +540,23 @@ start_failed:
}
}
+static void
+gst_v4l2_buffer_pool_free_buffers (GstV4l2BufferPool * pool)
+{
+ if (pool->num_buffers > 0) {
+ struct v4l2_requestbuffers breq;
+ memset (&breq, 0, sizeof (struct v4l2_requestbuffers));
+ breq.type = pool->obj->type;
+ breq.count = 0;
+ breq.memory = V4L2_MEMORY_MMAP;
+ if (v4l2_ioctl (pool->video_fd, VIDIOC_REQBUFS, &breq) < 0) {
+ GST_ERROR_OBJECT (pool, "error releasing buffers: %s",
+ g_strerror (errno));
+ }
+ pool->num_buffers = 0;
+ }
+}
+
static gboolean
gst_v4l2_buffer_pool_stop (GstBufferPool * bpool)
{
@@ -478,6 +575,7 @@ gst_v4l2_buffer_pool_stop (GstBufferPool * bpool)
break;
case GST_V4L2_IO_MMAP:
case GST_V4L2_IO_USERPTR:
+ case GST_V4L2_IO_DMABUF:
/* we actually need to sync on all queued buffers but not
* on the non-queued ones */
GST_DEBUG_OBJECT (pool, "STREAMOFF");
@@ -503,6 +601,8 @@ gst_v4l2_buffer_pool_stop (GstBufferPool * bpool)
g_free (pool->buffers);
pool->buffers = NULL;
+ gst_v4l2_buffer_pool_free_buffers (pool);
+
return ret;
/* ERRORS */
@@ -566,9 +666,12 @@ gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf)
}
index = meta->vbuffer.index;
+ meta->vbuffer.bytesused = gst_buffer_get_size (buf);
- GST_LOG_OBJECT (pool, "enqueue buffer %p, index:%d, queued:%d, flags:%08x",
- buf, index, pool->num_queued, meta->vbuffer.flags);
+ GST_LOG_OBJECT (pool,
+ "enqueue buffer %p, index:%d, queued:%d, flags:%08x used:%d",
+ buf, index, pool->num_queued, meta->vbuffer.flags,
+ meta->vbuffer.bytesused);
if (pool->buffers[index] != NULL)
goto already_queued;
@@ -605,17 +708,17 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer)
GstV4l2Object *obj = pool->obj;
GstClockTime timestamp;
- if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- /* select works for input devices when data is available. According to the
- * specs we can also poll to find out when a frame has been displayed but
- * that just seems to lock up here */
- if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK)
- goto poll_error;
- }
+ if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK)
+ goto poll_error;
memset (&vbuffer, 0x00, sizeof (vbuffer));
vbuffer.type = obj->type;
- vbuffer.memory = V4L2_MEMORY_MMAP;
+#if HAVE_DECL_V4L2_MEMORY_DMABUF
+ if (obj->mode == GST_V4L2_IO_DMABUF)
+ vbuffer.memory = V4L2_MEMORY_DMABUF;
+ else
+#endif
+ vbuffer.memory = V4L2_MEMORY_MMAP;
GST_LOG_OBJECT (pool, "doing DQBUF");
if (v4l2_ioctl (pool->video_fd, VIDIOC_DQBUF, &vbuffer) < 0)
@@ -651,8 +754,6 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer)
/* this can change at every frame, esp. with jpeg */
if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
gst_buffer_resize (outbuf, 0, vbuffer.bytesused);
- else
- gst_buffer_resize (outbuf, 0, vbuffer.length);
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
@@ -744,7 +845,7 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
buffer, params);
break;
-
+ case GST_V4L2_IO_DMABUF:
case GST_V4L2_IO_MMAP:
/* 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
@@ -756,6 +857,17 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
/* start copying buffers when we are running low on buffers */
if (pool->num_queued < pool->copy_threshold) {
GstBuffer *copy;
+#ifdef VIDIOC_CREATE_BUFS
+ if (pool->can_alloc) {
+ if (GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
+ &copy, params) == GST_FLOW_OK) {
+ gst_v4l2_buffer_pool_release_buffer (bpool, copy);
+ break;
+ } else {
+ pool->can_alloc = FALSE;
+ }
+ }
+#endif
/* copy the memory */
copy = gst_buffer_copy (*buffer);
@@ -832,6 +944,7 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, buffer);
break;
+ case GST_V4L2_IO_DMABUF:
case GST_V4L2_IO_MMAP:
/* queue back in the device */
gst_v4l2_buffer_pool_qbuf (pool, buffer);
@@ -854,12 +967,20 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
case GST_V4L2_IO_MMAP:
{
GstV4l2Meta *meta;
+ guint index;
meta = GST_V4L2_META_GET (buffer);
g_assert (meta != NULL);
- if (pool->buffers[meta->vbuffer.index] == NULL) {
- GST_LOG_OBJECT (pool, "buffer not queued, putting on free list");
+ index = meta->vbuffer.index;
+
+ if (pool->buffers[index] == NULL) {
+ GST_LOG_OBJECT (pool, "buffer %u not queued, putting on free list",
+ index);
+
+ /* reset to the full length, in case it was changed */
+ gst_buffer_resize (buffer, 0, meta->vbuffer.length);
+
/* playback, put the buffer back in the queue to refill later. */
GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool,
buffer);
@@ -867,7 +988,7 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
/* the buffer is queued in the device but maybe not played yet. We just
* leave it there and not make it available for future calls to acquire
* for now. The buffer will be dequeued and reused later. */
- GST_LOG_OBJECT (pool, "buffer is queued");
+ GST_LOG_OBJECT (pool, "buffer %u is queued", index);
}
break;
}
@@ -890,12 +1011,16 @@ gst_v4l2_buffer_pool_finalize (GObject * object)
{
GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (object);
+ gst_v4l2_buffer_pool_free_buffers (pool);
+
if (pool->video_fd >= 0)
v4l2_close (pool->video_fd);
if (pool->allocator)
gst_object_unref (pool->allocator);
g_free (pool->buffers);
+ gst_object_unref (pool->obj->element);
+
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -943,11 +1068,14 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps)
pool = (GstV4l2BufferPool *) g_object_new (GST_TYPE_V4L2_BUFFER_POOL, NULL);
pool->video_fd = fd;
pool->obj = obj;
+ pool->can_alloc = TRUE;
s = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
gst_buffer_pool_config_set_params (s, caps, obj->sizeimage, 2, 0);
gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pool), s);
+ gst_object_ref (obj->element);
+
return GST_BUFFER_POOL (pool);
/* ERRORS */
@@ -1084,14 +1212,14 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer * buf)
/* FIXME, do write() */
GST_WARNING_OBJECT (pool, "implement write()");
break;
-
+ case GST_V4L2_IO_DMABUF:
case GST_V4L2_IO_MMAP:
{
GstBuffer *to_queue;
if (buf->pool == bpool) {
/* nothing, we can queue directly */
- to_queue = buf;
+ to_queue = gst_buffer_ref (buf);
GST_LOG_OBJECT (pool, "processing buffer from our pool");
} else {
GST_LOG_OBJECT (pool, "alloc buffer from our pool");
@@ -1113,8 +1241,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer * buf)
/* this can block if all buffers are outstanding which would be
* strange because we would expect the upstream element to have
* allocated them and returned to us.. */
- ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
- &to_queue, NULL);
+ ret = gst_buffer_pool_acquire_buffer (bpool, &to_queue, NULL);
if (ret != GST_FLOW_OK)
goto acquire_failed;
@@ -1133,16 +1260,20 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer * buf)
goto start_failed;
if (pool->num_queued == pool->num_allocated) {
+ 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, &to_queue);
+ ret = gst_v4l2_buffer_pool_dqbuf (pool, &out);
if (ret != GST_FLOW_OK)
goto done;
/* release the rendered buffer back into the pool. This wakes up any
- * thread waiting for a buffer in _acquire() */
- gst_v4l2_buffer_pool_release_buffer (bpool, to_queue);
+ * thread waiting for a buffer in _acquire(). If the buffer still has
+ * a pool then this will happen when the refcount reaches 0 */
+ if (!out->pool)
+ gst_v4l2_buffer_pool_release_buffer (bpool, out);
}
+ gst_buffer_unref (to_queue);
break;
}
diff --git a/sys/v4l2/gstv4l2bufferpool.h b/sys/v4l2/gstv4l2bufferpool.h
index a9b022e9a..64e185227 100644
--- a/sys/v4l2/gstv4l2bufferpool.h
+++ b/sys/v4l2/gstv4l2bufferpool.h
@@ -55,6 +55,7 @@ struct _GstV4l2BufferPool
GstAllocationParams params;
guint size;
gboolean add_videometa;
+ gboolean can_alloc; /* if extra buffers can be allocated */
guint num_buffers; /* number of buffers we use */
guint num_allocated; /* number of buffers allocated by the driver */
diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c
index 843f3b425..f3985d40c 100644
--- a/sys/v4l2/gstv4l2object.c
+++ b/sys/v4l2/gstv4l2object.c
@@ -389,6 +389,7 @@ gst_v4l2_io_mode_get_type (void)
{GST_V4L2_IO_RW, "GST_V4L2_IO_RW", "rw"},
{GST_V4L2_IO_MMAP, "GST_V4L2_IO_MMAP", "mmap"},
{GST_V4L2_IO_USERPTR, "GST_V4L2_IO_USERPTR", "userptr"},
+ {GST_V4L2_IO_DMABUF, "GST_V4L2_IO_DMABUF", "dmabuf"},
{0, NULL, NULL}
};
@@ -489,6 +490,47 @@ gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class,
"I/O mode",
GST_TYPE_V4L2_IO_MODE, DEFAULT_PROP_IO_MODE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstV4l2Src:extra-controls
+ *
+ * Additional v4l2 controls for the device. The controls are identified
+ * by the control name (lowercase with '_' for any non-alphanumeric
+ * characters).
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property (gobject_class, PROP_EXTRA_CONTROLS,
+ g_param_spec_boxed ("extra-controls", "Extra Controls",
+ "Extra v4l2 controls (CIDs) for the device",
+ GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstV4l2Src:pixel-aspect-ratio
+ *
+ * The pixel aspect ratio of the device. This overwrites the pixel aspect
+ * ratio queried from the device.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
+ g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
+ "Overwrite the pixel aspect ratio of the device", "1/1",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstV4l2Src:force-aspect-ratio
+ *
+ * When enabled, the pixel aspect ratio queried from the device or set
+ * with the pixel-aspect-ratio property will be enforced.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
+ g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
+ "When enabled, the pixel aspect ratio will be enforced", TRUE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
}
GstV4l2Object *
@@ -525,6 +567,8 @@ gst_v4l2_object_new (GstElement * element,
v4l2object->xwindow_id = 0;
+ v4l2object->keep_aspect = TRUE;
+
return v4l2object;
}
@@ -653,6 +697,32 @@ gst_v4l2_object_set_property_helper (GstV4l2Object * v4l2object,
case PROP_IO_MODE:
v4l2object->req_mode = g_value_get_enum (value);
break;
+ case PROP_EXTRA_CONTROLS:{
+ const GstStructure *s = gst_value_get_structure (value);
+
+ if (v4l2object->extra_controls)
+ gst_structure_free (v4l2object->extra_controls);
+
+ v4l2object->extra_controls = s ? gst_structure_copy (s) : NULL;
+ if (GST_V4L2_IS_OPEN (v4l2object))
+ gst_v4l2_set_controls (v4l2object, v4l2object->extra_controls);
+ break;
+ }
+ case PROP_PIXEL_ASPECT_RATIO:
+ g_free (v4l2object->par);
+ v4l2object->par = g_new0 (GValue, 1);
+ g_value_init (v4l2object->par, GST_TYPE_FRACTION);
+ if (!g_value_transform (value, v4l2object->par)) {
+ g_warning ("Could not transform string to aspect ratio");
+ gst_value_set_fraction (v4l2object->par, 1, 1);
+ }
+ GST_DEBUG_OBJECT (v4l2object->element, "set PAR to %d/%d",
+ gst_value_get_fraction_numerator (v4l2object->par),
+ gst_value_get_fraction_denominator (v4l2object->par));
+ break;
+ case PROP_FORCE_ASPECT_RATIO:
+ v4l2object->keep_aspect = g_value_get_boolean (value);
+ break;
default:
return FALSE;
break;
@@ -729,6 +799,16 @@ gst_v4l2_object_get_property_helper (GstV4l2Object * v4l2object,
case PROP_IO_MODE:
g_value_set_enum (value, v4l2object->req_mode);
break;
+ case PROP_EXTRA_CONTROLS:
+ gst_value_set_structure (value, v4l2object->extra_controls);
+ break;
+ case PROP_PIXEL_ASPECT_RATIO:
+ if (v4l2object->par)
+ g_value_transform (v4l2object->par, value);
+ break;
+ case PROP_FORCE_ASPECT_RATIO:
+ g_value_set_boolean (value, v4l2object->keep_aspect);
+ break;
default:
return FALSE;
break;
@@ -879,7 +959,13 @@ static const GstV4L2FormatDesc gst_v4l2_formats[] = {
#endif
{V4L2_PIX_FMT_DV, TRUE},
{V4L2_PIX_FMT_MPEG, FALSE},
+#ifdef V4L2_PIX_FMT_MPEG4
+ {V4L2_PIX_FMT_MPEG4, TRUE},
+#endif
+#ifdef V4L2_PIX_FMT_H263
+ {V4L2_PIX_FMT_H263, TRUE},
+#endif
#ifdef V4L2_PIX_FMT_H264
{V4L2_PIX_FMT_H264, TRUE},
#endif
@@ -1203,6 +1289,19 @@ gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc)
case V4L2_PIX_FMT_HI240: /* 8 8-bit color */
/* FIXME: get correct fourccs here */
break;
+#ifdef V4L2_PIX_FMT_MPEG4
+ case V4L2_PIX_FMT_MPEG4:
+ structure = gst_structure_new ("video/mpeg",
+ "mpegversion", G_TYPE_INT, 4, "systemstream",
+ G_TYPE_BOOLEAN, FALSE, NULL);
+ break;
+#endif
+#ifdef V4L2_PIX_FMT_H263
+ case V4L2_PIX_FMT_H263:
+ structure = gst_structure_new ("video/x-h263",
+ "variant", G_TYPE_STRING, "itu", NULL);
+ break;
+#endif
#ifdef V4L2_PIX_FMT_H264
case V4L2_PIX_FMT_H264: /* H.264 */
structure = gst_structure_new_empty ("video/x-h264");
@@ -1403,11 +1502,10 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps,
mimetype = gst_structure_get_name (structure);
- if (g_str_equal (mimetype, "video/x-raw")) {
- /* raw caps, parse into video info */
- if (!gst_video_info_from_caps (info, caps))
- goto invalid_format;
+ if (!gst_video_info_from_caps (info, caps))
+ goto invalid_format;
+ if (g_str_equal (mimetype, "video/x-raw")) {
switch (GST_VIDEO_INFO_FORMAT (info)) {
case GST_VIDEO_FORMAT_I420:
fourcc = V4L2_PIX_FMT_YUV420;
@@ -1469,18 +1567,20 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps,
break;
}
} else {
- gboolean dimensions = TRUE;
-
- /* no video caps, construct videoinfo ourselves */
- gst_video_info_init (info);
-
if (g_str_equal (mimetype, "video/mpegts")) {
fourcc = V4L2_PIX_FMT_MPEG;
- dimensions = FALSE;
} else if (g_str_equal (mimetype, "video/x-dv")) {
fourcc = V4L2_PIX_FMT_DV;
} else if (g_str_equal (mimetype, "image/jpeg")) {
fourcc = V4L2_PIX_FMT_JPEG;
+#ifdef V4L2_PIX_FMT_MPEG4
+ } else if (g_str_equal (mimetype, "video/mpeg")) {
+ fourcc = V4L2_PIX_FMT_MPEG4;
+#endif
+#ifdef V4L2_PIX_FMT_H263
+ } else if (g_str_equal (mimetype, "video/x-h263")) {
+ fourcc = V4L2_PIX_FMT_H263;
+#endif
#ifdef V4L2_PIX_FMT_H264
} else if (g_str_equal (mimetype, "video/x-h264")) {
fourcc = V4L2_PIX_FMT_H264;
@@ -1502,26 +1602,6 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps,
fourcc = V4L2_PIX_FMT_PWC2;
}
#endif
-
- if (dimensions) {
- const gchar *interlace_mode;
-
- if (!gst_structure_get_int (structure, "width", &info->width))
- goto no_width;
-
- if (!gst_structure_get_int (structure, "height", &info->height))
- goto no_height;
-
- interlace_mode = gst_structure_get_string (structure, "interlace-mode");
- if (g_str_equal (interlace_mode, "progressive")) {
- info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
- } else {
- info->interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
- }
- if (!gst_structure_get_fraction (structure, "framerate", &info->fps_n,
- &info->fps_d))
- goto no_framerate;
- }
}
if (fourcc == 0)
@@ -1536,21 +1616,6 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps,
return TRUE;
/* ERRORS */
-no_width:
- {
- GST_DEBUG_OBJECT (v4l2object, "no width");
- return FALSE;
- }
-no_height:
- {
- GST_DEBUG_OBJECT (v4l2object, "no height");
- return FALSE;
- }
-no_framerate:
- {
- GST_DEBUG_OBJECT (v4l2object, "no framerate");
- return FALSE;
- }
invalid_format:
{
GST_DEBUG_OBJECT (v4l2object, "invalid format");
@@ -1573,6 +1638,43 @@ static gboolean
gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object,
guint32 pixelformat, gint * width, gint * height, gboolean * interlaced);
+static void
+gst_v4l2_object_add_aspect_ratio (GstV4l2Object * v4l2object, GstStructure * s)
+{
+ struct v4l2_cropcap cropcap;
+ int num = 1, den = 1;
+
+ if (!v4l2object->keep_aspect)
+ return;
+
+ if (v4l2object->par) {
+ num = gst_value_get_fraction_numerator (v4l2object->par);
+ den = gst_value_get_fraction_denominator (v4l2object->par);
+ goto done;
+ }
+
+ memset (&cropcap, 0, sizeof (cropcap));
+
+ cropcap.type = v4l2object->type;
+ if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_CROPCAP, &cropcap) < 0)
+ goto cropcap_failed;
+
+ num = cropcap.pixelaspect.numerator;
+ den = cropcap.pixelaspect.denominator;
+
+done:
+ gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, num, den,
+ NULL);
+ return;
+
+cropcap_failed:
+ if (errno != ENOTTY)
+ GST_WARNING_OBJECT (v4l2object->element,
+ "Failed to probe pixel aspect ratio with VIDIOC_CROPCAP: %s",
+ g_strerror (errno));
+ goto done;
+}
+
/* The frame interval enumeration code first appeared in Linux 2.6.19. */
#ifdef VIDIOC_ENUM_FRAMEINTERVALS
@@ -1759,9 +1861,11 @@ gst_v4l2_object_probe_caps_for_format_and_size (GstV4l2Object * v4l2object,
return_data:
s = gst_structure_copy (template);
gst_structure_set (s, "width", G_TYPE_INT, (gint) width,
- "height", G_TYPE_INT, (gint) height,
- "interlace-mode", G_TYPE_STRING, (interlaced ? "mixed" : "progressive"),
- "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
+ "height", G_TYPE_INT, (gint) height, NULL);
+ gst_v4l2_object_add_aspect_ratio (v4l2object, s);
+ if (g_str_equal (gst_structure_get_name (s), "video/x-raw"))
+ gst_structure_set (s, "interlace-mode", G_TYPE_STRING,
+ (interlaced ? "mixed" : "progressive"), NULL);
if (G_IS_VALUE (&rates)) {
/* only change the framerate on the template when we have a valid probed new
@@ -2021,10 +2125,10 @@ default_frame_sizes:
else
gst_structure_set (tmp, "height", GST_TYPE_INT_RANGE, min_h, max_h, NULL);
- gst_structure_set (tmp, "interlace-mode", G_TYPE_STRING,
- (interlaced ? "mixed" : "progressive"), NULL);
- gst_structure_set (tmp, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
- NULL);
+ if (g_str_equal (gst_structure_get_name (tmp), "video/x-raw"))
+ gst_structure_set (tmp, "interlace-mode", G_TYPE_STRING,
+ (interlaced ? "mixed" : "progressive"), NULL);
+ gst_v4l2_object_add_aspect_ratio (v4l2object, tmp);
gst_caps_append_structure (ret, tmp);
@@ -2439,7 +2543,7 @@ invalid_pixelformat:
get_parm_failed:
{
/* it's possible that this call is not supported */
- if (errno != EINVAL) {
+ if (errno != EINVAL && errno != ENOTTY) {
GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
(_("Could not get parameters on device '%s'"),
v4l2object->videodev), GST_ERROR_SYSTEM);
@@ -2462,6 +2566,21 @@ pool_failed:
}
gboolean
+gst_v4l2_object_caps_equal (GstV4l2Object * v4l2object, GstCaps * caps)
+{
+ GstStructure *s;
+ GstCaps *oldcaps;
+
+ if (!v4l2object->pool)
+ return FALSE;
+
+ s = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (v4l2object->pool));
+ gst_buffer_pool_config_get_params (s, &oldcaps, NULL, NULL, NULL);
+
+ return oldcaps && gst_caps_is_equal (caps, oldcaps);
+}
+
+gboolean
gst_v4l2_object_unlock (GstV4l2Object * v4l2object)
{
GST_LOG_OBJECT (v4l2object->element, "flush poll");
@@ -2506,7 +2625,10 @@ gboolean
gst_v4l2_object_copy (GstV4l2Object * v4l2object, GstBuffer * dest,
GstBuffer * src)
{
- if (v4l2object->info.finfo) {
+ const GstVideoFormatInfo *finfo = v4l2object->info.finfo;
+
+ if (finfo && (finfo->format != GST_VIDEO_FORMAT_UNKNOWN &&
+ finfo->format != GST_VIDEO_FORMAT_ENCODED)) {
GstVideoFrame src_frame, dest_frame;
GST_DEBUG_OBJECT (v4l2object->element, "copy video frame");
@@ -2530,8 +2652,9 @@ gst_v4l2_object_copy (GstV4l2Object * v4l2object, GstBuffer * dest,
GST_DEBUG_OBJECT (v4l2object->element, "copy raw bytes");
gst_buffer_map (src, &map, GST_MAP_READ);
- gst_buffer_fill (dest, 0, map.data, map.size);
+ gst_buffer_fill (dest, 0, map.data, gst_buffer_get_size (src));
gst_buffer_unmap (src, &map);
+ gst_buffer_resize (dest, 0, gst_buffer_get_size (src));
}
GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2object->element,
"slow copy into buffer %p", dest);
diff --git a/sys/v4l2/gstv4l2object.h b/sys/v4l2/gstv4l2object.h
index 9b3c5d270..2505f4796 100644
--- a/sys/v4l2/gstv4l2object.h
+++ b/sys/v4l2/gstv4l2object.h
@@ -75,7 +75,8 @@ typedef enum {
GST_V4L2_IO_AUTO = 0,
GST_V4L2_IO_RW = 1,
GST_V4L2_IO_MMAP = 2,
- GST_V4L2_IO_USERPTR = 3
+ GST_V4L2_IO_USERPTR = 3,
+ GST_V4L2_IO_DMABUF = 4
} GstV4l2IOMode;
typedef gboolean (*GstV4l2GetInOutFunction) (GstV4l2Object * v4l2object, gint * input);
@@ -142,11 +143,15 @@ struct _GstV4l2Object {
GList *colors;
GList *norms;
GList *channels;
+ GData *controls;
/* properties */
v4l2_std_id tv_norm;
gchar *channel;
gulong frequency;
+ GstStructure *extra_controls;
+ gboolean keep_aspect;
+ GValue *par;
/* X-overlay */
GstV4l2Xv *xv;
@@ -175,7 +180,10 @@ GType gst_v4l2_object_get_type (void);
PROP_SATURATION, \
PROP_HUE, \
PROP_TV_NORM, \
- PROP_IO_MODE
+ PROP_IO_MODE, \
+ PROP_EXTRA_CONTROLS, \
+ PROP_PIXEL_ASPECT_RATIO, \
+ PROP_FORCE_ASPECT_RATIO
/* create/destroy */
GstV4l2Object * gst_v4l2_object_new (GstElement * element,
@@ -226,6 +234,8 @@ GstStructure* gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc);
gboolean gst_v4l2_object_set_format (GstV4l2Object *v4l2object, GstCaps * caps);
+gboolean gst_v4l2_object_caps_equal (GstV4l2Object * v4l2object, GstCaps * caps);
+
gboolean gst_v4l2_object_unlock (GstV4l2Object *v4l2object);
gboolean gst_v4l2_object_unlock_stop (GstV4l2Object *v4l2object);
diff --git a/sys/v4l2/gstv4l2sink.c b/sys/v4l2/gstv4l2sink.c
index 115f0578e..dbc41a7ca 100644
--- a/sys/v4l2/gstv4l2sink.c
+++ b/sys/v4l2/gstv4l2sink.c
@@ -574,6 +574,10 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
return FALSE;
}
+ /* make sure the caps changed before doing anything */
+ if (gst_v4l2_object_caps_equal (obj, caps))
+ return TRUE;
+
if (!gst_v4l2_object_stop (obj))
goto stop_failed;
diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c
index d9349ac4d..cbd673a47 100644
--- a/sys/v4l2/gstv4l2src.c
+++ b/sys/v4l2/gstv4l2src.c
@@ -498,6 +498,10 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps)
v4l2src = GST_V4L2SRC (src);
obj = v4l2src->v4l2object;
+ /* make sure the caps changed before doing anything */
+ if (gst_v4l2_object_caps_equal (obj, caps))
+ return TRUE;
+
/* make sure we stop capturing and dealloc buffers */
if (!gst_v4l2_object_stop (obj))
return FALSE;
@@ -563,6 +567,7 @@ gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query)
break;
case GST_V4L2_IO_MMAP:
case GST_V4L2_IO_USERPTR:
+ case GST_V4L2_IO_DMABUF:
/* in streaming mode, prefer our own pool */
pool = GST_BUFFER_POOL_CAST (obj->pool);
size = obj->sizeimage;
diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c
index 7c2702469..4ace236ac 100644
--- a/sys/v4l2/v4l2_calls.c
+++ b/sys/v4l2/v4l2_calls.c
@@ -109,7 +109,8 @@ cap_failed:
static gboolean
gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
{
- gint n;
+ gint n, next;
+ struct v4l2_queryctrl control = { 0, };
GstElement *e;
@@ -203,7 +204,7 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
standard.index = n;
if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
- if (errno == EINVAL || errno == ENOTTY)
+ if (errno == EINVAL || errno == ENOTTY || errno == ENODATA)
break; /* end of enumeration */
else {
GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
@@ -236,11 +237,21 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
GST_DEBUG_OBJECT (e, " controls+menus");
/* and lastly, controls+menus (if appropriate) */
- for (n = V4L2_CID_BASE;; n++) {
- struct v4l2_queryctrl control = { 0, };
+#ifdef V4L2_CTRL_FLAG_NEXT_CTRL
+ next = V4L2_CTRL_FLAG_NEXT_CTRL;
+ n = 0;
+#else
+ next = 0;
+ n = V4L2_CID_BASE;
+#endif
+ control.id = next;
+ while (TRUE) {
GstV4l2ColorBalanceChannel *v4l2channel;
GstColorBalanceChannel *channel;
+ if (!next)
+ n++;
+
/* when we reached the last official CID, continue with private CIDs */
if (n == V4L2_CID_LASTP1) {
GST_DEBUG_OBJECT (e, "checking private CIDs");
@@ -248,8 +259,19 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
}
GST_DEBUG_OBJECT (e, "checking control %08x", n);
- control.id = n;
+ control.id = n | next;
if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCTRL, &control) < 0) {
+ if (next) {
+ if (n > 0) {
+ GST_DEBUG_OBJECT (e, "controls finished");
+ break;
+ } else {
+ GST_DEBUG_OBJECT (e, "V4L2_CTRL_FLAG_NEXT_CTRL not supported.");
+ next = 0;
+ n = V4L2_CID_BASE;
+ continue;
+ }
+ }
if (errno == EINVAL || errno == ENOTTY || errno == EIO || errno == ENOENT) {
if (n < V4L2_CID_PRIVATE_BASE) {
GST_DEBUG_OBJECT (e, "skipping control %08x", n);
@@ -265,10 +287,47 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
continue;
}
}
+ n = control.id;
if (control.flags & V4L2_CTRL_FLAG_DISABLED) {
GST_DEBUG_OBJECT (e, "skipping disabled control");
continue;
}
+#ifdef V4L2_CTRL_TYPE_CTRL_CLASS
+ if (control.type == V4L2_CTRL_TYPE_CTRL_CLASS) {
+ GST_DEBUG_OBJECT (e, "starting control class '%s'", control.name);
+ continue;
+ }
+#endif
+ switch (control.type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+#ifdef V4L2_CTRL_TYPE_INTEGER_MENU
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
+#endif
+#ifdef V4L2_CTRL_TYPE_BITMASK
+ case V4L2_CTRL_TYPE_BITMASK:
+#endif
+ case V4L2_CTRL_TYPE_BUTTON:{
+ int i;
+ control.name[31] = '\0';
+ for (i = 0; control.name[i]; ++i) {
+ control.name[i] = g_ascii_tolower (control.name[i]);
+ if (!g_ascii_isalnum (control.name[i]))
+ control.name[i] = '_';
+ }
+ GST_INFO_OBJECT (e, "adding generic controls '%s'", control.name);
+ g_datalist_id_set_data (&v4l2object->controls,
+ g_quark_from_string ((const gchar *) control.name),
+ GINT_TO_POINTER (n));
+ break;
+ }
+ default:
+ GST_DEBUG_OBJECT (e,
+ "Control type for '%s' not suppored for extra controls.",
+ control.name);
+ break;
+ }
switch (n) {
case V4L2_CID_BRIGHTNESS:
@@ -406,6 +465,8 @@ gst_v4l2_empty_lists (GstV4l2Object * v4l2object)
g_list_foreach (v4l2object->colors, (GFunc) g_object_unref, NULL);
g_list_free (v4l2object->colors);
v4l2object->colors = NULL;
+
+ g_datalist_clear (&v4l2object->controls);
}
/******************************************************
@@ -480,7 +541,13 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
pollfd.fd = v4l2object->video_fd;
gst_poll_add_fd (v4l2object->poll, &pollfd);
- gst_poll_fd_ctl_read (v4l2object->poll, &pollfd, TRUE);
+ if (v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ gst_poll_fd_ctl_read (v4l2object->poll, &pollfd, TRUE);
+ else
+ gst_poll_fd_ctl_write (v4l2object->poll, &pollfd, TRUE);
+
+ if (v4l2object->extra_controls)
+ gst_v4l2_set_controls (v4l2object, v4l2object->extra_controls);
return TRUE;
@@ -807,6 +874,33 @@ ctrl_failed:
}
}
+static gboolean
+set_contol (GQuark field_id, const GValue * value, gpointer user_data)
+{
+ GstV4l2Object *v4l2object = user_data;
+ gpointer *d = g_datalist_id_get_data (&v4l2object->controls, field_id);
+ if (!d) {
+ GST_WARNING_OBJECT (v4l2object,
+ "Control '%s' does not exist or has an unsupported type.",
+ g_quark_to_string (field_id));
+ return TRUE;
+ }
+ if (!G_VALUE_HOLDS (value, G_TYPE_INT)) {
+ GST_WARNING_OBJECT (v4l2object,
+ "'int' value expected for control '%s'.", g_quark_to_string (field_id));
+ return TRUE;
+ }
+ gst_v4l2_set_attribute (v4l2object, GPOINTER_TO_INT (d),
+ g_value_get_int (value));
+ return TRUE;
+}
+
+gboolean
+gst_v4l2_set_controls (GstV4l2Object * v4l2object, GstStructure * controls)
+{
+ return gst_structure_foreach (controls, set_contol, v4l2object);
+}
+
gboolean
gst_v4l2_get_input (GstV4l2Object * v4l2object, gint * input)
{
diff --git a/sys/v4l2/v4l2_calls.h b/sys/v4l2/v4l2_calls.h
index 36cf0f3a9..11d16b6ab 100644
--- a/sys/v4l2/v4l2_calls.h
+++ b/sys/v4l2/v4l2_calls.h
@@ -126,6 +126,9 @@ gboolean gst_v4l2_set_attribute (GstV4l2Object *v4l2object,
int attribute,
const int value);
+gboolean gst_v4l2_set_controls (GstV4l2Object * v4l2object,
+ GstStructure * controls);
+
gboolean gst_v4l2_get_capabilities (GstV4l2Object * v4l2object);