Blame SOURCES/0010-cheese-camera-device-Keep-track-of-highest-available.patch

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