2 * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3 * 2009 Andres Colubri <andres.colubri@gmail.com>
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.
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.
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.
22 * SECTION:element-ksvideosrc
25 * Provides low-latency video capture from WDM cameras on Windows.
27 * ## Example pipelines
29 * gst-launch-1.0 -v ksvideosrc do-stats=TRUE ! videoconvert ! dshowvideosink
30 * ]| Capture from a camera and render using dshowvideosink.
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.
42 #include "gstksvideosrc.h"
44 #include "gstksclock.h"
45 #include "gstksvideodevice.h"
46 #include "kshelpers.h"
47 #include "ksvideohelpers.h"
48 #include "ksdeviceprovider.h"
50 #include <versionhelpers.h>
52 #define DEFAULT_DEVICE_PATH NULL
53 #define DEFAULT_DEVICE_NAME NULL
54 #define DEFAULT_DEVICE_INDEX -1
55 #define DEFAULT_DO_STATS FALSE
56 #define DEFAULT_ENABLE_QUIRKS TRUE
69 GST_DEBUG_CATEGORY (gst_ks_debug);
70 #define GST_CAT_DEFAULT gst_ks_debug
72 #define KS_WORKER_LOCK(priv) g_mutex_lock (&priv->worker_lock)
73 #define KS_WORKER_UNLOCK(priv) g_mutex_unlock (&priv->worker_lock)
74 #define KS_WORKER_WAIT(priv) \
75 g_cond_wait (&priv->worker_notify_cond, &priv->worker_lock)
76 #define KS_WORKER_NOTIFY(priv) g_cond_signal (&priv->worker_notify_cond)
77 #define KS_WORKER_WAIT_FOR_RESULT(priv) \
78 g_cond_wait (&priv->worker_result_cond, &priv->worker_lock)
79 #define KS_WORKER_NOTIFY_RESULT(priv) \
80 g_cond_signal (&priv->worker_result_cond)
84 KS_WORKER_STATE_STARTING,
85 KS_WORKER_STATE_READY,
86 KS_WORKER_STATE_STOPPING,
87 KS_WORKER_STATE_ERROR,
90 struct _GstKsVideoSrcPrivate
97 gboolean enable_quirks;
101 GstKsVideoDevice *device;
106 GThread *worker_thread;
108 GCond worker_notify_cond;
109 GCond worker_result_cond;
110 KsWorkerState worker_state;
112 GstCaps *worker_pending_caps;
113 gboolean worker_setcaps_result;
115 gboolean worker_pending_run;
116 gboolean worker_run_result;
118 gulong worker_error_code;
121 GstClockTime last_sampling;
126 #define GST_KS_VIDEO_SRC_GET_PRIVATE(o) ((o)->priv)
128 static void gst_ks_video_src_finalize (GObject * object);
129 static void gst_ks_video_src_get_property (GObject * object, guint prop_id,
130 GValue * value, GParamSpec * pspec);
131 static void gst_ks_video_src_set_property (GObject * object, guint prop_id,
132 const GValue * value, GParamSpec * pspec);
134 G_GNUC_UNUSED static GArray
135 * gst_ks_video_src_get_device_name_values (GstKsVideoSrc * self);
136 static void gst_ks_video_src_reset (GstKsVideoSrc * self);
138 static GstStateChangeReturn gst_ks_video_src_change_state (GstElement * element,
139 GstStateChange transition);
140 static gboolean gst_ks_video_src_set_clock (GstElement * element,
143 static GstCaps *gst_ks_video_src_get_caps (GstBaseSrc * basesrc,
145 static gboolean gst_ks_video_src_set_caps (GstBaseSrc * basesrc,
147 static GstCaps *gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
148 static gboolean gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query);
149 static gboolean gst_ks_video_src_unlock (GstBaseSrc * basesrc);
150 static gboolean gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc);
152 static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc,
153 GstBuffer ** buffer);
154 static GstBuffer *gst_ks_video_src_alloc_buffer (guint size, guint alignment,
157 G_DEFINE_TYPE_WITH_PRIVATE (GstKsVideoSrc, gst_ks_video_src, GST_TYPE_PUSH_SRC);
159 static GstKsVideoSrcClass *parent_class = NULL;
162 gst_ks_video_src_class_init (GstKsVideoSrcClass * klass)
164 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
165 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
166 GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
167 GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
169 parent_class = g_type_class_peek_parent (klass);
171 gst_element_class_set_static_metadata (gstelement_class, "KsVideoSrc",
172 "Source/Video/Hardware",
173 "Stream data from a video capture device through Windows kernel streaming",
174 "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>\n"
175 "Haakon Sporsheim <hakon.sporsheim@tandberg.com>\n"
176 "Andres Colubri <andres.colubri@gmail.com>");
178 gst_element_class_add_pad_template (gstelement_class,
179 gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
180 ks_video_get_all_caps ()));
182 gobject_class->finalize = gst_ks_video_src_finalize;
183 gobject_class->get_property = gst_ks_video_src_get_property;
184 gobject_class->set_property = gst_ks_video_src_set_property;
186 gstelement_class->change_state =
187 GST_DEBUG_FUNCPTR (gst_ks_video_src_change_state);
188 gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_clock);
190 gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_get_caps);
191 gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_caps);
192 gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_ks_video_src_fixate);
193 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_ks_video_src_query);
194 gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock);
195 gstbasesrc_class->unlock_stop =
196 GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock_stop);
198 gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_ks_video_src_create);
200 g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
201 g_param_spec_string ("device-path", "Device Path",
202 "The device path", DEFAULT_DEVICE_PATH,
203 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
205 g_param_spec_string ("device-name", "Device Name",
206 "The human-readable device name", DEFAULT_DEVICE_NAME,
207 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
208 g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
209 g_param_spec_int ("device-index", "Device Index",
210 "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
211 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212 g_object_class_install_property (gobject_class, PROP_DO_STATS,
213 g_param_spec_boolean ("do-stats", "Enable statistics",
214 "Enable logging of statistics", DEFAULT_DO_STATS,
215 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
216 g_object_class_install_property (gobject_class, PROP_FPS,
217 g_param_spec_int ("fps", "Frames per second",
218 "Last measured framerate, if statistics are enabled",
219 -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
220 g_object_class_install_property (gobject_class, PROP_ENABLE_QUIRKS,
221 g_param_spec_boolean ("enable-quirks", "Enable quirks",
222 "Enable driver-specific quirks", DEFAULT_ENABLE_QUIRKS,
223 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227 gst_ks_video_src_init (GstKsVideoSrc * self)
229 GstBaseSrc *basesrc = GST_BASE_SRC (self);
230 GstKsVideoSrcPrivate *priv;
232 self->priv = gst_ks_video_src_get_instance_private (self);
233 priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
235 gst_base_src_set_live (basesrc, TRUE);
236 gst_base_src_set_format (basesrc, GST_FORMAT_TIME);
238 gst_ks_video_src_reset (self);
240 priv->device_path = DEFAULT_DEVICE_PATH;
241 priv->device_name = DEFAULT_DEVICE_NAME;
242 priv->device_index = DEFAULT_DEVICE_INDEX;
243 priv->do_stats = DEFAULT_DO_STATS;
244 priv->enable_quirks = DEFAULT_ENABLE_QUIRKS;
246 /* MediaFoundation does not support MinGW build */
249 static gsize deprecated_warn = 0;
251 if (g_once_init_enter (&deprecated_warn)) {
252 if (IsWindows8OrGreater ()) {
253 g_warning ("\"ksvideosrc\" is deprecated and will be removed"
254 "in the future. Use \"mfvideosrc\" element instead");
256 g_once_init_leave (&deprecated_warn, 1);
263 gst_ks_video_src_finalize (GObject * object)
265 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
266 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
268 g_free (priv->device_name);
269 g_free (priv->device_path);
271 G_OBJECT_CLASS (parent_class)->finalize (object);
275 gst_ks_video_src_get_property (GObject * object, guint prop_id,
276 GValue * value, GParamSpec * pspec)
278 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
279 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
282 case PROP_DEVICE_PATH:
283 g_value_set_string (value, priv->device_path);
285 case PROP_DEVICE_NAME:
286 g_value_set_string (value, priv->device_name);
288 case PROP_DEVICE_INDEX:
289 g_value_set_int (value, priv->device_index);
292 GST_OBJECT_LOCK (object);
293 g_value_set_boolean (value, priv->do_stats);
294 GST_OBJECT_UNLOCK (object);
297 GST_OBJECT_LOCK (object);
298 g_value_set_int (value, priv->fps);
299 GST_OBJECT_UNLOCK (object);
301 case PROP_ENABLE_QUIRKS:
302 g_value_set_boolean (value, priv->enable_quirks);
305 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
311 gst_ks_video_src_set_property (GObject * object, guint prop_id,
312 const GValue * value, GParamSpec * pspec)
314 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
315 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
318 case PROP_DEVICE_PATH:
320 const gchar *device_path = g_value_get_string (value);
321 g_free (priv->device_path);
322 priv->device_path = NULL;
323 if (device_path && strlen (device_path) != 0)
324 priv->device_path = g_value_dup_string (value);
327 case PROP_DEVICE_NAME:
329 const gchar *device_name = g_value_get_string (value);
330 g_free (priv->device_name);
331 priv->device_name = NULL;
332 if (device_name && strlen (device_name) != 0)
333 priv->device_name = g_strdup (device_name);
336 case PROP_DEVICE_INDEX:
337 priv->device_index = g_value_get_int (value);
340 GST_OBJECT_LOCK (object);
341 priv->do_stats = g_value_get_boolean (value);
342 GST_OBJECT_UNLOCK (object);
344 case PROP_ENABLE_QUIRKS:
345 priv->enable_quirks = g_value_get_boolean (value);
348 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
354 gst_ks_video_src_reset (GstKsVideoSrc * self)
356 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
358 /* Reset statistics */
359 priv->last_sampling = GST_CLOCK_TIME_NONE;
363 priv->running = FALSE;
367 gst_ks_video_src_apply_driver_quirks (GstKsVideoSrc * self)
372 * Logitech's driver software injects the following DLL into all processes
373 * spawned. This DLL does some nasty tricks, sitting in between the
374 * application and the low-level ntdll API (NtCreateFile, NtClose,
375 * NtDeviceIoControlFile, NtDuplicateObject, etc.), making all sorts
378 * The only regression that this quirk causes is that the video effects
379 * feature doesn't work.
381 mod = GetModuleHandle ("LVPrcInj.dll");
383 GST_DEBUG_OBJECT (self, "Logitech DLL detected, neutralizing it");
386 * We know that no-one's actually keeping this handle around to decrement
387 * its reference count, so we'll take care of that job. The DLL's DllMain
388 * implementation takes care of rolling back changes when it gets unloaded,
389 * so this seems to be the cleanest and most future-proof way that we can
394 /* Paranoia: verify that it's no longer there */
395 mod = GetModuleHandle ("LVPrcInj.dll");
397 GST_WARNING_OBJECT (self, "failed to neutralize Logitech DLL");
401 /*FIXME: when we have a devices API replacement */
402 G_GNUC_UNUSED static GArray *
403 gst_ks_video_src_get_device_name_values (GstKsVideoSrc * self)
405 GList *devices, *cur;
406 GArray *array = g_array_new (TRUE, TRUE, sizeof (GValue));
408 devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
412 devices = ks_video_device_list_sort_cameras_first (devices);
414 for (cur = devices; cur != NULL; cur = cur->next) {
415 GValue value = { 0, };
416 KsDeviceEntry *entry = cur->data;
418 g_value_init (&value, G_TYPE_STRING);
419 g_value_set_string (&value, entry->name);
420 g_array_append_val (array, value);
421 g_value_unset (&value);
423 ks_device_entry_free (entry);
426 g_list_free (devices);
431 gst_ks_video_src_open_device (GstKsVideoSrc * self)
433 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
434 GstKsVideoDevice *device = NULL;
435 GList *devices, *cur;
437 g_assert (priv->device == NULL);
439 devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
441 goto error_no_devices;
443 devices = ks_video_device_list_sort_cameras_first (devices);
445 for (cur = devices; cur != NULL; cur = cur->next) {
446 KsDeviceEntry *entry = cur->data;
448 GST_DEBUG_OBJECT (self, "device %d: name='%s' path='%s'",
449 entry->index, entry->name, entry->path);
452 for (cur = devices; cur != NULL; cur = cur->next) {
453 KsDeviceEntry *entry = cur->data;
456 if (device != NULL) {
457 ks_device_entry_free (entry);
460 if (priv->device_path != NULL) {
461 match = g_ascii_strcasecmp (entry->path, priv->device_path) == 0;
462 } else if (priv->device_name != NULL) {
463 match = g_ascii_strcasecmp (entry->name, priv->device_name) == 0;
464 } else if (priv->device_index >= 0) {
465 match = entry->index == priv->device_index;
467 match = TRUE; /* pick the first entry */
471 priv->ksclock = g_object_new (GST_TYPE_KS_CLOCK, NULL);
472 if (priv->ksclock != NULL && gst_ks_clock_open (priv->ksclock)) {
473 GstClock *clock = GST_ELEMENT_CLOCK (self);
475 gst_ks_clock_provide_master_clock (priv->ksclock, clock);
477 GST_WARNING_OBJECT (self, "failed to create/open KsClock");
478 g_object_unref (priv->ksclock);
479 priv->ksclock = NULL;
482 device = gst_ks_video_device_new (entry->path, priv->ksclock,
483 gst_ks_video_src_alloc_buffer, self);
486 ks_device_entry_free (entry);
489 g_list_free (devices);
494 if (!gst_ks_video_device_open (device))
497 priv->device = device;
504 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
505 ("No video capture devices found"), (NULL));
510 if (priv->device_path != NULL) {
511 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
512 ("Specified video capture device with path '%s' not found",
513 priv->device_path), (NULL));
514 } else if (priv->device_name != NULL) {
515 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
516 ("Specified video capture device with name '%s' not found",
517 priv->device_name), (NULL));
519 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
520 ("Specified video capture device with index %d not found",
521 priv->device_index), (NULL));
527 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
528 ("Failed to open device"), (NULL));
529 g_object_unref (device);
535 gst_ks_video_src_close_device (GstKsVideoSrc * self)
537 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
539 g_assert (priv->device != NULL);
541 gst_ks_video_device_close (priv->device);
542 g_object_unref (priv->device);
545 if (priv->ksclock != NULL) {
546 gst_ks_clock_close (priv->ksclock);
547 g_object_unref (priv->ksclock);
548 priv->ksclock = NULL;
551 gst_ks_video_src_reset (self);
555 * Worker thread that takes care of starting, configuring and stopping things.
557 * This is needed because Logitech's driver software injects a DLL that
558 * intercepts API functions like NtCreateFile, NtClose, NtDeviceIoControlFile
559 * and NtDuplicateObject so that they can provide in-place video effects to
560 * existing applications. Their assumption is that at least one thread tainted
561 * by their code stays around for the lifetime of the capture.
564 gst_ks_video_src_worker_func (gpointer data)
566 GstKsVideoSrc *self = data;
567 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
569 if (!gst_ks_video_src_open_device (self))
572 KS_WORKER_LOCK (priv);
573 priv->worker_state = KS_WORKER_STATE_READY;
574 KS_WORKER_NOTIFY_RESULT (priv);
576 while (priv->worker_state != KS_WORKER_STATE_STOPPING) {
577 KS_WORKER_WAIT (priv);
579 if (priv->worker_pending_caps != NULL) {
580 priv->worker_setcaps_result =
581 gst_ks_video_device_set_caps (priv->device,
582 priv->worker_pending_caps);
584 priv->worker_pending_caps = NULL;
585 KS_WORKER_NOTIFY_RESULT (priv);
586 } else if (priv->worker_pending_run) {
587 if (priv->ksclock != NULL)
588 gst_ks_clock_start (priv->ksclock);
589 priv->worker_run_result = gst_ks_video_device_set_state (priv->device,
590 KSSTATE_RUN, &priv->worker_error_code);
592 priv->worker_pending_run = FALSE;
593 KS_WORKER_NOTIFY_RESULT (priv);
597 KS_WORKER_UNLOCK (priv);
599 gst_ks_video_src_close_device (self);
606 KS_WORKER_LOCK (priv);
607 priv->worker_state = KS_WORKER_STATE_ERROR;
608 KS_WORKER_NOTIFY_RESULT (priv);
609 KS_WORKER_UNLOCK (priv);
616 gst_ks_video_src_start_worker (GstKsVideoSrc * self)
618 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
621 g_mutex_init (&priv->worker_lock);
622 g_cond_init (&priv->worker_notify_cond);
623 g_cond_init (&priv->worker_result_cond);
625 priv->worker_pending_caps = NULL;
626 priv->worker_pending_run = FALSE;
628 priv->worker_state = KS_WORKER_STATE_STARTING;
629 priv->worker_thread =
630 g_thread_new ("ks-worker", gst_ks_video_src_worker_func, self);
632 KS_WORKER_LOCK (priv);
633 while (priv->worker_state < KS_WORKER_STATE_READY)
634 KS_WORKER_WAIT_FOR_RESULT (priv);
635 result = priv->worker_state == KS_WORKER_STATE_READY;
636 KS_WORKER_UNLOCK (priv);
642 gst_ks_video_src_stop_worker (GstKsVideoSrc * self)
644 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
646 KS_WORKER_LOCK (priv);
647 priv->worker_state = KS_WORKER_STATE_STOPPING;
648 KS_WORKER_NOTIFY (priv);
649 KS_WORKER_UNLOCK (priv);
651 g_thread_join (priv->worker_thread);
652 priv->worker_thread = NULL;
654 g_cond_clear (&priv->worker_result_cond);
655 g_cond_clear (&priv->worker_notify_cond);
656 g_mutex_clear (&priv->worker_lock);
659 static GstStateChangeReturn
660 gst_ks_video_src_change_state (GstElement * element, GstStateChange transition)
662 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
663 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
664 GstStateChangeReturn ret;
666 switch (transition) {
667 case GST_STATE_CHANGE_NULL_TO_READY:
668 if (priv->enable_quirks)
669 gst_ks_video_src_apply_driver_quirks (self);
670 if (!gst_ks_video_src_start_worker (self))
677 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
679 switch (transition) {
680 case GST_STATE_CHANGE_READY_TO_NULL:
681 gst_ks_video_src_stop_worker (self);
692 gst_ks_video_src_stop_worker (self);
693 return GST_STATE_CHANGE_FAILURE;
698 gst_ks_video_src_set_clock (GstElement * element, GstClock * clock)
700 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
701 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
703 GST_OBJECT_LOCK (element);
704 if (clock != NULL && priv->ksclock != NULL)
705 gst_ks_clock_provide_master_clock (priv->ksclock, clock);
706 GST_OBJECT_UNLOCK (element);
708 return GST_ELEMENT_CLASS (parent_class)->set_clock (element, clock);
712 gst_ks_video_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
714 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
715 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
717 if (priv->device != NULL)
718 return gst_ks_video_device_get_available_caps (priv->device);
720 return NULL; /* BaseSrc will return template caps */
724 gst_ks_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
726 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
727 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
729 if (priv->device == NULL)
732 KS_WORKER_LOCK (priv);
733 priv->worker_pending_caps = caps;
734 KS_WORKER_NOTIFY (priv);
735 while (priv->worker_pending_caps == caps)
736 KS_WORKER_WAIT_FOR_RESULT (priv);
737 KS_WORKER_UNLOCK (priv);
739 GST_DEBUG ("Result is %d", priv->worker_setcaps_result);
740 return priv->worker_setcaps_result;
744 gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
746 GstStructure *structure;
747 GstCaps *fixated_caps;
750 fixated_caps = gst_caps_make_writable (caps);
752 for (i = 0; i < gst_caps_get_size (fixated_caps); ++i) {
753 structure = gst_caps_get_structure (fixated_caps, i);
754 gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT);
755 gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT);
756 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
760 return gst_caps_fixate (fixated_caps);
764 gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
766 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
767 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
768 gboolean result = FALSE;
770 switch (GST_QUERY_TYPE (query)) {
771 case GST_QUERY_LATENCY:{
772 GstClockTime min_latency, max_latency;
774 if (priv->device == NULL)
777 result = gst_ks_video_device_get_latency (priv->device, &min_latency,
782 GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
783 " max %" GST_TIME_FORMAT,
784 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
786 gst_query_set_latency (query, TRUE, min_latency, max_latency);
790 result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
799 gst_ks_video_src_unlock (GstBaseSrc * basesrc)
801 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
802 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
804 GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
806 gst_ks_video_device_cancel (priv->device);
811 gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc)
813 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
814 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
816 GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
818 gst_ks_video_device_cancel_stop (priv->device);
823 gst_ks_video_src_timestamp_buffer (GstKsVideoSrc * self, GstBuffer * buf,
824 GstClockTime presentation_time)
826 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
827 GstClockTime duration;
829 GstClockTime timestamp, base_time;
831 /* Don't timestamp muxed streams */
832 if (gst_ks_video_device_stream_is_muxed (priv->device)) {
833 duration = timestamp = GST_CLOCK_TIME_NONE;
837 duration = gst_ks_video_device_get_duration (priv->device);
839 GST_OBJECT_LOCK (self);
840 clock = GST_ELEMENT_CLOCK (self);
842 gst_object_ref (clock);
843 base_time = GST_ELEMENT (self)->base_time;
845 timestamp = GST_CLOCK_TIME_NONE;
847 GST_OBJECT_UNLOCK (self);
850 /* The time according to the current clock */
851 timestamp = gst_clock_get_time (clock) - base_time;
852 if (timestamp > duration)
853 timestamp -= duration;
857 gst_object_unref (clock);
862 GST_BUFFER_PTS (buf) = timestamp;
863 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
864 GST_BUFFER_DURATION (buf) = duration;
870 gst_ks_video_src_update_statistics (GstKsVideoSrc * self)
872 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
875 GST_OBJECT_LOCK (self);
876 clock = GST_ELEMENT_CLOCK (self);
878 gst_object_ref (clock);
879 GST_OBJECT_UNLOCK (self);
882 GstClockTime now = gst_clock_get_time (clock);
883 gst_object_unref (clock);
887 if (GST_CLOCK_TIME_IS_VALID (priv->last_sampling)) {
888 if (now - priv->last_sampling >= GST_SECOND) {
889 GST_OBJECT_LOCK (self);
890 priv->fps = priv->count;
891 GST_OBJECT_UNLOCK (self);
893 g_object_notify (G_OBJECT (self), "fps");
895 priv->last_sampling = now;
899 priv->last_sampling = now;
905 gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
907 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc);
908 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
909 GstFlowReturn result;
910 GstClockTime presentation_time;
914 g_assert (priv->device != NULL);
916 if (!gst_ks_video_device_has_caps (priv->device))
919 if (G_UNLIKELY (!priv->running)) {
920 KS_WORKER_LOCK (priv);
921 priv->worker_pending_run = TRUE;
922 KS_WORKER_NOTIFY (priv);
923 while (priv->worker_pending_run)
924 KS_WORKER_WAIT_FOR_RESULT (priv);
925 priv->running = priv->worker_run_result;
926 error_code = priv->worker_error_code;
927 KS_WORKER_UNLOCK (priv);
930 goto error_start_capture;
935 gst_buffer_unref (*buf);
939 result = gst_ks_video_device_read_frame (priv->device, buf,
940 &presentation_time, &error_code, &error_str);
941 if (G_UNLIKELY (result != GST_FLOW_OK))
942 goto error_read_frame;
944 while (!gst_ks_video_src_timestamp_buffer (self, *buf, presentation_time));
946 if (G_UNLIKELY (priv->do_stats))
947 gst_ks_video_src_update_statistics (self);
949 if (!gst_ks_video_device_postprocess_frame (priv->device, buf)) {
950 GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Postprocessing failed"),
951 ("Postprocessing failed"));
952 return GST_FLOW_ERROR;
960 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
961 ("not negotiated"), ("maybe setcaps failed?"));
963 return GST_FLOW_ERROR;
967 const gchar *debug_str = "failed to change pin state to KSSTATE_RUN";
969 switch (error_code) {
970 case ERROR_FILE_NOT_FOUND:
971 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
972 ("failed to start capture (device unplugged)"), ("%s", debug_str));
974 case ERROR_NO_SYSTEM_RESOURCES:
975 GST_ELEMENT_ERROR (self, RESOURCE, BUSY,
976 ("failed to start capture (device already in use)"), ("%s",
980 GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
981 ("failed to start capture (0x%08x)", (guint) error_code), ("%s",
986 return GST_FLOW_ERROR;
990 if (result == GST_FLOW_ERROR) {
991 if (error_str != NULL) {
992 GST_ELEMENT_ERROR (self, RESOURCE, READ,
993 ("read failed: %s [0x%08x]", error_str, (guint) error_code),
994 ("gst_ks_video_device_read_frame failed"));
996 } else if (result == GST_FLOW_CUSTOM_ERROR) {
997 GST_ELEMENT_ERROR (self, RESOURCE, READ,
998 ("read failed"), ("gst_ks_video_device_read_frame failed"));
1008 gst_ks_video_src_alloc_buffer (guint size, guint alignment, gpointer user_data)
1010 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (user_data);
1012 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
1014 buf = gst_buffer_new_allocate (NULL, size, ¶ms);
1016 goto error_alloc_buffer;
1022 GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL));
1029 plugin_init (GstPlugin * plugin)
1031 GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
1032 0, "Kernel streaming video source");
1034 if (!gst_element_register (plugin, "ksvideosrc",
1035 GST_RANK_PRIMARY, GST_TYPE_KS_VIDEO_SRC))
1038 if (!gst_device_provider_register (plugin, "ksdeviceprovider",
1039 GST_RANK_PRIMARY, GST_TYPE_KS_DEVICE_PROVIDER))
1045 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1048 "Windows kernel streaming plugin",
1049 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)