From 8b76b4ebc3f38e742f39ccded070be5e5290d41d Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Mon, 10 Jun 2013 14:15:58 +0200
Subject: [PATCH 10/35] cheese-camera-device: Keep track of highest available
framerate per format
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
libcheese/cheese-camera-device.c | 167 ++++++++++++++++++++++++++++++++++-----
1 file changed, 149 insertions(+), 18 deletions(-)
diff --git a/libcheese/cheese-camera-device.c b/libcheese/cheese-camera-device.c
index e5ec5fa..c684367 100644
--- a/libcheese/cheese-camera-device.c
+++ b/libcheese/cheese-camera-device.c
@@ -109,11 +109,29 @@ struct _CheeseCameraDevicePrivate
gchar *name;
guint v4lapi_version;
GstCaps *caps;
- GList *formats;
+ GList *formats; /* list members are CheeseVideoFormatFull structs */
GError *construct_error;
};
+typedef struct _CheeseVideoFormatFull CheeseVideoFormatFull;
+
+/*
+ * This is our private version of CheeseVideoFormat, with extra fields added
+ * at the end. IMPORTANT the first fields *must* be kept in sync with the
+ * public CheeseVideoFormat, since in various places we cast pointers to
+ * CheeseVideoFormatFull to CheeseVideoFormat.
+ */
+struct _CheeseVideoFormatFull
+{
+ /* CheeseVideoFormat members keep synced with cheese-camera-device.h! */
+ gint width;
+ gint height;
+ /* libcheese private members */
+ gint fr_numerator;
+ gint fr_denominator;
+};
+
GQuark cheese_camera_device_error_quark (void);
GQuark
@@ -145,8 +163,8 @@ G_DEFINE_BOXED_TYPE (CheeseVideoFormat, cheese_video_format,
static gint
compare_formats (gconstpointer a, gconstpointer b)
{
- const CheeseVideoFormat *c = a;
- const CheeseVideoFormat *d = b;
+ const CheeseVideoFormatFull *c = a;
+ const CheeseVideoFormatFull *d = b;
/* descending sort for rectangle area */
return (d->width * d->height - c->width * c->height);
@@ -197,42 +215,154 @@ cheese_camera_device_filter_caps (CheeseCameraDevice *device, GstCaps *caps, GSt
}
/*
+ * cheese_camera_device_get_highest_framerate:
+ * @framerate: a #GValue holding a framerate cap
+ * @numerator: destination to store the numerator of the highest rate
+ * @denominator: destination to store the denominator of the highest rate
+ *
+ * Get the numerator and denominator for the highest framerate stored in
+ * a framerate cap.
+ *
+ * Note this function does not handle framerate ranges, if @framerate
+ * contains a range it will return 0/0 as framerate
+ */
+static void
+cheese_camera_device_get_highest_framerate(const GValue *framerate,
+ gint *numerator, gint *denominator)
+{
+ *numerator = 0;
+ *denominator = 0;
+
+ if (GST_VALUE_HOLDS_FRACTION (framerate))
+ {
+ *numerator = gst_value_get_fraction_numerator (framerate);
+ *denominator = gst_value_get_fraction_denominator (framerate);
+ }
+ else if (GST_VALUE_HOLDS_ARRAY (framerate))
+ {
+ float curr, highest = 0;
+ guint i, size = gst_value_array_get_size (framerate);
+
+ for (i = 0; i < size; i++)
+ {
+ const GValue *val = gst_value_array_get_value(framerate, i);
+
+ if (!GST_VALUE_HOLDS_FRACTION (val) ||
+ gst_value_get_fraction_denominator(val) == 0) {
+ continue;
+ }
+
+ curr = (float)gst_value_get_fraction_numerator(val) /
+ (float)gst_value_get_fraction_denominator(val);
+ if (curr > highest && curr <= CHEESE_MAXIMUM_RATE)
+ {
+ highest = curr;
+ *numerator = gst_value_get_fraction_numerator (val);
+ *denominator = gst_value_get_fraction_denominator (val);
+ }
+ }
+ }
+ else if (GST_VALUE_HOLDS_LIST (framerate))
+ {
+ float curr, highest = 0;
+ guint i, size = gst_value_list_get_size (framerate);
+
+ for (i = 0; i < size; i++)
+ {
+ const GValue *val = gst_value_list_get_value(framerate, i);
+
+ if (!GST_VALUE_HOLDS_FRACTION (val) ||
+ gst_value_get_fraction_denominator(val) == 0) {
+ continue;
+ }
+
+ curr = (float)gst_value_get_fraction_numerator(val) /
+ (float)gst_value_get_fraction_denominator(val);
+ if (curr > highest && curr <= CHEESE_MAXIMUM_RATE)
+ {
+ highest = curr;
+ *numerator = gst_value_get_fraction_numerator (val);
+ *denominator = gst_value_get_fraction_denominator (val);
+ }
+ }
+ }
+}
+
+/*
+ * cheese_camera_device_format_update_framerate:
+ * @format: the #CheeseVideoFormatFull to update the framerate of
+ * @framerate: a #GValue holding a framerate cap
+ *
+ * This function updates the framerate in @format with the highest framerate
+ * from @framerate, if @framerate contains a framerate higher then the
+ * framerate currently stored in @format.
+ */
+static void
+cheese_camera_device_format_update_framerate(CheeseVideoFormatFull *format,
+ const GValue *framerate)
+{
+ float high, curr = (float)format->fr_numerator / format->fr_denominator;
+ gint high_numerator, high_denominator;
+
+ cheese_camera_device_get_highest_framerate (framerate, &high_numerator,
+ &high_denominator);
+ if (high_denominator == 0)
+ return;
+
+ high = (float)high_numerator / (float)high_denominator;
+ if (high > curr) {
+ format->fr_numerator = high_numerator;
+ format->fr_denominator = high_denominator;
+ GST_INFO ("%dx%d new framerate %d/%d", format->width, format->height,
+ format->fr_numerator, format->fr_denominator);
+ }
+}
+
+/*
* cheese_camera_device_add_format:
* @device: a #CheeseCameraDevice
- * @format: the #CheeseVideoFormat to add
+ * @format: the #CheeseVideoFormatFull to add
*
* Add the supplied @format to the list of formats supported by the @device.
*/
static void
-cheese_camera_device_add_format (CheeseCameraDevice *device, CheeseVideoFormat *format)
+cheese_camera_device_add_format (CheeseCameraDevice *device,
+ CheeseVideoFormatFull *format, const GValue *framerate)
{
CheeseCameraDevicePrivate *priv = device->priv;
GList *l;
for (l = priv->formats; l != NULL; l = l->next)
{
- CheeseVideoFormat *item = l->data;
+ CheeseVideoFormatFull *item = l->data;
if ((item != NULL) &&
(item->width == format->width) &&
- (item->height == format->height)) return;
+ (item->height == format->height))
+ {
+ cheese_camera_device_format_update_framerate (item, framerate);
+ return;
+ }
}
- GST_INFO ("%dx%d", format->width, format->height);
+ cheese_camera_device_get_highest_framerate (framerate,
+ &format->fr_numerator, &format->fr_denominator);
+ GST_INFO ("%dx%d framerate %d/%d", format->width, format->height,
+ format->fr_numerator, format->fr_denominator);
priv->formats = g_list_insert_sorted (priv->formats, format, compare_formats);
}
/*
* free_format_list_foreach:
- * @data: the #CheeseVideoFormat to free
+ * @data: the #CheeseVideoFormatFull to free
* @user_data: unused
*
- * Free the individual #CheeseVideoFormat.
+ * Free the individual #CheeseVideoFormatFull.
*/
static void
free_format_list_foreach (gpointer data, G_GNUC_UNUSED gpointer user_data)
{
- g_slice_free (CheeseVideoFormat, data);
+ g_slice_free (CheeseVideoFormatFull, data);
}
/*
@@ -271,19 +401,20 @@ cheese_camera_device_update_format_table (CheeseCameraDevice *device)
for (i = 0; i < num_structures; i++)
{
GstStructure *structure;
- const GValue *width, *height;
+ const GValue *width, *height, *framerate;
structure = gst_caps_get_structure (priv->caps, i);
width = gst_structure_get_value (structure, "width");
height = gst_structure_get_value (structure, "height");
+ framerate = gst_structure_get_value (structure, "framerate");
if (G_VALUE_HOLDS_INT (width))
{
- CheeseVideoFormat *format = g_slice_new0 (CheeseVideoFormat);
+ CheeseVideoFormatFull *format = g_slice_new0 (CheeseVideoFormatFull);
gst_structure_get_int (structure, "width", &(format->width));
gst_structure_get_int (structure, "height", &(format->height));
- cheese_camera_device_add_format (device, format);
+ cheese_camera_device_add_format (device, format, framerate);
}
else if (GST_VALUE_HOLDS_INT_RANGE (width))
{
@@ -310,14 +441,14 @@ cheese_camera_device_update_format_table (CheeseCameraDevice *device)
* we use <= here (and not below) to make this work */
while (cur_width <= max_width && cur_height <= max_height)
{
- CheeseVideoFormat *format = g_slice_new0 (CheeseVideoFormat);
+ CheeseVideoFormatFull *format = g_slice_new0 (CheeseVideoFormatFull);
/* Gstreamer wants resolutions for YUV formats where the width is
* a multiple of 8, and the height is a multiple of 2 */
format->width = cur_width & ~7;
format->height = cur_height & ~1;
- cheese_camera_device_add_format (device, format);
+ cheese_camera_device_add_format (device, format, framerate);
cur_width *= 2;
cur_height *= 2;
@@ -327,14 +458,14 @@ cheese_camera_device_update_format_table (CheeseCameraDevice *device)
cur_height = max_height;
while (cur_width > min_width && cur_height > min_height)
{
- CheeseVideoFormat *format = g_slice_new0 (CheeseVideoFormat);
+ CheeseVideoFormatFull *format = g_slice_new0 (CheeseVideoFormatFull);
/* Gstreamer wants resolutions for YUV formats where the width is
* a multiple of 8, and the height is a multiple of 2 */
format->width = cur_width & ~7;
format->height = cur_height & ~1;
- cheese_camera_device_add_format (device, format);
+ cheese_camera_device_add_format (device, format, framerate);
cur_width /= 2;
cur_height /= 2;
--
1.8.2.1