Можно ли получить хороший FPS с помощью камеры Raspberry Pi v4l2 в c++?

Я пытаюсь транслировать видео на Raspberry Pi, используя официальный драйвер V4L2 с камерой Raspberry Pi, с C++ на raspbian (выпуск 2015-02), и у меня проблемы с низким FPS.

В настоящее время я просто создаю окно и копирую буфер на экран (что занимает около 30 мс), тогда как select() занимает около 140 мс (всего 5-6 кадров в секунду). Я также пытался заснуть на 100 мс, и это уменьшило время select() на аналогичную величину (в результате тот же fps). Загрузка процессора составляет около 5-15%.

Я также пытался изменить частоту кадров драйвера из консоли (или system()), но это работает только вниз (например, если я установлю частоту кадров драйвера на 1 кадр/с, я получу 1 кадр/с, но если я установлю его на 90 кадров/с, я все равно получу 5-6 кадров/с, даже если драйвер подтверждает установку на 90 кадров в секунду). Кроме того, при запросе режимов FPS для используемого разрешения я получаю 90 кадров в секунду.

Я включил части кода, относящиеся к V4L2 (код между разными частями опущен):

//////////////////
// Open device
//////////////////
mFD = open(mDevName, O_RDWR | O_NONBLOCK, 0);
if (mFD == -1) ErrnoExit("Open device failed");

//////////////////
// Setup format
//////////////////
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
Xioctl(VIDIOC_G_FMT, &fmt);
mImgWidth = fmt.fmt.pix.width;
mImgHeight = fmt.fmt.pix.height;
cout << "width=" << mImgWidth << " height=" << mImgHeight << "\nbytesperline=" << fmt.fmt.pix.bytesperline << " sizeimage=" << fmt.fmt.pix.sizeimage << "\n";
// For some reason querying the format always sets pixelformat to JPEG
//  no matter the input, so set it back to YUYV
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if (Xioctl(VIDIOC_S_FMT, &fmt) == -1)
{
    cout << "Set video format failed : " << strerror(errno) << "\n";
}

//////////////////
// Setup streaming
//////////////////
struct v4l2_requestbuffers req;

memset(&req, 0, sizeof(req));

req.count = 20;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;

if (-1 == Xioctl(VIDIOC_REQBUFS, &req))
{
    ErrnoExit("Reqbufs");
}
if (req.count < 2)
    throw "Not enough buffer memory !";
mNBuffers = req.count;
mBuffers = new CBuffer[mNBuffers];
if (!mBuffers) throw "Out of memory !";

for (unsigned int i = 0; i < mNBuffers; i++)
{
    struct v4l2_buffer buf;
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    buf.index = i;

    if (-1 == Xioctl(VIDIOC_QUERYBUF, &buf))
        ErrnoExit("Querybuf");

    mBuffers[i].mLength = buf.length;
    mBuffers[i].pStart = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, mFD, buf.m.offset);

    if (mBuffers[i].pStart == MAP_FAILED)
        ErrnoExit("mmap");
}

//////////////////
// Start streaming
//////////////////
unsigned int i;
enum v4l2_buf_type type;
struct v4l2_buffer buf;

for (i = 0; i < mNBuffers; i++)
{
    memset(&buf, 0, sizeof(buf));

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = i;

    if (-1 == Xioctl(VIDIOC_QBUF, &buf))
        ErrnoExit("QBUF");
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (-1==Xioctl(VIDIOC_STREAMON, &type))
    ErrnoExit("STREAMON");

И последние две части в основном цикле:

//////////////////
// Get frame
//////////////////
FD_ZERO(&fds);
    FD_SET(mFD, &fds);
    tv.tv_sec = 3;
    tv.tv_usec = 0;

    struct timespec t0, t1;

    clock_gettime(CLOCK_REALTIME, &t0);

    // This line takes about 140ms which I don't get
    r = select(mFD + 1, &fds, NULL, NULL, &tv);

    clock_gettime(CLOCK_REALTIME, &t1);

    cout << "select time : " << ((float)(t1.tv_sec - t0.tv_sec))*1000.0f + ((float)(t1.tv_nsec - t0.tv_nsec))/1000000.0f << "\n";

    if (-1 == r)
    {
        if (EINTR == errno)
            continue;
        ErrnoExit("select");
    }

    if (r == 0)
        throw "Select timeout\n";

    // Read the frame
    //~ struct v4l2_buffer buf;
    memset(&mCurBuf, 0, sizeof(mCurBuf));
    mCurBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    mCurBuf.memory = V4L2_MEMORY_MMAP;

    // DQBUF about 2ms
    if (-1 == Xioctl(VIDIOC_DQBUF, &mCurBuf))
    {
        if (errno == EAGAIN) continue;
        ErrnoExit("DQBUF");
    }

    clock_gettime(CLOCK_REALTIME, &mCaptureTime);

    // Manage frame in mBuffers[buf.index]
    mCurBufIndex = mCurBuf.index;

    break;
}

//////////////////
// Release frame
//////////////////
if (-1 == Xioctl(VIDIOC_QBUF, &mCurBuf))
    ErrnoExit("VIDIOC_QBUF during mainloop");

person maxbc    schedule 17.04.2015    source источник
comment
Хотя select кажется слишком медленным, почему вы все равно его используете? Зачем вам нужен тайм-аут (который добавляет больше вычислений)?   -  person edmz    schedule 17.04.2015


Ответы (2)


Я рассматривал различные способы использования пикамера и вряд ли являюсь экспертом, но, похоже, именно настройки камеры по умолчанию сдерживают вас. Есть много режимов и переключателей. Я не знаю, доступны ли они через ioctl или как, я только начал. Но мне пришлось использовать программу под названием v4l-ctl, чтобы подготовить все для режима, который я хотел. Глубокий взгляд на этот источник и некоторая корректировка кода должны позволить вам достичь величия. О, и я сомневаюсь, что вызов select является проблемой, он просто ожидает дескриптора, который медленно становится читаемым. В зависимости от режима и т. Д. Может быть обязательное ожидание автоэкспозиции и т. Д. Редактировать: я хотел сказать «настройка по умолчанию», поскольку вы изменили некоторые. Есть также правила, не закрепленные в драйвере.

person cww    schedule 23.04.2015
comment
Спасибо за ответ. Я не проверял ответы, потому что сейчас использую raspicam, который представляет собой оболочку mmal и обеспечивает лучшую производительность. Я не хочу погружаться в саму mmal (недостойная инвестиция для меня!), поэтому промежуточный слой, предлагаемый raspicam, хорош. - person maxbc; 08.06.2015

Формат пикселя имеет значение. Я столкнулся с похожей проблемой низкой частоты кадров и потратил некоторое время на тестирование своей программы на Go и C++ с использованием V4L2 API. Я обнаружил, что Rpi Cam Module имеет хорошее ускорение с пиксельным форматом H.264/MJPG. Я могу легко получить 60 кадров в секунду при разрешении 640 * 480, как и в несжатых форматах, таких как YUYV / RGB. Однако JPEG работает очень медленно. Я могу получить только 4 кадра в секунду даже в 320 * 240. И я также обнаружил, что ток выше (> 700 мА) с JPEG по сравнению с 500 мА с H.264/MJPG.

person EaseWay    schedule 23.10.2016