mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-06 07:28:53 +00:00
v4l2: move capture code to device object
Move the details of how to capture to the device object. Remove the v4l2src_calls.[ch] files because they are empty now. Provide two simple methods to get and return a buffer to the device. Also do a slow copy when the buffer is not from our pool.
This commit is contained in:
parent
ebd03a3fad
commit
7c5f5b1b7f
10 changed files with 282 additions and 370 deletions
|
@ -17,7 +17,6 @@ libgstvideo4linux2_la_SOURCES = gstv4l2.c \
|
||||||
gstv4l2tuner.c \
|
gstv4l2tuner.c \
|
||||||
gstv4l2vidorient.c \
|
gstv4l2vidorient.c \
|
||||||
v4l2_calls.c \
|
v4l2_calls.c \
|
||||||
v4l2src_calls.c \
|
|
||||||
$(xv_source)
|
$(xv_source)
|
||||||
libgstvideo4linux2_la_SOURCES += gstv4l2sink.c
|
libgstvideo4linux2_la_SOURCES += gstv4l2sink.c
|
||||||
|
|
||||||
|
@ -53,5 +52,4 @@ noinst_HEADERS = \
|
||||||
gstv4l2tuner.h \
|
gstv4l2tuner.h \
|
||||||
gstv4l2vidorient.h \
|
gstv4l2vidorient.h \
|
||||||
gstv4l2xoverlay.h \
|
gstv4l2xoverlay.h \
|
||||||
v4l2_calls.h \
|
v4l2_calls.h
|
||||||
v4l2src_calls.h
|
|
||||||
|
|
|
@ -52,9 +52,9 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
|
GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
|
||||||
|
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
|
||||||
#define GST_CAT_DEFAULT v4l2_debug
|
#define GST_CAT_DEFAULT v4l2_debug
|
||||||
|
|
||||||
|
|
||||||
#define DEFAULT_PROP_DEVICE_NAME NULL
|
#define DEFAULT_PROP_DEVICE_NAME NULL
|
||||||
#define DEFAULT_PROP_DEVICE_FD -1
|
#define DEFAULT_PROP_DEVICE_FD -1
|
||||||
#define DEFAULT_PROP_FLAGS 0
|
#define DEFAULT_PROP_FLAGS 0
|
||||||
|
@ -2430,3 +2430,264 @@ stop_failed:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_v4l2_object_get_read (GstV4l2Object * v4l2object, GstBuffer ** buf)
|
||||||
|
{
|
||||||
|
GstFlowReturn res;
|
||||||
|
gint amount;
|
||||||
|
gint ret;
|
||||||
|
gpointer data;
|
||||||
|
gint buffersize;
|
||||||
|
|
||||||
|
buffersize = v4l2object->size;
|
||||||
|
|
||||||
|
/* In case the size per frame is unknown assume it's a streaming format (e.g.
|
||||||
|
* mpegts) and grab a reasonable default size instead */
|
||||||
|
if (buffersize == 0)
|
||||||
|
buffersize = 4096;
|
||||||
|
|
||||||
|
*buf = gst_buffer_new_and_alloc (buffersize);
|
||||||
|
data = gst_buffer_map (*buf, NULL, NULL, GST_MAP_WRITE);
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
|
||||||
|
if (G_UNLIKELY (ret < 0)) {
|
||||||
|
if (errno == EBUSY)
|
||||||
|
goto stopped;
|
||||||
|
if (errno == ENXIO) {
|
||||||
|
GST_DEBUG_OBJECT (v4l2object->element,
|
||||||
|
"v4l2 device doesn't support polling. Disabling");
|
||||||
|
v4l2object->can_poll_device = FALSE;
|
||||||
|
} else {
|
||||||
|
if (errno != EAGAIN && errno != EINTR)
|
||||||
|
goto select_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amount = v4l2_read (v4l2object->video_fd, data, buffersize);
|
||||||
|
|
||||||
|
if (amount == buffersize) {
|
||||||
|
break;
|
||||||
|
} else if (amount == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
goto read_error;
|
||||||
|
} else {
|
||||||
|
/* short reads can happen if a signal interrupts the read */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} while (TRUE);
|
||||||
|
|
||||||
|
gst_buffer_unmap (*buf, data, amount);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
select_error:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
|
||||||
|
("select error %d: %s (%d)", ret, g_strerror (errno), errno));
|
||||||
|
res = GST_FLOW_ERROR;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
stopped:
|
||||||
|
{
|
||||||
|
GST_DEBUG ("stop called");
|
||||||
|
res = GST_FLOW_WRONG_STATE;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
read_error:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ,
|
||||||
|
(_("Error reading %d bytes from device '%s'."),
|
||||||
|
buffersize, v4l2object->videodev), GST_ERROR_SYSTEM);
|
||||||
|
res = GST_FLOW_ERROR;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
cleanup:
|
||||||
|
{
|
||||||
|
gst_buffer_unmap (*buf, data, 0);
|
||||||
|
gst_buffer_unref (*buf);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_v4l2_object_grab_frame (GstV4l2Object * v4l2object, GstBuffer ** buf)
|
||||||
|
{
|
||||||
|
#define NUM_TRIALS 50
|
||||||
|
GstV4l2BufferPool *pool;
|
||||||
|
gint32 trials = NUM_TRIALS;
|
||||||
|
GstBuffer *pool_buffer;
|
||||||
|
gboolean need_copy;
|
||||||
|
gint ret;
|
||||||
|
|
||||||
|
pool = v4l2object->pool;
|
||||||
|
if (!pool)
|
||||||
|
goto no_buffer_pool;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (v4l2object->element, "grab frame");
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (v4l2object->can_poll_device) {
|
||||||
|
ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
|
||||||
|
if (G_UNLIKELY (ret < 0)) {
|
||||||
|
if (errno == EBUSY)
|
||||||
|
goto stopped;
|
||||||
|
if (errno == ENXIO) {
|
||||||
|
GST_DEBUG_OBJECT (v4l2object->element,
|
||||||
|
"v4l2 device doesn't support polling. Disabling");
|
||||||
|
v4l2object->can_poll_device = FALSE;
|
||||||
|
} else {
|
||||||
|
if (errno != EAGAIN && errno != EINTR)
|
||||||
|
goto select_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pool_buffer = gst_v4l2_buffer_pool_dqbuf (pool);
|
||||||
|
if (pool_buffer)
|
||||||
|
break;
|
||||||
|
|
||||||
|
GST_WARNING_OBJECT (v4l2object->element, "trials=%d", trials);
|
||||||
|
|
||||||
|
/* if the sync() got interrupted, we can retry */
|
||||||
|
switch (errno) {
|
||||||
|
case EINVAL:
|
||||||
|
case ENOMEM:
|
||||||
|
/* fatal */
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
|
||||||
|
case EAGAIN:
|
||||||
|
case EIO:
|
||||||
|
case EINTR:
|
||||||
|
default:
|
||||||
|
/* try again, until too many trials */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check nr. of attempts to capture */
|
||||||
|
if (--trials == -1) {
|
||||||
|
goto too_many_trials;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we are handing out the last buffer in the pool, we need to make a
|
||||||
|
* copy and bring the buffer back in the pool. */
|
||||||
|
need_copy = v4l2object->always_copy
|
||||||
|
|| !gst_v4l2_buffer_pool_available_buffers (pool);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (need_copy)) {
|
||||||
|
if (!v4l2object->always_copy) {
|
||||||
|
GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2object->element,
|
||||||
|
"running out of buffers, making a copy to reuse current one");
|
||||||
|
}
|
||||||
|
*buf = gst_buffer_copy (pool_buffer);
|
||||||
|
/* this will requeue */
|
||||||
|
gst_buffer_unref (pool_buffer);
|
||||||
|
} else {
|
||||||
|
*buf = pool_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
no_buffer_pool:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (v4l2object->element, "no buffer pool");
|
||||||
|
return GST_FLOW_WRONG_STATE;
|
||||||
|
}
|
||||||
|
select_error:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
|
||||||
|
("select error %d: %s (%d)", ret, g_strerror (errno), errno));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
stopped:
|
||||||
|
{
|
||||||
|
GST_DEBUG ("stop called");
|
||||||
|
return GST_FLOW_WRONG_STATE;
|
||||||
|
}
|
||||||
|
too_many_trials:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, FAILED,
|
||||||
|
(_("Failed trying to get video frames from device '%s'."),
|
||||||
|
v4l2object->videodev),
|
||||||
|
(_("Failed after %d tries. device %s. system error: %s"),
|
||||||
|
NUM_TRIALS, v4l2object->videodev, g_strerror (errno)));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_v4l2_object_get_mmap (GstV4l2Object * v4l2object, GstBuffer ** buf)
|
||||||
|
{
|
||||||
|
GstBuffer *temp;
|
||||||
|
GstFlowReturn ret;
|
||||||
|
guint size;
|
||||||
|
guint count = 0;
|
||||||
|
|
||||||
|
again:
|
||||||
|
ret = gst_v4l2_object_grab_frame (v4l2object, &temp);
|
||||||
|
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (v4l2object->size > 0) {
|
||||||
|
size = gst_buffer_get_size (temp);
|
||||||
|
|
||||||
|
/* if size does not match what we expected, try again */
|
||||||
|
if (size != v4l2object->size) {
|
||||||
|
GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, READ,
|
||||||
|
(_("Got unexpected frame size of %u instead of %u."),
|
||||||
|
size, v4l2object->size), (NULL));
|
||||||
|
gst_buffer_unref (temp);
|
||||||
|
if (count++ > 50)
|
||||||
|
goto size_error;
|
||||||
|
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf = temp;
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
size_error:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ,
|
||||||
|
(_("Error reading %d bytes on device '%s'."),
|
||||||
|
v4l2object->size, v4l2object->videodev), (NULL));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GstFlowReturn
|
||||||
|
gst_v4l2_object_get_buffer (GstV4l2Object * v4l2object, GstBuffer ** buf)
|
||||||
|
{
|
||||||
|
GstFlowReturn ret;
|
||||||
|
|
||||||
|
switch (v4l2object->type) {
|
||||||
|
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
||||||
|
if (v4l2object->use_mmap) {
|
||||||
|
ret = gst_v4l2_object_get_mmap (v4l2object, buf);
|
||||||
|
} else {
|
||||||
|
ret = gst_v4l2_object_get_read (v4l2object, buf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
|
||||||
|
ret = GST_FLOW_ERROR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = GST_FLOW_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstFlowReturn
|
||||||
|
gst_v4l2_object_output_buffer (GstV4l2Object * v4l2object, GstBuffer * buf)
|
||||||
|
{
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
|
|
@ -116,6 +116,7 @@ struct _GstV4l2Object {
|
||||||
|
|
||||||
/* optional pool */
|
/* optional pool */
|
||||||
guint32 num_buffers;
|
guint32 num_buffers;
|
||||||
|
gboolean always_copy;
|
||||||
gboolean use_mmap;
|
gboolean use_mmap;
|
||||||
GstV4l2BufferPool *pool;
|
GstV4l2BufferPool *pool;
|
||||||
|
|
||||||
|
@ -215,8 +216,14 @@ GstStructure* gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc);
|
||||||
|
|
||||||
gboolean gst_v4l2_object_set_format (GstV4l2Object *v4l2object, GstCaps * caps);
|
gboolean gst_v4l2_object_set_format (GstV4l2Object *v4l2object, GstCaps * caps);
|
||||||
|
|
||||||
gboolean gst_v4l2_object_start (GstV4l2Object *v4l2object);
|
gboolean gst_v4l2_object_start (GstV4l2Object *v4l2object);
|
||||||
gboolean gst_v4l2_object_stop (GstV4l2Object *v4l2object);
|
gboolean gst_v4l2_object_stop (GstV4l2Object *v4l2object);
|
||||||
|
|
||||||
|
|
||||||
|
/* capture returns a filled buffer, output returns an empty buffer */
|
||||||
|
GstFlowReturn gst_v4l2_object_get_buffer (GstV4l2Object * v4l2object, GstBuffer ** buf);
|
||||||
|
/* output the filled buffer */
|
||||||
|
GstFlowReturn gst_v4l2_object_output_buffer (GstV4l2Object * v4l2object, GstBuffer * buf);
|
||||||
|
|
||||||
|
|
||||||
#define GST_IMPLEMENT_V4L2_PROBE_METHODS(Type_Class, interface_as_function) \
|
#define GST_IMPLEMENT_V4L2_PROBE_METHODS(Type_Class, interface_as_function) \
|
||||||
|
|
|
@ -675,7 +675,7 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
|
||||||
|
|
||||||
meta = GST_META_V4L2_GET (buf);
|
meta = GST_META_V4L2_GET (buf);
|
||||||
|
|
||||||
if (meta == NULL) {
|
if (meta == NULL || meta->pool != obj->pool) {
|
||||||
guint8 *data;
|
guint8 *data;
|
||||||
gsize size;
|
gsize size;
|
||||||
|
|
||||||
|
|
|
@ -48,9 +48,10 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include "v4l2src_calls.h"
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "gstv4l2src.h"
|
||||||
|
|
||||||
#include "gstv4l2colorbalance.h"
|
#include "gstv4l2colorbalance.h"
|
||||||
#include "gstv4l2tuner.h"
|
#include "gstv4l2tuner.h"
|
||||||
#ifdef HAVE_XVIDEO
|
#ifdef HAVE_XVIDEO
|
||||||
|
@ -129,12 +130,6 @@ static void gst_v4l2src_set_property (GObject * object, guint prop_id,
|
||||||
static void gst_v4l2src_get_property (GObject * object, guint prop_id,
|
static void gst_v4l2src_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec);
|
GValue * value, GParamSpec * pspec);
|
||||||
|
|
||||||
/* get_frame io methods */
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf);
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_v4l2src_get_mmap (GstV4l2Src * v4l2src, GstBuffer ** buf);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_v4l2src_class_init (GstV4l2SrcClass * klass)
|
gst_v4l2src_class_init (GstV4l2SrcClass * klass)
|
||||||
{
|
{
|
||||||
|
@ -217,7 +212,7 @@ gst_v4l2src_init (GstV4l2Src * v4l2src)
|
||||||
/* number of buffers requested */
|
/* number of buffers requested */
|
||||||
v4l2src->v4l2object->num_buffers = PROP_DEF_QUEUE_SIZE;
|
v4l2src->v4l2object->num_buffers = PROP_DEF_QUEUE_SIZE;
|
||||||
|
|
||||||
v4l2src->always_copy = PROP_DEF_ALWAYS_COPY;
|
v4l2src->v4l2object->always_copy = PROP_DEF_ALWAYS_COPY;
|
||||||
v4l2src->decimate = PROP_DEF_DECIMATE;
|
v4l2src->decimate = PROP_DEF_DECIMATE;
|
||||||
|
|
||||||
gst_base_src_set_format (GST_BASE_SRC (v4l2src), GST_FORMAT_TIME);
|
gst_base_src_set_format (GST_BASE_SRC (v4l2src), GST_FORMAT_TIME);
|
||||||
|
@ -259,7 +254,7 @@ gst_v4l2src_set_property (GObject * object,
|
||||||
v4l2src->v4l2object->num_buffers = g_value_get_uint (value);
|
v4l2src->v4l2object->num_buffers = g_value_get_uint (value);
|
||||||
break;
|
break;
|
||||||
case PROP_ALWAYS_COPY:
|
case PROP_ALWAYS_COPY:
|
||||||
v4l2src->always_copy = g_value_get_boolean (value);
|
v4l2src->v4l2object->always_copy = g_value_get_boolean (value);
|
||||||
break;
|
break;
|
||||||
case PROP_DECIMATE:
|
case PROP_DECIMATE:
|
||||||
v4l2src->decimate = g_value_get_int (value);
|
v4l2src->decimate = g_value_get_int (value);
|
||||||
|
@ -284,7 +279,7 @@ gst_v4l2src_get_property (GObject * object,
|
||||||
g_value_set_uint (value, v4l2src->v4l2object->num_buffers);
|
g_value_set_uint (value, v4l2src->v4l2object->num_buffers);
|
||||||
break;
|
break;
|
||||||
case PROP_ALWAYS_COPY:
|
case PROP_ALWAYS_COPY:
|
||||||
g_value_set_boolean (value, v4l2src->always_copy);
|
g_value_set_boolean (value, v4l2src->v4l2object->always_copy);
|
||||||
break;
|
break;
|
||||||
case PROP_DECIMATE:
|
case PROP_DECIMATE:
|
||||||
g_value_set_int (value, v4l2src->decimate);
|
g_value_set_int (value, v4l2src->decimate);
|
||||||
|
@ -526,12 +521,6 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps)
|
||||||
/* error already posted */
|
/* error already posted */
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (obj->use_mmap) {
|
|
||||||
v4l2src->get_frame = gst_v4l2src_get_mmap;
|
|
||||||
} else {
|
|
||||||
v4l2src->get_frame = gst_v4l2src_get_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gst_v4l2_object_start (obj))
|
if (!gst_v4l2_object_start (obj))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -689,152 +678,24 @@ gst_v4l2src_change_state (GstElement * element, GstStateChange transition)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
|
||||||
{
|
|
||||||
GstFlowReturn res;
|
|
||||||
gint amount;
|
|
||||||
gint ret;
|
|
||||||
gpointer data;
|
|
||||||
gint buffersize;
|
|
||||||
|
|
||||||
buffersize = v4l2src->frame_byte_size;
|
|
||||||
/* In case the size per frame is unknown assume it's a streaming format (e.g.
|
|
||||||
* mpegts) and grab a reasonable default size instead */
|
|
||||||
if (buffersize == 0)
|
|
||||||
buffersize = GST_BASE_SRC (v4l2src)->blocksize;
|
|
||||||
|
|
||||||
*buf = gst_buffer_new_and_alloc (buffersize);
|
|
||||||
data = gst_buffer_map (*buf, NULL, NULL, GST_MAP_WRITE);
|
|
||||||
|
|
||||||
do {
|
|
||||||
ret = gst_poll_wait (v4l2src->v4l2object->poll, GST_CLOCK_TIME_NONE);
|
|
||||||
if (G_UNLIKELY (ret < 0)) {
|
|
||||||
if (errno == EBUSY)
|
|
||||||
goto stopped;
|
|
||||||
if (errno == ENXIO) {
|
|
||||||
GST_DEBUG_OBJECT (v4l2src,
|
|
||||||
"v4l2 device doesn't support polling. Disabling");
|
|
||||||
v4l2src->v4l2object->can_poll_device = FALSE;
|
|
||||||
} else {
|
|
||||||
if (errno != EAGAIN && errno != EINTR)
|
|
||||||
goto select_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
amount = v4l2_read (v4l2src->v4l2object->video_fd, data, buffersize);
|
|
||||||
|
|
||||||
if (amount == buffersize) {
|
|
||||||
break;
|
|
||||||
} else if (amount == -1) {
|
|
||||||
if (errno == EAGAIN || errno == EINTR) {
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
goto read_error;
|
|
||||||
} else {
|
|
||||||
/* short reads can happen if a signal interrupts the read */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} while (TRUE);
|
|
||||||
|
|
||||||
gst_buffer_unmap (*buf, data, amount);
|
|
||||||
|
|
||||||
/* we set the buffer metadata in gst_v4l2src_create() */
|
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
select_error:
|
|
||||||
{
|
|
||||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL),
|
|
||||||
("select error %d: %s (%d)", ret, g_strerror (errno), errno));
|
|
||||||
res = GST_FLOW_ERROR;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
stopped:
|
|
||||||
{
|
|
||||||
GST_DEBUG ("stop called");
|
|
||||||
res = GST_FLOW_WRONG_STATE;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
read_error:
|
|
||||||
{
|
|
||||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
|
|
||||||
(_("Error reading %d bytes from device '%s'."),
|
|
||||||
buffersize, v4l2src->v4l2object->videodev), GST_ERROR_SYSTEM);
|
|
||||||
res = GST_FLOW_ERROR;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
cleanup:
|
|
||||||
{
|
|
||||||
gst_buffer_unmap (*buf, data, 0);
|
|
||||||
gst_buffer_unref (*buf);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_v4l2src_get_mmap (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
|
||||||
{
|
|
||||||
GstBuffer *temp;
|
|
||||||
GstFlowReturn ret;
|
|
||||||
guint size;
|
|
||||||
guint count = 0;
|
|
||||||
|
|
||||||
again:
|
|
||||||
ret = gst_v4l2src_grab_frame (v4l2src, &temp);
|
|
||||||
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (v4l2src->frame_byte_size > 0) {
|
|
||||||
size = gst_buffer_get_size (temp);
|
|
||||||
|
|
||||||
/* if size does not match what we expected, try again */
|
|
||||||
if (size != v4l2src->frame_byte_size) {
|
|
||||||
GST_ELEMENT_WARNING (v4l2src, RESOURCE, READ,
|
|
||||||
(_("Got unexpected frame size of %u instead of %u."),
|
|
||||||
size, v4l2src->frame_byte_size), (NULL));
|
|
||||||
gst_buffer_unref (temp);
|
|
||||||
if (count++ > 50)
|
|
||||||
goto size_error;
|
|
||||||
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*buf = temp;
|
|
||||||
done:
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
size_error:
|
|
||||||
{
|
|
||||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
|
|
||||||
(_("Error reading %d bytes on device '%s'."),
|
|
||||||
v4l2src->frame_byte_size, v4l2src->v4l2object->videodev), (NULL));
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
|
gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
|
||||||
{
|
{
|
||||||
GstV4l2Src *v4l2src = GST_V4L2SRC (src);
|
GstV4l2Src *v4l2src = GST_V4L2SRC (src);
|
||||||
|
GstV4l2Object *obj = v4l2src->v4l2object;
|
||||||
int i;
|
int i;
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
|
|
||||||
if (v4l2src->get_frame == NULL)
|
|
||||||
goto not_negotiated;
|
|
||||||
|
|
||||||
/* decimate, just capture and throw away frames */
|
/* decimate, just capture and throw away frames */
|
||||||
for (i = 0; i < v4l2src->decimate - 1; i++) {
|
for (i = 0; i < v4l2src->decimate - 1; i++) {
|
||||||
ret = v4l2src->get_frame (v4l2src, buf);
|
ret = gst_v4l2_object_get_buffer (obj, buf);
|
||||||
if (ret != GST_FLOW_OK) {
|
if (ret != GST_FLOW_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
gst_buffer_unref (*buf);
|
gst_buffer_unref (*buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = v4l2src->get_frame (v4l2src, buf);
|
ret = gst_v4l2_object_get_buffer (obj, buf);
|
||||||
|
|
||||||
/* set buffer metadata */
|
/* set buffer metadata */
|
||||||
if (G_LIKELY (ret == GST_FLOW_OK && *buf)) {
|
if (G_LIKELY (ret == GST_FLOW_OK && *buf)) {
|
||||||
|
@ -857,7 +718,7 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
|
||||||
}
|
}
|
||||||
GST_OBJECT_UNLOCK (v4l2src);
|
GST_OBJECT_UNLOCK (v4l2src);
|
||||||
|
|
||||||
duration = v4l2src->v4l2object->duration;
|
duration = obj->duration;
|
||||||
|
|
||||||
if (G_LIKELY (clock)) {
|
if (G_LIKELY (clock)) {
|
||||||
/* the time now is the time of the clock minus the base time */
|
/* the time now is the time of the clock minus the base time */
|
||||||
|
@ -891,13 +752,6 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
|
||||||
GST_BUFFER_DURATION (*buf) = duration;
|
GST_BUFFER_DURATION (*buf) = duration;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
not_negotiated:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (src, "we are not negotiated");
|
|
||||||
return GST_FLOW_NOT_NEGOTIATED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,6 @@ G_BEGIN_DECLS
|
||||||
typedef struct _GstV4l2Src GstV4l2Src;
|
typedef struct _GstV4l2Src GstV4l2Src;
|
||||||
typedef struct _GstV4l2SrcClass GstV4l2SrcClass;
|
typedef struct _GstV4l2SrcClass GstV4l2SrcClass;
|
||||||
|
|
||||||
typedef GstFlowReturn (*GstV4l2SrcGetFunc)(GstV4l2Src * v4l2src, GstBuffer ** buf);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GstV4l2Src:
|
* GstV4l2Src:
|
||||||
*
|
*
|
||||||
|
@ -64,16 +62,11 @@ struct _GstV4l2Src
|
||||||
|
|
||||||
guint32 frame_byte_size;
|
guint32 frame_byte_size;
|
||||||
|
|
||||||
/* if the buffer will be or not used from directly mmap */
|
|
||||||
gboolean always_copy;
|
|
||||||
|
|
||||||
int decimate;
|
int decimate;
|
||||||
|
|
||||||
guint64 offset;
|
guint64 offset;
|
||||||
|
|
||||||
GstClockTime ctrl_time;
|
GstClockTime ctrl_time;
|
||||||
|
|
||||||
GstV4l2SrcGetFunc get_frame;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstV4l2SrcClass
|
struct _GstV4l2SrcClass
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include "gstv4l2tuner.h"
|
#include "gstv4l2tuner.h"
|
||||||
#include "gstv4l2object.h"
|
#include "gstv4l2object.h"
|
||||||
#include "v4l2_calls.h"
|
#include "v4l2_calls.h"
|
||||||
#include "v4l2src_calls.h"
|
|
||||||
|
|
||||||
static void gst_v4l2_tuner_channel_class_init (GstV4l2TunerChannelClass *
|
static void gst_v4l2_tuner_channel_class_init (GstV4l2TunerChannelClass *
|
||||||
klass);
|
klass);
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include "gstv4l2vidorient.h"
|
#include "gstv4l2vidorient.h"
|
||||||
#include "gstv4l2object.h"
|
#include "gstv4l2object.h"
|
||||||
#include "v4l2_calls.h"
|
#include "v4l2_calls.h"
|
||||||
#include "v4l2src_calls.h"
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (v4l2vo_debug);
|
GST_DEBUG_CATEGORY_STATIC (v4l2vo_debug);
|
||||||
#define GST_CAT_DEFAULT v4l2vo_debug
|
#define GST_CAT_DEFAULT v4l2vo_debug
|
||||||
|
|
|
@ -1,167 +0,0 @@
|
||||||
/* GStreamer
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
|
||||||
* 2006 Edgard Lima <edgard.lima@indt.org.br>
|
|
||||||
*
|
|
||||||
* v4l2src.c - system calls
|
|
||||||
*
|
|
||||||
* 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 <stdlib.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include "v4l2src_calls.h"
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#ifdef __sun
|
|
||||||
/* Needed on older Solaris Nevada builds (72 at least) */
|
|
||||||
#include <stropts.h>
|
|
||||||
#include <sys/ioccom.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "gstv4l2tuner.h"
|
|
||||||
#include "gstv4l2bufferpool.h"
|
|
||||||
|
|
||||||
#include "gst/gst-i18n-plugin.h"
|
|
||||||
|
|
||||||
#define GST_CAT_DEFAULT v4l2src_debug
|
|
||||||
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************
|
|
||||||
* gst_v4l2src_grab_frame ():
|
|
||||||
* grab a frame for capturing
|
|
||||||
* return value: GST_FLOW_OK, GST_FLOW_WRONG_STATE or GST_FLOW_ERROR
|
|
||||||
******************************************************/
|
|
||||||
GstFlowReturn
|
|
||||||
gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
|
||||||
{
|
|
||||||
#define NUM_TRIALS 50
|
|
||||||
GstV4l2Object *v4l2object;
|
|
||||||
GstV4l2BufferPool *pool;
|
|
||||||
gint32 trials = NUM_TRIALS;
|
|
||||||
GstBuffer *pool_buffer;
|
|
||||||
gboolean need_copy;
|
|
||||||
gint ret;
|
|
||||||
|
|
||||||
v4l2object = v4l2src->v4l2object;
|
|
||||||
pool = v4l2object->pool;
|
|
||||||
if (!pool)
|
|
||||||
goto no_buffer_pool;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (v4l2src, "grab frame");
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (v4l2object->can_poll_device) {
|
|
||||||
ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
|
|
||||||
if (G_UNLIKELY (ret < 0)) {
|
|
||||||
if (errno == EBUSY)
|
|
||||||
goto stopped;
|
|
||||||
if (errno == ENXIO) {
|
|
||||||
GST_DEBUG_OBJECT (v4l2src,
|
|
||||||
"v4l2 device doesn't support polling. Disabling");
|
|
||||||
v4l2object->can_poll_device = FALSE;
|
|
||||||
} else {
|
|
||||||
if (errno != EAGAIN && errno != EINTR)
|
|
||||||
goto select_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pool_buffer = gst_v4l2_buffer_pool_dqbuf (pool);
|
|
||||||
if (pool_buffer)
|
|
||||||
break;
|
|
||||||
|
|
||||||
GST_WARNING_OBJECT (v4l2src, "trials=%d", trials);
|
|
||||||
|
|
||||||
/* if the sync() got interrupted, we can retry */
|
|
||||||
switch (errno) {
|
|
||||||
case EINVAL:
|
|
||||||
case ENOMEM:
|
|
||||||
/* fatal */
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
|
|
||||||
case EAGAIN:
|
|
||||||
case EIO:
|
|
||||||
case EINTR:
|
|
||||||
default:
|
|
||||||
/* try again, until too many trials */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check nr. of attempts to capture */
|
|
||||||
if (--trials == -1) {
|
|
||||||
goto too_many_trials;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if we are handing out the last buffer in the pool, we need to make a
|
|
||||||
* copy and bring the buffer back in the pool. */
|
|
||||||
need_copy = v4l2src->always_copy
|
|
||||||
|| !gst_v4l2_buffer_pool_available_buffers (pool);
|
|
||||||
|
|
||||||
if (G_UNLIKELY (need_copy)) {
|
|
||||||
if (!v4l2src->always_copy) {
|
|
||||||
GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2src,
|
|
||||||
"running out of buffers, making a copy to reuse current one");
|
|
||||||
}
|
|
||||||
*buf = gst_buffer_copy (pool_buffer);
|
|
||||||
/* this will requeue */
|
|
||||||
gst_buffer_unref (pool_buffer);
|
|
||||||
} else {
|
|
||||||
*buf = pool_buffer;
|
|
||||||
}
|
|
||||||
/* we set the buffer metadata in gst_v4l2src_create() */
|
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
no_buffer_pool:
|
|
||||||
{
|
|
||||||
GST_DEBUG ("no buffer pool");
|
|
||||||
return GST_FLOW_WRONG_STATE;
|
|
||||||
}
|
|
||||||
select_error:
|
|
||||||
{
|
|
||||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL),
|
|
||||||
("select error %d: %s (%d)", ret, g_strerror (errno), errno));
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
stopped:
|
|
||||||
{
|
|
||||||
GST_DEBUG ("stop called");
|
|
||||||
return GST_FLOW_WRONG_STATE;
|
|
||||||
}
|
|
||||||
too_many_trials:
|
|
||||||
{
|
|
||||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, FAILED,
|
|
||||||
(_("Failed trying to get video frames from device '%s'."),
|
|
||||||
v4l2object->videodev),
|
|
||||||
(_("Failed after %d tries. device %s. system error: %s"),
|
|
||||||
NUM_TRIALS, v4l2object->videodev, g_strerror (errno)));
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/* GStreamer
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
|
||||||
* 2006 Edgard Lima <edgard.lima@indt.org.br>
|
|
||||||
*
|
|
||||||
* v4l2src.h - system calls
|
|
||||||
*
|
|
||||||
* 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 __V4L2SRC_CALLS_H__
|
|
||||||
#define __V4L2SRC_CALLS_H__
|
|
||||||
|
|
||||||
#include "gstv4l2src.h"
|
|
||||||
#include "v4l2_calls.h"
|
|
||||||
|
|
||||||
GstFlowReturn gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer **buf);
|
|
||||||
|
|
||||||
#endif /* __V4L2SRC_CALLS_H__ */
|
|
Loading…
Reference in a new issue