From: Wim Taymans Date: Mon, 29 Aug 2011 11:43:59 +0000 (+0200) Subject: Merge branch 'master' into 0.11 X-Git-Tag: 1.19.3~509^2~7660 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5b55cf057bb16d05e3d228ca37c08bc81b69add3;hp=-c;p=platform%2Fupstream%2Fgstreamer.git Merge branch 'master' into 0.11 Conflicts: sys/v4l2/v4l2src_calls.c --- 5b55cf057bb16d05e3d228ca37c08bc81b69add3 diff --combined sys/v4l2/gstv4l2object.c index 4af80a1,24a4123..7be11b8 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@@ -35,14 -35,12 +35,14 @@@ #include "v4l2_calls.h" #include "gstv4l2tuner.h" #ifdef HAVE_XVIDEO -#include "gstv4l2xoverlay.h" +#include "gstv4l2videooverlay.h" #endif #include "gstv4l2colorbalance.h" #include "gst/gst-i18n-plugin.h" +#include + /* videodev2.h is not versioned and we can't easily check for the presence * of enum values at compile time, but the V4L2_CAP_VIDEO_OUTPUT_OVERLAY define * was added in the same commit as V4L2_FIELD_INTERLACED_{TB,BT} (b2787845) */ @@@ -52,16 -50,15 +52,16 @@@ #endif GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); +GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE); #define GST_CAT_DEFAULT v4l2_debug - #define DEFAULT_PROP_DEVICE_NAME NULL #define DEFAULT_PROP_DEVICE_FD -1 #define DEFAULT_PROP_FLAGS 0 #define DEFAULT_PROP_TV_NORM 0 #define DEFAULT_PROP_CHANNEL NULL #define DEFAULT_PROP_FREQUENCY 0 +#define DEFAULT_PROP_IO_MODE GST_V4L2_IO_AUTO enum { @@@ -69,21 -66,21 +69,21 @@@ V4L2_STD_OBJECT_PROPS, }; +G_LOCK_DEFINE_STATIC (probe_lock); + const GList * gst_v4l2_probe_get_properties (GstPropertyProbe * probe) { GObjectClass *klass = G_OBJECT_GET_CLASS (probe); static GList *list = NULL; - /* well, not perfect, but better than no locking at all. - * In the worst case we leak a list node, so who cares? */ - GST_CLASS_LOCK (GST_OBJECT_CLASS (klass)); + G_LOCK (probe_lock); if (!list) { list = g_list_append (NULL, g_object_class_find_property (klass, "device")); } - GST_CLASS_UNLOCK (GST_OBJECT_CLASS (klass)); + G_UNLOCK (probe_lock); return list; } @@@ -371,26 -368,6 +371,26 @@@ gst_v4l2_tv_norm_get_type (void return v4l2_tv_norm; } +#define GST_TYPE_V4L2_IO_MODE (gst_v4l2_io_mode_get_type ()) +static GType +gst_v4l2_io_mode_get_type (void) +{ + static GType v4l2_io_mode = 0; + + if (!v4l2_io_mode) { + static const GEnumValue io_modes[] = { + {GST_V4L2_IO_AUTO, "GST_V4L2_IO_AUTO", "auto"}, + {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"}, + + {0, NULL, NULL} + }; + v4l2_io_mode = g_enum_register_static ("GstV4l2IOMode", io_modes); + } + return v4l2_io_mode; +} + void gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class, const char *default_device) @@@ -472,17 -449,6 +472,17 @@@ "video standard", GST_TYPE_V4L2_TV_NORM, DEFAULT_PROP_TV_NORM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstV4l2Src:io-mode + * + * IO Mode + */ + g_object_class_install_property (gobject_class, PROP_IO_MODE, + g_param_spec_enum ("io-mode", "IO mode", + "I/O mode", + GST_TYPE_V4L2_IO_MODE, DEFAULT_PROP_IO_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } GstV4l2Object * @@@ -510,7 -476,7 +510,7 @@@ gst_v4l2_object_new (GstElement * eleme 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; @@@ -644,9 -610,6 +644,9 @@@ gst_v4l2_object_set_property_helper (Gs } break; #endif + case PROP_IO_MODE: + v4l2object->req_mode = g_value_get_enum (value); + break; default: return FALSE; break; @@@ -720,9 -683,6 +720,9 @@@ gst_v4l2_object_get_property_helper (Gs case PROP_TV_NORM: g_value_set_enum (value, v4l2object->tv_norm); break; + case PROP_IO_MODE: + g_value_set_enum (value, v4l2object->req_mode); + break; default: return FALSE; break; @@@ -789,7 -749,7 +789,7 @@@ gst_v4l2_set_defaults (GstV4l2Object * } gboolean -gst_v4l2_object_start (GstV4l2Object * v4l2object) +gst_v4l2_object_open (GstV4l2Object * v4l2object) { if (gst_v4l2_open (v4l2object)) gst_v4l2_set_defaults (v4l2object); @@@ -797,17 -757,17 +797,17 @@@ return FALSE; #ifdef HAVE_XVIDEO - gst_v4l2_xoverlay_start (v4l2object); + gst_v4l2_video_overlay_start (v4l2object); #endif return TRUE; } gboolean -gst_v4l2_object_stop (GstV4l2Object * v4l2object) +gst_v4l2_object_close (GstV4l2Object * v4l2object) { #ifdef HAVE_XVIDEO - gst_v4l2_xoverlay_stop (v4l2object); + gst_v4l2_video_overlay_stop (v4l2object); #endif if (!gst_v4l2_close (v4l2object)) @@@ -1187,22 -1147,97 +1187,22 @@@ gst_v4l2_object_v4l2fourcc_to_structur case V4L2_PIX_FMT_JPEG: /* JFIF JPEG */ structure = gst_structure_new ("image/jpeg", NULL); break; + case V4L2_PIX_FMT_YYUV: /* 16 YUV 4:2:2 */ + case V4L2_PIX_FMT_HI240: /* 8 8-bit color */ + /* FIXME: get correct fourccs here */ + break; case V4L2_PIX_FMT_RGB332: - case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_RGB555X: - case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_RGB565X: + /* FIXME: get correct fourccs here */ + break; + case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_RGB32: - case V4L2_PIX_FMT_BGR32:{ - guint depth = 0, bpp = 0; - - gint endianness = 0; - - guint32 r_mask = 0, b_mask = 0, g_mask = 0; - - switch (fourcc) { - case V4L2_PIX_FMT_RGB332: - bpp = depth = 8; - endianness = G_BYTE_ORDER; /* 'like, whatever' */ - r_mask = 0xe0; - g_mask = 0x1c; - b_mask = 0x03; - break; - case V4L2_PIX_FMT_RGB555: - case V4L2_PIX_FMT_RGB555X: - bpp = 16; - depth = 15; - endianness = - fourcc == V4L2_PIX_FMT_RGB555X ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; - r_mask = 0x7c00; - g_mask = 0x03e0; - b_mask = 0x001f; - break; - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - bpp = depth = 16; - endianness = - fourcc == V4L2_PIX_FMT_RGB565X ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; - r_mask = 0xf800; - g_mask = 0x07e0; - b_mask = 0x001f; - break; - case V4L2_PIX_FMT_RGB24: - bpp = depth = 24; - endianness = G_BIG_ENDIAN; - r_mask = 0xff0000; - g_mask = 0x00ff00; - b_mask = 0x0000ff; - break; - case V4L2_PIX_FMT_BGR24: - bpp = depth = 24; - endianness = G_BIG_ENDIAN; - r_mask = 0x0000ff; - g_mask = 0x00ff00; - b_mask = 0xff0000; - break; - case V4L2_PIX_FMT_RGB32: - bpp = depth = 32; - endianness = G_BIG_ENDIAN; - r_mask = 0xff000000; - g_mask = 0x00ff0000; - b_mask = 0x0000ff00; - break; - case V4L2_PIX_FMT_BGR32: - bpp = depth = 32; - endianness = G_BIG_ENDIAN; - r_mask = 0x000000ff; - g_mask = 0x0000ff00; - b_mask = 0x00ff0000; - break; - default: - g_assert_not_reached (); - break; - } - structure = gst_structure_new ("video/x-raw-rgb", - "bpp", G_TYPE_INT, bpp, - "depth", G_TYPE_INT, depth, - "red_mask", G_TYPE_INT, r_mask, - "green_mask", G_TYPE_INT, g_mask, - "blue_mask", G_TYPE_INT, b_mask, - "endianness", G_TYPE_INT, endianness, NULL); - break; - } - case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ - structure = gst_structure_new ("video/x-raw-gray", - "bpp", G_TYPE_INT, 8, NULL); - break; - case V4L2_PIX_FMT_YYUV: /* 16 YUV 4:2:2 */ - case V4L2_PIX_FMT_HI240: /* 8 8-bit color */ - /* FIXME: get correct fourccs here */ - break; + case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_NV12: /* 12 Y/CbCr 4:2:0 */ case V4L2_PIX_FMT_NV21: /* 12 Y/CrCb 4:2:0 */ case V4L2_PIX_FMT_YVU410: @@@ -1211,84 -1246,59 +1211,84 @@@ case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_UYVY: +#if 0 case V4L2_PIX_FMT_Y41P: +#endif case V4L2_PIX_FMT_YUV422P: #ifdef V4L2_PIX_FMT_YVYU case V4L2_PIX_FMT_YVYU: #endif case V4L2_PIX_FMT_YUV411P:{ - guint32 fcc = 0; + GstVideoFormat format; switch (fourcc) { + case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ + format = GST_VIDEO_FORMAT_GRAY8; + break; + case V4L2_PIX_FMT_RGB555: + format = GST_VIDEO_FORMAT_RGB15; + break; + case V4L2_PIX_FMT_RGB565: + format = GST_VIDEO_FORMAT_RGB16; + break; + case V4L2_PIX_FMT_RGB24: + format = GST_VIDEO_FORMAT_RGB; + break; + case V4L2_PIX_FMT_BGR24: + format = GST_VIDEO_FORMAT_BGR; + break; + case V4L2_PIX_FMT_RGB32: + format = GST_VIDEO_FORMAT_RGBx; + break; + case V4L2_PIX_FMT_BGR32: + format = GST_VIDEO_FORMAT_BGRx; + break; case V4L2_PIX_FMT_NV12: - fcc = GST_MAKE_FOURCC ('N', 'V', '1', '2'); + format = GST_VIDEO_FORMAT_NV12; break; case V4L2_PIX_FMT_NV21: - fcc = GST_MAKE_FOURCC ('N', 'V', '2', '1'); + format = GST_VIDEO_FORMAT_NV21; break; case V4L2_PIX_FMT_YVU410: - fcc = GST_MAKE_FOURCC ('Y', 'V', 'U', '9'); + format = GST_VIDEO_FORMAT_YVU9; break; case V4L2_PIX_FMT_YUV410: - fcc = GST_MAKE_FOURCC ('Y', 'U', 'V', '9'); + format = GST_VIDEO_FORMAT_YUV9; break; case V4L2_PIX_FMT_YUV420: - fcc = GST_MAKE_FOURCC ('I', '4', '2', '0'); + format = GST_VIDEO_FORMAT_I420; break; case V4L2_PIX_FMT_YUYV: - fcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'); + format = GST_VIDEO_FORMAT_YUY2; break; case V4L2_PIX_FMT_YVU420: - fcc = GST_MAKE_FOURCC ('Y', 'V', '1', '2'); + format = GST_VIDEO_FORMAT_YV12; break; case V4L2_PIX_FMT_UYVY: - fcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); + format = GST_VIDEO_FORMAT_UYVY; break; +#if 0 case V4L2_PIX_FMT_Y41P: - fcc = GST_MAKE_FOURCC ('Y', '4', '1', 'P'); + format = GST_VIDEO_FORMAT_Y41P; break; +#endif case V4L2_PIX_FMT_YUV411P: - fcc = GST_MAKE_FOURCC ('Y', '4', '1', 'B'); + format = GST_VIDEO_FORMAT_Y41B; break; case V4L2_PIX_FMT_YUV422P: - fcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B'); + format = GST_VIDEO_FORMAT_Y42B; break; #ifdef V4L2_PIX_FMT_YVYU case V4L2_PIX_FMT_YVYU: - fcc = GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'); + format = GST_VIDEO_FORMAT_YVYU; break; #endif default: g_assert_not_reached (); break; } - structure = gst_structure_new ("video/x-raw-yuv", - "format", GST_TYPE_FOURCC, fcc, NULL); + structure = gst_structure_new ("video/x-raw", + "format", G_TYPE_STRING, gst_video_format_to_string (format), NULL); break; } case V4L2_PIX_FMT_DV: @@@ -1369,180 -1379,160 +1369,180 @@@ gst_v4l2_object_get_all_caps (void * @fps_n/@fps_d: location for framerate * @size: location for expected size of the frame or 0 if unknown */ -gboolean +static gboolean gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps, - struct v4l2_fmtdesc ** format, gint * w, gint * h, - gboolean * interlaced, guint * fps_n, guint * fps_d, guint * size) + struct v4l2_fmtdesc **format, GstVideoInfo * info) { GstStructure *structure; - const GValue *framerate; guint32 fourcc; const gchar *mimetype; - guint outsize; + struct v4l2_fmtdesc *fmt; /* default unknown values */ fourcc = 0; - outsize = 0; structure = gst_caps_get_structure (caps, 0); mimetype = gst_structure_get_name (structure); - if (strcmp (mimetype, "video/mpegts") == 0) { - fourcc = V4L2_PIX_FMT_MPEG; - *fps_n = 0; - *fps_d = 1; - goto done; - } - - if (!gst_structure_get_int (structure, "width", w)) - return FALSE; - - if (!gst_structure_get_int (structure, "height", h)) - return FALSE; - - if (!gst_structure_get_boolean (structure, "interlaced", interlaced)) - *interlaced = FALSE; + 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; - framerate = gst_structure_get_value (structure, "framerate"); - if (!framerate) - return FALSE; - - *fps_n = gst_value_get_fraction_numerator (framerate); - *fps_d = gst_value_get_fraction_denominator (framerate); - - if (!strcmp (mimetype, "video/x-raw-yuv")) { - gst_structure_get_fourcc (structure, "format", &fourcc); - - switch (fourcc) { - case GST_MAKE_FOURCC ('I', '4', '2', '0'): - case GST_MAKE_FOURCC ('I', 'Y', 'U', 'V'): + switch (GST_VIDEO_INFO_FORMAT (info)) { + case GST_VIDEO_FORMAT_I420: fourcc = V4L2_PIX_FMT_YUV420; - outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h); - outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * (GST_ROUND_UP_2 (*h) / 2)); break; - case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): + case GST_VIDEO_FORMAT_YUY2: fourcc = V4L2_PIX_FMT_YUYV; - outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; break; - case GST_MAKE_FOURCC ('Y', '4', '1', 'P'): +#if 0 + case GST_VIDEO_FORMAT_Y41P: fourcc = V4L2_PIX_FMT_Y41P; - outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; break; - case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): +#endif + case GST_VIDEO_FORMAT_UYVY: fourcc = V4L2_PIX_FMT_UYVY; - outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; break; - case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): + case GST_VIDEO_FORMAT_YV12: fourcc = V4L2_PIX_FMT_YVU420; - outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h); - outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * (GST_ROUND_UP_2 (*h) / 2)); break; - case GST_MAKE_FOURCC ('Y', '4', '1', 'B'): + case GST_VIDEO_FORMAT_Y41B: fourcc = V4L2_PIX_FMT_YUV411P; - outsize = GST_ROUND_UP_4 (*w) * *h; - outsize += 2 * ((GST_ROUND_UP_8 (*w) / 4) * *h); break; - case GST_MAKE_FOURCC ('Y', '4', '2', 'B'): + case GST_VIDEO_FORMAT_Y42B: fourcc = V4L2_PIX_FMT_YUV422P; - outsize = GST_ROUND_UP_4 (*w) * *h; - outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * *h); break; - case GST_MAKE_FOURCC ('N', 'V', '1', '2'): + case GST_VIDEO_FORMAT_NV12: fourcc = V4L2_PIX_FMT_NV12; - outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h); - outsize += (GST_ROUND_UP_4 (*w) * *h) / 2; break; - case GST_MAKE_FOURCC ('N', 'V', '2', '1'): + case GST_VIDEO_FORMAT_NV21: fourcc = V4L2_PIX_FMT_NV21; - outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h); - outsize += (GST_ROUND_UP_4 (*w) * *h) / 2; break; #ifdef V4L2_PIX_FMT_YVYU - case GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'): + case GST_VIDEO_FORMAT_YVYU: fourcc = V4L2_PIX_FMT_YVYU; - outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; break; #endif - } - } else if (!strcmp (mimetype, "video/x-raw-rgb")) { - gint depth, endianness, r_mask; - - gst_structure_get_int (structure, "depth", &depth); - gst_structure_get_int (structure, "endianness", &endianness); - gst_structure_get_int (structure, "red_mask", &r_mask); - - switch (depth) { - case 8: - fourcc = V4L2_PIX_FMT_RGB332; + case GST_VIDEO_FORMAT_RGB15: + fourcc = V4L2_PIX_FMT_RGB555; + break; + case GST_VIDEO_FORMAT_RGB16: + fourcc = V4L2_PIX_FMT_RGB565; break; - case 15: - fourcc = (endianness == G_LITTLE_ENDIAN) ? - V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB555X; + case GST_VIDEO_FORMAT_RGB: + fourcc = V4L2_PIX_FMT_RGB24; break; - case 16: - fourcc = (endianness == G_LITTLE_ENDIAN) ? - V4L2_PIX_FMT_RGB565 : V4L2_PIX_FMT_RGB565X; + case GST_VIDEO_FORMAT_BGR: + fourcc = V4L2_PIX_FMT_BGR24; break; - case 24: - fourcc = (r_mask == 0xFF) ? V4L2_PIX_FMT_BGR24 : V4L2_PIX_FMT_RGB24; + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_RGBA: + fourcc = V4L2_PIX_FMT_RGB32; break; - case 32: - fourcc = (r_mask == 0xFF) ? V4L2_PIX_FMT_BGR32 : V4L2_PIX_FMT_RGB32; + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_BGRA: + fourcc = V4L2_PIX_FMT_BGR32; + break; + case GST_VIDEO_FORMAT_GRAY8: + fourcc = V4L2_PIX_FMT_GREY; + default: break; } - } else if (strcmp (mimetype, "video/x-dv") == 0) { - fourcc = V4L2_PIX_FMT_DV; - } else if (strcmp (mimetype, "image/jpeg") == 0) { - fourcc = V4L2_PIX_FMT_JPEG; + } 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_SBGGR8 - } else if (strcmp (mimetype, "video/x-raw-bayer") == 0) { - fourcc = V4L2_PIX_FMT_SBGGR8; + } else if (g_str_equal (mimetype, "video/x-raw-bayer")) { + fourcc = V4L2_PIX_FMT_SBGGR8; #endif #ifdef V4L2_PIX_FMT_SN9C10X - } else if (strcmp (mimetype, "video/x-sonix") == 0) { - fourcc = V4L2_PIX_FMT_SN9C10X; + } else if (g_str_equal (mimetype, "video/x-sonix")) { + fourcc = V4L2_PIX_FMT_SN9C10X; #endif #ifdef V4L2_PIX_FMT_PWC1 - } else if (strcmp (mimetype, "video/x-pwc1") == 0) { - fourcc = V4L2_PIX_FMT_PWC1; + } else if (g_str_equal (mimetype, "video/x-pwc1")) { + fourcc = V4L2_PIX_FMT_PWC1; #endif #ifdef V4L2_PIX_FMT_PWC2 - } else if (strcmp (mimetype, "video/x-pwc2") == 0) { - fourcc = V4L2_PIX_FMT_PWC2; + } else if (g_str_equal (mimetype, "video/x-pwc2")) { + fourcc = V4L2_PIX_FMT_PWC2; + } #endif - } else if (strcmp (mimetype, "video/x-raw-gray") == 0) { - fourcc = V4L2_PIX_FMT_GREY; + + if (dimensions) { + gboolean interlaced; + + if (!gst_structure_get_int (structure, "width", &info->width)) + goto no_width; + + if (!gst_structure_get_int (structure, "height", &info->height)) + goto no_height; + + if (!gst_structure_get_boolean (structure, "interlaced", &interlaced)) + interlaced = FALSE; + if (interlaced) + info->flags |= GST_VIDEO_FLAG_INTERLACED; + + if (!gst_structure_get_fraction (structure, "framerate", &info->fps_n, + &info->fps_d)) + goto no_framerate; + } } if (fourcc == 0) - return FALSE; + goto unhandled_format; -done: - *format = gst_v4l2_object_get_format_from_fourcc (v4l2object, fourcc); - *size = outsize; + fmt = gst_v4l2_object_get_format_from_fourcc (v4l2object, fourcc); + if (fmt == NULL) + goto unsupported_format; + + *format = fmt; 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"); + return FALSE; + } +unhandled_format: + { + GST_DEBUG_OBJECT (v4l2object, "unhandled format"); + return FALSE; + } +unsupported_format: + { + GST_DEBUG_OBJECT (v4l2object, "unsupported format"); + return FALSE; + } } @@@ -2102,102 -2092,16 +2102,102 @@@ error return FALSE; } +static gboolean +gst_v4l2_object_setup_pool (GstV4l2Object * v4l2object, GstCaps * caps) +{ + GstV4l2IOMode mode; + + GST_DEBUG_OBJECT (v4l2object->element, "initializing the capture system"); + + GST_V4L2_CHECK_OPEN (v4l2object); + GST_V4L2_CHECK_NOT_ACTIVE (v4l2object); + + /* find transport */ + mode = v4l2object->req_mode; + + if (v4l2object->vcap.capabilities & V4L2_CAP_READWRITE) { + if (v4l2object->req_mode == GST_V4L2_IO_AUTO) + mode = GST_V4L2_IO_RW; + } else if (v4l2object->req_mode == GST_V4L2_IO_RW) + goto method_not_supported; + + if (v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) { + if (v4l2object->req_mode == GST_V4L2_IO_AUTO) + mode = GST_V4L2_IO_MMAP; + } else if (v4l2object->req_mode == GST_V4L2_IO_MMAP) + goto method_not_supported; + + /* if still no transport selected, error out */ + if (mode == GST_V4L2_IO_AUTO) + goto no_supported_capture_method; + + GST_INFO_OBJECT (v4l2object->element, "accessing buffers via mode %d", mode); + v4l2object->mode = mode; + + /* Map the buffers */ + GST_LOG_OBJECT (v4l2object->element, "initiating buffer pool"); + + if (!(v4l2object->pool = gst_v4l2_buffer_pool_new (v4l2object, caps))) + goto buffer_pool_new_failed; + + GST_V4L2_SET_ACTIVE (v4l2object); + + return TRUE; + + /* ERRORS */ +buffer_pool_new_failed: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, + (_("Could not map buffers from device '%s'"), + v4l2object->videodev), + ("Failed to create buffer pool: %s", g_strerror (errno))); + return FALSE; + } +method_not_supported: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, + (_("The driver of device '%s' does not support the IO method %d"), + v4l2object->videodev, mode), (NULL)); + return FALSE; + } +no_supported_capture_method: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, + (_("The driver of device '%s' does not support any known IO " + "method."), v4l2object->videodev), (NULL)); + return FALSE; + } +} + + +/* 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, guint32 pixelformat, - guint32 width, guint32 height, gboolean interlaced) +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; - - if (interlaced) { + guint32 pixelformat; + struct v4l2_fmtdesc *fmtdesc; + GstVideoInfo info; + gint width, height, fps_n, fps_d, stride; + + if (!gst_v4l2_object_get_caps_info (v4l2object, caps, &fmtdesc, &info)) + goto invalid_caps; + + pixelformat = fmtdesc->pixelformat; + width = GST_VIDEO_INFO_WIDTH (&info); + height = GST_VIDEO_INFO_HEIGHT (&info); + fps_n = GST_VIDEO_INFO_FPS_N (&info); + fps_d = GST_VIDEO_INFO_FPS_D (&info); + stride = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0); + + if (info.flags & GST_VIDEO_FLAG_INTERLACED) { 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.. @@@ -2208,9 -2112,8 +2208,9 @@@ field = V4L2_FIELD_NONE; } - GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format " - "%" GST_FOURCC_FORMAT, width, height, GST_FOURCC_ARGS (pixelformat)); + GST_DEBUG_OBJECT (v4l2object->element, "Desired format %dx%d, format " + "%" GST_FOURCC_FORMAT " stride: %d", width, height, + GST_FOURCC_ARGS (pixelformat), stride); GST_V4L2_CHECK_OPEN (v4l2object); GST_V4L2_CHECK_NOT_ACTIVE (v4l2object); @@@ -2218,7 -2121,7 +2218,7 @@@ /* 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; memset (&format, 0x00, sizeof (struct v4l2_format)); format.type = v4l2object->type; @@@ -2226,127 -2129,42 +2226,128 @@@ if (v4l2_ioctl (fd, VIDIOC_G_FMT, &format) < 0) goto get_fmt_failed; - 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) { - /* 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; + GST_DEBUG_OBJECT (v4l2object->element, "Got format to %dx%d, format " + "%" GST_FOURCC_FORMAT " bytesperline %d, colorspace %d", + format.fmt.pix.width, format.fmt.pix.height, + GST_FOURCC_ARGS (format.fmt.pix.pixelformat), format.fmt.pix.bytesperline, + format.fmt.pix.colorspace); + + 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 || format.fmt.pix.bytesperline != stride) { + /* something different, set the format */ + GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format " + "%" GST_FOURCC_FORMAT " bytesperline %d", width, height, + GST_FOURCC_ARGS (pixelformat), stride); + + format.type = v4l2object->type; + format.fmt.pix.width = width; + format.fmt.pix.height = height; + format.fmt.pix.pixelformat = pixelformat; + format.fmt.pix.field = field; + /* try to ask our prefered stride */ + format.fmt.pix.bytesperline = stride; + + 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; } - format.type = v4l2object->type; - format.fmt.pix.width = width; - format.fmt.pix.height = height; - format.fmt.pix.pixelformat = pixelformat; - format.fmt.pix.field = field; + /* figure out the frame layout */ + v4l2object->bytesperline = format.fmt.pix.bytesperline; + v4l2object->sizeimage = format.fmt.pix.sizeimage; + + GST_DEBUG_OBJECT (v4l2object->element, "Got sizeimage %u", + v4l2object->sizeimage); + + /* 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); + + 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_VIDEO_INFO_FPS_N (&info) = + streamparm.parm.capture.timeperframe.denominator; + GST_VIDEO_INFO_FPS_D (&info) = streamparm.parm.capture.timeperframe.numerator; + + if (v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + 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; - } ++ /* We used to skip frame rate setup if the camera was already setup ++ * with the requested frame rate. This breaks some cameras though, ++ * causing them to not output data (several models of Thinkpad cameras ++ * have this problem at least). ++ * So, don't skip. */ ++ 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; ++ } + - /* 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; ++ /* 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; - /* some cheap USB cam's won't accept any change */ - if (v4l2_ioctl (fd, VIDIOC_S_PARM, &streamparm) < 0) - goto set_parm_failed; - 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; + - /* get new values */ - fps_d = streamparm.parm.capture.timeperframe.numerator; - fps_n = streamparm.parm.capture.timeperframe.denominator; ++ /* get new values */ ++ fps_d = streamparm.parm.capture.timeperframe.numerator; ++ fps_n = streamparm.parm.capture.timeperframe.denominator; + - GST_INFO_OBJECT (v4l2object->element, "Set framerate to %u/%u", fps_n, - fps_d); ++ GST_INFO_OBJECT (v4l2object->element, "Set framerate to %u/%u", fps_n, ++ fps_d); + - GST_VIDEO_INFO_FPS_N (&info) = fps_n; - GST_VIDEO_INFO_FPS_D (&info) = fps_d; - } ++ GST_VIDEO_INFO_FPS_N (&info) = fps_n; ++ GST_VIDEO_INFO_FPS_D (&info) = fps_d; } - if (format.fmt.pix.width != width || format.fmt.pix.height != height) - goto invalid_dimensions; +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; + } + v4l2object->info = info; + v4l2object->fmtdesc = fmtdesc; - if (format.fmt.pix.pixelformat != pixelformat) - goto invalid_pixelformat; + /* now configure ther pools */ + if (!gst_v4l2_object_setup_pool (v4l2object, caps)) + goto pool_failed; return TRUE; /* ERRORS */ +invalid_caps: + { + GST_DEBUG_OBJECT (v4l2object->element, "can't parse caps %" GST_PTR_FORMAT, + caps); + return FALSE; + } get_fmt_failed: { GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, @@@ -2384,220 -2202,30 +2385,220 @@@ invalid_pixelformat GST_FOURCC_ARGS (format.fmt.pix.pixelformat))); return FALSE; } +get_parm_failed: + { + /* it's possible that this call is not supported */ + if (errno != EINVAL) { + 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 device did not accept new frame rate setting.")), + GST_ERROR_SYSTEM); + goto done; + } +pool_failed: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Video device could not create buffer pool.")), GST_ERROR_SYSTEM); + return FALSE; + } } gboolean -gst_v4l2_object_start_streaming (GstV4l2Object * v4l2object) +gst_v4l2_object_unlock (GstV4l2Object * v4l2object) { - if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_STREAMON, - &(v4l2object->type)) < 0) { - GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ, - (_("Error starting streaming on device '%s'."), v4l2object->videodev), - GST_ERROR_SYSTEM); - return FALSE; + GST_LOG_OBJECT (v4l2object->element, "flush poll"); + gst_poll_set_flushing (v4l2object->poll, TRUE); + + return TRUE; +} + +gboolean +gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object) +{ + GST_LOG_OBJECT (v4l2object->element, "flush stop poll"); + gst_poll_set_flushing (v4l2object->poll, FALSE); + + return TRUE; +} + +gboolean +gst_v4l2_object_stop (GstV4l2Object * v4l2object) +{ + GST_DEBUG_OBJECT (v4l2object->element, "stopping"); + + if (!GST_V4L2_IS_OPEN (v4l2object)) + goto done; + if (!GST_V4L2_IS_ACTIVE (v4l2object)) + goto done; + + if (v4l2object->pool) { + GST_DEBUG_OBJECT (v4l2object->element, "deactivating pool"); + gst_buffer_pool_set_active (GST_BUFFER_POOL_CAST (v4l2object->pool), FALSE); + gst_object_unref (v4l2object->pool); + v4l2object->pool = NULL; } + + GST_V4L2_SET_INACTIVE (v4l2object); + +done: 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_WRONG_STATE; + } +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_stop_streaming (GstV4l2Object * v4l2object) +gst_v4l2_object_copy (GstV4l2Object * v4l2object, GstBuffer * dest, + GstBuffer * src) { - if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_STREAMOFF, - &(v4l2object->type)) < 0) { - GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ, - (_("Error stopping streaming on device '%s'."), v4l2object->videodev), - GST_ERROR_SYSTEM); - return FALSE; + guint8 *data; + gsize size; + + if (v4l2object->info.finfo) { + GstVideoFrame src_frame, dest_frame; + + GST_DEBUG_OBJECT (v4l2object->element, "copy video frame"); + + /* we have raw video, use videoframe copy to get strides right */ + if (!gst_video_frame_map (&src_frame, &v4l2object->info, src, GST_MAP_READ)) + goto invalid_buffer; + + if (!gst_video_frame_map (&dest_frame, &v4l2object->info, dest, + GST_MAP_WRITE)) { + gst_video_frame_unmap (&src_frame); + goto invalid_buffer; + } + + gst_video_frame_copy (&dest_frame, &src_frame); + + gst_video_frame_unmap (&src_frame); + gst_video_frame_unmap (&dest_frame); + } else { + GST_DEBUG_OBJECT (v4l2object->element, "copy raw bytes"); + data = gst_buffer_map (src, &size, NULL, GST_MAP_READ); + gst_buffer_fill (dest, 0, data, size); + gst_buffer_unmap (src, data, size); } + GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2object->element, + "slow copy into buffer %p", dest); + return TRUE; + + /* ERRORS */ +invalid_buffer: + { + /* No Window available to put our image into */ + GST_WARNING_OBJECT (v4l2object->element, "could not map image"); + return FALSE; + } }