v4l2: Detect bad drivers timestamps

Even though the UVC driver do a great deal of effort to prevent bad
timestamp to be sent to userspace, there still exist UVC hardware that
are so buggy that the timestamp endup nearly random. This code detect
and ignore timestamp from these drivers, making these camera usable.
This has been tested on both invalid and valid cameras, making sure it
does not trigger for valid cameras.

https://bugzilla.gnome.org/show_bug.cgi?id=732910
This commit is contained in:
Nicolas Dufresne 2014-09-05 08:29:20 -04:00
parent 5c933fa781
commit b706103fab
2 changed files with 44 additions and 6 deletions

View file

@ -561,6 +561,9 @@ gst_v4l2src_start (GstBaseSrc * src)
v4l2src->ctrl_time = 0; v4l2src->ctrl_time = 0;
gst_object_sync_values (GST_OBJECT (src), v4l2src->ctrl_time); gst_object_sync_values (GST_OBJECT (src), v4l2src->ctrl_time);
v4l2src->has_bad_timestamp = FALSE;
v4l2src->last_timestamp = 0;
return TRUE; return TRUE;
} }
@ -575,6 +578,9 @@ static gboolean
gst_v4l2src_unlock_stop (GstBaseSrc * src) gst_v4l2src_unlock_stop (GstBaseSrc * src)
{ {
GstV4l2Src *v4l2src = GST_V4L2SRC (src); GstV4l2Src *v4l2src = GST_V4L2SRC (src);
v4l2src->last_timestamp = 0;
return gst_v4l2_object_unlock_stop (v4l2src->v4l2object); return gst_v4l2_object_unlock_stop (v4l2src->v4l2object);
} }
@ -670,7 +676,8 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
abs_time = GST_CLOCK_TIME_NONE; abs_time = GST_CLOCK_TIME_NONE;
} }
if (timestamp != GST_CLOCK_TIME_NONE) { retry:
if (!v4l2src->has_bad_timestamp && timestamp != GST_CLOCK_TIME_NONE) {
struct timespec now; struct timespec now;
GstClockTime gstnow; GstClockTime gstnow;
@ -680,7 +687,7 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
clock_gettime (CLOCK_MONOTONIC, &now); clock_gettime (CLOCK_MONOTONIC, &now);
gstnow = GST_TIMESPEC_TO_TIME (now); gstnow = GST_TIMESPEC_TO_TIME (now);
if (gstnow < timestamp && (timestamp - gstnow) > (10 * GST_SECOND)) { if (timestamp > gstnow || (gstnow - timestamp) > (10 * GST_SECOND)) {
GTimeVal now; GTimeVal now;
/* very large diff, fall back to system time */ /* very large diff, fall back to system time */
@ -688,12 +695,39 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
gstnow = GST_TIMEVAL_TO_TIME (now); gstnow = GST_TIMEVAL_TO_TIME (now);
} }
if (gstnow > timestamp) { /* Detect buggy drivers here, and stop using their timestamp. Failing any
delay = gstnow - timestamp; * of these condition would imply a very buggy driver:
} else { * - Timestamp in the future
delay = 0; * - 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 GST_DEBUG_OBJECT (v4l2src, "ts: %" GST_TIME_FORMAT " now %" GST_TIME_FORMAT
" delay %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), " delay %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
GST_TIME_ARGS (gstnow), GST_TIME_ARGS (delay)); GST_TIME_ARGS (gstnow), GST_TIME_ARGS (delay));

View file

@ -60,6 +60,10 @@ struct _GstV4l2Src
guint64 offset; guint64 offset;
GstClockTime ctrl_time; GstClockTime ctrl_time;
/* Timestamp sanity check */
GstClockTime last_timestamp;
gboolean has_bad_timestamp;
}; };
struct _GstV4l2SrcClass struct _GstV4l2SrcClass