3
votes

I have OMAP5432 EVM running Android 4.2 with 4 USB connected Logitech C270 cameras. I use V4L2 driver from NDK C-code to open and stream from cameras in MJPEG mode. Everything works fine except right after power cycling cameras.

After power cycling cameras two or sometimes three of them come up correctly but one start spitting frame buffers that are missing MJPEG SOI 0xFF, 0xD8 at the beginning of the buffer while EOI 0xFF, 0xD9 is present.

Closing and reopening offending camera files /dev/videoX with posix close() fixes the problem for good till next power cycle.

Never happens with single camera connected, only with 3 or 4 of those.

I've tried replacing USB hubs, connecting cameras directly to no avail.

struct v4l2_format fm = {0};
fm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int r = xioctl(c->fd, VIDIOC_G_FMT, &fm);
if (r != 0) {
    printf("VIDIOC_G_FMT %s r=%d error %d, %s", c->name, r, errno, strerror(errno));
    return -1;
}
fm.fmt.pix.width       = c->width; // 640
fm.fmt.pix.height      = c->height; // 480
fm.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
fm.fmt.pix.field       = V4L2_FIELD_ANY;
fm.fmt.pix.colorspace  = V4L2_COLORSPACE_JPEG;
if (c->fm != V4L2_PIX_FMT_MJPEG) {
    unsigned int min = fm.fmt.pix.width * 2;
    if (fm.fmt.pix.bytesperline < min) {
        fm.fmt.pix.bytesperline = min;
    }
    min = fm.fmt.pix.bytesperline * fm.fmt.pix.height;
    if (fm.fmt.pix.sizeimage < min) {
        fm.fmt.pix.sizeimage = min;
    }
} else {
    fm.fmt.pix.bytesperline = 0;
    fm.fmt.pix.sizeimage = 0;
}
r = xioctl(c->fd, VIDIOC_S_FMT, &fm);

followed by

static int init_mmap(camera_t_* c) {
    struct v4l2_requestbuffers rq = {0};
    rq.count = BUFFERS;
    rq.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    rq.memory = V4L2_MEMORY_MMAP;
    if (xioctl(c->fd, VIDIOC_REQBUFS, &rq) != 0) {
        if (EINVAL == errno) {
            printf("%s does not support memory mapping", c->name);
            return -1;
        } else {
            printf("VIDIOC_REQBUFS error %d, %s", errno, strerror(errno));
            return -1;
        }
    }
    if (rq.count < 2) {
        printf("Insufficient buffer memory on %s", c->name);
        return -1;
    }
    c->buffers = malloc(rq.count * sizeof(buffer_t));
    if (c->buffers == null) {
        printf("out of memory");
        return -1;
    }
    c->n_buffers = rq.count;
    for (int i = 0; i < rq.count; i++) {
        struct v4l2_buffer b = {0};
        b.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        b.memory = V4L2_MEMORY_MMAP;
        b.index = i;
        if (xioctl(c->fd, VIDIOC_QUERYBUF, &b) != 0) {
            printf("VIDIOC_QUERYBUF error %d, %s", errno, strerror(errno));
            return -1;
        }
        c->buffers[i].length = b.length;
        c->buffers[i].start = mmap(null, b.length, PROT_READ | PROT_WRITE, MAP_SHARED, c->fd, b.m.offset);
        if (c->buffers[i].start == MAP_FAILED) {
            printf("mmap error %d, %s", errno, strerror(errno));
            return -1;
        }
    }
    return 0;
}

the data I am getting back is like this:

missing FFD8 tag 0x688fb000 bytes=6946 length=213333
0x688fb000: AB 74 10 0C  EE FD 34 FB  B3 FA 3E D1  60 9D 33 B0  ?t????4???>?`?3?
0x688fb010: F1 83 7B 8A  AF B8 F9 BF  EF 33 EE FF  89 91 7F 9F  ??{??????3?????
0x688fb020: 47 EF A4 B9  73 1C BC FB  AD 46 6A D5  22 A2 2C 32  G???s????Fj?"?,2
0x688fb030: 2B A8 71 7E  56 56 73 23  15 7B 11 B2  F0 FA AA D3  +?q~VVs#?{??????
.............
0x688fcae2: 00 A2 80 0A  28 03 FF D5  C6 A2 80 0A  28 00 A2 80  ????(???????(???
0x688fcaf2: 0A 28 00 A2  80 0A 28 00  A2 80 0A 28  00 A2 80 0A  ?(????(????(????
0x688fcb02: 28 03 FF D6  C6 A2 80 0A  28 00 A2 80  0A 28 00 A2  (???????(????(??
0x688fcb12: 80 0A 28 00  A2 80 0A 28  00 A2 80 0A  28 03 FF D9  ??(????(????(???

and it is the same data for each of dequeued buffer...

Is it something wrong with C270 or V4L2 or something wrong with my code? Anybody experience similar issues?

1
Upon further investigation in kernel log: usb 1-2.2.1.2: link qh16-0001/ca5bf1c0 start 1 [1/0 us] uvcvideo: uvc_v4l2_ioctl(VIDIOC_G_FMT) uvcvideo: uvc_v4l2_ioctl(VIDIOC_S_FMT) uvcvideo: Trying format 0x47504a4d (MJPG): 640x480. uvcvideo: Using default frame interval 33333.3 us (30.0 fps). usb 1-2.2.1.2: <.process_name> timed out on ep0out len=0/26 uvcvideo: Failed to set UVC probe control : -110 (exp. 26). uvcvideo: uvc_v4l2_ioctl(VIDIOC_G_FMT) uvcvideo: uvc_v4l2_ioctl(VIDIOC_LOG_STATUS) uvcvideo: Unknown ioctl 0x00005646 Without reporting any VIDIOC_S_FMT failure to the application.Leo

1 Answers

1
votes

OK, it looks like I've found a root of the problem looking thru kernel logs with:

adb shell sudo echo 8 > /proc/sys/kernel/printk
adb >/tmp/adb_klog.txt 2>/tmp/adb_klog.txt shell sudo cat /proc/kmsg

commands. It looks like VIDIOC_G_FMT is failing for 1 or 2 out of 4 cameras without reporting the error to the user land ioctl() call:

<7>[ 7274.495178] uvcvideo: uvc_v4l2_open
<7>[ 7274.495239] ehci-omap ehci-omap.0: reused qh ca5bf1c0 schedule
<7>[ 7274.495269] usb 1-2.2.1.2: link qh16-0001/ca5bf1c0 start 1 [1/0 us]
<7>[ 7274.495330] uvcvideo: uvc_v4l2_ioctl(VIDIOC_G_FMT)
<7>[ 7274.495452] uvcvideo: uvc_v4l2_ioctl(VIDIOC_S_FMT)
<7>[ 7274.495483] uvcvideo: Trying format 0x47504a4d (MJPG): 640x480.
<7>[ 7274.495513] uvcvideo: Using default frame interval 33333.3 us (30.0 fps).
<7>[ 7279.495208] usb 1-2.2.1.2: .<process_name> timed out on ep0out len=0/26
<3>[ 7279.495239] uvcvideo: Failed to set UVC probe control : -110 (exp. 26).
<7>[ 7279.502746] uvcvideo: uvc_v4l2_ioctl(VIDIOC_G_FMT)
<7>[ 7279.507080] uvcvideo: uvc_v4l2_ioctl(VIDIOC_LOG_STATUS)
<7>[ 7279.507080] uvcvideo: Unknown ioctl 0x00005646

The simplest workaround I came up with is to start streaming after opening camera, wait (with timeout) till the frames start flowing and if the corrupted frames appear close and reopen the camera.

In my code it looks like this:

static bool has_bad_frames(camera_t* camera) {
    /* Logitech C270 randomly spits corrupted MJPEGs after power cycle. Known workaround is to reopen camera */
    camera_t_* c = (camera_t_*)camera;
    camera_set_callback(c, empty_callback); // otherwise MJPEG frames are not going to be decompressed
    camera_start(camera);
    int retry = 300; // 3 seconds max
    while (retry > 0 && c->good_frames == 0 && c->bad_frames == 0) {
        nsleep(NANOSECONDS_IN_SECOND / 100); /* 1/100 second */
        retry--;
    }
    bool ok = c->bad_frames == 0 && c->good_frames > 0;
    camera_stop(camera);
    return !ok;
}

int camera_open(void* that, camera_t* o, int id, int w, int h, int bpp) {
    int r = try_to_open(that, o, id, w, h, bpp);
    if (r == 0) {
        if (has_bad_frames(*o)) {
            camera_t_* c = (camera_t_*)*o;
            if (c->bad_frames + c->good_frames == 0) {
                trace("%s is not streaming; retrying...", c->name);
            } else {
                trace("%s spits corrupted frames. Probable VIDIOC_S_FMT silently failed; retrying...", c->name);
            }
            camera_close(*o);
            *o = null;
            return try_to_open(that, o, id, w, h, bpp);
        }
    }
    return r;
}