Hi Yann, What about the following patch ? It will print at the end of the stream the total number of frames and the number of frames with
- an early PTS (PTS present in at least one packet before the first non-empty packet) - an initial PTS (PTS present in the first non-empty packet) - a constant PTS through the whole frame - at least one SCR per non-empty packet - no two consecutive identical SCR Is there anything else worth adding ? Maybe the number of frames with a non-constant SCR.SOF ? --- drivers/media/video/uvc/uvc_video.c | 152 ++++++++++++++++++++++++++++++++++- drivers/media/video/uvc/uvcvideo.h | 34 ++++++++ 2 files changed, 185 insertions(+), 1 deletions(-) diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index fc766b9..19de225 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -358,6 +358,147 @@ 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; + + 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_le32(&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; + } + + /* Does the SCR.STC field 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) { + stream->stats.frame.nb_scr++; + stream->stats.frame.scr_stc = scr_stc; + stream->stats.frame.scr_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) +{ + 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\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); +} + +static void uvc_video_stats_init(struct uvc_streaming *stream) +{ + memset(&stream->stats, 0, sizeof(stream->stats)); +} + +/* ------------------------------------------------------------------------ * Video codecs */ @@ -431,8 +572,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 +1007,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 +1142,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..6ec6c53 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -495,6 +495,39 @@ struct uvc_streaming { __u32 sequence; __u8 last_fid; + + struct { + struct { + 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 */ + } 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 +599,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