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
This commit is contained in:
Руслан Ижбулатов 2015-04-28 21:43:56 +00:00 committed by Olivier Crête
parent ff12434b9f
commit a31855d618
7 changed files with 925 additions and 17 deletions

View file

@ -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) \

View file

@ -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;

View file

@ -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,

View file

@ -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;
}
}

View file

@ -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__ */

View file

@ -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;

View file

@ -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);