Selecting webcam resolutions
- From: James Liggett <jrliggett cox net>
- To: Cheese Mailing List <cheese-list gnome org>
- Subject: Selecting webcam resolutions
- Date: Fri, 25 Jan 2008 15:43:29 -0800
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]