Signed-off-by: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
---
 drivers/media/video/uvc/uvc_video.c |  196 ++++++++++++++++++++++++++++++++++-
 drivers/media/video/uvc/uvcvideo.h  |   39 +++++++
 2 files changed, 234 insertions(+), 1 deletions(-)

Hi Yann,

Here's a second version of the patch that prints the estimated SOF frequency.
With this information we should have all the stats we need. Let me know if you
think something is missing. I will then run this on the webcams I own and post
the results.

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 20107fd..2dfa309 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -495,6 +495,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 {
@@ -566,6 +604,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
-- 
Regards,

Laurent Pinchart

_______________________________________________
Linux-uvc-devel mailing list
Linux-uvc-devel@lists.berlios.de
https://lists.berlios.de/mailman/listinfo/linux-uvc-devel

Reply via email to