Selecting webcam resolutions



Hi Everyone,
I recently stumbled upon Cheese, and I really think it is a great
application...very nice work! With the glaring lack of decent webcam
apps for Linux, this program fills a pretty big gap. But, there was one
thing that bothered me: I can't select what resolution I want out of my
camera, for pictures or video, in Cheese. So I started hacking and came
up with this patch. It seems to work very well for both pictures and
video; I can record and take pictures in any resolution that my camera
supports. However, there is one problem: the thumbnailer seems to choke
on the highest resolution videos; anything less than that (mine goes up
to 1600x1200 @ 5 Hz) seems to work OK though. It gets bogged down and
the UI gets blocked trying to process it. 

I'm really not sure how we could solve this problem. I would say to try
threading it, but in my experience MT GTK is a *real* pain. Or should we
just cap recording resolution at say, 1024x768, as I saw was suggested
on Bugzilla someplace? 

Again, thanks for your great work. I'm looking forward to contributing.

Thanks,
James Liggett
Index: /home/jim/Projects/cheese/src/cheese-window.c
===================================================================
--- /home/jim/Projects/cheese/src/cheese-window.c	(revision 430)
+++ /home/jim/Projects/cheese/src/cheese-window.c	(working copy)
@@ -56,7 +56,15 @@
   WEBCAM_MODE_VIDEO
 } WebcamMode;
 
+/* Resolution combo box colums */
+enum 
+{
+  RESOLUTION_COL_NAME,
+  RESOLUTION_COL_FORMAT,
+  RESOLUTION_NUM_COLS
+};
 
+
 typedef struct 
 {
   gboolean recording;
@@ -99,6 +107,7 @@
 
   GtkWidget *screen;
   GtkWidget *take_picture;
+  GtkWidget *resolution;
 
   GtkActionGroup *actions_main;
   GtkActionGroup *actions_toggle;
@@ -169,6 +178,22 @@
 }
 
 static void
+cheese_window_resolution_changed_cb (GtkComboBox *combo_box, 
+                                     CheeseWebcam *webcam)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  CheeseVideoFormat *format;
+    
+  model = gtk_combo_box_get_model (combo_box);
+  gtk_combo_box_get_active_iter (combo_box, &iter);
+  gtk_tree_model_get (model, &iter, RESOLUTION_COL_FORMAT, &format, -1);
+        
+  cheese_webcam_set_video_format (webcam, format);
+  
+}
+
+static void
 cheese_window_video_saved_cb (CheeseWebcam *webcam, CheeseWindow *cheese_window)
 {
   // TODO look at this g_free
@@ -960,6 +985,7 @@
   cheese_window->notebook_bar        = glade_xml_get_widget (gxml, "notebook_bar");
   cheese_window->screen              = glade_xml_get_widget (gxml, "video_screen");
   cheese_window->take_picture        = glade_xml_get_widget (gxml, "take_picture");
+  cheese_window->resolution          = glade_xml_get_widget (gxml, "resolution");
 
   char *str = g_strconcat ("<b>", _("_Take a photo"), "</b>", NULL);
   gtk_label_set_text_with_mnemonic (GTK_LABEL (cheese_window->label_take_photo), str);
@@ -1088,6 +1114,52 @@
                     G_CALLBACK (cheese_window_button_press_event_cb), cheese_window);
 }
 
+static void
+cheese_window_fill_resolution (CheeseWindow *window)
+{
+  GtkListStore *model; 
+  GArray *formats;
+  GtkCellRenderer *renderer;
+  int i;
+  CheeseVideoFormat *format;
+  gchar *format_name;
+  GtkTreeIter iter;
+    
+  model = gtk_list_store_new (RESOLUTION_NUM_COLS, 
+                              G_TYPE_STRING, 
+                              G_TYPE_POINTER, 
+                              NULL);
+    
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (window->resolution), renderer, 
+                              FALSE);
+  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (window->resolution),
+                                 renderer, "text", RESOLUTION_COL_NAME);
+    
+  formats = cheese_webcam_get_video_formats (window->webcam);
+    
+  for (i = 0; i < formats->len; i++)
+  {
+    format = &g_array_index (formats, CheeseVideoFormat, i);
+    format_name = g_strdup_printf ("%i x %i", format->width, format->height);
+      
+    gtk_list_store_append (model, &iter);
+    gtk_list_store_set (model, &iter, 
+                        RESOLUTION_COL_NAME, format_name, 
+                        RESOLUTION_COL_FORMAT, format, 
+                        -1);
+      
+    g_free (format_name);
+  }
+    
+  gtk_combo_box_set_model (GTK_COMBO_BOX (window->resolution),
+                           GTK_TREE_MODEL (model));
+    
+  /* Select the highest resolution, consistent with existing behavior. 
+   * TODO: Remember selected resolution as a preference key. */
+  gtk_combo_box_set_active (GTK_COMBO_BOX (window->resolution), 0);
+}
+
 void 
 setup_camera (CheeseWindow *cheese_window)
 {
@@ -1097,11 +1169,16 @@
 
   cheese_window->webcam = cheese_webcam_new (cheese_window->screen, webcam_device);
   g_free (webcam_device);
+    
+  cheese_window_fill_resolution (cheese_window);
 
   g_signal_connect (cheese_window->webcam, "photo-saved",
                     G_CALLBACK (cheese_window_photo_saved_cb), cheese_window);
   g_signal_connect (cheese_window->webcam, "video-saved",
                     G_CALLBACK (cheese_window_video_saved_cb), cheese_window);
+  g_signal_connect (cheese_window->resolution, "changed",
+                    G_CALLBACK (cheese_window_resolution_changed_cb),
+                    cheese_window->webcam);
 
   cheese_webcam_set_effect (cheese_window->webcam, 
                             cheese_effect_chooser_get_selection (CHEESE_EFFECT_CHOOSER (cheese_window->effect_chooser)));
Index: /home/jim/Projects/cheese/src/cheese-webcam.c
===================================================================
--- /home/jim/Projects/cheese/src/cheese-webcam.c	(revision 430)
+++ /home/jim/Projects/cheese/src/cheese-webcam.c	(working copy)
@@ -39,21 +39,6 @@
 
 #define CHEESE_WEBCAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHEESE_TYPE_WEBCAM, CheeseWebcamPrivate))
 
-typedef struct 
-{
-  int numerator;
-  int denominator;
-} CheeseFramerate;
-
-typedef struct 
-{
-  char *mimetype;
-  int width;
-  int height;
-  int num_framerates;
-  CheeseFramerate *framerates; 
-} CheeseVideoFormat;
-
 typedef struct
 {
   char *video_device; 
@@ -78,6 +63,7 @@
   GstElement *video_save_bin;
 
   GstElement *video_source;
+  GstElement *capsfilter;
   GstElement *video_file_sink;
   GstElement *photo_sink;
   GstElement *audio_source;
@@ -98,6 +84,7 @@
   int num_webcam_devices;
   char *device_name;
   CheeseWebcamDevice *webcam_devices;
+  int selected_device;
 } CheeseWebcamPrivate;
 
 
@@ -558,6 +545,32 @@
   }
 }
 
+static void
+find_highest_framerate (CheeseVideoFormat *format, int *numerator, 
+                        int *denominator)
+{
+  int framerate_numerator;
+  int framerate_denominator;
+  int i;
+    
+  /* Select the highest framerate up to 30 Hz*/
+  framerate_numerator = 1;
+  framerate_denominator = 1;
+  for (i = 0; i < format->num_framerates; i++)
+  {
+    float framerate = format->framerates[i].numerator / format->framerates[i].denominator;
+    if (framerate > ((float)framerate_numerator / framerate_denominator)
+        && framerate <= 30)
+    {
+      framerate_numerator = format->framerates[i].numerator;
+      framerate_denominator = format->framerates[i].denominator;        
+    }
+  }
+    
+  *numerator = framerate_numerator;
+  *denominator = framerate_denominator;
+}
+
 static gboolean 
 cheese_webcam_create_webcam_source_bin (CheeseWebcam *webcam)
 {
@@ -574,17 +587,16 @@
   {
     CheeseVideoFormat *format; 
     int i;
-    int selected_device;
     int framerate_numerator, framerate_denominator;
 
     /* If we have a matching video device use that one, otherwise use the first */
-    selected_device = 0;
+    priv->selected_device = 0;
     for (i = 1; i < priv->num_webcam_devices ; i++)
     {
 	if (strcmp (priv->webcam_devices[i].video_device, priv->device_name) == 0)
-          selected_device = i;
+          priv->selected_device = i;
     }
-    CheeseWebcamDevice *selected_webcam = &(priv->webcam_devices[selected_device]);
+    CheeseWebcamDevice *selected_webcam = &(priv->webcam_devices[priv->selected_device]);
 
     /* Select the highest resolution */
     format = &(g_array_index (selected_webcam->video_formats, CheeseVideoFormat, 0));
@@ -596,21 +608,11 @@
         format = &(g_array_index (selected_webcam->video_formats, CheeseVideoFormat, i));
       }
     }
-    /* Select the highest framerate up to 30 Hz*/
-    framerate_numerator = 1;
-    framerate_denominator = 1;
-    for (i = 0; i < format->num_framerates; i++)
-    {
-      float framerate = format->framerates[i].numerator / format->framerates[i].denominator;
-      if (framerate > ((float)framerate_numerator / framerate_denominator)
-          && framerate <= 30)
-      {
-        framerate_numerator = format->framerates[i].numerator;
-        framerate_denominator = format->framerates[i].denominator;        
-      }
-    }
-
-    webcam_input = g_strdup_printf ("%s name=video_source device=%s ! %s,width=%d,height=%d,framerate=%d/%d ! identity",
+    
+    find_highest_framerate (format, &framerate_numerator, 
+                            &framerate_denominator);
+      
+    webcam_input = g_strdup_printf ("%s name=video_source device=%s ! capsfilter name=capsfilter caps=%s,width=%d,height=%d,framerate=%d/%d ! identity",
                                     selected_webcam->gstreamer_src,
                                     selected_webcam->video_device,
                                     format->mimetype,
@@ -631,6 +633,8 @@
   }
 
   priv->video_source = gst_bin_get_by_name (GST_BIN (priv->webcam_source_bin), "video_source");
+  priv->capsfilter = gst_bin_get_by_name (GST_BIN (priv->webcam_source_bin), 
+                                          "capsfilter");
   return TRUE;
 }
 
@@ -733,7 +737,6 @@
   GstElement *mux;
   GstPad *pad;
   gboolean ok;
-  GstCaps *caps;
 
   priv->video_save_bin = gst_bin_new ("video_save_bin");
 
@@ -765,15 +768,8 @@
   ok = gst_element_link_many (priv->audio_source, audio_queue, audio_convert, 
                               audio_enc, mux, priv->video_file_sink, NULL);
 
-  /* Record videos always in 320x240 */
-  ok &= gst_element_link (video_save_csp ,video_save_scale);
-  caps = gst_caps_new_simple ("video/x-raw-yuv",
-                              "width", G_TYPE_INT, 320,
-                              "height", G_TYPE_INT, 240,
-                              NULL);
-  ok &= gst_element_link_filtered (video_save_scale, video_enc, caps);
-  gst_caps_unref (caps);
-
+  ok &= gst_element_link_many (video_save_csp, video_save_scale, video_enc,
+                               NULL);
   ok &= gst_element_link (video_enc, mux);
 
   if (!ok)
@@ -1133,3 +1129,36 @@
   return webcam;
 }
 
+GArray *
+cheese_webcam_get_video_formats (CheeseWebcam *webcam)
+{
+  CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam); 
+    
+  return priv->webcam_devices[priv->selected_device].video_formats;
+}
+
+void
+cheese_webcam_set_video_format (CheeseWebcam *webcam, CheeseVideoFormat *format)
+{
+  CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
+  GstCaps *new_caps;
+  int framerate_numerator;
+  int framerate_denominator;
+    
+  find_highest_framerate (format, &framerate_numerator, &framerate_denominator);
+    
+  new_caps = gst_caps_new_simple (format->mimetype,
+                                  "width", G_TYPE_INT, 
+                                  format->width,
+                                  "height", G_TYPE_INT, 
+                                  format->height,
+                                  "framerate", GST_TYPE_FRACTION, 
+                                  framerate_numerator,
+                                  framerate_denominator,
+                                  NULL);
+    
+  cheese_webcam_stop (webcam);
+  g_object_set (priv->capsfilter, "caps", new_caps, NULL);
+  cheese_webcam_play (webcam);
+}
+
Index: /home/jim/Projects/cheese/src/cheese-webcam.h
===================================================================
--- /home/jim/Projects/cheese/src/cheese-webcam.h	(revision 430)
+++ /home/jim/Projects/cheese/src/cheese-webcam.h	(working copy)
@@ -35,6 +35,21 @@
 #define CHEESE_IS_WEBCAM_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), CHEESE_TYPE_WEBCAM))
 #define CHEESE_WEBCAM_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), CHEESE_TYPE_WEBCAM, CheeseWebcamClass))
 
+typedef struct 
+{
+  int numerator;
+  int denominator;
+} CheeseFramerate;
+
+typedef struct 
+{
+  char *mimetype;
+  int width;
+  int height;
+  int num_framerates;
+  CheeseFramerate *framerates; 
+} CheeseVideoFormat;
+
 typedef enum
 {
   CHEESE_WEBCAM_EFFECT_NO_EFFECT       = (0),
@@ -75,6 +90,9 @@
 void 		 cheese_webcam_take_photo 		(CheeseWebcam *webcam, char *filename);
 gboolean	 cheese_webcam_has_webcam 		(CheeseWebcam *webcam);
 int              cheese_webcam_get_num_webcam_devices   (CheeseWebcam *webcam);
+GArray *cheese_webcam_get_video_formats (CheeseWebcam *webcam);
+void cheese_webcam_set_video_format (CheeseWebcam *webcam,
+                                     CheeseVideoFormat *format);
 G_END_DECLS
 
 #endif /* __CHEESE_WEBCAM_H__ */
Index: /home/jim/Projects/cheese/data/cheese.glade
===================================================================
--- /home/jim/Projects/cheese/data/cheese.glade	(revision 430)
+++ /home/jim/Projects/cheese/data/cheese.glade	(working copy)
@@ -300,9 +300,14 @@
                   </widget>
                 </child>
                 <child>
-                  <placeholder/>
+                  <widget class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes">page 2</property>
+                  </widget>
                   <packing>
                     <property name="type">tab</property>
+                    <property name="tab_fill">False</property>
                   </packing>
                 </child>
                 <child>
@@ -318,9 +323,15 @@
                   </packing>
                 </child>
                 <child>
-                  <placeholder/>
+                  <widget class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes">page 3</property>
+                  </widget>
                   <packing>
                     <property name="type">tab</property>
+                    <property name="position">1</property>
+                    <property name="tab_fill">False</property>
                   </packing>
                 </child>
               </widget>
@@ -331,6 +342,39 @@
               </packing>
             </child>
             <child>
+              <widget class="GtkHBox" id="hbox7">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <child>
+                  <widget class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes">Resolution:</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" id="resolution">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
               <widget class="GtkScrolledWindow" id="thumb_scrollwindow">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
@@ -342,7 +386,7 @@
               </widget>
               <packing>
                 <property name="expand">False</property>
-                <property name="position">2</property>
+                <property name="position">3</property>
               </packing>
             </child>
           </widget>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]