diff options
-rw-r--r-- | configure.ac | 26 | ||||
-rw-r--r-- | libempathy-gtk/empathy-individual-menu.c | 7 | ||||
-rw-r--r-- | libempathy-gtk/empathy-log-window.c | 19 | ||||
-rw-r--r-- | libempathy-gtk/empathy-new-call-dialog.c | 19 | ||||
-rw-r--r-- | libempathy/Makefile.am | 19 | ||||
-rw-r--r-- | libempathy/cheese-camera-device-monitor.c | 412 | ||||
-rw-r--r-- | libempathy/cheese-camera-device-monitor.h | 67 | ||||
-rw-r--r-- | libempathy/empathy-camera-monitor.c | 186 | ||||
-rw-r--r-- | libempathy/empathy-camera-monitor.h | 56 | ||||
-rw-r--r-- | libempathy/empathy-contact.c | 15 | ||||
-rw-r--r-- | src/empathy-call-window.c | 24 |
11 files changed, 840 insertions, 10 deletions
diff --git a/configure.ac b/configure.ac index c151b3a3f..e4cb024f4 100644 --- a/configure.ac +++ b/configure.ac @@ -371,6 +371,31 @@ fi AM_CONDITIONAL(HAVE_WEBKIT, test "x$have_webkit" = "xyes") # ----------------------------------------------------------- +# gudev +# ----------------------------------------------------------- +AC_ARG_ENABLE(gudev, + AS_HELP_STRING([--enable-gudev=@<:@no/yes/auto@:>@], + [build with gudev support]), , + enable_gudev=auto) + +if test "x$enable_gudev" != "xno"; then + + PKG_CHECK_MODULES(UDEV, [gudev-1.0], + have_gudev="yes", have_gudev="no") + + if test "x$have_gudev" = "xyes"; then + AC_DEFINE(HAVE_UDEV, 1, [Define if you have gudev]) + fi +else + have_gudev=no +fi + +if test "x$enable_gudev" = "xyes" -a "x$have_gudev" != "xyes"; then + AC_MSG_ERROR([Could not find gudev dependencies.]) +fi +AM_CONDITIONAL(HAVE_UDEV, test "x$have_gudev" = "xyes") + +# ----------------------------------------------------------- # spellchecking checks: enchant and iso-codes # ----------------------------------------------------------- AC_ARG_ENABLE(spell, @@ -604,6 +629,7 @@ Configure summary: Adium themes (Webkit).......: ${have_webkit} Meego widgets...............: ${have_meego} Control center embedding....: ${have_control_center_embedding} + Camera monitoring...........: ${have_gudev} Connectivity: NetworkManager integration..: ${have_nm} diff --git a/libempathy-gtk/empathy-individual-menu.c b/libempathy-gtk/empathy-individual-menu.c index 1f01d8d54..5324c5013 100644 --- a/libempathy-gtk/empathy-individual-menu.c +++ b/libempathy-gtk/empathy-individual-menu.c @@ -31,6 +31,7 @@ #include <folks/folks.h> #include <folks/folks-telepathy.h> +#include <libempathy/empathy-camera-monitor.h> #include <libempathy/empathy-dispatcher.h> #include <libempathy/empathy-individual-manager.h> #include <libempathy/empathy-chatroom-manager.h> @@ -664,6 +665,7 @@ empathy_individual_video_call_menu_item_new (FolksIndividual *individual, { GtkWidget *item; GtkWidget *image; + EmpathyCameraMonitor *monitor; g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) || EMPATHY_IS_CONTACT (contact), @@ -688,6 +690,11 @@ empathy_individual_video_call_menu_item_new (FolksIndividual *individual, EMPATHY_ACTION_VIDEO_CALL); } + monitor = empathy_camera_monitor_dup_singleton (); + g_object_set_data_full (G_OBJECT (item), "monitor", monitor, g_object_unref); + g_object_bind_property (monitor, "available", item, "sensitive", + G_BINDING_SYNC_CREATE); + return item; } diff --git a/libempathy-gtk/empathy-log-window.c b/libempathy-gtk/empathy-log-window.c index 9f3ccfada..9b094652d 100644 --- a/libempathy-gtk/empathy-log-window.c +++ b/libempathy-gtk/empathy-log-window.c @@ -39,6 +39,7 @@ #include <extensions/extensions.h> #include <libempathy/action-chain-internal.h> +#include <libempathy/empathy-camera-monitor.h> #include <libempathy/empathy-chatroom-manager.h> #include <libempathy/empathy-chatroom.h> #include <libempathy/empathy-message.h> @@ -85,6 +86,9 @@ typedef struct TplActionChain *chain; TplLogManager *log_manager; + EmpathyCameraMonitor *camera_monitor; + GBinding *button_video_binding; + /* Used to cancel logger calls when no longer needed */ guint count; @@ -447,6 +451,7 @@ empathy_log_window_show (TpAccount *account, log_window->chain = _tpl_action_chain_new_async (NULL, NULL, NULL); log_window->log_manager = tpl_log_manager_dup_singleton (); + log_window->camera_monitor = empathy_camera_monitor_dup_singleton (); window = log_window; @@ -577,6 +582,7 @@ log_window_destroy_cb (GtkWidget *widget, g_free (window->last_find); _tpl_action_chain_free (window->chain); g_object_unref (window->log_manager); + tp_clear_object (&window->camera_monitor); tp_clear_object (&window->selected_account); g_free (window->selected_chat_id); @@ -1513,6 +1519,8 @@ log_window_update_buttons_sensitivity (EmpathyLogWindow *window, GtkTreePath *path; gboolean profile, chat, call, video; + tp_clear_object (&window->button_video_binding); + profile = chat = call = video = FALSE; if (!gtk_tree_model_get_iter_first (model, &iter)) @@ -1547,11 +1555,20 @@ log_window_update_buttons_sensitivity (EmpathyLogWindow *window, call = capabilities & EMPATHY_CAPABILITIES_AUDIO; video = capabilities & EMPATHY_CAPABILITIES_VIDEO; + if (video) + window->button_video_binding = g_object_bind_property ( + window->camera_monitor, "available", + window->button_video, "sensitive", + G_BINDING_SYNC_CREATE); + out: gtk_widget_set_sensitive (window->button_profile, profile); gtk_widget_set_sensitive (window->button_chat, chat); gtk_widget_set_sensitive (window->button_call, call); - gtk_widget_set_sensitive (window->button_video, video); + + /* Don't override the binding */ + if (!video) + gtk_widget_set_sensitive (window->button_video, video); } static void diff --git a/libempathy-gtk/empathy-new-call-dialog.c b/libempathy-gtk/empathy-new-call-dialog.c index 0c609eefc..288802295 100644 --- a/libempathy-gtk/empathy-new-call-dialog.c +++ b/libempathy-gtk/empathy-new-call-dialog.c @@ -31,6 +31,7 @@ #include <telepathy-yell/telepathy-yell.h> #include <libempathy/empathy-tp-contact-factory.h> +#include <libempathy/empathy-camera-monitor.h> #include <libempathy/empathy-contact-manager.h> #include <libempathy/empathy-utils.h> @@ -58,6 +59,8 @@ typedef struct { struct _EmpathyNewCallDialogPriv { GtkWidget *check_video; + + EmpathyCameraMonitor *monitor; }; #define GET_PRIV(o) \ @@ -177,6 +180,16 @@ empathy_new_call_dialog_account_filter (EmpathyContactSelectorDialog *dialog, tp_proxy_prepare_async (connection, features, conn_prepared_cb, cb_data); } +static void +empathy_new_call_dialog_dispose (GObject *object) +{ + EmpathyNewCallDialogPriv *priv = GET_PRIV (object); + + tp_clear_object (&priv->monitor); + + G_OBJECT_CLASS (empathy_new_call_dialog_parent_class)->dispose (object); +} + static GObject * empathy_new_call_dialog_constructor (GType type, guint n_props, @@ -210,8 +223,13 @@ empathy_new_call_dialog_init (EmpathyNewCallDialog *dialog) EmpathyNewCallDialogPriv *priv = GET_PRIV (dialog); GtkWidget *image; + priv->monitor = empathy_camera_monitor_dup_singleton (); + /* add video toggle */ priv->check_video = gtk_check_button_new_with_mnemonic (_("Send _Video")); + g_object_bind_property (priv->monitor, "available", + priv->check_video, "sensitive", + G_BINDING_SYNC_CREATE); gtk_box_pack_end (GTK_BOX (parent->vbox), priv->check_video, FALSE, TRUE, 0); @@ -247,6 +265,7 @@ empathy_new_call_dialog_class_init ( g_type_class_add_private (class, sizeof (EmpathyNewCallDialogPriv)); object_class->constructor = empathy_new_call_dialog_constructor; + object_class->dispose = empathy_new_call_dialog_dispose; dialog_class->response = empathy_new_call_dialog_response; diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am index 571980dd8..56f2f3a79 100644 --- a/libempathy/Makefile.am +++ b/libempathy/Makefile.am @@ -13,6 +13,7 @@ AM_CPPFLAGS = \ $(GEOCLUE_CFLAGS) \ $(NETWORK_MANAGER_CFLAGS) \ $(CONNMAN_CFLAGS) \ + $(UDEV_CFLAGS) \ $(WARN_CFLAGS) \ $(DISABLE_DEPRECATED) @@ -29,7 +30,8 @@ libempathy_headers = \ action-chain-internal.h \ empathy-account-settings.h \ empathy-auth-factory.h \ - empathy-channel-factory.h \ + empathy-camera-monitor.h \ + empathy-channel-factory.h \ empathy-chatroom-manager.h \ empathy-chatroom.h \ empathy-connection-managers.h \ @@ -66,12 +68,13 @@ libempathy_headers = \ empathy-types.h \ empathy-utils.h -libempathy_la_SOURCES = \ +libempathy_handwritten_source = \ $(libempathy_headers) \ action-chain.c \ empathy-account-settings.c \ empathy-auth-factory.c \ - empathy-channel-factory.c \ + empathy-camera-monitor.c \ + empathy-channel-factory.c \ empathy-chatroom-manager.c \ empathy-chatroom.c \ empathy-connection-managers.c \ @@ -105,6 +108,10 @@ libempathy_la_SOURCES = \ empathy-tp-streamed-media.c \ empathy-utils.c +libempathy_la_SOURCES = \ + $(libempathy_handwritten_source) \ + cheese-camera-device-monitor.c cheese-camera-device-monitor.h + # do not distribute generated files nodist_libempathy_la_SOURCES =\ $(BUILT_SOURCES) @@ -115,11 +122,11 @@ libempathy_la_LIBADD = \ $(EMPATHY_LIBS) \ $(GEOCLUE_LIBS) \ $(NETWORK_MANAGER_LIBS) \ - $(CONNMAN_LIBS) + $(CONNMAN_LIBS) \ + $(UDEV_LIBS) check_c_sources = \ - $(libempathy_la_SOURCES) \ - $(libempathy_headers) + $(libempathy_handwritten_source) include $(top_srcdir)/tools/check-coding-style.mk check-local: check-coding-style diff --git a/libempathy/cheese-camera-device-monitor.c b/libempathy/cheese-camera-device-monitor.c new file mode 100644 index 000000000..85c6a0aa4 --- /dev/null +++ b/libempathy/cheese-camera-device-monitor.c @@ -0,0 +1,412 @@ +/* + * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org> + * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org> + * Copyright © 2008 Ryan Zeigler <zeiglerr@gmail.com> + * Copyright © 2010 Filippo Argiolas <filippo.argiolas@gmail.com> + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <glib-object.h> +#include <string.h> + +#ifdef HAVE_UDEV + #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1 + #include <gudev/gudev.h> +#else + #include <fcntl.h> + #include <unistd.h> + #include <sys/ioctl.h> + #if USE_SYS_VIDEOIO_H > 0 + #include <sys/types.h> + #include <sys/videoio.h> + #elif defined (__sun) + #include <sys/types.h> + #include <sys/videodev2.h> + #endif /* USE_SYS_VIDEOIO_H */ +#endif + +#include "cheese-camera-device-monitor.h" +#include "empathy-marshal.h" + +/** + * SECTION:cheese-camera-device-monitor + * @short_description: Simple object to enumerate v4l devices + * @include: cheese/cheese-camera-device-monitor.h + * + * #CheeseCameraDeviceMonitor provides a basic interface for + * video4linux device enumeration and hotplugging. + * + * It uses either GUdev or some platform specific code to list video + * devices. It is also capable (right now in linux only, with the + * udev backend) to monitor device plugging and emit a + * CheeseCameraDeviceMonitor::added or + * CheeseCameraDeviceMonitor::removed signal when an event happens. + */ + +G_DEFINE_TYPE (CheeseCameraDeviceMonitor, cheese_camera_device_monitor, G_TYPE_OBJECT) + +#define CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \ + CheeseCameraDeviceMonitorPrivate)) + +#define CHEESE_CAMERA_DEVICE_MONITOR_ERROR cheese_camera_device_monitor_error_quark () + +GST_DEBUG_CATEGORY (cheese_device_monitor_cat); +#define GST_CAT_DEFAULT cheese_device_monitor_cat + +enum CheeseCameraDeviceMonitorError +{ + CHEESE_CAMERA_DEVICE_MONITOR_ERROR_UNKNOWN, + CHEESE_CAMERA_DEVICE_MONITOR_ERROR_ELEMENT_NOT_FOUND +}; + +typedef struct +{ +#ifdef HAVE_UDEV + GUdevClient *client; +#else + guint filler; +#endif /* HAVE_UDEV */ +} CheeseCameraDeviceMonitorPrivate; + +enum +{ + ADDED, + REMOVED, + LAST_SIGNAL +}; + +static guint monitor_signals[LAST_SIGNAL]; + +#if 0 +GQuark +cheese_camera_device_monitor_error_quark (void) +{ + return g_quark_from_static_string ("cheese-camera-error-quark"); +} +#endif + +#ifdef HAVE_UDEV +static void +cheese_camera_device_monitor_added (CheeseCameraDeviceMonitor *monitor, + GUdevDevice *udevice) +{ + const char *device_file; + const char *product_name; + const char *vendor; + const char *product; + const char *bus; + gint vendor_id = 0; + gint product_id = 0; + gint v4l_version = 0; + + const gchar *devpath = g_udev_device_get_property (udevice, "DEVPATH"); + + GST_INFO ("Checking udev device '%s'", devpath); + + bus = g_udev_device_get_property (udevice, "ID_BUS"); + if (g_strcmp0 (bus, "usb") == 0) + { + vendor = g_udev_device_get_property (udevice, "ID_VENDOR_ID"); + if (vendor != NULL) + vendor_id = g_ascii_strtoll (vendor, NULL, 16); + product = g_udev_device_get_property (udevice, "ID_MODEL_ID"); + if (product != NULL) + product_id = g_ascii_strtoll (product, NULL, 16); + if (vendor_id == 0 || product_id == 0) + { + GST_WARNING ("Error getting vendor and product id"); + } + else + { + GST_INFO ("Found device %04x:%04x, getting capabilities...", vendor_id, product_id); + } + } + else + { + GST_INFO ("Not an usb device, skipping vendor and model id retrieval"); + } + + device_file = g_udev_device_get_device_file (udevice); + if (device_file == NULL) + { + GST_WARNING ("Error getting V4L device"); + return; + } + + /* vbi devices support capture capability too, but cannot be used, + * so detect them by device name */ + if (strstr (device_file, "vbi")) + { + GST_INFO ("Skipping vbi device: %s", device_file); + return; + } + + v4l_version = g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION"); + if (v4l_version == 2 || v4l_version == 1) + { + const char *caps; + + caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES"); + if (caps == NULL || strstr (caps, ":capture:") == NULL) + { + GST_WARNING ("Device %s seems to not have the capture capability, (radio tuner?)" + "Removing it from device list.", device_file); + return; + } + product_name = g_udev_device_get_property (udevice, "ID_V4L_PRODUCT"); + } + else if (v4l_version == 0) + { + GST_ERROR ("Fix your udev installation to include v4l_id, ignoring %s", device_file); + return; + } + else + { + g_assert_not_reached (); + } + + g_signal_emit (monitor, monitor_signals[ADDED], 0, + devpath, + device_file, + product_name, + v4l_version); +} + +static void +cheese_camera_device_monitor_removed (CheeseCameraDeviceMonitor *monitor, + GUdevDevice *udevice) +{ + g_signal_emit (monitor, monitor_signals[REMOVED], 0, + g_udev_device_get_property (udevice, "DEVPATH")); +} + +static void +cheese_camera_device_monitor_uevent_cb (GUdevClient *client, + const gchar *action, + GUdevDevice *udevice, + CheeseCameraDeviceMonitor *monitor) +{ + if (g_str_equal (action, "remove")) + cheese_camera_device_monitor_removed (monitor, udevice); + else if (g_str_equal (action, "add")) + cheese_camera_device_monitor_added (monitor, udevice); +} + +/** + * cheese_camera_device_monitor_coldplug: + * @monitor: a #CheeseCameraDeviceMonitor object. + * + * Will actively look for plugged in cameras and emit + * ::added for those new cameras. + * This is only required when your program starts, so as to connect + * to those signals before they are emitted. + */ +void +cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor) +{ + CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor); + GList *devices, *l; + gint i = 0; + + if (priv->client == NULL) + return; + + GST_INFO ("Probing devices with udev..."); + + devices = g_udev_client_query_by_subsystem (priv->client, "video4linux"); + + /* Initialize camera structures */ + for (l = devices; l != NULL; l = l->next) + { + cheese_camera_device_monitor_added (monitor, l->data); + g_object_unref (l->data); + i++; + } + g_list_free (devices); + + if (i == 0) GST_WARNING ("No device found"); +} + +#else /* HAVE_UDEV */ +void +cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor) +{ + #if 0 + CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor); + struct v4l2_capability v2cap; + struct video_capability v1cap; + int fd, ok; + + if ((fd = open (device_path, O_RDONLY | O_NONBLOCK)) < 0) + { + g_warning ("Failed to open %s: %s", device_path, strerror (errno)); + return; + } + ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap); + if (ok < 0) + { + ok = ioctl (fd, VIDIOCGCAP, &v1cap); + if (ok < 0) + { + g_warning ("Error while probing v4l capabilities for %s: %s", + device_path, strerror (errno)); + close (fd); + return; + } + g_print ("Detected v4l device: %s\n", v1cap.name); + g_print ("Device type: %d\n", v1cap.type); + gstreamer_src = "v4lsrc"; + product_name = v1cap.name; + } + else + { + guint cap = v2cap.capabilities; + g_print ("Detected v4l2 device: %s\n", v2cap.card); + g_print ("Driver: %s, version: %d\n", v2cap.driver, v2cap.version); + + /* g_print ("Bus info: %s\n", v2cap.bus_info); */ /* Doesn't seem anything useful */ + g_print ("Capabilities: 0x%08X\n", v2cap.capabilities); + if (!(cap & V4L2_CAP_VIDEO_CAPTURE)) + { + g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n" + "Removing it from device list.\n", device_path); + close (fd); + return; + } + gstreamer_src = "v4l2src"; + product_name = (char *) v2cap.card; + } + close (fd); + + GList *devices, *l; + + g_print ("Probing devices with udev...\n"); + + if (priv->client == NULL) + return; + + devices = g_udev_client_query_by_subsystem (priv->client, "video4linux"); + + /* Initialize camera structures */ + for (l = devices; l != NULL; l = l->next) + { + cheese_camera_device_monitor_added (monitor, l->data); + g_object_unref (l->data); + } + g_list_free (devices); + #endif +} + +#endif /* HAVE_UDEV */ + +static void +cheese_camera_device_monitor_finalize (GObject *object) +{ +#ifdef HAVE_UDEV + CheeseCameraDeviceMonitor *monitor = CHEESE_CAMERA_DEVICE_MONITOR (object); + CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor); + + if (priv->client != NULL) + { + g_object_unref (priv->client); + priv->client = NULL; + } +#endif /* HAVE_UDEV */ + G_OBJECT_CLASS (cheese_camera_device_monitor_parent_class)->finalize (object); +} + +static void +cheese_camera_device_monitor_class_init (CheeseCameraDeviceMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + if (cheese_device_monitor_cat == NULL) + GST_DEBUG_CATEGORY_INIT (cheese_device_monitor_cat, + "cheese-device-monitor", + 0, "Cheese Camera Device Monitor"); + + object_class->finalize = cheese_camera_device_monitor_finalize; + + /** + * CheeseCameraDeviceMonitor::added: + * @device: A private object representing the newly added camera. + * @id: Device unique identifier. + * @device: Device file name (e.g. /dev/video2). + * @product_name: Device product name (human readable, intended to be displayed in a UI). + * @api_version: Supported video4linux API: 1 for v4l, 2 for v4l2. + * + * The ::added signal is emitted when a camera is added, or on start-up + * after #cheese_camera_device_monitor_colplug is called. + **/ + monitor_signals[ADDED] = g_signal_new ("added", G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (CheeseCameraDeviceMonitorClass, added), + NULL, NULL, + _empathy_marshal_VOID__STRING_STRING_STRING_INT, + G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); + + /** + * CheeseCameraDeviceMonitor::removed: + * @device: A private object representing the newly added camera + * @id: Device unique identifier. + * + * The ::removed signal is emitted when a camera is un-plugged, or + * disabled on the system. + **/ + monitor_signals[REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (CheeseCameraDeviceMonitorClass, removed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + + g_type_class_add_private (klass, sizeof (CheeseCameraDeviceMonitorPrivate)); +} + +static void +cheese_camera_device_monitor_init (CheeseCameraDeviceMonitor *monitor) +{ +#ifdef HAVE_UDEV + CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor); + const gchar *const subsystems[] = {"video4linux", NULL}; + + priv->client = g_udev_client_new (subsystems); + g_signal_connect (G_OBJECT (priv->client), "uevent", + G_CALLBACK (cheese_camera_device_monitor_uevent_cb), monitor); +#endif /* HAVE_UDEV */ +} + +/** + * cheese_camera_device_monitor_new: + * + * Returns a new #CheeseCameraDeviceMonitor object. + * + * Return value: a new #CheeseCameraDeviceMonitor object. + **/ +CheeseCameraDeviceMonitor * +cheese_camera_device_monitor_new (void) +{ + return g_object_new (CHEESE_TYPE_CAMERA_DEVICE_MONITOR, NULL); +} + +/* + * vim: sw=2 ts=8 cindent noai bs=2 + */ diff --git a/libempathy/cheese-camera-device-monitor.h b/libempathy/cheese-camera-device-monitor.h new file mode 100644 index 000000000..d0d98a45f --- /dev/null +++ b/libempathy/cheese-camera-device-monitor.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org> + * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org> + * Copyright © 2008 Ryan zeigler <zeiglerr@gmail.com> + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __CHEESE_CAMERA_DEVICE_MONITOR_H__ +#define __CHEESE_CAMERA_DEVICE_MONITOR_H__ + +#include <glib-object.h> +#include <gst/interfaces/xoverlay.h> + +G_BEGIN_DECLS + +#define CHEESE_TYPE_CAMERA_DEVICE_MONITOR (cheese_camera_device_monitor_get_type ()) +#define CHEESE_CAMERA_DEVICE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \ + CheeseCameraDeviceMonitor)) +#define CHEESE_CAMERA_DEVICE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \ + CheeseCameraDeviceMonitorClass)) +#define CHEESE_IS_CAMERA_DEVICE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CHEESE_TYPE_CAMERA_DEVICE_MONITOR)) +#define CHEESE_IS_CAMERA_DEVICE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CHEESE_TYPE_CAMERA_DEVICE_MONITOR)) +#define CHEESE_CAMERA_DEVICE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \ + CheeseCameraDeviceMonitorClass)) + +typedef struct _CheeseCameraDeviceMonitorClass CheeseCameraDeviceMonitorClass; +typedef struct _CheeseCameraDeviceMonitor CheeseCameraDeviceMonitor; + +struct _CheeseCameraDeviceMonitor +{ + GObject parent; +}; + +struct _CheeseCameraDeviceMonitorClass +{ + GObjectClass parent_class; + + void (*added)(CheeseCameraDeviceMonitor *camera, + const char *id, + const char *device_file, + const char *product_name, + int api_version); + void (*removed)(CheeseCameraDeviceMonitor *camera, const char *id); +}; + +GType cheese_camera_device_monitor_get_type (void) G_GNUC_CONST; +CheeseCameraDeviceMonitor *cheese_camera_device_monitor_new (void); +void cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor); + +G_END_DECLS + +#endif /* __CHEESE_CAMERA_DEVICE_MONITOR_H__ */ diff --git a/libempathy/empathy-camera-monitor.c b/libempathy/empathy-camera-monitor.c new file mode 100644 index 000000000..3d80d40b7 --- /dev/null +++ b/libempathy/empathy-camera-monitor.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2011 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> + */ + +#include <config.h> + +#include <string.h> + +#include <telepathy-glib/util.h> + +#include "empathy-camera-monitor.h" +#include "cheese-camera-device-monitor.h" +#include "empathy-utils.h" + +#define DEBUG_FLAG EMPATHY_DEBUG_OTHER +#include "empathy-debug.h" + +struct _EmpathyCameraMonitorPrivate +{ + CheeseCameraDeviceMonitor *cheese_monitor; + gint num_cameras; +}; + +enum +{ + PROP_0, + PROP_AVAILABLE, +}; + +G_DEFINE_TYPE (EmpathyCameraMonitor, empathy_camera_monitor, G_TYPE_OBJECT); + +static EmpathyCameraMonitor *manager_singleton = NULL; + +static void +on_camera_added (CheeseCameraDeviceMonitor *device, + gchar *id, + gchar *filename, + gchar *product_name, + gint api_version, + EmpathyCameraMonitor *self) +{ + self->priv->num_cameras++; + + if (self->priv->num_cameras == 1) + g_object_notify (G_OBJECT (self), "available"); +} + +static void +on_camera_removed (CheeseCameraDeviceMonitor *device, + gchar *id, + EmpathyCameraMonitor *self) +{ + self->priv->num_cameras--; + + if (self->priv->num_cameras == 0) + g_object_notify (G_OBJECT (self), "available"); +} + +static void +empathy_camera_monitor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyCameraMonitor *self = (EmpathyCameraMonitor *) object; + + switch (prop_id) + { + case PROP_AVAILABLE: + g_value_set_boolean (value, self->priv->num_cameras > 0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +empathy_camera_monitor_dispose (GObject *object) +{ + EmpathyCameraMonitor *self = EMPATHY_CAMERA_MONITOR (object); + + tp_clear_object (&self->priv->cheese_monitor); + + G_OBJECT_CLASS (empathy_camera_monitor_parent_class)->dispose (object); +} + +static GObject * +empathy_camera_monitor_constructor (GType type, + guint n_props, + GObjectConstructParam *props) +{ + GObject *retval; + + if (manager_singleton) + { + retval = g_object_ref (manager_singleton); + } + else + { + retval = + G_OBJECT_CLASS (empathy_camera_monitor_parent_class)-> + constructor (type, n_props, props); + + manager_singleton = EMPATHY_CAMERA_MONITOR (retval); + g_object_add_weak_pointer (retval, (gpointer) & manager_singleton); + } + + return retval; +} + +static void +empathy_camera_monitor_constructed (GObject *object) +{ + EmpathyCameraMonitor *self = (EmpathyCameraMonitor *) object; + + G_OBJECT_CLASS (empathy_camera_monitor_parent_class)->constructed (object); + + cheese_camera_device_monitor_coldplug (self->priv->cheese_monitor); +} + +static void +empathy_camera_monitor_class_init (EmpathyCameraMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = empathy_camera_monitor_dispose; + object_class->constructor = empathy_camera_monitor_constructor; + object_class->constructed = empathy_camera_monitor_constructed; + object_class->get_property = empathy_camera_monitor_get_property; + + g_object_class_install_property (object_class, PROP_AVAILABLE, + g_param_spec_boolean ("available", "Available", + "Camera available", TRUE, G_PARAM_READABLE)); + + g_type_class_add_private (object_class, + sizeof (EmpathyCameraMonitorPrivate)); +} + +static void +empathy_camera_monitor_init (EmpathyCameraMonitor *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitorPrivate); + + self->priv->cheese_monitor = cheese_camera_device_monitor_new (); + + g_signal_connect (self->priv->cheese_monitor, "added", + G_CALLBACK (on_camera_added), self); + g_signal_connect (self->priv->cheese_monitor, "removed", + G_CALLBACK (on_camera_removed), self); + +#ifndef HAVE_UDEV + /* No udev, assume there are cameras present */ + self->priv->num_cameras = 1; +#endif +} + +EmpathyCameraMonitor * +empathy_camera_monitor_dup_singleton (void) +{ + return g_object_new (EMPATHY_TYPE_CAMERA_MONITOR, NULL); +} + +gboolean empathy_camera_monitor_get_available (EmpathyCameraMonitor *self) +{ + g_return_val_if_fail (EMPATHY_IS_CAMERA_MONITOR (self), FALSE); + + return self->priv->num_cameras > 0; +} diff --git a/libempathy/empathy-camera-monitor.h b/libempathy/empathy-camera-monitor.h new file mode 100644 index 000000000..d2d9bb89e --- /dev/null +++ b/libempathy/empathy-camera-monitor.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2011 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> + */ + +#ifndef __EMPATHY_CAMERA_MONITOR_H__ +#define __EMPATHY_CAMERA_MONITOR_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS +#define EMPATHY_TYPE_CAMERA_MONITOR (empathy_camera_monitor_get_type ()) +#define EMPATHY_CAMERA_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitor)) +#define EMPATHY_CAMERA_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitorClass)) +#define EMPATHY_IS_CAMERA_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CAMERA_MONITOR)) +#define EMPATHY_IS_CAMERA_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CAMERA_MONITOR)) +#define EMPATHY_CAMERA_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitorClass)) + +typedef struct _EmpathyCameraMonitor EmpathyCameraMonitor; +typedef struct _EmpathyCameraMonitorClass EmpathyCameraMonitorClass; +typedef struct _EmpathyCameraMonitorPrivate EmpathyCameraMonitorPrivate; + +struct _EmpathyCameraMonitor +{ + GObject parent; + EmpathyCameraMonitorPrivate *priv; +}; + +struct _EmpathyCameraMonitorClass +{ + GObjectClass parent_class; +}; + +GType empathy_camera_monitor_get_type (void) G_GNUC_CONST; + +EmpathyCameraMonitor *empathy_camera_monitor_dup_singleton (void); + +gboolean empathy_camera_monitor_get_available (EmpathyCameraMonitor *self); + +G_END_DECLS +#endif /* __EMPATHY_CAMERA_MONITOR_H__ */ diff --git a/libempathy/empathy-contact.c b/libempathy/empathy-contact.c index 42faad483..955ee885f 100644 --- a/libempathy/empathy-contact.c +++ b/libempathy/empathy-contact.c @@ -40,6 +40,7 @@ #endif #include "empathy-contact.h" +#include "empathy-camera-monitor.h" #include "empathy-utils.h" #include "empathy-enum-types.h" #include "empathy-marshal.h" @@ -1223,8 +1224,18 @@ empathy_contact_can_do_action (EmpathyContact *self, sensitivity = empathy_contact_can_voip_audio (self); break; case EMPATHY_ACTION_VIDEO_CALL: - sensitivity = empathy_contact_can_voip_video (self); - break; + { + EmpathyCameraMonitor *monitor; + + monitor = empathy_camera_monitor_dup_singleton (); + + sensitivity = empathy_contact_can_voip_video (self); + sensitivity = sensitivity && + empathy_camera_monitor_get_available (monitor); + + g_object_unref (monitor); + break; + } case EMPATHY_ACTION_VIEW_LOGS: sensitivity = contact_has_log (self); break; diff --git a/src/empathy-call-window.c b/src/empathy-call-window.c index 40258169c..4728e9f64 100644 --- a/src/empathy-call-window.c +++ b/src/empathy-call-window.c @@ -36,6 +36,7 @@ #include <gst/farsight/fs-element-added-notifier.h> #include <gst/farsight/fs-utils.h> +#include <libempathy/empathy-camera-monitor.h> #include <libempathy/empathy-tp-contact-factory.h> #include <libempathy/empathy-utils.h> #include <libempathy-gtk/empathy-avatar-image.h> @@ -101,6 +102,8 @@ struct _EmpathyCallWindowPriv EmpathyContact *contact; + EmpathyCameraMonitor *camera_monitor; + guint call_state; gboolean outgoing; @@ -122,6 +125,7 @@ struct _EmpathyCallWindowPriv GtkWidget *pane; GtkAction *redial; GtkAction *menu_fullscreen; + GtkAction *action_camera; GtkAction *action_camera_on; GtkWidget *tool_button_camera_off; GtkWidget *tool_button_camera_preview; @@ -976,6 +980,7 @@ empathy_call_window_init (EmpathyCallWindow *self) "camera_off", &priv->tool_button_camera_off, "camera_preview", &priv->tool_button_camera_preview, "camera_on", &priv->tool_button_camera_on, + "camera", &priv->action_camera, "action_camera_on", &priv->action_camera_on, "details_vbox", &priv->details_vbox, "vcodec_encoding_label", &priv->vcodec_encoding_label, @@ -1008,6 +1013,21 @@ empathy_call_window_init (EmpathyCallWindow *self) gtk_action_set_sensitive (priv->menu_fullscreen, FALSE); + priv->camera_monitor = empathy_camera_monitor_dup_singleton (); + + g_object_bind_property (priv->camera_monitor, "available", + priv->tool_button_camera_off, "sensitive", + G_BINDING_SYNC_CREATE); + g_object_bind_property (priv->camera_monitor, "available", + priv->tool_button_camera_preview, "sensitive", + G_BINDING_SYNC_CREATE); + g_object_bind_property (priv->camera_monitor, "available", + priv->tool_button_camera_on, "sensitive", + G_BINDING_SYNC_CREATE); + g_object_bind_property (priv->camera_monitor, "available", + priv->action_camera, "sensitive", + G_BINDING_SYNC_CREATE); + priv->lock = g_mutex_new (); gtk_container_add (GTK_CONTAINER (self), top_vbox); @@ -1604,6 +1624,7 @@ empathy_call_window_dispose (GObject *object) tp_clear_object (&priv->video_tee); tp_clear_object (&priv->ui_manager); tp_clear_object (&priv->fullscreen); + tp_clear_object (&priv->camera_monitor); g_list_free_full (priv->notifiers, g_object_unref); @@ -2380,7 +2401,8 @@ empathy_call_window_state_changed_cb (EmpathyCallHandler *handler, empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING); can_send_video = priv->video_input != NULL && - empathy_contact_can_voip_video (priv->contact); + empathy_contact_can_voip_video (priv->contact) && + empathy_camera_monitor_get_available (priv->camera_monitor); g_object_get (priv->handler, "call-channel", &call, NULL); |