mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
v4l2: Add v4l2sink element
This also does the following changes: (1) pull the bufferpool code out into gstv4l2bufferpool.c, and make a bit more generic so it can be used both for v4l2src and v4l2sink (2) move some of the device probing/configuration/caps stuff into gstv4l2object.c so it does not have to be duplicated between v4l2src and v4l2sink Fixes bug #590280.
This commit is contained in:
parent
56850099a6
commit
f19cfbda96
14 changed files with 3051 additions and 1824 deletions
|
@ -12,7 +12,9 @@ plugin_LTLIBRARIES = libgstvideo4linux2.la
|
|||
libgstvideo4linux2_la_SOURCES = gstv4l2.c \
|
||||
gstv4l2colorbalance.c \
|
||||
gstv4l2object.c \
|
||||
gstv4l2bufferpool.c \
|
||||
gstv4l2src.c \
|
||||
gstv4l2sink.c \
|
||||
gstv4l2tuner.c \
|
||||
gstv4l2vidorient.c \
|
||||
v4l2_calls.c \
|
||||
|
@ -31,6 +33,7 @@ libgstvideo4linux2_la_LIBTOOLFLAGS = --tag=disable-static
|
|||
|
||||
libgstvideo4linux2_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
|
||||
$(GST_BASE_LIBS) \
|
||||
$(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) \
|
||||
-lgstinterfaces-$(GST_MAJORMINOR) \
|
||||
$(GST_LIBS) \
|
||||
$(xv_libs) \
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "gstv4l2object.h"
|
||||
#include "gstv4l2src.h"
|
||||
#include "gstv4l2sink.h"
|
||||
/* #include "gstv4l2jpegsrc.h" */
|
||||
/* #include "gstv4l2mjpegsrc.h" */
|
||||
/* #include "gstv4l2mjpegsink.h" */
|
||||
|
@ -44,13 +45,16 @@ plugin_init (GstPlugin * plugin)
|
|||
GST_DEBUG_CATEGORY_INIT (v4l2_debug, "v4l2", 0, "V4L2 API calls");
|
||||
|
||||
if (!gst_element_register (plugin, "v4l2src", GST_RANK_PRIMARY,
|
||||
GST_TYPE_V4L2SRC))
|
||||
/* !gst_element_register (plugin, "v4l2jpegsrc", */
|
||||
/* GST_RANK_NONE, GST_TYPE_V4L2JPEGSRC) || */
|
||||
/* !gst_element_register (plugin, "v4l2mjpegsrc", */
|
||||
/* GST_RANK_NONE, GST_TYPE_V4L2MJPEGSRC) || */
|
||||
/* !gst_element_register (plugin, "v4l2mjpegsink", */
|
||||
/* GST_RANK_NONE, GST_TYPE_V4L2MJPEGSINK)) */
|
||||
GST_TYPE_V4L2SRC) ||
|
||||
!gst_element_register (plugin, "v4l2sink", GST_RANK_PRIMARY,
|
||||
GST_TYPE_V4L2SINK) ||
|
||||
/* !gst_element_register (plugin, "v4l2jpegsrc", */
|
||||
/* GST_RANK_NONE, GST_TYPE_V4L2JPEGSRC) || */
|
||||
/* !gst_element_register (plugin, "v4l2mjpegsrc", */
|
||||
/* GST_RANK_NONE, GST_TYPE_V4L2MJPEGSRC) || */
|
||||
/* !gst_element_register (plugin, "v4l2mjpegsink", */
|
||||
/* GST_RANK_NONE, GST_TYPE_V4L2MJPEGSINK)) */
|
||||
FALSE)
|
||||
return FALSE;
|
||||
|
||||
#ifdef ENABLE_NLS
|
||||
|
|
630
sys/v4l2/gstv4l2bufferpool.c
Normal file
630
sys/v4l2/gstv4l2bufferpool.c
Normal file
|
@ -0,0 +1,630 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||
* 2006 Edgard Lima <edgard.lima@indt.org.br>
|
||||
* 2009 Texas Instruments, Inc - http://www.ti.com/
|
||||
*
|
||||
* gstv4l2src.h: BT8x8/V4L2 source element
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gstv4l2bufferpool.h>
|
||||
#include "gstv4l2src.h"
|
||||
#include "gstv4l2sink.h"
|
||||
#include "v4l2_calls.h"
|
||||
#include "gst/gst-i18n-plugin.h"
|
||||
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
|
||||
#define GST_CAT_DEFAULT v4l2_debug
|
||||
|
||||
|
||||
/*
|
||||
* GstV4l2Buffer:
|
||||
*/
|
||||
|
||||
static GstBufferClass *v4l2buffer_parent_class = NULL;
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_finalize (GstV4l2Buffer * buffer)
|
||||
{
|
||||
GstV4l2BufferPool *pool;
|
||||
gboolean resuscitated = FALSE;
|
||||
gint index;
|
||||
|
||||
pool = buffer->pool;
|
||||
|
||||
index = buffer->vbuffer.index;
|
||||
|
||||
GST_LOG_OBJECT (pool->v4l2elem, "finalizing buffer %p %d", buffer, index);
|
||||
|
||||
GST_V4L2_BUFFER_POOL_LOCK (pool);
|
||||
if (GST_BUFFER_SIZE (buffer) != 0) {
|
||||
/* BUFFER_SIZE is only set if the frame was dequeued */
|
||||
pool->num_live_buffers--;
|
||||
GST_DEBUG_OBJECT (pool->v4l2elem, "num_live_buffers--: %d",
|
||||
pool->num_live_buffers);
|
||||
}
|
||||
|
||||
if (pool->running) {
|
||||
if (pool->requeuebuf) {
|
||||
if (!gst_v4l2_buffer_pool_qbuf (pool, buffer)) {
|
||||
GST_WARNING ("could not requeue buffer %p %d", buffer, index);
|
||||
} else {
|
||||
resuscitated = TRUE;
|
||||
}
|
||||
} else {
|
||||
resuscitated = TRUE;
|
||||
/* XXX double check this... I think it is ok to not synchronize this
|
||||
* w.r.t. destruction of the pool, since the buffer is still live and
|
||||
* the buffer holds a ref to the pool..
|
||||
*/
|
||||
g_async_queue_push (pool->avail_buffers, buffer);
|
||||
}
|
||||
} else {
|
||||
GST_LOG_OBJECT (pool->v4l2elem, "the pool is shutting down");
|
||||
}
|
||||
|
||||
if (resuscitated) {
|
||||
/* FIXME: check that the caps didn't change */
|
||||
GST_LOG_OBJECT (pool->v4l2elem, "reviving buffer %p, %d", buffer, index);
|
||||
gst_buffer_ref (GST_BUFFER (buffer));
|
||||
GST_BUFFER_SIZE (buffer) = 0;
|
||||
pool->buffers[index] = buffer;
|
||||
}
|
||||
|
||||
GST_V4L2_BUFFER_POOL_UNLOCK (pool);
|
||||
|
||||
if (!resuscitated) {
|
||||
GST_LOG_OBJECT (pool->v4l2elem, "buffer %p not recovered, unmapping",
|
||||
buffer);
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (pool));
|
||||
v4l2_munmap ((void *) GST_BUFFER_DATA (buffer), buffer->vbuffer.length);
|
||||
|
||||
GST_MINI_OBJECT_CLASS (v4l2buffer_parent_class)->finalize (GST_MINI_OBJECT
|
||||
(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_class_init (gpointer g_class, gpointer class_data)
|
||||
{
|
||||
GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
|
||||
|
||||
v4l2buffer_parent_class = g_type_class_peek_parent (g_class);
|
||||
|
||||
mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
|
||||
gst_v4l2_buffer_finalize;
|
||||
}
|
||||
|
||||
GType
|
||||
gst_v4l2_buffer_get_type (void)
|
||||
{
|
||||
static GType _gst_v4l2_buffer_type;
|
||||
|
||||
if (G_UNLIKELY (_gst_v4l2_buffer_type == 0)) {
|
||||
static const GTypeInfo v4l2_buffer_info = {
|
||||
sizeof (GstBufferClass),
|
||||
NULL,
|
||||
NULL,
|
||||
gst_v4l2_buffer_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstV4l2Buffer),
|
||||
0,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
_gst_v4l2_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
|
||||
"GstV4l2Buffer", &v4l2_buffer_info, 0);
|
||||
}
|
||||
return _gst_v4l2_buffer_type;
|
||||
}
|
||||
|
||||
static GstV4l2Buffer *
|
||||
gst_v4l2_buffer_new (GstV4l2BufferPool * pool, guint index, GstCaps * caps)
|
||||
{
|
||||
GstV4l2Buffer *ret;
|
||||
guint8 *data;
|
||||
|
||||
ret = (GstV4l2Buffer *) gst_mini_object_new (GST_TYPE_V4L2_BUFFER);
|
||||
|
||||
GST_LOG_OBJECT (pool->v4l2elem, "creating buffer %u, %p in pool %p", index,
|
||||
ret, pool);
|
||||
|
||||
ret->pool =
|
||||
(GstV4l2BufferPool *) gst_mini_object_ref (GST_MINI_OBJECT (pool));
|
||||
|
||||
ret->vbuffer.index = index;
|
||||
ret->vbuffer.type = pool->type;
|
||||
ret->vbuffer.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (v4l2_ioctl (pool->video_fd, VIDIOC_QUERYBUF, &ret->vbuffer) < 0)
|
||||
goto querybuf_failed;
|
||||
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " index: %u", ret->vbuffer.index);
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " type: %d", ret->vbuffer.type);
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " bytesused: %u", ret->vbuffer.bytesused);
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " flags: %08x", ret->vbuffer.flags);
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " field: %d", ret->vbuffer.field);
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " memory: %d", ret->vbuffer.memory);
|
||||
if (ret->vbuffer.memory == V4L2_MEMORY_MMAP)
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " MMAP offset: %u",
|
||||
ret->vbuffer.m.offset);
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " length: %u", ret->vbuffer.length);
|
||||
GST_LOG_OBJECT (pool->v4l2elem, " input: %u", ret->vbuffer.input);
|
||||
|
||||
data = (guint8 *) v4l2_mmap (0, ret->vbuffer.length,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd,
|
||||
ret->vbuffer.m.offset);
|
||||
|
||||
if (data == MAP_FAILED)
|
||||
goto mmap_failed;
|
||||
|
||||
GST_BUFFER_DATA (ret) = data;
|
||||
GST_BUFFER_SIZE (ret) = ret->vbuffer.length;
|
||||
|
||||
GST_BUFFER_FLAG_SET (ret, GST_BUFFER_FLAG_READONLY);
|
||||
|
||||
gst_buffer_set_caps (GST_BUFFER (ret), caps);
|
||||
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
querybuf_failed:
|
||||
{
|
||||
gint errnosave = errno;
|
||||
|
||||
GST_WARNING ("Failed QUERYBUF: %s", g_strerror (errnosave));
|
||||
gst_buffer_unref (GST_BUFFER (ret));
|
||||
errno = errnosave;
|
||||
return NULL;
|
||||
}
|
||||
mmap_failed:
|
||||
{
|
||||
gint errnosave = errno;
|
||||
|
||||
GST_WARNING ("Failed to mmap: %s", g_strerror (errnosave));
|
||||
gst_buffer_unref (GST_BUFFER (ret));
|
||||
errno = errnosave;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GstV4l2BufferPool:
|
||||
*/
|
||||
|
||||
static GstMiniObjectClass *buffer_pool_parent_class = NULL;
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_pool_finalize (GstV4l2BufferPool * pool)
|
||||
{
|
||||
g_mutex_free (pool->lock);
|
||||
pool->lock = NULL;
|
||||
|
||||
g_async_queue_unref (pool->avail_buffers);
|
||||
pool->avail_buffers = NULL;
|
||||
|
||||
if (pool->video_fd >= 0)
|
||||
v4l2_close (pool->video_fd);
|
||||
|
||||
if (pool->buffers) {
|
||||
g_free (pool->buffers);
|
||||
pool->buffers = NULL;
|
||||
}
|
||||
|
||||
GST_MINI_OBJECT_CLASS (buffer_pool_parent_class)->finalize (GST_MINI_OBJECT
|
||||
(pool));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_pool_init (GstV4l2BufferPool * pool, gpointer g_class)
|
||||
{
|
||||
pool->lock = g_mutex_new ();
|
||||
pool->running = FALSE;
|
||||
pool->num_live_buffers = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_pool_class_init (gpointer g_class, gpointer class_data)
|
||||
{
|
||||
GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
|
||||
|
||||
buffer_pool_parent_class = g_type_class_peek_parent (g_class);
|
||||
|
||||
mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
|
||||
gst_v4l2_buffer_pool_finalize;
|
||||
}
|
||||
|
||||
GType
|
||||
gst_v4l2_buffer_pool_get_type (void)
|
||||
{
|
||||
static GType _gst_v4l2_buffer_pool_type;
|
||||
|
||||
if (G_UNLIKELY (_gst_v4l2_buffer_pool_type == 0)) {
|
||||
static const GTypeInfo v4l2_buffer_pool_info = {
|
||||
sizeof (GstBufferClass),
|
||||
NULL,
|
||||
NULL,
|
||||
gst_v4l2_buffer_pool_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstV4l2BufferPool),
|
||||
0,
|
||||
(GInstanceInitFunc) gst_v4l2_buffer_pool_init,
|
||||
NULL
|
||||
};
|
||||
_gst_v4l2_buffer_pool_type = g_type_register_static (GST_TYPE_MINI_OBJECT,
|
||||
"GstV4l2BufferPool", &v4l2_buffer_pool_info, 0);
|
||||
}
|
||||
return _gst_v4l2_buffer_pool_type;
|
||||
}
|
||||
|
||||
|
||||
/* this is somewhat of a hack.. but better to keep the hack in
|
||||
* one place than copy/pasting it around..
|
||||
*/
|
||||
static GstV4l2Object *
|
||||
get_v4l2_object (GstElement * v4l2elem)
|
||||
{
|
||||
GstV4l2Object *v4l2object = NULL;
|
||||
if (GST_IS_V4L2SRC (v4l2elem)) {
|
||||
v4l2object = (GST_V4L2SRC (v4l2elem))->v4l2object;
|
||||
} else if (GST_IS_V4L2SINK (v4l2elem)) {
|
||||
v4l2object = (GST_V4L2SINK (v4l2elem))->v4l2object;
|
||||
} else {
|
||||
GST_ERROR_OBJECT (v4l2elem, "unknown v4l2 element");
|
||||
}
|
||||
return v4l2object;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new buffer pool
|
||||
*
|
||||
* @v4l2elem the v4l2 element (src or sink) that owns this pool
|
||||
* @fd the video device file descriptor
|
||||
* @num_buffers the requested number of buffers in the pool
|
||||
* @caps the caps to set on the buffer
|
||||
* @requeuebuf if <code>TRUE</code>, and if the pool is still in the
|
||||
* <code>running</code> state, a buffer with no remaining references
|
||||
* is immediately passed back to v4l2 (<code>VIDIOC_QBUF</code>),
|
||||
* otherwise it is returned to the pool of available buffers
|
||||
* (which can be accessed via <code>gst_v4l2_buffer_pool_get()</code>.
|
||||
*/
|
||||
GstV4l2BufferPool *
|
||||
gst_v4l2_buffer_pool_new (GstElement * v4l2elem, gint fd, gint num_buffers,
|
||||
GstCaps * caps, gboolean requeuebuf, enum v4l2_buf_type type)
|
||||
{
|
||||
GstV4l2BufferPool *pool;
|
||||
gint n;
|
||||
struct v4l2_requestbuffers breq;
|
||||
|
||||
pool = (GstV4l2BufferPool *) gst_mini_object_new (GST_TYPE_V4L2_BUFFER_POOL);
|
||||
|
||||
pool->video_fd = v4l2_dup (fd);
|
||||
if (pool->video_fd < 0)
|
||||
goto dup_failed;
|
||||
|
||||
|
||||
/* first, lets request buffers, and see how many we can get: */
|
||||
GST_DEBUG_OBJECT (v4l2elem, "STREAMING, requesting %d MMAP buffers",
|
||||
num_buffers);
|
||||
|
||||
memset (&breq, 0, sizeof (struct v4l2_requestbuffers));
|
||||
breq.type = type;
|
||||
breq.count = num_buffers;
|
||||
breq.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (v4l2_ioctl (fd, VIDIOC_REQBUFS, &breq) < 0)
|
||||
goto reqbufs_failed;
|
||||
|
||||
GST_LOG_OBJECT (v4l2elem, " count: %u", breq.count);
|
||||
GST_LOG_OBJECT (v4l2elem, " type: %d", breq.type);
|
||||
GST_LOG_OBJECT (v4l2elem, " memory: %d", breq.memory);
|
||||
|
||||
if (breq.count < GST_V4L2_MIN_BUFFERS)
|
||||
goto no_buffers;
|
||||
|
||||
if (num_buffers != breq.count) {
|
||||
GST_WARNING_OBJECT (v4l2elem, "using %u buffers instead", breq.count);
|
||||
num_buffers = breq.count;
|
||||
}
|
||||
|
||||
pool->v4l2elem = v4l2elem;
|
||||
pool->requeuebuf = requeuebuf;
|
||||
pool->type = type;
|
||||
pool->buffer_count = num_buffers;
|
||||
pool->buffers = g_new0 (GstV4l2Buffer *, num_buffers);
|
||||
pool->avail_buffers = g_async_queue_new ();
|
||||
|
||||
/* now, map the buffers: */
|
||||
for (n = 0; n < num_buffers; n++) {
|
||||
pool->buffers[n] = gst_v4l2_buffer_new (pool, n, caps);
|
||||
if (!pool->buffers[n])
|
||||
goto buffer_new_failed;
|
||||
g_async_queue_push (pool->avail_buffers, pool->buffers[n]);
|
||||
}
|
||||
|
||||
return pool;
|
||||
|
||||
/* ERRORS */
|
||||
dup_failed:
|
||||
{
|
||||
gint errnosave = errno;
|
||||
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (pool));
|
||||
|
||||
errno = errnosave;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
reqbufs_failed:
|
||||
{
|
||||
GstV4l2Object *v4l2object = get_v4l2_object (v4l2elem);
|
||||
GST_ELEMENT_ERROR (v4l2elem, RESOURCE, READ,
|
||||
(_("Could not get buffers from device '%s'."),
|
||||
v4l2object->videodev),
|
||||
("error requesting %d buffers: %s", num_buffers, g_strerror (errno)));
|
||||
return NULL;
|
||||
}
|
||||
no_buffers:
|
||||
{
|
||||
GstV4l2Object *v4l2object = get_v4l2_object (v4l2elem);
|
||||
GST_ELEMENT_ERROR (v4l2elem, RESOURCE, READ,
|
||||
(_("Could not get enough buffers from device '%s'."),
|
||||
v4l2object->videodev),
|
||||
("we received %d from device '%s', we want at least %d",
|
||||
breq.count, v4l2object->videodev, GST_V4L2_MIN_BUFFERS));
|
||||
return NULL;
|
||||
}
|
||||
buffer_new_failed:
|
||||
{
|
||||
gint errnosave = errno;
|
||||
|
||||
gst_v4l2_buffer_pool_destroy (pool);
|
||||
|
||||
errno = errnosave;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool)
|
||||
{
|
||||
gint n;
|
||||
|
||||
GST_V4L2_BUFFER_POOL_LOCK (pool);
|
||||
pool->running = FALSE;
|
||||
GST_V4L2_BUFFER_POOL_UNLOCK (pool);
|
||||
|
||||
GST_DEBUG_OBJECT (pool->v4l2elem, "destroy pool");
|
||||
|
||||
/* after this point, no more buffers will be queued or dequeued; no buffer
|
||||
* from pool->buffers that is NULL will be set to a buffer, and no buffer that
|
||||
* is not NULL will be pushed out. */
|
||||
|
||||
/* miniobjects have no dispose, so they can't break ref-cycles, as buffers ref
|
||||
* the pool, we need to unref the buffer to properly finalize te pool */
|
||||
for (n = 0; n < pool->buffer_count; n++) {
|
||||
GstBuffer *buf;
|
||||
|
||||
GST_V4L2_BUFFER_POOL_LOCK (pool);
|
||||
buf = GST_BUFFER (pool->buffers[n]);
|
||||
GST_V4L2_BUFFER_POOL_UNLOCK (pool);
|
||||
|
||||
if (buf)
|
||||
/* we own the ref if the buffer is in pool->buffers; drop it. */
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (pool));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an available buffer in the pool
|
||||
*
|
||||
* @pool the "this" object
|
||||
* @blocking if <code>TRUE</code>, then suspend until a buffer is available
|
||||
*/
|
||||
GstV4l2Buffer *
|
||||
gst_v4l2_buffer_pool_get (GstV4l2BufferPool * pool, gboolean blocking)
|
||||
{
|
||||
GstV4l2Buffer *buf = NULL;
|
||||
|
||||
do {
|
||||
buf = g_async_queue_try_pop (pool->avail_buffers);
|
||||
|
||||
/* if there isn't a buffer avail, let's try to dequeue one:
|
||||
*/
|
||||
if (blocking && !buf) {
|
||||
GST_DEBUG_OBJECT (pool->v4l2elem, "No buffers available.. need to dqbuf");
|
||||
buf = gst_v4l2_buffer_pool_dqbuf (pool);
|
||||
|
||||
/* note: if we get a buf, we don't want to use it directly (because
|
||||
* someone else could still hold a ref).. but instead we release our
|
||||
* reference to it, and if no one else holds a ref it will be returned
|
||||
* to the pool of available buffers.. and if not, we keep looping.
|
||||
*/
|
||||
if (buf) {
|
||||
gst_buffer_unref (GST_BUFFER (buf));
|
||||
buf = NULL;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
if (buf) {
|
||||
pool->num_live_buffers++;
|
||||
GST_DEBUG_OBJECT (pool->v4l2elem, "num_live_buffers++: %d",
|
||||
pool->num_live_buffers);
|
||||
GST_BUFFER_SIZE (buf) = buf->vbuffer.length;
|
||||
}
|
||||
|
||||
pool->running = TRUE;
|
||||
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queue a buffer to the driver
|
||||
*
|
||||
* @pool the "this" object
|
||||
* @buf the buffer to queue
|
||||
*/
|
||||
gboolean
|
||||
gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstV4l2Buffer * buf)
|
||||
{
|
||||
GST_LOG_OBJECT (pool->v4l2elem, "enqueue pool buffer %d", buf->vbuffer.index);
|
||||
|
||||
if (v4l2_ioctl (pool->video_fd, VIDIOC_QBUF, &buf->vbuffer) < 0)
|
||||
return FALSE;
|
||||
|
||||
pool->num_live_buffers--;
|
||||
GST_DEBUG_OBJECT (pool->v4l2elem, "num_live_buffers--: %d",
|
||||
pool->num_live_buffers);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue a buffer from the driver. Some generic error handling is done in
|
||||
* this function, but any error handling specific to v4l2src (capture) or
|
||||
* v4l2sink (output) can be done outside this function by checking 'errno'
|
||||
*
|
||||
* @pool the "this" object
|
||||
*/
|
||||
GstV4l2Buffer *
|
||||
gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool)
|
||||
{
|
||||
GstV4l2Object *v4l2object = get_v4l2_object (pool->v4l2elem);
|
||||
GstV4l2Buffer *pool_buffer;
|
||||
struct v4l2_buffer buffer;
|
||||
|
||||
memset (&buffer, 0x00, sizeof (buffer));
|
||||
buffer.type = pool->type;
|
||||
buffer.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
|
||||
if (v4l2_ioctl (pool->video_fd, VIDIOC_DQBUF, &buffer) >= 0) {
|
||||
|
||||
GST_V4L2_BUFFER_POOL_LOCK (pool);
|
||||
|
||||
/* get our GstBuffer with that index from the pool, if the buffer was
|
||||
* outstanding we have a serious problem.
|
||||
*/
|
||||
pool_buffer = pool->buffers[buffer.index];
|
||||
|
||||
if (pool_buffer == NULL) {
|
||||
GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, FAILED,
|
||||
(_("Failed trying to get video frames from device '%s'."),
|
||||
v4l2object->videodev),
|
||||
(_("No free buffers found in the pool at index %d."), buffer.index));
|
||||
GST_V4L2_BUFFER_POOL_UNLOCK (pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (pool->v4l2elem,
|
||||
"grabbed frame %d (ix=%d), flags %08x, pool-ct=%d, buffer=%p",
|
||||
buffer.sequence, buffer.index, buffer.flags, pool->num_live_buffers,
|
||||
pool_buffer);
|
||||
|
||||
pool->num_live_buffers++;
|
||||
GST_DEBUG_OBJECT (pool->v4l2elem, "num_live_buffers++: %d",
|
||||
pool->num_live_buffers);
|
||||
|
||||
GST_V4L2_BUFFER_POOL_UNLOCK (pool);
|
||||
|
||||
/* this can change at every frame, esp. with jpeg */
|
||||
GST_BUFFER_SIZE (pool_buffer) = buffer.bytesused;
|
||||
|
||||
return pool_buffer;
|
||||
}
|
||||
|
||||
|
||||
GST_WARNING_OBJECT (pool->v4l2elem,
|
||||
"problem grabbing frame %d (ix=%d), pool-ct=%d, buf.flags=%d",
|
||||
buffer.sequence, buffer.index,
|
||||
GST_MINI_OBJECT_REFCOUNT (pool), buffer.flags);
|
||||
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
GST_WARNING_OBJECT (pool->v4l2elem,
|
||||
"Non-blocking I/O has been selected using O_NONBLOCK and"
|
||||
" no buffer was in the outgoing queue. device %s",
|
||||
v4l2object->videodev);
|
||||
break;
|
||||
case EINVAL:
|
||||
GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, FAILED,
|
||||
(_("Failed trying to get video frames from device '%s'."),
|
||||
v4l2object->videodev),
|
||||
(_("The buffer type is not supported, or the index is out of bounds,"
|
||||
" or no buffers have been allocated yet, or the userptr"
|
||||
" or length are invalid. device %s"), v4l2object->videodev));
|
||||
break;
|
||||
case ENOMEM:
|
||||
GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, FAILED,
|
||||
(_("Failed trying to get video frames from device '%s'. Not enough memory."), v4l2object->videodev), (_("insufficient memory to enqueue a user pointer buffer. device %s."), v4l2object->videodev));
|
||||
break;
|
||||
case EIO:
|
||||
GST_INFO_OBJECT (pool->v4l2elem,
|
||||
"VIDIOC_DQBUF failed due to an internal error."
|
||||
" Can also indicate temporary problems like signal loss."
|
||||
" Note the driver might dequeue an (empty) buffer despite"
|
||||
" returning an error, or even stop capturing."
|
||||
" device %s", v4l2object->videodev);
|
||||
/* have we de-queued a buffer ? */
|
||||
if (!(buffer.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))) {
|
||||
GST_DEBUG_OBJECT (pool->v4l2elem, "reenqueing buffer");
|
||||
/* FIXME ... should we do something here? */
|
||||
}
|
||||
break;
|
||||
case EINTR:
|
||||
GST_WARNING_OBJECT (pool->v4l2elem,
|
||||
"could not sync on a buffer on device %s", v4l2object->videodev);
|
||||
break;
|
||||
default:
|
||||
GST_WARNING_OBJECT (pool->v4l2elem,
|
||||
"Grabbing frame got interrupted on %s unexpectedly. %d: %s.",
|
||||
v4l2object->videodev, errno, g_strerror (errno));
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gint
|
||||
gst_v4l2_buffer_pool_available_buffers (GstV4l2BufferPool * pool)
|
||||
{
|
||||
return pool->buffer_count - pool->num_live_buffers;
|
||||
}
|
97
sys/v4l2/gstv4l2bufferpool.h
Normal file
97
sys/v4l2/gstv4l2bufferpool.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||
* 2006 Edgard Lima <edgard.lima@indt.org.br>
|
||||
* 2009 Texas Instruments, Inc - http://www.ti.com/
|
||||
*
|
||||
* gstv4l2src.h: BT8x8/V4L2 source element
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GSTV4L2BUFFER_H__
|
||||
#define __GSTV4L2BUFFER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "v4l2_calls.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (v4l2buffer_debug);
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
GType gst_v4l2_buffer_get_type (void);
|
||||
#define GST_TYPE_V4L2_BUFFER (gst_v4l2_buffer_get_type())
|
||||
#define GST_IS_V4L2_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_BUFFER))
|
||||
#define GST_V4L2_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_BUFFER, GstV4l2Buffer))
|
||||
|
||||
GType gst_v4l2_buffer_pool_get_type (void);
|
||||
#define GST_TYPE_V4L2_BUFFER_POOL (gst_v4l2_buffer_pool_get_type())
|
||||
#define GST_IS_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_BUFFER_POOL))
|
||||
#define GST_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_BUFFER_POOL, GstV4l2BufferPool))
|
||||
|
||||
|
||||
|
||||
typedef struct _GstV4l2BufferPool GstV4l2BufferPool;
|
||||
typedef struct _GstV4l2Buffer GstV4l2Buffer;
|
||||
|
||||
|
||||
struct _GstV4l2BufferPool
|
||||
{
|
||||
GstMiniObject parent;
|
||||
|
||||
GstElement *v4l2elem; /* the v4l2 src/sink that owns us.. maybe we should be owned by v4l2object? */
|
||||
gboolean requeuebuf; /* if true, unusued buffers are automatically re-QBUF'd */
|
||||
enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT */
|
||||
|
||||
GMutex *lock;
|
||||
gboolean running; /* with lock */
|
||||
gint num_live_buffers; /* number of buffers not with driver (capture) or not in avail buffer pool (display) */
|
||||
GAsyncQueue* avail_buffers;/* pool of available buffers, not with the driver and which aren't held outside the bufferpool */
|
||||
gint video_fd; /* a dup(2) of the v4l2object's video_fd */
|
||||
guint buffer_count;
|
||||
GstV4l2Buffer **buffers;
|
||||
};
|
||||
|
||||
struct _GstV4l2Buffer {
|
||||
GstBuffer buffer;
|
||||
|
||||
struct v4l2_buffer vbuffer;
|
||||
|
||||
/* FIXME: have GstV4l2Src* instead, as this has GstV4l2BufferPool* */
|
||||
/* FIXME: do we really want to fix this if GstV4l2Buffer/Pool is shared
|
||||
* between v4l2src and v4l2sink??
|
||||
*/
|
||||
GstV4l2BufferPool *pool;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
void gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool);
|
||||
GstV4l2BufferPool *gst_v4l2_buffer_pool_new (GstElement *v4l2elem, gint fd, gint num_buffers, GstCaps * caps, gboolean requeuebuf, enum v4l2_buf_type type);
|
||||
|
||||
|
||||
GstV4l2Buffer *gst_v4l2_buffer_pool_get (GstV4l2BufferPool *pool, gboolean blocking);
|
||||
gboolean gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool *pool, GstV4l2Buffer *buf);
|
||||
GstV4l2Buffer *gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool *pool);
|
||||
|
||||
gint gst_v4l2_buffer_pool_available_buffers (GstV4l2BufferPool *pool);
|
||||
|
||||
|
||||
#define GST_V4L2_BUFFER_POOL_LOCK(pool) g_mutex_lock ((pool)->lock)
|
||||
#define GST_V4L2_BUFFER_POOL_UNLOCK(pool) g_mutex_unlock ((pool)->lock)
|
||||
|
||||
#endif /* __GSTV4L2BUFFER_H__ */
|
File diff suppressed because it is too large
Load diff
|
@ -51,6 +51,16 @@
|
|||
|
||||
#include <gst/interfaces/propertyprobe.h>
|
||||
|
||||
|
||||
/* size of v4l2 buffer pool in streaming case */
|
||||
#define GST_V4L2_MAX_BUFFERS 16
|
||||
#define GST_V4L2_MIN_BUFFERS 1
|
||||
|
||||
/* max frame width/height */
|
||||
#define GST_V4L2_MAX_SIZE (1<<15) /* 2^15 == 32768 */
|
||||
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_V4L2_OBJECT(obj) (GstV4l2Object *)(obj)
|
||||
|
@ -77,6 +87,8 @@ struct _GstV4l2Object {
|
|||
/* the video buffer (mmap()'ed) */
|
||||
guint8 **buffer;
|
||||
|
||||
enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT */
|
||||
|
||||
/* the video device's capabilities */
|
||||
struct v4l2_capability vcap;
|
||||
|
||||
|
@ -87,6 +99,8 @@ struct _GstV4l2Object {
|
|||
struct v4l2_input vinput;
|
||||
|
||||
/* lists... */
|
||||
GSList *formats; /* list of available capture formats */
|
||||
|
||||
GList *colors;
|
||||
GList *norms;
|
||||
GList *channels;
|
||||
|
@ -121,13 +135,16 @@ GType gst_v4l2_object_get_type (void);
|
|||
|
||||
/* create/destroy */
|
||||
GstV4l2Object * gst_v4l2_object_new (GstElement * element,
|
||||
enum v4l2_buf_type type,
|
||||
char *default_device,
|
||||
GstV4l2GetInOutFunction get_in_out_func,
|
||||
GstV4l2SetInOutFunction set_in_out_func,
|
||||
GstV4l2UpdateFpsFunction update_fps_func);
|
||||
void gst_v4l2_object_destroy (GstV4l2Object * v4l2object);
|
||||
|
||||
/* properties */
|
||||
void gst_v4l2_object_install_properties_helper (GObjectClass *gobject_class);
|
||||
|
||||
void gst_v4l2_object_install_properties_helper (GObjectClass *gobject_class, const char *default_device);
|
||||
|
||||
gboolean gst_v4l2_object_set_property_helper (GstV4l2Object *v4l2object,
|
||||
guint prop_id, const GValue * value,
|
||||
|
@ -152,6 +169,26 @@ GValueArray* gst_v4l2_probe_get_values (GstPropertyProbe * probe, guint pro
|
|||
const GParamSpec * pspec,
|
||||
GList ** klass_devices);
|
||||
|
||||
GstCaps* gst_v4l2_object_probe_caps_for_format (GstV4l2Object *v4l2object, guint32 pixelformat,
|
||||
const GstStructure * template);
|
||||
|
||||
gboolean gst_v4l2_object_get_caps_info (GstV4l2Object *v4l2object, GstCaps *caps,
|
||||
struct v4l2_fmtdesc **format, gint *w, gint *h,
|
||||
guint *fps_n, guint *fps_d, guint *size);
|
||||
|
||||
|
||||
GSList* gst_v4l2_object_get_format_list (GstV4l2Object *v4l2object);
|
||||
|
||||
GstCaps* gst_v4l2_object_get_all_caps (void);
|
||||
|
||||
GstStructure* gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc);
|
||||
|
||||
gboolean gst_v4l2_object_set_format (GstV4l2Object *v4l2object, guint32 pixelformat, guint32 width, guint32 height);
|
||||
|
||||
gboolean gst_v4l2_object_start_streaming (GstV4l2Object *v4l2object);
|
||||
gboolean gst_v4l2_object_stop_streaming (GstV4l2Object *v4l2object);
|
||||
|
||||
|
||||
#define GST_IMPLEMENT_V4L2_PROBE_METHODS(Type_Class, interface_as_function) \
|
||||
\
|
||||
static void \
|
||||
|
|
711
sys/v4l2/gstv4l2sink.c
Normal file
711
sys/v4l2/gstv4l2sink.c
Normal file
|
@ -0,0 +1,711 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2009 Texas Instruments, Inc - http://www.ti.com/
|
||||
*
|
||||
* Description: V4L2 sink element
|
||||
* Created on: Jul 2, 2009
|
||||
* Author: Rob Clark <rob@ti.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-v4l2sink
|
||||
*
|
||||
* v4l2sink can be used to display video to v4l2 devices (screen overlays
|
||||
* provided by the graphics hardware, tv-out, etc)
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch lines</title>
|
||||
* |[
|
||||
* gst-launch videotestsrc ! v4l2sink device=/dev/video1
|
||||
* ]| This pipeline displays a test pattern on /dev/video1
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "gstv4l2colorbalance.h"
|
||||
#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */
|
||||
#include "gstv4l2xoverlay.h"
|
||||
#endif
|
||||
#include "gstv4l2vidorient.h"
|
||||
|
||||
#include "gstv4l2sink.h"
|
||||
#include "gst/gst-i18n-plugin.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static const GstElementDetails gst_v4l2sink_details =
|
||||
GST_ELEMENT_DETAILS ("Video (video4linux2) Sink",
|
||||
"Sink/Video",
|
||||
"Displays frames on a video4linux2 device",
|
||||
"Rob Clark <rob@ti.com>,");
|
||||
|
||||
GST_DEBUG_CATEGORY (v4l2sink_debug);
|
||||
#define GST_CAT_DEFAULT v4l2sink_debug
|
||||
|
||||
#define PROP_DEF_QUEUE_SIZE 8
|
||||
#define DEFAULT_PROP_DEVICE "/dev/video1"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
V4L2_STD_OBJECT_PROPS,
|
||||
PROP_QUEUE_SIZE,
|
||||
PROP_OVERLAY_TOP,
|
||||
PROP_OVERLAY_LEFT,
|
||||
PROP_OVERLAY_WIDTH,
|
||||
PROP_OVERLAY_HEIGHT,
|
||||
};
|
||||
|
||||
|
||||
GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2SinkClass, gst_v4l2sink);
|
||||
GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Sink, gst_v4l2sink);
|
||||
#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */
|
||||
GST_IMPLEMENT_V4L2_XOVERLAY_METHODS (GstV4l2Sink, gst_v4l2sink);
|
||||
#endif
|
||||
GST_IMPLEMENT_V4L2_VIDORIENT_METHODS (GstV4l2Sink, gst_v4l2sink);
|
||||
|
||||
static gboolean
|
||||
gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type)
|
||||
{
|
||||
GstV4l2Object *v4l2object = GST_V4L2SINK (iface)->v4l2object;
|
||||
|
||||
#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */
|
||||
g_assert (iface_type == GST_TYPE_X_OVERLAY ||
|
||||
iface_type == GST_TYPE_COLOR_BALANCE ||
|
||||
iface_type == GST_TYPE_VIDEO_ORIENTATION);
|
||||
#else
|
||||
g_assert (iface_type == GST_TYPE_COLOR_BALANCE ||
|
||||
iface_type == GST_TYPE_VIDEO_ORIENTATION);
|
||||
#endif
|
||||
|
||||
if (v4l2object->video_fd == -1)
|
||||
return FALSE;
|
||||
|
||||
#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */
|
||||
if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L2_IS_OVERLAY (v4l2object))
|
||||
return FALSE;
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2sink_interface_init (GstImplementsInterfaceClass * klass)
|
||||
{
|
||||
/*
|
||||
* default virtual functions
|
||||
*/
|
||||
klass->supported = gst_v4l2sink_iface_supported;
|
||||
}
|
||||
|
||||
void
|
||||
gst_v4l2sink_init_interfaces (GType type)
|
||||
{
|
||||
static const GInterfaceInfo v4l2iface_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2sink_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */
|
||||
static const GInterfaceInfo v4l2_xoverlay_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2sink_xoverlay_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
#endif
|
||||
static const GInterfaceInfo v4l2_colorbalance_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2sink_color_balance_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
static const GInterfaceInfo v4l2_videoorientation_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2sink_video_orientation_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
static const GInterfaceInfo v4l2_propertyprobe_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2sink_property_probe_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
g_type_add_interface_static (type,
|
||||
GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info);
|
||||
#if 0 /* overlay is still not implemented #ifdef HAVE_XVIDEO */
|
||||
g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info);
|
||||
#endif
|
||||
g_type_add_interface_static (type,
|
||||
GST_TYPE_COLOR_BALANCE, &v4l2_colorbalance_info);
|
||||
g_type_add_interface_static (type,
|
||||
GST_TYPE_VIDEO_ORIENTATION, &v4l2_videoorientation_info);
|
||||
g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
|
||||
&v4l2_propertyprobe_info);
|
||||
}
|
||||
|
||||
|
||||
GST_BOILERPLATE_FULL (GstV4l2Sink, gst_v4l2sink, GstVideoSink,
|
||||
GST_TYPE_VIDEO_SINK, gst_v4l2sink_init_interfaces);
|
||||
|
||||
|
||||
static void gst_v4l2sink_dispose (GObject * object);
|
||||
static void gst_v4l2sink_finalize (GstV4l2Sink * v4l2sink);
|
||||
|
||||
/* GObject methods: */
|
||||
static void gst_v4l2sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_v4l2sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
|
||||
/* GstElement methods: */
|
||||
static GstStateChangeReturn gst_v4l2sink_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
||||
/* GstBaseSink methods: */
|
||||
static GstCaps *gst_v4l2sink_get_caps (GstBaseSink * bsink);
|
||||
static gboolean gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
|
||||
static GstFlowReturn gst_v4l2sink_buffer_alloc (GstBaseSink * bsink,
|
||||
guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
|
||||
static GstFlowReturn gst_v4l2sink_show_frame (GstBaseSink * bsink,
|
||||
GstBuffer * buf);
|
||||
|
||||
|
||||
static void
|
||||
gst_v4l2sink_base_init (gpointer g_class)
|
||||
{
|
||||
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
|
||||
GstV4l2SinkClass *gstv4l2sink_class = GST_V4L2SINK_CLASS (g_class);
|
||||
|
||||
gstv4l2sink_class->v4l2_class_devices = NULL;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (v4l2sink_debug, "v4l2sink", 0, "V4L2 sink element");
|
||||
|
||||
gst_element_class_set_details (gstelement_class, &gst_v4l2sink_details);
|
||||
|
||||
gst_element_class_add_pad_template
|
||||
(gstelement_class,
|
||||
gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
||||
gst_v4l2_object_get_all_caps ()));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2sink_class_init (GstV4l2SinkClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *element_class;
|
||||
GstBaseSinkClass *basesink_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
element_class = GST_ELEMENT_CLASS (klass);
|
||||
basesink_class = GST_BASE_SINK_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gst_v4l2sink_dispose;
|
||||
gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2sink_finalize;
|
||||
gobject_class->set_property = gst_v4l2sink_set_property;
|
||||
gobject_class->get_property = gst_v4l2sink_get_property;
|
||||
|
||||
element_class->change_state = gst_v4l2sink_change_state;
|
||||
|
||||
gst_v4l2_object_install_properties_helper (gobject_class,
|
||||
DEFAULT_PROP_DEVICE);
|
||||
g_object_class_install_property (gobject_class, PROP_QUEUE_SIZE,
|
||||
g_param_spec_uint ("queue-size", "Queue size",
|
||||
"Number of buffers to be enqueud in the driver in streaming mode",
|
||||
GST_V4L2_MIN_BUFFERS, GST_V4L2_MAX_BUFFERS, PROP_DEF_QUEUE_SIZE,
|
||||
G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_OVERLAY_TOP,
|
||||
g_param_spec_int ("overlay-top", "Overlay top",
|
||||
"The topmost (y) coordinate of the video overlay; top left corner of screen is 0,0",
|
||||
0x80000000, 0x7fffffff, 0, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_OVERLAY_LEFT,
|
||||
g_param_spec_int ("overlay-left", "Overlay left",
|
||||
"The leftmost (x) coordinate of the video overlay; top left corner of screen is 0,0",
|
||||
0x80000000, 0x7fffffff, 0, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_OVERLAY_WIDTH,
|
||||
g_param_spec_uint ("overlay-width", "Overlay width",
|
||||
"The width of the video overlay; default is equal to negotiated image width",
|
||||
0, 0xffffffff, 0, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_OVERLAY_HEIGHT,
|
||||
g_param_spec_uint ("overlay-height", "Overlay height",
|
||||
"The height of the video overlay; default is equal to negotiated image height",
|
||||
0, 0xffffffff, 0, G_PARAM_READWRITE));
|
||||
|
||||
basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_get_caps);
|
||||
basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_set_caps);
|
||||
basesink_class->buffer_alloc = GST_DEBUG_FUNCPTR (gst_v4l2sink_buffer_alloc);
|
||||
basesink_class->preroll = GST_DEBUG_FUNCPTR (gst_v4l2sink_show_frame);
|
||||
basesink_class->render = GST_DEBUG_FUNCPTR (gst_v4l2sink_show_frame);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2sink_init (GstV4l2Sink * v4l2sink, GstV4l2SinkClass * klass)
|
||||
{
|
||||
v4l2sink->v4l2object = gst_v4l2_object_new (GST_ELEMENT (v4l2sink),
|
||||
V4L2_BUF_TYPE_VIDEO_OUTPUT, DEFAULT_PROP_DEVICE,
|
||||
gst_v4l2_get_input, gst_v4l2_set_input, NULL);
|
||||
|
||||
/* same default value for video output device as is used for
|
||||
* v4l2src/capture is no good.. so lets set a saner default
|
||||
* (which can be overridden by the one creating the v4l2sink
|
||||
* after the constructor returns)
|
||||
*/
|
||||
g_object_set (v4l2sink, "device", "/dev/video1", NULL);
|
||||
|
||||
/* number of buffers requested */
|
||||
v4l2sink->num_buffers = PROP_DEF_QUEUE_SIZE;
|
||||
|
||||
v4l2sink->probed_caps = NULL;
|
||||
v4l2sink->current_caps = NULL;
|
||||
|
||||
v4l2sink->overlay_fields_set = 0;
|
||||
v4l2sink->state = 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_v4l2sink_dispose (GObject * object)
|
||||
{
|
||||
GstV4l2Sink *v4l2sink = GST_V4L2SINK (object);
|
||||
|
||||
if (v4l2sink->probed_caps) {
|
||||
gst_caps_unref (v4l2sink->probed_caps);
|
||||
}
|
||||
|
||||
if (v4l2sink->current_caps) {
|
||||
gst_caps_unref (v4l2sink->current_caps);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_v4l2sink_finalize (GstV4l2Sink * v4l2sink)
|
||||
{
|
||||
gst_v4l2_object_destroy (v4l2sink->v4l2object);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (v4l2sink));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* State values
|
||||
*/
|
||||
enum
|
||||
{
|
||||
STATE_OFF = 0,
|
||||
STATE_PENDING_STREAMON,
|
||||
STATE_STREAMING
|
||||
};
|
||||
|
||||
/**
|
||||
* flags to indicate which overlay properties the user has set (and therefore
|
||||
* which ones should override the defaults from the driver)
|
||||
*/
|
||||
enum
|
||||
{
|
||||
OVERLAY_TOP_SET = 0x01,
|
||||
OVERLAY_LEFT_SET = 0x02,
|
||||
OVERLAY_WIDTH_SET = 0x04,
|
||||
OVERLAY_HEIGHT_SET = 0x08
|
||||
};
|
||||
|
||||
static void
|
||||
gst_v4l2sink_sync_overlay_fields (GstV4l2Sink * v4l2sink)
|
||||
{
|
||||
if (GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
|
||||
|
||||
gint fd = v4l2sink->v4l2object->video_fd;
|
||||
struct v4l2_format format;
|
||||
|
||||
memset (&format, 0x00, sizeof (struct v4l2_format));
|
||||
format.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
|
||||
|
||||
g_return_if_fail (v4l2_ioctl (fd, VIDIOC_G_FMT, &format) >= 0);
|
||||
|
||||
if (v4l2sink->overlay_fields_set) {
|
||||
if (v4l2sink->overlay_fields_set & OVERLAY_TOP_SET)
|
||||
format.fmt.win.w.top = v4l2sink->overlay.top;
|
||||
if (v4l2sink->overlay_fields_set & OVERLAY_LEFT_SET)
|
||||
format.fmt.win.w.left = v4l2sink->overlay.left;
|
||||
if (v4l2sink->overlay_fields_set & OVERLAY_WIDTH_SET)
|
||||
format.fmt.win.w.width = v4l2sink->overlay.width;
|
||||
if (v4l2sink->overlay_fields_set & OVERLAY_HEIGHT_SET)
|
||||
format.fmt.win.w.height = v4l2sink->overlay.height;
|
||||
|
||||
g_return_if_fail (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) >= 0);
|
||||
v4l2sink->overlay_fields_set = 0;
|
||||
}
|
||||
|
||||
v4l2sink->overlay = format.fmt.win.w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_v4l2sink_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstV4l2Sink *v4l2sink = GST_V4L2SINK (object);
|
||||
|
||||
if (!gst_v4l2_object_set_property_helper (v4l2sink->v4l2object,
|
||||
prop_id, value, pspec)) {
|
||||
switch (prop_id) {
|
||||
case PROP_QUEUE_SIZE:
|
||||
v4l2sink->num_buffers = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_OVERLAY_TOP:
|
||||
v4l2sink->overlay.top = g_value_get_int (value);
|
||||
v4l2sink->overlay_fields_set |= OVERLAY_TOP_SET;
|
||||
gst_v4l2sink_sync_overlay_fields (v4l2sink);
|
||||
break;
|
||||
case PROP_OVERLAY_LEFT:
|
||||
v4l2sink->overlay.left = g_value_get_int (value);
|
||||
v4l2sink->overlay_fields_set |= OVERLAY_LEFT_SET;
|
||||
gst_v4l2sink_sync_overlay_fields (v4l2sink);
|
||||
break;
|
||||
case PROP_OVERLAY_WIDTH:
|
||||
v4l2sink->overlay.width = g_value_get_uint (value);
|
||||
v4l2sink->overlay_fields_set |= OVERLAY_WIDTH_SET;
|
||||
gst_v4l2sink_sync_overlay_fields (v4l2sink);
|
||||
break;
|
||||
case PROP_OVERLAY_HEIGHT:
|
||||
v4l2sink->overlay.height = g_value_get_uint (value);
|
||||
v4l2sink->overlay_fields_set |= OVERLAY_HEIGHT_SET;
|
||||
gst_v4l2sink_sync_overlay_fields (v4l2sink);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_v4l2sink_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstV4l2Sink *v4l2sink = GST_V4L2SINK (object);
|
||||
|
||||
if (!gst_v4l2_object_get_property_helper (v4l2sink->v4l2object,
|
||||
prop_id, value, pspec)) {
|
||||
switch (prop_id) {
|
||||
case PROP_QUEUE_SIZE:
|
||||
g_value_set_uint (value, v4l2sink->num_buffers);
|
||||
break;
|
||||
case PROP_OVERLAY_TOP:
|
||||
g_value_set_int (value, v4l2sink->overlay.top);
|
||||
break;
|
||||
case PROP_OVERLAY_LEFT:
|
||||
g_value_set_int (value, v4l2sink->overlay.left);
|
||||
break;
|
||||
case PROP_OVERLAY_WIDTH:
|
||||
g_value_set_uint (value, v4l2sink->overlay.width);
|
||||
break;
|
||||
case PROP_OVERLAY_HEIGHT:
|
||||
g_value_set_uint (value, v4l2sink->overlay.height);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_v4l2sink_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||
GstV4l2Sink *v4l2sink = GST_V4L2SINK (element);
|
||||
|
||||
GST_DEBUG_OBJECT (v4l2sink, "%d -> %d",
|
||||
GST_STATE_TRANSITION_CURRENT (transition),
|
||||
GST_STATE_TRANSITION_NEXT (transition));
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
/* open the device */
|
||||
if (!gst_v4l2_object_start (v4l2sink->v4l2object))
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
if (v4l2sink->state == STATE_STREAMING) {
|
||||
if (!gst_v4l2_object_stop_streaming (v4l2sink->v4l2object)) {
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
v4l2sink->state = STATE_OFF;
|
||||
}
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||
/* close the device */
|
||||
if (!gst_v4l2_object_stop (v4l2sink->v4l2object))
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static GstCaps *
|
||||
gst_v4l2sink_get_caps (GstBaseSink * bsink)
|
||||
{
|
||||
GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
|
||||
GstCaps *ret;
|
||||
GSList *walk;
|
||||
GSList *formats;
|
||||
|
||||
if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
|
||||
/* FIXME: copy? */
|
||||
GST_DEBUG_OBJECT (v4l2sink, "device is not open");
|
||||
return
|
||||
gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
|
||||
(v4l2sink)));
|
||||
}
|
||||
|
||||
if (v4l2sink->probed_caps) {
|
||||
LOG_CAPS (v4l2sink, v4l2sink->probed_caps);
|
||||
return gst_caps_ref (v4l2sink->probed_caps);
|
||||
}
|
||||
|
||||
formats = gst_v4l2_object_get_format_list (v4l2sink->v4l2object);
|
||||
|
||||
ret = gst_caps_new_empty ();
|
||||
|
||||
for (walk = v4l2sink->v4l2object->formats; walk; walk = walk->next) {
|
||||
struct v4l2_fmtdesc *format;
|
||||
|
||||
GstStructure *template;
|
||||
|
||||
format = (struct v4l2_fmtdesc *) walk->data;
|
||||
|
||||
template = gst_v4l2_object_v4l2fourcc_to_structure (format->pixelformat);
|
||||
|
||||
if (template) {
|
||||
GstCaps *tmp;
|
||||
|
||||
tmp =
|
||||
gst_v4l2_object_probe_caps_for_format (v4l2sink->v4l2object,
|
||||
format->pixelformat, template);
|
||||
if (tmp)
|
||||
gst_caps_append (ret, tmp);
|
||||
|
||||
gst_structure_free (template);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (v4l2sink, "unknown format %u", format->pixelformat);
|
||||
}
|
||||
}
|
||||
|
||||
v4l2sink->probed_caps = gst_caps_ref (ret);
|
||||
|
||||
GST_INFO_OBJECT (v4l2sink, "probed caps: %p", ret);
|
||||
LOG_CAPS (v4l2sink, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
||||
{
|
||||
GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
|
||||
gint w = 0, h = 0;
|
||||
struct v4l2_fmtdesc *format;
|
||||
guint fps_n, fps_d;
|
||||
guint size;
|
||||
|
||||
LOG_CAPS (v4l2sink, caps);
|
||||
|
||||
if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
|
||||
GST_DEBUG_OBJECT (v4l2sink, "device is not open");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (v4l2sink->current_caps) {
|
||||
GST_DEBUG_OBJECT (v4l2sink, "already have caps set.. are they equal?");
|
||||
LOG_CAPS (v4l2sink, v4l2sink->current_caps);
|
||||
if (gst_caps_is_equal (v4l2sink->current_caps, caps)) {
|
||||
GST_DEBUG_OBJECT (v4l2sink, "yes they are!");
|
||||
return TRUE;
|
||||
}
|
||||
GST_DEBUG_OBJECT (v4l2sink, "no they aren't!");
|
||||
}
|
||||
|
||||
if (v4l2sink->pool) {
|
||||
/* TODO: if we've already allocated buffers, we probably need to
|
||||
* do something here to free and reallocate....
|
||||
*
|
||||
* gst_v4l2_object_stop_streaming()
|
||||
* gst_v4l2_buffer_pool_destroy()
|
||||
*
|
||||
*/
|
||||
GST_DEBUG_OBJECT (v4l2sink, "warning, changing caps not supported yet");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* we want our own v4l2 type of fourcc codes */
|
||||
if (!gst_v4l2_object_get_caps_info (v4l2sink->v4l2object, caps,
|
||||
&format, &w, &h, &fps_n, &fps_d, &size)) {
|
||||
GST_DEBUG_OBJECT (v4l2sink, "can't get capture format from caps %p", caps);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!format) {
|
||||
GST_DEBUG_OBJECT (v4l2sink, "unrecognized caps!!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, format->pixelformat, w,
|
||||
h)) {
|
||||
/* error already posted */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_v4l2sink_sync_overlay_fields (v4l2sink);
|
||||
|
||||
v4l2sink->current_caps = gst_caps_ref (caps);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/** buffer alloc function to implement pad_alloc for upstream element */
|
||||
static GstFlowReturn
|
||||
gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
|
||||
GstCaps * caps, GstBuffer ** buf)
|
||||
{
|
||||
GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
|
||||
GstV4l2Buffer *v4l2buf;
|
||||
|
||||
if (v4l2sink->v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) {
|
||||
|
||||
/* initialize the buffer pool if not initialized yet (first buffer): */
|
||||
if (G_UNLIKELY (!v4l2sink->pool)) {
|
||||
|
||||
/* set_caps() might not be called yet.. so just to make sure: */
|
||||
if (!gst_v4l2sink_set_caps (bsink, caps)) {
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
GST_V4L2_CHECK_OPEN (v4l2sink->v4l2object);
|
||||
|
||||
if (!(v4l2sink->pool = gst_v4l2_buffer_pool_new (GST_ELEMENT (v4l2sink),
|
||||
v4l2sink->v4l2object->video_fd,
|
||||
v4l2sink->num_buffers, caps, FALSE,
|
||||
V4L2_BUF_TYPE_VIDEO_OUTPUT))) {
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
#ifdef OMAPZOOM
|
||||
if (!gst_v4l2_object_start_streaming (v4l2sink->v4l2object)) {
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
v4l2sink->state = STATE_STREAMING;
|
||||
#else
|
||||
v4l2sink->state = STATE_PENDING_STREAMON;
|
||||
#endif
|
||||
|
||||
GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()");
|
||||
|
||||
if (v4l2sink->num_buffers != v4l2sink->pool->buffer_count) {
|
||||
v4l2sink->num_buffers = v4l2sink->pool->buffer_count;
|
||||
g_object_notify (G_OBJECT (v4l2sink), "queue-size");
|
||||
}
|
||||
}
|
||||
|
||||
v4l2buf = gst_v4l2_buffer_pool_get (v4l2sink->pool, TRUE);
|
||||
|
||||
GST_DEBUG_OBJECT (v4l2sink, "allocated buffer: %p\n", v4l2buf);
|
||||
|
||||
if (G_UNLIKELY (!v4l2buf)) {
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
*buf = GST_BUFFER (v4l2buf);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
} else {
|
||||
GST_ERROR_OBJECT (v4l2sink, "only supporting streaming mode for now...");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/** called after A/V sync to render frame */
|
||||
static GstFlowReturn
|
||||
gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
|
||||
{
|
||||
GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
|
||||
GstBuffer *newbuf = NULL;
|
||||
|
||||
GST_DEBUG_OBJECT (v4l2sink, "render buffer: %p\n", buf);
|
||||
|
||||
if (!GST_IS_V4L2_BUFFER (buf)) {
|
||||
GstFlowReturn ret;
|
||||
|
||||
GST_DEBUG_OBJECT (v4l2sink, "slow-path.. I got a %s so I need to memcpy",
|
||||
g_type_name (G_OBJECT_TYPE (buf)));
|
||||
|
||||
ret = gst_v4l2sink_buffer_alloc (bsink,
|
||||
GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf), GST_BUFFER_CAPS (buf),
|
||||
&newbuf);
|
||||
|
||||
if (GST_FLOW_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy (GST_BUFFER_DATA (newbuf),
|
||||
GST_BUFFER_DATA (buf),
|
||||
MIN (GST_BUFFER_SIZE (newbuf), GST_BUFFER_SIZE (buf)));
|
||||
|
||||
GST_DEBUG_OBJECT (v4l2sink, "render copied buffer: %p\n", newbuf);
|
||||
|
||||
buf = newbuf;
|
||||
}
|
||||
|
||||
if (!gst_v4l2_buffer_pool_qbuf (v4l2sink->pool, GST_V4L2_BUFFER (buf))) {
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
#ifndef OMAPZOOM
|
||||
if (v4l2sink->state == STATE_PENDING_STREAMON) {
|
||||
if (!gst_v4l2_object_start_streaming (v4l2sink->v4l2object)) {
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
v4l2sink->state = STATE_STREAMING;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!newbuf) {
|
||||
gst_buffer_ref (buf);
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
88
sys/v4l2/gstv4l2sink.h
Normal file
88
sys/v4l2/gstv4l2sink.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2009 Texas Instruments, Inc - http://www.ti.com/
|
||||
*
|
||||
* Description: V4L2 sink element
|
||||
* Created on: Jul 2, 2009
|
||||
* Author: Rob Clark <rob@ti.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GSTV4L2SINK_H__
|
||||
#define __GSTV4L2SINK_H__
|
||||
|
||||
#include <gst/video/gstvideosink.h>
|
||||
#include <gstv4l2object.h>
|
||||
#include <gstv4l2bufferpool.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (v4l2sink_debug);
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_V4L2SINK \
|
||||
(gst_v4l2sink_get_type())
|
||||
#define GST_V4L2SINK(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_V4L2SINK, GstV4l2Sink))
|
||||
#define GST_V4L2SINK_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_V4L2SINK, GstV4l2SinkClass))
|
||||
#define GST_IS_V4L2SINK(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_V4L2SINK))
|
||||
#define GST_IS_V4L2SINK_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_V4L2SINK))
|
||||
|
||||
typedef struct _GstV4l2Sink GstV4l2Sink;
|
||||
typedef struct _GstV4l2SinkClass GstV4l2SinkClass;
|
||||
|
||||
|
||||
struct _GstV4l2Sink {
|
||||
GstVideoSink videosink;
|
||||
|
||||
/*< private >*/
|
||||
GstV4l2Object * v4l2object;
|
||||
GstCaps *probed_caps; /* all supported caps of underlying v4l2 device */
|
||||
GstCaps *current_caps; /* the current negotiated caps */
|
||||
GstV4l2BufferPool *pool;
|
||||
guint32 num_buffers;
|
||||
|
||||
/**
|
||||
* field to store requested overlay-top/left/width/height props:
|
||||
* note, could maybe be combined with 'vwin' field in GstV4l2Object?
|
||||
*/
|
||||
struct v4l2_rect overlay;
|
||||
|
||||
/**
|
||||
* bitmask to track which 'overlay' fields user has requested by
|
||||
* setting properties:
|
||||
*/
|
||||
guint8 overlay_fields_set;
|
||||
|
||||
guint8 state;
|
||||
};
|
||||
|
||||
struct _GstV4l2SinkClass {
|
||||
GstVideoSinkClass parent_class;
|
||||
|
||||
GList *v4l2_class_devices;
|
||||
};
|
||||
|
||||
GType gst_v4l2sink_get_type(void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GSTV4L2SINK_H__ */
|
|
@ -56,6 +56,8 @@
|
|||
#endif
|
||||
#include "gstv4l2vidorient.h"
|
||||
|
||||
#include "gst/gst-i18n-plugin.h"
|
||||
|
||||
static const GstElementDetails gst_v4l2src_details =
|
||||
GST_ELEMENT_DETAILS ("Video (video4linux2) Source",
|
||||
"Source/Video",
|
||||
|
@ -70,6 +72,8 @@ GST_DEBUG_CATEGORY (v4l2src_debug);
|
|||
#define PROP_DEF_QUEUE_SIZE 2
|
||||
#define PROP_DEF_ALWAYS_COPY TRUE
|
||||
|
||||
#define DEFAULT_PROP_DEVICE "/dev/video0"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
@ -78,66 +82,6 @@ enum
|
|||
PROP_ALWAYS_COPY
|
||||
};
|
||||
|
||||
static const guint32 gst_v4l2_formats[] = {
|
||||
/* from Linux 2.6.15 videodev2.h */
|
||||
V4L2_PIX_FMT_RGB332,
|
||||
V4L2_PIX_FMT_RGB555,
|
||||
V4L2_PIX_FMT_RGB565,
|
||||
V4L2_PIX_FMT_RGB555X,
|
||||
V4L2_PIX_FMT_RGB565X,
|
||||
V4L2_PIX_FMT_BGR24,
|
||||
V4L2_PIX_FMT_RGB24,
|
||||
V4L2_PIX_FMT_BGR32,
|
||||
V4L2_PIX_FMT_RGB32,
|
||||
V4L2_PIX_FMT_GREY,
|
||||
V4L2_PIX_FMT_YVU410,
|
||||
V4L2_PIX_FMT_YVU420,
|
||||
V4L2_PIX_FMT_YUYV,
|
||||
V4L2_PIX_FMT_UYVY,
|
||||
V4L2_PIX_FMT_YUV422P,
|
||||
V4L2_PIX_FMT_YUV411P,
|
||||
V4L2_PIX_FMT_Y41P,
|
||||
|
||||
/* two planes -- one Y, one Cr + Cb interleaved */
|
||||
V4L2_PIX_FMT_NV12,
|
||||
V4L2_PIX_FMT_NV21,
|
||||
|
||||
/* The following formats are not defined in the V4L2 specification */
|
||||
V4L2_PIX_FMT_YUV410,
|
||||
V4L2_PIX_FMT_YUV420,
|
||||
V4L2_PIX_FMT_YYUV,
|
||||
V4L2_PIX_FMT_HI240,
|
||||
|
||||
/* see http://www.siliconimaging.com/RGB%20Bayer.htm */
|
||||
#ifdef V4L2_PIX_FMT_SBGGR8
|
||||
V4L2_PIX_FMT_SBGGR8,
|
||||
#endif
|
||||
|
||||
/* compressed formats */
|
||||
V4L2_PIX_FMT_MJPEG,
|
||||
V4L2_PIX_FMT_JPEG,
|
||||
V4L2_PIX_FMT_DV,
|
||||
V4L2_PIX_FMT_MPEG,
|
||||
|
||||
/* Vendor-specific formats */
|
||||
V4L2_PIX_FMT_WNVA,
|
||||
|
||||
#ifdef V4L2_PIX_FMT_SN9C10X
|
||||
V4L2_PIX_FMT_SN9C10X,
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_PWC1
|
||||
V4L2_PIX_FMT_PWC1,
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_PWC2
|
||||
V4L2_PIX_FMT_PWC2,
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_YVYU
|
||||
V4L2_PIX_FMT_YVYU,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define GST_V4L2_FORMAT_COUNT (G_N_ELEMENTS (gst_v4l2_formats))
|
||||
|
||||
GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2SrcClass, gst_v4l2src);
|
||||
GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Src, gst_v4l2src);
|
||||
GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Src, gst_v4l2src);
|
||||
|
@ -259,8 +203,6 @@ static void gst_v4l2src_set_property (GObject * object, guint prop_id,
|
|||
static void gst_v4l2src_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstCaps *gst_v4l2src_get_all_caps (void);
|
||||
|
||||
static void
|
||||
gst_v4l2src_base_init (gpointer g_class)
|
||||
{
|
||||
|
@ -276,7 +218,7 @@ gst_v4l2src_base_init (gpointer g_class)
|
|||
gst_element_class_add_pad_template
|
||||
(gstelement_class,
|
||||
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
|
||||
gst_v4l2src_get_all_caps ()));
|
||||
gst_v4l2_object_get_all_caps ()));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -299,7 +241,8 @@ gst_v4l2src_class_init (GstV4l2SrcClass * klass)
|
|||
|
||||
element_class->change_state = gst_v4l2src_change_state;
|
||||
|
||||
gst_v4l2_object_install_properties_helper (gobject_class);
|
||||
gst_v4l2_object_install_properties_helper (gobject_class,
|
||||
DEFAULT_PROP_DEVICE);
|
||||
g_object_class_install_property (gobject_class, PROP_QUEUE_SIZE,
|
||||
g_param_spec_uint ("queue-size", "Queue size",
|
||||
"Number of buffers to be enqueud in the driver in streaming mode",
|
||||
|
@ -328,6 +271,7 @@ gst_v4l2src_init (GstV4l2Src * v4l2src, GstV4l2SrcClass * klass)
|
|||
{
|
||||
/* fixme: give an update_fps_function */
|
||||
v4l2src->v4l2object = gst_v4l2_object_new (GST_ELEMENT (v4l2src),
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE, DEFAULT_PROP_DEVICE,
|
||||
gst_v4l2_get_input, gst_v4l2_set_input, NULL);
|
||||
|
||||
/* number of buffers requested */
|
||||
|
@ -335,8 +279,6 @@ gst_v4l2src_init (GstV4l2Src * v4l2src, GstV4l2SrcClass * klass)
|
|||
|
||||
v4l2src->always_copy = PROP_DEF_ALWAYS_COPY;
|
||||
|
||||
v4l2src->formats = NULL;
|
||||
|
||||
v4l2src->is_capturing = FALSE;
|
||||
|
||||
gst_base_src_set_format (GST_BASE_SRC (v4l2src), GST_FORMAT_TIME);
|
||||
|
@ -352,10 +294,6 @@ gst_v4l2src_dispose (GObject * object)
|
|||
{
|
||||
GstV4l2Src *v4l2src = GST_V4L2SRC (object);
|
||||
|
||||
if (v4l2src->formats) {
|
||||
gst_v4l2src_clear_format_list (v4l2src);
|
||||
}
|
||||
|
||||
if (v4l2src->probed_caps) {
|
||||
gst_caps_unref (v4l2src->probed_caps);
|
||||
}
|
||||
|
@ -470,6 +408,8 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc)
|
|||
/* first see what is possible on our source pad */
|
||||
thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc));
|
||||
GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
|
||||
LOG_CAPS (basesrc, thiscaps);
|
||||
|
||||
/* nothing or anything is allowed, we're done */
|
||||
if (thiscaps == NULL || gst_caps_is_any (thiscaps))
|
||||
goto no_nego_needed;
|
||||
|
@ -477,6 +417,7 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc)
|
|||
/* get the peer caps */
|
||||
peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc));
|
||||
GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
|
||||
LOG_CAPS (basesrc, peercaps);
|
||||
if (peercaps && !gst_caps_is_any (peercaps)) {
|
||||
GstCaps *icaps = NULL;
|
||||
int i;
|
||||
|
@ -487,6 +428,7 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc)
|
|||
GstCaps *ipcaps = gst_caps_copy_nth (peercaps, i);
|
||||
|
||||
GST_DEBUG_OBJECT (basesrc, "peer: %" GST_PTR_FORMAT, ipcaps);
|
||||
LOG_CAPS (basesrc, ipcaps);
|
||||
|
||||
icaps = gst_caps_intersect (thiscaps, ipcaps);
|
||||
gst_caps_unref (ipcaps);
|
||||
|
@ -499,6 +441,7 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc)
|
|||
}
|
||||
|
||||
GST_DEBUG_OBJECT (basesrc, "intersect: %" GST_PTR_FORMAT, icaps);
|
||||
LOG_CAPS (basesrc, icaps);
|
||||
if (icaps) {
|
||||
/* If there are multiple intersections pick the one with the smallest
|
||||
* resolution strictly bigger then the first peer caps */
|
||||
|
@ -553,6 +496,7 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc)
|
|||
if (!gst_caps_is_empty (caps)) {
|
||||
gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
|
||||
GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);
|
||||
LOG_CAPS (basesrc, caps);
|
||||
|
||||
if (gst_caps_is_any (caps)) {
|
||||
/* hmm, still anything, so element can do anything and
|
||||
|
@ -577,268 +521,13 @@ no_nego_needed:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static GstStructure *
|
||||
gst_v4l2src_v4l2fourcc_to_structure (guint32 fourcc)
|
||||
{
|
||||
GstStructure *structure = NULL;
|
||||
|
||||
switch (fourcc) {
|
||||
case V4L2_PIX_FMT_MJPEG: /* Motion-JPEG */
|
||||
case V4L2_PIX_FMT_JPEG: /* JFIF JPEG */
|
||||
structure = gst_structure_new ("image/jpeg", NULL);
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB332:
|
||||
case V4L2_PIX_FMT_RGB555:
|
||||
case V4L2_PIX_FMT_RGB555X:
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
case V4L2_PIX_FMT_RGB565X:
|
||||
case V4L2_PIX_FMT_RGB24:
|
||||
case V4L2_PIX_FMT_BGR24:
|
||||
case V4L2_PIX_FMT_RGB32:
|
||||
case V4L2_PIX_FMT_BGR32:{
|
||||
guint depth = 0, bpp = 0;
|
||||
|
||||
gint endianness = 0;
|
||||
|
||||
guint32 r_mask = 0, b_mask = 0, g_mask = 0;
|
||||
|
||||
switch (fourcc) {
|
||||
case V4L2_PIX_FMT_RGB332:
|
||||
bpp = depth = 8;
|
||||
endianness = G_BYTE_ORDER; /* 'like, whatever' */
|
||||
r_mask = 0xe0;
|
||||
g_mask = 0x1c;
|
||||
b_mask = 0x03;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB555:
|
||||
case V4L2_PIX_FMT_RGB555X:
|
||||
bpp = 16;
|
||||
depth = 15;
|
||||
endianness =
|
||||
fourcc == V4L2_PIX_FMT_RGB555X ? G_BIG_ENDIAN : G_LITTLE_ENDIAN;
|
||||
r_mask = 0x7c00;
|
||||
g_mask = 0x03e0;
|
||||
b_mask = 0x001f;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
case V4L2_PIX_FMT_RGB565X:
|
||||
bpp = depth = 16;
|
||||
endianness =
|
||||
fourcc == V4L2_PIX_FMT_RGB565X ? G_BIG_ENDIAN : G_LITTLE_ENDIAN;
|
||||
r_mask = 0xf800;
|
||||
g_mask = 0x07e0;
|
||||
b_mask = 0x001f;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB24:
|
||||
bpp = depth = 24;
|
||||
endianness = G_BIG_ENDIAN;
|
||||
r_mask = 0xff0000;
|
||||
g_mask = 0x00ff00;
|
||||
b_mask = 0x0000ff;
|
||||
break;
|
||||
case V4L2_PIX_FMT_BGR24:
|
||||
bpp = depth = 24;
|
||||
endianness = G_BIG_ENDIAN;
|
||||
r_mask = 0x0000ff;
|
||||
g_mask = 0x00ff00;
|
||||
b_mask = 0xff0000;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB32:
|
||||
bpp = depth = 32;
|
||||
endianness = G_BIG_ENDIAN;
|
||||
r_mask = 0xff000000;
|
||||
g_mask = 0x00ff0000;
|
||||
b_mask = 0x0000ff00;
|
||||
break;
|
||||
case V4L2_PIX_FMT_BGR32:
|
||||
bpp = depth = 32;
|
||||
endianness = G_BIG_ENDIAN;
|
||||
r_mask = 0x000000ff;
|
||||
g_mask = 0x0000ff00;
|
||||
b_mask = 0x00ff0000;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
structure = gst_structure_new ("video/x-raw-rgb",
|
||||
"bpp", G_TYPE_INT, bpp,
|
||||
"depth", G_TYPE_INT, depth,
|
||||
"red_mask", G_TYPE_INT, r_mask,
|
||||
"green_mask", G_TYPE_INT, g_mask,
|
||||
"blue_mask", G_TYPE_INT, b_mask,
|
||||
"endianness", G_TYPE_INT, endianness, NULL);
|
||||
break;
|
||||
}
|
||||
case V4L2_PIX_FMT_GREY: /* 8 Greyscale */
|
||||
structure = gst_structure_new ("video/x-raw-gray",
|
||||
"bpp", G_TYPE_INT, 8, NULL);
|
||||
break;
|
||||
case V4L2_PIX_FMT_YYUV: /* 16 YUV 4:2:2 */
|
||||
case V4L2_PIX_FMT_HI240: /* 8 8-bit color */
|
||||
/* FIXME: get correct fourccs here */
|
||||
break;
|
||||
case V4L2_PIX_FMT_NV12: /* 12 Y/CbCr 4:2:0 */
|
||||
case V4L2_PIX_FMT_NV21: /* 12 Y/CrCb 4:2:0 */
|
||||
case V4L2_PIX_FMT_YVU410:
|
||||
case V4L2_PIX_FMT_YUV410:
|
||||
case V4L2_PIX_FMT_YUV420: /* I420/IYUV */
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_YVU420:
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
case V4L2_PIX_FMT_Y41P:
|
||||
case V4L2_PIX_FMT_YUV422P:
|
||||
#ifdef V4L2_PIX_FMT_YVYU
|
||||
case V4L2_PIX_FMT_YVYU:
|
||||
#endif
|
||||
case V4L2_PIX_FMT_YUV411P:{
|
||||
guint32 fcc = 0;
|
||||
|
||||
switch (fourcc) {
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
fcc = GST_MAKE_FOURCC ('N', 'V', '1', '2');
|
||||
break;
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
fcc = GST_MAKE_FOURCC ('N', 'V', '2', '1');
|
||||
break;
|
||||
case V4L2_PIX_FMT_YVU410:
|
||||
fcc = GST_MAKE_FOURCC ('Y', 'V', 'U', '9');
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUV410:
|
||||
fcc = GST_MAKE_FOURCC ('Y', 'U', 'V', '9');
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUV420:
|
||||
fcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
fcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
|
||||
break;
|
||||
case V4L2_PIX_FMT_YVU420:
|
||||
fcc = GST_MAKE_FOURCC ('Y', 'V', '1', '2');
|
||||
break;
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
fcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y');
|
||||
break;
|
||||
case V4L2_PIX_FMT_Y41P:
|
||||
fcc = GST_MAKE_FOURCC ('Y', '4', '1', 'P');
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUV411P:
|
||||
fcc = GST_MAKE_FOURCC ('Y', '4', '1', 'B');
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUV422P:
|
||||
fcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B');
|
||||
break;
|
||||
#ifdef V4L2_PIX_FMT_YVYU
|
||||
case V4L2_PIX_FMT_YVYU:
|
||||
fcc = GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U');
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
structure = gst_structure_new ("video/x-raw-yuv",
|
||||
"format", GST_TYPE_FOURCC, fcc, NULL);
|
||||
break;
|
||||
}
|
||||
case V4L2_PIX_FMT_DV:
|
||||
structure =
|
||||
gst_structure_new ("video/x-dv", "systemstream", G_TYPE_BOOLEAN, TRUE,
|
||||
NULL);
|
||||
break;
|
||||
case V4L2_PIX_FMT_MPEG: /* MPEG */
|
||||
/* someone figure out the MPEG format used... */
|
||||
break;
|
||||
case V4L2_PIX_FMT_WNVA: /* Winnov hw compres */
|
||||
break;
|
||||
#ifdef V4L2_PIX_FMT_SBGGR8
|
||||
case V4L2_PIX_FMT_SBGGR8:
|
||||
structure = gst_structure_new ("video/x-raw-bayer", NULL);
|
||||
break;
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_SN9C10X
|
||||
case V4L2_PIX_FMT_SN9C10X:
|
||||
structure = gst_structure_new ("video/x-sonix", NULL);
|
||||
break;
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_PWC1
|
||||
case V4L2_PIX_FMT_PWC1:
|
||||
structure = gst_structure_new ("video/x-pwc1", NULL);
|
||||
break;
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_PWC2
|
||||
case V4L2_PIX_FMT_PWC2:
|
||||
structure = gst_structure_new ("video/x-pwc2", NULL);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
GST_DEBUG ("Unknown fourcc 0x%08x %" GST_FOURCC_FORMAT,
|
||||
fourcc, GST_FOURCC_ARGS (fourcc));
|
||||
break;
|
||||
}
|
||||
|
||||
return structure;
|
||||
}
|
||||
|
||||
static struct v4l2_fmtdesc *
|
||||
gst_v4l2src_get_format_from_fourcc (GstV4l2Src * v4l2src, guint32 fourcc)
|
||||
{
|
||||
struct v4l2_fmtdesc *fmt;
|
||||
GSList *walk;
|
||||
|
||||
if (fourcc == 0)
|
||||
return NULL;
|
||||
|
||||
walk = v4l2src->formats;
|
||||
while (walk) {
|
||||
fmt = (struct v4l2_fmtdesc *) walk->data;
|
||||
if (fmt->pixelformat == fourcc)
|
||||
return fmt;
|
||||
/* special case for jpeg */
|
||||
if ((fmt->pixelformat == V4L2_PIX_FMT_MJPEG && fourcc == V4L2_PIX_FMT_JPEG)
|
||||
|| (fmt->pixelformat == V4L2_PIX_FMT_JPEG
|
||||
&& fourcc == V4L2_PIX_FMT_MJPEG)) {
|
||||
return fmt;
|
||||
}
|
||||
walk = g_slist_next (walk);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_v4l2src_get_all_caps (void)
|
||||
{
|
||||
static GstCaps *caps = NULL;
|
||||
|
||||
if (caps == NULL) {
|
||||
GstStructure *structure;
|
||||
|
||||
guint i;
|
||||
|
||||
caps = gst_caps_new_empty ();
|
||||
for (i = 0; i < GST_V4L2_FORMAT_COUNT; i++) {
|
||||
structure = gst_v4l2src_v4l2fourcc_to_structure (gst_v4l2_formats[i]);
|
||||
if (structure) {
|
||||
gst_structure_set (structure,
|
||||
"width", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE,
|
||||
"height", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE,
|
||||
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 100, 1, NULL);
|
||||
gst_caps_append_structure (caps, structure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_v4l2src_get_caps (GstBaseSrc * src)
|
||||
{
|
||||
GstV4l2Src *v4l2src = GST_V4L2SRC (src);
|
||||
GstCaps *ret;
|
||||
GSList *walk;
|
||||
GSList *formats;
|
||||
|
||||
if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) {
|
||||
/* FIXME: copy? */
|
||||
|
@ -850,25 +539,25 @@ gst_v4l2src_get_caps (GstBaseSrc * src)
|
|||
if (v4l2src->probed_caps)
|
||||
return gst_caps_ref (v4l2src->probed_caps);
|
||||
|
||||
if (!v4l2src->formats)
|
||||
gst_v4l2src_fill_format_list (v4l2src);
|
||||
formats = gst_v4l2_object_get_format_list (v4l2src->v4l2object);
|
||||
|
||||
ret = gst_caps_new_empty ();
|
||||
|
||||
for (walk = v4l2src->formats; walk; walk = walk->next) {
|
||||
for (walk = v4l2src->v4l2object->formats; walk; walk = walk->next) {
|
||||
struct v4l2_fmtdesc *format;
|
||||
|
||||
GstStructure *template;
|
||||
|
||||
format = (struct v4l2_fmtdesc *) walk->data;
|
||||
|
||||
template = gst_v4l2src_v4l2fourcc_to_structure (format->pixelformat);
|
||||
template = gst_v4l2_object_v4l2fourcc_to_structure (format->pixelformat);
|
||||
|
||||
if (template) {
|
||||
GstCaps *tmp;
|
||||
|
||||
tmp = gst_v4l2src_probe_caps_for_format (v4l2src, format->pixelformat,
|
||||
template);
|
||||
tmp =
|
||||
gst_v4l2_object_probe_caps_for_format (v4l2src->v4l2object,
|
||||
format->pixelformat, template);
|
||||
if (tmp)
|
||||
gst_caps_append (ret, tmp);
|
||||
|
||||
|
@ -885,158 +574,6 @@ gst_v4l2src_get_caps (GstBaseSrc * src)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* collect data for the given caps
|
||||
* @caps: given input caps
|
||||
* @format: location for the v4l format
|
||||
* @w/@h: location for width and height
|
||||
* @fps_n/@fps_d: location for framerate
|
||||
* @size: location for expected size of the frame or 0 if unknown
|
||||
*/
|
||||
static gboolean
|
||||
gst_v4l2_get_caps_info (GstV4l2Src * v4l2src, GstCaps * caps,
|
||||
struct v4l2_fmtdesc **format, gint * w, gint * h, guint * fps_n,
|
||||
guint * fps_d, guint * size)
|
||||
{
|
||||
GstStructure *structure;
|
||||
const GValue *framerate;
|
||||
guint32 fourcc;
|
||||
const gchar *mimetype;
|
||||
guint outsize;
|
||||
|
||||
/* default unknown values */
|
||||
fourcc = 0;
|
||||
outsize = 0;
|
||||
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
if (!gst_structure_get_int (structure, "width", w))
|
||||
return FALSE;
|
||||
|
||||
if (!gst_structure_get_int (structure, "height", h))
|
||||
return FALSE;
|
||||
|
||||
framerate = gst_structure_get_value (structure, "framerate");
|
||||
if (!framerate)
|
||||
return FALSE;
|
||||
|
||||
*fps_n = gst_value_get_fraction_numerator (framerate);
|
||||
*fps_d = gst_value_get_fraction_denominator (framerate);
|
||||
|
||||
mimetype = gst_structure_get_name (structure);
|
||||
|
||||
if (!strcmp (mimetype, "video/x-raw-yuv")) {
|
||||
gst_structure_get_fourcc (structure, "format", &fourcc);
|
||||
|
||||
switch (fourcc) {
|
||||
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
|
||||
case GST_MAKE_FOURCC ('I', 'Y', 'U', 'V'):
|
||||
fourcc = V4L2_PIX_FMT_YUV420;
|
||||
outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h);
|
||||
outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * (GST_ROUND_UP_2 (*h) / 2));
|
||||
break;
|
||||
case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
|
||||
fourcc = V4L2_PIX_FMT_YUYV;
|
||||
outsize = (GST_ROUND_UP_2 (*w) * 2) * *h;
|
||||
break;
|
||||
case GST_MAKE_FOURCC ('Y', '4', '1', 'P'):
|
||||
fourcc = V4L2_PIX_FMT_Y41P;
|
||||
outsize = (GST_ROUND_UP_2 (*w) * 2) * *h;
|
||||
break;
|
||||
case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
|
||||
fourcc = V4L2_PIX_FMT_UYVY;
|
||||
outsize = (GST_ROUND_UP_2 (*w) * 2) * *h;
|
||||
break;
|
||||
case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
|
||||
fourcc = V4L2_PIX_FMT_YVU420;
|
||||
outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h);
|
||||
outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * (GST_ROUND_UP_2 (*h) / 2));
|
||||
break;
|
||||
case GST_MAKE_FOURCC ('Y', '4', '1', 'B'):
|
||||
fourcc = V4L2_PIX_FMT_YUV411P;
|
||||
outsize = GST_ROUND_UP_4 (*w) * *h;
|
||||
outsize += 2 * ((GST_ROUND_UP_8 (*w) / 4) * *h);
|
||||
break;
|
||||
case GST_MAKE_FOURCC ('Y', '4', '2', 'B'):
|
||||
fourcc = V4L2_PIX_FMT_YUV422P;
|
||||
outsize = GST_ROUND_UP_4 (*w) * *h;
|
||||
outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * *h);
|
||||
break;
|
||||
case GST_MAKE_FOURCC ('N', 'V', '1', '2'):
|
||||
fourcc = V4L2_PIX_FMT_NV12;
|
||||
outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h);
|
||||
outsize += (GST_ROUND_UP_4 (*w) * *h) / 2;
|
||||
break;
|
||||
case GST_MAKE_FOURCC ('N', 'V', '2', '1'):
|
||||
fourcc = V4L2_PIX_FMT_NV21;
|
||||
outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h);
|
||||
outsize += (GST_ROUND_UP_4 (*w) * *h) / 2;
|
||||
break;
|
||||
#ifdef V4L2_PIX_FMT_YVYU
|
||||
case GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'):
|
||||
fourcc = V4L2_PIX_FMT_YVYU;
|
||||
outsize = (GST_ROUND_UP_2 (*w) * 2) * *h;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
} else if (!strcmp (mimetype, "video/x-raw-rgb")) {
|
||||
gint depth, endianness, r_mask;
|
||||
|
||||
gst_structure_get_int (structure, "depth", &depth);
|
||||
gst_structure_get_int (structure, "endianness", &endianness);
|
||||
gst_structure_get_int (structure, "red_mask", &r_mask);
|
||||
|
||||
switch (depth) {
|
||||
case 8:
|
||||
fourcc = V4L2_PIX_FMT_RGB332;
|
||||
break;
|
||||
case 15:
|
||||
fourcc = (endianness == G_LITTLE_ENDIAN) ?
|
||||
V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB555X;
|
||||
break;
|
||||
case 16:
|
||||
fourcc = (endianness == G_LITTLE_ENDIAN) ?
|
||||
V4L2_PIX_FMT_RGB565 : V4L2_PIX_FMT_RGB565X;
|
||||
break;
|
||||
case 24:
|
||||
fourcc = (r_mask == 0xFF) ? V4L2_PIX_FMT_BGR24 : V4L2_PIX_FMT_RGB24;
|
||||
break;
|
||||
case 32:
|
||||
fourcc = (r_mask == 0xFF) ? V4L2_PIX_FMT_BGR32 : V4L2_PIX_FMT_RGB32;
|
||||
break;
|
||||
}
|
||||
} else if (strcmp (mimetype, "video/x-dv") == 0) {
|
||||
fourcc = V4L2_PIX_FMT_DV;
|
||||
} else if (strcmp (mimetype, "image/jpeg") == 0) {
|
||||
fourcc = V4L2_PIX_FMT_JPEG;
|
||||
#ifdef V4L2_PIX_FMT_SBGGR8
|
||||
} else if (strcmp (mimetype, "video/x-raw-bayer") == 0) {
|
||||
fourcc = V4L2_PIX_FMT_SBGGR8;
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_SN9C10X
|
||||
} else if (strcmp (mimetype, "video/x-sonix") == 0) {
|
||||
fourcc = V4L2_PIX_FMT_SN9C10X;
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_PWC1
|
||||
} else if (strcmp (mimetype, "video/x-pwc1") == 0) {
|
||||
fourcc = V4L2_PIX_FMT_PWC1;
|
||||
#endif
|
||||
#ifdef V4L2_PIX_FMT_PWC2
|
||||
} else if (strcmp (mimetype, "video/x-pwc2") == 0) {
|
||||
fourcc = V4L2_PIX_FMT_PWC2;
|
||||
#endif
|
||||
} else if (strcmp (mimetype, "video/x-raw-gray") == 0) {
|
||||
fourcc = V4L2_PIX_FMT_GREY;
|
||||
}
|
||||
|
||||
if (fourcc == 0)
|
||||
return FALSE;
|
||||
|
||||
*format = gst_v4l2src_get_format_from_fourcc (v4l2src, fourcc);
|
||||
*size = outsize;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps)
|
||||
{
|
||||
|
@ -1062,8 +599,8 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps)
|
|||
}
|
||||
|
||||
/* we want our own v4l2 type of fourcc codes */
|
||||
if (!gst_v4l2_get_caps_info (v4l2src, caps, &format, &w, &h, &fps_n, &fps_d,
|
||||
&size)) {
|
||||
if (!gst_v4l2_object_get_caps_info (v4l2src->v4l2object, caps, &format, &w,
|
||||
&h, &fps_n, &fps_d, &size)) {
|
||||
GST_DEBUG_OBJECT (v4l2src,
|
||||
"can't get capture format from caps %" GST_PTR_FORMAT, caps);
|
||||
return FALSE;
|
||||
|
@ -1346,7 +883,6 @@ static GstFlowReturn
|
|||
gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
|
||||
{
|
||||
GstV4l2Src *v4l2src = GST_V4L2SRC (src);
|
||||
|
||||
GstFlowReturn ret;
|
||||
|
||||
if (v4l2src->use_mmap) {
|
||||
|
|
|
@ -25,16 +25,10 @@
|
|||
#define __GST_V4L2SRC_H__
|
||||
|
||||
#include <gstv4l2object.h>
|
||||
#include <gstv4l2bufferpool.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (v4l2src_debug);
|
||||
|
||||
/* size of v4l2 buffer pool in streaming case */
|
||||
#define GST_V4L2_MAX_BUFFERS 16
|
||||
#define GST_V4L2_MIN_BUFFERS 1
|
||||
|
||||
/* max frame width/height */
|
||||
#define GST_V4L2_MAX_SIZE (1<<15) /* 2^15 == 32768 */
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_V4L2SRC \
|
||||
|
@ -48,34 +42,10 @@ G_BEGIN_DECLS
|
|||
#define GST_IS_V4L2SRC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2SRC))
|
||||
|
||||
typedef struct _GstV4l2BufferPool GstV4l2BufferPool;
|
||||
typedef struct _GstV4l2Buffer GstV4l2Buffer;
|
||||
typedef struct _GstV4l2Src GstV4l2Src;
|
||||
typedef struct _GstV4l2SrcClass GstV4l2SrcClass;
|
||||
|
||||
|
||||
/* global info */
|
||||
struct _GstV4l2BufferPool
|
||||
{
|
||||
GstMiniObject parent;
|
||||
|
||||
GMutex *lock;
|
||||
gboolean running; /* with lock */
|
||||
gint num_live_buffers; /* with lock */
|
||||
gint video_fd; /* a dup(2) of the v4l2object's video_fd */
|
||||
guint buffer_count;
|
||||
GstV4l2Buffer **buffers; /* with lock; buffers[n] is NULL that buffer has been
|
||||
* dequeued and pushed out */
|
||||
};
|
||||
|
||||
struct _GstV4l2Buffer {
|
||||
GstBuffer buffer;
|
||||
|
||||
struct v4l2_buffer vbuffer;
|
||||
|
||||
/* FIXME: have GstV4l2Src* instead, as this has GstV4l2BufferPool* */
|
||||
GstV4l2BufferPool *pool;
|
||||
};
|
||||
|
||||
/**
|
||||
* GstV4l2Src:
|
||||
|
@ -93,9 +63,6 @@ struct _GstV4l2Src
|
|||
/* pads */
|
||||
GstCaps *probed_caps;
|
||||
|
||||
/* internal lists */
|
||||
GSList *formats; /* list of available capture formats */
|
||||
|
||||
/* buffer handling */
|
||||
GstV4l2BufferPool *pool;
|
||||
|
||||
|
|
|
@ -46,6 +46,9 @@
|
|||
#include "gstv4l2colorbalance.h"
|
||||
|
||||
#include "gstv4l2src.h"
|
||||
#include "gstv4l2sink.h"
|
||||
|
||||
#include "gst/gst-i18n-plugin.h"
|
||||
|
||||
/* Those are ioctl calls */
|
||||
#ifndef V4L2_CID_HCENTER
|
||||
|
@ -449,10 +452,14 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
|
|||
goto error;
|
||||
|
||||
/* do we need to be a capture device? */
|
||||
if (GST_IS_V4L2SRC (v4l2object) &&
|
||||
if (GST_IS_V4L2SRC (v4l2object->element) &&
|
||||
!(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
|
||||
goto not_capture;
|
||||
|
||||
if (GST_IS_V4L2SINK (v4l2object->element) &&
|
||||
!(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT))
|
||||
goto not_output;
|
||||
|
||||
/* create enumerations, posts errors. */
|
||||
if (!gst_v4l2_fill_lists (v4l2object))
|
||||
goto error;
|
||||
|
@ -497,6 +504,14 @@ not_capture:
|
|||
("Capabilities: 0x%x", v4l2object->vcap.capabilities));
|
||||
goto error;
|
||||
}
|
||||
not_output:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND,
|
||||
(_("Device '%s' is not a output device."),
|
||||
v4l2object->videodev),
|
||||
("Capabilities: 0x%x", v4l2object->vcap.capabilities));
|
||||
goto error;
|
||||
}
|
||||
error:
|
||||
{
|
||||
if (GST_V4L2_IS_OPEN (v4l2object)) {
|
||||
|
|
|
@ -25,18 +25,20 @@
|
|||
#define __V4L2_CALLS_H__
|
||||
|
||||
#include "gstv4l2object.h"
|
||||
#include "gst/gst-i18n-plugin.h"
|
||||
|
||||
#ifdef HAVE_LIBV4L2
|
||||
#include <libv4l2.h>
|
||||
# include <libv4l2.h>
|
||||
#else
|
||||
#define v4l2_fd_open(fd, flags) (fd)
|
||||
#define v4l2_close close
|
||||
#define v4l2_dup dup
|
||||
#define v4l2_ioctl ioctl
|
||||
#define v4l2_read read
|
||||
#define v4l2_mmap mmap
|
||||
#define v4l2_munmap munmap
|
||||
# include <sys/ioctl.h>
|
||||
# include <linux/videodev.h>
|
||||
# include <linux/videodev2.h>
|
||||
# define v4l2_fd_open(fd, flags) (fd)
|
||||
# define v4l2_close close
|
||||
# define v4l2_dup dup
|
||||
# define v4l2_ioctl ioctl
|
||||
# define v4l2_read read
|
||||
# define v4l2_mmap mmap
|
||||
# define v4l2_munmap munmap
|
||||
#endif
|
||||
|
||||
/* simple check whether the device is open */
|
||||
|
@ -137,4 +139,43 @@ gboolean gst_v4l2_set_attribute (GstV4l2Object *v4l2object,
|
|||
|
||||
gboolean gst_v4l2_get_capabilities (GstV4l2Object * v4l2object);
|
||||
|
||||
|
||||
/* note: in case this is a build with TTIF logging, we can optimize slightly
|
||||
* and avoid the gst_caps_to_string() in case logging isn't enabled by using
|
||||
* the TTIF_TRACE_ARG_PROCESSOR feature of ttif_trace_fprintf():
|
||||
*/
|
||||
#ifdef GST_LOG_OVER_TTIF
|
||||
# define LOG_CAPS(obj, caps) G_STMT_START { \
|
||||
if (caps) { \
|
||||
static TTIF_TRACE_ARG_PROCESSOR proc = { \
|
||||
.convert = (char (*)(void *))gst_caps_to_string, \
|
||||
.free = (void (*)(char *))g_free \
|
||||
}; \
|
||||
GST_DEBUG_OBJECT (obj, "%s: %qs", #caps, &proc, (caps)); \
|
||||
} else { \
|
||||
GST_DEBUG_OBJECT (obj, "null"); \
|
||||
} \
|
||||
} G_STMT_END
|
||||
#else
|
||||
# define LOG_CAPS(obj, caps) G_STMT_START { \
|
||||
if (caps) { \
|
||||
gchar *capstr = gst_caps_to_string (caps); \
|
||||
GST_DEBUG_OBJECT (obj, "%s: %s", #caps, capstr); \
|
||||
g_free (capstr); \
|
||||
} else { \
|
||||
GST_DEBUG_OBJECT (obj, "null"); \
|
||||
} \
|
||||
} G_STMT_END
|
||||
#endif
|
||||
|
||||
/* note: the omapzoom kernel v4l2 display driver deviates from the v4l2 API
|
||||
* spec in a few areas. For example, we must always have one buffer with
|
||||
* the driver before STREAMON until after STREAMOFF. And some interfaces,
|
||||
* such as rotation (and mirroring?) are different.
|
||||
*
|
||||
* this is only a temporary hack, as we should switch to the new driver soon
|
||||
*/
|
||||
#define OMAPZOOM
|
||||
|
||||
|
||||
#endif /* __V4L2_CALLS_H__ */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -41,10 +41,5 @@ GstFlowReturn gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer **buf)
|
|||
gboolean gst_v4l2src_capture_stop (GstV4l2Src * v4l2src);
|
||||
gboolean gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src);
|
||||
|
||||
gboolean gst_v4l2src_fill_format_list (GstV4l2Src * v4l2src);
|
||||
gboolean gst_v4l2src_clear_format_list (GstV4l2Src * v4l2src);
|
||||
|
||||
GstCaps* gst_v4l2src_probe_caps_for_format (GstV4l2Src * v4l2src, guint32 pixelformat,
|
||||
const GstStructure *template);
|
||||
|
||||
#endif /* __V4L2SRC_CALLS_H__ */
|
||||
|
|
Loading…
Reference in a new issue