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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-ksvideosrc
24 * Provides low-latency video capture from WDM cameras on Windows.
27 * <title>Example pipelines</title>
29 * gst-launch -v ksvideosrc do-stats=TRUE ! ffmpegcolorspace ! dshowvideosink
30 * ]| Capture from a camera and render using dshowvideosink.
32 * gst-launch -v ksvideosrc do-stats=TRUE ! image/jpeg, width=640, height=480
33 * ! jpegdec ! ffmpegcolorspace ! 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"
49 #define DEFAULT_DEVICE_PATH NULL
50 #define DEFAULT_DEVICE_NAME NULL
51 #define DEFAULT_DEVICE_INDEX -1
52 #define DEFAULT_DO_STATS FALSE
53 #define DEFAULT_ENABLE_QUIRKS TRUE
66 GST_DEBUG_CATEGORY (gst_ks_debug);
67 #define GST_CAT_DEFAULT gst_ks_debug
69 #define KS_WORKER_LOCK(priv) g_mutex_lock (priv->worker_lock)
70 #define KS_WORKER_UNLOCK(priv) g_mutex_unlock (priv->worker_lock)
71 #define KS_WORKER_WAIT(priv) \
72 g_cond_wait (priv->worker_notify_cond, priv->worker_lock)
73 #define KS_WORKER_NOTIFY(priv) g_cond_signal (priv->worker_notify_cond)
74 #define KS_WORKER_WAIT_FOR_RESULT(priv) \
75 g_cond_wait (priv->worker_result_cond, priv->worker_lock)
76 #define KS_WORKER_NOTIFY_RESULT(priv) \
77 g_cond_signal (priv->worker_result_cond)
81 KS_WORKER_STATE_STARTING,
82 KS_WORKER_STATE_READY,
83 KS_WORKER_STATE_STOPPING,
84 KS_WORKER_STATE_ERROR,
87 struct _GstKsVideoSrcPrivate
94 gboolean enable_quirks;
98 GstKsVideoDevice *device;
101 GstClockTime prev_ts;
105 GThread *worker_thread;
107 GCond *worker_notify_cond;
108 GCond *worker_result_cond;
109 KsWorkerState worker_state;
111 GstCaps *worker_pending_caps;
112 gboolean worker_setcaps_result;
114 gboolean worker_pending_run;
115 gboolean worker_run_result;
117 gulong worker_error_code;
120 GstClockTime last_sampling;
125 #define GST_KS_VIDEO_SRC_GET_PRIVATE(o) ((o)->priv)
127 static void gst_ks_video_src_init_interfaces (GType type);
129 static void gst_ks_video_src_finalize (GObject * object);
130 static void gst_ks_video_src_get_property (GObject * object, guint prop_id,
131 GValue * value, GParamSpec * pspec);
132 static void gst_ks_video_src_set_property (GObject * object, guint prop_id,
133 const GValue * value, GParamSpec * pspec);
135 static void gst_ks_video_src_probe_interface_init (GstPropertyProbeInterface *
137 static const GList *gst_ks_video_src_probe_get_properties (GstPropertyProbe *
139 static GValueArray *gst_ks_video_src_probe_get_values (GstPropertyProbe * probe,
140 guint prop_id, const GParamSpec * pspec);
141 static GValueArray *gst_ks_video_src_get_device_name_values (GstKsVideoSrc *
143 static void gst_ks_video_src_reset (GstKsVideoSrc * self);
145 static GstStateChangeReturn gst_ks_video_src_change_state (GstElement * element,
146 GstStateChange transition);
147 static gboolean gst_ks_video_src_set_clock (GstElement * element,
150 static GstCaps *gst_ks_video_src_get_caps (GstBaseSrc * basesrc);
151 static gboolean gst_ks_video_src_set_caps (GstBaseSrc * basesrc,
153 static void gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
154 static gboolean gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query);
155 static gboolean gst_ks_video_src_unlock (GstBaseSrc * basesrc);
156 static gboolean gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc);
158 static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc,
159 GstBuffer ** buffer);
160 static GstBuffer *gst_ks_video_src_alloc_buffer (guint size, guint alignment,
163 GST_BOILERPLATE_FULL (GstKsVideoSrc, gst_ks_video_src, GstPushSrc,
164 GST_TYPE_PUSH_SRC, gst_ks_video_src_init_interfaces);
167 gst_ks_video_src_base_init (gpointer gclass)
169 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
171 gst_element_class_set_details_simple (element_class, "KsVideoSrc",
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 (element_class,
179 gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
180 ks_video_get_all_caps ()));
184 gst_ks_video_src_class_init (GstKsVideoSrcClass * klass)
186 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
187 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
188 GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
189 GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
191 g_type_class_add_private (klass, sizeof (GstKsVideoSrcPrivate));
193 gobject_class->finalize = gst_ks_video_src_finalize;
194 gobject_class->get_property = gst_ks_video_src_get_property;
195 gobject_class->set_property = gst_ks_video_src_set_property;
197 gstelement_class->change_state =
198 GST_DEBUG_FUNCPTR (gst_ks_video_src_change_state);
199 gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_clock);
201 gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_get_caps);
202 gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_caps);
203 gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_ks_video_src_fixate);
204 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_ks_video_src_query);
205 gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock);
206 gstbasesrc_class->unlock_stop =
207 GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock_stop);
209 gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_ks_video_src_create);
211 g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
212 g_param_spec_string ("device-path", "Device Path",
213 "The device path", DEFAULT_DEVICE_PATH,
214 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
215 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
216 g_param_spec_string ("device-name", "Device Name",
217 "The human-readable device name", DEFAULT_DEVICE_NAME,
218 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
219 g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
220 g_param_spec_int ("device-index", "Device Index",
221 "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
222 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
223 g_object_class_install_property (gobject_class, PROP_DO_STATS,
224 g_param_spec_boolean ("do-stats", "Enable statistics",
225 "Enable logging of statistics", DEFAULT_DO_STATS,
226 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227 g_object_class_install_property (gobject_class, PROP_FPS,
228 g_param_spec_int ("fps", "Frames per second",
229 "Last measured framerate, if statistics are enabled",
230 -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
231 g_object_class_install_property (gobject_class, PROP_ENABLE_QUIRKS,
232 g_param_spec_boolean ("enable-quirks", "Enable quirks",
233 "Enable driver-specific quirks", DEFAULT_ENABLE_QUIRKS,
234 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
236 GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
237 0, "Kernel streaming video source");
241 gst_ks_video_src_init (GstKsVideoSrc * self, GstKsVideoSrcClass * gclass)
243 GstBaseSrc *basesrc = GST_BASE_SRC (self);
244 GstKsVideoSrcPrivate *priv;
246 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_KS_VIDEO_SRC,
247 GstKsVideoSrcPrivate);
248 priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
250 gst_base_src_set_live (basesrc, TRUE);
251 gst_base_src_set_format (basesrc, GST_FORMAT_TIME);
253 gst_ks_video_src_reset (self);
255 priv->device_path = DEFAULT_DEVICE_PATH;
256 priv->device_name = DEFAULT_DEVICE_NAME;
257 priv->device_index = DEFAULT_DEVICE_INDEX;
258 priv->do_stats = DEFAULT_DO_STATS;
259 priv->enable_quirks = DEFAULT_ENABLE_QUIRKS;
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:
319 g_free (priv->device_path);
320 priv->device_path = g_value_dup_string (value);
322 case PROP_DEVICE_NAME:
324 const gchar *device_name = g_value_get_string (value);
325 g_free (priv->device_name);
326 priv->device_name = NULL;
327 if (device_name && strlen (device_name) != 0)
328 priv->device_name = g_strdup (device_name);
331 case PROP_DEVICE_INDEX:
332 priv->device_index = g_value_get_int (value);
335 GST_OBJECT_LOCK (object);
336 priv->do_stats = g_value_get_boolean (value);
337 GST_OBJECT_UNLOCK (object);
339 case PROP_ENABLE_QUIRKS:
340 priv->enable_quirks = g_value_get_boolean (value);
343 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349 gst_ks_video_src_reset (GstKsVideoSrc * self)
351 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
353 /* Reset statistics */
354 priv->last_sampling = GST_CLOCK_TIME_NONE;
358 /* Reset timestamping state */
360 priv->prev_ts = GST_CLOCK_TIME_NONE;
362 priv->running = FALSE;
366 gst_ks_video_src_apply_driver_quirks (GstKsVideoSrc * self)
368 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (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");
402 gst_ks_video_src_init_interfaces (GType type)
404 static const GInterfaceInfo ksvideosrc_info = {
405 (GInterfaceInitFunc) gst_ks_video_src_probe_interface_init,
410 g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE, &ksvideosrc_info);
414 gst_ks_video_src_probe_interface_init (GstPropertyProbeInterface * iface)
416 iface->get_properties = gst_ks_video_src_probe_get_properties;
417 iface->get_values = gst_ks_video_src_probe_get_values;
421 gst_ks_video_src_probe_get_properties (GstPropertyProbe * probe)
423 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
424 static GList *props = NULL;
429 pspec = g_object_class_find_property (klass, "device-name");
430 props = g_list_append (props, pspec);
437 gst_ks_video_src_probe_get_values (GstPropertyProbe * probe, guint prop_id,
438 const GParamSpec * pspec)
440 GstKsVideoSrc *src = GST_KS_VIDEO_SRC (probe);
441 GValueArray *array = NULL;
444 case PROP_DEVICE_NAME:
445 array = gst_ks_video_src_get_device_name_values (src);
448 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
456 gst_ks_video_src_get_device_name_values (GstKsVideoSrc * self)
458 GList *devices, *cur;
459 GValueArray *array = g_value_array_new (0);
461 devices = ks_enumerate_devices (&KSCATEGORY_VIDEO);
465 devices = ks_video_device_list_sort_cameras_first (devices);
467 for (cur = devices; cur != NULL; cur = cur->next) {
468 GValue value = { 0, };
469 KsDeviceEntry *entry = cur->data;
471 g_value_init (&value, G_TYPE_STRING);
472 g_value_set_string (&value, entry->name);
473 g_value_array_append (array, &value);
474 g_value_unset (&value);
476 ks_device_entry_free (entry);
479 g_list_free (devices);
484 gst_ks_video_src_open_device (GstKsVideoSrc * self)
486 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
487 GstKsVideoDevice *device = NULL;
488 GList *devices, *cur;
490 g_assert (priv->device == NULL);
492 devices = ks_enumerate_devices (&KSCATEGORY_VIDEO);
494 goto error_no_devices;
496 devices = ks_video_device_list_sort_cameras_first (devices);
498 for (cur = devices; cur != NULL; cur = cur->next) {
499 KsDeviceEntry *entry = cur->data;
501 GST_DEBUG_OBJECT (self, "device %d: name='%s' path='%s'",
502 entry->index, entry->name, entry->path);
505 for (cur = devices; cur != NULL && device == NULL; cur = cur->next) {
506 KsDeviceEntry *entry = cur->data;
509 if (priv->device_path != NULL) {
510 match = g_strcasecmp (entry->path, priv->device_path) == 0;
511 } else if (priv->device_name != NULL) {
512 match = g_strcasecmp (entry->name, priv->device_name) == 0;
513 } else if (priv->device_index >= 0) {
514 match = entry->index == priv->device_index;
516 match = TRUE; /* pick the first entry */
520 priv->ksclock = g_object_new (GST_TYPE_KS_CLOCK, NULL);
521 if (priv->ksclock != NULL && gst_ks_clock_open (priv->ksclock)) {
522 GstClock *clock = GST_ELEMENT_CLOCK (self);
524 gst_ks_clock_provide_master_clock (priv->ksclock, clock);
526 GST_WARNING_OBJECT (self, "failed to create/open KsClock");
527 g_object_unref (priv->ksclock);
528 priv->ksclock = NULL;
531 device = gst_ks_video_device_new (entry->path, priv->ksclock,
532 gst_ks_video_src_alloc_buffer, self);
535 ks_device_entry_free (entry);
538 g_list_free (devices);
543 if (!gst_ks_video_device_open (device))
546 priv->device = device;
553 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
554 ("No video capture devices found"), (NULL));
559 if (priv->device_path != NULL) {
560 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
561 ("Specified video capture device with path '%s' not found",
562 priv->device_path), (NULL));
563 } else if (priv->device_name != NULL) {
564 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
565 ("Specified video capture device with name '%s' not found",
566 priv->device_name), (NULL));
568 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
569 ("Specified video capture device with index %d not found",
570 priv->device_index), (NULL));
576 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
577 ("Failed to open device"), (NULL));
578 g_object_unref (device);
584 gst_ks_video_src_close_device (GstKsVideoSrc * self)
586 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
588 g_assert (priv->device != NULL);
590 gst_ks_video_device_close (priv->device);
591 g_object_unref (priv->device);
594 if (priv->ksclock != NULL) {
595 gst_ks_clock_close (priv->ksclock);
596 g_object_unref (priv->ksclock);
597 priv->ksclock = NULL;
600 gst_ks_video_src_reset (self);
604 * Worker thread that takes care of starting, configuring and stopping things.
606 * This is needed because Logitech's driver software injects a DLL that
607 * intercepts API functions like NtCreateFile, NtClose, NtDeviceIoControlFile
608 * and NtDuplicateObject so that they can provide in-place video effects to
609 * existing applications. Their assumption is that at least one thread tainted
610 * by their code stays around for the lifetime of the capture.
613 gst_ks_video_src_worker_func (gpointer data)
615 GstKsVideoSrc *self = data;
616 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
618 if (!gst_ks_video_src_open_device (self))
621 KS_WORKER_LOCK (priv);
622 priv->worker_state = KS_WORKER_STATE_READY;
623 KS_WORKER_NOTIFY_RESULT (priv);
625 while (priv->worker_state != KS_WORKER_STATE_STOPPING) {
626 KS_WORKER_WAIT (priv);
628 if (priv->worker_pending_caps != NULL) {
629 priv->worker_setcaps_result =
630 gst_ks_video_device_set_caps (priv->device,
631 priv->worker_pending_caps);
633 priv->worker_pending_caps = NULL;
634 KS_WORKER_NOTIFY_RESULT (priv);
635 } else if (priv->worker_pending_run) {
636 if (priv->ksclock != NULL)
637 gst_ks_clock_start (priv->ksclock);
638 priv->worker_run_result = gst_ks_video_device_set_state (priv->device,
639 KSSTATE_RUN, &priv->worker_error_code);
641 priv->worker_pending_run = FALSE;
642 KS_WORKER_NOTIFY_RESULT (priv);
646 KS_WORKER_UNLOCK (priv);
648 gst_ks_video_src_close_device (self);
655 KS_WORKER_LOCK (priv);
656 priv->worker_state = KS_WORKER_STATE_ERROR;
657 KS_WORKER_NOTIFY_RESULT (priv);
658 KS_WORKER_UNLOCK (priv);
665 gst_ks_video_src_start_worker (GstKsVideoSrc * self)
667 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
670 priv->worker_lock = g_mutex_new ();
671 priv->worker_notify_cond = g_cond_new ();
672 priv->worker_result_cond = g_cond_new ();
674 priv->worker_pending_caps = NULL;
675 priv->worker_pending_run = FALSE;
677 priv->worker_state = KS_WORKER_STATE_STARTING;
678 priv->worker_thread =
679 g_thread_create (gst_ks_video_src_worker_func, self, TRUE, NULL);
681 KS_WORKER_LOCK (priv);
682 while (priv->worker_state < KS_WORKER_STATE_READY)
683 KS_WORKER_WAIT_FOR_RESULT (priv);
684 result = priv->worker_state == KS_WORKER_STATE_READY;
685 KS_WORKER_UNLOCK (priv);
691 gst_ks_video_src_stop_worker (GstKsVideoSrc * self)
693 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
695 KS_WORKER_LOCK (priv);
696 priv->worker_state = KS_WORKER_STATE_STOPPING;
697 KS_WORKER_NOTIFY (priv);
698 KS_WORKER_UNLOCK (priv);
700 g_thread_join (priv->worker_thread);
701 priv->worker_thread = NULL;
703 g_cond_free (priv->worker_result_cond);
704 priv->worker_result_cond = NULL;
705 g_cond_free (priv->worker_notify_cond);
706 priv->worker_notify_cond = NULL;
707 g_mutex_free (priv->worker_lock);
708 priv->worker_lock = NULL;
711 static GstStateChangeReturn
712 gst_ks_video_src_change_state (GstElement * element, GstStateChange transition)
714 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
715 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
716 GstStateChangeReturn ret;
718 switch (transition) {
719 case GST_STATE_CHANGE_NULL_TO_READY:
720 if (priv->enable_quirks)
721 gst_ks_video_src_apply_driver_quirks (self);
722 if (!gst_ks_video_src_start_worker (self))
727 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
729 switch (transition) {
730 case GST_STATE_CHANGE_READY_TO_NULL:
731 gst_ks_video_src_stop_worker (self);
740 gst_ks_video_src_stop_worker (self);
741 return GST_STATE_CHANGE_FAILURE;
746 gst_ks_video_src_set_clock (GstElement * element, GstClock * clock)
748 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
749 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
751 GST_OBJECT_LOCK (element);
752 if (clock != NULL && priv->ksclock != NULL)
753 gst_ks_clock_provide_master_clock (priv->ksclock, clock);
754 GST_OBJECT_UNLOCK (element);
760 gst_ks_video_src_get_caps (GstBaseSrc * basesrc)
762 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
763 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
765 if (priv->device != NULL)
766 return gst_ks_video_device_get_available_caps (priv->device);
768 return NULL; /* BaseSrc will return template caps */
772 gst_ks_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
774 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
775 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
777 if (priv->device == NULL)
780 KS_WORKER_LOCK (priv);
781 priv->worker_pending_caps = caps;
782 KS_WORKER_NOTIFY (priv);
783 while (priv->worker_pending_caps == caps)
784 KS_WORKER_WAIT_FOR_RESULT (priv);
785 KS_WORKER_UNLOCK (priv);
787 return priv->worker_setcaps_result;
791 gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
793 GstStructure *structure = gst_caps_get_structure (caps, 0);
795 gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT);
796 gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT);
797 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
802 gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
804 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
805 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
806 gboolean result = FALSE;
808 switch (GST_QUERY_TYPE (query)) {
809 case GST_QUERY_LATENCY:{
810 GstClockTime min_latency, max_latency;
812 if (priv->device == NULL)
815 result = gst_ks_video_device_get_latency (priv->device, &min_latency,
820 GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
821 " max %" GST_TIME_FORMAT,
822 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
824 gst_query_set_latency (query, TRUE, min_latency, max_latency);
828 result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
837 gst_ks_video_src_unlock (GstBaseSrc * basesrc)
839 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
840 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
842 GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
844 gst_ks_video_device_cancel (priv->device);
849 gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc)
851 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
852 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
854 GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
856 gst_ks_video_device_cancel_stop (priv->device);
861 gst_ks_video_src_timestamp_buffer (GstKsVideoSrc * self, GstBuffer * buf,
862 GstClockTime presentation_time)
864 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
865 GstClockTime duration;
867 GstClockTime timestamp;
869 duration = gst_ks_video_device_get_duration (priv->device);
871 GST_OBJECT_LOCK (self);
872 clock = GST_ELEMENT_CLOCK (self);
874 gst_object_ref (clock);
875 timestamp = GST_ELEMENT (self)->base_time;
877 if (GST_CLOCK_TIME_IS_VALID (presentation_time)) {
878 if (presentation_time > GST_ELEMENT (self)->base_time)
879 presentation_time -= GST_ELEMENT (self)->base_time;
881 presentation_time = 0;
884 timestamp = GST_CLOCK_TIME_NONE;
886 GST_OBJECT_UNLOCK (self);
890 /* The time according to the current clock */
891 timestamp = gst_clock_get_time (clock) - timestamp;
892 if (timestamp > duration)
893 timestamp -= duration;
897 if (GST_CLOCK_TIME_IS_VALID (presentation_time)) {
899 * We don't use this for anything yet, need to ponder how to deal
900 * with pins that use an internal clock and timestamp from 0.
902 GstClockTimeDiff diff = GST_CLOCK_DIFF (presentation_time, timestamp);
903 GST_DEBUG_OBJECT (self, "diff between gst and driver timestamp: %"
904 G_GINT64_FORMAT, diff);
907 gst_object_unref (clock);
910 /* Unless it's the first frame, align the current timestamp on a multiple
911 * of duration since the previous */
912 if (GST_CLOCK_TIME_IS_VALID (priv->prev_ts)) {
914 guint delta_remainder, delta_offset;
916 /* REVISIT: I've seen this happen with the GstSystemClock on Windows,
918 if (timestamp < priv->prev_ts) {
919 GST_INFO_OBJECT (self, "clock is ticking backwards");
923 /* Round to a duration boundary */
924 delta = timestamp - priv->prev_ts;
925 delta_remainder = delta % duration;
927 if (delta_remainder < duration / 3)
928 timestamp -= delta_remainder;
930 timestamp += duration - delta_remainder;
932 /* How many frames are we off then? */
933 delta = timestamp - priv->prev_ts;
934 delta_offset = delta / duration;
936 if (delta_offset == 1) /* perfect */
937 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
938 else if (delta_offset > 1) {
939 guint lost = delta_offset - 1;
940 GST_INFO_OBJECT (self, "lost %d frame%s, setting discont flag",
941 lost, (lost > 1) ? "s" : "");
942 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
943 } else if (delta_offset == 0) { /* overproduction, skip this frame */
944 GST_INFO_OBJECT (self, "skipping frame");
948 priv->offset += delta_offset;
951 priv->prev_ts = timestamp;
954 GST_BUFFER_OFFSET (buf) = priv->offset;
955 GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf) + 1;
956 GST_BUFFER_TIMESTAMP (buf) = timestamp;
957 GST_BUFFER_DURATION (buf) = duration;
963 gst_ks_video_src_update_statistics (GstKsVideoSrc * self)
965 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
968 GST_OBJECT_LOCK (self);
969 clock = GST_ELEMENT_CLOCK (self);
971 gst_object_ref (clock);
972 GST_OBJECT_UNLOCK (self);
975 GstClockTime now = gst_clock_get_time (clock);
976 gst_object_unref (clock);
980 if (GST_CLOCK_TIME_IS_VALID (priv->last_sampling)) {
981 if (now - priv->last_sampling >= GST_SECOND) {
982 GST_OBJECT_LOCK (self);
983 priv->fps = priv->count;
984 GST_OBJECT_UNLOCK (self);
986 g_object_notify (G_OBJECT (self), "fps");
988 priv->last_sampling = now;
992 priv->last_sampling = now;
998 gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
1000 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc);
1001 GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
1002 GstFlowReturn result;
1003 GstClockTime presentation_time;
1007 g_assert (priv->device != NULL);
1009 if (!gst_ks_video_device_has_caps (priv->device))
1012 if (G_UNLIKELY (!priv->running)) {
1013 KS_WORKER_LOCK (priv);
1014 priv->worker_pending_run = TRUE;
1015 KS_WORKER_NOTIFY (priv);
1016 while (priv->worker_pending_run)
1017 KS_WORKER_WAIT_FOR_RESULT (priv);
1018 priv->running = priv->worker_run_result;
1019 error_code = priv->worker_error_code;
1020 KS_WORKER_UNLOCK (priv);
1023 goto error_start_capture;
1028 gst_buffer_unref (*buf);
1032 result = gst_ks_video_device_read_frame (priv->device, buf,
1033 &presentation_time, &error_code, &error_str);
1034 if (G_UNLIKELY (result != GST_FLOW_OK))
1035 goto error_read_frame;
1037 while (!gst_ks_video_src_timestamp_buffer (self, *buf, presentation_time));
1039 if (G_UNLIKELY (priv->do_stats))
1040 gst_ks_video_src_update_statistics (self);
1042 gst_ks_video_device_postprocess_frame (priv->device,
1043 GST_BUFFER_DATA (*buf), GST_BUFFER_SIZE (*buf));
1050 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
1051 ("not negotiated"), ("maybe setcaps failed?"));
1053 return GST_FLOW_ERROR;
1055 error_start_capture:
1057 const gchar *debug_str = "failed to change pin state to KSSTATE_RUN";
1059 switch (error_code) {
1060 case ERROR_FILE_NOT_FOUND:
1061 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
1062 ("failed to start capture (device unplugged)"), (debug_str));
1064 case ERROR_NO_SYSTEM_RESOURCES:
1065 GST_ELEMENT_ERROR (self, RESOURCE, BUSY,
1066 ("failed to start capture (device already in use)"), (debug_str));
1069 GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
1070 ("failed to start capture (0x%08x)", error_code), (debug_str));
1074 return GST_FLOW_ERROR;
1078 if (result == GST_FLOW_ERROR) {
1079 if (error_str != NULL) {
1080 GST_ELEMENT_ERROR (self, RESOURCE, READ,
1081 ("read failed: %s [0x%08x]", error_str, error_code),
1082 ("gst_ks_video_device_read_frame failed"));
1084 } else if (result == GST_FLOW_UNEXPECTED) {
1085 GST_ELEMENT_ERROR (self, RESOURCE, READ,
1086 ("read failed"), ("gst_ks_video_device_read_frame failed"));
1096 gst_ks_video_src_alloc_buffer (guint size, guint alignment, gpointer user_data)
1098 GstKsVideoSrc *self = GST_KS_VIDEO_SRC (user_data);
1101 GstFlowReturn flow_ret;
1103 caps = gst_pad_get_negotiated_caps (GST_BASE_SRC_PAD (self));
1106 flow_ret = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (self), 0,
1107 size + (alignment - 1), caps, &buf);
1108 gst_caps_unref (caps);
1109 if (G_UNLIKELY (flow_ret != GST_FLOW_OK))
1110 goto error_alloc_buffer;
1112 GST_BUFFER_DATA (buf) =
1113 GSIZE_TO_POINTER ((GPOINTER_TO_SIZE (GST_BUFFER_DATA (buf)) + (alignment -
1114 1)) & ~(alignment - 1));
1115 GST_BUFFER_SIZE (buf) = size;
1121 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
1122 ("not negotiated"), ("maybe setcaps failed?"));
1128 GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL));
1135 plugin_init (GstPlugin * plugin)
1137 return gst_element_register (plugin, "ksvideosrc",
1138 GST_RANK_NONE, GST_TYPE_KS_VIDEO_SRC);
1141 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1144 "Windows kernel streaming plugin",
1145 plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")