summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);