GstFlowReturn gst_ks_video_device_read_frame (GstKsVideoDevice * self, guint8 * buf, gulong buf_size, gulong * bytes_read, GstClockTime * presentation_time, gulong * error_code, gchar ** error_str) { GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self); guint req_idx; DWORD wait_ret; BOOL success; DWORD bytes_returned; g_assert (priv->cur_media_type != NULL); /* First time we're called, submit the requests. */ if (G_UNLIKELY (!priv->requests_submitted)) { priv->requests_submitted = TRUE; for (req_idx = 0; req_idx < priv->num_requests; req_idx++) { ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx); if (!gst_ks_video_device_request_frame (self, req, error_code, error_str)) goto error_request_failed; } } do { /* Wait for either a request to complete, a cancel or a timeout */ wait_ret = WaitForMultipleObjects (priv->request_events->len, (HANDLE *) priv->request_events->data, FALSE, READ_TIMEOUT); if (wait_ret == WAIT_TIMEOUT) goto error_timeout; else if (wait_ret == WAIT_FAILED) goto error_wait; /* Stopped? */ if (WaitForSingleObject (priv->cancel_event, 0) == WAIT_OBJECT_0) goto error_cancel; *bytes_read = 0; /* Find the last ReadRequest that finished and get the result, immediately * re-issuing each request that has completed. */ for (req_idx = wait_ret - WAIT_OBJECT_0; req_idx < priv->num_requests; req_idx++) { ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx); /* * Completed? WaitForMultipleObjects() returns the lowest index if * multiple objects are in the signaled state, and we know that requests * are processed one by one so there's no point in looking further once * we've found the first that's non-signaled. */ if (WaitForSingleObject (req->overlapped.hEvent, 0) != WAIT_OBJECT_0) break; success = GetOverlappedResult (priv->pin_handle, &req->overlapped, &bytes_returned, TRUE); ResetEvent (req->overlapped.hEvent); if (success) { 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; } } } else if (GetLastError () != ERROR_OPERATION_ABORTED) goto error_get_result; /* Submit a new request immediately */ if (!gst_ks_video_device_request_frame (self, req, error_code, error_str)) goto error_request_failed; } } while (*bytes_read == 0); return GST_FLOW_OK; /* ERRORS */ error_request_failed: { return GST_FLOW_ERROR; } error_timeout: { GST_DEBUG ("IOCTL_KS_READ_STREAM timed out"); if (error_code != NULL) *error_code = 0; if (error_str != NULL) *error_str = NULL; return GST_FLOW_UNEXPECTED; } error_wait: { gst_ks_video_device_parse_win32_error ("WaitForMultipleObjects", GetLastError (), error_code, error_str); return GST_FLOW_ERROR; } error_cancel: { if (error_code != NULL) *error_code = 0; if (error_str != NULL) *error_str = NULL; return GST_FLOW_WRONG_STATE; } error_get_result: { gst_ks_video_device_parse_win32_error ("GetOverlappedResult", GetLastError (), error_code, error_str); return GST_FLOW_ERROR; } }
GstFlowReturn gst_ks_video_device_read_frame (GstKsVideoDevice * self, GstBuffer ** buf, GstClockTime * presentation_time, gulong * error_code, gchar ** error_str) { GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self); guint req_idx; DWORD wait_ret; BOOL success; DWORD bytes_returned; g_assert (priv->cur_media_type != NULL); /* First time we're called, submit the requests. */ if (G_UNLIKELY (!priv->requests_submitted)) { priv->requests_submitted = TRUE; for (req_idx = 0; req_idx < priv->num_requests; req_idx++) { ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx); if (!gst_ks_video_device_request_frame (self, req, error_code, error_str)) goto error_request_failed; } } *buf = NULL; do { /* Wait for either a request to complete, a cancel or a timeout */ wait_ret = WaitForMultipleObjects (priv->request_events->len, (HANDLE *) priv->request_events->data, FALSE, READ_TIMEOUT); if (wait_ret == WAIT_TIMEOUT) goto error_timeout; else if (wait_ret == WAIT_FAILED) goto error_wait; /* Stopped? */ if (WaitForSingleObject (priv->cancel_event, 0) == WAIT_OBJECT_0) goto error_cancel; /* Find the last ReadRequest that finished and get the result, immediately * re-issuing each request that has completed. */ for (req_idx = wait_ret - WAIT_OBJECT_0; req_idx < priv->num_requests; req_idx++) { ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx); /* * Completed? WaitForMultipleObjects() returns the lowest index if * multiple objects are in the signaled state, and we know that requests * are processed one by one so there's no point in looking further once * we've found the first that's non-signaled. */ if (WaitForSingleObject (req->overlapped.hEvent, 0) != WAIT_OBJECT_0) break; success = GetOverlappedResult (priv->pin_handle, &req->overlapped, &bytes_returned, TRUE); ResetEvent (req->overlapped.hEvent); if (success) { 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; UNREF_BUFFER (buf); if (G_LIKELY (hdr->DataUsed != 0)) { /* Assume it's a good frame */ GST_BUFFER_SIZE (req->buf) = hdr->DataUsed; *buf = gst_buffer_ref (req->buf); } 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: %lu 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_INFO ("got an old frame (last_timestamp=%" GST_TIME_FORMAT ", timestamp=%" GST_TIME_FORMAT ")", GST_TIME_ARGS (priv->last_timestamp), GST_TIME_ARGS (timestamp)); UNREF_BUFFER (buf); } else { priv->last_timestamp = timestamp; } } } else if (GetLastError () != ERROR_OPERATION_ABORTED) { goto error_get_result; } /* Submit a new request immediately */ if (!gst_ks_video_device_request_frame (self, req, error_code, error_str)) goto error_request_failed; } } while (*buf == NULL); return GST_FLOW_OK; /* ERRORS */ error_request_failed: { UNREF_BUFFER (buf); return GST_FLOW_ERROR; } error_timeout: { GST_DEBUG ("IOCTL_KS_READ_STREAM timed out"); if (error_code != NULL) *error_code = 0; if (error_str != NULL) *error_str = NULL; return GST_FLOW_UNEXPECTED; } error_wait: { gst_ks_video_device_parse_win32_error ("WaitForMultipleObjects", GetLastError (), error_code, error_str); return GST_FLOW_ERROR; } error_cancel: { if (error_code != NULL) *error_code = 0; if (error_str != NULL) *error_str = NULL; return GST_FLOW_FLUSHING; } error_get_result: { gst_ks_video_device_parse_win32_error ("GetOverlappedResult", GetLastError (), error_code, error_str); return GST_FLOW_ERROR; } }