1 /*
2 * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3 * 2009 Andres Colubri <andres.colubri@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:element-ksvideosrc
23 * @title: ksvideosrc
24 *
25 * Provides low-latency video capture from WDM cameras on Windows.
26 *
27 * ## Example pipelines
28 * |[
29 * gst-launch-1.0 -v ksvideosrc do-stats=TRUE ! videoconvert ! dshowvideosink
30 * ]| Capture from a camera and render using dshowvideosink.
31 * |[
32 * gst-launch-1.0 -v ksvideosrc do-stats=TRUE ! image/jpeg, width=640, height=480
33 * ! jpegdec ! videoconvert ! dshowvideosink
34 * ]| Capture from an MJPEG camera and render using dshowvideosink.
35 *
36 */
37
38 #ifdef HAVE_CONFIG_H
39 # include <config.h>
40 #endif
41
42 #include "gstksvideosrc.h"
43
44 #include "gstksclock.h"
45 #include "gstksvideodevice.h"
46 #include "kshelpers.h"
47 #include "ksvideohelpers.h"
48 #include "ksdeviceprovider.h"
49
50 #define DEFAULT_DEVICE_PATH NULL
51 #define DEFAULT_DEVICE_NAME NULL
52 #define DEFAULT_DEVICE_INDEX -1
53 #define DEFAULT_DO_STATS FALSE
54 #define DEFAULT_ENABLE_QUIRKS TRUE
55
56 enum
57 {
58 PROP_0,
59 PROP_DEVICE_PATH,
60 PROP_DEVICE_NAME,
61 PROP_DEVICE_INDEX,
62 PROP_DO_STATS,
63 PROP_FPS,
64 PROP_ENABLE_QUIRKS,
65 };
66
67 GST_DEBUG_CATEGORY (gst_ks_debug);
68 #define GST_CAT_DEFAULT gst_ks_debug
69
70 #define KS_WORKER_LOCK(priv) g_mutex_lock (&priv->worker_lock)
71 #define KS_WORKER_UNLOCK(priv) g_mutex_unlock (&priv->worker_lock)
72 #define KS_WORKER_WAIT(priv) \
73 g_cond_wait (&priv->worker_notify_cond, &priv->worker_lock)
74 #define KS_WORKER_NOTIFY(priv) g_cond_signal (&priv->worker_notify_cond)
75 #define KS_WORKER_WAIT_FOR_RESULT(priv) \
76 g_cond_wait (&priv->worker_result_cond, &priv->worker_lock)
77 #define KS_WORKER_NOTIFY_RESULT(priv) \
78 g_cond_signal (&priv->worker_result_cond)
79
80 typedef enum
81 {
82 KS_WORKER_STATE_STARTING,
83 KS_WORKER_STATE_READY,
84 KS_WORKER_STATE_STOPPING,
85 KS_WORKER_STATE_ERROR,
86 } KsWorkerState;
87
88 struct _GstKsVideoSrcPrivate
89 {
90 /* Properties */
91 gchar *device_path;
92 gchar *device_name;
93 gint device_index;
94 gboolean do_stats;
95 gboolean enable_quirks;
96
97 /* State */
98 GstKsClock *ksclock;
99 GstKsVideoDevice *device;
100
101 gboolean running;
102
103 /* Worker thread */
104 GThread *worker_thread;
105 GMutex worker_lock;
106 GCond worker_notify_cond;
107 GCond worker_result_cond;
108 KsWorkerState worker_state;
109
110 GstCaps *worker_pending_caps;
111 gboolean worker_setcaps_result;
112
113 gboolean worker_pending_run;
114 gboolean worker_run_result;
115
116 gulong worker_error_code;
117
118 /* Statistics */
119 GstClockTime last_sampling;
120 guint count;
121 guint fps;
122 };
123
124 #define GST_KS_VIDEO_SRC_GET_PRIVATE(o) ((o)->priv)
125
126 static void gst_ks_video_src_finalize (GObject * object);
127 static void gst_ks_video_src_get_property (GObject * object, guint prop_id,
128 GValue * value, GParamSpec * pspec);
129 static void gst_ks_video_src_set_property (GObject * object, guint prop_id,
130 const GValue * value, GParamSpec * pspec);
131
132 G_GNUC_UNUSED static GArray
133 * gst_ks_video_src_get_device_name_values (GstKsVideoSrc * self);
134 static void gst_ks_video_src_reset (GstKsVideoSrc * self);
135
136 static GstStateChangeReturn gst_ks_video_src_change_state (GstElement * element,
137 GstStateChange transition);
138 static gboolean gst_ks_video_src_set_clock (GstElement * element,
139 GstClock * clock);
140
141 static GstCaps *gst_ks_video_src_get_caps (GstBaseSrc * basesrc,
142 GstCaps * filter);
143 static gboolean gst_ks_video_src_set_caps (GstBaseSrc * basesrc,
144 GstCaps * caps);
145 static GstCaps *gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
146 static gboolean gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query);
147 static gboolean gst_ks_video_src_unlock (GstBaseSrc * basesrc);
148 static gboolean gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc);
149
150 static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc,
151 GstBuffer ** buffer);
152 static GstBuffer *gst_ks_video_src_alloc_buffer (guint size, guint alignment,
153 gpointer user_data);
154
155 G_DEFINE_TYPE_WITH_PRIVATE (GstKsVideoSrc, gst_ks_video_src, GST_TYPE_PUSH_SRC);
156
157 static GstKsVideoSrcClass *parent_class = NULL;
158
159 static void
gst_ks_video_src_class_init(GstKsVideoSrcClass * klass)160 gst_ks_video_src_class_init (GstKsVideoSrcClass * klass)
161 {
162 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
163 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
164 GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
165 GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
166
167 parent_class = g_type_class_peek_parent (klass);
168
169 gst_element_class_set_static_metadata (gstelement_class, "KsVideoSrc",
170 "Source/Video/Hardware",
171 "Stream data from a video capture device through Windows kernel streaming",
172 "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>\n"
173 "Haakon Sporsheim <hakon.sporsheim@tandberg.com>\n"
174 "Andres Colubri <andres.colubri@gmail.com>");
175
176 gst_element_class_add_pad_template (gstelement_class,
177 gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
178 ks_video_get_all_caps ()));
179
180 gobject_class->finalize = gst_ks_video_src_finalize;
181 gobject_class->get_property = gst_ks_video_src_get_property;
182 gobject_class->set_property = gst_ks_video_src_set_property;
183
184 gstelement_class->change_state =
185 GST_DEBUG_FUNCPTR (gst_ks_video_src_change_state);
186 gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_clock);
187
188 gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_get_caps);
189 gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_caps);
190 gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_ks_video_src_fixate);
191 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_ks_video_src_query);
192 gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock);
193 gstbasesrc_class->unlock_stop =
194 GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock_stop);
195
196 gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_ks_video_src_create);
197
198 g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
199 g_param_spec_string ("device-path", "Device Path",
200 "The device path", DEFAULT_DEVICE_PATH,
201 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
202 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
203 g_param_spec_string ("device-name", "Device Name",
204 "The human-readable device name", DEFAULT_DEVICE_NAME,
205 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
206 g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
207 g_param_spec_int ("device-index", "Device Index",
208 "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
209 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
210 g_object_class_install_property (gobject_class, PROP_DO_STATS,
211 g_param_spec_boolean ("do-stats", "Enable statistics",
212 "Enable logging of statistics", DEFAULT_DO_STATS,
213 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
214 g_object_class_install_property (gobject_class, PROP_FPS,
215 g_param_spec_int ("fps", "Frames per second",
216 "Last measured framerate, if statistics are enabled",
217 -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
218 g_object_class_install_property (gobject_class, PROP_ENABLE_QUIRKS,
219 g_param_spec_boolean ("enable-quirks", "Enable quirks",
220 "Enable driver-specific quirks", DEFAULT_ENABLE_QUIRKS,
221 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
222 }
223
224 static void
gst_ks_video_src_init(GstKsVideoSrc * self)225 gst_ks_video_src_init (GstKsVideoSrc * self)
226 {
227 GstBaseSrc *basesrc = GST_BASE_SRC (self);
228 GstKsVideoSrcPrivate *priv;
229
230 self->priv = gst_ks_video_src_get_instance_private (self);
231 priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
232
233 gst_base_src_set_live (basesrc, TRUE);
234 gst_base_src_set_format (basesrc, GST_FORMAT_TIME);
235
236 gst_ks_video_src_reset (self);
237
238 priv->device_path = DEFAULT_DEVICE_PATH;
239 priv->device_name = DEFAULT_DEVICE_NAME;
240 priv->device_index = DEFAULT_DEVICE_INDEX;
241 priv->do_stats = DEFAULT_DO_STATS;
242 priv->enable_quirks = DEFAULT_ENABLE_QUIRKS;
243 }
244
245 static void
gst_ks_video_src_finalize(GObject * object)246 gst_ks_video_src_finalize (GObject * object)
247 {
248 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
249 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
250
251 g_free (priv->device_name);
252 g_free (priv->device_path);
253
254 G_OBJECT_CLASS (parent_class)->finalize (object);
255 }
256
257 static void
gst_ks_video_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)258 gst_ks_video_src_get_property (GObject * object, guint prop_id,
259 GValue * value, GParamSpec * pspec)
260 {
261 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
262 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
263
264 switch (prop_id) {
265 case PROP_DEVICE_PATH:
266 g_value_set_string (value, priv->device_path);
267 break;
268 case PROP_DEVICE_NAME:
269 g_value_set_string (value, priv->device_name);
270 break;
271 case PROP_DEVICE_INDEX:
272 g_value_set_int (value, priv->device_index);
273 break;
274 case PROP_DO_STATS:
275 GST_OBJECT_LOCK (object);
276 g_value_set_boolean (value, priv->do_stats);
277 GST_OBJECT_UNLOCK (object);
278 break;
279 case PROP_FPS:
280 GST_OBJECT_LOCK (object);
281 g_value_set_int (value, priv->fps);
282 GST_OBJECT_UNLOCK (object);
283 break;
284 case PROP_ENABLE_QUIRKS:
285 g_value_set_boolean (value, priv->enable_quirks);
286 break;
287 default:
288 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
289 break;
290 }
291 }
292
293 static void
gst_ks_video_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)294 gst_ks_video_src_set_property (GObject * object, guint prop_id,
295 const GValue * value, GParamSpec * pspec)
296 {
297 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
298 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
299
300 switch (prop_id) {
301 case PROP_DEVICE_PATH:
302 {
303 const gchar *device_path = g_value_get_string (value);
304 g_free (priv->device_path);
305 priv->device_path = NULL;
306 if (device_path && strlen (device_path) != 0)
307 priv->device_path = g_value_dup_string (value);
308 }
309 break;
310 case PROP_DEVICE_NAME:
311 {
312 const gchar *device_name = g_value_get_string (value);
313 g_free (priv->device_name);
314 priv->device_name = NULL;
315 if (device_name && strlen (device_name) != 0)
316 priv->device_name = g_strdup (device_name);
317 }
318 break;
319 case PROP_DEVICE_INDEX:
320 priv->device_index = g_value_get_int (value);
321 break;
322 case PROP_DO_STATS:
323 GST_OBJECT_LOCK (object);
324 priv->do_stats = g_value_get_boolean (value);
325 GST_OBJECT_UNLOCK (object);
326 break;
327 case PROP_ENABLE_QUIRKS:
328 priv->enable_quirks = g_value_get_boolean (value);
329 break;
330 default:
331 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332 break;
333 }
334 }
335
336 static void
gst_ks_video_src_reset(GstKsVideoSrc * self)337 gst_ks_video_src_reset (GstKsVideoSrc * self)
338 {
339 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
340
341 /* Reset statistics */
342 priv->last_sampling = GST_CLOCK_TIME_NONE;
343 priv->count = 0;
344 priv->fps = -1;
345
346 priv->running = FALSE;
347 }
348
349 static void
gst_ks_video_src_apply_driver_quirks(GstKsVideoSrc * self)350 gst_ks_video_src_apply_driver_quirks (GstKsVideoSrc * self)
351 {
352 HMODULE mod;
353
354 /*
355 * Logitech's driver software injects the following DLL into all processes
356 * spawned. This DLL does some nasty tricks, sitting in between the
357 * application and the low-level ntdll API (NtCreateFile, NtClose,
358 * NtDeviceIoControlFile, NtDuplicateObject, etc.), making all sorts
359 * of assumptions.
360 *
361 * The only regression that this quirk causes is that the video effects
362 * feature doesn't work.
363 */
364 mod = GetModuleHandle ("LVPrcInj.dll");
365 if (mod != NULL) {
366 GST_DEBUG_OBJECT (self, "Logitech DLL detected, neutralizing it");
367
368 /*
369 * We know that no-one's actually keeping this handle around to decrement
370 * its reference count, so we'll take care of that job. The DLL's DllMain
371 * implementation takes care of rolling back changes when it gets unloaded,
372 * so this seems to be the cleanest and most future-proof way that we can
373 * get rid of it...
374 */
375 FreeLibrary (mod);
376
377 /* Paranoia: verify that it's no longer there */
378 mod = GetModuleHandle ("LVPrcInj.dll");
379 if (mod != NULL)
380 GST_WARNING_OBJECT (self, "failed to neutralize Logitech DLL");
381 }
382 }
383
384 /*FIXME: when we have a devices API replacement */
385 G_GNUC_UNUSED static GArray *
gst_ks_video_src_get_device_name_values(GstKsVideoSrc * self)386 gst_ks_video_src_get_device_name_values (GstKsVideoSrc * self)
387 {
388 GList *devices, *cur;
389 GArray *array = g_array_new (TRUE, TRUE, sizeof (GValue));
390
391 devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
392 if (devices == NULL)
393 return array;
394
395 devices = ks_video_device_list_sort_cameras_first (devices);
396
397 for (cur = devices; cur != NULL; cur = cur->next) {
398 GValue value = { 0, };
399 KsDeviceEntry *entry = cur->data;
400
401 g_value_init (&value, G_TYPE_STRING);
402 g_value_set_string (&value, entry->name);
403 g_array_append_val (array, value);
404 g_value_unset (&value);
405
406 ks_device_entry_free (entry);
407 }
408
409 g_list_free (devices);
410 return array;
411 }
412
413 static gboolean
gst_ks_video_src_open_device(GstKsVideoSrc * self)414 gst_ks_video_src_open_device (GstKsVideoSrc * self)
415 {
416 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
417 GstKsVideoDevice *device = NULL;
418 GList *devices, *cur;
419
420 g_assert (priv->device == NULL);
421
422 devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
423 if (devices == NULL)
424 goto error_no_devices;
425
426 devices = ks_video_device_list_sort_cameras_first (devices);
427
428 for (cur = devices; cur != NULL; cur = cur->next) {
429 KsDeviceEntry *entry = cur->data;
430
431 GST_DEBUG_OBJECT (self, "device %d: name='%s' path='%s'",
432 entry->index, entry->name, entry->path);
433 }
434
435 for (cur = devices; cur != NULL; cur = cur->next) {
436 KsDeviceEntry *entry = cur->data;
437 gboolean match;
438
439 if (device != NULL) {
440 ks_device_entry_free (entry);
441 continue;
442 }
443 if (priv->device_path != NULL) {
444 match = g_ascii_strcasecmp (entry->path, priv->device_path) == 0;
445 } else if (priv->device_name != NULL) {
446 match = g_ascii_strcasecmp (entry->name, priv->device_name) == 0;
447 } else if (priv->device_index >= 0) {
448 match = entry->index == priv->device_index;
449 } else {
450 match = TRUE; /* pick the first entry */
451 }
452
453 if (match) {
454 priv->ksclock = g_object_new (GST_TYPE_KS_CLOCK, NULL);
455 if (priv->ksclock != NULL && gst_ks_clock_open (priv->ksclock)) {
456 GstClock *clock = GST_ELEMENT_CLOCK (self);
457 if (clock != NULL)
458 gst_ks_clock_provide_master_clock (priv->ksclock, clock);
459 } else {
460 GST_WARNING_OBJECT (self, "failed to create/open KsClock");
461 g_object_unref (priv->ksclock);
462 priv->ksclock = NULL;
463 }
464
465 device = gst_ks_video_device_new (entry->path, priv->ksclock,
466 gst_ks_video_src_alloc_buffer, self);
467 }
468
469 ks_device_entry_free (entry);
470 }
471
472 g_list_free (devices);
473
474 if (device == NULL)
475 goto error_no_match;
476
477 if (!gst_ks_video_device_open (device))
478 goto error_open;
479
480 priv->device = device;
481
482 return TRUE;
483
484 /* ERRORS */
485 error_no_devices:
486 {
487 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
488 ("No video capture devices found"), (NULL));
489 return FALSE;
490 }
491 error_no_match:
492 {
493 if (priv->device_path != NULL) {
494 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
495 ("Specified video capture device with path '%s' not found",
496 priv->device_path), (NULL));
497 } else if (priv->device_name != NULL) {
498 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
499 ("Specified video capture device with name '%s' not found",
500 priv->device_name), (NULL));
501 } else {
502 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
503 ("Specified video capture device with index %d not found",
504 priv->device_index), (NULL));
505 }
506 return FALSE;
507 }
508 error_open:
509 {
510 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
511 ("Failed to open device"), (NULL));
512 g_object_unref (device);
513 return FALSE;
514 }
515 }
516
517 static void
gst_ks_video_src_close_device(GstKsVideoSrc * self)518 gst_ks_video_src_close_device (GstKsVideoSrc * self)
519 {
520 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
521
522 g_assert (priv->device != NULL);
523
524 gst_ks_video_device_close (priv->device);
525 g_object_unref (priv->device);
526 priv->device = NULL;
527
528 if (priv->ksclock != NULL) {
529 gst_ks_clock_close (priv->ksclock);
530 g_object_unref (priv->ksclock);
531 priv->ksclock = NULL;
532 }
533
534 gst_ks_video_src_reset (self);
535 }
536
537 /*
538 * Worker thread that takes care of starting, configuring and stopping things.
539 *
540 * This is needed because Logitech's driver software injects a DLL that
541 * intercepts API functions like NtCreateFile, NtClose, NtDeviceIoControlFile
542 * and NtDuplicateObject so that they can provide in-place video effects to
543 * existing applications. Their assumption is that at least one thread tainted
544 * by their code stays around for the lifetime of the capture.
545 */
546 static gpointer
gst_ks_video_src_worker_func(gpointer data)547 gst_ks_video_src_worker_func (gpointer data)
548 {
549 GstKsVideoSrc *self = data;
550 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
551
552 if (!gst_ks_video_src_open_device (self))
553 goto open_failed;
554
555 KS_WORKER_LOCK (priv);
556 priv->worker_state = KS_WORKER_STATE_READY;
557 KS_WORKER_NOTIFY_RESULT (priv);
558
559 while (priv->worker_state != KS_WORKER_STATE_STOPPING) {
560 KS_WORKER_WAIT (priv);
561
562 if (priv->worker_pending_caps != NULL) {
563 priv->worker_setcaps_result =
564 gst_ks_video_device_set_caps (priv->device,
565 priv->worker_pending_caps);
566
567 priv->worker_pending_caps = NULL;
568 KS_WORKER_NOTIFY_RESULT (priv);
569 } else if (priv->worker_pending_run) {
570 if (priv->ksclock != NULL)
571 gst_ks_clock_start (priv->ksclock);
572 priv->worker_run_result = gst_ks_video_device_set_state (priv->device,
573 KSSTATE_RUN, &priv->worker_error_code);
574
575 priv->worker_pending_run = FALSE;
576 KS_WORKER_NOTIFY_RESULT (priv);
577 }
578 }
579
580 KS_WORKER_UNLOCK (priv);
581
582 gst_ks_video_src_close_device (self);
583
584 return NULL;
585
586 /* ERRORS */
587 open_failed:
588 {
589 KS_WORKER_LOCK (priv);
590 priv->worker_state = KS_WORKER_STATE_ERROR;
591 KS_WORKER_NOTIFY_RESULT (priv);
592 KS_WORKER_UNLOCK (priv);
593
594 return NULL;
595 }
596 }
597
598 static gboolean
gst_ks_video_src_start_worker(GstKsVideoSrc * self)599 gst_ks_video_src_start_worker (GstKsVideoSrc * self)
600 {
601 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
602 gboolean result;
603
604 g_mutex_init (&priv->worker_lock);
605 g_cond_init (&priv->worker_notify_cond);
606 g_cond_init (&priv->worker_result_cond);
607
608 priv->worker_pending_caps = NULL;
609 priv->worker_pending_run = FALSE;
610
611 priv->worker_state = KS_WORKER_STATE_STARTING;
612 priv->worker_thread =
613 g_thread_new ("ks-worker", gst_ks_video_src_worker_func, self);
614
615 KS_WORKER_LOCK (priv);
616 while (priv->worker_state < KS_WORKER_STATE_READY)
617 KS_WORKER_WAIT_FOR_RESULT (priv);
618 result = priv->worker_state == KS_WORKER_STATE_READY;
619 KS_WORKER_UNLOCK (priv);
620
621 return result;
622 }
623
624 static void
gst_ks_video_src_stop_worker(GstKsVideoSrc * self)625 gst_ks_video_src_stop_worker (GstKsVideoSrc * self)
626 {
627 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
628
629 KS_WORKER_LOCK (priv);
630 priv->worker_state = KS_WORKER_STATE_STOPPING;
631 KS_WORKER_NOTIFY (priv);
632 KS_WORKER_UNLOCK (priv);
633
634 g_thread_join (priv->worker_thread);
635 priv->worker_thread = NULL;
636
637 g_cond_clear (&priv->worker_result_cond);
638 g_cond_clear (&priv->worker_notify_cond);
639 g_mutex_clear (&priv->worker_lock);
640 }
641
642 static GstStateChangeReturn
gst_ks_video_src_change_state(GstElement * element,GstStateChange transition)643 gst_ks_video_src_change_state (GstElement * element, GstStateChange transition)
644 {
645 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
646 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
647 GstStateChangeReturn ret;
648
649 switch (transition) {
650 case GST_STATE_CHANGE_NULL_TO_READY:
651 if (priv->enable_quirks)
652 gst_ks_video_src_apply_driver_quirks (self);
653 if (!gst_ks_video_src_start_worker (self))
654 goto open_failed;
655 break;
656 default:
657 break;
658 }
659
660 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
661
662 switch (transition) {
663 case GST_STATE_CHANGE_READY_TO_NULL:
664 gst_ks_video_src_stop_worker (self);
665 break;
666 default:
667 break;
668 }
669
670 return ret;
671
672 /* ERRORS */
673 open_failed:
674 {
675 gst_ks_video_src_stop_worker (self);
676 return GST_STATE_CHANGE_FAILURE;
677 }
678 }
679
680 static gboolean
gst_ks_video_src_set_clock(GstElement * element,GstClock * clock)681 gst_ks_video_src_set_clock (GstElement * element, GstClock * clock)
682 {
683 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
684 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
685
686 GST_OBJECT_LOCK (element);
687 if (clock != NULL && priv->ksclock != NULL)
688 gst_ks_clock_provide_master_clock (priv->ksclock, clock);
689 GST_OBJECT_UNLOCK (element);
690
691 return GST_ELEMENT_CLASS (parent_class)->set_clock (element, clock);
692 }
693
694 static GstCaps *
gst_ks_video_src_get_caps(GstBaseSrc * basesrc,GstCaps * filter)695 gst_ks_video_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
696 {
697 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
698 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
699
700 if (priv->device != NULL)
701 return gst_ks_video_device_get_available_caps (priv->device);
702 else
703 return NULL; /* BaseSrc will return template caps */
704 }
705
706 static gboolean
gst_ks_video_src_set_caps(GstBaseSrc * basesrc,GstCaps * caps)707 gst_ks_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
708 {
709 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
710 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
711
712 if (priv->device == NULL)
713 return FALSE;
714
715 KS_WORKER_LOCK (priv);
716 priv->worker_pending_caps = caps;
717 KS_WORKER_NOTIFY (priv);
718 while (priv->worker_pending_caps == caps)
719 KS_WORKER_WAIT_FOR_RESULT (priv);
720 KS_WORKER_UNLOCK (priv);
721
722 GST_DEBUG ("Result is %d", priv->worker_setcaps_result);
723 return priv->worker_setcaps_result;
724 }
725
726 static GstCaps *
gst_ks_video_src_fixate(GstBaseSrc * basesrc,GstCaps * caps)727 gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
728 {
729 GstStructure *structure;
730 GstCaps *fixated_caps;
731 gint i;
732
733 fixated_caps = gst_caps_make_writable (caps);
734
735 for (i = 0; i < gst_caps_get_size (fixated_caps); ++i) {
736 structure = gst_caps_get_structure (fixated_caps, i);
737 gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT);
738 gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT);
739 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
740 G_MAXINT, 1);
741 }
742
743 return gst_caps_fixate (fixated_caps);
744 }
745
746 static gboolean
gst_ks_video_src_query(GstBaseSrc * basesrc,GstQuery * query)747 gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
748 {
749 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
750 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
751 gboolean result = FALSE;
752
753 switch (GST_QUERY_TYPE (query)) {
754 case GST_QUERY_LATENCY:{
755 GstClockTime min_latency, max_latency;
756
757 if (priv->device == NULL)
758 goto beach;
759
760 result = gst_ks_video_device_get_latency (priv->device, &min_latency,
761 &max_latency);
762 if (!result)
763 goto beach;
764
765 GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
766 " max %" GST_TIME_FORMAT,
767 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
768
769 gst_query_set_latency (query, TRUE, min_latency, max_latency);
770 break;
771 }
772 default:
773 result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
774 break;
775 }
776
777 beach:
778 return result;
779 }
780
781 static gboolean
gst_ks_video_src_unlock(GstBaseSrc * basesrc)782 gst_ks_video_src_unlock (GstBaseSrc * basesrc)
783 {
784 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
785 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
786
787 GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
788
789 gst_ks_video_device_cancel (priv->device);
790 return TRUE;
791 }
792
793 static gboolean
gst_ks_video_src_unlock_stop(GstBaseSrc * basesrc)794 gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc)
795 {
796 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
797 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
798
799 GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
800
801 gst_ks_video_device_cancel_stop (priv->device);
802 return TRUE;
803 }
804
805 static gboolean
gst_ks_video_src_timestamp_buffer(GstKsVideoSrc * self,GstBuffer * buf,GstClockTime presentation_time)806 gst_ks_video_src_timestamp_buffer (GstKsVideoSrc * self, GstBuffer * buf,
807 GstClockTime presentation_time)
808 {
809 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
810 GstClockTime duration;
811 GstClock *clock;
812 GstClockTime timestamp, base_time;
813
814 /* Don't timestamp muxed streams */
815 if (gst_ks_video_device_stream_is_muxed (priv->device)) {
816 duration = timestamp = GST_CLOCK_TIME_NONE;
817 goto timestamp;
818 }
819
820 duration = gst_ks_video_device_get_duration (priv->device);
821
822 GST_OBJECT_LOCK (self);
823 clock = GST_ELEMENT_CLOCK (self);
824 if (clock != NULL) {
825 gst_object_ref (clock);
826 base_time = GST_ELEMENT (self)->base_time;
827 } else {
828 timestamp = GST_CLOCK_TIME_NONE;
829 }
830 GST_OBJECT_UNLOCK (self);
831
832 if (clock != NULL) {
833 /* The time according to the current clock */
834 timestamp = gst_clock_get_time (clock) - base_time;
835 if (timestamp > duration)
836 timestamp -= duration;
837 else
838 timestamp = 0;
839
840 gst_object_unref (clock);
841 clock = NULL;
842 }
843
844 timestamp:
845 GST_BUFFER_PTS (buf) = timestamp;
846 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
847 GST_BUFFER_DURATION (buf) = duration;
848
849 return TRUE;
850 }
851
852 static void
gst_ks_video_src_update_statistics(GstKsVideoSrc * self)853 gst_ks_video_src_update_statistics (GstKsVideoSrc * self)
854 {
855 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
856 GstClock *clock;
857
858 GST_OBJECT_LOCK (self);
859 clock = GST_ELEMENT_CLOCK (self);
860 if (clock != NULL)
861 gst_object_ref (clock);
862 GST_OBJECT_UNLOCK (self);
863
864 if (clock != NULL) {
865 GstClockTime now = gst_clock_get_time (clock);
866 gst_object_unref (clock);
867
868 priv->count++;
869
870 if (GST_CLOCK_TIME_IS_VALID (priv->last_sampling)) {
871 if (now - priv->last_sampling >= GST_SECOND) {
872 GST_OBJECT_LOCK (self);
873 priv->fps = priv->count;
874 GST_OBJECT_UNLOCK (self);
875
876 g_object_notify (G_OBJECT (self), "fps");
877
878 priv->last_sampling = now;
879 priv->count = 0;
880 }
881 } else {
882 priv->last_sampling = now;
883 }
884 }
885 }
886
887 static GstFlowReturn
gst_ks_video_src_create(GstPushSrc * pushsrc,GstBuffer ** buf)888 gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
889 {
890 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc);
891 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
892 GstFlowReturn result;
893 GstClockTime presentation_time;
894 gulong error_code;
895 gchar *error_str;
896
897 g_assert (priv->device != NULL);
898
899 if (!gst_ks_video_device_has_caps (priv->device))
900 goto error_no_caps;
901
902 if (G_UNLIKELY (!priv->running)) {
903 KS_WORKER_LOCK (priv);
904 priv->worker_pending_run = TRUE;
905 KS_WORKER_NOTIFY (priv);
906 while (priv->worker_pending_run)
907 KS_WORKER_WAIT_FOR_RESULT (priv);
908 priv->running = priv->worker_run_result;
909 error_code = priv->worker_error_code;
910 KS_WORKER_UNLOCK (priv);
911
912 if (!priv->running)
913 goto error_start_capture;
914 }
915
916 do {
917 if (*buf != NULL) {
918 gst_buffer_unref (*buf);
919 *buf = NULL;
920 }
921
922 result = gst_ks_video_device_read_frame (priv->device, buf,
923 &presentation_time, &error_code, &error_str);
924 if (G_UNLIKELY (result != GST_FLOW_OK))
925 goto error_read_frame;
926 }
927 while (!gst_ks_video_src_timestamp_buffer (self, *buf, presentation_time));
928
929 if (G_UNLIKELY (priv->do_stats))
930 gst_ks_video_src_update_statistics (self);
931
932 if (!gst_ks_video_device_postprocess_frame (priv->device, buf)) {
933 GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Postprocessing failed"),
934 ("Postprocessing failed"));
935 return GST_FLOW_ERROR;
936 }
937
938 return GST_FLOW_OK;
939
940 /* ERRORS */
941 error_no_caps:
942 {
943 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
944 ("not negotiated"), ("maybe setcaps failed?"));
945
946 return GST_FLOW_ERROR;
947 }
948 error_start_capture:
949 {
950 const gchar *debug_str = "failed to change pin state to KSSTATE_RUN";
951
952 switch (error_code) {
953 case ERROR_FILE_NOT_FOUND:
954 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
955 ("failed to start capture (device unplugged)"), ("%s", debug_str));
956 break;
957 case ERROR_NO_SYSTEM_RESOURCES:
958 GST_ELEMENT_ERROR (self, RESOURCE, BUSY,
959 ("failed to start capture (device already in use)"), ("%s",
960 debug_str));
961 break;
962 default:
963 GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
964 ("failed to start capture (0x%08x)", (guint) error_code), ("%s",
965 debug_str));
966 break;
967 }
968
969 return GST_FLOW_ERROR;
970 }
971 error_read_frame:
972 {
973 if (result == GST_FLOW_ERROR) {
974 if (error_str != NULL) {
975 GST_ELEMENT_ERROR (self, RESOURCE, READ,
976 ("read failed: %s [0x%08x]", error_str, (guint) error_code),
977 ("gst_ks_video_device_read_frame failed"));
978 }
979 } else if (result == GST_FLOW_CUSTOM_ERROR) {
980 GST_ELEMENT_ERROR (self, RESOURCE, READ,
981 ("read failed"), ("gst_ks_video_device_read_frame failed"));
982 }
983
984 g_free (error_str);
985
986 return result;
987 }
988 }
989
990 static GstBuffer *
gst_ks_video_src_alloc_buffer(guint size,guint alignment,gpointer user_data)991 gst_ks_video_src_alloc_buffer (guint size, guint alignment, gpointer user_data)
992 {
993 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (user_data);
994 GstBuffer *buf;
995 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
996
997 buf = gst_buffer_new_allocate (NULL, size, ¶ms);
998 if (buf == NULL)
999 goto error_alloc_buffer;
1000
1001 return buf;
1002
1003 error_alloc_buffer:
1004 {
1005 GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL));
1006
1007 return NULL;
1008 }
1009 }
1010
1011 static gboolean
plugin_init(GstPlugin * plugin)1012 plugin_init (GstPlugin * plugin)
1013 {
1014 GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
1015 0, "Kernel streaming video source");
1016
1017 if (!gst_element_register (plugin, "ksvideosrc",
1018 GST_RANK_PRIMARY, GST_TYPE_KS_VIDEO_SRC))
1019 return FALSE;
1020
1021 if (!gst_device_provider_register (plugin, "ksdeviceprovider",
1022 GST_RANK_PRIMARY, GST_TYPE_KS_DEVICE_PROVIDER))
1023 return FALSE;
1024
1025 return TRUE;
1026 }
1027
1028 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1029 GST_VERSION_MINOR,
1030 winks,
1031 "Windows kernel streaming plugin",
1032 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
1033