80bb4775b2545ece3bce47666a31a147cde1de72
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / winks / kshelpers.c
1 /*
2  * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "kshelpers.h"
21
22 /* This plugin is from the era of Windows XP and uses APIs that have been
23  * deprecated since then. Let's pretend we're Windows XP too so that Windows
24  * lets us use that deprecated API. */
25 #undef NTDDI_VERSION
26 #undef _WIN32_WINNT
27 #define NTDDI_VERSION NTDDI_WINXP
28 #define _WIN32_WINNT _WIN32_WINNT_WINXP
29
30 #include <ksmedia.h>
31 #include <setupapi.h>
32 #include <gst/gst.h>
33
34 GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
35 #define GST_CAT_DEFAULT gst_ks_debug
36
37 #ifndef STATIC_KSPROPSETID_Wave_Queued
38 #define STATIC_KSPROPSETID_Wave_Queued \
39     0x16a15b10L, 0x16f0, 0x11d0, { 0xa1, 0x95, 0x00, 0x20, 0xaf, 0xd1, 0x56, 0xe4 }
40 DEFINE_GUIDSTRUCT ("16a15b10-16f0-11d0-a195-0020afd156e4",
41     KSPROPSETID_Wave_Queued);
42 #endif
43
44 gboolean
45 ks_is_valid_handle (HANDLE h)
46 {
47   return (h != INVALID_HANDLE_VALUE && h != NULL);
48 }
49
50 GList *
51 ks_enumerate_devices (const GUID * devtype, const GUID * direction_category)
52 {
53   GList *result = NULL;
54   HDEVINFO devinfo;
55   gint i;
56
57   devinfo = SetupDiGetClassDevsW (devtype, NULL, NULL,
58       DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
59   if (!ks_is_valid_handle (devinfo))
60     return NULL;                /* no devices */
61
62   for (i = 0;; i++) {
63     BOOL success;
64     SP_DEVICE_INTERFACE_DATA if_data = { 0, };
65     SP_DEVICE_INTERFACE_DATA if_alias_data = { 0, };
66     SP_DEVICE_INTERFACE_DETAIL_DATA_W *if_detail_data;
67     DWORD if_detail_data_size;
68     SP_DEVINFO_DATA devinfo_data = { 0, };
69     DWORD req_size;
70
71     if_data.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
72
73     success = SetupDiEnumDeviceInterfaces (devinfo, NULL, devtype, i, &if_data);
74     if (!success)               /* all devices enumerated? */
75       break;
76
77     if_alias_data.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
78     success =
79         SetupDiGetDeviceInterfaceAlias (devinfo, &if_data, direction_category,
80         &if_alias_data);
81     if (!success)
82       continue;
83
84     if_detail_data_size = (MAX_PATH - 1) * sizeof (gunichar2);
85     if_detail_data = g_malloc0 (if_detail_data_size);
86     if_detail_data->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA_W);
87
88     devinfo_data.cbSize = sizeof (SP_DEVINFO_DATA);
89
90     success = SetupDiGetDeviceInterfaceDetailW (devinfo, &if_data,
91         if_detail_data, if_detail_data_size, &req_size, &devinfo_data);
92     if (success) {
93       KsDeviceEntry *entry;
94       WCHAR buf[512];
95
96       entry = g_new0 (KsDeviceEntry, 1);
97       entry->index = i;
98       entry->path =
99           g_utf16_to_utf8 (if_detail_data->DevicePath, -1, NULL, NULL, NULL);
100
101       if (SetupDiGetDeviceRegistryPropertyW (devinfo, &devinfo_data,
102               SPDRP_FRIENDLYNAME, NULL, (BYTE *) buf, sizeof (buf), NULL)) {
103         entry->name = g_utf16_to_utf8 (buf, -1, NULL, NULL, NULL);
104       }
105
106       if (entry->name == NULL) {
107         if (SetupDiGetDeviceRegistryPropertyW (devinfo, &devinfo_data,
108                 SPDRP_DEVICEDESC, NULL, (BYTE *) buf, sizeof (buf), NULL)) {
109           entry->name = g_utf16_to_utf8 (buf, -1, NULL, NULL, NULL);
110         }
111       }
112
113       if (entry->name != NULL)
114         result = g_list_prepend (result, entry);
115       else
116         ks_device_entry_free (entry);
117     }
118
119     g_free (if_detail_data);
120   }
121
122   SetupDiDestroyDeviceInfoList (devinfo);
123
124   return g_list_reverse (result);
125 }
126
127 void
128 ks_device_entry_free (KsDeviceEntry * entry)
129 {
130   if (entry == NULL)
131     return;
132
133   g_free (entry->path);
134   g_free (entry->name);
135
136   g_free (entry);
137 }
138
139 void
140 ks_device_list_free (GList * devices)
141 {
142   GList *cur;
143
144   for (cur = devices; cur != NULL; cur = cur->next)
145     ks_device_entry_free (cur->data);
146
147   g_list_free (devices);
148 }
149
150 static gboolean
151 ks_sync_device_io_control (HANDLE device, gulong io_control_code,
152     gpointer in_buffer, gulong in_buffer_size, gpointer out_buffer,
153     gulong out_buffer_size, gulong * bytes_returned, gulong * error)
154 {
155   OVERLAPPED overlapped = { 0, };
156   BOOL success;
157
158   overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
159
160   success = DeviceIoControl (device, io_control_code, in_buffer,
161       in_buffer_size, out_buffer, out_buffer_size, bytes_returned, &overlapped);
162   if (!success) {
163     DWORD err;
164
165     if ((err = GetLastError ()) == ERROR_IO_PENDING) {
166       success = GetOverlappedResult (device, &overlapped, bytes_returned, TRUE);
167       if (!success)
168         err = GetLastError ();
169     }
170
171     if (error != NULL)
172       *error = err;
173   }
174
175   CloseHandle (overlapped.hEvent);
176
177   return success ? TRUE : FALSE;
178 }
179
180 gboolean
181 ks_filter_get_pin_property (HANDLE filter_handle, gulong pin_id,
182     GUID prop_set, gulong prop_id, gpointer value, gulong value_size,
183     gulong * error)
184 {
185   KSP_PIN prop;
186   DWORD bytes_returned = 0;
187
188   memset (&prop, 0, sizeof (KSP_PIN));
189
190   prop.PinId = pin_id;
191   prop.Property.Set = prop_set;
192   prop.Property.Id = prop_id;
193   prop.Property.Flags = KSPROPERTY_TYPE_GET;
194
195   return ks_sync_device_io_control (filter_handle, IOCTL_KS_PROPERTY, &prop,
196       sizeof (prop), value, value_size, &bytes_returned, error);
197 }
198
199 gboolean
200 ks_filter_get_pin_property_multi (HANDLE filter_handle, gulong pin_id,
201     GUID prop_set, gulong prop_id, KSMULTIPLE_ITEM ** items, gulong * error)
202 {
203   KSP_PIN prop;
204   DWORD items_size = 0, bytes_written = 0;
205   gulong err;
206   gboolean ret;
207
208   memset (&prop, 0, sizeof (KSP_PIN));
209   *items = NULL;
210
211   prop.PinId = pin_id;
212   prop.Property.Set = prop_set;
213   prop.Property.Id = prop_id;
214   prop.Property.Flags = KSPROPERTY_TYPE_GET;
215
216   ret = ks_sync_device_io_control (filter_handle, IOCTL_KS_PROPERTY,
217       &prop.Property, sizeof (prop), NULL, 0, &items_size, &err);
218   if (!ret && err != ERROR_INSUFFICIENT_BUFFER && err != ERROR_MORE_DATA)
219     goto ioctl_failed;
220
221   *items = g_malloc0 (items_size);
222
223   ret = ks_sync_device_io_control (filter_handle, IOCTL_KS_PROPERTY, &prop,
224       sizeof (prop), *items, items_size, &bytes_written, &err);
225   if (!ret)
226     goto ioctl_failed;
227
228   return ret;
229
230 ioctl_failed:
231   if (error != NULL)
232     *error = err;
233
234   g_free (*items);
235   *items = NULL;
236
237   return FALSE;
238 }
239
240 gboolean
241 ks_object_query_property (HANDLE handle, GUID prop_set, gulong prop_id,
242     gulong prop_flags, gpointer * value, gulong * value_size, gulong * error)
243 {
244   KSPROPERTY prop;
245   DWORD req_value_size = 0, bytes_written = 0;
246   gulong err;
247   gboolean ret;
248
249   memset (&prop, 0, sizeof (KSPROPERTY));
250   *value = NULL;
251
252   prop.Set = prop_set;
253   prop.Id = prop_id;
254   prop.Flags = prop_flags;
255
256   if (value_size == NULL || *value_size == 0) {
257     ret = ks_sync_device_io_control (handle, IOCTL_KS_PROPERTY,
258         &prop, sizeof (prop), NULL, 0, &req_value_size, &err);
259     if (!ret && err != ERROR_INSUFFICIENT_BUFFER && err != ERROR_MORE_DATA)
260       goto ioctl_failed;
261   } else {
262     req_value_size = *value_size;
263   }
264
265   *value = g_malloc0 (req_value_size);
266
267   ret = ks_sync_device_io_control (handle, IOCTL_KS_PROPERTY, &prop,
268       sizeof (prop), *value, req_value_size, &bytes_written, &err);
269   if (!ret)
270     goto ioctl_failed;
271
272   if (value_size != NULL)
273     *value_size = bytes_written;
274
275   return ret;
276
277 ioctl_failed:
278   if (error != NULL)
279     *error = err;
280
281   g_free (*value);
282   *value = NULL;
283
284   if (value_size != NULL)
285     *value_size = 0;
286
287   return FALSE;
288 }
289
290 gboolean
291 ks_object_get_property (HANDLE handle, GUID prop_set, gulong prop_id,
292     gpointer * value, gulong * value_size, gulong * error)
293 {
294   return ks_object_query_property (handle, prop_set, prop_id,
295       KSPROPERTY_TYPE_GET, value, value_size, error);
296 }
297
298 gboolean
299 ks_object_set_property (HANDLE handle, GUID prop_set, gulong prop_id,
300     gpointer value, gulong value_size, gulong * error)
301 {
302   KSPROPERTY prop;
303   DWORD bytes_returned;
304
305   memset (&prop, 0, sizeof (KSPROPERTY));
306   prop.Set = prop_set;
307   prop.Id = prop_id;
308   prop.Flags = KSPROPERTY_TYPE_SET;
309
310   return ks_sync_device_io_control (handle, IOCTL_KS_PROPERTY, &prop,
311       sizeof (prop), value, value_size, &bytes_returned, error);
312 }
313
314 gboolean
315 ks_object_get_supported_property_sets (HANDLE handle, GUID ** propsets,
316     gulong * len)
317 {
318   gulong size = 0;
319   gulong error;
320
321   *propsets = NULL;
322   *len = 0;
323
324   if (ks_object_query_property (handle, GUID_NULL, 0,
325           KSPROPERTY_TYPE_SETSUPPORT, (void *) propsets, &size, &error)) {
326     if (size % sizeof (GUID) == 0) {
327       *len = size / sizeof (GUID);
328       return TRUE;
329     }
330   }
331
332   g_free (*propsets);
333   *propsets = NULL;
334   *len = 0;
335   return FALSE;
336 }
337
338 gboolean
339 ks_object_set_connection_state (HANDLE handle, KSSTATE state, gulong * error)
340 {
341   return ks_object_set_property (handle, KSPROPSETID_Connection,
342       KSPROPERTY_CONNECTION_STATE, &state, sizeof (state), error);
343 }
344
345 gchar *
346 ks_guid_to_string (const GUID * guid)
347 {
348   return g_strdup_printf ("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
349       (guint) guid->Data1, (guint) guid->Data2, (guint) guid->Data3,
350       (guint) guid->Data4[0], (guint) guid->Data4[1], (guint) guid->Data4[2],
351       (guint) guid->Data4[3], (guint) guid->Data4[4], (guint) guid->Data4[5],
352       (guint) guid->Data4[6], (guint) guid->Data4[7]);
353 }
354
355 const gchar *
356 ks_state_to_string (KSSTATE state)
357 {
358   switch (state) {
359     case KSSTATE_STOP:
360       return "KSSTATE_STOP";
361     case KSSTATE_ACQUIRE:
362       return "KSSTATE_ACQUIRE";
363     case KSSTATE_PAUSE:
364       return "KSSTATE_PAUSE";
365     case KSSTATE_RUN:
366       return "KSSTATE_RUN";
367     default:
368       g_assert_not_reached ();
369   }
370
371   return "UNKNOWN";
372 }
373
374 #define CHECK_OPTIONS_FLAG(flag) \
375   if (flags & KSSTREAM_HEADER_OPTIONSF_##flag)\
376   {\
377     if (str->len > 0)\
378       g_string_append (str, "|");\
379     g_string_append (str, G_STRINGIFY (flag));\
380     flags &= ~KSSTREAM_HEADER_OPTIONSF_##flag;\
381   }
382
383 gchar *
384 ks_options_flags_to_string (gulong flags)
385 {
386   gchar *ret;
387   GString *str;
388
389   str = g_string_sized_new (128);
390
391   CHECK_OPTIONS_FLAG (SPLICEPOINT);
392   CHECK_OPTIONS_FLAG (PREROLL);
393   CHECK_OPTIONS_FLAG (DATADISCONTINUITY);
394   CHECK_OPTIONS_FLAG (TYPECHANGED);
395   CHECK_OPTIONS_FLAG (TIMEVALID);
396   CHECK_OPTIONS_FLAG (TIMEDISCONTINUITY);
397   CHECK_OPTIONS_FLAG (FLUSHONPAUSE);
398   CHECK_OPTIONS_FLAG (DURATIONVALID);
399   CHECK_OPTIONS_FLAG (ENDOFSTREAM);
400   CHECK_OPTIONS_FLAG (BUFFEREDTRANSFER);
401   CHECK_OPTIONS_FLAG (VRAM_DATA_TRANSFER);
402   CHECK_OPTIONS_FLAG (LOOPEDDATA);
403
404   if (flags != 0)
405     g_string_append_printf (str, "|0x%08x", (guint) flags);
406
407   ret = str->str;
408   g_string_free (str, FALSE);
409
410   return ret;
411 }
412
413 typedef struct
414 {
415   const GUID guid;
416   const gchar *name;
417 } KsPropertySetMapping;
418
419 #ifndef STATIC_KSPROPSETID_GM
420 #define STATIC_KSPROPSETID_GM \
421     0xAF627536, 0xE719, 0x11D2, { 0x8A, 0x1D, 0x00, 0x60, 0x97, 0xD2, 0xDF, 0x5D }
422 #endif
423 #ifndef STATIC_KSPROPSETID_Jack
424 #define STATIC_KSPROPSETID_Jack \
425     0x4509F757, 0x2D46, 0x4637, { 0x8E, 0x62, 0xCE, 0x7D, 0xB9, 0x44, 0xF5, 0x7B }
426 #endif
427
428 #ifndef STATIC_PROPSETID_VIDCAP_SELECTOR
429 #define STATIC_PROPSETID_VIDCAP_SELECTOR \
430     0x1ABDAECA, 0x68B6, 0x4F83, { 0x93, 0x71, 0xB4, 0x13, 0x90, 0x7C, 0x7B, 0x9F }
431 #endif
432 #ifndef STATIC_PROPSETID_EXT_DEVICE
433 #define STATIC_PROPSETID_EXT_DEVICE \
434     0xB5730A90, 0x1A2C, 0x11cf, { 0x8c, 0x23, 0x00, 0xAA, 0x00, 0x6B, 0x68, 0x14 }
435 #endif
436 #ifndef STATIC_PROPSETID_EXT_TRANSPORT
437 #define STATIC_PROPSETID_EXT_TRANSPORT \
438     0xA03CD5F0, 0x3045, 0x11cf, { 0x8c, 0x44, 0x00, 0xAA, 0x00, 0x6B, 0x68, 0x14 }
439 #endif
440 #ifndef STATIC_PROPSETID_TIMECODE_READER
441 #define STATIC_PROPSETID_TIMECODE_READER \
442     0x9B496CE1, 0x811B, 0x11cf, { 0x8C, 0x77, 0x00, 0xAA, 0x00, 0x6B, 0x68, 0x14 }
443 #endif
444
445 /* GCC warns about this, but it seems to be correct and MSVC doesn't warn about
446  * it. XXX: Check again after the toolchain is updated:
447  * https://gitlab.freedesktop.org/gstreamer/cerbero/merge_requests/69 */
448 #ifdef __GNUC__
449 #pragma GCC diagnostic ignored "-Wmissing-braces"
450 #endif
451 static const KsPropertySetMapping known_property_sets[] = {
452   {{STATIC_KSPROPSETID_General}, "General"},
453   {{STATIC_KSPROPSETID_MediaSeeking}, "MediaSeeking"},
454   {{STATIC_KSPROPSETID_Topology}, "Topology"},
455   {{STATIC_KSPROPSETID_GM}, "GM"},
456   {{STATIC_KSPROPSETID_Pin}, "Pin"},
457   {{STATIC_KSPROPSETID_Quality}, "Quality"},
458   {{STATIC_KSPROPSETID_Connection}, "Connection"},
459   {{STATIC_KSPROPSETID_MemoryTransport}, "MemoryTransport"},
460   {{STATIC_KSPROPSETID_StreamAllocator}, "StreamAllocator"},
461   {{STATIC_KSPROPSETID_StreamInterface}, "StreamInterface"},
462   {{STATIC_KSPROPSETID_Stream}, "Stream"},
463   {{STATIC_KSPROPSETID_Clock}, "Clock"},
464
465   {{STATIC_KSPROPSETID_DirectSound3DListener}, "DirectSound3DListener"},
466   {{STATIC_KSPROPSETID_DirectSound3DBuffer}, "DirectSound3DBuffer"},
467   {{STATIC_KSPROPSETID_Hrtf3d}, "Hrtf3d"},
468   {{STATIC_KSPROPSETID_Itd3d}, "Itd3d"},
469   {{STATIC_KSPROPSETID_Bibliographic}, "Bibliographic"},
470   {{STATIC_KSPROPSETID_TopologyNode}, "TopologyNode"},
471   {{STATIC_KSPROPSETID_RtAudio}, "RtAudio"},
472   {{STATIC_KSPROPSETID_DrmAudioStream}, "DrmAudioStream"},
473   {{STATIC_KSPROPSETID_Audio}, "Audio"},
474   {{STATIC_KSPROPSETID_Acoustic_Echo_Cancel}, "Acoustic_Echo_Cancel"},
475   {{STATIC_KSPROPSETID_Wave_Queued}, "Wave_Queued"},
476   {{STATIC_KSPROPSETID_Wave}, "Wave"},
477   {{STATIC_KSPROPSETID_WaveTable}, "WaveTable"},
478   {{STATIC_KSPROPSETID_Cyclic}, "Cyclic"},
479   {{STATIC_KSPROPSETID_Sysaudio}, "Sysaudio"},
480   {{STATIC_KSPROPSETID_Sysaudio_Pin}, "Sysaudio_Pin"},
481   {{STATIC_KSPROPSETID_AudioGfx}, "AudioGfx"},
482   {{STATIC_KSPROPSETID_Linear}, "Linear"},
483   {{STATIC_KSPROPSETID_Mpeg2Vid}, "Mpeg2Vid"},
484   {{STATIC_KSPROPSETID_AC3}, "AC3"},
485   {{STATIC_KSPROPSETID_AudioDecoderOut}, "AudioDecoderOut"},
486   {{STATIC_KSPROPSETID_DvdSubPic}, "DvdSubPic"},
487   {{STATIC_KSPROPSETID_CopyProt}, "CopyProt"},
488   {{STATIC_KSPROPSETID_VBICAP_PROPERTIES}, "VBICAP_PROPERTIES"},
489   {{STATIC_KSPROPSETID_VBICodecFiltering}, "VBICodecFiltering"},
490   {{STATIC_KSPROPSETID_VramCapture}, "VramCapture"},
491   {{STATIC_KSPROPSETID_OverlayUpdate}, "OverlayUpdate"},
492   {{STATIC_KSPROPSETID_VPConfig}, "VPConfig"},
493   {{STATIC_KSPROPSETID_VPVBIConfig}, "VPVBIConfig"},
494   {{STATIC_KSPROPSETID_TSRateChange}, "TSRateChange"},
495   {{STATIC_KSPROPSETID_Jack}, "Jack"},
496
497   {{STATIC_PROPSETID_ALLOCATOR_CONTROL}, "ALLOCATOR_CONTROL"},
498   {{STATIC_PROPSETID_VIDCAP_VIDEOPROCAMP}, "VIDCAP_VIDEOPROCAMP"},
499   {{STATIC_PROPSETID_VIDCAP_SELECTOR}, "VIDCAP_SELECTOR"},
500   {{STATIC_PROPSETID_TUNER}, "TUNER"},
501   {{STATIC_PROPSETID_VIDCAP_VIDEOENCODER}, "VIDCAP_VIDEOENCODER"},
502   {{STATIC_PROPSETID_VIDCAP_VIDEODECODER}, "VIDCAP_VIDEODECODER"},
503   {{STATIC_PROPSETID_VIDCAP_CAMERACONTROL}, "VIDCAP_CAMERACONTROL"},
504   {{STATIC_PROPSETID_EXT_DEVICE}, "EXT_DEVICE"},
505   {{STATIC_PROPSETID_EXT_TRANSPORT}, "EXT_TRANSPORT"},
506   {{STATIC_PROPSETID_TIMECODE_READER}, "TIMECODE_READER"},
507   {{STATIC_PROPSETID_VIDCAP_CROSSBAR}, "VIDCAP_CROSSBAR"},
508   {{STATIC_PROPSETID_VIDCAP_TVAUDIO}, "VIDCAP_TVAUDIO"},
509   {{STATIC_PROPSETID_VIDCAP_VIDEOCOMPRESSION}, "VIDCAP_VIDEOCOMPRESSION"},
510   {{STATIC_PROPSETID_VIDCAP_VIDEOCONTROL}, "VIDCAP_VIDEOCONTROL"},
511   {{STATIC_PROPSETID_VIDCAP_DROPPEDFRAMES}, "VIDCAP_DROPPEDFRAMES"},
512 };
513
514 gchar *
515 ks_property_set_to_string (const GUID * guid)
516 {
517   guint i;
518
519   for (i = 0;
520       i < sizeof (known_property_sets) / sizeof (known_property_sets[0]); i++) {
521     if (IsEqualGUID (guid, &known_property_sets[i].guid))
522       return g_strdup_printf ("KSPROPSETID_%s", known_property_sets[i].name);
523   }
524
525   return ks_guid_to_string (guid);
526 }