libv4l: add detection for short frames
authorHans de Goede <hdegoede@redhat.com>
Fri, 7 May 2010 09:57:50 +0000 (11:57 +0200)
committerHans de Goede <hdegoede@redhat.com>
Fri, 7 May 2010 09:57:50 +0000 (11:57 +0200)
Sometimes cam generate short frames (esp. at the beginning of the stream),
detect this and retry a number of times. If retrying fails, pass allong
the short frame as it is better then nothing.

This patch also cleans up the JPEG decoding error handling code to use
the same mechanism.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
lib/libv4l2/libv4l2.c
lib/libv4lconvert/libv4lconvert-priv.h
lib/libv4lconvert/libv4lconvert.c

index 9ac44bd..2d97881 100644 (file)
@@ -268,7 +268,7 @@ static int v4l2_queue_read_buffer(int index, int buffer_index)
 static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
                unsigned char *dest, int dest_size)
 {
-       const int max_tries = 10;
+       const int max_tries = V4L2_IGNORE_FIRST_FRAME_ERRORS + 1;
        int result, tries = max_tries;
 
        /* Make sure we have the real v4l2 buffers mapped */
@@ -308,7 +308,7 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
                if (result < 0) {
                        int saved_err = errno;
 
-                       if (errno == EAGAIN)
+                       if (errno == EAGAIN || errno == EPIPE)
                                V4L2_LOG("warning error while converting frame data: %s",
                                                v4lconvert_get_error_message(devices[index].convert));
                        else
@@ -319,7 +319,7 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
                        errno = saved_err;
                }
                tries--;
-       } while (result < 0 && errno == EAGAIN && tries);
+       } while (result < 0 && (errno == EAGAIN || errno == EPIPE) && tries);
 
        if (result < 0 && errno == EAGAIN) {
                V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s",
@@ -327,12 +327,19 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
                errno = EAGAIN;
        }
 
+       if (result < 0 && errno == EPIPE) {
+               V4L2_LOG("got %d consecutive short frame errors, "
+                        "returning short frame");
+               result = devices[index].dest_fmt.fmt.pix.sizeimage;
+               errno = 0;
+       }
+
        return result;
 }
 
 static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size)
 {
-       const int max_tries = 10;
+       const int max_tries = V4L2_IGNORE_FIRST_FRAME_ERRORS + 1;
        int result, buf_size, tries = max_tries;
 
        buf_size = devices[index].dest_fmt.fmt.pix.sizeimage;
@@ -376,7 +383,7 @@ static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size)
                if (result < 0) {
                        int saved_err = errno;
 
-                       if (errno == EAGAIN)
+                       if (errno == EAGAIN || errno == EPIPE)
                                V4L2_LOG("warning error while converting frame data: %s",
                                                v4lconvert_get_error_message(devices[index].convert));
                        else
@@ -386,11 +393,19 @@ static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size)
                        errno = saved_err;
                }
                tries--;
-       } while (result < 0 && errno == EAGAIN && tries);
+       } while (result < 0 && (errno == EAGAIN || errno == EPIPE) && tries);
 
        if (result < 0 && errno == EAGAIN) {
                V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s",
                                max_tries, v4lconvert_get_error_message(devices[index].convert));
+               errno = EAGAIN;
+       }
+
+       if (result < 0 && errno == EPIPE) {
+               V4L2_LOG("got %d consecutive short frame errors, "
+                        "returning short frame");
+               result = devices[index].dest_fmt.fmt.pix.sizeimage;
+               errno = 0;
        }
 
        return result;
index 0094f1b..c9fda05 100644 (file)
@@ -37,7 +37,6 @@
 
 /* Card flags */
 #define V4LCONVERT_IS_UVC                0x01
-#define V4LCONVERT_IS_SN9C20X            0x02
 
 /* Pixformat flags */
 #define V4LCONVERT_COMPRESSED            0x01 /* Compressed format */
index a4a8aa0..014c600 100644 (file)
@@ -134,8 +134,6 @@ struct v4lconvert_data *v4lconvert_create(int fd)
        if (SYS_IOCTL(data->fd, VIDIOC_QUERYCAP, &cap) == 0) {
                if (!strcmp((char *)cap.driver, "uvcvideo"))
                        data->flags |= V4LCONVERT_IS_UVC;
-               else if (!strcmp((char *)cap.driver, "sn9c20x"))
-                       data->flags |= V4LCONVERT_IS_SN9C20X;
 
                if ((cap.capabilities & 0xff) & ~V4L2_CAP_VIDEO_CAPTURE)
                        always_needs_conversion = 0;
@@ -622,23 +620,21 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                }
 
                if (result) {
-                       /* Pixart webcam's seem to regulary generate corrupt frames, which
-                          are best thrown away to avoid flashes in the video stream. Tell
-                          the upper layer this is an intermediate fault and it should try
-                          again with a new buffer by setting errno to EAGAIN */
-                       if (src_pix_fmt == V4L2_PIX_FMT_PJPG ||
-                                       data->flags & V4LCONVERT_IS_SN9C20X) {
-                               V4LCONVERT_ERR("decompressing JPEG: %s",
-                                               tinyjpeg_get_errorstring(data->jdec));
-                               errno = EAGAIN;
-                               return -1;
-                       } else {
-                               /* If the JPEG header checked out ok and we get an error during actual
-                                  decompression, log the error, but don't return an errorcode to the
-                                  application, so that the user gets what we managed to decompress */
-                               fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s",
-                                               tinyjpeg_get_errorstring(data->jdec));
-                       }
+                       /* The JPEG header checked out ok but we got an error
+                          during decompression. Some webcams, esp pixart and
+                          sn9c20x based ones regulary generate corrupt frames,
+                          which are best thrown away to avoid flashes in the
+                          video stream. We use EPIPE to signal the upper layer
+                          we have some video data, but it is incomplete.
+
+                          The upper layer (usually libv4l2) should respond to
+                          this by trying a number of times to get a new frame
+                          and if that fails just passing up whatever we did
+                          manage to decompress. */
+                       V4LCONVERT_ERR("decompressing JPEG: %s",
+                                       tinyjpeg_get_errorstring(data->jdec));
+                       errno = EPIPE;
+                       result = -1;
                }
                break;
 
@@ -803,6 +799,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                /* Deliberate fall through to raw bayer fmt code! */
                src_pix_fmt = tmpfmt.fmt.pix.pixelformat;
                src = tmpbuf;
+               src_size = width * height;
                /* fall through */
        }
 
@@ -825,6 +822,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_bayer_to_yuv420(src, dest, width, height, src_pix_fmt, 1);
                        break;
                }
+               if (src_size < (width * height)) {
+                       V4LCONVERT_ERR("short raw bayer data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        case V4L2_PIX_FMT_RGB565:
@@ -842,6 +844,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_rgb565_to_yuv420(src, dest, fmt, 1);
                        break;
                }
+               if (src_size < (width * height * 2)) {
+                       V4LCONVERT_ERR("short rgb565 data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        case V4L2_PIX_FMT_RGB24:
@@ -856,6 +863,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1);
                        break;
                }
+               if (src_size < (width * height * 3)) {
+                       V4LCONVERT_ERR("short rgb24 data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        case V4L2_PIX_FMT_BGR24:
@@ -870,6 +882,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1);
                        break;
                }
+               if (src_size < (width * height * 3)) {
+                       V4LCONVERT_ERR("short bgr24 data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        case V4L2_PIX_FMT_YUV420:
@@ -886,6 +903,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_swap_uv(src, dest, fmt);
                        break;
                }
+               if (src_size < (width * height * 3 / 2)) {
+                       V4LCONVERT_ERR("short yuv420 data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        case V4L2_PIX_FMT_YVU420:
@@ -902,6 +924,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_swap_uv(src, dest, fmt);
                        break;
                }
+               if (src_size < (width * height * 3 / 2)) {
+                       V4LCONVERT_ERR("short yvu420 data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        case V4L2_PIX_FMT_YUYV:
@@ -919,6 +946,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_yuyv_to_yuv420(src, dest, width, height, 1);
                        break;
                }
+               if (src_size < (width * height * 2)) {
+                       V4LCONVERT_ERR("short yuyv data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        case V4L2_PIX_FMT_YVYU:
@@ -938,6 +970,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_yuyv_to_yuv420(src, dest, width, height, 0);
                        break;
                }
+               if (src_size < (width * height * 2)) {
+                       V4LCONVERT_ERR("short yvyu data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        case V4L2_PIX_FMT_UYVY:
@@ -955,6 +992,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                        v4lconvert_uyvy_to_yuv420(src, dest, width, height, 1);
                        break;
                }
+               if (src_size < (width * height * 2)) {
+                       V4LCONVERT_ERR("short uyvy data frame\n");
+                       errno = EPIPE;
+                       result = -1;
+               }
                break;
 
        default:
@@ -966,7 +1008,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
        fmt->fmt.pix.pixelformat = dest_pix_fmt;
        v4lconvert_fixup_fmt(fmt);
 
-       return 0;
+       return result;
 }
 
 int v4lconvert_convert(struct v4lconvert_data *data,