Драйвер V4L2 повреждает буферы для 4 USB-камер на Android OMAP5432

У меня OMAP5432 EVM под управлением Android 4.2 с 4 подключенными к USB камерами Logitech C270. Я использую драйвер V4L2 из C-кода NDK для открытия и потоковой передачи с камер в режиме MJPEG. Все работает отлично, кроме сразу после включения камеры.

После включения / выключения камер две или иногда три из них работают правильно, но одна начинает разбрызгивать кадровые буферы, в которых отсутствуют MJPEG SOI 0xFF, 0xD8 в начале буфера, в то время как EOI 0xFF, 0xD9 присутствует.

Закрытие и повторное открытие поврежденных файлов камеры /dev/videoX с помощью posix close() исправляет проблему до следующего цикла питания.

Никогда не бывает с одной подключенной камерой, только с 3 или 4 из них.

Я попытался заменить концентраторы USB, подключив камеры напрямую, но безрезультатно.

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

с последующим

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

данные, которые я получаю, выглядят так:

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  ??(????(????(???

и это одни и те же данные для каждого из буферов в очереди...

Что-то не так с C270 или V4L2 или что-то не так с моим кодом? Кто-нибудь испытывает подобные проблемы?

1 ответ

Хорошо, похоже, что я нашел корень проблемы при просмотре журналов ядра с помощью:

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

команды. Похоже, что VIDIOC_G_FMT выходит из строя для 1 или 2 из 4 камер, не сообщая об ошибке в вызов пользователя land ioctl():

<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

Самый простой обходной путь, который я нашел, - это начать потоковую передачу после открытия камеры, подождать (с таймаутом), пока кадры не начнут течь и, если поврежденные кадры появятся близко, и снова открыть камеру.

В моем коде это выглядит так:

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;
}
Другие вопросы по тегам