2
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
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.
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.
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., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
21
* SECTION:element-ksvideosrc
23
* Provides low-latency video capture from WDM cameras on Windows.
26
* <title>Example pipelines</title>
28
* gst-launch -v ksvideosrc do-stats=TRUE ! ffmpegcolorspace ! dshowvideosink
29
* ]| Capture from a camera and render using dshowvideosink.
31
* gst-launch -v ksvideosrc do-stats=TRUE ! image/jpeg, width=640, height=480
32
* ! jpegdec ! ffmpegcolorspace ! dshowvideosink
33
* ]| Capture from an MJPEG camera and render using dshowvideosink.
41
#include "gstksvideosrc.h"
43
#include "gstksclock.h"
44
#include "gstksvideodevice.h"
45
#include "kshelpers.h"
46
#include "ksvideohelpers.h"
48
#define DEFAULT_DEVICE_PATH NULL
49
#define DEFAULT_DEVICE_NAME NULL
50
#define DEFAULT_DEVICE_INDEX -1
51
#define DEFAULT_DO_STATS FALSE
52
#define DEFAULT_ENABLE_QUIRKS TRUE
65
GST_DEBUG_CATEGORY (gst_ks_debug);
66
#define GST_CAT_DEFAULT gst_ks_debug
68
#define KS_WORKER_LOCK(priv) g_mutex_lock (priv->worker_lock)
69
#define KS_WORKER_UNLOCK(priv) g_mutex_unlock (priv->worker_lock)
70
#define KS_WORKER_WAIT(priv) \
71
g_cond_wait (priv->worker_notify_cond, priv->worker_lock)
72
#define KS_WORKER_NOTIFY(priv) g_cond_signal (priv->worker_notify_cond)
73
#define KS_WORKER_WAIT_FOR_RESULT(priv) \
74
g_cond_wait (priv->worker_result_cond, priv->worker_lock)
75
#define KS_WORKER_NOTIFY_RESULT(priv) \
76
g_cond_signal (priv->worker_result_cond)
80
KS_WORKER_STATE_STARTING,
81
KS_WORKER_STATE_READY,
82
KS_WORKER_STATE_STOPPING,
83
KS_WORKER_STATE_ERROR,
93
gboolean enable_quirks;
97
GstKsVideoDevice *device;
100
GstClockTime prev_ts;
104
GThread *worker_thread;
106
GCond *worker_notify_cond;
107
GCond *worker_result_cond;
108
KsWorkerState worker_state;
110
GstCaps *worker_pending_caps;
111
gboolean worker_setcaps_result;
113
gboolean worker_pending_run;
114
gboolean worker_run_result;
117
GstClockTime last_sampling;
120
} GstKsVideoSrcPrivate;
122
#define GST_KS_VIDEO_SRC_GET_PRIVATE(o) \
123
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_KS_VIDEO_SRC, \
124
GstKsVideoSrcPrivate))
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);
132
static void gst_ks_video_src_reset (GstKsVideoSrc * self);
134
static GstStateChangeReturn gst_ks_video_src_change_state (GstElement * element,
135
GstStateChange transition);
136
static gboolean gst_ks_video_src_set_clock (GstElement * element,
139
static GstCaps *gst_ks_video_src_get_caps (GstBaseSrc * basesrc);
140
static gboolean gst_ks_video_src_set_caps (GstBaseSrc * basesrc,
142
static void gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
143
static gboolean gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query);
144
static gboolean gst_ks_video_src_unlock (GstBaseSrc * basesrc);
145
static gboolean gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc);
147
static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc,
148
GstBuffer ** buffer);
150
GST_BOILERPLATE (GstKsVideoSrc, gst_ks_video_src, GstPushSrc,
154
gst_ks_video_src_base_init (gpointer gclass)
156
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
157
static GstElementDetails element_details = {
160
"Stream data from a video capture device through Windows kernel streaming",
161
"Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>\n"
162
"Haakon Sporsheim <hakon.sporsheim@tandberg.com>"
165
gst_element_class_set_details (element_class, &element_details);
167
gst_element_class_add_pad_template (element_class,
168
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
169
ks_video_get_all_caps ()));
173
gst_ks_video_src_class_init (GstKsVideoSrcClass * klass)
175
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
176
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
177
GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
178
GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
180
g_type_class_add_private (klass, sizeof (GstKsVideoSrcPrivate));
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));
225
GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
226
0, "Kernel streaming video source");
230
gst_ks_video_src_init (GstKsVideoSrc * self, GstKsVideoSrcClass * gclass)
232
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
233
GstBaseSrc *basesrc = GST_BASE_SRC (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;
248
gst_ks_video_src_finalize (GObject * object)
250
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
251
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
253
g_free (priv->device_name);
254
g_free (priv->device_path);
256
G_OBJECT_CLASS (parent_class)->finalize (object);
260
gst_ks_video_src_get_property (GObject * object, guint prop_id,
261
GValue * value, GParamSpec * pspec)
263
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
264
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
267
case PROP_DEVICE_PATH:
268
g_value_set_string (value, priv->device_path);
270
case PROP_DEVICE_NAME:
271
g_value_set_string (value, priv->device_name);
273
case PROP_DEVICE_INDEX:
274
g_value_set_int (value, priv->device_index);
277
GST_OBJECT_LOCK (object);
278
g_value_set_boolean (value, priv->do_stats);
279
GST_OBJECT_UNLOCK (object);
282
GST_OBJECT_LOCK (object);
283
g_value_set_int (value, priv->fps);
284
GST_OBJECT_UNLOCK (object);
286
case PROP_ENABLE_QUIRKS:
287
g_value_set_boolean (value, priv->enable_quirks);
290
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
296
gst_ks_video_src_set_property (GObject * object, guint prop_id,
297
const GValue * value, GParamSpec * pspec)
299
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
300
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
303
case PROP_DEVICE_PATH:
304
g_free (priv->device_path);
305
priv->device_path = g_value_dup_string (value);
307
case PROP_DEVICE_NAME:
308
g_free (priv->device_name);
309
priv->device_name = g_value_dup_string (value);
311
case PROP_DEVICE_INDEX:
312
priv->device_index = g_value_get_int (value);
315
GST_OBJECT_LOCK (object);
316
priv->do_stats = g_value_get_boolean (value);
317
GST_OBJECT_UNLOCK (object);
319
case PROP_ENABLE_QUIRKS:
320
priv->enable_quirks = g_value_get_boolean (value);
323
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
329
gst_ks_video_src_reset (GstKsVideoSrc * self)
331
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
333
/* Reset statistics */
334
priv->last_sampling = GST_CLOCK_TIME_NONE;
338
/* Reset timestamping state */
340
priv->prev_ts = GST_CLOCK_TIME_NONE;
342
priv->running = FALSE;
346
gst_ks_video_src_apply_driver_quirks (GstKsVideoSrc * self)
348
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
352
* Logitech's driver software injects the following DLL into all processes
353
* spawned. This DLL does some nasty tricks, sitting in between the
354
* application and the low-level ntdll API (NtCreateFile, NtClose,
355
* NtDeviceIoControlFile, NtDuplicateObject, etc.), making all sorts
358
* The only regression that this quirk causes is that the video effects
359
* feature doesn't work.
361
mod = GetModuleHandle ("LVPrcInj.dll");
363
GST_DEBUG_OBJECT (self, "Logitech DLL detected, neutralizing it");
366
* We know that no-one's actually keeping this handle around to decrement
367
* its reference count, so we'll take care of that job. The DLL's DllMain
368
* implementation takes care of rolling back changes when it gets unloaded,
369
* so this seems to be the cleanest and most future-proof way that we can
374
/* Paranoia: verify that it's no longer there */
375
mod = GetModuleHandle ("LVPrcInj.dll");
377
GST_WARNING_OBJECT (self, "failed to neutralize Logitech DLL");
382
gst_ks_video_src_open_device (GstKsVideoSrc * self)
384
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
385
GstKsVideoDevice *device = NULL;
386
GList *devices, *cur;
388
g_assert (priv->device == NULL);
390
devices = ks_enumerate_devices (&KSCATEGORY_VIDEO);
392
goto error_no_devices;
394
for (cur = devices; cur != NULL; cur = cur->next) {
395
KsDeviceEntry *entry = cur->data;
397
GST_DEBUG_OBJECT (self, "device %d: name='%s' path='%s'",
398
entry->index, entry->name, entry->path);
401
for (cur = devices; cur != NULL && device == NULL; cur = cur->next) {
402
KsDeviceEntry *entry = cur->data;
405
if (priv->device_path != NULL) {
406
match = g_strcasecmp (entry->path, priv->device_path) == 0;
407
} else if (priv->device_name != NULL) {
408
match = g_strcasecmp (entry->name, priv->device_name) == 0;
409
} else if (priv->device_index >= 0) {
410
match = entry->index == priv->device_index;
412
match = TRUE; /* pick the first entry */
416
priv->ksclock = g_object_new (GST_TYPE_KS_CLOCK, NULL);
417
if (priv->ksclock != NULL && gst_ks_clock_open (priv->ksclock)) {
418
GstClock *clock = GST_ELEMENT_CLOCK (self);
420
gst_ks_clock_provide_master_clock (priv->ksclock, clock);
422
GST_WARNING_OBJECT (self, "failed to create/open KsClock");
423
g_object_unref (priv->ksclock);
424
priv->ksclock = NULL;
427
device = g_object_new (GST_TYPE_KS_VIDEO_DEVICE,
428
"clock", priv->ksclock, "device-path", entry->path, NULL);
431
ks_device_entry_free (entry);
434
g_list_free (devices);
439
if (!gst_ks_video_device_open (device))
442
priv->device = device;
449
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
450
("No video capture devices found"), (NULL));
455
if (priv->device_path != NULL) {
456
GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
457
("Specified video capture device with path '%s' not found",
458
priv->device_path), (NULL));
459
} else if (priv->device_name != NULL) {
460
GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
461
("Specified video capture device with name '%s' not found",
462
priv->device_name), (NULL));
464
GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
465
("Specified video capture device with index %d not found",
466
priv->device_index), (NULL));
472
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
473
("Failed to open device"), (NULL));
474
g_object_unref (device);
480
gst_ks_video_src_close_device (GstKsVideoSrc * self)
482
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
484
g_assert (priv->device != NULL);
486
gst_ks_video_device_close (priv->device);
487
g_object_unref (priv->device);
490
if (priv->ksclock != NULL) {
491
gst_ks_clock_close (priv->ksclock);
492
g_object_unref (priv->ksclock);
493
priv->ksclock = NULL;
496
gst_ks_video_src_reset (self);
500
* Worker thread that takes care of starting, configuring and stopping things.
502
* This is needed because Logitech's driver software injects a DLL that
503
* intercepts API functions like NtCreateFile, NtClose, NtDeviceIoControlFile
504
* and NtDuplicateObject so that they can provide in-place video effects to
505
* existing applications. Their assumption is that at least one thread tainted
506
* by their code stays around for the lifetime of the capture.
509
gst_ks_video_src_worker_func (gpointer data)
511
GstKsVideoSrc *self = data;
512
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
514
if (!gst_ks_video_src_open_device (self))
517
KS_WORKER_LOCK (priv);
518
priv->worker_state = KS_WORKER_STATE_READY;
519
KS_WORKER_NOTIFY_RESULT (priv);
521
while (priv->worker_state != KS_WORKER_STATE_STOPPING) {
522
KS_WORKER_WAIT (priv);
524
if (priv->worker_pending_caps != NULL) {
525
priv->worker_setcaps_result =
526
gst_ks_video_device_set_caps (priv->device,
527
priv->worker_pending_caps);
529
priv->worker_pending_caps = NULL;
530
KS_WORKER_NOTIFY_RESULT (priv);
531
} else if (priv->worker_pending_run) {
532
if (priv->ksclock != NULL)
533
gst_ks_clock_start (priv->ksclock);
534
priv->worker_run_result =
535
gst_ks_video_device_set_state (priv->device, KSSTATE_RUN);
537
priv->worker_pending_run = FALSE;
538
KS_WORKER_NOTIFY_RESULT (priv);
542
KS_WORKER_UNLOCK (priv);
544
gst_ks_video_src_close_device (self);
551
KS_WORKER_LOCK (priv);
552
priv->worker_state = KS_WORKER_STATE_ERROR;
553
KS_WORKER_NOTIFY_RESULT (priv);
554
KS_WORKER_UNLOCK (priv);
561
gst_ks_video_src_start_worker (GstKsVideoSrc * self)
563
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
566
priv->worker_lock = g_mutex_new ();
567
priv->worker_notify_cond = g_cond_new ();
568
priv->worker_result_cond = g_cond_new ();
570
priv->worker_pending_caps = NULL;
571
priv->worker_pending_run = FALSE;
573
priv->worker_state = KS_WORKER_STATE_STARTING;
574
priv->worker_thread =
575
g_thread_create (gst_ks_video_src_worker_func, self, TRUE, NULL);
577
KS_WORKER_LOCK (priv);
578
while (priv->worker_state < KS_WORKER_STATE_READY)
579
KS_WORKER_WAIT_FOR_RESULT (priv);
580
result = priv->worker_state == KS_WORKER_STATE_READY;
581
KS_WORKER_UNLOCK (priv);
587
gst_ks_video_src_stop_worker (GstKsVideoSrc * self)
589
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
591
KS_WORKER_LOCK (priv);
592
priv->worker_state = KS_WORKER_STATE_STOPPING;
593
KS_WORKER_NOTIFY (priv);
594
KS_WORKER_UNLOCK (priv);
596
g_thread_join (priv->worker_thread);
597
priv->worker_thread = NULL;
599
g_cond_free (priv->worker_result_cond);
600
priv->worker_result_cond = NULL;
601
g_cond_free (priv->worker_notify_cond);
602
priv->worker_notify_cond = NULL;
603
g_mutex_free (priv->worker_lock);
604
priv->worker_lock = NULL;
607
static GstStateChangeReturn
608
gst_ks_video_src_change_state (GstElement * element, GstStateChange transition)
610
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
611
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
612
GstStateChangeReturn ret;
614
switch (transition) {
615
case GST_STATE_CHANGE_NULL_TO_READY:
616
if (priv->enable_quirks)
617
gst_ks_video_src_apply_driver_quirks (self);
618
if (!gst_ks_video_src_start_worker (self))
623
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
625
switch (transition) {
626
case GST_STATE_CHANGE_READY_TO_NULL:
627
gst_ks_video_src_stop_worker (self);
636
gst_ks_video_src_stop_worker (self);
637
return GST_STATE_CHANGE_FAILURE;
642
gst_ks_video_src_set_clock (GstElement * element, GstClock * clock)
644
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
645
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
647
GST_OBJECT_LOCK (element);
648
if (clock != NULL && priv->ksclock != NULL)
649
gst_ks_clock_provide_master_clock (priv->ksclock, clock);
650
GST_OBJECT_UNLOCK (element);
656
gst_ks_video_src_get_caps (GstBaseSrc * basesrc)
658
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
659
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
661
if (priv->device != NULL)
662
return gst_ks_video_device_get_available_caps (priv->device);
664
return NULL; /* BaseSrc will return template caps */
668
gst_ks_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
670
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
671
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
673
if (priv->device == NULL)
676
KS_WORKER_LOCK (priv);
677
priv->worker_pending_caps = caps;
678
KS_WORKER_NOTIFY (priv);
679
while (priv->worker_pending_caps == caps)
680
KS_WORKER_WAIT_FOR_RESULT (priv);
681
KS_WORKER_UNLOCK (priv);
683
return priv->worker_setcaps_result;
687
gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
689
GstStructure *structure = gst_caps_get_structure (caps, 0);
691
gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT);
692
gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT);
693
gst_structure_fixate_field_nearest_fraction (structure, "framerate",
698
gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
700
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
701
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
702
gboolean result = FALSE;
704
switch (GST_QUERY_TYPE (query)) {
705
case GST_QUERY_LATENCY:{
706
GstClockTime min_latency, max_latency;
708
if (priv->device == NULL)
711
result = gst_ks_video_device_get_latency (priv->device, &min_latency,
716
GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
717
" max %" GST_TIME_FORMAT,
718
GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
720
gst_query_set_latency (query, TRUE, min_latency, max_latency);
724
result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
733
gst_ks_video_src_unlock (GstBaseSrc * basesrc)
735
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
736
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
738
GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
740
gst_ks_video_device_cancel (priv->device);
745
gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc)
747
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
748
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
750
GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
752
gst_ks_video_device_cancel_stop (priv->device);
757
gst_ks_video_src_timestamp_buffer (GstKsVideoSrc * self, GstBuffer * buf,
758
GstClockTime presentation_time)
760
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
761
GstClockTime duration;
763
GstClockTime timestamp;
765
duration = gst_ks_video_device_get_duration (priv->device);
767
GST_OBJECT_LOCK (self);
768
clock = GST_ELEMENT_CLOCK (self);
770
gst_object_ref (clock);
771
timestamp = GST_ELEMENT (self)->base_time;
773
if (GST_CLOCK_TIME_IS_VALID (presentation_time)) {
774
if (presentation_time > GST_ELEMENT (self)->base_time)
775
presentation_time -= GST_ELEMENT (self)->base_time;
777
presentation_time = 0;
780
timestamp = GST_CLOCK_TIME_NONE;
782
GST_OBJECT_UNLOCK (self);
786
/* The time according to the current clock */
787
timestamp = gst_clock_get_time (clock) - timestamp;
788
if (timestamp > duration)
789
timestamp -= duration;
793
if (GST_CLOCK_TIME_IS_VALID (presentation_time)) {
795
* We don't use this for anything yet, need to ponder how to deal
796
* with pins that use an internal clock and timestamp from 0.
798
GstClockTimeDiff diff = GST_CLOCK_DIFF (presentation_time, timestamp);
799
GST_DEBUG_OBJECT (self, "diff between gst and driver timestamp: %"
800
G_GINT64_FORMAT, diff);
803
gst_object_unref (clock);
806
/* Unless it's the first frame, align the current timestamp on a multiple
807
* of duration since the previous */
808
if (GST_CLOCK_TIME_IS_VALID (priv->prev_ts)) {
810
guint delta_remainder, delta_offset;
812
/* REVISIT: I've seen this happen with the GstSystemClock on Windows,
814
if (timestamp < priv->prev_ts) {
815
GST_WARNING_OBJECT (self, "clock is ticking backwards");
819
/* Round to a duration boundary */
820
delta = timestamp - priv->prev_ts;
821
delta_remainder = delta % duration;
823
if (delta_remainder < duration / 3)
824
timestamp -= delta_remainder;
826
timestamp += duration - delta_remainder;
828
/* How many frames are we off then? */
829
delta = timestamp - priv->prev_ts;
830
delta_offset = delta / duration;
832
if (delta_offset == 1) /* perfect */
833
GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
834
else if (delta_offset > 1) {
835
guint lost = delta_offset - 1;
836
GST_INFO_OBJECT (self, "lost %d frame%s, setting discont flag",
837
lost, (lost > 1) ? "s" : "");
838
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
839
} else if (delta_offset == 0) { /* overproduction, skip this frame */
840
GST_INFO_OBJECT (self, "skipping frame");
844
priv->offset += delta_offset;
847
priv->prev_ts = timestamp;
850
GST_BUFFER_OFFSET (buf) = priv->offset;
851
GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf) + 1;
852
GST_BUFFER_TIMESTAMP (buf) = timestamp;
853
GST_BUFFER_DURATION (buf) = duration;
859
gst_ks_video_src_update_statistics (GstKsVideoSrc * self)
861
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
864
GST_OBJECT_LOCK (self);
865
clock = GST_ELEMENT_CLOCK (self);
867
gst_object_ref (clock);
868
GST_OBJECT_UNLOCK (self);
871
GstClockTime now = gst_clock_get_time (clock);
872
gst_object_unref (clock);
876
if (GST_CLOCK_TIME_IS_VALID (priv->last_sampling)) {
877
if (now - priv->last_sampling >= GST_SECOND) {
878
GST_OBJECT_LOCK (self);
879
priv->fps = priv->count;
880
GST_OBJECT_UNLOCK (self);
882
g_object_notify (G_OBJECT (self), "fps");
884
priv->last_sampling = now;
888
priv->last_sampling = now;
894
gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
896
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc);
897
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
900
GstBuffer *buf = NULL;
901
GstFlowReturn result;
902
GstClockTime presentation_time;
906
g_assert (priv->device != NULL);
908
if (!gst_ks_video_device_has_caps (priv->device))
911
buf_size = gst_ks_video_device_get_frame_size (priv->device);
914
caps = gst_pad_get_negotiated_caps (GST_BASE_SRC_PAD (self));
917
result = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (self), priv->offset,
918
buf_size, caps, &buf);
919
gst_caps_unref (caps);
920
if (G_UNLIKELY (result != GST_FLOW_OK))
921
goto error_alloc_buffer;
923
if (G_UNLIKELY (!priv->running)) {
924
KS_WORKER_LOCK (priv);
925
priv->worker_pending_run = TRUE;
926
KS_WORKER_NOTIFY (priv);
927
while (priv->worker_pending_run)
928
KS_WORKER_WAIT_FOR_RESULT (priv);
929
priv->running = priv->worker_run_result;
930
KS_WORKER_UNLOCK (priv);
933
goto error_start_capture;
939
result = gst_ks_video_device_read_frame (priv->device,
940
GST_BUFFER_DATA (buf), buf_size, &bytes_read, &presentation_time,
941
&error_code, &error_str);
942
if (G_UNLIKELY (result != GST_FLOW_OK))
943
goto error_read_frame;
945
GST_BUFFER_SIZE (buf) = bytes_read;
947
while (!gst_ks_video_src_timestamp_buffer (self, buf, presentation_time));
949
if (G_UNLIKELY (priv->do_stats))
950
gst_ks_video_src_update_statistics (self);
952
gst_ks_video_device_postprocess_frame (priv->device,
953
GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
961
GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
962
("not negotiated"), ("maybe setcaps failed?"));
964
return GST_FLOW_ERROR;
968
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
969
("could not start capture"),
970
("failed to change pin state to KSSTATE_RUN"));
972
return GST_FLOW_ERROR;
976
GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL));
982
if (result != GST_FLOW_WRONG_STATE && result != GST_FLOW_UNEXPECTED) {
983
GST_ELEMENT_ERROR (self, RESOURCE, READ,
984
("read failed: %s [0x%08x]", error_str, error_code),
985
("gst_ks_video_device_read_frame failed"));
989
gst_buffer_unref (buf);
996
plugin_init (GstPlugin * plugin)
998
return gst_element_register (plugin, "ksvideosrc",
999
GST_RANK_NONE, GST_TYPE_KS_VIDEO_SRC);
1002
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1005
"Windows kernel streaming plugin",
1006
plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")