diff --git a/sys/winks/Makefile.am b/sys/winks/Makefile.am index 4233b64932..10626b1046 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 e67e02f17a..8d46905b7e 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 3dab4aced8..859699af9a 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 0000000000..54429ebe6b --- /dev/null +++ b/sys/winks/ksdeviceprovider.c @@ -0,0 +1,795 @@ +/* GStreamer + * Copyright (C) 2015 Руслан Ижбулатов + * + * 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 + +#include /* for DBT_* consts and [_]DEV_* structs */ +#include /* for GUID_DEVCLASS_WCEUSBS */ +#include /* for DIGCF_ALLCLASSES */ + +#include + +#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", + "Руслан Ижбулатов "); +} + +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 (¬ification_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, + ¬ification_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 0000000000..24d14a0d90 --- /dev/null +++ b/sys/winks/ksdeviceprovider.h @@ -0,0 +1,101 @@ +/* GStreamer + * Copyright (C) 2015 Руслан Ижбулатов + * + * 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 +#include + +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 3f7b2d25fb..bbb9b066f1 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 6e5871c15b..5181bfcd11 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);