--- libcheese/cheese-camera-device-monitor.c.orig 2012-08-22 21:04:40.000000000 +0200 +++ libcheese/cheese-camera-device-monitor.c 2013-09-15 10:26:16.000000000 +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 @@ -44,6 +52,10 @@ #include "cheese-camera-device-monitor.h" +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel) +static void cheese_camera_device_monitor_init_event (CheeseCameraDeviceMonitor *monitor); +#endif + /** * SECTION:cheese-camera-device-monitor * @short_description: Simple object to enumerate v4l devices @@ -302,7 +314,134 @@ g_list_free (devices); } -#else /* HAVE_UDEV */ +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel) +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 + { + 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 void +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 + { + 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; +} + void cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor) { @@ -430,6 +569,41 @@ 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; + GDir *dir; + GError *error = NULL; + + event_fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (event_fd < 0) + { + GST_WARNING ("Failed to create devd socket: %s", g_strerror (errno)); + 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); + } + else + { + GST_WARNING("Failed to connect to /var/run/devd.pipe: %s", + g_strerror (errno)); + close (event_fd); + } +} +#endif + static void cheese_camera_device_monitor_init (CheeseCameraDeviceMonitor *monitor) { @@ -440,6 +614,58 @@ 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) + GDir *dir; + GError *error = NULL; + const char *fname; + + cheese_camera_device_monitor_init_event (monitor); + + dir = g_dir_open ("/var/run", 0, &error); + if (dir == NULL) + { + GST_WARNING ("Failed to open /var/run for reading: %s", + (error != NULL) ? error->message : "Unknown error"); + return; + } + + while ((fname = g_dir_read_name (dir)) != NULL) + { + if (strncmp (fname, "webcamd.", strlen ("webcamd.")) == 0) + { + GRegex *rex; + GMatchInfo *info; + + rex = g_regex_new ("webcamd\\.[0-9]+\\.[0-9]+\\.([0-9]+)\\.pid", 0, 0, NULL); + if (g_regex_match (rex, fname, 0, &info)) + { + char *devname, *dnum, *product; + CheeseCameraDevice *device; + GError *derr = NULL; + + dnum = g_match_info_fetch (info, 1); + + devname = g_strdup_printf ("/dev/video%s", dnum); + 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 (dnum); + g_free (product); + g_free (devname); + } + g_match_info_free (info); + g_regex_unref (rex); + } + } + g_dir_close (dir); #endif /* HAVE_UDEV */ }