diff options
author | Michael Olbrich <m.olbrich@pengutronix.de> | 2013-09-13 16:30:57 +0200 |
---|---|---|
committer | Michael Olbrich <m.olbrich@pengutronix.de> | 2013-09-13 16:30:57 +0200 |
commit | b5bd0ad35223b44bdbd5dc0f80c921ff60bce9eb (patch) | |
tree | 82e7f726af222ab4b7600c00b2503622cff1fb52 | |
parent | f631080120a597aee8f8b97621593fb54145ec41 (diff) | |
parent | e49c918fa3527405cf803bebc87e757993fc88e3 (diff) | |
download | gst-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.ac | 18 | ||||
-rw-r--r-- | gst/avi/gstavimux.c | 2 | ||||
-rw-r--r-- | sys/v4l2/Makefile.am | 6 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2bufferpool.c | 201 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2bufferpool.h | 1 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2object.c | 233 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2object.h | 14 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2sink.c | 4 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2src.c | 5 | ||||
-rw-r--r-- | sys/v4l2/v4l2_calls.c | 106 | ||||
-rw-r--r-- | sys/v4l2/v4l2_calls.h | 3 |
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, + ©, 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); |