summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorРуслан Ижбулатов <lrn1986@gmail.com>2015-04-28 21:43:56 +0000
committerOlivier Crête <olivier.crete@collabora.com>2015-04-28 20:24:26 -0400
commita31855d61808dfb4393c939fc42160ee765501b0 (patch)
tree16bdf02a3af2cb0ffda1456884ab46c80bad4a51
parentff12434b9fe934f91c8d50a97732837ab45ac040 (diff)
downloadgstreamer-plugins-bad-a31855d61808dfb4393c939fc42160ee765501b0.tar.gz
GstDeviceProvider implementation for WIN Kernel Streaming plugin
gst_ks_device_provider_probe() is a no-braier, just runs ks_enumerate_devices() and reports the results. Monitoring is a bit more tricky. We have to create a dummy message-processing window and register device change notifications for it. As kernel streaming can (and should) be used for audio capture and audio playback, this change also has certain placeholders for such. https://bugzilla.gnome.org/show_bug.cgi?id=747757
-rw-r--r--sys/winks/Makefile.am1
-rw-r--r--sys/winks/gstksclock.c2
-rw-r--r--sys/winks/gstksvideosrc.c28
-rw-r--r--sys/winks/ksdeviceprovider.c795
-rw-r--r--sys/winks/ksdeviceprovider.h101
-rw-r--r--sys/winks/kshelpers.c13
-rw-r--r--sys/winks/kshelpers.h2
7 files changed, 925 insertions, 17 deletions
diff --git a/sys/winks/Makefile.am b/sys/winks/Makefile.am
index 4233b6493..10626b104 100644
--- a/sys/winks/Makefile.am
+++ b/sys/winks/Makefile.am
@@ -3,6 +3,7 @@ plugin_LTLIBRARIES = libgstwinks.la
libgstwinks_la_SOURCES = gstksclock.c \
gstksvideodevice.c \
gstksvideosrc.c \
+ ksdeviceprovider.c ksdeviceprovider.h \
kshelpers.c kshelpers.h \
ksvideohelpers.c
libgstwinks_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \
diff --git a/sys/winks/gstksclock.c b/sys/winks/gstksclock.c
index e67e02f17..8d46905b7 100644
--- a/sys/winks/gstksclock.c
+++ b/sys/winks/gstksclock.c
@@ -133,7 +133,7 @@ gst_ks_clock_open (GstKsClock * self)
priv->state = KSSTATE_STOP;
- devices = ks_enumerate_devices (&KSCATEGORY_CLOCK);
+ devices = ks_enumerate_devices (&KSCATEGORY_CLOCK, &KSCATEGORY_CAPTURE);
if (devices == NULL)
goto error;
diff --git a/sys/winks/gstksvideosrc.c b/sys/winks/gstksvideosrc.c
index 3dab4aced..859699af9 100644
--- a/sys/winks/gstksvideosrc.c
+++ b/sys/winks/gstksvideosrc.c
@@ -45,6 +45,7 @@
#include "gstksvideodevice.h"
#include "kshelpers.h"
#include "ksvideohelpers.h"
+#include "ksdeviceprovider.h"
#define DEFAULT_DEVICE_PATH NULL
#define DEFAULT_DEVICE_NAME NULL
@@ -222,9 +223,6 @@ gst_ks_video_src_class_init (GstKsVideoSrcClass * klass)
g_param_spec_boolean ("enable-quirks", "Enable quirks",
"Enable driver-specific quirks", DEFAULT_ENABLE_QUIRKS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
- 0, "Kernel streaming video source");
}
static void
@@ -394,7 +392,7 @@ gst_ks_video_src_get_device_name_values (GstKsVideoSrc * self)
GList *devices, *cur;
GArray *array = g_array_new (TRUE, TRUE, sizeof (GValue));
- devices = ks_enumerate_devices (&KSCATEGORY_VIDEO);
+ devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
if (devices == NULL)
return array;
@@ -425,7 +423,7 @@ gst_ks_video_src_open_device (GstKsVideoSrc * self)
g_assert (priv->device == NULL);
- devices = ks_enumerate_devices (&KSCATEGORY_VIDEO);
+ devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
if (devices == NULL)
goto error_no_devices;
@@ -438,10 +436,14 @@ gst_ks_video_src_open_device (GstKsVideoSrc * self)
entry->index, entry->name, entry->path);
}
- for (cur = devices; cur != NULL && device == NULL; cur = cur->next) {
+ for (cur = devices; cur != NULL; cur = cur->next) {
KsDeviceEntry *entry = cur->data;
gboolean match;
+ if (device != NULL) {
+ ks_device_entry_free (entry);
+ continue;
+ }
if (priv->device_path != NULL) {
match = g_ascii_strcasecmp (entry->path, priv->device_path) == 0;
} else if (priv->device_name != NULL) {
@@ -1069,8 +1071,18 @@ error_alloc_buffer:
static gboolean
plugin_init (GstPlugin * plugin)
{
- return gst_element_register (plugin, "ksvideosrc",
- GST_RANK_NONE, GST_TYPE_KS_VIDEO_SRC);
+ GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
+ 0, "Kernel streaming video source");
+
+ if (!gst_element_register (plugin, "ksvideosrc",
+ GST_RANK_NONE, GST_TYPE_KS_VIDEO_SRC))
+ return FALSE;
+
+ if (!gst_device_provider_register (plugin, "ksdeviceprovider",
+ GST_RANK_PRIMARY, GST_TYPE_KS_DEVICE_PROVIDER))
+ return FALSE;
+
+ return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
diff --git a/sys/winks/ksdeviceprovider.c b/sys/winks/ksdeviceprovider.c
new file mode 100644
index 000000000..54429ebe6
--- /dev/null
+++ b/sys/winks/ksdeviceprovider.c
@@ -0,0 +1,795 @@
+/* GStreamer
+ * Copyright (C) 2015 Руслан Ижбулатов <lrn1986@gmail.com>
+ *
+ * ksdeviceprovider.c: Kernel Streaming device probing and monitoring
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstksvideosrc.h"
+#include "ksdeviceprovider.h"
+
+#include <string.h>
+
+#include <dbt.h> /* for DBT_* consts and [_]DEV_* structs */
+#include <devguid.h> /* for GUID_DEVCLASS_WCEUSBS */
+#include <setupapi.h> /* for DIGCF_ALLCLASSES */
+
+#include <gst/gst.h>
+
+#include "kshelpers.h"
+#include "ksvideohelpers.h"
+
+
+GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
+#define GST_CAT_DEFAULT gst_ks_debug
+
+
+static GstDevice *gst_ks_device_new (guint id,
+ const gchar * device_name, GstCaps * caps, const gchar * device_path,
+ GstKsDeviceType type);
+
+G_DEFINE_TYPE (GstKsDeviceProvider, gst_ks_device_provider,
+ GST_TYPE_DEVICE_PROVIDER);
+
+static GList *gst_ks_device_provider_probe (GstDeviceProvider * provider);
+static gboolean gst_ks_device_provider_start (GstDeviceProvider * provider);
+static void gst_ks_device_provider_stop (GstDeviceProvider * provider);
+
+static void
+gst_ks_device_provider_class_init (GstKsDeviceProviderClass * klass)
+{
+ GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
+
+ dm_class->probe = gst_ks_device_provider_probe;
+ dm_class->start = gst_ks_device_provider_start;
+ dm_class->stop = gst_ks_device_provider_stop;
+
+ gst_device_provider_class_set_static_metadata (dm_class,
+ "KernelStreaming Device Provider", "Sink/Source/Audio/Video",
+ "List and provide KernelStreaming source and sink devices",
+ "Руслан Ижбулатов <lrn1986@gmail.com>");
+}
+
+static void
+gst_ks_device_provider_init (GstKsDeviceProvider * self)
+{
+}
+
+static GstDevice *
+new_video_source (const KsDeviceEntry * info)
+{
+ GstCaps *caps;
+ HANDLE filter_handle;
+ GList *media_types;
+ GList *cur;
+
+ g_assert (info->path != NULL);
+
+ caps = gst_caps_new_empty ();
+
+ filter_handle = CreateFile (info->path,
+ GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+ if (!ks_is_valid_handle (filter_handle))
+ goto error;
+
+ media_types = ks_video_probe_filter_for_caps (filter_handle);
+
+ for (cur = media_types; cur != NULL; cur = cur->next) {
+ KsVideoMediaType *media_type = cur->data;
+
+ gst_caps_append (caps, gst_caps_copy (media_type->translated_caps));
+
+ ks_video_media_type_free (media_type);
+ }
+
+ CloseHandle (filter_handle);
+ g_list_free (media_types);
+
+ return gst_ks_device_new (info->index, info->name,
+ caps, info->path, GST_KS_DEVICE_TYPE_VIDEO_SOURCE);
+error:
+ gst_caps_unref (caps);
+ return NULL;
+}
+
+static GList *
+gst_ks_device_provider_probe (GstDeviceProvider * provider)
+{
+ /*GstKsDeviceProvider *self = GST_KS_DEVICE_PROVIDER (provider); */
+ GList *devices, *cur;
+ GList *result;
+
+ result = NULL;
+
+ devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
+ if (devices == NULL)
+ return result;
+
+ devices = ks_video_device_list_sort_cameras_first (devices);
+
+ for (cur = devices; cur != NULL; cur = cur->next) {
+ GstDevice *source;
+ KsDeviceEntry *entry = cur->data;
+
+ source = new_video_source (entry);
+ if (source)
+ result = g_list_prepend (result, gst_object_ref_sink (source));
+
+ ks_device_entry_free (entry);
+ }
+
+ result = g_list_reverse (result);
+
+ g_list_free (devices);
+
+ return result;
+}
+
+static const gchar *
+get_dev_type (DEV_BROADCAST_HDR * dev_msg_header)
+{
+ switch (dev_msg_header->dbch_devicetype) {
+ case DBT_DEVTYP_DEVICEINTERFACE:
+ return "Device interface class";
+ case DBT_DEVTYP_HANDLE:
+ return "Filesystem handle";
+ case DBT_DEVTYP_OEM:
+ return "OEM or IHV device type";
+ case DBT_DEVTYP_PORT:
+ return "Port device";
+ case DBT_DEVTYP_VOLUME:
+ return "Logical volume";
+ default:
+ return "Unknown device type";
+ }
+}
+
+#define KS_MSG_WINDOW_CLASS "gst_winks_device_msg_window"
+#define WM_QUITTHREAD (WM_USER + 0)
+
+static void unreg_msg_window_class (ATOM class_id, const char *class_name,
+ HINSTANCE inst);
+
+static HDEVNOTIFY
+register_device_interface (GstKsDeviceProvider * self,
+ GUID interface_class_guid, HWND window_handle)
+{
+ DEV_BROADCAST_DEVICEINTERFACE notification_filter;
+ HDEVNOTIFY notification_handle;
+ DWORD error;
+
+ memset (&notification_filter, 0, sizeof (notification_filter));
+ notification_filter.dbcc_size = sizeof (notification_filter);
+ notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ notification_filter.dbcc_classguid = interface_class_guid;
+
+ notification_handle = RegisterDeviceNotificationW (window_handle,
+ &notification_filter,
+ DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
+ error = GetLastError ();
+
+ if (notification_handle == NULL)
+ GST_ERROR_OBJECT (self,
+ "Could not register for a device notification: %lu", error);
+
+ return notification_handle;
+}
+
+static INT_PTR WINAPI
+msg_window_message_proc (HWND window_handle, UINT message,
+ WPARAM wparam, LPARAM lparam)
+{
+ LRESULT result;
+ LONG_PTR user_data;
+ GstKsDeviceProvider *self;
+ PDEV_BROADCAST_DEVICEINTERFACE bcdi;
+ DEV_BROADCAST_HDR *dev_msg_header;
+ struct _DEV_BROADCAST_USERDEFINED *user_dev_msg_header;
+ CREATESTRUCT *create_data;
+ DWORD error;
+ HINSTANCE inst;
+ GstKsDevice *dev;
+ GstDevice *source;
+ GList *item;
+ GstDeviceProvider *provider;
+ GList *devices;
+ gchar *guid_str;
+
+ result = TRUE;
+
+ switch (message) {
+ case WM_CREATE:
+ create_data = (CREATESTRUCT *) lparam;
+
+ if (create_data->lpCreateParams == NULL) {
+ /* DO SOMETHING!! */
+ }
+
+ self = GST_KS_DEVICE_PROVIDER (create_data->lpCreateParams);
+
+ SetLastError (0);
+ SetWindowLongPtr (window_handle, GWLP_USERDATA, (LONG_PTR) self);
+ error = GetLastError ();
+ if (error != NO_ERROR) {
+ GST_ERROR_OBJECT (self,
+ "Could not attach user data to the message window: %lu", error);
+ DestroyWindow (window_handle);
+ inst = (HINSTANCE) GetModuleHandle (NULL);
+ GST_OBJECT_LOCK (self);
+ unreg_msg_window_class (self->message_window_class, KS_MSG_WINDOW_CLASS,
+ inst);
+ self->message_window_class = 0;
+ GST_OBJECT_UNLOCK (self);
+ }
+ result = FALSE;
+ break;
+ case WM_DEVICECHANGE:
+ GST_DEBUG ("WM_DEVICECHANGE for %x %x", (unsigned int) wparam,
+ (unsigned int) lparam);
+
+ user_data = GetWindowLongPtr (window_handle, GWLP_USERDATA);
+ if (user_data == 0)
+ break;
+
+ self = GST_KS_DEVICE_PROVIDER (user_data);
+ provider = GST_DEVICE_PROVIDER (self);
+
+ dev_msg_header = (DEV_BROADCAST_HDR *) lparam;
+
+ switch (wparam) {
+ case DBT_CONFIGCHANGECANCELED:
+ GST_DEBUG_OBJECT (self, "DBT_CONFIGCHANGECANCELED for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_CONFIGCHANGED:
+ GST_DEBUG_OBJECT (self, "DBT_CONFIGCHANGED for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_CUSTOMEVENT:
+ GST_DEBUG_OBJECT (self, "DBT_CUSTOMEVENT for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_DEVICEARRIVAL:
+ GST_DEBUG_OBJECT (self, "DBT_DEVICEARRIVAL for %s",
+ get_dev_type (dev_msg_header));
+
+ if (dev_msg_header->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ break;
+
+ bcdi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
+ guid_str = ks_guid_to_string (&bcdi->dbcc_classguid);
+ GST_INFO_OBJECT (self, "New device, class interface GUID %s, path %s",
+ guid_str, bcdi->dbcc_name);
+ g_free (guid_str);
+ break;
+ case DBT_DEVICEQUERYREMOVE:
+ GST_DEBUG_OBJECT (self, "DBT_DEVICEQUERYREMOVE for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_DEVICEQUERYREMOVEFAILED:
+ GST_DEBUG_OBJECT (self, "DBT_DEVICEQUERYREMOVEFAILED for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_DEVICEREMOVECOMPLETE:
+ GST_DEBUG_OBJECT (self, "DBT_DEVICEREMOVECOMPLETE for %s",
+ get_dev_type (dev_msg_header));
+
+ if (dev_msg_header->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ break;
+
+ bcdi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
+
+ guid_str = ks_guid_to_string (&bcdi->dbcc_classguid);
+ GST_INFO_OBJECT (self,
+ "Removed device, class interface GUID %s, path %s", guid_str,
+ bcdi->dbcc_name);
+ g_free (guid_str);
+ break;
+ case DBT_DEVICEREMOVEPENDING:
+ GST_DEBUG_OBJECT (self, "DBT_DEVICEREMOVEPENDING for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_DEVICETYPESPECIFIC:
+ GST_DEBUG_OBJECT (self, "DBT_DEVICETYPESPECIFIC for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_DEVNODES_CHANGED:
+ GST_DEBUG_OBJECT (self, "DBT_DEVNODES_CHANGED for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_QUERYCHANGECONFIG:
+ GST_DEBUG_OBJECT (self, "DBT_QUERYCHANGECONFIG for %s",
+ get_dev_type (dev_msg_header));
+ break;
+ case DBT_USERDEFINED:
+ user_dev_msg_header = (struct _DEV_BROADCAST_USERDEFINED *) lparam;
+ dev_msg_header =
+ (DEV_BROADCAST_HDR *) & user_dev_msg_header->dbud_dbh;
+ GST_DEBUG_OBJECT (self, "DBT_USERDEFINED for %s: %s",
+ get_dev_type (dev_msg_header), user_dev_msg_header->dbud_szName);
+ break;
+ default:
+ break;
+ }
+
+ switch (wparam) {
+ case DBT_DEVICEARRIVAL:
+ if (dev_msg_header->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ break;
+
+ bcdi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
+
+ if (!IsEqualGUID (&bcdi->dbcc_classguid, &KSCATEGORY_CAPTURE) &&
+ !IsEqualGUID (&bcdi->dbcc_classguid, &KSCATEGORY_RENDER))
+ break;
+
+ devices =
+ ks_enumerate_devices (&bcdi->dbcc_classguid,
+ &bcdi->dbcc_classguid);
+ if (devices == NULL)
+ break;
+
+ source = NULL;
+ for (item = devices; item != NULL; item = item->next) {
+ KsDeviceEntry *entry = item->data;
+ GST_DEBUG_OBJECT (self, "Listed device %s = %s", entry->name,
+ entry->path);
+
+ if ((source == NULL) &&
+ (strcasecmp (entry->path, bcdi->dbcc_name) == 0))
+ source = new_video_source (entry);
+
+ ks_device_entry_free (entry);
+ }
+
+ if (source)
+ gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), source);
+
+ g_list_free (devices);
+ break;
+ case DBT_DEVICEREMOVECOMPLETE:
+ if (dev_msg_header->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ break;
+
+ bcdi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
+ dev = NULL;
+
+ GST_OBJECT_LOCK (self);
+ for (item = provider->devices; item; item = item->next) {
+ dev = item->data;
+
+ if (strcasecmp (dev->path, bcdi->dbcc_name) == 0) {
+ guid_str = gst_device_get_display_name (GST_DEVICE (dev));
+ GST_INFO_OBJECT (self, "Device matches to %s", guid_str);
+ g_free (guid_str);
+ gst_object_ref (dev);
+ break;
+ }
+ dev = NULL;
+ }
+ GST_OBJECT_UNLOCK (self);
+
+ if (dev) {
+ gst_device_provider_device_remove (GST_DEVICE_PROVIDER (self),
+ GST_DEVICE (dev));
+ gst_object_unref (dev);
+ }
+ break;
+ default:
+ break;
+ }
+ result = FALSE;
+ break;
+ case WM_DESTROY:
+ PostQuitMessage (0);
+ result = FALSE;
+ break;
+ case WM_QUITTHREAD:
+ DestroyWindow (window_handle);
+ result = FALSE;
+ break;
+ default:
+ result = DefWindowProc (window_handle, message, wparam, lparam);
+ break;
+ }
+
+ return result;
+}
+
+static ATOM
+reg_msg_window_class (const char *class_name, HINSTANCE inst)
+{
+ WNDCLASSEXA classex;
+
+ memset (&classex, 0, sizeof (classex));
+ classex.cbSize = sizeof (classex);
+ classex.hInstance = inst;
+ classex.lpfnWndProc = (WNDPROC) msg_window_message_proc;
+ classex.lpszClassName = class_name;
+
+ return RegisterClassExA (&classex);
+}
+
+static void
+unreg_msg_window_class (ATOM class_id, const char *class_name, HINSTANCE inst)
+{
+ if (class_id != 0)
+ UnregisterClassA ((LPCSTR) MAKELPARAM (class_id, 0), inst);
+ else
+ UnregisterClassA (class_name, inst);
+}
+
+static gpointer
+ks_provider_msg_window_thread (gpointer dat)
+{
+ GstKsDeviceProvider *self;
+ MSG msg;
+ ATOM wnd_class;
+ BOOL message_status;
+ HINSTANCE inst;
+ HANDLE msg_window = NULL;
+ DWORD error;
+ HDEVNOTIFY devnotify = NULL;
+
+ g_return_val_if_fail (dat != NULL, NULL);
+
+ self = GST_KS_DEVICE_PROVIDER (dat);
+
+ GST_DEBUG_OBJECT (self, "Entering message window thread: %p",
+ g_thread_self ());
+
+ GST_OBJECT_LOCK (self);
+ wnd_class = self->message_window_class;
+ GST_OBJECT_UNLOCK (self);
+
+ inst = (HINSTANCE) GetModuleHandle (NULL);
+
+ msg_window = CreateWindowExA (0,
+ wnd_class != 0 ? (LPCSTR) MAKELPARAM (wnd_class, 0) : KS_MSG_WINDOW_CLASS,
+ "", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, inst, self);
+ error = GetLastError ();
+
+ if (msg_window == NULL) {
+ GST_ERROR_OBJECT (self, "Could not create a message window: %lu", error);
+ GST_OBJECT_LOCK (self);
+ unreg_msg_window_class (wnd_class, KS_MSG_WINDOW_CLASS, inst);
+ self->message_window_class = 0;
+ SetEvent (self->wakeup_event);
+ GST_OBJECT_UNLOCK (self);
+ return NULL;
+ }
+
+ GST_OBJECT_LOCK (self);
+ self->message_window = msg_window;
+
+ devnotify =
+ register_device_interface (self, GUID_DEVCLASS_WCEUSBS, msg_window);
+ if (devnotify == NULL) {
+ DestroyWindow (msg_window);
+ unreg_msg_window_class (wnd_class, KS_MSG_WINDOW_CLASS, inst);
+ self->message_window_class = 0;
+ self->message_window = NULL;
+ SetEvent (self->wakeup_event);
+ GST_OBJECT_UNLOCK (self);
+ return NULL;
+ }
+
+ self->device_notify_handle = devnotify;
+ SetEvent (self->wakeup_event);
+ GST_OBJECT_UNLOCK (self);
+
+ while ((message_status = GetMessage (&msg, NULL, 0, 0)) != 0) {
+ if (message_status < 0 || msg.message == WM_QUIT)
+ break;
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+
+ GST_DEBUG_OBJECT (self, "Exiting internal window thread: %p",
+ g_thread_self ());
+
+ return NULL;
+}
+
+static gboolean
+gst_ks_device_provider_start (GstDeviceProvider * provider)
+{
+ ATOM wnd_class = 0;
+ HINSTANCE inst;
+ HANDLE wakeup_event;
+ HWND message_window;
+ DWORD error;
+ GList *devs;
+ GList *dev;
+ GstKsDeviceProvider *self = GST_KS_DEVICE_PROVIDER (provider);
+
+ GST_OBJECT_LOCK (self);
+ g_assert (self->message_window == NULL);
+ GST_OBJECT_UNLOCK (self);
+
+ /* We get notifications on *change*, so before we get to that,
+ * we need to obtain a complete list of devices, which we will
+ * watch for changes.
+ */
+ devs = gst_ks_device_provider_probe (provider);
+ for (dev = devs; dev; dev = dev->next) {
+ if (dev->data)
+ gst_device_provider_device_add (provider, (GstDevice *) dev->data);
+ }
+ g_list_free (devs);
+
+ inst = (HINSTANCE) GetModuleHandle (NULL);
+
+ wakeup_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ error = GetLastError ();
+ if (wakeup_event == NULL) {
+ GST_OBJECT_LOCK (self);
+ GST_ERROR_OBJECT (self, "Could not create a wakeup event: %lu", error);
+ GST_OBJECT_UNLOCK (self);
+ return FALSE;
+ }
+
+ wnd_class = reg_msg_window_class (KS_MSG_WINDOW_CLASS, inst);
+ error = GetLastError ();
+
+ if ((wnd_class == 0) && (error != ERROR_CLASS_ALREADY_EXISTS)) {
+ GST_ERROR_OBJECT (self,
+ "Could not register message window class: %lu", error);
+ CloseHandle (wakeup_event);
+ return FALSE;
+ }
+
+ GST_OBJECT_LOCK (self);
+ self->message_window_class = wnd_class;
+ self->wakeup_event = wakeup_event;
+
+ self->message_thread =
+ g_thread_new ("ks-device-provider-message-window-thread",
+ (GThreadFunc) ks_provider_msg_window_thread, self);
+ if (self->message_thread == NULL) {
+ GST_ERROR_OBJECT (self, "Could not create message window thread");
+ unreg_msg_window_class (wnd_class, KS_MSG_WINDOW_CLASS, inst);
+ self->message_window_class = 0;
+ CloseHandle (self->wakeup_event);
+ GST_OBJECT_UNLOCK (self);
+ return FALSE;
+ }
+ GST_OBJECT_UNLOCK (self);
+
+ if (WaitForSingleObject (wakeup_event, INFINITE) != WAIT_OBJECT_0) {
+ GST_ERROR_OBJECT (self,
+ "Failed to wait for the message thread to initialize");
+ }
+
+ GST_OBJECT_LOCK (self);
+ CloseHandle (self->wakeup_event);
+ self->wakeup_event = NULL;
+ message_window = self->message_window;
+ GST_OBJECT_UNLOCK (self);
+
+ if (message_window == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gst_ks_device_provider_stop (GstDeviceProvider * provider)
+{
+ HINSTANCE inst;
+ GThread *message_thread;
+ GstKsDeviceProvider *self = GST_KS_DEVICE_PROVIDER (provider);
+
+ GST_OBJECT_LOCK (self);
+
+ g_assert (self->message_window != NULL);
+
+ UnregisterDeviceNotification (self->device_notify_handle);
+ self->device_notify_handle = NULL;
+ PostMessage (self->message_window, WM_QUITTHREAD, 0, 0);
+ message_thread = self->message_thread;
+ GST_OBJECT_UNLOCK (self);
+
+ g_thread_join (message_thread);
+
+ GST_OBJECT_LOCK (self);
+ self->message_window = NULL;
+ self->message_thread = NULL;
+
+ inst = (HINSTANCE) GetModuleHandle (NULL);
+
+ unreg_msg_window_class (self->message_window_class, KS_MSG_WINDOW_CLASS,
+ inst);
+
+ self->message_window_class = 0;
+ GST_OBJECT_UNLOCK (self);
+}
+
+enum
+{
+ PROP_PATH = 1
+};
+
+G_DEFINE_TYPE (GstKsDevice, gst_ks_device, GST_TYPE_DEVICE);
+
+static void gst_ks_device_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_ks_device_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_ks_device_finalize (GObject * object);
+static GstElement *gst_ks_device_create_element (GstDevice * device,
+ const gchar * name);
+static gboolean gst_ks_device_reconfigure_element (GstDevice * device,
+ GstElement * element);
+
+static void
+gst_ks_device_class_init (GstKsDeviceClass * klass)
+{
+ GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ dev_class->create_element = gst_ks_device_create_element;
+ dev_class->reconfigure_element = gst_ks_device_reconfigure_element;
+
+ object_class->get_property = gst_ks_device_get_property;
+ object_class->set_property = gst_ks_device_set_property;
+ object_class->finalize = gst_ks_device_finalize;
+
+ g_object_class_install_property (object_class, PROP_PATH,
+ g_param_spec_string ("path", "System device path",
+ "The system path to the device", "",
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gst_ks_device_init (GstKsDevice * device)
+{
+}
+
+static void
+gst_ks_device_finalize (GObject * object)
+{
+ GstKsDevice *device = GST_KS_DEVICE (object);
+
+ g_free (device->path);
+
+ G_OBJECT_CLASS (gst_ks_device_parent_class)->finalize (object);
+}
+
+static GstElement *
+gst_ks_device_create_element (GstDevice * device, const gchar * name)
+{
+ GstKsDevice *ks_dev = GST_KS_DEVICE (device);
+ GstElement *elem;
+
+ elem = gst_element_factory_make (ks_dev->element, name);
+ g_object_set (elem, "device-path", ks_dev->path, NULL);
+
+ return elem;
+}
+
+static gboolean
+gst_ks_device_reconfigure_element (GstDevice * device, GstElement * element)
+{
+ GstKsDevice *ks_dev = GST_KS_DEVICE (device);
+
+ if (!strcmp (ks_dev->element, "ksvideosrc")) {
+ if (!GST_IS_KS_VIDEO_SRC (element))
+ return FALSE;
+/*
+ } else if (!strcmp (ks_dev->element, "ksaudiosrc")) {
+ if (!GST_IS_KS_AUDIO_SRC (element))
+ return FALSE;
+ } else if (!strcmp (ks_dev->element, "ksaudiosink")) {
+ if (!GST_IS_KS_AUDIO_SINK (element))
+ return FALSE;
+*/
+ } else {
+ g_assert_not_reached ();
+ }
+
+ g_object_set (element, "path", ks_dev->path, NULL);
+
+ return TRUE;
+}
+
+static GstDevice *
+gst_ks_device_new (guint device_index, const gchar * device_name,
+ GstCaps * caps, const gchar * device_path, GstKsDeviceType type)
+{
+ GstKsDevice *gstdev;
+ const gchar *element = NULL;
+ const gchar *klass = NULL;
+
+ g_return_val_if_fail (device_name, NULL);
+ g_return_val_if_fail (device_path, NULL);
+ g_return_val_if_fail (caps, NULL);
+
+
+ switch (type) {
+ case GST_KS_DEVICE_TYPE_VIDEO_SOURCE:
+ element = "ksvideosrc";
+ klass = "Video/Source";
+ break;
+ case GST_KS_DEVICE_TYPE_AUDIO_SOURCE:
+ element = "ksaudiosrc";
+ klass = "Audio/Source";
+ break;
+ case GST_KS_DEVICE_TYPE_AUDIO_SINK:
+ element = "ksaudiosink";
+ klass = "Audio/Sink";
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+
+ gstdev = g_object_new (GST_TYPE_KS_DEVICE,
+ "display-name", device_name, "caps", caps, "device-class", klass,
+ "path", device_path, NULL);
+
+ gstdev->type = type;
+ gstdev->device_index = device_index;
+ gstdev->path = g_strdup (device_path);
+ gstdev->element = element;
+
+ return GST_DEVICE (gstdev);
+}
+
+
+static void
+gst_ks_device_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstKsDevice *device;
+
+ device = GST_KS_DEVICE_CAST (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ g_value_set_string (value, device->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_ks_device_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstKsDevice *device;
+
+ device = GST_KS_DEVICE_CAST (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ device->path = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
diff --git a/sys/winks/ksdeviceprovider.h b/sys/winks/ksdeviceprovider.h
new file mode 100644
index 000000000..24d14a0d9
--- /dev/null
+++ b/sys/winks/ksdeviceprovider.h
@@ -0,0 +1,101 @@
+/* GStreamer
+ * Copyright (C) 2015 Руслан Ижбулатов <lrn1986@gmail.com>
+ *
+ * ksdeviceprovider.h: Kernel Streaming device probing and monitoring
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_KS_DEVICE_PROVIDER_H__
+#define __GST_KS_DEVICE_PROVIDER_H__
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <windows.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstKsDeviceProvider GstKsDeviceProvider;
+typedef struct _GstKsDeviceProviderPrivate GstKsDeviceProviderPrivate;
+typedef struct _GstKsDeviceProviderClass GstKsDeviceProviderClass;
+
+#define GST_TYPE_KS_DEVICE_PROVIDER (gst_ks_device_provider_get_type())
+#define GST_IS_KS_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_KS_DEVICE_PROVIDER))
+#define GST_IS_KS_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_KS_DEVICE_PROVIDER))
+#define GST_KS_DEVICE_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_KS_DEVICE_PROVIDER, GstKsDeviceProviderClass))
+#define GST_KS_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_KS_DEVICE_PROVIDER, GstKsDeviceProvider))
+#define GST_KS_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstKsDeviceProviderClass))
+#define GST_KS_DEVICE_PROVIDER_CAST(obj) ((GstKsDeviceProvider *)(obj))
+
+struct _GstKsDeviceProvider {
+ GstDeviceProvider parent;
+
+ HANDLE message_window;
+ ATOM message_window_class;
+ HDEVNOTIFY device_notify_handle;
+ HANDLE wakeup_event;
+ GThread *message_thread;
+};
+
+typedef enum {
+ GST_KS_DEVICE_TYPE_INVALID = 0,
+ GST_KS_DEVICE_TYPE_VIDEO_SOURCE,
+ GST_KS_DEVICE_TYPE_VIDEO_SINK,
+ GST_KS_DEVICE_TYPE_AUDIO_SOURCE,
+ GST_KS_DEVICE_TYPE_AUDIO_SINK
+} GstKsDeviceType;
+
+struct _GstKsDeviceProviderClass {
+ GstDeviceProviderClass parent_class;
+};
+
+GType gst_ks_device_provider_get_type (void);
+
+
+typedef struct _GstKsDevice GstKsDevice;
+typedef struct _GstKsDevicePrivate GstKsDevicePrivate;
+typedef struct _GstKsDeviceClass GstKsDeviceClass;
+
+#define GST_TYPE_KS_DEVICE (gst_ks_device_get_type())
+#define GST_IS_KS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_KS_DEVICE))
+#define GST_IS_KS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_KS_DEVICE))
+#define GST_KS_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_KS_DEVICE, GstKsDeviceClass))
+#define GST_KS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_KS_DEVICE, GstKsDevice))
+#define GST_KS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstKsDeviceClass))
+#define GST_KS_DEVICE_CAST(obj) ((GstKsDevice *)(obj))
+
+struct _GstKsDevice {
+ GstDevice parent;
+
+ GstKsDeviceType type;
+ guint device_index;
+ gchar *path;
+ const gchar *element;
+};
+
+struct _GstKsDeviceClass {
+ GstDeviceClass parent_class;
+};
+
+GType gst_ks_device_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_KS_DEVICE_PROVIDER_H__ */
diff --git a/sys/winks/kshelpers.c b/sys/winks/kshelpers.c
index 3f7b2d25f..bbb9b066f 100644
--- a/sys/winks/kshelpers.c
+++ b/sys/winks/kshelpers.c
@@ -40,13 +40,13 @@ ks_is_valid_handle (HANDLE h)
}
GList *
-ks_enumerate_devices (const GUID * category)
+ks_enumerate_devices (const GUID * devtype, const GUID * direction_category)
{
GList *result = NULL;
HDEVINFO devinfo;
gint i;
- devinfo = SetupDiGetClassDevsW (category, NULL, NULL,
+ devinfo = SetupDiGetClassDevsW (devtype, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (!ks_is_valid_handle (devinfo))
return NULL; /* no devices */
@@ -54,7 +54,7 @@ ks_enumerate_devices (const GUID * category)
for (i = 0;; i++) {
BOOL success;
SP_DEVICE_INTERFACE_DATA if_data = { 0, };
- SP_DEVICE_INTERFACE_DATA if_alias_data = { 0,};
+ SP_DEVICE_INTERFACE_DATA if_alias_data = { 0, };
SP_DEVICE_INTERFACE_DETAIL_DATA_W *if_detail_data;
DWORD if_detail_data_size;
SP_DEVINFO_DATA devinfo_data = { 0, };
@@ -62,14 +62,13 @@ ks_enumerate_devices (const GUID * category)
if_data.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
- success = SetupDiEnumDeviceInterfaces (devinfo, NULL, category, i,
- &if_data);
+ success = SetupDiEnumDeviceInterfaces (devinfo, NULL, devtype, i, &if_data);
if (!success) /* all devices enumerated? */
break;
- /* Enumerate only capture devices */
if_alias_data.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
- success = SetupDiGetDeviceInterfaceAlias (devinfo, &if_data, &KSCATEGORY_CAPTURE,
+ success =
+ SetupDiGetDeviceInterfaceAlias (devinfo, &if_data, direction_category,
&if_alias_data);
if (!success)
continue;
diff --git a/sys/winks/kshelpers.h b/sys/winks/kshelpers.h
index 6e5871c15..5181bfcd1 100644
--- a/sys/winks/kshelpers.h
+++ b/sys/winks/kshelpers.h
@@ -37,7 +37,7 @@ struct _KsDeviceEntry
gboolean ks_is_valid_handle (HANDLE h);
-GList * ks_enumerate_devices (const GUID * category);
+GList * ks_enumerate_devices (const GUID * devtype, const GUID * direction_category);
void ks_device_entry_free (KsDeviceEntry * entry);
void ks_device_list_free (GList * devices);