[media] uvcvideo: Extract timestamp-related statistics
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Thu, 3 Nov 2011 15:30:17 +0000 (12:30 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sun, 11 Dec 2011 13:22:08 +0000 (11:22 -0200)
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/uvc/uvc_video.c
drivers/media/video/uvc/uvcvideo.h

index 1908dd859433ee53a036ba480071032dcdc3e5a7..513ba30f8d57b85439a8922fea022bbff2c7a6f6 100644 (file)
@@ -365,6 +365,11 @@ 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 uninitialized_var(scr_sof);
+       u32 uninitialized_var(scr_stc);
+       u32 uninitialized_var(pts);
 
        if (stream->stats.stream.nb_frames == 0 &&
            stream->stats.frame.nb_packets == 0)
@@ -373,12 +378,16 @@ static void uvc_video_stats_decode(struct uvc_streaming *stream,
        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;
@@ -391,6 +400,63 @@ static void uvc_video_stats_decode(struct uvc_streaming *stream,
                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;
+       }
+
        /* Record the first non-empty packet number. */
        if (stream->stats.frame.size == 0 && len > header_size)
                stream->stats.frame.first_data = stream->stats.frame.nb_packets;
@@ -411,9 +477,16 @@ static void uvc_video_stats_update(struct uvc_streaming *stream)
 {
        struct uvc_stats_frame *frame = &stream->stats.frame;
 
-       uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets\n",
+       uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, "
+                 "%u/%u/%u pts (%searly %sinitial), %u/%u scr, "
+                 "last pts/stc/sof %u/%u/%u\n",
                  stream->sequence, frame->first_data,
-                 frame->nb_packets - frame->nb_empty, frame->nb_packets);
+                 frame->nb_packets - frame->nb_empty, 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,
+                 frame->pts, frame->scr_stc, frame->scr_sof);
 
        stream->stats.stream.nb_frames++;
        stream->stats.stream.nb_packets += stream->stats.frame.nb_packets;
@@ -421,14 +494,47 @@ static void uvc_video_stats_update(struct uvc_streaming *stream)
        stream->stats.stream.nb_errors += stream->stats.frame.nb_errors;
        stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid;
 
+       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_packets - frame->nb_empty)
+               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));
 }
 
 size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
                            size_t size)
 {
+       unsigned int scr_sof_freq;
+       unsigned int duration;
+       struct timespec ts;
        size_t count = 0;
 
+       ts.tv_sec = stream->stats.stream.stop_ts.tv_sec
+                 - stream->stats.stream.start_ts.tv_sec;
+       ts.tv_nsec = stream->stats.stream.stop_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;
+
        count += scnprintf(buf + count, size - count,
                           "frames:  %u\npackets: %u\nempty:   %u\n"
                           "errors:  %u\ninvalid: %u\n",
@@ -437,6 +543,20 @@ size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
                           stream->stats.stream.nb_empty,
                           stream->stats.stream.nb_errors,
                           stream->stats.stream.nb_invalid);
+       count += scnprintf(buf + count, size - count,
+                          "pts: %u early, %u initial, %u ok\n",
+                          stream->stats.stream.nb_pts_early,
+                          stream->stats.stream.nb_pts_initial,
+                          stream->stats.stream.nb_pts_constant);
+       count += scnprintf(buf + count, size - count,
+                          "scr: %u count ok, %u diff ok\n",
+                          stream->stats.stream.nb_scr_count_ok,
+                          stream->stats.stream.nb_scr_diffs_ok);
+       count += scnprintf(buf + count, size - count,
+                          "sof: %u <= sof <= %u, freq %u.%03u kHz\n",
+                          stream->stats.stream.min_sof,
+                          stream->stats.stream.max_sof,
+                          scr_sof_freq / 1000, scr_sof_freq % 1000);
 
        return count;
 }
@@ -444,6 +564,7 @@ size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
 static void uvc_video_stats_start(struct uvc_streaming *stream)
 {
        memset(&stream->stats, 0, sizeof(stream->stats));
+       stream->stats.stream.min_sof = 2048;
 }
 
 static void uvc_video_stats_stop(struct uvc_streaming *stream)
index f9ee62ed01503f10e6f7b954a4b96881b5aeba42..e4d4b6d02024f6797a5555d2c4ed7833fad72d30 100644 (file)
@@ -364,6 +364,18 @@ struct uvc_stats_frame {
        unsigned int nb_empty;          /* Number of empty packets */
        unsigned int nb_invalid;        /* 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 */
 };
 
 struct uvc_stats_stream {
@@ -376,6 +388,17 @@ struct uvc_stats_stream {
        unsigned int nb_empty;          /* Number of empty packets */
        unsigned int nb_invalid;        /* Number of packets with an invalid header */
        unsigned int nb_errors;         /* Number of packets with the error bit set */
+
+       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 */
 };
 
 struct uvc_streaming {