diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-11-03 11:30:17 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-12-11 08:22:08 -0500 |
commit | 25738cbd72db53ca1c326bf94915d41086cb4297 (patch) | |
tree | dfff4075245deb1af2a13c6998771632a965313d | |
parent | 7bc5edb00bbd02449576289a50d2900f58e7187a (diff) |
[media] uvcvideo: Extract timestamp-related statistics
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/uvc/uvc_video.c | 125 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 23 |
2 files changed, 146 insertions, 2 deletions
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 1908dd859433..513ba30f8d57 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c | |||
@@ -365,6 +365,11 @@ static void uvc_video_stats_decode(struct uvc_streaming *stream, | |||
365 | const __u8 *data, int len) | 365 | const __u8 *data, int len) |
366 | { | 366 | { |
367 | unsigned int header_size; | 367 | unsigned int header_size; |
368 | bool has_pts = false; | ||
369 | bool has_scr = false; | ||
370 | u16 uninitialized_var(scr_sof); | ||
371 | u32 uninitialized_var(scr_stc); | ||
372 | u32 uninitialized_var(pts); | ||
368 | 373 | ||
369 | if (stream->stats.stream.nb_frames == 0 && | 374 | if (stream->stats.stream.nb_frames == 0 && |
370 | stream->stats.frame.nb_packets == 0) | 375 | stream->stats.frame.nb_packets == 0) |
@@ -373,12 +378,16 @@ static void uvc_video_stats_decode(struct uvc_streaming *stream, | |||
373 | switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { | 378 | switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { |
374 | case UVC_STREAM_PTS | UVC_STREAM_SCR: | 379 | case UVC_STREAM_PTS | UVC_STREAM_SCR: |
375 | header_size = 12; | 380 | header_size = 12; |
381 | has_pts = true; | ||
382 | has_scr = true; | ||
376 | break; | 383 | break; |
377 | case UVC_STREAM_PTS: | 384 | case UVC_STREAM_PTS: |
378 | header_size = 6; | 385 | header_size = 6; |
386 | has_pts = true; | ||
379 | break; | 387 | break; |
380 | case UVC_STREAM_SCR: | 388 | case UVC_STREAM_SCR: |
381 | header_size = 8; | 389 | header_size = 8; |
390 | has_scr = true; | ||
382 | break; | 391 | break; |
383 | default: | 392 | default: |
384 | header_size = 2; | 393 | header_size = 2; |
@@ -391,6 +400,63 @@ static void uvc_video_stats_decode(struct uvc_streaming *stream, | |||
391 | return; | 400 | return; |
392 | } | 401 | } |
393 | 402 | ||
403 | /* Extract the timestamps. */ | ||
404 | if (has_pts) | ||
405 | pts = get_unaligned_le32(&data[2]); | ||
406 | |||
407 | if (has_scr) { | ||
408 | scr_stc = get_unaligned_le32(&data[header_size - 6]); | ||
409 | scr_sof = get_unaligned_le16(&data[header_size - 2]); | ||
410 | } | ||
411 | |||
412 | /* Is PTS constant through the whole frame ? */ | ||
413 | if (has_pts && stream->stats.frame.nb_pts) { | ||
414 | if (stream->stats.frame.pts != pts) { | ||
415 | stream->stats.frame.nb_pts_diffs++; | ||
416 | stream->stats.frame.last_pts_diff = | ||
417 | stream->stats.frame.nb_packets; | ||
418 | } | ||
419 | } | ||
420 | |||
421 | if (has_pts) { | ||
422 | stream->stats.frame.nb_pts++; | ||
423 | stream->stats.frame.pts = pts; | ||
424 | } | ||
425 | |||
426 | /* Do all frames have a PTS in their first non-empty packet, or before | ||
427 | * their first empty packet ? | ||
428 | */ | ||
429 | if (stream->stats.frame.size == 0) { | ||
430 | if (len > header_size) | ||
431 | stream->stats.frame.has_initial_pts = has_pts; | ||
432 | if (len == header_size && has_pts) | ||
433 | stream->stats.frame.has_early_pts = true; | ||
434 | } | ||
435 | |||
436 | /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */ | ||
437 | if (has_scr && stream->stats.frame.nb_scr) { | ||
438 | if (stream->stats.frame.scr_stc != scr_stc) | ||
439 | stream->stats.frame.nb_scr_diffs++; | ||
440 | } | ||
441 | |||
442 | if (has_scr) { | ||
443 | /* Expand the SOF counter to 32 bits and store its value. */ | ||
444 | if (stream->stats.stream.nb_frames > 0 || | ||
445 | stream->stats.frame.nb_scr > 0) | ||
446 | stream->stats.stream.scr_sof_count += | ||
447 | (scr_sof - stream->stats.stream.scr_sof) % 2048; | ||
448 | stream->stats.stream.scr_sof = scr_sof; | ||
449 | |||
450 | stream->stats.frame.nb_scr++; | ||
451 | stream->stats.frame.scr_stc = scr_stc; | ||
452 | stream->stats.frame.scr_sof = scr_sof; | ||
453 | |||
454 | if (scr_sof < stream->stats.stream.min_sof) | ||
455 | stream->stats.stream.min_sof = scr_sof; | ||
456 | if (scr_sof > stream->stats.stream.max_sof) | ||
457 | stream->stats.stream.max_sof = scr_sof; | ||
458 | } | ||
459 | |||
394 | /* Record the first non-empty packet number. */ | 460 | /* Record the first non-empty packet number. */ |
395 | if (stream->stats.frame.size == 0 && len > header_size) | 461 | if (stream->stats.frame.size == 0 && len > header_size) |
396 | stream->stats.frame.first_data = stream->stats.frame.nb_packets; | 462 | stream->stats.frame.first_data = stream->stats.frame.nb_packets; |
@@ -411,9 +477,16 @@ static void uvc_video_stats_update(struct uvc_streaming *stream) | |||
411 | { | 477 | { |
412 | struct uvc_stats_frame *frame = &stream->stats.frame; | 478 | struct uvc_stats_frame *frame = &stream->stats.frame; |
413 | 479 | ||
414 | uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets\n", | 480 | uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, " |
481 | "%u/%u/%u pts (%searly %sinitial), %u/%u scr, " | ||
482 | "last pts/stc/sof %u/%u/%u\n", | ||
415 | stream->sequence, frame->first_data, | 483 | stream->sequence, frame->first_data, |
416 | frame->nb_packets - frame->nb_empty, frame->nb_packets); | 484 | frame->nb_packets - frame->nb_empty, frame->nb_packets, |
485 | frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts, | ||
486 | frame->has_early_pts ? "" : "!", | ||
487 | frame->has_initial_pts ? "" : "!", | ||
488 | frame->nb_scr_diffs, frame->nb_scr, | ||
489 | frame->pts, frame->scr_stc, frame->scr_sof); | ||
417 | 490 | ||
418 | stream->stats.stream.nb_frames++; | 491 | stream->stats.stream.nb_frames++; |
419 | stream->stats.stream.nb_packets += stream->stats.frame.nb_packets; | 492 | stream->stats.stream.nb_packets += stream->stats.frame.nb_packets; |
@@ -421,14 +494,47 @@ static void uvc_video_stats_update(struct uvc_streaming *stream) | |||
421 | stream->stats.stream.nb_errors += stream->stats.frame.nb_errors; | 494 | stream->stats.stream.nb_errors += stream->stats.frame.nb_errors; |
422 | stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid; | 495 | stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid; |
423 | 496 | ||
497 | if (frame->has_early_pts) | ||
498 | stream->stats.stream.nb_pts_early++; | ||
499 | if (frame->has_initial_pts) | ||
500 | stream->stats.stream.nb_pts_initial++; | ||
501 | if (frame->last_pts_diff <= frame->first_data) | ||
502 | stream->stats.stream.nb_pts_constant++; | ||
503 | if (frame->nb_scr >= frame->nb_packets - frame->nb_empty) | ||
504 | stream->stats.stream.nb_scr_count_ok++; | ||
505 | if (frame->nb_scr_diffs + 1 == frame->nb_scr) | ||
506 | stream->stats.stream.nb_scr_diffs_ok++; | ||
507 | |||
424 | memset(&stream->stats.frame, 0, sizeof(stream->stats.frame)); | 508 | memset(&stream->stats.frame, 0, sizeof(stream->stats.frame)); |
425 | } | 509 | } |
426 | 510 | ||
427 | size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, | 511 | size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, |
428 | size_t size) | 512 | size_t size) |
429 | { | 513 | { |
514 | unsigned int scr_sof_freq; | ||
515 | unsigned int duration; | ||
516 | struct timespec ts; | ||
430 | size_t count = 0; | 517 | size_t count = 0; |
431 | 518 | ||
519 | ts.tv_sec = stream->stats.stream.stop_ts.tv_sec | ||
520 | - stream->stats.stream.start_ts.tv_sec; | ||
521 | ts.tv_nsec = stream->stats.stream.stop_ts.tv_nsec | ||
522 | - stream->stats.stream.start_ts.tv_nsec; | ||
523 | if (ts.tv_nsec < 0) { | ||
524 | ts.tv_sec--; | ||
525 | ts.tv_nsec += 1000000000; | ||
526 | } | ||
527 | |||
528 | /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF | ||
529 | * frequency this will not overflow before more than 1h. | ||
530 | */ | ||
531 | duration = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; | ||
532 | if (duration != 0) | ||
533 | scr_sof_freq = stream->stats.stream.scr_sof_count * 1000 | ||
534 | / duration; | ||
535 | else | ||
536 | scr_sof_freq = 0; | ||
537 | |||
432 | count += scnprintf(buf + count, size - count, | 538 | count += scnprintf(buf + count, size - count, |
433 | "frames: %u\npackets: %u\nempty: %u\n" | 539 | "frames: %u\npackets: %u\nempty: %u\n" |
434 | "errors: %u\ninvalid: %u\n", | 540 | "errors: %u\ninvalid: %u\n", |
@@ -437,6 +543,20 @@ size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, | |||
437 | stream->stats.stream.nb_empty, | 543 | stream->stats.stream.nb_empty, |
438 | stream->stats.stream.nb_errors, | 544 | stream->stats.stream.nb_errors, |
439 | stream->stats.stream.nb_invalid); | 545 | stream->stats.stream.nb_invalid); |
546 | count += scnprintf(buf + count, size - count, | ||
547 | "pts: %u early, %u initial, %u ok\n", | ||
548 | stream->stats.stream.nb_pts_early, | ||
549 | stream->stats.stream.nb_pts_initial, | ||
550 | stream->stats.stream.nb_pts_constant); | ||
551 | count += scnprintf(buf + count, size - count, | ||
552 | "scr: %u count ok, %u diff ok\n", | ||
553 | stream->stats.stream.nb_scr_count_ok, | ||
554 | stream->stats.stream.nb_scr_diffs_ok); | ||
555 | count += scnprintf(buf + count, size - count, | ||
556 | "sof: %u <= sof <= %u, freq %u.%03u kHz\n", | ||
557 | stream->stats.stream.min_sof, | ||
558 | stream->stats.stream.max_sof, | ||
559 | scr_sof_freq / 1000, scr_sof_freq % 1000); | ||
440 | 560 | ||
441 | return count; | 561 | return count; |
442 | } | 562 | } |
@@ -444,6 +564,7 @@ size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, | |||
444 | static void uvc_video_stats_start(struct uvc_streaming *stream) | 564 | static void uvc_video_stats_start(struct uvc_streaming *stream) |
445 | { | 565 | { |
446 | memset(&stream->stats, 0, sizeof(stream->stats)); | 566 | memset(&stream->stats, 0, sizeof(stream->stats)); |
567 | stream->stats.stream.min_sof = 2048; | ||
447 | } | 568 | } |
448 | 569 | ||
449 | static void uvc_video_stats_stop(struct uvc_streaming *stream) | 570 | static void uvc_video_stats_stop(struct uvc_streaming *stream) |
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index f9ee62ed0150..e4d4b6d02024 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -364,6 +364,18 @@ struct uvc_stats_frame { | |||
364 | unsigned int nb_empty; /* Number of empty packets */ | 364 | unsigned int nb_empty; /* Number of empty packets */ |
365 | unsigned int nb_invalid; /* Number of packets with an invalid header */ | 365 | unsigned int nb_invalid; /* Number of packets with an invalid header */ |
366 | unsigned int nb_errors; /* Number of packets with the error bit set */ | 366 | unsigned int nb_errors; /* Number of packets with the error bit set */ |
367 | |||
368 | unsigned int nb_pts; /* Number of packets with a PTS timestamp */ | ||
369 | unsigned int nb_pts_diffs; /* Number of PTS differences inside a frame */ | ||
370 | unsigned int last_pts_diff; /* Index of the last PTS difference */ | ||
371 | bool has_initial_pts; /* Whether the first non-empty packet has a PTS */ | ||
372 | bool has_early_pts; /* Whether a PTS is present before the first non-empty packet */ | ||
373 | u32 pts; /* PTS of the last packet */ | ||
374 | |||
375 | unsigned int nb_scr; /* Number of packets with a SCR timestamp */ | ||
376 | unsigned int nb_scr_diffs; /* Number of SCR.STC differences inside a frame */ | ||
377 | u16 scr_sof; /* SCR.SOF of the last packet */ | ||
378 | u32 scr_stc; /* SCR.STC of the last packet */ | ||
367 | }; | 379 | }; |
368 | 380 | ||
369 | struct uvc_stats_stream { | 381 | struct uvc_stats_stream { |
@@ -376,6 +388,17 @@ struct uvc_stats_stream { | |||
376 | unsigned int nb_empty; /* Number of empty packets */ | 388 | unsigned int nb_empty; /* Number of empty packets */ |
377 | unsigned int nb_invalid; /* Number of packets with an invalid header */ | 389 | unsigned int nb_invalid; /* Number of packets with an invalid header */ |
378 | unsigned int nb_errors; /* Number of packets with the error bit set */ | 390 | unsigned int nb_errors; /* Number of packets with the error bit set */ |
391 | |||
392 | unsigned int nb_pts_constant; /* Number of frames with constant PTS */ | ||
393 | unsigned int nb_pts_early; /* Number of frames with early PTS */ | ||
394 | unsigned int nb_pts_initial; /* Number of frames with initial PTS */ | ||
395 | |||
396 | unsigned int nb_scr_count_ok; /* Number of frames with at least one SCR per non empty packet */ | ||
397 | unsigned int nb_scr_diffs_ok; /* Number of frames with varying SCR.STC */ | ||
398 | unsigned int scr_sof_count; /* STC.SOF counter accumulated since stream start */ | ||
399 | unsigned int scr_sof; /* STC.SOF of the last packet */ | ||
400 | unsigned int min_sof; /* Minimum STC.SOF value */ | ||
401 | unsigned int max_sof; /* Maximum STC.SOF value */ | ||
379 | }; | 402 | }; |
380 | 403 | ||
381 | struct uvc_streaming { | 404 | struct uvc_streaming { |