---
drivers/media/video/uvc/uvc_video.c | 196
++++++++++++++++++++++++++++++++++-
drivers/media/video/uvc/uvcvideo.h | 39 +++++++
2 files changed, 234 insertions(+), 1 deletions(-)
diff --git a/drivers/media/video/uvc/uvc_video.c
b/drivers/media/video/uvc/uvc_video.c
index fc766b9..feb585b 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -358,6 +358,191 @@ int uvc_commit_video(struct uvc_streaming *stream,
}
/*
------------------------------------------------------------------------
+ * Timestamp statistics
+ */
+
+static void uvc_video_stats_decode(struct uvc_streaming *stream,
+ const __u8 *data, int len)
+{
+ unsigned int header_size;
+ bool has_pts = false;
+ bool has_scr = false;
+ u16 scr_sof;
+ u32 scr_stc;
+ u32 pts;
+
+ if (stream->stats.stream.nb_frames == 0 &&
+ stream->stats.frame.nb_packets == 0)
+ ktime_get_ts(&stream->stats.stream.start_ts);
+
+ switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
+ case UVC_STREAM_PTS | UVC_STREAM_SCR:
+ header_size = 12;
+ has_pts = true;
+ has_scr = true;
+ break;
+ case UVC_STREAM_PTS:
+ header_size = 6;
+ has_pts = true;
+ break;
+ case UVC_STREAM_SCR:
+ header_size = 8;
+ has_scr = true;
+ break;
+ default:
+ header_size = 2;
+ break;
+ }
+
+ /* Check for invalid headers. */
+ if (len < header_size) {
+ stream->stats.frame.nb_invalid_headers++;
+ return;
+ }
+
+ /* Extract the timestamps. */
+ if (has_pts)
+ pts = get_unaligned_le32(&data[2]);
+
+ if (has_scr) {
+ scr_stc = get_unaligned_le32(&data[header_size - 6]);
+ scr_sof = get_unaligned_le16(&data[header_size - 2]);
+ }
+
+ /* Is PTS constant through the whole frame ? */
+ if (has_pts && stream->stats.frame.nb_pts) {
+ if (stream->stats.frame.pts != pts) {
+ stream->stats.frame.nb_pts_diffs++;
+ stream->stats.frame.last_pts_diff =
+ stream->stats.frame.nb_packets;
+ }
+ }
+
+ if (has_pts) {
+ stream->stats.frame.nb_pts++;
+ stream->stats.frame.pts = pts;
+ }
+
+ /* Do all frames have a PTS in their first non-empty packet, or before
+ * their first empty packet ?
+ */
+ if (stream->stats.frame.size == 0) {
+ if (len > header_size)
+ stream->stats.frame.has_initial_pts = has_pts;
+ if (len == header_size && has_pts)
+ stream->stats.frame.has_early_pts = true;
+ }
+
+ /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */
+ if (has_scr && stream->stats.frame.nb_scr) {
+ if (stream->stats.frame.scr_stc != scr_stc)
+ stream->stats.frame.nb_scr_diffs++;
+ }
+
+ if (has_scr) {
+ /* Expand the SOF counter to 32 bits and store its value. */
+ if (stream->stats.stream.nb_frames > 0 ||
+ stream->stats.frame.nb_scr > 0)
+ stream->stats.stream.scr_sof_count +=
+ (scr_sof - stream->stats.stream.scr_sof) % 2048;
+ stream->stats.stream.scr_sof = scr_sof;
+
+ stream->stats.frame.nb_scr++;
+ stream->stats.frame.scr_stc = scr_stc;
+ stream->stats.frame.scr_sof = scr_sof;
+
+ if (scr_sof < stream->stats.stream.min_sof)
+ stream->stats.stream.min_sof = scr_sof;
+ if (scr_sof > stream->stats.stream.max_sof)
+ stream->stats.stream.max_sof = scr_sof;
+ }
+
+ if (stream->stats.frame.size == 0 && len > header_size)
+ stream->stats.frame.first_data = stream->stats.frame.nb_packets;
+
+ stream->stats.frame.size += len - header_size;
+ stream->stats.frame.nb_packets++;
+ if (len > header_size)
+ stream->stats.frame.nb_non_empty_packets++;
+
+ if (data[1] & UVC_STREAM_ERR)
+ stream->stats.frame.nb_errors++;
+}
+
+static void uvc_video_stats_update(struct uvc_streaming *stream)
+{
+ struct uvc_stats_frame *frame = &stream->stats.frame;
+
+ uvc_trace(UVC_TRACE_TIMESTAMP, "frame %u stats: %u/%u/%u packets "
+ "%u/%u/%u pts (%searly %sinitial) %u/%u scr\n",
+ stream->sequence, frame->first_data,
+ frame->nb_non_empty_packets, frame->nb_packets,
+ frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts,
+ frame->has_early_pts ? "" : "!",
+ frame->has_initial_pts ? "" : "!",
+ frame->nb_scr_diffs, frame->nb_scr);
+
+ stream->stats.stream.nb_frames++;
+
+ if (frame->has_early_pts)
+ stream->stats.stream.nb_pts_early++;
+ if (frame->has_initial_pts)
+ stream->stats.stream.nb_pts_initial++;
+ if (frame->last_pts_diff <= frame->first_data)
+ stream->stats.stream.nb_pts_constant++;
+ if (frame->nb_scr >= frame->nb_non_empty_packets)
+ stream->stats.stream.nb_scr_count_ok++;
+ if (frame->nb_scr_diffs + 1 == frame->nb_scr)
+ stream->stats.stream.nb_scr_diffs_ok++;
+
+ memset(&stream->stats.frame, 0, sizeof(stream->stats.frame));
+}
+
+static void uvc_video_stats_dump(struct uvc_streaming *stream)
+{
+ unsigned int scr_sof_freq;
+ unsigned int duration;
+ struct timespec ts;
+
+ ktime_get_ts(&ts);
+
+ ts.tv_sec -= stream->stats.stream.start_ts.tv_sec;
+ ts.tv_nsec -= stream->stats.stream.start_ts.tv_nsec;
+ if (ts.tv_nsec < 0) {
+ ts.tv_sec--;
+ ts.tv_nsec += 1000000000;
+ }
+
+ /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF
+ * frequency this will not overflow before more than 1h.
+ */
+ duration = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ if (duration != 0)
+ scr_sof_freq = stream->stats.stream.scr_sof_count * 1000
+ / duration;
+ else
+ scr_sof_freq = 0;
+
+ uvc_trace(UVC_TRACE_TIMESTAMP, "stream stats: %u frames %u early pts "
+ "%u initial pts %u pts ok %u scr count ok %u scr diff ok, "
+ "%u <= sof <= %u, sof freq %u.%03u kHz\n",
+ stream->stats.stream.nb_frames,
+ stream->stats.stream.nb_pts_early,
+ stream->stats.stream.nb_pts_initial,
+ stream->stats.stream.nb_pts_constant,
+ stream->stats.stream.nb_scr_count_ok,
+ stream->stats.stream.nb_scr_diffs_ok,
+ stream->stats.stream.min_sof, stream->stats.stream.max_sof,
+ scr_sof_freq / 1000, scr_sof_freq % 1000);
+}
+
+static void uvc_video_stats_init(struct uvc_streaming *stream)
+{
+ memset(&stream->stats, 0, sizeof(stream->stats));
+ stream->stats.stream.min_sof = 2048;
+}
+
+/* ------------------------------------------------------------------------
* Video codecs
*/
@@ -431,8 +616,13 @@ static int uvc_video_decode_start(struct
uvc_streaming *stream,
/* Increase the sequence number regardless of any buffer states, so
* that discontinuous sequence numbers always indicate lost frames.
*/
- if (stream->last_fid != fid)
+ if (stream->last_fid != fid) {
stream->sequence++;
+ if (stream->sequence)
+ uvc_video_stats_update(stream);
+ }
+
+ uvc_video_stats_decode(stream, data, len);
/* Store the payload FID bit and return immediately when the buffer is
* NULL.
@@ -861,6 +1051,8 @@ static void uvc_uninit_video(struct uvc_streaming
*stream, int free_buffers)
struct urb *urb;
unsigned int i;
+ uvc_video_stats_dump(stream);
+
for (i = 0; i < UVC_URBS; ++i) {
urb = stream->urb[i];
if (urb == NULL)
@@ -994,6 +1186,8 @@ static int uvc_init_video(struct uvc_streaming
*stream, gfp_t gfp_flags)
stream->bulk.skip_payload = 0;
stream->bulk.payload_size = 0;
+ uvc_video_stats_init(stream);
+
if (intf->num_altsetting > 1) {
struct usb_host_endpoint *best_ep = NULL;
unsigned int best_psize = 3 * 1024;
diff --git a/drivers/media/video/uvc/uvcvideo.h
b/drivers/media/video/uvc/uvcvideo.h
index 45f01e7..40f461c 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -462,6 +462,44 @@ struct uvc_streaming {
__u32 sequence;
__u8 last_fid;
+
+ struct {
+ struct {
+ struct timespec start_ts; /* Stream start timestamp */
+ unsigned int nb_frames; /* Number of frames */
+ unsigned int nb_pts_constant; /* Number of frames with
constant PTS */
+ unsigned int nb_pts_early; /* Number of frames with
early PTS */
+ unsigned int nb_pts_initial; /* Number of frames with
initial PTS */
+ unsigned int nb_scr_count_ok; /* Number of frames with
at least one SCR per non empty packet */
+ unsigned int nb_scr_diffs_ok; /* Number of frames with
varying SCR.STC */
+ unsigned int scr_sof_count; /* STC.SOF counter
accumulated since stream start */
+ unsigned int scr_sof; /* STC.SOF of the last packet */
+ unsigned int min_sof; /* Minimum STC.SOF value */
+ unsigned int max_sof; /* Maximum STC.SOF value */
+ } stream;
+
+ struct uvc_stats_frame {
+ unsigned int size; /* Number of bytes captured */
+ unsigned int first_data; /* Index of the first non-empty
packet */
+
+ unsigned int nb_packets; /* Number of packets */
+ unsigned int nb_non_empty_packets; /* Number of
non-empty packets */
+ unsigned int nb_invalid_headers;/* Number of packets with
an invalid header */
+ unsigned int nb_errors; /* Number of packets with
the error bit set */
+
+ unsigned int nb_pts; /* Number of packets with a PTS
timestamp */
+ unsigned int nb_pts_diffs; /* Number of PTS differences
inside a frame */
+ unsigned int last_pts_diff; /* Index of the last PTS
difference */
+ bool has_initial_pts; /* Whether the first non-empty
packet has a PTS */
+ bool has_early_pts; /* Whether a PTS is present
before the first non-empty packet */
+ u32 pts; /* PTS of the last packet */
+
+ unsigned int nb_scr; /* Number of packets with a SCR
timestamp */
+ unsigned int nb_scr_diffs; /* Number of SCR.STC
differences inside a frame */
+ u16 scr_sof; /* SCR.SOF of the last packet */
+ u32 scr_stc; /* SCR.STC of the last packet */
+ } frame;
+ } stats;
};
enum uvc_device_state {
@@ -529,6 +567,7 @@ struct uvc_driver {
#define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9)
#define UVC_TRACE_VIDEO (1 << 10)
+#define UVC_TRACE_TIMESTAMP (1 << 11)
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
--
1.7.4.1
_______________________________________________
Linux-uvc-devel mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/linux-uvc-devel