mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-22 08:17:01 +00:00
a31855d618
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
795 lines
22 KiB
C
795 lines
22 KiB
C
/* 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 (¬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;
|
|
}
|
|
}
|