summaryrefslogtreecommitdiffstats
path: root/sys/winks/gstksvideodevice.c
diff options
context:
space:
mode:
authorOle André Vadla Ravnås <ole.andre.ravnas@tandberg.com>2008-09-09 23:58:02 +0000
committerOle André Vadla Ravnås <ole.andre.ravnas@tandberg.com>2008-09-09 23:58:02 +0000
commit0ff4dc306f16324f12889137400aeb0cd9949938 (patch)
treef33d2c54ab8c1965d619995718d0560b054cc9da /sys/winks/gstksvideodevice.c
parente262a72516fe2e4e67e400a7d35058f19a71d0da (diff)
downloadgst-plugins-bad-0ff4dc306f16324f12889137400aeb0cd9949938.tar.gz
gst-plugins-bad-0ff4dc306f16324f12889137400aeb0cd9949938.tar.bz2
gst-plugins-bad-0ff4dc306f16324f12889137400aeb0cd9949938.zip
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS: * sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin, gst_ks_video_device_set_state, gst_ks_video_device_request_frame, gst_ks_video_device_read_frame): Guard against capturing old frames by keeping track of the last timestamp and also zero-fill the buffers before each capture. Only assign a master clock if the pin hasn't already got one. Actually free buffers on the way down to avoid a huge memory leak, as this was previously done when changing state to ACQUIRE downwards and we now skip that state on the way down. Add some debug. * sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME, DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK, KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT, KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING, KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING, KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name, device_index, running, worker_thread, worker_lock, worker_notify_cond, worker_result_cond, worker_state, worker_pending_caps, worker_setcaps_result, worker_pending_run, worker_run_result, gst_ks_video_src_reset, gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device, gst_ks_video_src_close_device, gst_ks_video_src_worker_func, gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker, gst_ks_video_src_change_state, gst_ks_video_src_set_clock, gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer, gst_ks_video_src_create): Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all. Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the GStreamer clock, it doesn't seem to hurt and matches DirectShow's behavior. As an added bonus we usually get PresentationTime set for each frame, so we can expand on this later for smarter latency reporting (by looking at the diff between the timestamp from the driver and the time according to the GStreamer clock). Use an internal worker thread for opening the device, setting caps, changing its state and closing it. This way we're a lot more compatible with drivers that rely on hacks to do video-effects between the low-level NT API and the application. Ick. Start the ks clock and set the pin to KSSTATE_RUN on the first create() so that we'll hopefully get hold of the GStreamer clock from the very beginning. This way there's no chance that the timestamps will make a sudden jump in the beginning of the stream when we're running with a clock. * sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG, ks_options_flags_to_string): Reorder the flags to match the headerfile order, and make the string a bit more compact. * sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps): Avoid leaking KSPROPERTY_PIN_DATARANGES.
Diffstat (limited to 'sys/winks/gstksvideodevice.c')
-rw-r--r--sys/winks/gstksvideodevice.c166
1 files changed, 120 insertions, 46 deletions
diff --git a/sys/winks/gstksvideodevice.c b/sys/winks/gstksvideodevice.c
index cb094831..db04d521 100644
--- a/sys/winks/gstksvideodevice.c
+++ b/sys/winks/gstksvideodevice.c
@@ -33,6 +33,9 @@
GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
#define GST_CAT_DEFAULT gst_ks_debug
+#define GST_DEBUG_IS_ENABLED() \
+ (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG)
+
enum
{
PROP_0,
@@ -81,6 +84,7 @@ typedef struct
gulong num_requests;
GArray *requests;
GArray *request_events;
+ GstClockTime last_timestamp;
} GstKsVideoDevicePrivate;
#define GST_KS_VIDEO_DEVICE_GET_PRIVATE(o) \
@@ -301,6 +305,13 @@ gst_ks_video_device_prepare_buffers (GstKsVideoDevice * self)
}
g_array_append_val (priv->request_events, priv->cancel_event);
+
+ /*
+ * REVISIT: Could probably remove this later, for now it's here to help
+ * track down the case where we capture old frames. This has been
+ * observed with UVC cameras, presumably with some system load.
+ */
+ priv->last_timestamp = GST_CLOCK_TIME_NONE;
}
static void
@@ -558,17 +569,28 @@ gst_ks_video_device_create_pin (GstKsVideoDevice * self,
}
/*
- * Override the clock if we have one.
+ * Override the clock if we have one and the pin doesn't have any either.
*/
if (priv->clock != NULL) {
- HANDLE clock_handle = gst_ks_clock_get_handle (priv->clock);
-
- if (ks_object_set_property (pin_handle, KSPROPSETID_Stream,
- KSPROPERTY_STREAM_MASTERCLOCK, &clock_handle,
- sizeof (clock_handle))) {
- gst_ks_clock_prepare (priv->clock);
+ HANDLE *cur_clock_handle = NULL;
+ gulong cur_clock_handle_size = sizeof (HANDLE);
+
+ if (ks_object_get_property (pin_handle, KSPROPSETID_Stream,
+ KSPROPERTY_STREAM_MASTERCLOCK, (gpointer *) & cur_clock_handle,
+ &cur_clock_handle_size)) {
+ GST_DEBUG ("current master clock handle: 0x%08x", *cur_clock_handle);
+ CloseHandle (*cur_clock_handle);
+ g_free (cur_clock_handle);
} else {
- GST_WARNING ("failed to set pin's master clock");
+ HANDLE new_clock_handle = gst_ks_clock_get_handle (priv->clock);
+
+ if (ks_object_set_property (pin_handle, KSPROPSETID_Stream,
+ KSPROPERTY_STREAM_MASTERCLOCK, &new_clock_handle,
+ sizeof (new_clock_handle))) {
+ gst_ks_clock_prepare (priv->clock);
+ } else {
+ GST_WARNING ("failed to set pin's master clock");
+ }
}
}
@@ -779,7 +801,7 @@ gst_ks_video_device_set_state (GstKsVideoDevice * self, KSSTATE state)
if (priv->state == KSSTATE_PAUSE && addend > 0)
gst_ks_video_device_prepare_buffers (self);
- else if (priv->state == KSSTATE_ACQUIRE && addend < 0)
+ else if (priv->state == KSSTATE_STOP && addend < 0)
gst_ks_video_device_clear_buffers (self);
} else {
GST_WARNING ("Failed to change pin state to %s",
@@ -856,6 +878,15 @@ gst_ks_video_device_request_frame (GstKsVideoDevice * self, ReadRequest * req,
params->header.Data = req->buf;
params->frame_info.ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
+ /*
+ * Clear the buffer like DirectShow does
+ *
+ * REVISIT: Could probably remove this later, for now it's here to help
+ * track down the case where we capture old frames. This has been
+ * observed with UVC cameras, presumably with some system load.
+ */
+ memset (params->header.Data, 0, params->header.FrameExtent);
+
success = DeviceIoControl (priv->pin_handle, IOCTL_KS_READ_STREAM, NULL, 0,
params, params->header.Size, &bytes_returned, &req->overlapped);
if (!success && GetLastError () != ERROR_IO_PENDING)
@@ -933,46 +964,89 @@ gst_ks_video_device_read_frame (GstKsVideoDevice * self, guint8 * buf,
ResetEvent (req->overlapped.hEvent);
if (success) {
- /* Grab the frame data */
- g_assert (buf_size >= req->params.header.DataUsed);
- memcpy (buf, req->buf, req->params.header.DataUsed);
- *bytes_read = req->params.header.DataUsed;
- if (req->params.header.PresentationTime.Time != 0)
- *presentation_time = req->params.header.PresentationTime.Time * 100;
- else
- *presentation_time = GST_CLOCK_TIME_NONE;
-
- if (priv->is_mjpeg) {
- /*
- * Workaround for cameras/drivers that intermittently provide us with
- * incomplete or corrupted MJPEG frames.
- *
- * Happens with for instance Microsoft LifeCam VX-7000.
- */
-
- gboolean valid = FALSE;
- guint padding = 0;
-
- /* JFIF SOI marker */
- if (*bytes_read > MJPEG_MAX_PADDING
- && buf[0] == 0xff && buf[1] == 0xd8) {
- guint8 *p = buf + *bytes_read - 2;
-
- /* JFIF EOI marker (but skip any padding) */
- while (padding < MJPEG_MAX_PADDING - 1 - 2 && !valid) {
- if (p[0] == 0xff && p[1] == 0xd9) {
- valid = TRUE;
- } else {
- padding++;
- p--;
+ KSSTREAM_HEADER *hdr = &req->params.header;
+ KS_FRAME_INFO *frame_info = &req->params.frame_info;
+ GstClockTime timestamp = GST_CLOCK_TIME_NONE;
+ GstClockTime duration = GST_CLOCK_TIME_NONE;
+
+ if (hdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID)
+ timestamp = hdr->PresentationTime.Time * 100;
+
+ if (hdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DURATIONVALID)
+ duration = hdr->Duration * 100;
+
+ /* Assume it's a good frame */
+ *bytes_read = hdr->DataUsed;
+
+ if (G_LIKELY (presentation_time != NULL))
+ *presentation_time = timestamp;
+
+ if (G_UNLIKELY (GST_DEBUG_IS_ENABLED ())) {
+ gchar *options_flags_str =
+ ks_options_flags_to_string (hdr->OptionsFlags);
+
+ GST_DEBUG ("PictureNumber=%" G_GUINT64_FORMAT ", DropCount=%"
+ G_GUINT64_FORMAT ", PresentationTime=%" GST_TIME_FORMAT
+ ", Duration=%" GST_TIME_FORMAT ", OptionsFlags=%s: %d bytes",
+ frame_info->PictureNumber, frame_info->DropCount,
+ GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration),
+ options_flags_str, hdr->DataUsed);
+
+ g_free (options_flags_str);
+ }
+
+ /* Protect against old frames. This should never happen, see previous
+ * comment on last_timestamp. */
+ if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (timestamp))) {
+ if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->last_timestamp) &&
+ timestamp < priv->last_timestamp)) {
+ GST_WARNING ("got an old frame (last_timestamp=%" GST_TIME_FORMAT
+ ", timestamp=%" GST_TIME_FORMAT ")",
+ GST_TIME_ARGS (priv->last_timestamp),
+ GST_TIME_ARGS (timestamp));
+ *bytes_read = 0;
+ } else {
+ priv->last_timestamp = timestamp;
+ }
+ }
+
+ if (*bytes_read > 0) {
+ /* Grab the frame data */
+ g_assert (buf_size >= hdr->DataUsed);
+ memcpy (buf, req->buf, hdr->DataUsed);
+
+ if (priv->is_mjpeg) {
+ /*
+ * Workaround for cameras/drivers that intermittently provide us
+ * with incomplete or corrupted MJPEG frames.
+ *
+ * Happens with for instance Microsoft LifeCam VX-7000.
+ */
+
+ gboolean valid = FALSE;
+ guint padding = 0;
+
+ /* JFIF SOI marker */
+ if (*bytes_read > MJPEG_MAX_PADDING
+ && buf[0] == 0xff && buf[1] == 0xd8) {
+ guint8 *p = buf + *bytes_read - 2;
+
+ /* JFIF EOI marker (but skip any padding) */
+ while (padding < MJPEG_MAX_PADDING - 1 - 2 && !valid) {
+ if (p[0] == 0xff && p[1] == 0xd9) {
+ valid = TRUE;
+ } else {
+ padding++;
+ p--;
+ }
}
}
- }
- if (valid)
- *bytes_read -= padding;
- else
- *bytes_read = 0;
+ if (valid)
+ *bytes_read -= padding;
+ else
+ *bytes_read = 0;
+ }
}
} else if (GetLastError () != ERROR_OPERATION_ABORTED)
goto error_get_result;