diff --git a/ChangeLog b/ChangeLog index b2273257d9..121a7fb30c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2007-10-13 Tim-Philipp Müller + + * sys/v4l2/gstv4l2src.c: + * sys/v4l2/v4l2src_calls.c: + When probing the formats and sizes a camera supports, make + sure the best ones (highest resolution, prefered format) + end up at the beginning of the probed caps and the less + desirable ones at the end. This is important because the + order within the caps matters for things like fixation and + negotiation, ie. what format is chosen in the end. + With recent kernels, the current probing code will end up + querying the supported sizes from lowest resolution to + highest resolution, adding them to the probed caps in that + order, resulting to v4l2src fixating to the lowest possible + resolution if downstream does not express a size preference. + Also make up a somewhat random ranking of prefered output + formats for the same reason. Fixes #485828. + 2007-10-11 Tim-Philipp Müller Based on patch by: Jason Kivlighn diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c index 5a892bda84..9fce1846a9 100644 --- a/sys/v4l2/gstv4l2src.c +++ b/sys/v4l2/gstv4l2src.c @@ -240,7 +240,7 @@ static GstCaps *gst_v4l2src_get_caps (GstBaseSrc * src); static gboolean gst_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query); static GstFlowReturn gst_v4l2src_create (GstPushSrc * src, GstBuffer ** out); -static void gst_v4l2src_fixate (GstPad * pad, GstCaps * caps); +static void gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps); static void gst_v4l2src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -295,6 +295,7 @@ gst_v4l2src_class_init (GstV4l2SrcClass * klass) basesrc_class->start = GST_DEBUG_FUNCPTR (gst_v4l2src_start); basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2src_stop); basesrc_class->query = GST_DEBUG_FUNCPTR (gst_v4l2src_query); + basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_v4l2src_fixate); pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_v4l2src_create); } @@ -313,9 +314,6 @@ gst_v4l2src_init (GstV4l2Src * v4l2src, GstV4l2SrcClass * klass) v4l2src->is_capturing = FALSE; - gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (v4l2src), - gst_v4l2src_fixate); - gst_base_src_set_format (GST_BASE_SRC (v4l2src), GST_FORMAT_TIME); gst_base_src_set_live (GST_BASE_SRC (v4l2src), TRUE); @@ -388,15 +386,12 @@ gst_v4l2src_get_property (GObject * object, /* this function is a bit of a last resort */ static void -gst_v4l2src_fixate (GstPad * pad, GstCaps * caps) +gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps) { GstStructure *structure; gint i; - G_GNUC_UNUSED gchar *caps_str; - caps_str = gst_caps_to_string (caps); - GST_DEBUG_OBJECT (GST_PAD_PARENT (pad), "fixating caps %s", caps_str); - g_free (caps_str); + GST_DEBUG_OBJECT (basesrc, "fixating caps %" GST_PTR_FORMAT, caps); for (i = 0; i < gst_caps_get_size (caps); ++i) { structure = gst_caps_get_structure (caps, i); @@ -405,7 +400,7 @@ gst_v4l2src_fixate (GstPad * pad, GstCaps * caps) /* FIXME such sizes? we usually fixate to something in the 320x200 * range... */ /* We are fixating to greater possble size (limited to GST_V4L2_MAX_SIZE) - and framarate closer to 15/2 that is common in web-cams */ + and framerate closer to 15/2 that is common in web-cams */ gst_structure_fixate_field_nearest_int (structure, "width", GST_V4L2_MAX_SIZE); gst_structure_fixate_field_nearest_int (structure, "height", @@ -697,6 +692,8 @@ gst_v4l2src_get_caps (GstBaseSrc * src) v4l2src->probed_caps = gst_caps_ref (ret); + GST_INFO_OBJECT (v4l2src, "probed caps: %" GST_PTR_FORMAT, ret); + return ret; } diff --git a/sys/v4l2/v4l2src_calls.c b/sys/v4l2/v4l2src_calls.c index 0ed28a8820..21022c8ccb 100644 --- a/sys/v4l2/v4l2src_calls.c +++ b/sys/v4l2/v4l2src_calls.c @@ -373,6 +373,97 @@ gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool) gst_mini_object_unref (GST_MINI_OBJECT (pool)); } +/* 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 GREY_BASE_RANK 5 + +static gint +gst_v4l2src_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; + + case V4L2_PIX_FMT_SBGGR8: + return BAYER_BASE_RANK; + + default: + break; + } + + return 0; +} + +static gint +gst_v4l2src_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_v4l2src_format_get_rank (pf2) - gst_v4l2src_format_get_rank (pf1); +} + /****************************************************** * gst_v4l2src_fill_format_list(): * create list of supported capture formats @@ -407,9 +498,12 @@ gst_v4l2src_fill_format_list (GstV4l2Src * v4l2src) GST_LOG_OBJECT (v4l2src, "pixelformat: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (format->pixelformat)); - v4l2src->formats = g_slist_prepend (v4l2src->formats, format); + /* 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 */ + v4l2src->formats = g_slist_insert_sorted (v4l2src->formats, format, + (GCompareFunc) gst_v4l2src_format_cmp_func); } - v4l2src->formats = g_slist_reverse (v4l2src->formats); GST_DEBUG_OBJECT (v4l2src, "got %d format(s)", n); @@ -638,6 +732,7 @@ gst_v4l2src_probe_caps_for_format (GstV4l2Src * v4l2src, guint32 pixelformat, { GstCaps *ret; GstStructure *tmp; + GList *results = NULL; #ifdef VIDIOC_ENUM_FRAMESIZES gint fd = v4l2src->v4l2object->video_fd; @@ -661,7 +756,7 @@ gst_v4l2src_probe_caps_for_format (GstV4l2Src * v4l2src, guint32 pixelformat, tmp = gst_v4l2src_probe_caps_for_format_and_size (v4l2src, pixelformat, w, h, template); if (tmp) - gst_caps_append_structure (ret, tmp); + results = g_list_prepend (results, tmp); size.index++; } while (ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &size) >= 0); @@ -697,6 +792,17 @@ gst_v4l2src_probe_caps_for_format (GstV4l2Src * v4l2src, guint32 pixelformat, goto unknown_type; } + /* we use an intermediary list to store the results of the probing because + * we probe from lowest resolution to highest resolution, but want the caps + * to contain the results in reverse order starting with the highest + * resolution, as order in caps matters for things like fixation. However, + * there's no gst_caps_prepend_structure(), so we use the list as helper to + * reverse the order */ + while (results != NULL) { + gst_caps_append_structure (ret, GST_STRUCTURE (results->data)); + results = g_list_delete_link (results, results); + } + return ret; /* ERRORS */