Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7bc5edb0 authored by Alexey Fisher's avatar Alexey Fisher Committed by Mauro Carvalho Chehab
Browse files

[media] uvcvideo: Extract video stream statistics



Export the statistics through debugfs.

Signed-off-by: default avatarAlexey Fisher <bug-track@fisher-privat.net>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent edbaa398
Loading
Loading
Loading
Loading
+61 −2
Original line number Diff line number Diff line
@@ -18,6 +18,57 @@

#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
 */
@@ -37,13 +88,21 @@ int uvc_debugfs_init_stream(struct uvc_streaming *stream)

	dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir);
	if (IS_ERR_OR_NULL(dent)) {
		uvc_printk(KERN_INFO, "Unable to create debugfs %s directory.\n",
			   dir_name);
		uvc_printk(KERN_INFO, "Unable to create debugfs %s "
			   "directory.\n", dir_name);
		return -ENODEV;
	}

	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;
}

+107 −2
Original line number Diff line number Diff line
@@ -357,6 +357,100 @@ static int uvc_commit_video(struct uvc_streaming *stream,
	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
 */
@@ -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 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;
	}

	fid = data[1] & UVC_STREAM_FID;

	/* 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.
@@ -860,6 +961,8 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
	struct urb *urb;
	unsigned int i;

	uvc_video_stats_stop(stream);

	for (i = 0; i < UVC_URBS; ++i) {
		urb = stream->urb[i];
		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.payload_size = 0;

	uvc_video_stats_start(stream);

	if (intf->num_altsetting > 1) {
		struct usb_host_endpoint *best_ep = NULL;
		unsigned int best_psize = 3 * 1024;
+31 −1
Original line number Diff line number Diff line
@@ -356,6 +356,28 @@ struct uvc_video_chain {
	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 list_head list;
	struct uvc_device *dev;
@@ -406,6 +428,10 @@ struct uvc_streaming {

	/* debugfs */
	struct dentry *debugfs_dir;
	struct {
		struct uvc_stats_frame frame;
		struct uvc_stats_stream stream;
	} stats;
};

enum uvc_device_state {
@@ -477,6 +503,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_STATS		(1 << 11)

#define UVC_WARN_MINMAX		0
#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,
		struct uvc_buffer *buf);

/* debugfs */
/* debugfs and statistics */
int uvc_debugfs_init(void);
void uvc_debugfs_cleanup(void);
int uvc_debugfs_init_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