[media] uvcvideo: Extract video stream statistics
Export the statistics through debugfs. Signed-off-by: Alexey Fisher <bug-track@fisher-privat.net> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
edbaa39842
commit
7bc5edb00b
@ -18,6 +18,57 @@
|
|||||||
|
|
||||||
#include "uvcvideo.h"
|
#include "uvcvideo.h"
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* Statistics
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define UVC_DEBUGFS_BUF_SIZE 1024
|
||||||
|
|
||||||
|
struct uvc_debugfs_buffer {
|
||||||
|
size_t count;
|
||||||
|
char data[UVC_DEBUGFS_BUF_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int uvc_debugfs_stats_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
struct uvc_streaming *stream = inode->i_private;
|
||||||
|
struct uvc_debugfs_buffer *buf;
|
||||||
|
|
||||||
|
buf = kmalloc(sizeof(*buf), GFP_KERNEL);
|
||||||
|
if (buf == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
buf->count = uvc_video_stats_dump(stream, buf->data, sizeof(buf->data));
|
||||||
|
|
||||||
|
file->private_data = buf;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf,
|
||||||
|
size_t nbytes, loff_t *ppos)
|
||||||
|
{
|
||||||
|
struct uvc_debugfs_buffer *buf = file->private_data;
|
||||||
|
|
||||||
|
return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data,
|
||||||
|
buf->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uvc_debugfs_stats_release(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
kfree(file->private_data);
|
||||||
|
file->private_data = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations uvc_debugfs_stats_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = uvc_debugfs_stats_open,
|
||||||
|
.llseek = no_llseek,
|
||||||
|
.read = uvc_debugfs_stats_read,
|
||||||
|
.release = uvc_debugfs_stats_release,
|
||||||
|
};
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
* Global and stream initialization/cleanup
|
* Global and stream initialization/cleanup
|
||||||
*/
|
*/
|
||||||
@ -37,13 +88,21 @@ int uvc_debugfs_init_stream(struct uvc_streaming *stream)
|
|||||||
|
|
||||||
dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir);
|
dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir);
|
||||||
if (IS_ERR_OR_NULL(dent)) {
|
if (IS_ERR_OR_NULL(dent)) {
|
||||||
uvc_printk(KERN_INFO, "Unable to create debugfs %s directory.\n",
|
uvc_printk(KERN_INFO, "Unable to create debugfs %s "
|
||||||
dir_name);
|
"directory.\n", dir_name);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->debugfs_dir = dent;
|
stream->debugfs_dir = dent;
|
||||||
|
|
||||||
|
dent = debugfs_create_file("stats", 0444, stream->debugfs_dir,
|
||||||
|
stream, &uvc_debugfs_stats_fops);
|
||||||
|
if (IS_ERR_OR_NULL(dent)) {
|
||||||
|
uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n");
|
||||||
|
uvc_debugfs_cleanup_stream(stream);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,6 +357,100 @@ static int uvc_commit_video(struct uvc_streaming *stream,
|
|||||||
return uvc_set_video_ctrl(stream, probe, 0);
|
return uvc_set_video_ctrl(stream, probe, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
* Stream statistics
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void uvc_video_stats_decode(struct uvc_streaming *stream,
|
||||||
|
const __u8 *data, int len)
|
||||||
|
{
|
||||||
|
unsigned int header_size;
|
||||||
|
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
case UVC_STREAM_PTS:
|
||||||
|
header_size = 6;
|
||||||
|
break;
|
||||||
|
case UVC_STREAM_SCR:
|
||||||
|
header_size = 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
header_size = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for invalid headers. */
|
||||||
|
if (len < header_size || data[0] < header_size) {
|
||||||
|
stream->stats.frame.nb_invalid++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* Update the frame size. */
|
||||||
|
stream->stats.frame.size += len - header_size;
|
||||||
|
|
||||||
|
/* Update the packets counters. */
|
||||||
|
stream->stats.frame.nb_packets++;
|
||||||
|
if (len > header_size)
|
||||||
|
stream->stats.frame.nb_empty++;
|
||||||
|
|
||||||
|
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_STATS, "frame %u stats: %u/%u/%u packets\n",
|
||||||
|
stream->sequence, frame->first_data,
|
||||||
|
frame->nb_packets - frame->nb_empty, frame->nb_packets);
|
||||||
|
|
||||||
|
stream->stats.stream.nb_frames++;
|
||||||
|
stream->stats.stream.nb_packets += stream->stats.frame.nb_packets;
|
||||||
|
stream->stats.stream.nb_empty += stream->stats.frame.nb_empty;
|
||||||
|
stream->stats.stream.nb_errors += stream->stats.frame.nb_errors;
|
||||||
|
stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid;
|
||||||
|
|
||||||
|
memset(&stream->stats.frame, 0, sizeof(stream->stats.frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
count += scnprintf(buf + count, size - count,
|
||||||
|
"frames: %u\npackets: %u\nempty: %u\n"
|
||||||
|
"errors: %u\ninvalid: %u\n",
|
||||||
|
stream->stats.stream.nb_frames,
|
||||||
|
stream->stats.stream.nb_packets,
|
||||||
|
stream->stats.stream.nb_empty,
|
||||||
|
stream->stats.stream.nb_errors,
|
||||||
|
stream->stats.stream.nb_invalid);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uvc_video_stats_start(struct uvc_streaming *stream)
|
||||||
|
{
|
||||||
|
memset(&stream->stats, 0, sizeof(stream->stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uvc_video_stats_stop(struct uvc_streaming *stream)
|
||||||
|
{
|
||||||
|
ktime_get_ts(&stream->stats.stream.stop_ts);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------
|
||||||
* Video codecs
|
* Video codecs
|
||||||
*/
|
*/
|
||||||
@ -406,16 +500,23 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
|
|||||||
* - bHeaderLength value must be at least 2 bytes (see above)
|
* - bHeaderLength value must be at least 2 bytes (see above)
|
||||||
* - bHeaderLength value can't be larger than the packet size.
|
* - bHeaderLength value can't be larger than the packet size.
|
||||||
*/
|
*/
|
||||||
if (len < 2 || data[0] < 2 || data[0] > len)
|
if (len < 2 || data[0] < 2 || data[0] > len) {
|
||||||
|
stream->stats.frame.nb_invalid++;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
fid = data[1] & UVC_STREAM_FID;
|
fid = data[1] & UVC_STREAM_FID;
|
||||||
|
|
||||||
/* Increase the sequence number regardless of any buffer states, so
|
/* Increase the sequence number regardless of any buffer states, so
|
||||||
* that discontinuous sequence numbers always indicate lost frames.
|
* that discontinuous sequence numbers always indicate lost frames.
|
||||||
*/
|
*/
|
||||||
if (stream->last_fid != fid)
|
if (stream->last_fid != fid) {
|
||||||
stream->sequence++;
|
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
|
/* Store the payload FID bit and return immediately when the buffer is
|
||||||
* NULL.
|
* NULL.
|
||||||
@ -860,6 +961,8 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
|
|||||||
struct urb *urb;
|
struct urb *urb;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
uvc_video_stats_stop(stream);
|
||||||
|
|
||||||
for (i = 0; i < UVC_URBS; ++i) {
|
for (i = 0; i < UVC_URBS; ++i) {
|
||||||
urb = stream->urb[i];
|
urb = stream->urb[i];
|
||||||
if (urb == NULL)
|
if (urb == NULL)
|
||||||
@ -999,6 +1102,8 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
|
|||||||
stream->bulk.skip_payload = 0;
|
stream->bulk.skip_payload = 0;
|
||||||
stream->bulk.payload_size = 0;
|
stream->bulk.payload_size = 0;
|
||||||
|
|
||||||
|
uvc_video_stats_start(stream);
|
||||||
|
|
||||||
if (intf->num_altsetting > 1) {
|
if (intf->num_altsetting > 1) {
|
||||||
struct usb_host_endpoint *best_ep = NULL;
|
struct usb_host_endpoint *best_ep = NULL;
|
||||||
unsigned int best_psize = 3 * 1024;
|
unsigned int best_psize = 3 * 1024;
|
||||||
|
@ -356,6 +356,28 @@ struct uvc_video_chain {
|
|||||||
struct mutex ctrl_mutex; /* Protects ctrl.info */
|
struct mutex ctrl_mutex; /* Protects ctrl.info */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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_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 */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct uvc_stats_stream {
|
||||||
|
struct timespec start_ts; /* Stream start timestamp */
|
||||||
|
struct timespec stop_ts; /* Stream stop timestamp */
|
||||||
|
|
||||||
|
unsigned int nb_frames; /* Number of frames */
|
||||||
|
|
||||||
|
unsigned int nb_packets; /* Number of packets */
|
||||||
|
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 */
|
||||||
|
};
|
||||||
|
|
||||||
struct uvc_streaming {
|
struct uvc_streaming {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct uvc_device *dev;
|
struct uvc_device *dev;
|
||||||
@ -406,6 +428,10 @@ struct uvc_streaming {
|
|||||||
|
|
||||||
/* debugfs */
|
/* debugfs */
|
||||||
struct dentry *debugfs_dir;
|
struct dentry *debugfs_dir;
|
||||||
|
struct {
|
||||||
|
struct uvc_stats_frame frame;
|
||||||
|
struct uvc_stats_stream stream;
|
||||||
|
} stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum uvc_device_state {
|
enum uvc_device_state {
|
||||||
@ -477,6 +503,7 @@ struct uvc_driver {
|
|||||||
#define UVC_TRACE_SUSPEND (1 << 8)
|
#define UVC_TRACE_SUSPEND (1 << 8)
|
||||||
#define UVC_TRACE_STATUS (1 << 9)
|
#define UVC_TRACE_STATUS (1 << 9)
|
||||||
#define UVC_TRACE_VIDEO (1 << 10)
|
#define UVC_TRACE_VIDEO (1 << 10)
|
||||||
|
#define UVC_TRACE_STATS (1 << 11)
|
||||||
|
|
||||||
#define UVC_WARN_MINMAX 0
|
#define UVC_WARN_MINMAX 0
|
||||||
#define UVC_WARN_PROBE_DEF 1
|
#define UVC_WARN_PROBE_DEF 1
|
||||||
@ -609,10 +636,13 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
|
|||||||
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
|
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
|
||||||
struct uvc_buffer *buf);
|
struct uvc_buffer *buf);
|
||||||
|
|
||||||
/* debugfs */
|
/* debugfs and statistics */
|
||||||
int uvc_debugfs_init(void);
|
int uvc_debugfs_init(void);
|
||||||
void uvc_debugfs_cleanup(void);
|
void uvc_debugfs_cleanup(void);
|
||||||
int uvc_debugfs_init_stream(struct uvc_streaming *stream);
|
int uvc_debugfs_init_stream(struct uvc_streaming *stream);
|
||||||
void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream);
|
void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream);
|
||||||
|
|
||||||
|
size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user