gstreamer/sys/winks/ksdeviceprovider.c
Nirbheek Chauhan 28f0df681c winks: Port to MSVC and pretend to be Windows XP
strcasecmp is not defined on MSVC, so just use the glib wrapper. Also pretend to
be Windows XP explicitly since the API we use was deprecated and removed
(ifdef-ed) from the SDK after this version of Windows. This will be especially
relevant once we stop supporting Windows XP soon:

https://bugzilla.gnome.org/show_bug.cgi?id=756866
2016-06-21 11:32:29 +01:00

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 (&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) &&
(g_ascii_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 (g_ascii_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;
}
}