static GstCaps * gst_v4l2src_get_caps (GstBaseSrc * src, GstCaps * filter) { GstV4l2Src *v4l2src; GstV4l2Object *obj; GstCaps *ret; GSList *walk; GSList *formats; v4l2src = GST_V4L2SRC (src); obj = v4l2src->v4l2object; if (!GST_V4L2_IS_OPEN (obj)) { return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (v4l2src)); } if (v4l2src->probed_caps) return gst_caps_ref (v4l2src->probed_caps); formats = gst_v4l2_object_get_format_list (obj); ret = gst_caps_new_empty (); for (walk = formats; walk; walk = walk->next) { struct v4l2_fmtdesc *format; GstStructure *template;
static gboolean gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps) { GstV4l2Src *v4l2src; GstV4l2Object *obj; v4l2src = GST_V4L2SRC (src); obj = v4l2src->v4l2object; /* make sure the caps changed before doing anything */ if (gst_v4l2_object_caps_equal (obj, caps)) return TRUE; /* make sure we stop capturing and dealloc buffers */ if (!gst_v4l2_object_stop (obj)) return FALSE; g_signal_emit (v4l2src, gst_v4l2_signals[SIGNAL_PRE_SET_FORMAT], 0, v4l2src->v4l2object->video_fd, caps); if (!gst_v4l2_object_set_format (obj, caps)) /* error already posted */ return FALSE; return TRUE; }
static GstCaps * gst_v4l2src_get_caps (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); GstCaps *ret; GSList *walk; GSList *formats; if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) { /* FIXME: copy? */ return gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (v4l2src))); } if (v4l2src->probed_caps) return gst_caps_ref (v4l2src->probed_caps); formats = gst_v4l2_object_get_format_list (v4l2src->v4l2object); ret = gst_caps_new_empty (); for (walk = formats; walk; walk = walk->next) { struct v4l2_fmtdesc *format; GstStructure *template;
static gboolean gst_v4l2src_iface_supported (GstImplementsInterface * iface, GType iface_type) { GstV4l2Object *v4l2object = GST_V4L2SRC (iface)->v4l2object; #ifdef HAVE_XVIDEO g_assert (iface_type == GST_TYPE_TUNER || iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_COLOR_BALANCE || iface_type == GST_TYPE_VIDEO_ORIENTATION); #else g_assert (iface_type == GST_TYPE_TUNER || iface_type == GST_TYPE_COLOR_BALANCE || iface_type == GST_TYPE_VIDEO_ORIENTATION); #endif if (v4l2object->video_fd == -1) return FALSE; #ifdef HAVE_XVIDEO if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L2_IS_OVERLAY (v4l2object)) return FALSE; #endif return TRUE; }
static gboolean gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps) { GstV4l2Src *v4l2src; GstV4l2Object *obj; v4l2src = GST_V4L2SRC (src); obj = v4l2src->v4l2object; /* make sure the caps changed before doing anything */ if (gst_v4l2_object_caps_equal (obj, caps)) return TRUE; if (GST_V4L2_IS_ACTIVE (obj)) { /* Just check if the format is acceptable, once we know * no buffers should be outstanding we try S_FMT. * * Basesrc will do an allocation query that * should indirectly reclaim buffers, after that we can * set the format and then configure our pool */ if (gst_v4l2_object_try_format (obj, caps)) { v4l2src->renegotiation_adjust = v4l2src->offset + 1; v4l2src->pending_set_fmt = TRUE; } else return FALSE; } else { /* make sure we stop capturing and dealloc buffers */ if (!gst_v4l2_object_stop (obj)) return FALSE; return gst_v4l2src_set_format (v4l2src, caps); } return TRUE; }
static GstStateChangeReturn gst_v4l2src_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstV4l2Src *v4l2src = GST_V4L2SRC (element); GstV4l2Object *obj = v4l2src->v4l2object; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: /* open the device */ if (!gst_v4l2_object_open (obj)) return GST_STATE_CHANGE_FAILURE; break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_READY_TO_NULL: /* close the device */ if (!gst_v4l2_object_close (obj)) return GST_STATE_CHANGE_FAILURE; break; default: break; } return ret; }
static gboolean gst_v4l2src_unlock_stop (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); v4l2src->last_timestamp = 0; return gst_v4l2_object_unlock_stop (v4l2src->v4l2object); }
static void gst_v4l2src_dispose (GObject * object) { GstV4l2Src *v4l2src = GST_V4L2SRC (object); if (v4l2src->probed_caps) { gst_caps_unref (v4l2src->probed_caps); } G_OBJECT_CLASS (parent_class)->dispose (object); }
static gchar * gst_v4l2src_uri_get_uri (GstURIHandler * handler) { GstV4l2Src *v4l2src = GST_V4L2SRC (handler); if (v4l2src->v4l2object->videodev != NULL) { return g_strdup_printf ("v4l2://%s", v4l2src->v4l2object->videodev); } return g_strdup ("v4l2://"); }
static void gst_v4l2src_dispose (GObject * object) { GstV4l2Src *v4l2src = GST_V4L2SRC (object); if (v4l2src->formats) { gst_v4l2src_clear_format_list (v4l2src); } G_OBJECT_CLASS (parent_class)->dispose (object); }
static gboolean gst_v4l2src_stop (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); GstV4l2Object *obj = v4l2src->v4l2object; if (GST_V4L2_IS_ACTIVE (obj)) { if (!gst_v4l2_object_stop (obj)) return FALSE; } return TRUE; }
/* start and stop are not symmetric -- start will open the device, but not start * capture. it's setcaps that will start capture, which is called via basesrc's * negotiate method. stop will both stop capture and close the device. */ static gboolean gst_v4l2src_start (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); v4l2src->offset = 0; /* activate settings for first frame */ v4l2src->ctrl_time = 0; gst_object_sync_values (GST_OBJECT (src), v4l2src->ctrl_time); return TRUE; }
/* this is somewhat of a hack.. but better to keep the hack in * one place than copy/pasting it around.. */ static GstV4l2Object * get_v4l2_object (GstElement * v4l2elem) { GstV4l2Object *v4l2object = NULL; if (GST_IS_V4L2SRC (v4l2elem)) { v4l2object = (GST_V4L2SRC (v4l2elem))->v4l2object; } else if (GST_IS_V4L2SINK (v4l2elem)) { v4l2object = (GST_V4L2SINK (v4l2elem))->v4l2object; } else { GST_ERROR_OBJECT (v4l2elem, "unknown v4l2 element"); } return v4l2object; }
static gboolean gst_v4l2src_uri_set_uri (GstURIHandler * handler, const gchar * uri, GError ** error) { GstV4l2Src *v4l2src = GST_V4L2SRC (handler); const gchar *device = DEFAULT_PROP_DEVICE; if (strcmp (uri, "v4l2://") != 0) { device = uri + 7; } g_object_set (v4l2src, "device", device, NULL); return TRUE; }
static GstCaps * gst_v4l2src_get_caps (GstBaseSrc * src, GstCaps * filter) { GstV4l2Src *v4l2src; GstV4l2Object *obj; v4l2src = GST_V4L2SRC (src); obj = v4l2src->v4l2object; if (!GST_V4L2_IS_OPEN (obj)) { return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (v4l2src)); } return gst_v4l2_object_get_caps (obj, filter); }
static void gst_v4l2src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstV4l2Src *v4l2src = GST_V4L2SRC (object); if (!gst_v4l2_object_get_property_helper (v4l2src->v4l2object, prop_id, value, pspec)) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } }
/* start and stop are not symmetric -- start will open the device, but not start * capture. it's setcaps that will start capture, which is called via basesrc's * negotiate method. stop will both stop capture and close the device. */ static gboolean gst_v4l2src_start (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); v4l2src->offset = 0; v4l2src->renegotiation_adjust = 0; /* activate settings for first frame */ v4l2src->ctrl_time = 0; gst_object_sync_values (GST_OBJECT (src), v4l2src->ctrl_time); v4l2src->has_bad_timestamp = FALSE; v4l2src->last_timestamp = 0; return TRUE; }
static void gst_v4l2src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstV4l2Src *v4l2src = GST_V4L2SRC (object); if (!gst_v4l2_object_get_property_helper (v4l2src->v4l2object, prop_id, value, pspec)) { switch (prop_id) { case PROP_QUEUE_SIZE: g_value_set_uint (value, v4l2src->num_buffers); break; case PROP_ALWAYS_COPY: g_value_set_boolean (value, v4l2src->always_copy); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } }
static gboolean gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) { GstV4l2Src *src = GST_V4L2SRC (bsrc); gboolean ret = TRUE; if (src->pending_set_fmt) { GstCaps *caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc)); if (!gst_v4l2_object_stop (src->v4l2object)) return FALSE; ret = gst_v4l2src_set_format (src, caps); gst_caps_unref (caps); src->pending_set_fmt = FALSE; } if (ret) { ret = gst_v4l2_object_decide_allocation (src->v4l2object, query); if (ret) ret = GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query); } if (ret) { if (!gst_buffer_pool_set_active (src->v4l2object->pool, TRUE)) goto activate_failed; } return ret; activate_failed: { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (_("Failed to allocate required memory.")), ("Buffer pool activation failed")); return FALSE; } }
static gboolean gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) { GstV4l2Src *src = GST_V4L2SRC (bsrc); gboolean ret = FALSE; if (gst_v4l2_object_decide_allocation (src->v4l2object, query)) ret = GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query); if (ret) { if (!gst_buffer_pool_set_active (src->v4l2object->pool, TRUE)) goto activate_failed; } return ret; activate_failed: { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (_("Failed to allocate required memory.")), ("Buffer pool activation failed")); return FALSE; } }
static gboolean gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) { GstV4l2Src *src; GstV4l2Object *obj; GstBufferPool *pool; guint size, min, max; gboolean update; src = GST_V4L2SRC (bsrc); obj = src->v4l2object; if (gst_query_get_n_allocation_pools (query) > 0) { gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); update = TRUE; } else { pool = NULL; min = max = 0; size = 0; update = FALSE; } GST_DEBUG_OBJECT (src, "allocation: size:%u min:%u max:%u pool:%" GST_PTR_FORMAT, size, min, max, pool); if (min != 0) { /* if there is a min-buffers suggestion, use it. We add 1 because we need 1 * buffer extra to capture while the other two buffers are downstream */ min += 1; } else { min = 2; } /* select a pool */ switch (obj->mode) { case GST_V4L2_IO_RW: if (pool == NULL) { /* no downstream pool, use our own then */ GST_DEBUG_OBJECT (src, "read/write mode: no downstream pool, using our own"); pool = GST_BUFFER_POOL_CAST (obj->pool); size = obj->sizeimage; } else { /* in READ/WRITE mode, prefer a downstream pool because our own pool * doesn't help much, we have to write to it as well */ GST_DEBUG_OBJECT (src, "read/write mode: using downstream pool"); /* use the bigest size, when we use our own pool we can't really do any * other size than what the hardware gives us but for downstream pools * we can try */ size = MAX (size, obj->sizeimage); } break; case GST_V4L2_IO_MMAP: case GST_V4L2_IO_USERPTR: case GST_V4L2_IO_DMABUF: /* in streaming mode, prefer our own pool */ pool = GST_BUFFER_POOL_CAST (obj->pool); size = obj->sizeimage; GST_DEBUG_OBJECT (src, "streaming mode: using our own pool %" GST_PTR_FORMAT, pool); break; case GST_V4L2_IO_AUTO: default: GST_WARNING_OBJECT (src, "unhandled mode"); break; } if (pool) { GstStructure *config; GstCaps *caps; config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL); gst_buffer_pool_config_set_params (config, caps, size, min, max); /* if downstream supports video metadata, add this to the pool config */ if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) { GST_DEBUG_OBJECT (pool, "activate Video Meta"); gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); } gst_buffer_pool_set_config (pool, config); } if (update) gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); else gst_query_add_allocation_pool (query, pool, size, min, max); return GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query); }
static GstFlowReturn gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); GstV4l2Object *obj = v4l2src->v4l2object; GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL_CAST (obj->pool); GstFlowReturn ret; GstClock *clock; GstClockTime abs_time, base_time, timestamp, duration; GstClockTime delay; do { ret = GST_BASE_SRC_CLASS (parent_class)->alloc (GST_BASE_SRC (src), 0, obj->info.size, buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto alloc_failed; ret = gst_v4l2_buffer_pool_process (pool, buf); } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto error; timestamp = GST_BUFFER_TIMESTAMP (*buf); duration = obj->duration; /* timestamps, LOCK to get clock and base time. */ /* FIXME: element clock and base_time is rarely changing */ GST_OBJECT_LOCK (v4l2src); if ((clock = GST_ELEMENT_CLOCK (v4l2src))) { /* we have a clock, get base time and ref clock */ base_time = GST_ELEMENT (v4l2src)->base_time; gst_object_ref (clock); } else { /* no clock, can't set timestamps */ base_time = GST_CLOCK_TIME_NONE; } GST_OBJECT_UNLOCK (v4l2src); /* sample pipeline clock */ if (clock) { abs_time = gst_clock_get_time (clock); gst_object_unref (clock); } else { abs_time = GST_CLOCK_TIME_NONE; } if (timestamp != GST_CLOCK_TIME_NONE) { struct timespec now; GstClockTime gstnow; /* v4l2 specs say to use the system time although many drivers switched to * the more desirable monotonic time. We first try to use the monotonic time * and see how that goes */ clock_gettime (CLOCK_MONOTONIC, &now); gstnow = GST_TIMESPEC_TO_TIME (now); if (gstnow < timestamp && (timestamp - gstnow) > (10 * GST_SECOND)) { GTimeVal now; /* very large diff, fall back to system time */ g_get_current_time (&now); gstnow = GST_TIMEVAL_TO_TIME (now); } if (gstnow > timestamp) { delay = gstnow - timestamp; } else { delay = 0; } GST_DEBUG_OBJECT (v4l2src, "ts: %" GST_TIME_FORMAT " now %" GST_TIME_FORMAT " delay %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (gstnow), GST_TIME_ARGS (delay)); } else { /* we assume 1 frame latency otherwise */ if (GST_CLOCK_TIME_IS_VALID (duration)) delay = duration; else delay = 0; } /* set buffer metadata */ GST_BUFFER_OFFSET (*buf) = v4l2src->offset++; GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset; if (G_LIKELY (abs_time != GST_CLOCK_TIME_NONE)) { /* the time now is the time of the clock minus the base time */ timestamp = abs_time - base_time; /* adjust for delay in the device */ if (timestamp > delay) timestamp -= delay; else timestamp = 0; } else { timestamp = GST_CLOCK_TIME_NONE; } /* activate settings for next frame */ if (GST_CLOCK_TIME_IS_VALID (duration)) { v4l2src->ctrl_time += duration; } else { /* this is not very good (as it should be the next timestamp), * still good enough for linear fades (as long as it is not -1) */ v4l2src->ctrl_time = timestamp; } gst_object_sync_values (GST_OBJECT (src), v4l2src->ctrl_time); GST_INFO_OBJECT (src, "sync to %" GST_TIME_FORMAT " out ts %" GST_TIME_FORMAT, GST_TIME_ARGS (v4l2src->ctrl_time), GST_TIME_ARGS (timestamp)); GST_BUFFER_TIMESTAMP (*buf) = timestamp; GST_BUFFER_DURATION (*buf) = duration; return ret; /* ERROR */ alloc_failed: { if (ret != GST_FLOW_FLUSHING) GST_ELEMENT_ERROR (src, RESOURCE, NO_SPACE_LEFT, ("Failed to allocate a buffer"), (NULL)); return ret; } error: { if (ret == GST_V4L2_FLOW_LAST_BUFFER) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Driver returned a buffer with no payload, this most likely " "indicate a bug in the driver."), (NULL)); ret = GST_FLOW_ERROR; } else { GST_DEBUG_OBJECT (src, "error processing buffer %d (%s)", ret, gst_flow_get_name (ret)); } return ret; } }
static gboolean gst_v4l2src_unlock (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); return gst_v4l2_object_unlock (v4l2src->v4l2object); }
static GstFlowReturn gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); GstV4l2Object *obj = v4l2src->v4l2object; GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL_CAST (obj->pool); GstFlowReturn ret; GstClock *clock; GstClockTime abs_time, base_time, timestamp, duration; GstClockTime delay; GstMessage *qos_msg; do { ret = GST_BASE_SRC_CLASS (parent_class)->alloc (GST_BASE_SRC (src), 0, obj->info.size, buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto alloc_failed; ret = gst_v4l2_buffer_pool_process (pool, buf); } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto error; timestamp = GST_BUFFER_TIMESTAMP (*buf); duration = obj->duration; /* timestamps, LOCK to get clock and base time. */ /* FIXME: element clock and base_time is rarely changing */ GST_OBJECT_LOCK (v4l2src); if ((clock = GST_ELEMENT_CLOCK (v4l2src))) { /* we have a clock, get base time and ref clock */ base_time = GST_ELEMENT (v4l2src)->base_time; gst_object_ref (clock); } else { /* no clock, can't set timestamps */ base_time = GST_CLOCK_TIME_NONE; } GST_OBJECT_UNLOCK (v4l2src); /* sample pipeline clock */ if (clock) { abs_time = gst_clock_get_time (clock); gst_object_unref (clock); } else { abs_time = GST_CLOCK_TIME_NONE; } retry: if (!v4l2src->has_bad_timestamp && timestamp != GST_CLOCK_TIME_NONE) { struct timespec now; GstClockTime gstnow; /* v4l2 specs say to use the system time although many drivers switched to * the more desirable monotonic time. We first try to use the monotonic time * and see how that goes */ clock_gettime (CLOCK_MONOTONIC, &now); gstnow = GST_TIMESPEC_TO_TIME (now); if (timestamp > gstnow || (gstnow - timestamp) > (10 * GST_SECOND)) { GTimeVal now; /* very large diff, fall back to system time */ g_get_current_time (&now); gstnow = GST_TIMEVAL_TO_TIME (now); } /* Detect buggy drivers here, and stop using their timestamp. Failing any * of these condition would imply a very buggy driver: * - Timestamp in the future * - Timestamp is going backward compare to last seen timestamp * - Timestamp is jumping forward for less then a frame duration * - Delay is bigger then the actual timestamp * */ if (timestamp > gstnow) { GST_WARNING_OBJECT (v4l2src, "Timestamp in the future detected, ignoring driver timestamps"); v4l2src->has_bad_timestamp = TRUE; goto retry; } if (v4l2src->last_timestamp > timestamp) { GST_WARNING_OBJECT (v4l2src, "Timestamp going backward, ignoring driver timestamps"); v4l2src->has_bad_timestamp = TRUE; goto retry; } delay = gstnow - timestamp; if (delay > timestamp) { GST_WARNING_OBJECT (v4l2src, "Timestamp does not correlate with any clock, ignoring driver timestamps"); v4l2src->has_bad_timestamp = TRUE; goto retry; } /* Save last timestamp for sanity checks */ v4l2src->last_timestamp = timestamp; GST_DEBUG_OBJECT (v4l2src, "ts: %" GST_TIME_FORMAT " now %" GST_TIME_FORMAT " delay %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (gstnow), GST_TIME_ARGS (delay)); } else { /* we assume 1 frame latency otherwise */ if (GST_CLOCK_TIME_IS_VALID (duration)) delay = duration; else delay = 0; } /* set buffer metadata */ if (G_LIKELY (abs_time != GST_CLOCK_TIME_NONE)) { /* the time now is the time of the clock minus the base time */ timestamp = abs_time - base_time; /* adjust for delay in the device */ if (timestamp > delay) timestamp -= delay; else timestamp = 0; } else { timestamp = GST_CLOCK_TIME_NONE; } /* activate settings for next frame */ if (GST_CLOCK_TIME_IS_VALID (duration)) { v4l2src->ctrl_time += duration; } else { /* this is not very good (as it should be the next timestamp), * still good enough for linear fades (as long as it is not -1) */ v4l2src->ctrl_time = timestamp; } gst_object_sync_values (GST_OBJECT (src), v4l2src->ctrl_time); GST_INFO_OBJECT (src, "sync to %" GST_TIME_FORMAT " out ts %" GST_TIME_FORMAT, GST_TIME_ARGS (v4l2src->ctrl_time), GST_TIME_ARGS (timestamp)); /* use generated offset values only if there are not already valid ones * set by the v4l2 device */ if (!GST_BUFFER_OFFSET_IS_VALID (*buf) || !GST_BUFFER_OFFSET_END_IS_VALID (*buf)) { GST_BUFFER_OFFSET (*buf) = v4l2src->offset++; GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset; } else { /* adjust raw v4l2 device sequence, will restart at null in case of renegotiation * (streamoff/streamon) */ GST_BUFFER_OFFSET (*buf) += v4l2src->renegotiation_adjust; GST_BUFFER_OFFSET_END (*buf) += v4l2src->renegotiation_adjust; /* check for frame loss with given (from v4l2 device) buffer offset */ if ((v4l2src->offset != 0) && (GST_BUFFER_OFFSET (*buf) != (v4l2src->offset + 1))) { guint64 lost_frame_count = GST_BUFFER_OFFSET (*buf) - v4l2src->offset - 1; GST_WARNING_OBJECT (v4l2src, "lost frames detected: count = %" G_GUINT64_FORMAT " - ts: %" GST_TIME_FORMAT, lost_frame_count, GST_TIME_ARGS (timestamp)); qos_msg = gst_message_new_qos (GST_OBJECT_CAST (v4l2src), TRUE, GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, timestamp, GST_CLOCK_TIME_IS_VALID (duration) ? lost_frame_count * duration : GST_CLOCK_TIME_NONE); gst_element_post_message (GST_ELEMENT_CAST (v4l2src), qos_msg); } v4l2src->offset = GST_BUFFER_OFFSET (*buf); } GST_BUFFER_TIMESTAMP (*buf) = timestamp; GST_BUFFER_DURATION (*buf) = duration; return ret; /* ERROR */ alloc_failed: { if (ret != GST_FLOW_FLUSHING) GST_ELEMENT_ERROR (src, RESOURCE, NO_SPACE_LEFT, ("Failed to allocate a buffer"), (NULL)); return ret; } error: { if (ret == GST_V4L2_FLOW_LAST_BUFFER) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Driver returned a buffer with no payload, this most likely " "indicate a bug in the driver."), (NULL)); ret = GST_FLOW_ERROR; } else { GST_DEBUG_OBJECT (src, "error processing buffer %d (%s)", ret, gst_flow_get_name (ret)); } return ret; } }
static gboolean gst_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query) { GstV4l2Src *src; GstV4l2Object *obj; gboolean res = FALSE; src = GST_V4L2SRC (bsrc); obj = src->v4l2object; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_LATENCY:{ GstClockTime min_latency, max_latency; guint32 fps_n, fps_d; guint num_buffers = 0; /* device must be open */ if (!GST_V4L2_IS_OPEN (obj)) { GST_WARNING_OBJECT (src, "Can't give latency since device isn't open !"); goto done; } fps_n = GST_V4L2_FPS_N (obj); fps_d = GST_V4L2_FPS_D (obj); /* we must have a framerate */ if (fps_n <= 0 || fps_d <= 0) { GST_WARNING_OBJECT (src, "Can't give latency since framerate isn't fixated !"); goto done; } /* min latency is the time to capture one frame */ min_latency = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n); /* max latency is total duration of the frame buffer */ if (obj->pool != NULL) num_buffers = GST_V4L2_BUFFER_POOL_CAST (obj->pool)->max_latency; if (num_buffers == 0) max_latency = -1; else max_latency = num_buffers * min_latency; GST_DEBUG_OBJECT (bsrc, "report latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT, GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency)); /* we are always live, the min latency is 1 frame and the max latency is * the complete buffer of frames. */ gst_query_set_latency (query, TRUE, min_latency, max_latency); res = TRUE; break; } default: res = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query); break; } done: return res; }
static gboolean gst_v4l2src_negotiate (GstBaseSrc * basesrc) { GstV4l2Src *v4l2src; GstV4l2Object *obj; GstCaps *thiscaps; GstCaps *caps = NULL; GstCaps *peercaps = NULL; gboolean result = FALSE; v4l2src = GST_V4L2SRC (basesrc); obj = v4l2src->v4l2object; /* We don't allow renegotiation, just return TRUE in that case */ if (GST_V4L2_IS_ACTIVE (obj)) return TRUE; /* first see what is possible on our source pad */ thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL); GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps); LOG_CAPS (basesrc, thiscaps); /* nothing or anything is allowed, we're done */ if (thiscaps == NULL || gst_caps_is_any (thiscaps)) goto no_nego_needed; /* get the peer caps without a filter as we'll filter ourselves later on */ peercaps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (basesrc), NULL); GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps); LOG_CAPS (basesrc, peercaps); if (peercaps && !gst_caps_is_any (peercaps)) { GstCaps *icaps = NULL; int i; /* Prefer the first caps we are compatible with that the peer proposed */ for (i = 0; i < gst_caps_get_size (peercaps); i++) { /* get intersection */ GstCaps *ipcaps = gst_caps_copy_nth (peercaps, i); GST_DEBUG_OBJECT (basesrc, "peer: %" GST_PTR_FORMAT, ipcaps); LOG_CAPS (basesrc, ipcaps); icaps = gst_caps_intersect (thiscaps, ipcaps); gst_caps_unref (ipcaps); if (!gst_caps_is_empty (icaps)) break; gst_caps_unref (icaps); icaps = NULL; } GST_DEBUG_OBJECT (basesrc, "intersect: %" GST_PTR_FORMAT, icaps); LOG_CAPS (basesrc, icaps); if (icaps) { /* If there are multiple intersections pick the one with the smallest * resolution strictly bigger then the first peer caps */ if (gst_caps_get_size (icaps) > 1) { GstStructure *s = gst_caps_get_structure (peercaps, 0); int best = 0; int twidth, theight; int width = G_MAXINT, height = G_MAXINT; if (gst_structure_get_int (s, "width", &twidth) && gst_structure_get_int (s, "height", &theight)) { /* Walk the structure backwards to get the first entry of the * smallest resolution bigger (or equal to) the preferred resolution) */ for (i = gst_caps_get_size (icaps) - 1; i >= 0; i--) { GstStructure *is = gst_caps_get_structure (icaps, i); int w, h; if (gst_structure_get_int (is, "width", &w) && gst_structure_get_int (is, "height", &h)) { if (w >= twidth && w <= width && h >= theight && h <= height) { width = w; height = h; best = i; } } } } caps = gst_caps_copy_nth (icaps, best); gst_caps_unref (icaps); } else { caps = icaps; } } gst_caps_unref (thiscaps); } else { /* no peer or peer have ANY caps, work with our own caps then */ caps = thiscaps; } if (peercaps) gst_caps_unref (peercaps); if (caps) { caps = gst_caps_truncate (caps); /* now fixate */ if (!gst_caps_is_empty (caps)) { caps = gst_v4l2src_fixate (basesrc, caps); GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps); LOG_CAPS (basesrc, caps); if (gst_caps_is_any (caps)) { /* hmm, still anything, so element can do anything and * nego is not needed */ result = TRUE; } else if (gst_caps_is_fixed (caps)) { /* yay, fixed caps, use those then */ result = gst_base_src_set_caps (basesrc, caps); } } gst_caps_unref (caps); } return result; no_nego_needed: { GST_DEBUG_OBJECT (basesrc, "no negotiation needed"); if (thiscaps) gst_caps_unref (thiscaps); return TRUE; } }