--- libcheese/cheese-camera-device-monitor.c.orig 2012-08-22 21:04:40.000000000 +0200 +++ libcheese/cheese-camera-device-monitor.c 2013-09-22 23:12:35.072353163 +0200 @@ -33,6 +33,14 @@ #include #include #include + #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + #include + #include + #include + #include + #include + #include + #endif #if USE_SYS_VIDEOIO_H > 0 #include #include @@ -302,6 +310,220 @@ g_list_free (devices); } +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +static void cheese_camera_device_monitor_init_event (CheeseCameraDeviceMonitor *monitor); +static gboolean cheese_camera_device_monitor_is_camera (const char *devname); + +static gboolean cheese_camera_device_monitor_event_inited = FALSE; + +static gboolean +cheese_camera_device_monitor_is_camera (const char *devname){ + gboolean is_camera = FALSE; + int fd; + struct v4l2_capability v2cap; + g_return_val_if_fail (devname != NULL, FALSE); + + fd = open (devname, O_RDONLY); + if (fd < 0) + { + GST_WARNING("Failed to query: %s", devname); + return FALSE; + } + else{ + if (ioctl (fd, VIDIOC_QUERYCAP, &v2cap) == 0) + { + is_camera = ((v2cap.capabilities & 0x00000001)==1); + } + else{ + GST_WARNING("Failed to get product name for %s: %s", devname, + g_strerror (errno)); + } + } + + close (fd); + + return is_camera; +} + +static char * +cheese_camera_device_monitor_get_product (const char *devname) +{ + int fd; + struct v4l2_capability v2cap; + char *product = NULL; + + g_return_val_if_fail (devname != NULL, NULL); + + fd = open (devname, O_RDONLY); + if (fd < 0) + { + GST_WARNING("Failed to get product name for %s: %s", devname, + g_strerror (errno)); + return NULL; + } + + if (ioctl (fd, VIDIOC_QUERYCAP, &v2cap) == 0) + { + product = g_strdup ((const char *) v2cap.card); + } + else + { + GST_WARNING("Failed to get product name for %s: %s", devname, + g_strerror (errno)); + } + + close (fd); + + return product; +} + +static void +cheese_camera_device_monitor_process_event (const char *event, + CheeseCameraDeviceMonitor *monitor) +{ + g_return_if_fail (event != NULL); + + GST_INFO ("Received devd event: %s", event); + + switch (event[0]) + { + case '!': + { + GRegex *rex; + GMatchInfo *info; + + rex = g_regex_new ("subsystem=CDEV type=(CREATE|DESTROY) cdev=(video[0-9]+)", 0, 0, NULL); + if (g_regex_match (rex, event, 0, &info)) + { + char *devname, *type, *vdev, *product = NULL; + CheeseCameraDevice *device; + GError *error = NULL; + + type = g_match_info_fetch (info, 1); + vdev = g_match_info_fetch (info, 2); + + devname = g_strdup_printf ("/dev/%s", vdev); + + if (g_strcmp0 (type, "DESTROY") == 0) + { + g_signal_emit (monitor, monitor_signals[REMOVED], 0, + devname); + } + else + { + if(cheese_camera_device_monitor_is_camera (devname)) + { + product = cheese_camera_device_monitor_get_product (devname); + if (product == NULL) + product = g_strdup ("WebCamd Device"); + device = cheese_camera_device_new (devname, devname, + product, + 2, + &error); + if (device == NULL) + GST_WARNING ("Device initialization for %s failed: %s", + devname, + (error != NULL) ? error->message : "Unknown reason"); + g_signal_emit (monitor, monitor_signals[ADDED], 0, device); + } + + g_free (product); + } + g_free (devname); + g_free (vdev); + g_free (type); + } + g_match_info_free (info); + g_regex_unref (rex); + break; + } + default: + break; + } +} + +static gboolean +cheese_camera_device_monitor_event_cb (GIOChannel *source, + GIOCondition condition, + gpointer user_data) +{ + char *event; + gsize terminator; + GIOStatus status; + CheeseCameraDeviceMonitor *monitor; + + monitor = (CheeseCameraDeviceMonitor *) user_data; + + status = g_io_channel_read_line (source, &event, NULL, &terminator, NULL); + if (status == G_IO_STATUS_NORMAL) + { + event[terminator] = 0; + cheese_camera_device_monitor_process_event (event, monitor); + g_free (event); + } + else + { + if (cheese_camera_device_monitor_event_inited) + { + int fd; + + cheese_camera_device_monitor_init_event (monitor); + fd = g_io_channel_unix_get_fd (source); + g_io_channel_shutdown (source, FALSE, NULL); + close (fd); + + return FALSE; + } + } + + return TRUE; +} + +void +cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor) +{ + GDir *dir; + GError *error = NULL; + const char *fname; + + dir = g_dir_open ("/dev", 0, &error); + if (dir == NULL) + { + GST_WARNING ("Failed to open /dev for reading: %s", + (error != NULL) ? error->message : "Unknown error"); + return; + } + + while ((fname = g_dir_read_name (dir)) != NULL) + { + if ( strncmp (fname, "video", strlen ("video")) == 0) + { + char *devname, *product; + + devname = g_strdup_printf ("/dev/%s", fname); + if (cheese_camera_device_monitor_is_camera (devname)) + { + CheeseCameraDevice *device; + GError *derr = NULL; + + product = cheese_camera_device_monitor_get_product (devname); + if (product == NULL) + product = g_strdup ("WebCamd Device"); + + device = cheese_camera_device_new (devname, devname, product, 2, &derr); + if (device == NULL) + GST_WARNING ("Device initialization for %s failed: %s", devname, + (derr != NULL) ? derr->message : "Unknown reason"); + + g_signal_emit (monitor, monitor_signals[ADDED], 0, device); + + g_free (product); + } + g_free (devname); + } + } + g_dir_close (dir); +} #else /* HAVE_UDEV */ void cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor) @@ -430,6 +652,42 @@ g_type_class_add_private (klass, sizeof (CheeseCameraDeviceMonitorPrivate)); } +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +static void +cheese_camera_device_monitor_init_event (CheeseCameraDeviceMonitor *monitor) +{ + int event_fd; + struct sockaddr_un addr; + + event_fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (event_fd < 0) + { + GST_WARNING ("Failed to create devd socket: %s", g_strerror (errno)); + cheese_camera_device_monitor_event_inited = FALSE; + return; + } + + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, "/var/run/devd.pipe", sizeof (addr.sun_path)); + if (connect (event_fd, (struct sockaddr *) &addr, sizeof (addr)) == 0) + { + GIOChannel *channel; + + channel = g_io_channel_unix_new (event_fd); + g_io_add_watch (channel, G_IO_IN, cheese_camera_device_monitor_event_cb, monitor); + g_io_channel_unref (channel); + cheese_camera_device_monitor_event_inited = TRUE; + } + else + { + GST_WARNING("Failed to connect to /var/run/devd.pipe: %s", + g_strerror (errno)); + close (event_fd); + cheese_camera_device_monitor_event_inited = FALSE; + } +} +#endif + static void cheese_camera_device_monitor_init (CheeseCameraDeviceMonitor *monitor) { @@ -440,6 +698,8 @@ priv->client = g_udev_client_new (subsystems); g_signal_connect (G_OBJECT (priv->client), "uevent", G_CALLBACK (cheese_camera_device_monitor_uevent_cb), monitor); +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + cheese_camera_device_monitor_init_event (monitor); #endif /* HAVE_UDEV */ }