1 /* GStreamer
2 * Copyright (C) 2015 Руслан Ижбулатов <lrn1986@gmail.com>
3 *
4 * ksdeviceprovider.c: Kernel Streaming device probing and monitoring
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "gstksvideosrc.h"
27 #include "ksdeviceprovider.h"
28
29 #include <string.h>
30
31 #include <dbt.h> /* for DBT_* consts and [_]DEV_* structs */
32 #include <devguid.h> /* for GUID_DEVCLASS_WCEUSBS */
33 #include <setupapi.h> /* for DIGCF_ALLCLASSES */
34
35 #include <gst/gst.h>
36
37 #include "kshelpers.h"
38 #include "ksvideohelpers.h"
39
40
41 GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
42 #define GST_CAT_DEFAULT gst_ks_debug
43
44
45 static GstDevice *gst_ks_device_new (guint id,
46 const gchar * device_name, GstCaps * caps, const gchar * device_path,
47 GstKsDeviceType type);
48
49 G_DEFINE_TYPE (GstKsDeviceProvider, gst_ks_device_provider,
50 GST_TYPE_DEVICE_PROVIDER);
51
52 static GList *gst_ks_device_provider_probe (GstDeviceProvider * provider);
53 static gboolean gst_ks_device_provider_start (GstDeviceProvider * provider);
54 static void gst_ks_device_provider_stop (GstDeviceProvider * provider);
55
56 static void
gst_ks_device_provider_class_init(GstKsDeviceProviderClass * klass)57 gst_ks_device_provider_class_init (GstKsDeviceProviderClass * klass)
58 {
59 GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
60
61 dm_class->probe = gst_ks_device_provider_probe;
62 dm_class->start = gst_ks_device_provider_start;
63 dm_class->stop = gst_ks_device_provider_stop;
64
65 gst_device_provider_class_set_static_metadata (dm_class,
66 "KernelStreaming Device Provider", "Sink/Source/Audio/Video",
67 "List and provide KernelStreaming source and sink devices",
68 "Руслан Ижбулатов <lrn1986@gmail.com>");
69 }
70
71 static void
gst_ks_device_provider_init(GstKsDeviceProvider * self)72 gst_ks_device_provider_init (GstKsDeviceProvider * self)
73 {
74 }
75
76 static GstDevice *
new_video_source(const KsDeviceEntry * info)77 new_video_source (const KsDeviceEntry * info)
78 {
79 GstCaps *caps;
80 HANDLE filter_handle;
81 GList *media_types;
82 GList *cur;
83
84 g_assert (info->path != NULL);
85
86 caps = gst_caps_new_empty ();
87
88 filter_handle = CreateFile (info->path,
89 GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
90 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
91 if (!ks_is_valid_handle (filter_handle))
92 goto error;
93
94 media_types = ks_video_probe_filter_for_caps (filter_handle);
95
96 for (cur = media_types; cur != NULL; cur = cur->next) {
97 KsVideoMediaType *media_type = cur->data;
98
99 gst_caps_append (caps, gst_caps_copy (media_type->translated_caps));
100
101 ks_video_media_type_free (media_type);
102 }
103
104 CloseHandle (filter_handle);
105 g_list_free (media_types);
106
107 return gst_ks_device_new (info->index, info->name,
108 caps, info->path, GST_KS_DEVICE_TYPE_VIDEO_SOURCE);
109 error:
110 gst_caps_unref (caps);
111 return NULL;
112 }
113
114 static GList *
gst_ks_device_provider_probe(GstDeviceProvider * provider)115 gst_ks_device_provider_probe (GstDeviceProvider * provider)
116 {
117 /*GstKsDeviceProvider *self = GST_KS_DEVICE_PROVIDER (provider); */
118 GList *devices, *cur;
119 GList *result;
120
121 result = NULL;
122
123 devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
124 if (devices == NULL)
125 return result;
126
127 devices = ks_video_device_list_sort_cameras_first (devices);
128
129 for (cur = devices; cur != NULL; cur = cur->next) {
130 GstDevice *source;
131 KsDeviceEntry *entry = cur->data;
132
133 source = new_video_source (entry);
134 if (source)
135 result = g_list_prepend (result, gst_object_ref_sink (source));
136
137 ks_device_entry_free (entry);
138 }
139
140 result = g_list_reverse (result);
141
142 g_list_free (devices);
143
144 return result;
145 }
146
147 static const gchar *
get_dev_type(DEV_BROADCAST_HDR * dev_msg_header)148 get_dev_type (DEV_BROADCAST_HDR * dev_msg_header)
149 {
150 switch (dev_msg_header->dbch_devicetype) {
151 case DBT_DEVTYP_DEVICEINTERFACE:
152 return "Device interface class";
153 case DBT_DEVTYP_HANDLE:
154 return "Filesystem handle";
155 case DBT_DEVTYP_OEM:
156 return "OEM or IHV device type";
157 case DBT_DEVTYP_PORT:
158 return "Port device";
159 case DBT_DEVTYP_VOLUME:
160 return "Logical volume";
161 default:
162 return "Unknown device type";
163 }
164 }
165
166 #define KS_MSG_WINDOW_CLASS "gst_winks_device_msg_window"
167 #define WM_QUITTHREAD (WM_USER + 0)
168
169 static void unreg_msg_window_class (ATOM class_id, const char *class_name,
170 HINSTANCE inst);
171
172 static HDEVNOTIFY
register_device_interface(GstKsDeviceProvider * self,GUID interface_class_guid,HWND window_handle)173 register_device_interface (GstKsDeviceProvider * self,
174 GUID interface_class_guid, HWND window_handle)
175 {
176 DEV_BROADCAST_DEVICEINTERFACE notification_filter;
177 HDEVNOTIFY notification_handle;
178 DWORD error;
179
180 memset (¬ification_filter, 0, sizeof (notification_filter));
181 notification_filter.dbcc_size = sizeof (notification_filter);
182 notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
183 notification_filter.dbcc_classguid = interface_class_guid;
184
185 notification_handle = RegisterDeviceNotificationW (window_handle,
186 ¬ification_filter,
187 DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
188 error = GetLastError ();
189
190 if (notification_handle == NULL)
191 GST_ERROR_OBJECT (self,
192 "Could not register for a device notification: %lu", error);
193
194 return notification_handle;
195 }
196
197 static INT_PTR WINAPI
msg_window_message_proc(HWND window_handle,UINT message,WPARAM wparam,LPARAM lparam)198 msg_window_message_proc (HWND window_handle, UINT message,
199 WPARAM wparam, LPARAM lparam)
200 {
201 LRESULT result;
202 LONG_PTR user_data;
203 GstKsDeviceProvider *self;
204 PDEV_BROADCAST_DEVICEINTERFACE bcdi;
205 DEV_BROADCAST_HDR *dev_msg_header;
206 struct _DEV_BROADCAST_USERDEFINED *user_dev_msg_header;
207 CREATESTRUCT *create_data;
208 DWORD error;
209 HINSTANCE inst;
210 GstKsDevice *dev;
211 GstDevice *source;
212 GList *item;
213 GstDeviceProvider *provider;
214 GList *devices;
215 gchar *guid_str;
216
217 result = TRUE;
218
219 switch (message) {
220 case WM_CREATE:
221 create_data = (CREATESTRUCT *) lparam;
222
223 if (create_data->lpCreateParams == NULL) {
224 /* DO SOMETHING!! */
225 }
226
227 self = GST_KS_DEVICE_PROVIDER (create_data->lpCreateParams);
228
229 SetLastError (0);
230 SetWindowLongPtr (window_handle, GWLP_USERDATA, (LONG_PTR) self);
231 error = GetLastError ();
232 if (error != NO_ERROR) {
233 GST_ERROR_OBJECT (self,
234 "Could not attach user data to the message window: %lu", error);
235 DestroyWindow (window_handle);
236 inst = (HINSTANCE) GetModuleHandle (NULL);
237 GST_OBJECT_LOCK (self);
238 unreg_msg_window_class (self->message_window_class, KS_MSG_WINDOW_CLASS,
239 inst);
240 self->message_window_class = 0;
241 GST_OBJECT_UNLOCK (self);
242 }
243 result = FALSE;
244 break;
245 case WM_DEVICECHANGE:
246 GST_DEBUG ("WM_DEVICECHANGE for %x %x", (unsigned int) wparam,
247 (unsigned int) lparam);
248
249 user_data = GetWindowLongPtr (window_handle, GWLP_USERDATA);
250 if (user_data == 0)
251 break;
252
253 self = GST_KS_DEVICE_PROVIDER (user_data);
254 provider = GST_DEVICE_PROVIDER (self);
255
256 dev_msg_header = (DEV_BROADCAST_HDR *) lparam;
257
258 switch (wparam) {
259 case DBT_CONFIGCHANGECANCELED:
260 GST_DEBUG_OBJECT (self, "DBT_CONFIGCHANGECANCELED for %s",
261 get_dev_type (dev_msg_header));
262 break;
263 case DBT_CONFIGCHANGED:
264 GST_DEBUG_OBJECT (self, "DBT_CONFIGCHANGED for %s",
265 get_dev_type (dev_msg_header));
266 break;
267 case DBT_CUSTOMEVENT:
268 GST_DEBUG_OBJECT (self, "DBT_CUSTOMEVENT for %s",
269 get_dev_type (dev_msg_header));
270 break;
271 case DBT_DEVICEARRIVAL:
272 GST_DEBUG_OBJECT (self, "DBT_DEVICEARRIVAL for %s",
273 get_dev_type (dev_msg_header));
274
275 if (dev_msg_header->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
276 break;
277
278 bcdi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
279 guid_str = ks_guid_to_string (&bcdi->dbcc_classguid);
280 GST_INFO_OBJECT (self, "New device, class interface GUID %s, path %s",
281 guid_str, bcdi->dbcc_name);
282 g_free (guid_str);
283 break;
284 case DBT_DEVICEQUERYREMOVE:
285 GST_DEBUG_OBJECT (self, "DBT_DEVICEQUERYREMOVE for %s",
286 get_dev_type (dev_msg_header));
287 break;
288 case DBT_DEVICEQUERYREMOVEFAILED:
289 GST_DEBUG_OBJECT (self, "DBT_DEVICEQUERYREMOVEFAILED for %s",
290 get_dev_type (dev_msg_header));
291 break;
292 case DBT_DEVICEREMOVECOMPLETE:
293 GST_DEBUG_OBJECT (self, "DBT_DEVICEREMOVECOMPLETE for %s",
294 get_dev_type (dev_msg_header));
295
296 if (dev_msg_header->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
297 break;
298
299 bcdi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
300
301 guid_str = ks_guid_to_string (&bcdi->dbcc_classguid);
302 GST_INFO_OBJECT (self,
303 "Removed device, class interface GUID %s, path %s", guid_str,
304 bcdi->dbcc_name);
305 g_free (guid_str);
306 break;
307 case DBT_DEVICEREMOVEPENDING:
308 GST_DEBUG_OBJECT (self, "DBT_DEVICEREMOVEPENDING for %s",
309 get_dev_type (dev_msg_header));
310 break;
311 case DBT_DEVICETYPESPECIFIC:
312 GST_DEBUG_OBJECT (self, "DBT_DEVICETYPESPECIFIC for %s",
313 get_dev_type (dev_msg_header));
314 break;
315 case DBT_DEVNODES_CHANGED:
316 GST_DEBUG_OBJECT (self, "DBT_DEVNODES_CHANGED for %s",
317 get_dev_type (dev_msg_header));
318 break;
319 case DBT_QUERYCHANGECONFIG:
320 GST_DEBUG_OBJECT (self, "DBT_QUERYCHANGECONFIG for %s",
321 get_dev_type (dev_msg_header));
322 break;
323 case DBT_USERDEFINED:
324 user_dev_msg_header = (struct _DEV_BROADCAST_USERDEFINED *) lparam;
325 dev_msg_header =
326 (DEV_BROADCAST_HDR *) & user_dev_msg_header->dbud_dbh;
327 GST_DEBUG_OBJECT (self, "DBT_USERDEFINED for %s: %s",
328 get_dev_type (dev_msg_header), user_dev_msg_header->dbud_szName);
329 break;
330 default:
331 break;
332 }
333
334 switch (wparam) {
335 case DBT_DEVICEARRIVAL:
336 if (dev_msg_header->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
337 break;
338
339 bcdi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
340
341 /* Since both video and audio capture device declare KSCATEGORY_CAPTURE, we filter on
342 KSCATEGORY_VIDEO here. To add audio support we should accept also KSCATEGORY_AUDIO. */
343 if (!IsEqualGUID (&bcdi->dbcc_classguid, &KSCATEGORY_VIDEO))
344 break;
345
346 devices =
347 ks_enumerate_devices (&bcdi->dbcc_classguid, &KSCATEGORY_CAPTURE);
348 if (devices == NULL)
349 break;
350
351 source = NULL;
352 for (item = devices; item != NULL; item = item->next) {
353 KsDeviceEntry *entry = item->data;
354 GST_DEBUG_OBJECT (self, "Listed device %s = %s", entry->name,
355 entry->path);
356
357 if ((source == NULL) &&
358 (g_ascii_strcasecmp (entry->path, bcdi->dbcc_name) == 0))
359 source = new_video_source (entry); /* Or audio source, not implemented yet */
360
361 ks_device_entry_free (entry);
362 }
363
364 if (source)
365 gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), source);
366
367 g_list_free (devices);
368 break;
369 case DBT_DEVICEREMOVECOMPLETE:
370 if (dev_msg_header->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
371 break;
372
373 bcdi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
374 dev = NULL;
375
376 GST_OBJECT_LOCK (self);
377 for (item = provider->devices; item; item = item->next) {
378 dev = item->data;
379
380 if (g_ascii_strcasecmp (dev->path, bcdi->dbcc_name) == 0) {
381 guid_str = gst_device_get_display_name (GST_DEVICE (dev));
382 GST_INFO_OBJECT (self, "Device matches to %s", guid_str);
383 g_free (guid_str);
384 gst_object_ref (dev);
385 break;
386 }
387 dev = NULL;
388 }
389 GST_OBJECT_UNLOCK (self);
390
391 if (dev) {
392 gst_device_provider_device_remove (GST_DEVICE_PROVIDER (self),
393 GST_DEVICE (dev));
394 gst_object_unref (dev);
395 }
396 break;
397 default:
398 break;
399 }
400 result = FALSE;
401 break;
402 case WM_DESTROY:
403 PostQuitMessage (0);
404 result = FALSE;
405 break;
406 case WM_QUITTHREAD:
407 DestroyWindow (window_handle);
408 result = FALSE;
409 break;
410 default:
411 result = DefWindowProc (window_handle, message, wparam, lparam);
412 break;
413 }
414
415 return result;
416 }
417
418 static ATOM
reg_msg_window_class(const char * class_name,HINSTANCE inst)419 reg_msg_window_class (const char *class_name, HINSTANCE inst)
420 {
421 WNDCLASSEXA classex;
422
423 memset (&classex, 0, sizeof (classex));
424 classex.cbSize = sizeof (classex);
425 classex.hInstance = inst;
426 classex.lpfnWndProc = (WNDPROC) msg_window_message_proc;
427 classex.lpszClassName = class_name;
428
429 return RegisterClassExA (&classex);
430 }
431
432 static void
unreg_msg_window_class(ATOM class_id,const char * class_name,HINSTANCE inst)433 unreg_msg_window_class (ATOM class_id, const char *class_name, HINSTANCE inst)
434 {
435 if (class_id != 0)
436 UnregisterClassA ((LPCSTR) MAKELPARAM (class_id, 0), inst);
437 else
438 UnregisterClassA (class_name, inst);
439 }
440
441 static gpointer
ks_provider_msg_window_thread(gpointer dat)442 ks_provider_msg_window_thread (gpointer dat)
443 {
444 GstKsDeviceProvider *self;
445 MSG msg;
446 ATOM wnd_class;
447 BOOL message_status;
448 HINSTANCE inst;
449 HANDLE msg_window = NULL;
450 DWORD error;
451 HDEVNOTIFY devnotify = NULL;
452
453 g_return_val_if_fail (dat != NULL, NULL);
454
455 self = GST_KS_DEVICE_PROVIDER (dat);
456
457 GST_DEBUG_OBJECT (self, "Entering message window thread: %p",
458 g_thread_self ());
459
460 GST_OBJECT_LOCK (self);
461 wnd_class = self->message_window_class;
462 GST_OBJECT_UNLOCK (self);
463
464 inst = (HINSTANCE) GetModuleHandle (NULL);
465
466 msg_window = CreateWindowExA (0,
467 wnd_class != 0 ? (LPCSTR) MAKELPARAM (wnd_class, 0) : KS_MSG_WINDOW_CLASS,
468 "", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, inst, self);
469 error = GetLastError ();
470
471 if (msg_window == NULL) {
472 GST_ERROR_OBJECT (self, "Could not create a message window: %lu", error);
473 GST_OBJECT_LOCK (self);
474 unreg_msg_window_class (wnd_class, KS_MSG_WINDOW_CLASS, inst);
475 self->message_window_class = 0;
476 SetEvent (self->wakeup_event);
477 GST_OBJECT_UNLOCK (self);
478 return NULL;
479 }
480
481 GST_OBJECT_LOCK (self);
482 self->message_window = msg_window;
483
484 devnotify =
485 register_device_interface (self, GUID_DEVCLASS_WCEUSBS, msg_window);
486 if (devnotify == NULL) {
487 DestroyWindow (msg_window);
488 unreg_msg_window_class (wnd_class, KS_MSG_WINDOW_CLASS, inst);
489 self->message_window_class = 0;
490 self->message_window = NULL;
491 SetEvent (self->wakeup_event);
492 GST_OBJECT_UNLOCK (self);
493 return NULL;
494 }
495
496 self->device_notify_handle = devnotify;
497 SetEvent (self->wakeup_event);
498 GST_OBJECT_UNLOCK (self);
499
500 while ((message_status = GetMessage (&msg, NULL, 0, 0)) != 0) {
501 if (message_status < 0 || msg.message == WM_QUIT)
502 break;
503 TranslateMessage (&msg);
504 DispatchMessage (&msg);
505 }
506
507 GST_DEBUG_OBJECT (self, "Exiting internal window thread: %p",
508 g_thread_self ());
509
510 return NULL;
511 }
512
513 static gboolean
gst_ks_device_provider_start(GstDeviceProvider * provider)514 gst_ks_device_provider_start (GstDeviceProvider * provider)
515 {
516 ATOM wnd_class = 0;
517 HINSTANCE inst;
518 HANDLE wakeup_event;
519 HWND message_window;
520 DWORD error;
521 GList *devs;
522 GList *dev;
523 GstKsDeviceProvider *self = GST_KS_DEVICE_PROVIDER (provider);
524
525 GST_OBJECT_LOCK (self);
526 g_assert (self->message_window == NULL);
527 GST_OBJECT_UNLOCK (self);
528
529 /* We get notifications on *change*, so before we get to that,
530 * we need to obtain a complete list of devices, which we will
531 * watch for changes.
532 */
533 devs = gst_ks_device_provider_probe (provider);
534 for (dev = devs; dev; dev = dev->next) {
535 if (dev->data)
536 gst_device_provider_device_add (provider, (GstDevice *) dev->data);
537 }
538 g_list_free (devs);
539
540 inst = (HINSTANCE) GetModuleHandle (NULL);
541
542 wakeup_event = CreateEvent (NULL, TRUE, FALSE, NULL);
543 error = GetLastError ();
544 if (wakeup_event == NULL) {
545 GST_OBJECT_LOCK (self);
546 GST_ERROR_OBJECT (self, "Could not create a wakeup event: %lu", error);
547 GST_OBJECT_UNLOCK (self);
548 return FALSE;
549 }
550
551 wnd_class = reg_msg_window_class (KS_MSG_WINDOW_CLASS, inst);
552 error = GetLastError ();
553
554 if ((wnd_class == 0) && (error != ERROR_CLASS_ALREADY_EXISTS)) {
555 GST_ERROR_OBJECT (self,
556 "Could not register message window class: %lu", error);
557 CloseHandle (wakeup_event);
558 return FALSE;
559 }
560
561 GST_OBJECT_LOCK (self);
562 self->message_window_class = wnd_class;
563 self->wakeup_event = wakeup_event;
564
565 self->message_thread =
566 g_thread_new ("ks-device-provider-message-window-thread",
567 (GThreadFunc) ks_provider_msg_window_thread, self);
568 if (self->message_thread == NULL) {
569 GST_ERROR_OBJECT (self, "Could not create message window thread");
570 unreg_msg_window_class (wnd_class, KS_MSG_WINDOW_CLASS, inst);
571 self->message_window_class = 0;
572 CloseHandle (self->wakeup_event);
573 GST_OBJECT_UNLOCK (self);
574 return FALSE;
575 }
576 GST_OBJECT_UNLOCK (self);
577
578 if (WaitForSingleObject (wakeup_event, INFINITE) != WAIT_OBJECT_0) {
579 GST_ERROR_OBJECT (self,
580 "Failed to wait for the message thread to initialize");
581 }
582
583 GST_OBJECT_LOCK (self);
584 CloseHandle (self->wakeup_event);
585 self->wakeup_event = NULL;
586 message_window = self->message_window;
587 GST_OBJECT_UNLOCK (self);
588
589 if (message_window == NULL)
590 return FALSE;
591
592 return TRUE;
593 }
594
595 static void
gst_ks_device_provider_stop(GstDeviceProvider * provider)596 gst_ks_device_provider_stop (GstDeviceProvider * provider)
597 {
598 HINSTANCE inst;
599 GThread *message_thread;
600 GstKsDeviceProvider *self = GST_KS_DEVICE_PROVIDER (provider);
601
602 GST_OBJECT_LOCK (self);
603
604 g_assert (self->message_window != NULL);
605
606 UnregisterDeviceNotification (self->device_notify_handle);
607 self->device_notify_handle = NULL;
608 PostMessage (self->message_window, WM_QUITTHREAD, 0, 0);
609 message_thread = self->message_thread;
610 GST_OBJECT_UNLOCK (self);
611
612 g_thread_join (message_thread);
613
614 GST_OBJECT_LOCK (self);
615 self->message_window = NULL;
616 self->message_thread = NULL;
617
618 inst = (HINSTANCE) GetModuleHandle (NULL);
619
620 unreg_msg_window_class (self->message_window_class, KS_MSG_WINDOW_CLASS,
621 inst);
622
623 self->message_window_class = 0;
624 GST_OBJECT_UNLOCK (self);
625 }
626
627 enum
628 {
629 PROP_PATH = 1
630 };
631
632 G_DEFINE_TYPE (GstKsDevice, gst_ks_device, GST_TYPE_DEVICE);
633
634 static void gst_ks_device_get_property (GObject * object, guint prop_id,
635 GValue * value, GParamSpec * pspec);
636 static void gst_ks_device_set_property (GObject * object, guint prop_id,
637 const GValue * value, GParamSpec * pspec);
638 static void gst_ks_device_finalize (GObject * object);
639 static GstElement *gst_ks_device_create_element (GstDevice * device,
640 const gchar * name);
641 static gboolean gst_ks_device_reconfigure_element (GstDevice * device,
642 GstElement * element);
643
644 static void
gst_ks_device_class_init(GstKsDeviceClass * klass)645 gst_ks_device_class_init (GstKsDeviceClass * klass)
646 {
647 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
648 GObjectClass *object_class = G_OBJECT_CLASS (klass);
649
650 dev_class->create_element = gst_ks_device_create_element;
651 dev_class->reconfigure_element = gst_ks_device_reconfigure_element;
652
653 object_class->get_property = gst_ks_device_get_property;
654 object_class->set_property = gst_ks_device_set_property;
655 object_class->finalize = gst_ks_device_finalize;
656
657 g_object_class_install_property (object_class, PROP_PATH,
658 g_param_spec_string ("path", "System device path",
659 "The system path to the device", "",
660 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
661 }
662
663 static void
gst_ks_device_init(GstKsDevice * device)664 gst_ks_device_init (GstKsDevice * device)
665 {
666 }
667
668 static void
gst_ks_device_finalize(GObject * object)669 gst_ks_device_finalize (GObject * object)
670 {
671 GstKsDevice *device = GST_KS_DEVICE (object);
672
673 g_free (device->path);
674
675 G_OBJECT_CLASS (gst_ks_device_parent_class)->finalize (object);
676 }
677
678 static GstElement *
gst_ks_device_create_element(GstDevice * device,const gchar * name)679 gst_ks_device_create_element (GstDevice * device, const gchar * name)
680 {
681 GstKsDevice *ks_dev = GST_KS_DEVICE (device);
682 GstElement *elem;
683
684 elem = gst_element_factory_make (ks_dev->element, name);
685 g_object_set (elem, "device-path", ks_dev->path, NULL);
686
687 return elem;
688 }
689
690 static gboolean
gst_ks_device_reconfigure_element(GstDevice * device,GstElement * element)691 gst_ks_device_reconfigure_element (GstDevice * device, GstElement * element)
692 {
693 GstKsDevice *ks_dev = GST_KS_DEVICE (device);
694
695 if (!strcmp (ks_dev->element, "ksvideosrc")) {
696 if (!GST_IS_KS_VIDEO_SRC (element))
697 return FALSE;
698 /*
699 } else if (!strcmp (ks_dev->element, "ksaudiosrc")) {
700 if (!GST_IS_KS_AUDIO_SRC (element))
701 return FALSE;
702 } else if (!strcmp (ks_dev->element, "ksaudiosink")) {
703 if (!GST_IS_KS_AUDIO_SINK (element))
704 return FALSE;
705 */
706 } else {
707 g_assert_not_reached ();
708 }
709
710 g_object_set (element, "path", ks_dev->path, NULL);
711
712 return TRUE;
713 }
714
715 static GstDevice *
gst_ks_device_new(guint device_index,const gchar * device_name,GstCaps * caps,const gchar * device_path,GstKsDeviceType type)716 gst_ks_device_new (guint device_index, const gchar * device_name,
717 GstCaps * caps, const gchar * device_path, GstKsDeviceType type)
718 {
719 GstKsDevice *gstdev;
720 const gchar *element = NULL;
721 const gchar *klass = NULL;
722
723 g_return_val_if_fail (device_name, NULL);
724 g_return_val_if_fail (device_path, NULL);
725 g_return_val_if_fail (caps, NULL);
726
727
728 switch (type) {
729 case GST_KS_DEVICE_TYPE_VIDEO_SOURCE:
730 element = "ksvideosrc";
731 klass = "Video/Source";
732 break;
733 case GST_KS_DEVICE_TYPE_AUDIO_SOURCE:
734 element = "ksaudiosrc";
735 klass = "Audio/Source";
736 break;
737 case GST_KS_DEVICE_TYPE_AUDIO_SINK:
738 element = "ksaudiosink";
739 klass = "Audio/Sink";
740 break;
741 default:
742 g_assert_not_reached ();
743 break;
744 }
745
746
747 gstdev = g_object_new (GST_TYPE_KS_DEVICE,
748 "display-name", device_name, "caps", caps, "device-class", klass,
749 "path", device_path, NULL);
750
751 gstdev->type = type;
752 gstdev->device_index = device_index;
753 gstdev->path = g_strdup (device_path);
754 gstdev->element = element;
755
756 return GST_DEVICE (gstdev);
757 }
758
759
760 static void
gst_ks_device_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)761 gst_ks_device_get_property (GObject * object, guint prop_id,
762 GValue * value, GParamSpec * pspec)
763 {
764 GstKsDevice *device;
765
766 device = GST_KS_DEVICE_CAST (object);
767
768 switch (prop_id) {
769 case PROP_PATH:
770 g_value_set_string (value, device->path);
771 break;
772 default:
773 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
774 break;
775 }
776 }
777
778
779 static void
gst_ks_device_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)780 gst_ks_device_set_property (GObject * object, guint prop_id,
781 const GValue * value, GParamSpec * pspec)
782 {
783 GstKsDevice *device;
784
785 device = GST_KS_DEVICE_CAST (object);
786
787 switch (prop_id) {
788 case PROP_PATH:
789 device->path = g_value_dup_string (value);
790 break;
791 default:
792 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
793 break;
794 }
795 }
796