taglist, plugins: fix compiler warnings with GLib >= 2.76
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / winks / gstksvideodevice.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 "gstksvideodevice.h"
21
22 #include "gstksclock.h"
23 #include "kshelpers.h"
24 #include "ksvideohelpers.h"
25
26 #define READ_TIMEOUT           (10 * 1000)
27 #define MJPEG_MAX_PADDING      128
28 #define MAX_OUTSTANDING_FRAMES 128
29
30 #define KS_BUFFER_ALIGNMENT    4096
31
32 #define DEFAULT_DEVICE_PATH NULL
33
34 GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
35 #define GST_CAT_DEFAULT gst_ks_debug
36
37 #define GST_DEBUG_IS_ENABLED()  \
38     (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG)
39 #define UNREF_BUFFER(b)         \
40   G_STMT_START {                \
41     if (*(b) != NULL) {         \
42       gst_buffer_unref (*(b));  \
43       *(b) = NULL;              \
44     }                           \
45   } G_STMT_END
46
47 enum
48 {
49   PROP_0,
50   PROP_CLOCK,
51   PROP_DEVICE_PATH,
52 };
53
54 typedef struct
55 {
56   KSSTREAM_HEADER header;
57   KS_FRAME_INFO frame_info;
58 } KSSTREAM_READ_PARAMS;
59
60 typedef struct
61 {
62   KSSTREAM_READ_PARAMS params;
63   GstBuffer *buf;
64   OVERLAPPED overlapped;
65 } ReadRequest;
66
67 struct _GstKsVideoDevicePrivate
68 {
69   gboolean open;
70   KSSTATE state;
71
72   GstKsClock *clock;
73   gchar *dev_path;
74   HANDLE filter_handle;
75   GList *media_types;
76   GstCaps *cached_caps;
77   HANDLE cancel_event;
78
79   KsVideoMediaType *cur_media_type;
80   GstCaps *cur_fixed_caps;
81   guint width;
82   guint height;
83   guint fps_n;
84   guint fps_d;
85   guint8 *rgb_swap_buf;
86   gboolean is_muxed;
87
88   HANDLE pin_handle;
89
90   gboolean requests_submitted;
91   gulong num_requests;
92   GArray *requests;
93   GArray *request_events;
94   GstBuffer *spare_buffers[2];
95   GstClockTime last_timestamp;
96 };
97
98 #define GST_KS_VIDEO_DEVICE_GET_PRIVATE(o) ((o)->priv)
99
100 static void gst_ks_video_device_dispose (GObject * object);
101 static void gst_ks_video_device_get_property (GObject * object, guint prop_id,
102     GValue * value, GParamSpec * pspec);
103 static void gst_ks_video_device_set_property (GObject * object, guint prop_id,
104     const GValue * value, GParamSpec * pspec);
105
106 static void gst_ks_video_device_reset_caps (GstKsVideoDevice * self);
107 static guint gst_ks_video_device_get_frame_size (GstKsVideoDevice * self);
108
109 G_DEFINE_TYPE_WITH_PRIVATE (GstKsVideoDevice, gst_ks_video_device,
110     G_TYPE_OBJECT);
111
112 static GstKsVideoDeviceClass *parent_class = NULL;
113
114 static void
115 gst_ks_video_device_class_init (GstKsVideoDeviceClass * klass)
116 {
117   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
118
119   parent_class = g_type_class_peek_parent (klass);
120
121   gobject_class->dispose = gst_ks_video_device_dispose;
122   gobject_class->get_property = gst_ks_video_device_get_property;
123   gobject_class->set_property = gst_ks_video_device_set_property;
124
125   g_object_class_install_property (gobject_class, PROP_CLOCK,
126       g_param_spec_object ("clock", "Clock to use",
127           "Clock to use", GST_TYPE_KS_CLOCK,
128           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
129
130   g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
131       g_param_spec_string ("device-path", "Device Path",
132           "The device path", DEFAULT_DEVICE_PATH,
133           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
134 }
135
136 static void
137 gst_ks_video_device_init (GstKsVideoDevice * self)
138 {
139   GstKsVideoDevicePrivate *priv;
140
141   self->priv = gst_ks_video_device_get_instance_private (self);
142
143   priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
144   priv->open = FALSE;
145   priv->state = KSSTATE_STOP;
146 }
147
148 static void
149 gst_ks_video_device_dispose (GObject * object)
150 {
151   GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
152   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
153
154   gst_ks_video_device_reset_caps (self);
155   gst_ks_video_device_close (self);
156
157   if (priv->clock != NULL) {
158     g_object_unref (priv->clock);
159     priv->clock = NULL;
160   }
161
162   G_OBJECT_CLASS (parent_class)->dispose (object);
163 }
164
165 static void
166 gst_ks_video_device_get_property (GObject * object, guint prop_id,
167     GValue * value, GParamSpec * pspec)
168 {
169   GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
170   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
171
172   switch (prop_id) {
173     case PROP_CLOCK:
174       g_value_set_object (value, priv->clock);
175       break;
176     case PROP_DEVICE_PATH:
177       g_value_set_string (value, priv->dev_path);
178       break;
179     default:
180       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
181       break;
182   }
183 }
184
185 static void
186 gst_ks_video_device_set_property (GObject * object, guint prop_id,
187     const GValue * value, GParamSpec * pspec)
188 {
189   GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
190   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
191
192   switch (prop_id) {
193     case PROP_CLOCK:
194       if (priv->clock != NULL)
195         g_object_unref (priv->clock);
196       priv->clock = g_value_dup_object (value);
197       break;
198     case PROP_DEVICE_PATH:
199       g_free (priv->dev_path);
200       priv->dev_path = g_value_dup_string (value);
201       break;
202     default:
203       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
204       break;
205   }
206 }
207
208 static void
209 gst_ks_video_device_parse_win32_error (const gchar * func_name,
210     DWORD error_code, gulong * ret_error_code, gchar ** ret_error_str)
211 {
212   if (ret_error_code != NULL)
213     *ret_error_code = error_code;
214
215   if (ret_error_str != NULL) {
216     GString *message;
217     gchar buf[1480];
218     DWORD result;
219
220     message = g_string_sized_new (1600);
221     g_string_append_printf (message, "%s returned ", func_name);
222
223     result =
224         FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
225         FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error_code, 0, buf, sizeof (buf),
226         NULL);
227     if (result != 0) {
228       g_string_append_printf (message, "0x%08x: %s", (guint) error_code,
229           g_strchomp (buf));
230     } else {
231       DWORD format_error_code = GetLastError ();
232
233       g_string_append_printf (message,
234           "<0x%08x (FormatMessage error code: %s)>", (guint) error_code,
235           (format_error_code == ERROR_MR_MID_NOT_FOUND)
236           ? "no system error message found"
237           : "failed to retrieve system error message");
238     }
239
240     *ret_error_str = g_string_free (message, FALSE);
241   }
242 }
243
244 static void
245 gst_ks_video_device_clear_buffers (GstKsVideoDevice * self)
246 {
247   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
248   guint i;
249
250   if (priv->requests == NULL)
251     return;
252
253   /* Join any pending requests */
254   for (i = 0; i < priv->num_requests; i++) {
255     ReadRequest *req = &g_array_index (priv->requests, ReadRequest, i);
256     HANDLE ev = g_array_index (priv->request_events, HANDLE, i);
257     DWORD n;
258
259     if (!GetOverlappedResult (priv->pin_handle, &req->overlapped, &n, FALSE)) {
260       if (WaitForSingleObject (ev, 1000) == WAIT_OBJECT_0)
261         GetOverlappedResult (priv->pin_handle, &req->overlapped, &n, FALSE);
262     }
263   }
264
265   /* Clean up */
266   for (i = 0; i < G_N_ELEMENTS (priv->spare_buffers); i++) {
267     gst_buffer_unref (priv->spare_buffers[i]);
268     priv->spare_buffers[i] = NULL;
269   }
270
271   for (i = 0; i < priv->requests->len; i++) {
272     ReadRequest *req = &g_array_index (priv->requests, ReadRequest, i);
273     HANDLE ev = g_array_index (priv->request_events, HANDLE, i);
274
275     gst_buffer_unref (req->buf);
276
277     if (ev)
278       CloseHandle (ev);
279   }
280
281   g_array_free (priv->requests, TRUE);
282   priv->requests = NULL;
283
284   g_array_free (priv->request_events, TRUE);
285   priv->request_events = NULL;
286 }
287
288 static void
289 gst_ks_video_device_prepare_buffers (GstKsVideoDevice * self)
290 {
291   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
292   guint i;
293   guint frame_size;
294
295   g_assert (priv->cur_media_type != NULL);
296
297   gst_ks_video_device_clear_buffers (self);
298
299   priv->requests = g_array_sized_new (FALSE, TRUE, sizeof (ReadRequest),
300       priv->num_requests);
301   priv->request_events = g_array_sized_new (FALSE, TRUE, sizeof (HANDLE),
302       priv->num_requests + 1);
303
304   frame_size = gst_ks_video_device_get_frame_size (self);
305
306   for (i = 0; i < G_N_ELEMENTS (priv->spare_buffers); i++) {
307     priv->spare_buffers[i] = self->allocfunc (frame_size, KS_BUFFER_ALIGNMENT,
308         self->allocfunc_data);
309   }
310
311   for (i = 0; i < priv->num_requests; i++) {
312     ReadRequest req;
313     memset (&req, '0', sizeof (ReadRequest));
314
315     req.buf = self->allocfunc (frame_size, KS_BUFFER_ALIGNMENT,
316         self->allocfunc_data);
317
318     req.overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
319
320     g_array_append_val (priv->requests, req);
321     g_array_append_val (priv->request_events, req.overlapped.hEvent);
322   }
323
324   g_array_append_val (priv->request_events, priv->cancel_event);
325
326   /*
327    * REVISIT: Could probably remove this later, for now it's here to help
328    *          track down the case where we capture old frames. This has been
329    *          observed with UVC cameras, presumably with some system load.
330    */
331   priv->last_timestamp = GST_CLOCK_TIME_NONE;
332 }
333
334 static void
335 gst_ks_video_device_dump_supported_property_sets (GstKsVideoDevice * self,
336     const gchar * obj_name, const GUID * propsets, gulong propsets_len)
337 {
338   guint i;
339
340   GST_DEBUG ("%s supports %lu property set%s", obj_name, propsets_len,
341       (propsets_len != 1) ? "s" : "");
342
343   for (i = 0; i < propsets_len; i++) {
344     gchar *propset_name = ks_property_set_to_string (&propsets[i]);
345     GST_DEBUG ("[%d] %s", i, propset_name);
346     g_free (propset_name);
347   }
348 }
349
350 GstKsVideoDevice *
351 gst_ks_video_device_new (const gchar * device_path, GstKsClock * clock,
352     GstKsAllocFunction allocfunc, gpointer allocfunc_data)
353 {
354   GstKsVideoDevice *device;
355
356   device = g_object_new (GST_TYPE_KS_VIDEO_DEVICE,
357       "device-path", device_path, "clock", clock, NULL);
358   device->allocfunc = allocfunc;
359   device->allocfunc_data = allocfunc_data;
360
361   return device;
362 }
363
364 gboolean
365 gst_ks_video_device_open (GstKsVideoDevice * self)
366 {
367   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
368   GUID *propsets = NULL;
369   gulong propsets_len;
370   GList *cur;
371
372   g_assert (!priv->open);
373   g_assert (priv->dev_path != NULL);
374
375   /*
376    * Open the filter.
377    */
378   priv->filter_handle = CreateFile (priv->dev_path,
379       GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
380       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
381   if (!ks_is_valid_handle (priv->filter_handle))
382     goto error;
383
384   /*
385    * Query the filter for supported property sets.
386    */
387   if (ks_object_get_supported_property_sets (priv->filter_handle, &propsets,
388           &propsets_len)) {
389     gst_ks_video_device_dump_supported_property_sets (self, "filter",
390         propsets, propsets_len);
391     g_free (propsets);
392   } else {
393     GST_DEBUG ("failed to query filter for supported property sets");
394   }
395
396   /*
397    * Probe for supported media types.
398    */
399   priv->media_types = ks_video_probe_filter_for_caps (priv->filter_handle);
400   priv->cached_caps = gst_caps_new_empty ();
401
402   for (cur = priv->media_types; cur != NULL; cur = cur->next) {
403     KsVideoMediaType *media_type = cur->data;
404
405     gst_caps_append (priv->cached_caps,
406         gst_caps_copy (media_type->translated_caps));
407
408 #if 1
409     {
410       gchar *str;
411       str = gst_caps_to_string (media_type->translated_caps);
412       GST_DEBUG ("pin[%d]: found media type: %s", media_type->pin_id, str);
413       g_free (str);
414     }
415 #endif
416   }
417
418   priv->cancel_event = CreateEvent (NULL, TRUE, FALSE, NULL);
419
420   priv->open = TRUE;
421
422   return TRUE;
423
424 error:
425   g_free (priv->dev_path);
426   priv->dev_path = NULL;
427
428   return FALSE;
429 }
430
431 void
432 gst_ks_video_device_close (GstKsVideoDevice * self)
433 {
434   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
435   GList *cur;
436
437   gst_ks_video_device_reset_caps (self);
438
439   g_free (priv->dev_path);
440   priv->dev_path = NULL;
441
442   if (ks_is_valid_handle (priv->filter_handle)) {
443     CloseHandle (priv->filter_handle);
444     priv->filter_handle = INVALID_HANDLE_VALUE;
445   }
446
447   for (cur = priv->media_types; cur != NULL; cur = cur->next) {
448     KsVideoMediaType *mt = cur->data;
449     ks_video_media_type_free (mt);
450   }
451
452   if (priv->media_types != NULL) {
453     g_list_free (priv->media_types);
454     priv->media_types = NULL;
455   }
456
457   if (priv->cached_caps != NULL) {
458     gst_caps_unref (priv->cached_caps);
459     priv->cached_caps = NULL;
460   }
461
462   if (ks_is_valid_handle (priv->cancel_event))
463     CloseHandle (priv->cancel_event);
464   priv->cancel_event = INVALID_HANDLE_VALUE;
465
466   priv->open = FALSE;
467 }
468
469 GstCaps *
470 gst_ks_video_device_get_available_caps (GstKsVideoDevice * self)
471 {
472   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
473
474   g_assert (priv->open);
475
476   return gst_caps_ref (priv->cached_caps);
477 }
478
479 gboolean
480 gst_ks_video_device_has_caps (GstKsVideoDevice * self)
481 {
482   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
483
484   return (priv->cur_media_type != NULL) ? TRUE : FALSE;
485 }
486
487 static HANDLE
488 gst_ks_video_device_create_pin (GstKsVideoDevice * self,
489     KsVideoMediaType * media_type, gulong * num_outstanding)
490 {
491   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
492
493   HANDLE pin_handle = INVALID_HANDLE_VALUE;
494   KSPIN_CONNECT *pin_conn = NULL;
495   DWORD ret;
496   guint retry_count;
497
498   GUID *propsets = NULL;
499   gulong propsets_len;
500   gboolean supports_mem_transport = FALSE;
501
502   KSALLOCATOR_FRAMING *framing = NULL;
503   gulong framing_size = sizeof (KSALLOCATOR_FRAMING);
504   KSALLOCATOR_FRAMING_EX *framing_ex = NULL;
505   gulong alignment;
506
507   DWORD mem_transport;
508
509   /*
510    * Instantiate the pin.
511    */
512   pin_conn = ks_video_create_pin_conn_from_media_type (media_type);
513
514   for (retry_count = 0; retry_count != 5; retry_count++) {
515
516     GST_DEBUG ("calling KsCreatePin with pin_id = %d", media_type->pin_id);
517
518     ret = KsCreatePin (priv->filter_handle, pin_conn, GENERIC_READ,
519         &pin_handle);
520     if (ret != ERROR_NOT_READY)
521       break;
522
523     /* wait and retry, like the reference implementation does */
524     if (WaitForSingleObject (priv->cancel_event, 1000) == WAIT_OBJECT_0)
525       goto cancelled;
526   }
527
528   if (ret != ERROR_SUCCESS)
529     goto error_create_pin;
530
531   GST_DEBUG ("KsCreatePin succeeded, pin %p created", pin_handle);
532
533   g_free (pin_conn);
534   pin_conn = NULL;
535
536   /*
537    * Query the pin for supported property sets.
538    */
539   if (ks_object_get_supported_property_sets (pin_handle, &propsets,
540           &propsets_len)) {
541     guint i;
542
543     gst_ks_video_device_dump_supported_property_sets (self, "pin", propsets,
544         propsets_len);
545
546     for (i = 0; i < propsets_len; i++) {
547       if (IsEqualGUID (&propsets[i], &KSPROPSETID_MemoryTransport))
548         supports_mem_transport = TRUE;
549     }
550
551     g_free (propsets);
552   } else {
553     GST_DEBUG ("failed to query pin for supported property sets");
554   }
555
556   /*
557    * Figure out how many simultaneous requests it prefers.
558    *
559    * This is really important as it depends on the driver and the device.
560    * Doing too few will result in poor capture performance, whilst doing too
561    * many will make some drivers crash really horribly and leave you with a
562    * BSOD. I've experienced the latter with older Logitech drivers.
563    */
564   *num_outstanding = 0;
565   alignment = 0;
566
567   if (ks_object_get_property (pin_handle, KSPROPSETID_Connection,
568           KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX, (void *) &framing_ex, NULL,
569           NULL)) {
570     if (framing_ex->CountItems >= 1) {
571       *num_outstanding = framing_ex->FramingItem[0].Frames;
572       alignment = framing_ex->FramingItem[0].FileAlignment;
573     } else {
574       GST_DEBUG ("ignoring empty ALLOCATORFRAMING_EX");
575     }
576   } else {
577     GST_DEBUG ("query for ALLOCATORFRAMING_EX failed, trying "
578         "ALLOCATORFRAMING");
579
580     if (ks_object_get_property (pin_handle, KSPROPSETID_Connection,
581             KSPROPERTY_CONNECTION_ALLOCATORFRAMING, (void *) &framing,
582             &framing_size, NULL)) {
583       *num_outstanding = framing->Frames;
584       alignment = framing->FileAlignment;
585     } else {
586       GST_DEBUG ("query for ALLOCATORFRAMING failed");
587     }
588   }
589
590   GST_DEBUG ("num_outstanding: %lu alignment: 0x%08x", *num_outstanding,
591       (guint) alignment);
592
593   if (*num_outstanding == 0 || *num_outstanding > MAX_OUTSTANDING_FRAMES) {
594     GST_DEBUG ("setting number of allowable outstanding frames to 1");
595     *num_outstanding = 1;
596   }
597
598   g_free (framing);
599   framing = NULL;
600   g_free (framing_ex);
601   framing_ex = NULL;
602
603   /*
604    * TODO: We also need to respect alignment, but for now we just assume
605    *       that allocfunc provides the appropriate alignment...
606    */
607
608   /* Set the memory transport to use. */
609   if (supports_mem_transport) {
610     mem_transport = 0;          /* REVISIT: use the constant here */
611     if (!ks_object_set_property (pin_handle, KSPROPSETID_MemoryTransport,
612             KSPROPERTY_MEMORY_TRANSPORT, &mem_transport,
613             sizeof (mem_transport), NULL)) {
614       GST_DEBUG ("failed to set memory transport, sticking with the default");
615     }
616   }
617
618   /*
619    * Override the clock if we have one and the pin doesn't have any either.
620    */
621   if (priv->clock != NULL) {
622     HANDLE *cur_clock_handle = NULL;
623     gulong cur_clock_handle_size = sizeof (HANDLE);
624
625     if (ks_object_get_property (pin_handle, KSPROPSETID_Stream,
626             KSPROPERTY_STREAM_MASTERCLOCK, (gpointer *) & cur_clock_handle,
627             &cur_clock_handle_size, NULL)) {
628       GST_DEBUG ("current master clock handle: %p", *cur_clock_handle);
629       CloseHandle (*cur_clock_handle);
630       g_free (cur_clock_handle);
631     } else {
632       HANDLE new_clock_handle = gst_ks_clock_get_handle (priv->clock);
633
634       if (ks_object_set_property (pin_handle, KSPROPSETID_Stream,
635               KSPROPERTY_STREAM_MASTERCLOCK, &new_clock_handle,
636               sizeof (new_clock_handle), NULL)) {
637         gst_ks_clock_prepare (priv->clock);
638       } else {
639         GST_WARNING ("failed to set pin's master clock");
640       }
641     }
642   }
643
644   return pin_handle;
645
646   /* ERRORS */
647 error_create_pin:
648   {
649     gchar *str;
650
651     gst_ks_video_device_parse_win32_error ("KsCreatePin", ret, NULL, &str);
652     GST_ERROR ("%s", str);
653     g_free (str);
654
655     goto beach;
656   }
657 cancelled:
658 beach:
659   {
660     g_free (framing);
661     g_free (framing_ex);
662     if (ks_is_valid_handle (pin_handle))
663       CloseHandle (pin_handle);
664     g_free (pin_conn);
665
666     return INVALID_HANDLE_VALUE;
667   }
668 }
669
670 static void
671 gst_ks_video_device_close_current_pin (GstKsVideoDevice * self)
672 {
673   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
674
675   if (!ks_is_valid_handle (priv->pin_handle))
676     return;
677
678   gst_ks_video_device_set_state (self, KSSTATE_STOP, NULL);
679
680   CloseHandle (priv->pin_handle);
681   priv->pin_handle = INVALID_HANDLE_VALUE;
682 }
683
684 static void
685 gst_ks_video_device_reset_caps (GstKsVideoDevice * self)
686 {
687   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
688
689   gst_ks_video_device_close_current_pin (self);
690
691   ks_video_media_type_free (priv->cur_media_type);
692   priv->cur_media_type = NULL;
693
694   priv->width = priv->height = priv->fps_n = priv->fps_d = 0;
695
696   g_free (priv->rgb_swap_buf);
697   priv->rgb_swap_buf = NULL;
698
699   if (priv->cur_fixed_caps != NULL) {
700     gst_caps_unref (priv->cur_fixed_caps);
701     priv->cur_fixed_caps = NULL;
702   }
703 }
704
705 gboolean
706 gst_ks_video_device_set_caps (GstKsVideoDevice * self, GstCaps * caps)
707 {
708   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
709   GList *cur;
710   GstStructure *s;
711
712   /* State to be committed on success */
713   KsVideoMediaType *media_type = NULL;
714   gint width, height, fps_n, fps_d;
715   HANDLE pin_handle = INVALID_HANDLE_VALUE;
716
717   /* Reset? */
718   if (caps == NULL) {
719     gst_ks_video_device_reset_caps (self);
720     return TRUE;
721   }
722
723   /* Validate the caps */
724   if (!gst_caps_is_subset (caps, priv->cached_caps)) {
725     gchar *string_caps = gst_caps_to_string (caps);
726     gchar *string_c_caps = gst_caps_to_string (priv->cached_caps);
727
728     GST_ERROR ("caps (%s) is not a subset of device caps (%s)",
729         string_caps, string_c_caps);
730
731     g_free (string_caps);
732     g_free (string_c_caps);
733
734     goto error;
735   }
736
737   for (cur = priv->media_types; cur != NULL; cur = cur->next) {
738     KsVideoMediaType *mt = cur->data;
739
740     if (gst_caps_is_subset (caps, mt->translated_caps)) {
741       media_type = ks_video_media_type_dup (mt);
742       break;
743     }
744   }
745
746   if (media_type == NULL)
747     goto error;
748
749   s = gst_caps_get_structure (caps, 0);
750   if (!gst_structure_get_int (s, "width", &width) ||
751       !gst_structure_get_int (s, "height", &height) ||
752       !gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
753     gst_structure_get_boolean (s, "systemstream", &priv->is_muxed);
754     if (!priv->is_muxed) {
755       GST_ERROR ("Failed to get width/height/fps");
756       goto error;
757     }
758   } else {
759     if (!ks_video_fixate_media_type (media_type->range,
760             media_type->format, width, height, fps_n, fps_d))
761       goto error;
762   }
763
764   if (priv->cur_media_type != NULL) {
765     if (media_type->format_size == priv->cur_media_type->format_size &&
766         memcmp (media_type->format, priv->cur_media_type->format,
767             priv->cur_media_type->format_size) == 0) {
768       GST_DEBUG ("%s: re-using existing pin", G_STRFUNC);
769       goto same_caps;
770     } else {
771       GST_DEBUG ("%s: re-creating pin", G_STRFUNC);
772     }
773   }
774
775   gst_ks_video_device_close_current_pin (self);
776
777   pin_handle = gst_ks_video_device_create_pin (self, media_type,
778       &priv->num_requests);
779   if (!ks_is_valid_handle (pin_handle)) {
780     /* Re-create the old pin */
781     if (priv->cur_media_type != NULL)
782       priv->pin_handle = gst_ks_video_device_create_pin (self,
783           priv->cur_media_type, &priv->num_requests);
784     goto error;
785   }
786
787   /* Commit state: no turning back past this */
788   gst_ks_video_device_reset_caps (self);
789
790   priv->cur_media_type = media_type;
791   priv->width = width;
792   priv->height = height;
793   priv->fps_n = fps_n;
794   priv->fps_d = fps_d;
795
796   if (media_type->is_rgb)
797     priv->rgb_swap_buf = g_malloc (media_type->sample_size / priv->height);
798   else
799     priv->rgb_swap_buf = NULL;
800
801   priv->pin_handle = pin_handle;
802
803   priv->cur_fixed_caps = gst_caps_copy (caps);
804
805   return TRUE;
806
807 error:
808   {
809     ks_video_media_type_free (media_type);
810     return FALSE;
811   }
812 same_caps:
813   {
814     ks_video_media_type_free (media_type);
815     return TRUE;
816   }
817 }
818
819 gboolean
820 gst_ks_video_device_set_state (GstKsVideoDevice * self, KSSTATE state,
821     gulong * error_code)
822 {
823   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
824   KSSTATE initial_state;
825   gint addend;
826
827   g_assert (priv->cur_media_type != NULL);
828
829   if (state == priv->state)
830     return TRUE;
831
832   initial_state = priv->state;
833   addend = (state > priv->state) ? 1 : -1;
834
835   GST_DEBUG ("Initiating pin state change from %s to %s",
836       ks_state_to_string (priv->state), ks_state_to_string (state));
837
838   while (priv->state != state) {
839     KSSTATE next_state = priv->state + addend;
840
841     /* Skip the ACQUIRE step on the way down like DirectShow does */
842     if (addend < 0 && next_state == KSSTATE_ACQUIRE)
843       next_state = KSSTATE_STOP;
844
845     GST_DEBUG ("Changing pin state from %s to %s",
846         ks_state_to_string (priv->state), ks_state_to_string (next_state));
847
848     if (ks_object_set_connection_state (priv->pin_handle, next_state,
849             error_code)) {
850       priv->state = next_state;
851
852       GST_DEBUG ("Changed pin state to %s", ks_state_to_string (priv->state));
853
854       if (priv->state == KSSTATE_PAUSE && addend > 0)
855         gst_ks_video_device_prepare_buffers (self);
856       else if (priv->state == KSSTATE_STOP && addend < 0)
857         gst_ks_video_device_clear_buffers (self);
858     } else {
859       GST_WARNING ("Failed to change pin state to %s",
860           ks_state_to_string (next_state));
861
862       return FALSE;
863     }
864   }
865
866   GST_DEBUG ("Finished pin state change from %s to %s",
867       ks_state_to_string (initial_state), ks_state_to_string (state));
868
869   return TRUE;
870 }
871
872 static guint
873 gst_ks_video_device_get_frame_size (GstKsVideoDevice * self)
874 {
875   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
876
877   g_assert (priv->cur_media_type != NULL);
878
879   return priv->cur_media_type->sample_size;
880 }
881
882 GstClockTime
883 gst_ks_video_device_get_duration (GstKsVideoDevice * self)
884 {
885   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
886
887   g_assert (priv->cur_media_type != NULL);
888
889   return gst_util_uint64_scale_int (GST_SECOND, priv->fps_d, priv->fps_n);
890 }
891
892 gboolean
893 gst_ks_video_device_get_latency (GstKsVideoDevice * self,
894     GstClockTime * min_latency, GstClockTime * max_latency)
895 {
896   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
897
898   if (priv->cur_media_type == NULL)
899     return FALSE;
900
901   *min_latency =
902       gst_util_uint64_scale_int (GST_SECOND, priv->fps_d, priv->fps_n);
903   *max_latency = *min_latency;
904
905   return TRUE;
906 }
907
908 static gboolean
909 gst_ks_read_request_pick_buffer (GstKsVideoDevice * self, ReadRequest * req)
910 {
911   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
912   gboolean buffer_found = FALSE;
913   guint i;
914
915   buffer_found = gst_buffer_is_writable (req->buf)
916       && gst_buffer_is_all_memory_writable (req->buf);
917
918   for (i = 0; !buffer_found && i < G_N_ELEMENTS (priv->spare_buffers); i++) {
919     if (gst_buffer_is_writable (priv->spare_buffers[i])
920         && gst_buffer_is_all_memory_writable (priv->spare_buffers[i])) {
921       GstBuffer *hold;
922
923       hold = req->buf;
924       req->buf = priv->spare_buffers[i];
925       priv->spare_buffers[i] = hold;
926
927       buffer_found = TRUE;
928     }
929   }
930
931   if (!buffer_found) {
932     gst_buffer_unref (req->buf);
933     req->buf = self->allocfunc (gst_ks_video_device_get_frame_size (self),
934         KS_BUFFER_ALIGNMENT, self->allocfunc_data);
935   }
936
937   if (req->buf != NULL) {
938     GST_BUFFER_FLAGS (req->buf) = 0;
939     return TRUE;
940   } else {
941     return FALSE;
942   }
943 }
944
945 static gboolean
946 gst_ks_video_device_request_frame (GstKsVideoDevice * self, ReadRequest * req,
947     gulong * error_code, gchar ** error_str)
948 {
949   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
950   HANDLE event;
951   KSSTREAM_READ_PARAMS *params;
952   BOOL success;
953   DWORD bytes_returned = 0;
954   GstMapInfo info;
955
956   if (!gst_ks_read_request_pick_buffer (self, req))
957     goto error_pick_buffer;
958
959   /* Reset the OVERLAPPED structure */
960   event = req->overlapped.hEvent;
961   memset (&req->overlapped, 0, sizeof (OVERLAPPED));
962   req->overlapped.hEvent = event;
963
964   /* Fill out KSSTREAM_HEADER and KS_FRAME_INFO */
965   params = &req->params;
966   memset (params, 0, sizeof (KSSTREAM_READ_PARAMS));
967
968   if (!gst_buffer_map (req->buf, &info, GST_MAP_WRITE))
969     goto map_failed;
970
971   params->header.Size = sizeof (KSSTREAM_HEADER);
972   if (!priv->is_muxed) {
973     params->header.Size += sizeof (KS_FRAME_INFO);
974   }
975   params->header.PresentationTime.Numerator = 1;
976   params->header.PresentationTime.Denominator = 1;
977   params->header.FrameExtent = gst_ks_video_device_get_frame_size (self);
978   params->header.Data = info.data;
979   params->frame_info.ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
980
981   success = DeviceIoControl (priv->pin_handle, IOCTL_KS_READ_STREAM, NULL, 0,
982       params, params->header.Size, &bytes_returned, &req->overlapped);
983   gst_buffer_unmap (req->buf, &info);
984   if (!success && GetLastError () != ERROR_IO_PENDING)
985     goto error_ioctl;
986
987   return TRUE;
988
989   /* ERRORS */
990 error_pick_buffer:
991   {
992     if (error_code != NULL)
993       *error_code = 0;
994     if (error_str != NULL)
995       *error_str = NULL;
996     return FALSE;
997   }
998 error_ioctl:
999   {
1000     gst_ks_video_device_parse_win32_error ("DeviceIoControl", GetLastError (),
1001         error_code, error_str);
1002     return FALSE;
1003   }
1004 map_failed:
1005   {
1006     return FALSE;
1007   }
1008 }
1009
1010 GstFlowReturn
1011 gst_ks_video_device_read_frame (GstKsVideoDevice * self, GstBuffer ** buf,
1012     GstClockTime * presentation_time, gulong * error_code, gchar ** error_str)
1013 {
1014   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1015   guint req_idx;
1016   DWORD wait_ret;
1017   BOOL success;
1018   DWORD bytes_returned;
1019
1020   g_assert (priv->cur_media_type != NULL);
1021
1022   /* First time we're called, submit the requests. */
1023   if (G_UNLIKELY (!priv->requests_submitted)) {
1024     priv->requests_submitted = TRUE;
1025
1026     for (req_idx = 0; req_idx < priv->num_requests; req_idx++) {
1027       ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx);
1028
1029       if (!gst_ks_video_device_request_frame (self, req, error_code, error_str))
1030         goto error_request_failed;
1031     }
1032   }
1033
1034   *buf = NULL;
1035
1036   do {
1037     /* Wait for either a request to complete, a cancel or a timeout */
1038     wait_ret = WaitForMultipleObjects (priv->request_events->len,
1039         (HANDLE *) priv->request_events->data, FALSE, READ_TIMEOUT);
1040     if (wait_ret == WAIT_TIMEOUT)
1041       goto error_timeout;
1042     else if (wait_ret == WAIT_FAILED)
1043       goto error_wait;
1044
1045     /* Stopped? */
1046     if (WaitForSingleObject (priv->cancel_event, 0) == WAIT_OBJECT_0)
1047       goto error_cancel;
1048
1049     /* Find the last ReadRequest that finished and get the result, immediately
1050      * re-issuing each request that has completed. */
1051     for (req_idx = wait_ret - WAIT_OBJECT_0;
1052         req_idx < priv->num_requests; req_idx++) {
1053       ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx);
1054
1055       /*
1056        * Completed? WaitForMultipleObjects() returns the lowest index if
1057        * multiple objects are in the signaled state, and we know that requests
1058        * are processed one by one so there's no point in looking further once
1059        * we've found the first that's non-signaled.
1060        */
1061       if (WaitForSingleObject (req->overlapped.hEvent, 0) != WAIT_OBJECT_0)
1062         break;
1063
1064       success = GetOverlappedResult (priv->pin_handle, &req->overlapped,
1065           &bytes_returned, TRUE);
1066
1067       ResetEvent (req->overlapped.hEvent);
1068
1069       if (success) {
1070         KSSTREAM_HEADER *hdr = &req->params.header;
1071         KS_FRAME_INFO *frame_info = &req->params.frame_info;
1072         GstClockTime timestamp = GST_CLOCK_TIME_NONE;
1073         GstClockTime duration = GST_CLOCK_TIME_NONE;
1074
1075         if (hdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID)
1076           timestamp = hdr->PresentationTime.Time * 100;
1077
1078         if (hdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DURATIONVALID)
1079           duration = hdr->Duration * 100;
1080
1081         UNREF_BUFFER (buf);
1082
1083         if (G_LIKELY (hdr->DataUsed != 0)) {
1084           /* Assume it's a good frame */
1085           gst_buffer_set_size (req->buf, hdr->DataUsed);
1086           *buf = gst_buffer_ref (req->buf);
1087         }
1088
1089         if (G_LIKELY (presentation_time != NULL))
1090           *presentation_time = timestamp;
1091
1092         if (G_UNLIKELY (GST_DEBUG_IS_ENABLED ())) {
1093           gchar *options_flags_str =
1094               ks_options_flags_to_string (hdr->OptionsFlags);
1095
1096           GST_DEBUG ("PictureNumber=%" G_GUINT64_FORMAT ", DropCount=%"
1097               G_GUINT64_FORMAT ", PresentationTime=%" GST_TIME_FORMAT
1098               ", Duration=%" GST_TIME_FORMAT ", OptionsFlags=%s: %lu bytes",
1099               frame_info->PictureNumber, frame_info->DropCount,
1100               GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration),
1101               options_flags_str, hdr->DataUsed);
1102
1103           g_free (options_flags_str);
1104         }
1105
1106         /* Protect against old frames. This should never happen, see previous
1107          * comment on last_timestamp. */
1108         if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (timestamp))) {
1109           if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->last_timestamp) &&
1110                   timestamp < priv->last_timestamp)) {
1111             GST_INFO ("got an old frame (last_timestamp=%" GST_TIME_FORMAT
1112                 ", timestamp=%" GST_TIME_FORMAT ")",
1113                 GST_TIME_ARGS (priv->last_timestamp),
1114                 GST_TIME_ARGS (timestamp));
1115             UNREF_BUFFER (buf);
1116           } else {
1117             priv->last_timestamp = timestamp;
1118           }
1119         }
1120       } else if (GetLastError () != ERROR_OPERATION_ABORTED) {
1121         goto error_get_result;
1122       }
1123
1124       /* Submit a new request immediately */
1125       if (!gst_ks_video_device_request_frame (self, req, error_code, error_str))
1126         goto error_request_failed;
1127     }
1128   } while (*buf == NULL);
1129
1130   return GST_FLOW_OK;
1131
1132   /* ERRORS */
1133 error_request_failed:
1134   {
1135     UNREF_BUFFER (buf);
1136
1137     return GST_FLOW_ERROR;
1138   }
1139 error_timeout:
1140   {
1141     GST_DEBUG ("IOCTL_KS_READ_STREAM timed out");
1142
1143     if (error_code != NULL)
1144       *error_code = 0;
1145     if (error_str != NULL)
1146       *error_str = NULL;
1147
1148     return GST_FLOW_CUSTOM_ERROR;
1149   }
1150 error_wait:
1151   {
1152     gst_ks_video_device_parse_win32_error ("WaitForMultipleObjects",
1153         GetLastError (), error_code, error_str);
1154
1155     return GST_FLOW_ERROR;
1156   }
1157 error_cancel:
1158   {
1159     if (error_code != NULL)
1160       *error_code = 0;
1161     if (error_str != NULL)
1162       *error_str = NULL;
1163
1164     return GST_FLOW_FLUSHING;
1165   }
1166 error_get_result:
1167   {
1168     gst_ks_video_device_parse_win32_error ("GetOverlappedResult",
1169         GetLastError (), error_code, error_str);
1170
1171     return GST_FLOW_ERROR;
1172   }
1173 }
1174
1175 gboolean
1176 gst_ks_video_device_postprocess_frame (GstKsVideoDevice * self,
1177     GstBuffer ** bufptr)
1178 {
1179   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1180   GstBuffer *buf = *bufptr;
1181
1182   /* If it's RGB we need to flip the image */
1183   if (priv->rgb_swap_buf != NULL) {
1184     GstMapInfo info;
1185     gint stride, line;
1186     guint8 *dst, *src;
1187
1188     /* Need to make the buffer writable because
1189      * the pseudo-bufferpool of requests keeps a ref */
1190     buf = gst_buffer_make_writable (buf);
1191
1192     if (!gst_buffer_map (buf, &info, GST_MAP_READWRITE))
1193       return FALSE;
1194
1195     stride = info.size / priv->height;
1196     dst = info.data;
1197     src = info.data + info.size - stride;
1198
1199     for (line = 0; line < priv->height / 2; line++) {
1200       memcpy (priv->rgb_swap_buf, dst, stride);
1201
1202       memcpy (dst, src, stride);
1203       memcpy (src, priv->rgb_swap_buf, stride);
1204
1205       dst += stride;
1206       src -= stride;
1207     }
1208
1209     gst_buffer_unmap (buf, &info);
1210   }
1211   *bufptr = buf;
1212
1213   return TRUE;
1214 }
1215
1216 void
1217 gst_ks_video_device_cancel (GstKsVideoDevice * self)
1218 {
1219   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1220
1221   SetEvent (priv->cancel_event);
1222 }
1223
1224 void
1225 gst_ks_video_device_cancel_stop (GstKsVideoDevice * self)
1226 {
1227   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1228
1229   ResetEvent (priv->cancel_event);
1230 }
1231
1232 gboolean
1233 gst_ks_video_device_stream_is_muxed (GstKsVideoDevice * self)
1234 {
1235   GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1236
1237   return priv->is_muxed;
1238 }