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