summaryrefslogtreecommitdiffstats
path: root/sys/winks/gstksvideodevice.c
diff options
context:
space:
mode:
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;