* 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,
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
{GST_V4L2_IO_RW, "GST_V4L2_IO_RW", "rw"},
{GST_V4L2_IO_MMAP, "GST_V4L2_IO_MMAP", "mmap"},
{GST_V4L2_IO_USERPTR, "GST_V4L2_IO_USERPTR", "userptr"},
+ {GST_V4L2_IO_DMABUF, "GST_V4L2_IO_DMABUF", "dmabuf"},
{0, NULL, NULL}
};
"I/O mode",
GST_TYPE_V4L2_IO_MODE, DEFAULT_PROP_IO_MODE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstV4l2Src:extra-controls
+ *
+ * Additional v4l2 controls for the device. The controls are identified
+ * by the control name (lowercase with '_' for any non-alphanumeric
+ * characters).
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property (gobject_class, PROP_EXTRA_CONTROLS,
+ g_param_spec_boxed ("extra-controls", "Extra Controls",
+ "Extra v4l2 controls (CIDs) for the device",
+ GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstV4l2Src:pixel-aspect-ratio
+ *
+ * The pixel aspect ratio of the device. This overwrites the pixel aspect
+ * ratio queried from the device.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
+ g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
+ "Overwrite the pixel aspect ratio of the device", "1/1",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstV4l2Src:force-aspect-ratio
+ *
+ * When enabled, the pixel aspect ratio queried from the device or set
+ * with the pixel-aspect-ratio property will be enforced.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
+ g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
+ "When enabled, the pixel aspect ratio will be enforced", TRUE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
}
GstV4l2Object *
v4l2object->xwindow_id = 0;
+ v4l2object->keep_aspect = TRUE;
+
return v4l2object;
}
case PROP_IO_MODE:
v4l2object->req_mode = g_value_get_enum (value);
break;
+ case PROP_EXTRA_CONTROLS:{
+ const GstStructure *s = gst_value_get_structure (value);
+
+ if (v4l2object->extra_controls)
+ gst_structure_free (v4l2object->extra_controls);
+
+ v4l2object->extra_controls = s ? gst_structure_copy (s) : NULL;
+ if (GST_V4L2_IS_OPEN (v4l2object))
+ gst_v4l2_set_controls (v4l2object, v4l2object->extra_controls);
+ break;
+ }
+ case PROP_PIXEL_ASPECT_RATIO:
+ g_free (v4l2object->par);
+ v4l2object->par = g_new0 (GValue, 1);
+ g_value_init (v4l2object->par, GST_TYPE_FRACTION);
+ if (!g_value_transform (value, v4l2object->par)) {
+ g_warning ("Could not transform string to aspect ratio");
+ gst_value_set_fraction (v4l2object->par, 1, 1);
+ }
+ GST_DEBUG_OBJECT (v4l2object->element, "set PAR to %d/%d",
+ gst_value_get_fraction_numerator (v4l2object->par),
+ gst_value_get_fraction_denominator (v4l2object->par));
+ break;
+ case PROP_FORCE_ASPECT_RATIO:
+ v4l2object->keep_aspect = g_value_get_boolean (value);
+ break;
default:
return FALSE;
break;
case PROP_IO_MODE:
g_value_set_enum (value, v4l2object->req_mode);
break;
+ case PROP_EXTRA_CONTROLS:
+ gst_value_set_structure (value, v4l2object->extra_controls);
+ break;
+ case PROP_PIXEL_ASPECT_RATIO:
+ if (v4l2object->par)
+ g_value_transform (v4l2object->par, value);
+ break;
+ case PROP_FORCE_ASPECT_RATIO:
+ g_value_set_boolean (value, v4l2object->keep_aspect);
+ break;
default:
return FALSE;
break;
#endif
{V4L2_PIX_FMT_DV, TRUE},
{V4L2_PIX_FMT_MPEG, FALSE},
+#ifdef V4L2_PIX_FMT_MPEG4
+ {V4L2_PIX_FMT_MPEG4, TRUE},
+#endif
+
+#ifdef V4L2_PIX_FMT_H263
+ {V4L2_PIX_FMT_H263, TRUE},
+#endif
+#ifdef V4L2_PIX_FMT_H264
+ {V4L2_PIX_FMT_H264, TRUE},
+#endif
/* Vendor-specific formats */
{V4L2_PIX_FMT_WNVA, TRUE},
case V4L2_PIX_FMT_HI240: /* 8 8-bit color */
/* FIXME: get correct fourccs here */
break;
+#ifdef V4L2_PIX_FMT_MPEG4
+ case V4L2_PIX_FMT_MPEG4:
+ structure = gst_structure_new ("video/mpeg",
+ "mpegversion", G_TYPE_INT, 4, "systemstream",
+ G_TYPE_BOOLEAN, FALSE, NULL);
+ break;
+#endif
+#ifdef V4L2_PIX_FMT_H263
+ case V4L2_PIX_FMT_H263:
+ structure = gst_structure_new ("video/x-h263",
+ "variant", G_TYPE_STRING, "itu", NULL);
+ break;
+#endif
+#ifdef V4L2_PIX_FMT_H264
+ case V4L2_PIX_FMT_H264: /* H.264 */
+ structure = gst_structure_new_empty ("video/x-h264");
+ break;
+#endif
case V4L2_PIX_FMT_RGB332:
case V4L2_PIX_FMT_RGB555X:
case V4L2_PIX_FMT_RGB565X:
break;
#endif
default:
+ format = GST_VIDEO_FORMAT_UNKNOWN;
g_assert_not_reached ();
break;
}
mimetype = gst_structure_get_name (structure);
- if (g_str_equal (mimetype, "video/x-raw")) {
- /* raw caps, parse into video info */
- if (!gst_video_info_from_caps (info, caps))
- goto invalid_format;
+ if (!gst_video_info_from_caps (info, caps))
+ goto invalid_format;
+ if (g_str_equal (mimetype, "video/x-raw")) {
switch (GST_VIDEO_INFO_FORMAT (info)) {
case GST_VIDEO_FORMAT_I420:
fourcc = V4L2_PIX_FMT_YUV420;
break;
}
} else {
- gboolean dimensions = TRUE;
-
- /* no video caps, construct videoinfo ourselves */
- gst_video_info_init (info);
-
if (g_str_equal (mimetype, "video/mpegts")) {
fourcc = V4L2_PIX_FMT_MPEG;
- dimensions = FALSE;
} else if (g_str_equal (mimetype, "video/x-dv")) {
fourcc = V4L2_PIX_FMT_DV;
} else if (g_str_equal (mimetype, "image/jpeg")) {
fourcc = V4L2_PIX_FMT_JPEG;
+#ifdef V4L2_PIX_FMT_MPEG4
+ } else if (g_str_equal (mimetype, "video/mpeg")) {
+ fourcc = V4L2_PIX_FMT_MPEG4;
+#endif
+#ifdef V4L2_PIX_FMT_H263
+ } else if (g_str_equal (mimetype, "video/x-h263")) {
+ fourcc = V4L2_PIX_FMT_H263;
+#endif
+#ifdef V4L2_PIX_FMT_H264
+ } else if (g_str_equal (mimetype, "video/x-h264")) {
+ fourcc = V4L2_PIX_FMT_H264;
+#endif
#ifdef V4L2_PIX_FMT_SBGGR8
} else if (g_str_equal (mimetype, "video/x-bayer")) {
fourcc = V4L2_PIX_FMT_SBGGR8;
fourcc = V4L2_PIX_FMT_PWC2;
}
#endif
-
- if (dimensions) {
- const gchar *interlace_mode;
-
- if (!gst_structure_get_int (structure, "width", &info->width))
- goto no_width;
-
- if (!gst_structure_get_int (structure, "height", &info->height))
- goto no_height;
-
- interlace_mode = gst_structure_get_string (structure, "interlace-mode");
- if (g_str_equal (interlace_mode, "progressive")) {
- info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
- } else {
- info->interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
- }
- if (!gst_structure_get_fraction (structure, "framerate", &info->fps_n,
- &info->fps_d))
- goto no_framerate;
- }
}
if (fourcc == 0)
return TRUE;
/* ERRORS */
-no_width:
- {
- GST_DEBUG_OBJECT (v4l2object, "no width");
- return FALSE;
- }
-no_height:
- {
- GST_DEBUG_OBJECT (v4l2object, "no height");
- return FALSE;
- }
-no_framerate:
- {
- GST_DEBUG_OBJECT (v4l2object, "no framerate");
- return FALSE;
- }
invalid_format:
{
GST_DEBUG_OBJECT (v4l2object, "invalid format");
gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object,
guint32 pixelformat, gint * width, gint * height, gboolean * interlaced);
+static void
+gst_v4l2_object_add_aspect_ratio (GstV4l2Object * v4l2object, GstStructure * s)
+{
+ struct v4l2_cropcap cropcap;
+ int num = 1, den = 1;
+
+ if (!v4l2object->keep_aspect)
+ return;
+
+ if (v4l2object->par) {
+ num = gst_value_get_fraction_numerator (v4l2object->par);
+ den = gst_value_get_fraction_denominator (v4l2object->par);
+ goto done;
+ }
+
+ memset (&cropcap, 0, sizeof (cropcap));
+
+ cropcap.type = v4l2object->type;
+ if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_CROPCAP, &cropcap) < 0)
+ goto cropcap_failed;
+
+ num = cropcap.pixelaspect.numerator;
+ den = cropcap.pixelaspect.denominator;
+
+done:
+ gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, num, den,
+ NULL);
+ return;
+
+cropcap_failed:
+ if (errno != ENOTTY)
+ GST_WARNING_OBJECT (v4l2object->element,
+ "Failed to probe pixel aspect ratio with VIDIOC_CROPCAP: %s",
+ g_strerror (errno));
+ goto done;
+}
+
/* The frame interval enumeration code first appeared in Linux 2.6.19. */
#ifdef VIDIOC_ENUM_FRAMEINTERVALS
gint int_width = width;
gint int_height = height;
- /* interlaced detection using VIDIOC_TRY/S_FMT */
- if (!gst_v4l2_object_get_nearest_size (v4l2object, pixelformat,
- &int_width, &int_height, &interlaced))
- return NULL;
+ if (!strcmp ((char *) v4l2object->vcap.driver, "uvcvideo")) {
+ /*
+ * UVC devices are never interlaced, and doing VIDIOC_TRY_FMT on them
+ * causes expensive and slow USB IO, so don't probe them for interlaced
+ */
+ interlaced = FALSE;
+ } else {
+ /* Interlaced detection using VIDIOC_TRY/S_FMT */
+ if (!gst_v4l2_object_get_nearest_size (v4l2object, pixelformat,
+ &int_width, &int_height, &interlaced))
+ return NULL;
+ }
memset (&ival, 0, sizeof (struct v4l2_frmivalenum));
ival.index = 0;
return_data:
s = gst_structure_copy (template);
gst_structure_set (s, "width", G_TYPE_INT, (gint) width,
- "height", G_TYPE_INT, (gint) height,
- "interlace-mode", G_TYPE_STRING, (interlaced ? "mixed" : "progressive"),
- "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
+ "height", G_TYPE_INT, (gint) height, NULL);
+ gst_v4l2_object_add_aspect_ratio (v4l2object, s);
+ if (g_str_equal (gst_structure_get_name (s), "video/x-raw"))
+ gst_structure_set (s, "interlace-mode", G_TYPE_STRING,
+ (interlaced ? "mixed" : "progressive"), NULL);
if (G_IS_VALUE (&rates)) {
/* only change the framerate on the template when we have a valid probed new
else
gst_structure_set (tmp, "height", GST_TYPE_INT_RANGE, min_h, max_h, NULL);
- gst_structure_set (tmp, "interlace-mode", G_TYPE_STRING,
- (interlaced ? "mixed" : "progressive"), NULL);
- gst_structure_set (tmp, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
- NULL);
+ if (g_str_equal (gst_structure_get_name (tmp), "video/x-raw"))
+ gst_structure_set (tmp, "interlace-mode", G_TYPE_STRING,
+ (interlaced ? "mixed" : "progressive"), NULL);
+ gst_v4l2_object_add_aspect_ratio (v4l2object, tmp);
gst_caps_append_structure (ret, tmp);
/* Some drivers are buggy and will modify the currently set format
when processing VIDIOC_TRY_FMT, so we remember what is set at the
minute, and will reset it when done. */
+ prevfmt.type = v4l2object->type;
prevfmt_valid = (v4l2_ioctl (fd, VIDIOC_G_FMT, &prevfmt) >= 0);
/* get size delimiters */
}
}
-
-/* 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)
{
fps_d = GST_VIDEO_INFO_FPS_D (&info);
stride = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0);
- if (info.flags & GST_VIDEO_FLAG_INTERLACED) {
+ if (GST_VIDEO_INFO_IS_INTERLACED (&info)) {
GST_DEBUG_OBJECT (v4l2object->element, "interlaced video");
/* ideally we would differentiate between types of interlaced video
* but there is not sufficient information in the caps..
GST_V4L2_CHECK_OPEN (v4l2object);
GST_V4L2_CHECK_NOT_ACTIVE (v4l2object);
+ /* MPEG-TS source cameras don't get their format set for some reason.
+ * It looks wrong and we weren't able to track down the reason for that code
+ * so it is disabled until someone who has an mpeg-ts camera complains...
+ */
+#if 0
/* Only unconditionally accept mpegts for sources */
if ((v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
(pixelformat == GST_MAKE_FOURCC ('M', 'P', 'E', 'G')))
goto done;
+#endif
memset (&format, 0x00, sizeof (struct v4l2_format));
format.type = v4l2object->type;
get_parm_failed:
{
/* it's possible that this call is not supported */
- if (errno != EINVAL) {
+ if (errno != EINVAL && errno != ENOTTY) {
GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
(_("Could not get parameters on device '%s'"),
v4l2object->videodev), GST_ERROR_SYSTEM);
}
gboolean
+gst_v4l2_object_caps_equal (GstV4l2Object * v4l2object, GstCaps * caps)
+{
+ GstStructure *s;
+ GstCaps *oldcaps;
+
+ if (!v4l2object->pool)
+ return FALSE;
+
+ s = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (v4l2object->pool));
+ gst_buffer_pool_config_get_params (s, &oldcaps, NULL, NULL, NULL);
+
+ return oldcaps && gst_caps_is_equal (caps, oldcaps);
+}
+
+gboolean
gst_v4l2_object_unlock (GstV4l2Object * v4l2object)
{
GST_LOG_OBJECT (v4l2object->element, "flush poll");
return TRUE;
}
-#if 0
-static GstFlowReturn
-gst_v4l2_object_get_mmap (GstV4l2Object * v4l2object, GstBuffer ** buf)
-{
- GstFlowReturn res;
-#define NUM_TRIALS 50
- GstBufferPool *pool;
- gint32 trials = NUM_TRIALS;
- GstBuffer *pool_buffer;
- gboolean need_copy;
-
- pool = v4l2object->pool;
- if (!pool)
- goto no_buffer_pool;
-
- GST_DEBUG_OBJECT (v4l2object->element, "grab frame");
-
- for (;;) {
- if ((res = gst_v4l2_object_poll (v4l2object)) != GST_FLOW_OK)
- goto poll_error;
-
- res = gst_buffer_pool_acquire_buffer (pool, &pool_buffer, NULL);
- if (res != GST_FLOW_OK)
- goto no_buffer;
-
- if (v4l2object->size > 0) {
- gsize size = gst_buffer_get_size (pool_buffer);
-
- /* 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 (pool_buffer);
- goto no_buffer;
- }
- }
- /* when we get here all is fine */
- break;
-
- no_buffer:
- 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_FLUSHING;
- }
-poll_error:
- {
- return res;
- }
-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;
- }
-}
-#endif
-
gboolean
gst_v4l2_object_copy (GstV4l2Object * v4l2object, GstBuffer * dest,
GstBuffer * src)
{
- if (v4l2object->info.finfo) {
+ const GstVideoFormatInfo *finfo = v4l2object->info.finfo;
+
+ if (finfo && (finfo->format != GST_VIDEO_FORMAT_UNKNOWN &&
+ finfo->format != GST_VIDEO_FORMAT_ENCODED)) {
GstVideoFrame src_frame, dest_frame;
GST_DEBUG_OBJECT (v4l2object->element, "copy video frame");
GST_DEBUG_OBJECT (v4l2object->element, "copy raw bytes");
gst_buffer_map (src, &map, GST_MAP_READ);
- gst_buffer_fill (dest, 0, map.data, map.size);
+ gst_buffer_fill (dest, 0, map.data, gst_buffer_get_size (src));
gst_buffer_unmap (src, &map);
+ gst_buffer_resize (dest, 0, gst_buffer_get_size (src));
}
GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2object->element,
"slow copy into buffer %p", dest);