v4l2object->video_fd = -1;
v4l2object->poll = gst_poll_new (TRUE);
- v4l2object->buffer = NULL;
+ v4l2object->active = FALSE;
v4l2object->videodev = g_strdup (default_device);
v4l2object->norms = NULL;
return TRUE;
}
+/* Note about fraction simplification
+ * * n1/d1 == n2/d2 is also written as n1 == ( n2 * d1 ) / d2
+ * */
+#define fractions_are_equal(n1,d1,n2,d2) ((n1) == gst_util_uint64_scale_int((n2), (d1), (d2)))
+
gboolean
gst_v4l2_object_set_format (GstV4l2Object * v4l2object, GstCaps * caps)
{
gint fd = v4l2object->video_fd;
struct v4l2_format *format;
+ struct v4l2_streamparm *streamparm;
enum v4l2_field field;
guint32 pixelformat;
gint width;
&fmtdesc, &width, &height, &interlaced, &fps_n, &fps_d, &size))
goto invalid_caps;
- v4l2object->fps_n = fps_n;
- v4l2object->fps_d = fps_d;
v4l2object->size = size;
- v4l2object->width = width;
- v4l2object->height = height;
pixelformat = fmtdesc->pixelformat;
field = V4L2_FIELD_NONE;
}
- GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format "
+ GST_DEBUG_OBJECT (v4l2object->element, "Desired format %dx%d, format "
"%" GST_FOURCC_FORMAT, width, height, GST_FOURCC_ARGS (pixelformat));
GST_V4L2_CHECK_OPEN (v4l2object);
/* Only unconditionally accept mpegts for sources */
if ((v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
(pixelformat == GST_MAKE_FOURCC ('M', 'P', 'E', 'G')))
- return TRUE;
+ goto done;
format = &v4l2object->format;
goto get_fmt_failed;
GST_DEBUG_OBJECT (v4l2object->element, "Got format to %dx%d, format "
- "%" GST_FOURCC_FORMAT " stride %d", format->fmt.pix.width,
+ "%" GST_FOURCC_FORMAT " bytesperline %d", format->fmt.pix.width,
format->fmt.pix.height, GST_FOURCC_ARGS (format->fmt.pix.pixelformat),
format->fmt.pix.bytesperline);
- if (format->type == v4l2object->type &&
- format->fmt.pix.width == width &&
- format->fmt.pix.height == height &&
- format->fmt.pix.pixelformat == pixelformat &&
- format->fmt.pix.field == field) {
- GST_DEBUG_OBJECT (v4l2object->element, "format was good");
- /* Nothing to do. We want to succeed immediately
- * here because setting the same format back
- * can still fail due to EBUSY. By short-circuiting
- * here, we allow pausing and re-playing pipelines
- * with changed caps, as long as the changed caps
- * do not change the webcam's format. Otherwise,
- * any caps change would require us to go to NULL
- * state to close the device and set format.
- */
- return TRUE;
+ if (format->type != v4l2object->type ||
+ GST_V4L2_WIDTH (v4l2object) != width ||
+ GST_V4L2_HEIGHT (v4l2object) != height ||
+ GST_V4L2_PIXELFORMAT (v4l2object) != pixelformat ||
+ GST_V4L2_FIELD (v4l2object) != field) {
+ /* something different, set the format */
+ GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format "
+ "%" GST_FOURCC_FORMAT, width, height, GST_FOURCC_ARGS (pixelformat));
+
+ format->type = v4l2object->type;
+ format->fmt.pix.width = width;
+ format->fmt.pix.height = height;
+ format->fmt.pix.pixelformat = pixelformat;
+ format->fmt.pix.field = field;
+
+ if (v4l2_ioctl (fd, VIDIOC_S_FMT, format) < 0)
+ goto set_fmt_failed;
+
+ GST_DEBUG_OBJECT (v4l2object->element, "Got format to %dx%d, format "
+ "%" GST_FOURCC_FORMAT " stride %d", format->fmt.pix.width,
+ format->fmt.pix.height, GST_FOURCC_ARGS (format->fmt.pix.pixelformat),
+ format->fmt.pix.bytesperline);
+
+ if (format->fmt.pix.width != width || format->fmt.pix.height != height)
+ goto invalid_dimensions;
+
+ if (format->fmt.pix.pixelformat != pixelformat)
+ goto invalid_pixelformat;
}
- GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format "
- "%" GST_FOURCC_FORMAT, width, height, GST_FOURCC_ARGS (pixelformat));
+ /* Is there a reason we require the caller to always specify a framerate? */
+ GST_DEBUG_OBJECT (v4l2object->element, "Desired framerate: %u/%u", fps_n,
+ fps_d);
+
+ streamparm = &v4l2object->streamparm;
+
+ memset (streamparm, 0x00, sizeof (struct v4l2_streamparm));
+ streamparm->type = v4l2object->type;
+
+ if (v4l2_ioctl (fd, VIDIOC_G_PARM, streamparm) < 0)
+ goto get_parm_failed;
+
+ GST_DEBUG_OBJECT (v4l2object->element, "Got framerate: %u/%u",
+ streamparm->parm.capture.timeperframe.denominator,
+ streamparm->parm.capture.timeperframe.numerator);
+
+ /* Note: V4L2 provides the frame interval, we have the frame rate */
+ if (!fractions_are_equal (streamparm->parm.capture.timeperframe.numerator,
+ streamparm->parm.capture.timeperframe.denominator, fps_d, fps_n)) {
+ GST_LOG_OBJECT (v4l2object->element, "Setting framerate to %u/%u", fps_n,
+ fps_d);
+ /* We want to change the frame rate, so check whether we can. Some cheap USB
+ * cameras don't have the capability */
+ if ((streamparm->parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) {
+ GST_DEBUG_OBJECT (v4l2object->element,
+ "Not setting framerate (not supported)");
+ goto done;
+ }
- format->type = v4l2object->type;
- format->fmt.pix.width = width;
- format->fmt.pix.height = height;
- format->fmt.pix.pixelformat = pixelformat;
- format->fmt.pix.field = field;
+ /* Note: V4L2 wants the frame interval, we have the frame rate */
+ streamparm->parm.capture.timeperframe.numerator = fps_d;
+ streamparm->parm.capture.timeperframe.denominator = fps_n;
- if (v4l2_ioctl (fd, VIDIOC_S_FMT, format) < 0)
- goto set_fmt_failed;
+ /* some cheap USB cam's won't accept any change */
+ if (v4l2_ioctl (fd, VIDIOC_S_PARM, streamparm) < 0)
+ goto set_parm_failed;
- GST_DEBUG_OBJECT (v4l2object->element, "Got format to %dx%d, format "
- "%" GST_FOURCC_FORMAT " stride %d", format->fmt.pix.width,
- format->fmt.pix.height, GST_FOURCC_ARGS (format->fmt.pix.pixelformat),
- format->fmt.pix.bytesperline);
+ /* get new values */
+ fps_d = streamparm->parm.capture.timeperframe.numerator;
+ fps_n = streamparm->parm.capture.timeperframe.denominator;
- if (format->fmt.pix.width != width || format->fmt.pix.height != height)
- goto invalid_dimensions;
+ GST_INFO_OBJECT (v4l2object->element, "Set framerate to %u/%u", fps_n,
+ fps_d);
+ }
- if (format->fmt.pix.pixelformat != pixelformat)
- goto invalid_pixelformat;
+done:
+ /* if we have a framerate pre-calculate duration */
+ if (fps_n > 0 && fps_d > 0) {
+ v4l2object->duration = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
+ } else {
+ v4l2object->duration = GST_CLOCK_TIME_NONE;
+ }
return TRUE;
GST_FOURCC_ARGS (format->fmt.pix.pixelformat)));
return FALSE;
}
+get_parm_failed:
+ {
+ GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+ (_("Could not get parameters on device '%s'"),
+ v4l2object->videodev), GST_ERROR_SYSTEM);
+ goto done;
+ }
+set_parm_failed:
+ {
+ GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+ (_("Video input device did not accept new frame rate setting.")),
+ GST_ERROR_SYSTEM);
+ goto done;
+ }
}
gboolean
typedef gboolean (*GstV4l2SetInOutFunction) (GstV4l2Object * v4l2object, gint input);
typedef gboolean (*GstV4l2UpdateFpsFunction) (GstV4l2Object * v4l2object);
+#define GST_V4L2_WIDTH(o) ((o)->format.fmt.pix.width)
+#define GST_V4L2_HEIGHT(o) ((o)->format.fmt.pix.height)
+#define GST_V4L2_PIXELFORMAT(o) ((o)->format.fmt.pix.pixelformat)
+#define GST_V4L2_FIELD(o) ((o)->format.fmt.pix.field)
+#define GST_V4L2_FPS_N(o) ((o)->streamparm.parm.capture.timeperframe.denominator)
+#define GST_V4L2_FPS_D(o) ((o)->streamparm.parm.capture.timeperframe.numerator)
+
+/* simple check whether the device is open */
+#define GST_V4L2_IS_OPEN(o) ((o)->video_fd > 0)
+
+/* check whether the device is 'active' */
+#define GST_V4L2_IS_ACTIVE(o) ((o)->active)
+#define GST_V4L2_SET_ACTIVE(o) ((o)->active = TRUE)
+#define GST_V4L2_SET_INACTIVE(o) ((o)->active = FALSE)
+
struct _GstV4l2Object {
GstElement * element;
+ enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT */
+
/* the video device */
char *videodev;
GstPoll * poll;
gboolean can_poll_device;
- /* the video buffer (mmap()'ed) */
- guint8 **buffer;
-
- enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT */
+ gboolean active;
/* the current format */
struct v4l2_format format;
- guint width, height;
- guint fps_n, fps_d;
+ struct v4l2_streamparm streamparm;
guint size;
+ GstClockTime duration;
/* the video device's capabilities */
struct v4l2_capability vcap;
g_object_notify (G_OBJECT (v4l2sink), "queue-size");
}
- v4l2sink->video_width = v4l2sink->v4l2object->width;
- v4l2sink->video_height = v4l2sink->v4l2object->height;
+ v4l2sink->video_width = GST_V4L2_WIDTH (v4l2sink->v4l2object);
+ v4l2sink->video_height = GST_V4L2_HEIGHT (v4l2sink->v4l2object);
/* TODO: videosink width/height should be scaled according to
* pixel-aspect-ratio
gst_base_src_set_format (GST_BASE_SRC (v4l2src), GST_FORMAT_TIME);
gst_base_src_set_live (GST_BASE_SRC (v4l2src), TRUE);
-
- v4l2src->fps_d = 0;
- v4l2src->fps_n = 0;
}
-
static void
gst_v4l2src_dispose (GObject * object)
{
return FALSE;
}
- if (!gst_v4l2src_set_capture (v4l2src, caps))
+ if (!gst_v4l2_object_set_format (v4l2src->v4l2object, caps))
/* error already posted */
return FALSE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_LATENCY:{
GstClockTime min_latency, max_latency;
+ guint32 fps_n, fps_d;
/* device must be open */
if (!GST_V4L2_IS_OPEN (src->v4l2object)) {
goto done;
}
+ fps_n = GST_V4L2_FPS_N (src->v4l2object);
+ fps_d = GST_V4L2_FPS_D (src->v4l2object);
+
/* we must have a framerate */
- if (src->fps_n <= 0 || src->fps_d <= 0) {
+ if (fps_n <= 0 || fps_d <= 0) {
GST_WARNING_OBJECT (src,
"Can't give latency since framerate isn't fixated !");
goto done;
}
/* min latency is the time to capture one frame */
- min_latency =
- gst_util_uint64_scale_int (GST_SECOND, src->fps_d, src->fps_n);
+ min_latency = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
/* max latency is total duration of the frame buffer */
max_latency = src->num_buffers * min_latency;
{
GstV4l2Src *v4l2src = GST_V4L2SRC (src);
- if (GST_V4L2_IS_ACTIVE (v4l2src->v4l2object)
- && !gst_v4l2src_capture_stop (v4l2src))
- return FALSE;
-
- if (v4l2src->v4l2object->buffer != NULL) {
+ if (GST_V4L2_IS_ACTIVE (v4l2src->v4l2object)) {
+ if (!gst_v4l2src_capture_stop (v4l2src))
+ return FALSE;
if (!gst_v4l2src_capture_deinit (v4l2src))
return FALSE;
}
-
- v4l2src->fps_d = 0;
- v4l2src->fps_n = 0;
-
return TRUE;
}
/* set buffer metadata */
if (G_LIKELY (ret == GST_FLOW_OK && *buf)) {
GstClock *clock;
- GstClockTime timestamp;
+ GstClockTime timestamp, duration;
GST_BUFFER_OFFSET (*buf) = v4l2src->offset++;
GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset;
}
GST_OBJECT_UNLOCK (v4l2src);
+ duration = v4l2src->v4l2object->duration;
+
if (G_LIKELY (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);
/* if we have a framerate adjust timestamp for frame latency */
- if (GST_CLOCK_TIME_IS_VALID (v4l2src->duration)) {
- if (timestamp > v4l2src->duration)
- timestamp -= v4l2src->duration;
+ if (GST_CLOCK_TIME_IS_VALID (duration)) {
+ if (timestamp > duration)
+ timestamp -= duration;
else
timestamp = 0;
}
}
/* activate settings for next frame */
- if (GST_CLOCK_TIME_IS_VALID (v4l2src->duration)) {
- v4l2src->ctrl_time += v4l2src->duration;
+ if (GST_CLOCK_TIME_IS_VALID (duration)) {
+ v4l2src->ctrl_time += duration;
} else {
/* this is not very good (as it should be the next timestamp),
* still good enough for linear fades (as long as it is not -1)
/* FIXME: use the timestamp from the buffer itself! */
GST_BUFFER_TIMESTAMP (*buf) = timestamp;
- GST_BUFFER_DURATION (*buf) = v4l2src->duration;
+ GST_BUFFER_DURATION (*buf) = duration;
}
return ret;
guint64 offset;
- gint fps_d, fps_n; /* framerate if device is open */
- GstClockTime duration; /* duration of one frame */
-
GstClockTime ctrl_time;
GstV4l2SrcGetFunc get_frame;
# define v4l2_munmap munmap
#endif
-/* simple check whether the device is open */
-#define GST_V4L2_IS_OPEN(v4l2object) \
- (v4l2object->video_fd > 0)
-
-/* check whether the device is 'active' */
-#define GST_V4L2_IS_ACTIVE(v4l2object) \
- (v4l2object->buffer != NULL)
-
#define GST_V4L2_IS_OVERLAY(v4l2object) \
(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_OVERLAY)
#define GST_CAT_DEFAULT v4l2src_debug
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
-/* lalala... */
-#define GST_V4L2_SET_ACTIVE(element) (element)->buffer = GINT_TO_POINTER (-1)
-#define GST_V4L2_SET_INACTIVE(element) (element)->buffer = NULL
-
-/* On some systems MAP_FAILED seems to be missing */
-#ifndef MAP_FAILED
-#define MAP_FAILED ((caddr_t) -1)
-#endif
-
-
/* Local functions */
static gboolean
}
}
-/* Note about fraction simplification
- * n1/d1 == n2/d2 is also written as n1 == ( n2 * d1 ) / d2
- */
-#define fractions_are_equal(n1,d1,n2,d2) ((n1) == gst_util_uint64_scale_int((n2), (d1), (d2)))
-
-/******************************************************
- * gst_v4l2src_set_capture():
- * set capture parameters
- * return value: TRUE on success, FALSE on error
- ******************************************************/
-gboolean
-gst_v4l2src_set_capture (GstV4l2Src * v4l2src, GstCaps * caps)
-{
- gint fd = v4l2src->v4l2object->video_fd;
- struct v4l2_streamparm stream;
- guint fps_n, fps_d;
-
- if (!gst_v4l2_object_set_format (v4l2src->v4l2object, caps))
- /* error already reported */
- return FALSE;
-
- fps_n = v4l2src->v4l2object->fps_n;
- fps_d = v4l2src->v4l2object->fps_d;
-
- /* Is there a reason we require the caller to always specify a framerate? */
- GST_DEBUG_OBJECT (v4l2src, "Desired framerate: %u/%u", fps_n, fps_d);
-
- memset (&stream, 0x00, sizeof (struct v4l2_streamparm));
- stream.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (v4l2_ioctl (fd, VIDIOC_G_PARM, &stream) < 0) {
- GST_ELEMENT_WARNING (v4l2src, RESOURCE, SETTINGS,
- (_("Could not get parameters on device '%s'"),
- v4l2src->v4l2object->videodev), GST_ERROR_SYSTEM);
- goto done;
- }
-
- /* Note: V4L2 provides the frame interval, we have the frame rate */
- if (fractions_are_equal (stream.parm.capture.timeperframe.numerator,
- stream.parm.capture.timeperframe.denominator, fps_d, fps_n)) {
- GST_DEBUG_OBJECT (v4l2src, "Desired framerate already set");
- goto already_set;
- }
-
- /* We want to change the frame rate, so check whether we can. Some cheap USB
- * cameras don't have the capability */
- if ((stream.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) {
- GST_DEBUG_OBJECT (v4l2src, "Not setting framerate (not supported)");
- goto done;
- }
-
- GST_LOG_OBJECT (v4l2src, "Setting framerate to %u/%u", fps_n, fps_d);
-
- /* Note: V4L2 wants the frame interval, we have the frame rate */
- stream.parm.capture.timeperframe.numerator = fps_d;
- stream.parm.capture.timeperframe.denominator = fps_n;
-
- /* some cheap USB cam's won't accept any change */
- if (v4l2_ioctl (fd, VIDIOC_S_PARM, &stream) < 0) {
- GST_ELEMENT_WARNING (v4l2src, RESOURCE, SETTINGS,
- (_("Video input device did not accept new frame rate setting.")),
- GST_ERROR_SYSTEM);
- goto done;
- }
-
-already_set:
-
- v4l2src->fps_n = fps_n;
- v4l2src->fps_d = fps_d;
-
- /* if we have a framerate pre-calculate duration */
- if (fps_n > 0 && fps_d > 0) {
- v4l2src->duration = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
- } else {
- v4l2src->duration = GST_CLOCK_TIME_NONE;
- }
-
- GST_INFO_OBJECT (v4l2src,
- "Set framerate to %u/%u and duration to %" GST_TIME_FORMAT, fps_n, fps_d,
- GST_TIME_ARGS (v4l2src->duration));
-done:
-
- return TRUE;
-}
-
/******************************************************
* gst_v4l2src_capture_init():
* initialize the capture system
{
GST_DEBUG_OBJECT (v4l2src, "stopping capturing");
- if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) {
+ if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object))
goto done;
- }
- if (!GST_V4L2_IS_ACTIVE (v4l2src->v4l2object)) {
+ if (!GST_V4L2_IS_ACTIVE (v4l2src->v4l2object))
goto done;
- }
if (v4l2src->use_mmap) {
/* we actually need to sync on all queued buffers but not
#include "gstv4l2src.h"
#include "v4l2_calls.h"
-gboolean gst_v4l2src_set_capture (GstV4l2Src * v4l2src, GstCaps *caps);
-
gboolean gst_v4l2src_capture_init (GstV4l2Src * v4l2src);
gboolean gst_v4l2src_capture_start (GstV4l2Src * v4l2src);