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