static gboolean gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); gboolean result = FALSE; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_LATENCY:{ GstClockTime min_latency, max_latency; if (priv->device == NULL) goto beach; result = gst_ks_video_device_get_latency (priv->device, &min_latency, &max_latency); if (!result) goto beach; GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT, GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency)); gst_query_set_latency (query, TRUE, min_latency, max_latency); break; } default: result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query); break; } beach: return result; }
static GstStateChangeReturn gst_ks_video_src_change_state (GstElement * element, GstStateChange transition) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); GstStateChangeReturn ret; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: if (priv->enable_quirks) gst_ks_video_src_apply_driver_quirks (self); if (!gst_ks_video_src_start_worker (self)) goto open_failed; break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_READY_TO_NULL: gst_ks_video_src_stop_worker (self); break; } return ret; /* ERRORS */ open_failed: { gst_ks_video_src_stop_worker (self); return GST_STATE_CHANGE_FAILURE; } }
static void gst_ks_video_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); switch (prop_id) { case PROP_DEVICE_PATH: g_value_set_string (value, priv->device_path); break; case PROP_DEVICE_NAME: g_value_set_string (value, priv->device_name); break; case PROP_DEVICE_INDEX: g_value_set_int (value, priv->device_index); break; case PROP_DO_STATS: GST_OBJECT_LOCK (object); g_value_set_boolean (value, priv->do_stats); GST_OBJECT_UNLOCK (object); break; case PROP_FPS: GST_OBJECT_LOCK (object); g_value_set_int (value, priv->fps); GST_OBJECT_UNLOCK (object); break; case PROP_ENABLE_QUIRKS: g_value_set_boolean (value, priv->enable_quirks); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); GST_DEBUG_OBJECT (self, "%s", G_STRFUNC); gst_ks_video_device_cancel_stop (priv->device); return TRUE; }
static GstCaps * gst_ks_video_src_get_caps (GstBaseSrc * basesrc) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); if (priv->device != NULL) return gst_ks_video_device_get_available_caps (priv->device); else return NULL; /* BaseSrc will return template caps */ }
static void gst_ks_video_src_finalize (GObject * object) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); g_free (priv->device_name); g_free (priv->device_path); G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_ks_video_src_set_clock (GstElement * element, GstClock * clock) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); GST_OBJECT_LOCK (element); if (clock != NULL && priv->ksclock != NULL) gst_ks_clock_provide_master_clock (priv->ksclock, clock); GST_OBJECT_UNLOCK (element); return TRUE; }
static gboolean gst_ks_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); if (priv->device == NULL) return FALSE; KS_WORKER_LOCK (priv); priv->worker_pending_caps = caps; KS_WORKER_NOTIFY (priv); while (priv->worker_pending_caps == caps) KS_WORKER_WAIT_FOR_RESULT (priv); KS_WORKER_UNLOCK (priv); return priv->worker_setcaps_result; }
static GValueArray * gst_ks_video_src_probe_get_values (GstPropertyProbe * probe, guint prop_id, const GParamSpec * pspec) { GstKsVideoSrc *src = GST_KS_VIDEO_SRC (probe); GValueArray *array = NULL; switch (prop_id) { case PROP_DEVICE_NAME: array = gst_ks_video_src_get_device_name_values (src); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); break; } return array; }
static GstBuffer * gst_ks_video_src_alloc_buffer (guint size, guint alignment, gpointer user_data) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (user_data); GstBuffer *buf; GstAllocationParams params = { 0, alignment - 1, 0, 0, }; buf = gst_buffer_new_allocate (NULL, size, ¶ms); if (buf == NULL) goto error_alloc_buffer; return buf; error_alloc_buffer: { GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL)); return NULL; } }
static GstBuffer * gst_ks_video_src_alloc_buffer (guint size, guint alignment, gpointer user_data) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (user_data); GstBuffer *buf; GstCaps *caps; GstFlowReturn flow_ret; caps = gst_pad_get_negotiated_caps (GST_BASE_SRC_PAD (self)); if (caps == NULL) goto error_no_caps; flow_ret = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (self), 0, size + (alignment - 1), caps, &buf); gst_caps_unref (caps); if (G_UNLIKELY (flow_ret != GST_FLOW_OK)) goto error_alloc_buffer; GST_BUFFER_DATA (buf) = GSIZE_TO_POINTER ((GPOINTER_TO_SIZE (GST_BUFFER_DATA (buf)) + (alignment - 1)) & ~(alignment - 1)); GST_BUFFER_SIZE (buf) = size; return buf; error_no_caps: { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("not negotiated"), ("maybe setcaps failed?")); return NULL; } error_alloc_buffer: { GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL)); return NULL; } }
static void gst_ks_video_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object); GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self); switch (prop_id) { case PROP_DEVICE_PATH: g_free (priv->device_path); priv->device_path = g_value_dup_string (value); break; case PROP_DEVICE_NAME: { const gchar *device_name = g_value_get_string (value); g_free (priv->device_name); priv->device_name = NULL; if (device_name && strlen (device_name) != 0) priv->device_name = g_strdup (device_name); } break; case PROP_DEVICE_INDEX: priv->device_index = g_value_get_int (value); break; case PROP_DO_STATS: GST_OBJECT_LOCK (object); priv->do_stats = g_value_get_boolean (value); GST_OBJECT_UNLOCK (object); break; case PROP_ENABLE_QUIRKS: priv->enable_quirks = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
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); gst_ks_video_device_postprocess_frame (priv->device, GST_BUFFER_DATA (*buf), GST_BUFFER_SIZE (*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: { 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)"), (debug_str)); break; case ERROR_NO_SYSTEM_RESOURCES: GST_ELEMENT_ERROR (self, RESOURCE, BUSY, ("failed to start capture (device already in use)"), (debug_str)); break; default: GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("failed to start capture (0x%08x)", error_code), (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, error_code), ("gst_ks_video_device_read_frame failed")); } } else if (result == GST_FLOW_UNEXPECTED) { 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; } }