| /* 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; |
| } |
| } |