diff options
Diffstat (limited to 'sys/v4l2/gstv4l2object.c')
-rw-r--r-- | sys/v4l2/gstv4l2object.c | 1343 |
1 files changed, 1330 insertions, 13 deletions
diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index b9a18cc0..021e49db 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -15,7 +15,7 @@ * 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. + * USA. */ #ifdef HAVE_CONFIG_H @@ -39,7 +39,13 @@ #endif #include "gstv4l2colorbalance.h" -#define DEFAULT_PROP_DEVICE "/dev/video0" +#include "gst/gst-i18n-plugin.h" + + +GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); +#define GST_CAT_DEFAULT v4l2_debug + + #define DEFAULT_PROP_DEVICE_NAME NULL #define DEFAULT_PROP_DEVICE_FD -1 #define DEFAULT_PROP_FLAGS 0 @@ -53,9 +59,6 @@ enum V4L2_STD_OBJECT_PROPS, }; -GST_DEBUG_CATEGORY_EXTERN (v4l2src_debug); -#define GST_CAT_DEFAULT v4l2src_debug - const GList * gst_v4l2_probe_get_properties (GstPropertyProbe * probe) { @@ -151,7 +154,7 @@ gst_v4l2_class_probe_devices (GstElementClass * klass, gboolean check, } /* - * detect /dev entries + * detect /dev entries */ for (n = 0; n < 64; n++) { for (base = 0; dev_base[base] != NULL; base++) { @@ -161,11 +164,11 @@ gst_v4l2_class_probe_devices (GstElementClass * klass, gboolean check, n); /* - * does the /dev/ entry exist at all? + * does the /dev/ entry exist at all? */ if (stat (device, &s) == 0) { /* - * yes: is a device attached? + * yes: is a device attached? */ if (S_ISCHR (s.st_mode)) { @@ -307,11 +310,12 @@ gst_v4l2_device_get_type (void) } void -gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class) +gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class, + const char *default_device) { g_object_class_install_property (gobject_class, PROP_DEVICE, g_param_spec_string ("device", "Device", "Device location", - DEFAULT_PROP_DEVICE, G_PARAM_READWRITE)); + default_device, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_DEVICE_NAME, g_param_spec_string ("device-name", "Device name", "Name of the device", DEFAULT_PROP_DEVICE_NAME, G_PARAM_READABLE)); @@ -326,6 +330,8 @@ gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class) GstV4l2Object * gst_v4l2_object_new (GstElement * element, + enum v4l2_buf_type type, + char *default_device, GstV4l2GetInOutFunction get_in_out_func, GstV4l2SetInOutFunction set_in_out_func, GstV4l2UpdateFpsFunction update_fps_func) @@ -333,10 +339,13 @@ gst_v4l2_object_new (GstElement * element, GstV4l2Object *v4l2object; /* - * some default values + * some default values */ v4l2object = g_new0 (GstV4l2Object, 1); + v4l2object->type = type; + v4l2object->formats = NULL; + v4l2object->element = element; v4l2object->get_in_out_func = get_in_out_func; v4l2object->set_in_out_func = set_in_out_func; @@ -345,7 +354,7 @@ gst_v4l2_object_new (GstElement * element, v4l2object->video_fd = -1; v4l2object->poll = gst_poll_new (TRUE); v4l2object->buffer = NULL; - v4l2object->videodev = g_strdup (DEFAULT_PROP_DEVICE); + v4l2object->videodev = g_strdup (default_device); v4l2object->norms = NULL; v4l2object->channels = NULL; @@ -356,6 +365,9 @@ gst_v4l2_object_new (GstElement * element, return v4l2object; } +static gboolean gst_v4l2_object_clear_format_list (GstV4l2Object * v4l2object); + + void gst_v4l2_object_destroy (GstV4l2Object * v4l2object) { @@ -373,9 +385,25 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object) if (v4l2object->norm) g_free (v4l2object->norm); + if (v4l2object->formats) { + gst_v4l2_object_clear_format_list (v4l2object); + } + g_free (v4l2object); } + +static gboolean +gst_v4l2_object_clear_format_list (GstV4l2Object * v4l2object) +{ + g_slist_foreach (v4l2object->formats, (GFunc) g_free, NULL); + g_slist_free (v4l2object->formats); + v4l2object->formats = NULL; + + return TRUE; +} + + gboolean gst_v4l2_object_set_property_helper (GstV4l2Object * v4l2object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -500,7 +528,12 @@ gst_v4l2_set_defaults (GstV4l2Object * v4l2object) { GstTunerNorm *norm = NULL; GstTunerChannel *channel = NULL; - GstTuner *tuner = GST_TUNER (v4l2object->element); + GstTuner *tuner; + + if (!GST_IS_TUNER (v4l2object->element)) + return; + + tuner = GST_TUNER (v4l2object->element); if (v4l2object->norm) norm = gst_tuner_find_norm_by_name (tuner, v4l2object->norm); @@ -573,3 +606,1287 @@ gst_v4l2_object_stop (GstV4l2Object * v4l2object) return TRUE; } + + +/* + * common format / caps utilities: + */ + + +static const guint32 gst_v4l2_formats[] = { + /* from Linux 2.6.15 videodev2.h */ + V4L2_PIX_FMT_RGB332, + V4L2_PIX_FMT_RGB555, + V4L2_PIX_FMT_RGB565, + V4L2_PIX_FMT_RGB555X, + V4L2_PIX_FMT_RGB565X, + V4L2_PIX_FMT_BGR24, + V4L2_PIX_FMT_RGB24, + V4L2_PIX_FMT_BGR32, + V4L2_PIX_FMT_RGB32, + V4L2_PIX_FMT_GREY, + V4L2_PIX_FMT_YVU410, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_UYVY, + V4L2_PIX_FMT_YUV422P, + V4L2_PIX_FMT_YUV411P, + V4L2_PIX_FMT_Y41P, + + /* two planes -- one Y, one Cr + Cb interleaved */ + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + + /* The following formats are not defined in the V4L2 specification */ + V4L2_PIX_FMT_YUV410, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YYUV, + V4L2_PIX_FMT_HI240, + + /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ +#ifdef V4L2_PIX_FMT_SBGGR8 + V4L2_PIX_FMT_SBGGR8, +#endif + + /* compressed formats */ + V4L2_PIX_FMT_MJPEG, + V4L2_PIX_FMT_JPEG, + V4L2_PIX_FMT_DV, + V4L2_PIX_FMT_MPEG, + + /* Vendor-specific formats */ + V4L2_PIX_FMT_WNVA, + +#ifdef V4L2_PIX_FMT_SN9C10X + V4L2_PIX_FMT_SN9C10X, +#endif +#ifdef V4L2_PIX_FMT_PWC1 + V4L2_PIX_FMT_PWC1, +#endif +#ifdef V4L2_PIX_FMT_PWC2 + V4L2_PIX_FMT_PWC2, +#endif +#ifdef V4L2_PIX_FMT_YVYU + V4L2_PIX_FMT_YVYU, +#endif +}; + +#define GST_V4L2_FORMAT_COUNT (G_N_ELEMENTS (gst_v4l2_formats)) + + +static struct v4l2_fmtdesc * +gst_v4l2_object_get_format_from_fourcc (GstV4l2Object * v4l2object, + guint32 fourcc) +{ + struct v4l2_fmtdesc *fmt; + GSList *walk; + + if (fourcc == 0) + return NULL; + + walk = gst_v4l2_object_get_format_list (v4l2object); + while (walk) { + fmt = (struct v4l2_fmtdesc *) walk->data; + if (fmt->pixelformat == fourcc) + return fmt; + /* special case for jpeg */ + if ((fmt->pixelformat == V4L2_PIX_FMT_MJPEG && fourcc == V4L2_PIX_FMT_JPEG) + || (fmt->pixelformat == V4L2_PIX_FMT_JPEG + && fourcc == V4L2_PIX_FMT_MJPEG)) { + return fmt; + } + walk = g_slist_next (walk); + } + + return NULL; +} + + + +/* complete made up ranking, the values themselves are meaningless */ +#define YUV_BASE_RANK 1000 +#define JPEG_BASE_RANK 500 +#define DV_BASE_RANK 200 +#define RGB_BASE_RANK 100 +#define YUV_ODD_BASE_RANK 50 +#define RGB_ODD_BASE_RANK 25 +#define BAYER_BASE_RANK 15 +#define S910_BASE_RANK 10 +#define GREY_BASE_RANK 5 +#define PWC_BASE_RANK 1 + +static gint +gst_v4l2_object_format_get_rank (guint32 fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_MJPEG: + return JPEG_BASE_RANK; + case V4L2_PIX_FMT_JPEG: + return JPEG_BASE_RANK + 1; + + 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: + return RGB_ODD_BASE_RANK; + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + return RGB_BASE_RANK - 1; + + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + return RGB_BASE_RANK; + + case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ + return GREY_BASE_RANK; + + 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_YYUV: /* 16 YUV 4:2:2 */ + case V4L2_PIX_FMT_HI240: /* 8 8-bit color */ + return YUV_ODD_BASE_RANK; + + case V4L2_PIX_FMT_YVU410: /* YVU9, 9 bits per pixel */ + return YUV_BASE_RANK + 3; + case V4L2_PIX_FMT_YUV410: /* YUV9, 9 bits per pixel */ + return YUV_BASE_RANK + 2; + case V4L2_PIX_FMT_YUV420: /* I420, 12 bits per pixel */ + return YUV_BASE_RANK + 7; + case V4L2_PIX_FMT_YUYV: /* YUY2, 16 bits per pixel */ + return YUV_BASE_RANK + 10; + case V4L2_PIX_FMT_YVU420: /* YV12, 12 bits per pixel */ + return YUV_BASE_RANK + 6; + case V4L2_PIX_FMT_UYVY: /* UYVY, 16 bits per pixel */ + return YUV_BASE_RANK + 9; + case V4L2_PIX_FMT_Y41P: /* Y41P, 12 bits per pixel */ + return YUV_BASE_RANK + 5; + case V4L2_PIX_FMT_YUV411P: /* Y41B, 12 bits per pixel */ + return YUV_BASE_RANK + 4; + case V4L2_PIX_FMT_YUV422P: /* Y42B, 16 bits per pixel */ + return YUV_BASE_RANK + 8; + + case V4L2_PIX_FMT_DV: + return DV_BASE_RANK; + + case V4L2_PIX_FMT_MPEG: /* MPEG */ + case V4L2_PIX_FMT_WNVA: /* Winnov hw compres */ + return 0; + +#ifdef V4L2_PIX_FMT_SBGGR8 + case V4L2_PIX_FMT_SBGGR8: + return BAYER_BASE_RANK; +#endif + +#ifdef V4L2_PIX_FMT_SN9C10X + case V4L2_PIX_FMT_SN9C10X: + return S910_BASE_RANK; +#endif + +#ifdef V4L2_PIX_FMT_PWC1 + case V4L2_PIX_FMT_PWC1: + return PWC_BASE_RANK; +#endif +#ifdef V4L2_PIX_FMT_PWC2 + case V4L2_PIX_FMT_PWC2: + return PWC_BASE_RANK; +#endif + + default: + break; + } + + return 0; +} + + + +static gint +format_cmp_func (gconstpointer a, gconstpointer b) +{ + guint32 pf1 = ((struct v4l2_fmtdesc *) a)->pixelformat; + guint32 pf2 = ((struct v4l2_fmtdesc *) b)->pixelformat; + + if (pf1 == pf2) + return 0; + + return gst_v4l2_object_format_get_rank (pf2) - + gst_v4l2_object_format_get_rank (pf1); +} + +/****************************************************** + * gst_v4l2_object_fill_format_list(): + * create list of supported capture formats + * return value: TRUE on success, FALSE on error + ******************************************************/ +static gboolean +gst_v4l2_object_fill_format_list (GstV4l2Object * v4l2object) +{ + gint n; + struct v4l2_fmtdesc *format; + + GST_DEBUG_OBJECT (v4l2object->element, "getting src format enumerations"); + + /* format enumeration */ + for (n = 0;; n++) { + format = g_new0 (struct v4l2_fmtdesc, 1); + + format->index = n; + format->type = v4l2object->type; + + if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_ENUM_FMT, format) < 0) { + if (errno == EINVAL) { + g_free (format); + break; /* end of enumeration */ + } else { + goto failed; + } + } + + GST_LOG_OBJECT (v4l2object->element, "index: %u", format->index); + GST_LOG_OBJECT (v4l2object->element, "type: %d", format->type); + GST_LOG_OBJECT (v4l2object->element, "flags: %08x", format->flags); + GST_LOG_OBJECT (v4l2object->element, "description: '%s'", + format->description); + GST_LOG_OBJECT (v4l2object->element, "pixelformat: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (format->pixelformat)); + + /* sort formats according to our preference; we do this, because caps + * are probed in the order the formats are in the list, and the order of + * formats in the final probed caps matters for things like fixation */ + v4l2object->formats = g_slist_insert_sorted (v4l2object->formats, format, + (GCompareFunc) format_cmp_func); + } + + GST_DEBUG_OBJECT (v4l2object->element, "got %d format(s)", n); + + return TRUE; + + /* ERRORS */ +failed: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Failed to enumerate possible video formats device '%s' can work with"), v4l2object->videodev), ("Failed to get number %d in pixelformat enumeration for %s. (%d - %s)", n, v4l2object->videodev, errno, g_strerror (errno))); + g_free (format); + return FALSE; + } +} + +/** + * Get the list of supported capture formats, a list of + * <code>struct v4l2_fmtdesc</code>. + */ +GSList * +gst_v4l2_object_get_format_list (GstV4l2Object * v4l2object) +{ + if (!v4l2object->formats) + gst_v4l2_object_fill_format_list (v4l2object); + return v4l2object->formats; +} + + +GstStructure * +gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc) +{ + GstStructure *structure = NULL; + + switch (fourcc) { + case V4L2_PIX_FMT_MJPEG: /* Motion-JPEG */ + case V4L2_PIX_FMT_JPEG: /* JFIF JPEG */ + structure = gst_structure_new ("image/jpeg", NULL); + 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: + 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_NV12: /* 12 Y/CbCr 4:2:0 */ + case V4L2_PIX_FMT_NV21: /* 12 Y/CrCb 4:2:0 */ + case V4L2_PIX_FMT_YVU410: + case V4L2_PIX_FMT_YUV410: + case V4L2_PIX_FMT_YUV420: /* I420/IYUV */ + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_Y41P: + case V4L2_PIX_FMT_YUV422P: +#ifdef V4L2_PIX_FMT_YVYU + case V4L2_PIX_FMT_YVYU: +#endif + case V4L2_PIX_FMT_YUV411P:{ + guint32 fcc = 0; + + switch (fourcc) { + case V4L2_PIX_FMT_NV12: + fcc = GST_MAKE_FOURCC ('N', 'V', '1', '2'); + break; + case V4L2_PIX_FMT_NV21: + fcc = GST_MAKE_FOURCC ('N', 'V', '2', '1'); + break; + case V4L2_PIX_FMT_YVU410: + fcc = GST_MAKE_FOURCC ('Y', 'V', 'U', '9'); + break; + case V4L2_PIX_FMT_YUV410: + fcc = GST_MAKE_FOURCC ('Y', 'U', 'V', '9'); + break; + case V4L2_PIX_FMT_YUV420: + fcc = GST_MAKE_FOURCC ('I', '4', '2', '0'); + break; + case V4L2_PIX_FMT_YUYV: + fcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'); + break; + case V4L2_PIX_FMT_YVU420: + fcc = GST_MAKE_FOURCC ('Y', 'V', '1', '2'); + break; + case V4L2_PIX_FMT_UYVY: + fcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); + break; + case V4L2_PIX_FMT_Y41P: + fcc = GST_MAKE_FOURCC ('Y', '4', '1', 'P'); + break; + case V4L2_PIX_FMT_YUV411P: + fcc = GST_MAKE_FOURCC ('Y', '4', '1', 'B'); + break; + case V4L2_PIX_FMT_YUV422P: + fcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B'); + break; +#ifdef V4L2_PIX_FMT_YVYU + case V4L2_PIX_FMT_YVYU: + fcc = GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'); + break; +#endif + default: + g_assert_not_reached (); + break; + } + structure = gst_structure_new ("video/x-raw-yuv", + "format", GST_TYPE_FOURCC, fcc, NULL); + break; + } + case V4L2_PIX_FMT_DV: + structure = + gst_structure_new ("video/x-dv", "systemstream", G_TYPE_BOOLEAN, TRUE, + NULL); + break; + case V4L2_PIX_FMT_MPEG: /* MPEG */ + /* someone figure out the MPEG format used... */ + break; + case V4L2_PIX_FMT_WNVA: /* Winnov hw compres */ + break; +#ifdef V4L2_PIX_FMT_SBGGR8 + case V4L2_PIX_FMT_SBGGR8: + structure = gst_structure_new ("video/x-raw-bayer", NULL); + break; +#endif +#ifdef V4L2_PIX_FMT_SN9C10X + case V4L2_PIX_FMT_SN9C10X: + structure = gst_structure_new ("video/x-sonix", NULL); + break; +#endif +#ifdef V4L2_PIX_FMT_PWC1 + case V4L2_PIX_FMT_PWC1: + structure = gst_structure_new ("video/x-pwc1", NULL); + break; +#endif +#ifdef V4L2_PIX_FMT_PWC2 + case V4L2_PIX_FMT_PWC2: + structure = gst_structure_new ("video/x-pwc2", NULL); + break; +#endif + default: + GST_DEBUG ("Unknown fourcc 0x%08x %" GST_FOURCC_FORMAT, + fourcc, GST_FOURCC_ARGS (fourcc)); + break; + } + + return structure; +} + + + +GstCaps * +gst_v4l2_object_get_all_caps (void) +{ + static GstCaps *caps = NULL; + + if (caps == NULL) { + GstStructure *structure; + + guint i; + + caps = gst_caps_new_empty (); + for (i = 0; i < GST_V4L2_FORMAT_COUNT; i++) { + structure = gst_v4l2_object_v4l2fourcc_to_structure (gst_v4l2_formats[i]); + if (structure) { + gst_structure_set (structure, + "width", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE, + "height", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 100, 1, NULL); + gst_caps_append_structure (caps, structure); + } + } + } + + return gst_caps_ref (caps); +} + + +/* collect data for the given caps + * @caps: given input caps + * @format: location for the v4l format + * @w/@h: location for width and height + * @fps_n/@fps_d: location for framerate + * @size: location for expected size of the frame or 0 if unknown + */ +gboolean +gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps, + struct v4l2_fmtdesc ** format, gint * w, gint * h, guint * fps_n, + guint * fps_d, guint * size) +{ + GstStructure *structure; + const GValue *framerate; + guint32 fourcc; + const gchar *mimetype; + guint outsize; + + /* default unknown values */ + fourcc = 0; + outsize = 0; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "width", w)) + return FALSE; + + if (!gst_structure_get_int (structure, "height", h)) + return FALSE; + + 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); + + mimetype = gst_structure_get_name (structure); + + 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'): + 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'): + fourcc = V4L2_PIX_FMT_YUYV; + outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; + break; + case GST_MAKE_FOURCC ('Y', '4', '1', 'P'): + fourcc = V4L2_PIX_FMT_Y41P; + outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; + break; + case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): + fourcc = V4L2_PIX_FMT_UYVY; + outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; + break; + case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): + 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'): + 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'): + 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'): + 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'): + 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'): + 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; + break; + case 15: + fourcc = (endianness == G_LITTLE_ENDIAN) ? + V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB555X; + break; + case 16: + fourcc = (endianness == G_LITTLE_ENDIAN) ? + V4L2_PIX_FMT_RGB565 : V4L2_PIX_FMT_RGB565X; + break; + case 24: + fourcc = (r_mask == 0xFF) ? V4L2_PIX_FMT_BGR24 : V4L2_PIX_FMT_RGB24; + break; + case 32: + fourcc = (r_mask == 0xFF) ? V4L2_PIX_FMT_BGR32 : V4L2_PIX_FMT_RGB32; + 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; +#ifdef V4L2_PIX_FMT_SBGGR8 + } else if (strcmp (mimetype, "video/x-raw-bayer") == 0) { + fourcc = V4L2_PIX_FMT_SBGGR8; +#endif +#ifdef V4L2_PIX_FMT_SN9C10X + } else if (strcmp (mimetype, "video/x-sonix") == 0) { + fourcc = V4L2_PIX_FMT_SN9C10X; +#endif +#ifdef V4L2_PIX_FMT_PWC1 + } else if (strcmp (mimetype, "video/x-pwc1") == 0) { + fourcc = V4L2_PIX_FMT_PWC1; +#endif +#ifdef V4L2_PIX_FMT_PWC2 + } else if (strcmp (mimetype, "video/x-pwc2") == 0) { + fourcc = V4L2_PIX_FMT_PWC2; +#endif + } else if (strcmp (mimetype, "video/x-raw-gray") == 0) { + fourcc = V4L2_PIX_FMT_GREY; + } + + if (fourcc == 0) + return FALSE; + + *format = gst_v4l2_object_get_format_from_fourcc (v4l2object, fourcc); + *size = outsize; + + return TRUE; +} + + + +/* The frame interval enumeration code first appeared in Linux 2.6.19. */ +#ifdef VIDIOC_ENUM_FRAMEINTERVALS +static GstStructure * +gst_v4l2_object_probe_caps_for_format_and_size (GstV4l2Object * v4l2object, + guint32 pixelformat, + guint32 width, guint32 height, const GstStructure * template) +{ + gint fd = v4l2object->video_fd; + struct v4l2_frmivalenum ival; + guint32 num, denom; + GstStructure *s; + GValue rates = { 0, }; + + memset (&ival, 0, sizeof (struct v4l2_frmivalenum)); + ival.index = 0; + ival.pixel_format = pixelformat; + ival.width = width; + ival.height = height; + + GST_LOG_OBJECT (v4l2object->element, + "get frame interval for %ux%u, %" GST_FOURCC_FORMAT, width, height, + GST_FOURCC_ARGS (pixelformat)); + + /* keep in mind that v4l2 gives us frame intervals (durations); we invert the + * fraction to get framerate */ + if (v4l2_ioctl (fd, VIDIOC_ENUM_FRAMEINTERVALS, &ival) < 0) + goto enum_frameintervals_failed; + + if (ival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { + GValue rate = { 0, }; + + g_value_init (&rates, GST_TYPE_LIST); + g_value_init (&rate, GST_TYPE_FRACTION); + + do { + num = ival.discrete.numerator; + denom = ival.discrete.denominator; + + if (num > G_MAXINT || denom > G_MAXINT) { + /* let us hope we don't get here... */ + num >>= 1; + denom >>= 1; + } + + GST_LOG_OBJECT (v4l2object->element, "adding discrete framerate: %d/%d", + denom, num); + + /* swap to get the framerate */ + gst_value_set_fraction (&rate, denom, num); + gst_value_list_append_value (&rates, &rate); + + ival.index++; + } while (v4l2_ioctl (fd, VIDIOC_ENUM_FRAMEINTERVALS, &ival) >= 0); + } else if (ival.type == V4L2_FRMIVAL_TYPE_STEPWISE) { + GValue min = { 0, }; + GValue step = { 0, }; + GValue max = { 0, }; + gboolean added = FALSE; + guint32 minnum, mindenom; + guint32 maxnum, maxdenom; + + g_value_init (&rates, GST_TYPE_LIST); + + g_value_init (&min, GST_TYPE_FRACTION); + g_value_init (&step, GST_TYPE_FRACTION); + g_value_init (&max, GST_TYPE_FRACTION); + + /* get the min */ + minnum = ival.stepwise.min.numerator; + mindenom = ival.stepwise.min.denominator; + if (minnum > G_MAXINT || mindenom > G_MAXINT) { + minnum >>= 1; + mindenom >>= 1; + } + GST_LOG_OBJECT (v4l2object->element, "stepwise min frame interval: %d/%d", + minnum, mindenom); + gst_value_set_fraction (&min, minnum, mindenom); + + /* get the max */ + maxnum = ival.stepwise.max.numerator; + maxdenom = ival.stepwise.max.denominator; + if (maxnum > G_MAXINT || maxdenom > G_MAXINT) { + maxnum >>= 1; + maxdenom >>= 1; + } + + GST_LOG_OBJECT (v4l2object->element, "stepwise max frame interval: %d/%d", + maxnum, maxdenom); + gst_value_set_fraction (&max, maxnum, maxdenom); + + /* get the step */ + num = ival.stepwise.step.numerator; + denom = ival.stepwise.step.denominator; + if (num > G_MAXINT || denom > G_MAXINT) { + num >>= 1; + denom >>= 1; + } + + if (num == 0 || denom == 0) { + /* in this case we have a wrong fraction or no step, set the step to max + * so that we only add the min value in the loop below */ + num = maxnum; + denom = maxdenom; + } + + /* since we only have gst_value_fraction_subtract and not add, negate the + * numerator */ + GST_LOG_OBJECT (v4l2object->element, "stepwise step frame interval: %d/%d", + num, denom); + gst_value_set_fraction (&step, -num, denom); + + while (gst_value_compare (&min, &max) <= 0) { + GValue rate = { 0, }; + + num = gst_value_get_fraction_numerator (&min); + denom = gst_value_get_fraction_denominator (&min); + GST_LOG_OBJECT (v4l2object->element, "adding stepwise framerate: %d/%d", + denom, num); + + /* invert to get the framerate */ + g_value_init (&rate, GST_TYPE_FRACTION); + gst_value_set_fraction (&rate, denom, num); + gst_value_list_append_value (&rates, &rate); + added = TRUE; + + /* we're actually adding because step was negated above. This is because + * there is no _add function... */ + if (!gst_value_fraction_subtract (&min, &min, &step)) { + GST_WARNING_OBJECT (v4l2object->element, "could not step fraction!"); + break; + } + } + if (!added) { + /* no range was added, leave the default range from the template */ + GST_WARNING_OBJECT (v4l2object->element, + "no range added, leaving default"); + g_value_unset (&rates); + } + } else if (ival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) { + guint32 maxnum, maxdenom; + + g_value_init (&rates, GST_TYPE_FRACTION_RANGE); + + num = ival.stepwise.min.numerator; + denom = ival.stepwise.min.denominator; + if (num > G_MAXINT || denom > G_MAXINT) { + num >>= 1; + denom >>= 1; + } + + maxnum = ival.stepwise.max.numerator; + maxdenom = ival.stepwise.max.denominator; + if (maxnum > G_MAXINT || maxdenom > G_MAXINT) { + maxnum >>= 1; + maxdenom >>= 1; + } + + GST_LOG_OBJECT (v4l2object->element, + "continuous frame interval %d/%d to %d/%d", maxdenom, maxnum, denom, + num); + + gst_value_set_fraction_range_full (&rates, maxdenom, maxnum, denom, num); + } else { + goto unknown_type; + } + +return_data: + s = gst_structure_copy (template); + gst_structure_set (s, "width", G_TYPE_INT, (gint) width, + "height", G_TYPE_INT, (gint) height, NULL); + + if (G_IS_VALUE (&rates)) { + /* only change the framerate on the template when we have a valid probed new + * value */ + gst_structure_set_value (s, "framerate", &rates); + g_value_unset (&rates); + } else { + gst_structure_set (s, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 100, 1, + NULL); + } + return s; + + /* ERRORS */ +enum_frameintervals_failed: + { + GST_DEBUG_OBJECT (v4l2object->element, + "Unable to enumerate intervals for %" GST_FOURCC_FORMAT "@%ux%u", + GST_FOURCC_ARGS (pixelformat), width, height); + goto return_data; + } +unknown_type: + { + /* I don't see how this is actually an error, we ignore the format then */ + GST_WARNING_OBJECT (v4l2object->element, + "Unknown frame interval type at %" GST_FOURCC_FORMAT "@%ux%u: %u", + GST_FOURCC_ARGS (pixelformat), width, height, ival.type); + return NULL; + } +} +#endif /* defined VIDIOC_ENUM_FRAMEINTERVALS */ + +#ifdef VIDIOC_ENUM_FRAMESIZES +static gint +sort_by_frame_size (GstStructure * s1, GstStructure * s2) +{ + int w1, h1, w2, h2; + + gst_structure_get_int (s1, "width", &w1); + gst_structure_get_int (s1, "height", &h1); + gst_structure_get_int (s2, "width", &w2); + gst_structure_get_int (s2, "height", &h2); + + /* I think it's safe to assume that this won't overflow for a while */ + return ((w2 * h2) - (w1 * h1)); +} +#endif + +static gboolean +gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object, + guint32 pixelformat, gint * width, gint * height); + +GstCaps * +gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object, + guint32 pixelformat, const GstStructure * template) +{ + GstCaps *ret = gst_caps_new_empty (); + GstStructure *tmp; + +#ifdef VIDIOC_ENUM_FRAMESIZES + gint fd = v4l2object->video_fd; + struct v4l2_frmsizeenum size; + GList *results = NULL; + guint32 w, h; + + memset (&size, 0, sizeof (struct v4l2_frmsizeenum)); + size.index = 0; + size.pixel_format = pixelformat; + + GST_DEBUG_OBJECT (v4l2object->element, "Enumerating frame sizes"); + + if (v4l2_ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &size) < 0) + goto enum_framesizes_failed; + + if (size.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + do { + GST_LOG_OBJECT (v4l2object->element, "got discrete frame size %dx%d", + size.discrete.width, size.discrete.height); + + w = MIN (size.discrete.width, G_MAXINT); + h = MIN (size.discrete.height, G_MAXINT); + + if (w && h) { + tmp = + gst_v4l2_object_probe_caps_for_format_and_size (v4l2object, + pixelformat, w, h, template); + + if (tmp) + results = g_list_prepend (results, tmp); + } + + size.index++; + } while (v4l2_ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &size) >= 0); + GST_DEBUG_OBJECT (v4l2object->element, + "done iterating discrete frame sizes"); + } else if (size.type == V4L2_FRMSIZE_TYPE_STEPWISE) { + GST_DEBUG_OBJECT (v4l2object->element, "we have stepwise frame sizes:"); + GST_DEBUG_OBJECT (v4l2object->element, "min width: %d", + size.stepwise.min_width); + GST_DEBUG_OBJECT (v4l2object->element, "min height: %d", + size.stepwise.min_height); + GST_DEBUG_OBJECT (v4l2object->element, "max width: %d", + size.stepwise.max_width); + GST_DEBUG_OBJECT (v4l2object->element, "min height: %d", + size.stepwise.max_height); + GST_DEBUG_OBJECT (v4l2object->element, "step width: %d", + size.stepwise.step_width); + GST_DEBUG_OBJECT (v4l2object->element, "step height: %d", + size.stepwise.step_height); + + for (w = size.stepwise.min_width, h = size.stepwise.min_height; + w < size.stepwise.max_width && h < size.stepwise.max_height; + w += size.stepwise.step_width, h += size.stepwise.step_height) { + if (w == 0 || h == 0) + continue; + + tmp = + gst_v4l2_object_probe_caps_for_format_and_size (v4l2object, + pixelformat, w, h, template); + + if (tmp) + results = g_list_prepend (results, tmp); + } + GST_DEBUG_OBJECT (v4l2object->element, + "done iterating stepwise frame sizes"); + } else if (size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { + guint32 maxw, maxh; + + GST_DEBUG_OBJECT (v4l2object->element, "we have continuous frame sizes:"); + GST_DEBUG_OBJECT (v4l2object->element, "min width: %d", + size.stepwise.min_width); + GST_DEBUG_OBJECT (v4l2object->element, "min height: %d", + size.stepwise.min_height); + GST_DEBUG_OBJECT (v4l2object->element, "max width: %d", + size.stepwise.max_width); + GST_DEBUG_OBJECT (v4l2object->element, "min height: %d", + size.stepwise.max_height); + + w = MAX (size.stepwise.min_width, 1); + h = MAX (size.stepwise.min_height, 1); + maxw = MIN (size.stepwise.max_width, G_MAXINT); + maxh = MIN (size.stepwise.max_height, G_MAXINT); + + tmp = + gst_v4l2_object_probe_caps_for_format_and_size (v4l2object, pixelformat, + w, h, template); + if (tmp) { + gst_structure_set (tmp, "width", GST_TYPE_INT_RANGE, (gint) w, + (gint) maxw, "height", GST_TYPE_INT_RANGE, (gint) h, (gint) maxh, + NULL); + + /* no point using the results list here, since there's only one struct */ + gst_caps_append_structure (ret, tmp); + } + } else { + goto unknown_type; + } + + /* we use an intermediary list to store and then sort the results of the + * probing because we can't make any assumptions about the order in which + * the driver will give us the sizes, but we want the final caps to contain + * the results starting with the highest resolution and having the lowest + * resolution last, since order in caps matters for things like fixation. */ + results = g_list_sort (results, (GCompareFunc) sort_by_frame_size); + while (results != NULL) { + gst_caps_append_structure (ret, GST_STRUCTURE (results->data)); + results = g_list_delete_link (results, results); + } + + if (gst_caps_is_empty (ret)) + goto enum_framesizes_no_results; + + return ret; + + /* ERRORS */ +enum_framesizes_failed: + { + /* I don't see how this is actually an error */ + GST_DEBUG_OBJECT (v4l2object->element, + "Failed to enumerate frame sizes for pixelformat %" GST_FOURCC_FORMAT + " (%s)", GST_FOURCC_ARGS (pixelformat), g_strerror (errno)); + goto default_frame_sizes; + } +enum_framesizes_no_results: + { + /* it's possible that VIDIOC_ENUM_FRAMESIZES is defined but the driver in + * question doesn't actually support it yet */ + GST_DEBUG_OBJECT (v4l2object->element, + "No results for pixelformat %" GST_FOURCC_FORMAT + " enumerating frame sizes, trying fallback", + GST_FOURCC_ARGS (pixelformat)); + goto default_frame_sizes; + } +unknown_type: + { + GST_WARNING_OBJECT (v4l2object->element, + "Unknown frame sizeenum type for pixelformat %" GST_FOURCC_FORMAT + ": %u", GST_FOURCC_ARGS (pixelformat), size.type); + goto default_frame_sizes; + } +default_frame_sizes: +#endif /* defined VIDIOC_ENUM_FRAMESIZES */ + { + gint min_w, max_w, min_h, max_h, fix_num = 0, fix_denom = 0; + + /* This code is for Linux < 2.6.19 */ + min_w = min_h = 1; + max_w = max_h = GST_V4L2_MAX_SIZE; + if (!gst_v4l2_object_get_nearest_size (v4l2object, pixelformat, &min_w, + &min_h)) { + GST_WARNING_OBJECT (v4l2object->element, + "Could not probe minimum capture size for pixelformat %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (pixelformat)); + } + if (!gst_v4l2_object_get_nearest_size (v4l2object, pixelformat, &max_w, + &max_h)) { + GST_WARNING_OBJECT (v4l2object->element, + "Could not probe maximum capture size for pixelformat %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (pixelformat)); + } + + /* Since we can't get framerate directly, try to use the current norm */ + if (v4l2object->norm && v4l2object->norms) { + GList *norms; + GstTunerNorm *norm; + + for (norms = v4l2object->norms; norms != NULL; norms = norms->next) { + norm = (GstTunerNorm *) norms->data; + if (!strcmp (norm->label, v4l2object->norm)) + break; + } + /* If it's possible, set framerate to that (discrete) value */ + if (norm) { + fix_num = gst_value_get_fraction_numerator (&norm->framerate); + fix_denom = gst_value_get_fraction_denominator (&norm->framerate); + } + } + + tmp = gst_structure_copy (template); + if (fix_num) { + gst_structure_set (tmp, + "width", GST_TYPE_INT_RANGE, min_w, max_w, + "height", GST_TYPE_INT_RANGE, min_h, max_h, + "framerate", GST_TYPE_FRACTION, fix_num, fix_denom, NULL); + } else { + /* if norm can't be used, copy the template framerate */ + gst_structure_set (tmp, + "width", GST_TYPE_INT_RANGE, min_w, max_w, + "height", GST_TYPE_INT_RANGE, min_h, max_h, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 100, 1, NULL); + } + gst_caps_append_structure (ret, tmp); + + return ret; + } +} + +static gboolean +gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object, + guint32 pixelformat, gint * width, gint * height) +{ + struct v4l2_format fmt; + int fd; + int r; + + g_return_val_if_fail (width != NULL, FALSE); + g_return_val_if_fail (height != NULL, FALSE); + + GST_LOG_OBJECT (v4l2object->element, + "getting nearest size to %dx%d with format %" GST_FOURCC_FORMAT, + *width, *height, GST_FOURCC_ARGS (pixelformat)); + + fd = v4l2object->video_fd; + + /* get size delimiters */ + memset (&fmt, 0, sizeof (fmt)); + fmt.type = v4l2object->type; + fmt.fmt.pix.width = *width; + fmt.fmt.pix.height = *height; + fmt.fmt.pix.pixelformat = pixelformat; + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + + r = v4l2_ioctl (fd, VIDIOC_TRY_FMT, &fmt); + if (r < 0 && errno == EINVAL) { + /* try again with progressive video */ + fmt.fmt.pix.width = *width; + fmt.fmt.pix.height = *height; + fmt.fmt.pix.pixelformat = pixelformat; + fmt.fmt.pix.field = V4L2_FIELD_NONE; + r = v4l2_ioctl (fd, VIDIOC_TRY_FMT, &fmt); + } + + if (r < 0) { + /* The driver might not implement TRY_FMT, in which case we will try + S_FMT to probe */ + if (errno != ENOTTY) + return FALSE; + + /* Only try S_FMT if we're not actively capturing yet, which we shouldn't + be, because we're still probing */ + if (GST_V4L2_IS_ACTIVE (v4l2object)) + return FALSE; + + GST_LOG_OBJECT (v4l2object->element, + "Failed to probe size limit with VIDIOC_TRY_FMT, trying VIDIOC_S_FMT"); + + fmt.fmt.pix.width = *width; + fmt.fmt.pix.height = *height; + + r = v4l2_ioctl (fd, VIDIOC_S_FMT, &fmt); + if (r < 0 && errno == EINVAL) { + /* try again with progressive video */ + fmt.fmt.pix.width = *width; + fmt.fmt.pix.height = *height; + fmt.fmt.pix.pixelformat = pixelformat; + fmt.fmt.pix.field = V4L2_FIELD_NONE; + r = v4l2_ioctl (fd, VIDIOC_S_FMT, &fmt); + } + + if (r < 0) + return FALSE; + } + + GST_LOG_OBJECT (v4l2object->element, + "got nearest size %dx%d", fmt.fmt.pix.width, fmt.fmt.pix.height); + + *width = fmt.fmt.pix.width; + *height = fmt.fmt.pix.height; + + return TRUE; +} + + +gboolean +gst_v4l2_object_set_format (GstV4l2Object * v4l2object, guint32 pixelformat, + guint32 width, guint32 height) +{ + gint fd = v4l2object->video_fd; + struct v4l2_format format; + + GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format " + "%" GST_FOURCC_FORMAT, width, height, GST_FOURCC_ARGS (pixelformat)); + + GST_V4L2_CHECK_OPEN (v4l2object); + GST_V4L2_CHECK_NOT_ACTIVE (v4l2object); + + memset (&format, 0x00, sizeof (struct v4l2_format)); + format.type = v4l2object->type; + + if (v4l2_ioctl (fd, VIDIOC_G_FMT, &format) < 0) + goto get_fmt_failed; + + format.type = v4l2object->type; + format.fmt.pix.width = width; + format.fmt.pix.height = height; + format.fmt.pix.pixelformat = pixelformat; + /* request whole frames; change when gstreamer supports interlaced video + * (INTERLACED mode returns frames where the fields have already been + * combined, there are other modes for requesting fields individually) */ + format.fmt.pix.field = V4L2_FIELD_INTERLACED; + + if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) { + if (errno != EINVAL) + goto set_fmt_failed; + + GST_DEBUG_OBJECT (v4l2object->element, "trying again..."); + + /* try again with progressive video */ + format.fmt.pix.width = width; + format.fmt.pix.height = height; + format.fmt.pix.pixelformat = pixelformat; + format.fmt.pix.field = V4L2_FIELD_NONE; + if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) + goto set_fmt_failed; + } + + if (format.fmt.pix.width != width || format.fmt.pix.height != height) + goto invalid_dimensions; + + if (format.fmt.pix.pixelformat != pixelformat) + goto invalid_pixelformat; + + return TRUE; + + /* ERRORS */ +get_fmt_failed: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' does not support video capture"), + v4l2object->videodev), + ("Call to G_FMT failed: (%s)", g_strerror (errno))); + return FALSE; + } +set_fmt_failed: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' cannot capture at %dx%d"), + v4l2object->videodev, width, height), + ("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s", + GST_FOURCC_ARGS (pixelformat), width, height, g_strerror (errno))); + return FALSE; + } +invalid_dimensions: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' cannot capture at %dx%d"), + v4l2object->videodev, width, height), + ("Tried to capture at %dx%d, but device returned size %dx%d", + width, height, format.fmt.pix.width, format.fmt.pix.height)); + return FALSE; + } +invalid_pixelformat: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' cannot capture in the specified format"), + v4l2object->videodev), + ("Tried to capture in %" GST_FOURCC_FORMAT + ", but device returned format" " %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (pixelformat), + GST_FOURCC_ARGS (format.fmt.pix.pixelformat))); + return FALSE; + } +} + +gboolean +gst_v4l2_object_start_streaming (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; + } + return TRUE; +} + +gboolean +gst_v4l2_object_stop_streaming (GstV4l2Object * v4l2object) +{ + 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; + } + return TRUE; +} |