static gboolean gst_ks_video_src_open_device (GstKsVideoSrc * self) { GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); GstKsVideoDevice *device = NULL; GList *devices, *cur; g_assert (priv->device == NULL); devices = ks_enumerate_devices (&KSCATEGORY_VIDEO); if (devices == NULL) goto error_no_devices; devices = ks_video_device_list_sort_cameras_first (devices); for (cur = devices; cur != NULL; cur = cur->next) { KsDeviceEntry *entry = cur->data; GST_DEBUG_OBJECT (self, "device %d: name='%s' path='%s'", entry->index, entry->name, entry->path); } for (cur = devices; cur != NULL && device == NULL; cur = cur->next) { KsDeviceEntry *entry = cur->data; gboolean match; if (priv->device_path != NULL) { match = g_strcasecmp (entry->path, priv->device_path) == 0; } else if (priv->device_name != NULL) { match = g_strcasecmp (entry->name, priv->device_name) == 0; } else if (priv->device_index >= 0) { match = entry->index == priv->device_index; } else { match = TRUE; /* pick the first entry */ } if (match) { priv->ksclock = g_object_new (GST_TYPE_KS_CLOCK, NULL); if (priv->ksclock != NULL && gst_ks_clock_open (priv->ksclock)) { GstClock *clock = GST_ELEMENT_CLOCK (self); if (clock != NULL) gst_ks_clock_provide_master_clock (priv->ksclock, clock); } else { GST_WARNING_OBJECT (self, "failed to create/open KsClock"); g_object_unref (priv->ksclock); priv->ksclock = NULL; } device = gst_ks_video_device_new (entry->path, priv->ksclock, gst_ks_video_src_alloc_buffer, self); } ks_device_entry_free (entry); } g_list_free (devices); if (device == NULL) goto error_no_match; if (!gst_ks_video_device_open (device)) goto error_open; priv->device = device; return TRUE; /* ERRORS */ error_no_devices: { GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("No video capture devices found"), (NULL)); return FALSE; } error_no_match: { if (priv->device_path != NULL) { GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("Specified video capture device with path '%s' not found", priv->device_path), (NULL)); } else if (priv->device_name != NULL) { GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("Specified video capture device with name '%s' not found", priv->device_name), (NULL)); } else { GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("Specified video capture device with index %d not found", priv->device_index), (NULL)); } return FALSE; } error_open: { GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, ("Failed to open device"), (NULL)); g_object_unref (device); return FALSE; } }
static gboolean gst_ks_video_src_timestamp_buffer (GstKsVideoSrc * self, GstBuffer * buf, GstClockTime presentation_time) { GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); GstClockTime duration; GstClock *clock; GstClockTime timestamp; /* Don't timestamp muxed streams */ if (gst_ks_video_device_stream_is_muxed (priv->device)) { duration = timestamp = GST_CLOCK_TIME_NONE; priv->offset++; goto timestamp; } duration = gst_ks_video_device_get_duration (priv->device); GST_OBJECT_LOCK (self); clock = GST_ELEMENT_CLOCK (self); if (clock != NULL) { gst_object_ref (clock); timestamp = GST_ELEMENT (self)->base_time; if (GST_CLOCK_TIME_IS_VALID (presentation_time)) { if (presentation_time > GST_ELEMENT (self)->base_time) presentation_time -= GST_ELEMENT (self)->base_time; else presentation_time = 0; } } else { timestamp = GST_CLOCK_TIME_NONE; } GST_OBJECT_UNLOCK (self); if (clock != NULL) { /* The time according to the current clock */ timestamp = gst_clock_get_time (clock) - timestamp; if (timestamp > duration) timestamp -= duration; else timestamp = 0; if (GST_CLOCK_TIME_IS_VALID (presentation_time)) { /* * We don't use this for anything yet, need to ponder how to deal * with pins that use an internal clock and timestamp from 0. */ GstClockTimeDiff diff = GST_CLOCK_DIFF (presentation_time, timestamp); GST_DEBUG_OBJECT (self, "diff between gst and driver timestamp: %" G_GINT64_FORMAT, diff); } gst_object_unref (clock); clock = NULL; /* Unless it's the first frame, align the current timestamp on a multiple * of duration since the previous */ if (GST_CLOCK_TIME_IS_VALID (priv->prev_ts)) { GstClockTime delta; guint delta_remainder, delta_offset; /* REVISIT: I've seen this happen with the GstSystemClock on Windows, * scary... */ if (timestamp < priv->prev_ts) { GST_INFO_OBJECT (self, "clock is ticking backwards"); return FALSE; } /* Round to a duration boundary */ delta = timestamp - priv->prev_ts; delta_remainder = delta % duration; if (delta_remainder < duration / 3) timestamp -= delta_remainder; else timestamp += duration - delta_remainder; /* How many frames are we off then? */ delta = timestamp - priv->prev_ts; delta_offset = delta / duration; if (delta_offset == 1) /* perfect */ GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT); else if (delta_offset > 1) { guint lost = delta_offset - 1; GST_INFO_OBJECT (self, "lost %d frame%s, setting discont flag", lost, (lost > 1) ? "s" : ""); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); } else if (delta_offset == 0) { /* overproduction, skip this frame */ GST_INFO_OBJECT (self, "skipping frame"); return FALSE; } priv->offset += delta_offset; } priv->prev_ts = timestamp; } timestamp: GST_BUFFER_OFFSET (buf) = priv->offset; GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf) + 1; GST_BUFFER_PTS (buf) = timestamp; GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (buf) = duration; return TRUE; }
static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); GstFlowReturn result; GstClockTime presentation_time; gulong error_code; gchar *error_str; g_assert (priv->device != NULL); if (!gst_ks_video_device_has_caps (priv->device)) goto error_no_caps; if (G_UNLIKELY (!priv->running)) { KS_WORKER_LOCK (priv); priv->worker_pending_run = TRUE; KS_WORKER_NOTIFY (priv); while (priv->worker_pending_run) KS_WORKER_WAIT_FOR_RESULT (priv); priv->running = priv->worker_run_result; error_code = priv->worker_error_code; KS_WORKER_UNLOCK (priv); if (!priv->running) goto error_start_capture; } do { if (*buf != NULL) { gst_buffer_unref (*buf); *buf = NULL; } result = gst_ks_video_device_read_frame (priv->device, buf, &presentation_time, &error_code, &error_str); if (G_UNLIKELY (result != GST_FLOW_OK)) goto error_read_frame; } while (!gst_ks_video_src_timestamp_buffer (self, *buf, presentation_time)); if (G_UNLIKELY (priv->do_stats)) gst_ks_video_src_update_statistics (self); if (!gst_ks_video_device_postprocess_frame (priv->device, *buf)) { GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Postprocessing failed"), ("Postprocessing failed")); return GST_FLOW_ERROR; } return GST_FLOW_OK; /* ERRORS */ error_no_caps: { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("not negotiated"), ("maybe setcaps failed?")); return GST_FLOW_ERROR; } error_start_capture: { const gchar *debug_str = "failed to change pin state to KSSTATE_RUN"; switch (error_code) { case ERROR_FILE_NOT_FOUND: GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("failed to start capture (device unplugged)"), ("%s", debug_str)); break; case ERROR_NO_SYSTEM_RESOURCES: GST_ELEMENT_ERROR (self, RESOURCE, BUSY, ("failed to start capture (device already in use)"), ("%s", debug_str)); break; default: GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("failed to start capture (0x%08x)", (guint) error_code), ("%s", debug_str)); break; } return GST_FLOW_ERROR; } error_read_frame: { if (result == GST_FLOW_ERROR) { if (error_str != NULL) { GST_ELEMENT_ERROR (self, RESOURCE, READ, ("read failed: %s [0x%08x]", error_str, (guint) error_code), ("gst_ks_video_device_read_frame failed")); } } else if (result == GST_FLOW_CUSTOM_ERROR) { GST_ELEMENT_ERROR (self, RESOURCE, READ, ("read failed"), ("gst_ks_video_device_read_frame failed")); } g_free (error_str); return result; } }
static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); guint buf_size; GstCaps *caps; GstBuffer *buf = NULL; GstFlowReturn result; GstClockTime presentation_time; gulong error_code; gchar *error_str; g_assert (priv->device != NULL); if (!gst_ks_video_device_has_caps (priv->device)) goto error_no_caps; buf_size = gst_ks_video_device_get_frame_size (priv->device); g_assert (buf_size); caps = gst_pad_get_negotiated_caps (GST_BASE_SRC_PAD (self)); if (caps == NULL) goto error_no_caps; result = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (self), priv->offset, buf_size, caps, &buf); gst_caps_unref (caps); if (G_UNLIKELY (result != GST_FLOW_OK)) goto error_alloc_buffer; if (G_UNLIKELY (!priv->running)) { KS_WORKER_LOCK (priv); priv->worker_pending_run = TRUE; KS_WORKER_NOTIFY (priv); while (priv->worker_pending_run) KS_WORKER_WAIT_FOR_RESULT (priv); priv->running = priv->worker_run_result; KS_WORKER_UNLOCK (priv); if (!priv->running) goto error_start_capture; } do { gulong bytes_read; result = gst_ks_video_device_read_frame (priv->device, GST_BUFFER_DATA (buf), buf_size, &bytes_read, &presentation_time, &error_code, &error_str); if (G_UNLIKELY (result != GST_FLOW_OK)) goto error_read_frame; GST_BUFFER_SIZE (buf) = bytes_read; } while (!gst_ks_video_src_timestamp_buffer (self, buf, presentation_time)); if (G_UNLIKELY (priv->do_stats)) gst_ks_video_src_update_statistics (self); gst_ks_video_device_postprocess_frame (priv->device, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); *buffer = buf; return GST_FLOW_OK; /* ERRORS */ error_no_caps: { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("not negotiated"), ("maybe setcaps failed?")); return GST_FLOW_ERROR; } error_start_capture: { GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, ("could not start capture"), ("failed to change pin state to KSSTATE_RUN")); return GST_FLOW_ERROR; } error_alloc_buffer: { GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL)); return result; } error_read_frame: { if (result != GST_FLOW_WRONG_STATE && result != GST_FLOW_UNEXPECTED) { GST_ELEMENT_ERROR (self, RESOURCE, READ, ("read failed: %s [0x%08x]", error_str, error_code), ("gst_ks_video_device_read_frame failed")); } g_free (error_str); gst_buffer_unref (buf); return result; } }