mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-10 17:35:59 +00:00
sys/v4l2/v4l2src_calls.c (gst_v4l2_buffer_finalize) (gst_v4l2_buffer_class_init, gst_v4l2_buffer_get_type)
Original commit message from CVS: 2007-06-12 Andy Wingo <wingo@pobox.com> * sys/v4l2/v4l2src_calls.c (gst_v4l2_buffer_finalize) (gst_v4l2_buffer_class_init, gst_v4l2_buffer_get_type) (gst_v4l2_buffer_new): Behave more like ximagesink's buffers, with finalization and resuscitation. No longer public. (gst_v4l2_buffer_pool_finalize, gst_v4l2_buffer_pool_init) (gst_v4l2_buffer_pool_class_init, gst_v4l2_buffer_pool_get_type) (gst_v4l2_buffer_pool_new, gst_v4l2_buffer_pool_activate) (gst_v4l2_buffer_pool_destroy): Make the pool follow common miniobject semantics, and be threadsafe. (gst_v4l2src_queue_frame): Remove this function, as we just call the ioctls directly in the two places where we queue buffers. (gst_v4l2src_grab_frame): Return a flowreturn and fill the buffer directly. (gst_v4l2src_capture_init): Use the new buffer_pool_new function to allocate the pool, which also preallocates the GstBuffers. (gst_v4l2src_capture_start): Call buffer_pool_activate instead of queueing the frames directly. * sys/v4l2/gstv4l2src.h (struct _GstV4l2BufferPool): Make this a real MiniObject instead of rolling our own refcounting and finalizing. Give it a lock. (struct _GstV4l2Buffer): Remove one intermediary object, having the buffers hold the struct v4l2_buffer directly. * sys/v4l2/gstv4l2src.c (gst_v4l2src_set_caps): Pass the caps to capture_init so that it can set them on the buffers that it will create. (gst_v4l2src_get_read): For better or for worse, include the timestamping and offsetting code here; really we should be using bufferalloc though. (gst_v4l2src_get_mmap): Just make grab_frame return one of our preallocated, mmap'd buffers.
This commit is contained in:
parent
e359a41517
commit
cde8c8bdc4
5 changed files with 437 additions and 309 deletions
35
ChangeLog
35
ChangeLog
|
@ -1,3 +1,38 @@
|
|||
2007-06-12 Andy Wingo <wingo@pobox.com>
|
||||
|
||||
* sys/v4l2/v4l2src_calls.c (gst_v4l2_buffer_finalize)
|
||||
(gst_v4l2_buffer_class_init, gst_v4l2_buffer_get_type)
|
||||
(gst_v4l2_buffer_new): Behave more like ximagesink's buffers, with
|
||||
finalization and resuscitation. No longer public.
|
||||
(gst_v4l2_buffer_pool_finalize, gst_v4l2_buffer_pool_init)
|
||||
(gst_v4l2_buffer_pool_class_init, gst_v4l2_buffer_pool_get_type)
|
||||
(gst_v4l2_buffer_pool_new, gst_v4l2_buffer_pool_activate)
|
||||
(gst_v4l2_buffer_pool_destroy): Make the pool follow common
|
||||
miniobject semantics, and be threadsafe.
|
||||
(gst_v4l2src_queue_frame): Remove this function, as we just call
|
||||
the ioctls directly in the two places where we queue buffers.
|
||||
(gst_v4l2src_grab_frame): Return a flowreturn and fill the buffer
|
||||
directly.
|
||||
(gst_v4l2src_capture_init): Use the new buffer_pool_new function
|
||||
to allocate the pool, which also preallocates the GstBuffers.
|
||||
(gst_v4l2src_capture_start): Call buffer_pool_activate instead of
|
||||
queueing the frames directly.
|
||||
|
||||
* sys/v4l2/gstv4l2src.h (struct _GstV4l2BufferPool): Make this a
|
||||
real MiniObject instead of rolling our own refcounting and
|
||||
finalizing. Give it a lock.
|
||||
(struct _GstV4l2Buffer): Remove one intermediary object, having
|
||||
the buffers hold the struct v4l2_buffer directly.
|
||||
|
||||
* sys/v4l2/gstv4l2src.c (gst_v4l2src_set_caps): Pass the caps to
|
||||
capture_init so that it can set them on the buffers that it will
|
||||
create.
|
||||
(gst_v4l2src_get_read): For better or for worse, include the
|
||||
timestamping and offsetting code here; really we should be using
|
||||
bufferalloc though.
|
||||
(gst_v4l2src_get_mmap): Just make grab_frame return one of our
|
||||
preallocated, mmap'd buffers.
|
||||
|
||||
2007-06-11 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
Patch by: daniel fischer <dan at f3c dot com>
|
||||
|
|
|
@ -817,7 +817,7 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps)
|
|||
/* error already posted */
|
||||
return FALSE;
|
||||
|
||||
if (!gst_v4l2src_capture_init (v4l2src))
|
||||
if (!gst_v4l2src_capture_init (v4l2src, caps))
|
||||
return FALSE;
|
||||
|
||||
if (!gst_v4l2src_capture_start (v4l2src))
|
||||
|
@ -873,7 +873,7 @@ gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
|||
|
||||
buffersize = v4l2src->frame_byte_size;
|
||||
|
||||
*buf = gst_v4l2src_buffer_new (v4l2src, buffersize, NULL, NULL);
|
||||
*buf = gst_buffer_new_and_alloc (buffersize);
|
||||
|
||||
do {
|
||||
amount =
|
||||
|
@ -892,6 +892,34 @@ gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
|||
}
|
||||
} while (TRUE);
|
||||
|
||||
GST_BUFFER_OFFSET (*buf) = v4l2src->offset++;
|
||||
GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset;
|
||||
/* timestamps, LOCK to get clock and base time. */
|
||||
{
|
||||
GstClock *clock;
|
||||
GstClockTime timestamp;
|
||||
|
||||
GST_OBJECT_LOCK (v4l2src);
|
||||
if ((clock = GST_ELEMENT_CLOCK (v4l2src))) {
|
||||
/* we have a clock, get base time and ref clock */
|
||||
timestamp = GST_ELEMENT (v4l2src)->base_time;
|
||||
gst_object_ref (clock);
|
||||
} else {
|
||||
/* no clock, can't set timestamps */
|
||||
timestamp = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (v4l2src);
|
||||
|
||||
if (clock) {
|
||||
/* the time now is the time of the clock minus the base time */
|
||||
timestamp = gst_clock_get_time (clock) - timestamp;
|
||||
gst_object_unref (clock);
|
||||
}
|
||||
|
||||
/* FIXME: use the timestamp from the buffer itself! */
|
||||
GST_BUFFER_TIMESTAMP (*buf) = timestamp;
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
/* ERRORS */
|
||||
|
@ -908,52 +936,7 @@ read_error:
|
|||
static GstFlowReturn
|
||||
gst_v4l2src_get_mmap (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
||||
{
|
||||
gint i, num;
|
||||
|
||||
/* grab a frame from the device, post an error */
|
||||
num = gst_v4l2src_grab_frame (v4l2src);
|
||||
if (num == -1)
|
||||
goto grab_failed;
|
||||
|
||||
i = v4l2src->frame_byte_size;
|
||||
|
||||
/* check if this is the last buffer in the queue. If so do a memcpy to put it back asap
|
||||
to avoid framedrops and deadlocks because of stupid elements */
|
||||
/* FIXME: we should use the userptr interface instead, will remove this
|
||||
problem */
|
||||
if (g_atomic_int_get (&v4l2src->pool->refcount) == v4l2src->num_buffers) {
|
||||
GST_LOG_OBJECT (v4l2src, "using memcpy'd buffer");
|
||||
|
||||
*buf = gst_v4l2src_buffer_new (v4l2src, i, NULL, NULL);
|
||||
memcpy (GST_BUFFER_DATA (*buf), v4l2src->pool->buffers[num].start, i);
|
||||
|
||||
/* posts an error message if something went wrong */
|
||||
if (!gst_v4l2src_queue_frame (v4l2src, num))
|
||||
goto queue_failed;
|
||||
} else {
|
||||
GST_LOG_OBJECT (v4l2src, "using mmap'd buffer");
|
||||
*buf =
|
||||
gst_v4l2src_buffer_new (v4l2src, i, v4l2src->pool->buffers[num].start,
|
||||
&v4l2src->pool->buffers[num]);
|
||||
|
||||
/* no need to be careful here, both are > 0, because the element uses them */
|
||||
g_atomic_int_inc (&v4l2src->pool->buffers[num].refcount);
|
||||
g_atomic_int_inc (&v4l2src->pool->refcount);
|
||||
}
|
||||
return GST_FLOW_OK;
|
||||
|
||||
/* ERRORS */
|
||||
grab_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (v4l2src, "failed to grab a frame");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
queue_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (v4l2src, "failed to queue frame");
|
||||
gst_buffer_unref (*buf);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
return gst_v4l2src_grab_frame (v4l2src, buf);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
|
|
|
@ -54,18 +54,22 @@ typedef struct _GstV4l2SrcClass GstV4l2SrcClass;
|
|||
/* global info */
|
||||
struct _GstV4l2BufferPool
|
||||
{
|
||||
gint refcount; /* number of users: 1 for every buffer, 1 for element */
|
||||
gint video_fd;
|
||||
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;
|
||||
GstV4l2Buffer **buffers; /* with lock; buffers[n] is NULL that buffer has been
|
||||
* dequeued and pushed out */
|
||||
};
|
||||
|
||||
struct _GstV4l2Buffer
|
||||
{
|
||||
struct v4l2_buffer buffer;
|
||||
guint8 *start;
|
||||
guint length;
|
||||
gint refcount; /* add 1 if in use by element, add 1 if in use by GstBuffer */
|
||||
struct _GstV4l2Buffer {
|
||||
GstBuffer buffer;
|
||||
|
||||
struct v4l2_buffer vbuffer;
|
||||
|
||||
GstV4l2BufferPool *pool;
|
||||
};
|
||||
|
||||
|
|
|
@ -56,6 +56,298 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2src_debug);
|
|||
#define MAP_FAILED ((caddr_t) -1)
|
||||
#endif
|
||||
|
||||
|
||||
#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))
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_finalize (GstV4l2Buffer * buffer)
|
||||
{
|
||||
GstV4l2BufferPool *pool;
|
||||
gboolean resuscitated = FALSE;
|
||||
|
||||
pool = buffer->pool;
|
||||
|
||||
GST_LOG ("finalizing buffer %d", buffer->vbuffer.index);
|
||||
|
||||
g_mutex_lock (pool->lock);
|
||||
if (GST_BUFFER_SIZE (buffer) != 0)
|
||||
/* BUFFER_SIZE is only set if the frame was dequeued */
|
||||
pool->num_live_buffers--;
|
||||
|
||||
if (pool->running) {
|
||||
if (ioctl (pool->video_fd, VIDIOC_QBUF, &buffer->vbuffer) < 0) {
|
||||
GST_WARNING ("could not requeue buffer %d", buffer->vbuffer.index);
|
||||
} else {
|
||||
/* FIXME: check that the caps didn't change */
|
||||
gst_buffer_ref (GST_BUFFER (buffer));
|
||||
GST_BUFFER_SIZE (buffer) = 0;
|
||||
pool->buffers[buffer->vbuffer.index] = buffer;
|
||||
resuscitated = TRUE;
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (pool->lock);
|
||||
|
||||
if (!resuscitated) {
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (pool));
|
||||
munmap (GST_BUFFER_DATA (buffer), buffer->vbuffer.length);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_init (GstV4l2Buffer * xvimage, gpointer g_class)
|
||||
{
|
||||
/* NOP */
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_class_init (gpointer g_class, gpointer class_data)
|
||||
{
|
||||
GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
|
||||
|
||||
mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
|
||||
gst_v4l2_buffer_finalize;
|
||||
}
|
||||
|
||||
static 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,
|
||||
(GInstanceInitFunc) gst_v4l2_buffer_init,
|
||||
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;
|
||||
|
||||
ret = (GstV4l2Buffer *) gst_mini_object_new (GST_TYPE_V4L2_BUFFER);
|
||||
|
||||
ret->pool = pool;
|
||||
gst_mini_object_ref (GST_MINI_OBJECT (pool));
|
||||
memset (&ret->vbuffer, 0x00, sizeof (ret->vbuffer));
|
||||
ret->vbuffer.index = index;
|
||||
ret->vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
ret->vbuffer.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (ioctl (pool->video_fd, VIDIOC_QUERYBUF, &ret->vbuffer) < 0)
|
||||
goto querybuf_failed;
|
||||
|
||||
GST_BUFFER_DATA (ret) = mmap (0, ret->vbuffer.length,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd,
|
||||
ret->vbuffer.m.offset);
|
||||
|
||||
if (GST_BUFFER_DATA (ret) == MAP_FAILED)
|
||||
goto mmap_failed;
|
||||
|
||||
GST_BUFFER_FLAG_SET (ret, GST_BUFFER_FLAG_READONLY);
|
||||
gst_buffer_set_caps (GST_BUFFER (ret), caps);
|
||||
|
||||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#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))
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_pool_finalize (GstV4l2BufferPool * pool)
|
||||
{
|
||||
g_mutex_free (pool->lock);
|
||||
pool->lock = NULL;
|
||||
|
||||
if (pool->video_fd >= 0)
|
||||
close (pool->video_fd);
|
||||
|
||||
if (pool->buffers)
|
||||
g_free (pool->buffers);
|
||||
pool->buffers = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
|
||||
gst_v4l2_buffer_pool_finalize;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static GstV4l2BufferPool *
|
||||
gst_v4l2_buffer_pool_new (GstV4l2Src * v4l2src, gint fd, gint num_buffers,
|
||||
GstCaps * caps)
|
||||
{
|
||||
GstV4l2BufferPool *pool;
|
||||
gint n;
|
||||
|
||||
pool = (GstV4l2BufferPool *) gst_mini_object_new (GST_TYPE_V4L2_BUFFER_POOL);
|
||||
|
||||
pool->video_fd = dup (fd);
|
||||
if (pool->video_fd < 0)
|
||||
goto dup_failed;
|
||||
|
||||
pool->buffer_count = num_buffers;
|
||||
pool->buffers = g_new0 (GstV4l2Buffer *, num_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;
|
||||
}
|
||||
|
||||
return pool;
|
||||
|
||||
dup_failed:
|
||||
{
|
||||
gint errnosave = errno;
|
||||
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (pool));
|
||||
|
||||
errno = errnosave;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
buffer_new_failed:
|
||||
{
|
||||
gint errnosave = errno;
|
||||
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (pool));
|
||||
|
||||
errno = errnosave;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2_buffer_pool_activate (GstV4l2BufferPool * pool, GstV4l2Src * v4l2src)
|
||||
{
|
||||
gint n;
|
||||
|
||||
g_mutex_lock (pool->lock);
|
||||
|
||||
for (n = 0; n < pool->buffer_count; n++) {
|
||||
struct v4l2_buffer *buf = &pool->buffers[n]->vbuffer;
|
||||
|
||||
if (ioctl (pool->video_fd, VIDIOC_QBUF, buf) < 0)
|
||||
goto queue_failed;
|
||||
}
|
||||
|
||||
pool->running = TRUE;
|
||||
|
||||
g_mutex_unlock (pool->lock);
|
||||
|
||||
return TRUE;
|
||||
|
||||
queue_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
|
||||
(_("Could not enqueue buffers in device '%s'."),
|
||||
v4l2src->v4l2object->videodev),
|
||||
("enqueing buffer %d/%d failed: %s",
|
||||
n, v4l2src->num_buffers, g_strerror (errno)));
|
||||
g_mutex_unlock (pool->lock);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool)
|
||||
{
|
||||
gint n;
|
||||
|
||||
g_mutex_lock (pool->lock);
|
||||
pool->running = FALSE;
|
||||
g_mutex_unlock (pool->lock);
|
||||
|
||||
/* 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. */
|
||||
|
||||
for (n = 0; n < pool->buffer_count; n++) {
|
||||
GstBuffer *buf;
|
||||
|
||||
g_mutex_lock (pool->lock);
|
||||
buf = GST_BUFFER (pool->buffers[n]);
|
||||
g_mutex_unlock (pool->lock);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* gst_v4l2src_fill_format_list():
|
||||
* create list of supported capture formats
|
||||
|
@ -364,41 +656,13 @@ unknown_type:
|
|||
#endif /* defined VIDIOC_ENUM_FRAMESIZES */
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* gst_v4l2src_queue_frame():
|
||||
* queue a frame for capturing
|
||||
* return value: TRUE on success, FALSE on error
|
||||
******************************************************/
|
||||
gboolean
|
||||
gst_v4l2src_queue_frame (GstV4l2Src * v4l2src, guint i)
|
||||
{
|
||||
GST_LOG_OBJECT (v4l2src, "queueing frame %u", i);
|
||||
|
||||
if (ioctl (v4l2src->v4l2object->video_fd, VIDIOC_QBUF,
|
||||
&v4l2src->pool->buffers[i].buffer) < 0)
|
||||
goto qbuf_failed;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
qbuf_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, WRITE,
|
||||
(_("Could not exchange data with device '%s'."),
|
||||
v4l2src->v4l2object->videodev),
|
||||
("Error queueing buffer %u on device %s. system error: %s", i,
|
||||
v4l2src->v4l2object->videodev, g_strerror (errno)));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* gst_v4l2src_grab_frame ():
|
||||
* grab a frame for capturing
|
||||
* return value: The captured frame number or -1 on error.
|
||||
******************************************************/
|
||||
gint
|
||||
gst_v4l2src_grab_frame (GstV4l2Src * v4l2src)
|
||||
GstFlowReturn
|
||||
gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
||||
{
|
||||
#define NUM_TRIALS 50
|
||||
struct v4l2_buffer buffer;
|
||||
|
@ -407,12 +671,13 @@ gst_v4l2src_grab_frame (GstV4l2Src * v4l2src)
|
|||
memset (&buffer, 0x00, sizeof (buffer));
|
||||
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buffer.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
while (ioctl (v4l2src->v4l2object->video_fd, VIDIOC_DQBUF, &buffer) < 0) {
|
||||
|
||||
GST_LOG_OBJECT (v4l2src,
|
||||
"problem grabbing frame %d (ix=%d), trials=%d, pool-ct=%d, buf.flags=%d",
|
||||
buffer.sequence, buffer.index, trials, v4l2src->pool->refcount,
|
||||
buffer.flags);
|
||||
buffer.sequence, buffer.index, trials,
|
||||
GST_MINI_OBJECT_REFCOUNT (v4l2src->pool), buffer.flags);
|
||||
|
||||
/* if the sync() got interrupted, we can retry */
|
||||
switch (errno) {
|
||||
|
@ -471,10 +736,50 @@ gst_v4l2src_grab_frame (GstV4l2Src * v4l2src)
|
|||
}
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (v4l2src, "grabbed frame %d (ix=%d), pool-ct=%d",
|
||||
buffer.sequence, buffer.index, v4l2src->pool->refcount);
|
||||
g_mutex_lock (v4l2src->pool->lock);
|
||||
|
||||
return buffer.index;
|
||||
*buf = GST_BUFFER (v4l2src->pool->buffers[buffer.index]);
|
||||
v4l2src->pool->buffers[buffer.index] = NULL;
|
||||
v4l2src->pool->num_live_buffers++;
|
||||
|
||||
g_mutex_unlock (v4l2src->pool->lock);
|
||||
|
||||
/* this can change at every frame, esp. with jpeg */
|
||||
GST_BUFFER_SIZE (*buf) = buffer.bytesused;
|
||||
|
||||
GST_BUFFER_OFFSET (*buf) = v4l2src->offset++;
|
||||
GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset;
|
||||
|
||||
/* timestamps, LOCK to get clock and base time. */
|
||||
{
|
||||
GstClock *clock;
|
||||
GstClockTime timestamp;
|
||||
|
||||
GST_OBJECT_LOCK (v4l2src);
|
||||
if ((clock = GST_ELEMENT_CLOCK (v4l2src))) {
|
||||
/* we have a clock, get base time and ref clock */
|
||||
timestamp = GST_ELEMENT (v4l2src)->base_time;
|
||||
gst_object_ref (clock);
|
||||
} else {
|
||||
/* no clock, can't set timestamps */
|
||||
timestamp = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (v4l2src);
|
||||
|
||||
if (clock) {
|
||||
/* the time now is the time of the clock minus the base time */
|
||||
timestamp = gst_clock_get_time (clock) - timestamp;
|
||||
gst_object_unref (clock);
|
||||
}
|
||||
|
||||
/* FIXME: use the timestamp from the buffer itself! */
|
||||
GST_BUFFER_TIMESTAMP (*buf) = timestamp;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (v4l2src, "grabbed frame %d (ix=%d), pool-ct=%d",
|
||||
buffer.sequence, buffer.index, v4l2src->pool->num_live_buffers);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
/* ERRORS */
|
||||
einval:
|
||||
|
@ -486,13 +791,13 @@ einval:
|
|||
" or no buffers have been allocated yet, or the userptr"
|
||||
" or length are invalid. device %s"),
|
||||
v4l2src->v4l2object->videodev));
|
||||
return -1;
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
enomem:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, FAILED,
|
||||
(_("Failed trying to get video frames from device '%s'. Not enough memory."), v4l2src->v4l2object->videodev), (_("insufficient memory to enqueue a user pointer buffer. device %s."), v4l2src->v4l2object->videodev));
|
||||
return -1;
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
too_many_trials:
|
||||
{
|
||||
|
@ -501,7 +806,7 @@ too_many_trials:
|
|||
v4l2src->v4l2object->videodev),
|
||||
(_("Failed after %d tries. device %s. system error: %s"),
|
||||
NUM_TRIALS, v4l2src->v4l2object->videodev, g_strerror (errno)));
|
||||
return -1;
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
qbuf_failed:
|
||||
{
|
||||
|
@ -510,7 +815,7 @@ qbuf_failed:
|
|||
v4l2src->v4l2object->videodev),
|
||||
("Error queueing buffer on device %s. system error: %s",
|
||||
v4l2src->v4l2object->videodev, g_strerror (errno)));
|
||||
return -1;
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -646,9 +951,8 @@ invalid_framerate:
|
|||
* return value: TRUE on success, FALSE on error
|
||||
******************************************************/
|
||||
gboolean
|
||||
gst_v4l2src_capture_init (GstV4l2Src * v4l2src)
|
||||
gst_v4l2src_capture_init (GstV4l2Src * v4l2src, GstCaps * caps)
|
||||
{
|
||||
gint n;
|
||||
gint fd = v4l2src->v4l2object->video_fd;
|
||||
struct v4l2_requestbuffers breq;
|
||||
|
||||
|
@ -681,33 +985,9 @@ gst_v4l2src_capture_init (GstV4l2Src * v4l2src)
|
|||
/* Map the buffers */
|
||||
GST_LOG_OBJECT (v4l2src, "initiating buffer pool");
|
||||
|
||||
v4l2src->pool = g_new (GstV4l2BufferPool, 1);
|
||||
gst_atomic_int_set (&v4l2src->pool->refcount, 1);
|
||||
v4l2src->pool->video_fd = fd;
|
||||
v4l2src->pool->buffer_count = v4l2src->num_buffers;
|
||||
v4l2src->pool->buffers = g_new0 (GstV4l2Buffer, v4l2src->num_buffers);
|
||||
|
||||
for (n = 0; n < v4l2src->num_buffers; n++) {
|
||||
GstV4l2Buffer *buffer = &v4l2src->pool->buffers[n];
|
||||
|
||||
gst_atomic_int_set (&buffer->refcount, 1);
|
||||
buffer->pool = v4l2src->pool;
|
||||
memset (&buffer->buffer, 0x00, sizeof (buffer->buffer));
|
||||
buffer->buffer.index = n;
|
||||
buffer->buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buffer->buffer.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (ioctl (fd, VIDIOC_QUERYBUF, &buffer->buffer) < 0)
|
||||
goto querybuf_failed;
|
||||
|
||||
buffer->start = mmap (0, buffer->buffer.length, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, fd, buffer->buffer.m.offset);
|
||||
|
||||
if (buffer->start == MAP_FAILED)
|
||||
goto mmap_failed;
|
||||
|
||||
buffer->length = buffer->buffer.length;
|
||||
}
|
||||
if (!(v4l2src->pool = gst_v4l2_buffer_pool_new (v4l2src, fd,
|
||||
v4l2src->num_buffers, caps)))
|
||||
goto buffer_pool_new_failed;
|
||||
|
||||
GST_INFO_OBJECT (v4l2src, "capturing buffers via mmap()");
|
||||
v4l2src->use_mmap = TRUE;
|
||||
|
@ -742,22 +1022,12 @@ no_buffers:
|
|||
breq.count, v4l2src->v4l2object->videodev, GST_V4L2_MIN_BUFFERS));
|
||||
return FALSE;
|
||||
}
|
||||
querybuf_failed:
|
||||
buffer_pool_new_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
|
||||
(_("Could not retrieve buffer from device '%s'"),
|
||||
(_("Could not map buffers from device '%s'"),
|
||||
v4l2src->v4l2object->videodev),
|
||||
("Failed QUERYBUF: %s", g_strerror (errno)));
|
||||
gst_v4l2src_capture_deinit (v4l2src);
|
||||
return FALSE;
|
||||
}
|
||||
mmap_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
|
||||
(_("Could not map memory in device '%s'."),
|
||||
v4l2src->v4l2object->videodev),
|
||||
("mmap failed: %s", g_strerror (errno)));
|
||||
gst_v4l2src_capture_deinit (v4l2src);
|
||||
("Failed to create buffer pool: %s", g_strerror (errno)));
|
||||
return FALSE;
|
||||
}
|
||||
no_supported_capture_method:
|
||||
|
@ -779,7 +1049,6 @@ gboolean
|
|||
gst_v4l2src_capture_start (GstV4l2Src * v4l2src)
|
||||
{
|
||||
gint type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
gint n;
|
||||
gint fd = v4l2src->v4l2object->video_fd;
|
||||
|
||||
GST_DEBUG_OBJECT (v4l2src, "starting the capturing");
|
||||
|
@ -789,9 +1058,8 @@ gst_v4l2src_capture_start (GstV4l2Src * v4l2src)
|
|||
v4l2src->quit = FALSE;
|
||||
|
||||
if (v4l2src->use_mmap) {
|
||||
for (n = 0; n < v4l2src->num_buffers; n++)
|
||||
if (!gst_v4l2src_queue_frame (v4l2src, n))
|
||||
goto queue_failed;
|
||||
if (!gst_v4l2_buffer_pool_activate (v4l2src->pool, v4l2src))
|
||||
goto pool_activate_failed;
|
||||
|
||||
if (ioctl (fd, VIDIOC_STREAMON, &type) < 0)
|
||||
goto streamon_failed;
|
||||
|
@ -802,13 +1070,9 @@ gst_v4l2src_capture_start (GstV4l2Src * v4l2src)
|
|||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
queue_failed:
|
||||
pool_activate_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
|
||||
(_("Could not enqueue buffers in device '%s'."),
|
||||
v4l2src->v4l2object->videodev),
|
||||
("enqueing buffer %d/%d failed: %s",
|
||||
n, v4l2src->num_buffers, g_strerror (errno)));
|
||||
/* already errored */
|
||||
return FALSE;
|
||||
}
|
||||
streamon_failed:
|
||||
|
@ -864,22 +1128,6 @@ streamoff_failed:
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2src_buffer_pool_free (GstV4l2BufferPool * pool, gboolean do_close)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < pool->buffer_count; i++) {
|
||||
gst_atomic_int_set (&pool->buffers[i].refcount, 0);
|
||||
munmap (pool->buffers[i].start, pool->buffers[i].length);
|
||||
}
|
||||
g_free (pool->buffers);
|
||||
gst_atomic_int_set (&pool->refcount, 0);
|
||||
if (do_close)
|
||||
close (pool->video_fd);
|
||||
g_free (pool);
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* gst_v4l2src_capture_deinit():
|
||||
* deinitialize the capture system
|
||||
|
@ -898,11 +1146,7 @@ gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src)
|
|||
}
|
||||
|
||||
if (v4l2src->pool) {
|
||||
if (g_atomic_int_dec_and_test (&v4l2src->pool->refcount)) {
|
||||
/* FIXME: this is crack, should either use user pointer io method or some
|
||||
strategy similar to what filesrc uses with automatic freeing */
|
||||
gst_v4l2src_buffer_pool_free (v4l2src->pool, FALSE);
|
||||
}
|
||||
gst_v4l2_buffer_pool_destroy (v4l2src->pool);
|
||||
v4l2src->pool = NULL;
|
||||
}
|
||||
|
||||
|
@ -964,134 +1208,3 @@ gst_v4l2src_get_size_limits (GstV4l2Src * v4l2src,
|
|||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define GST_TYPE_V4L2SRC_BUFFER (gst_v4l2src_buffer_get_type())
|
||||
#define GST_IS_V4L2SRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2SRC_BUFFER))
|
||||
#define GST_V4L2SRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2SRC_BUFFER, GstV4l2SrcBuffer))
|
||||
|
||||
typedef struct _GstV4l2SrcBuffer
|
||||
{
|
||||
GstBuffer buffer;
|
||||
|
||||
GstV4l2Buffer *buf;
|
||||
} GstV4l2SrcBuffer;
|
||||
|
||||
static void gst_v4l2src_buffer_class_init (gpointer g_class,
|
||||
gpointer class_data);
|
||||
static void gst_v4l2src_buffer_init (GTypeInstance * instance,
|
||||
gpointer g_class);
|
||||
static void gst_v4l2src_buffer_finalize (GstV4l2SrcBuffer * v4l2src_buffer);
|
||||
|
||||
GType
|
||||
gst_v4l2src_buffer_get_type (void)
|
||||
{
|
||||
static GType _gst_v4l2src_buffer_type;
|
||||
|
||||
if (G_UNLIKELY (_gst_v4l2src_buffer_type == 0)) {
|
||||
static const GTypeInfo v4l2src_buffer_info = {
|
||||
sizeof (GstBufferClass),
|
||||
NULL,
|
||||
NULL,
|
||||
gst_v4l2src_buffer_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstV4l2SrcBuffer),
|
||||
0,
|
||||
gst_v4l2src_buffer_init,
|
||||
NULL
|
||||
};
|
||||
_gst_v4l2src_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
|
||||
"GstV4l2SrcBuffer", &v4l2src_buffer_info, 0);
|
||||
}
|
||||
return _gst_v4l2src_buffer_type;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2src_buffer_class_init (gpointer g_class, gpointer class_data)
|
||||
{
|
||||
GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
|
||||
|
||||
mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
|
||||
gst_v4l2src_buffer_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2src_buffer_init (GTypeInstance * instance, gpointer g_class)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2src_buffer_finalize (GstV4l2SrcBuffer * v4l2src_buffer)
|
||||
{
|
||||
GstV4l2Buffer *buf = v4l2src_buffer->buf;
|
||||
|
||||
if (buf) {
|
||||
GST_LOG ("freeing buffer %p (nr. %d)", buf, buf->buffer.index);
|
||||
|
||||
if (!g_atomic_int_dec_and_test (&buf->refcount)) {
|
||||
/* we're still in use, add to queue again
|
||||
* note: this might fail because the device is already stopped (race)
|
||||
*/
|
||||
if (ioctl (buf->pool->video_fd, VIDIOC_QBUF, &buf->buffer) < 0)
|
||||
GST_INFO ("readding to queue failed, assuming video device is stopped");
|
||||
}
|
||||
if (g_atomic_int_dec_and_test (&buf->pool->refcount)) {
|
||||
/* we're last thing that used all this */
|
||||
gst_v4l2src_buffer_pool_free (buf->pool, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a V4l2Src buffer from our mmap'd data area */
|
||||
GstBuffer *
|
||||
gst_v4l2src_buffer_new (GstV4l2Src * v4l2src, guint size, guint8 * data,
|
||||
GstV4l2Buffer * srcbuf)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
GstClockTime timestamp;
|
||||
GstClock *clock;
|
||||
|
||||
if (data == NULL) {
|
||||
buf = gst_buffer_new_and_alloc (size);
|
||||
} else {
|
||||
buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_V4L2SRC_BUFFER);
|
||||
GST_BUFFER_DATA (buf) = data;
|
||||
GST_V4L2SRC_BUFFER (buf)->buf = srcbuf;
|
||||
GST_LOG_OBJECT (v4l2src,
|
||||
"creating buffer %p (nr. %d)", srcbuf, srcbuf->buffer.index);
|
||||
}
|
||||
GST_BUFFER_SIZE (buf) = size;
|
||||
|
||||
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY);
|
||||
|
||||
/* timestamps, LOCK to get clock and base time. */
|
||||
GST_OBJECT_LOCK (v4l2src);
|
||||
if ((clock = GST_ELEMENT_CLOCK (v4l2src))) {
|
||||
/* we have a clock, get base time and ref clock */
|
||||
timestamp = GST_ELEMENT (v4l2src)->base_time;
|
||||
gst_object_ref (clock);
|
||||
} else {
|
||||
/* no clock, can't set timestamps */
|
||||
timestamp = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (v4l2src);
|
||||
|
||||
if (clock) {
|
||||
/* the time now is the time of the clock minus the base time */
|
||||
timestamp = gst_clock_get_time (clock) - timestamp;
|
||||
gst_object_unref (clock);
|
||||
}
|
||||
|
||||
/* FIXME: use the timestamp from the buffer itself! */
|
||||
GST_BUFFER_TIMESTAMP (buf) = timestamp;
|
||||
|
||||
/* offsets */
|
||||
GST_BUFFER_OFFSET (buf) = v4l2src->offset++;
|
||||
GST_BUFFER_OFFSET_END (buf) = v4l2src->offset;
|
||||
|
||||
/* the negotiate() method already set caps on the source pad */
|
||||
gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (v4l2src)));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -33,11 +33,10 @@ gboolean gst_v4l2src_set_capture (GstV4l2Src * v4l2src,
|
|||
guint32 width, guint32 height,
|
||||
guint32 fps_n, guint32 fps_d);
|
||||
|
||||
gboolean gst_v4l2src_capture_init (GstV4l2Src * v4l2src);
|
||||
gboolean gst_v4l2src_capture_init (GstV4l2Src * v4l2src, GstCaps *caps);
|
||||
gboolean gst_v4l2src_capture_start (GstV4l2Src * v4l2src);
|
||||
|
||||
gint gst_v4l2src_grab_frame (GstV4l2Src * v4l2src);
|
||||
gboolean gst_v4l2src_queue_frame (GstV4l2Src * v4l2src, guint i);
|
||||
GstFlowReturn gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer **buf);
|
||||
|
||||
gboolean gst_v4l2src_capture_stop (GstV4l2Src * v4l2src);
|
||||
gboolean gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src);
|
||||
|
@ -52,11 +51,5 @@ gboolean gst_v4l2src_get_size_limits (GstV4l2Src * v4l2src,
|
|||
struct v4l2_fmtdesc *fmt,
|
||||
gint * min_w, gint * max_w,
|
||||
gint * min_h, gint * max_h);
|
||||
/* buffers */
|
||||
GstBuffer* gst_v4l2src_buffer_new (GstV4l2Src * v4l2src,
|
||||
guint size, guint8 * data,
|
||||
GstV4l2Buffer * srcbuf);
|
||||
void gst_v4l2src_free_buffer (GstBuffer * buffer);
|
||||
|
||||
|
||||
#endif /* __V4L2SRC_CALLS_H__ */
|
||||
|
|
Loading…
Reference in a new issue