Я пытаюсь транслировать видео на Raspberry Pi, используя официальный драйвер V4L2 с камерой Raspberry Pi, с C ++ на raspbian (выпуск 2015-02), и у меня проблемы с низким FPS.
В настоящее время я просто создаю окно и копирую буфер на экран (который занимает около 30 мс), тогда как select()
занимает около 140 мс (всего 5-6 кадров в секунду). Я также пытался спать в течение 100 мс, и это уменьшает select()
время на ту же сумму (в результате того же FPS). Загрузка процессора составляет около 5-15%.
Я также попытался изменить драйвер FPS с консоли (или system()
), но это работает только вниз (например, если я установлю fps драйвера равным 1fps, я получу 1fps, но если я установлю его на 90fps, я все равно получу 5-6fps, даже если драйвер подтвердит установку его на 90fps).
Кроме того, при запросе режимов FPS для используемого разрешения я получаю 90fps.
Я включил части кода, связанные с 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");
Я изучал различные способы использования Picamera, и вряд ли я эксперт, но может показаться, что настройки камеры по умолчанию удерживают вас. Есть много режимов и переключателей. Я не знаю, выставляются ли они через ioctls или как, я только начал. Но мне пришлось использовать программу v4l-ctl, чтобы подготовить все к желаемому режиму. Глубокий взгляд на этот источник и некоторый подъем кода позволят вам достичь величия. О, и я сомневаюсь, что вызов select является проблемой, он просто ожидает дескриптор, который медленно становится читаемым. В зависимости от режима и т. Д. Может произойти обязательное ожидание автоэкспозиции и т. Д.
Изменить: я хотел сказать «настройки по умолчанию», как вы изменили некоторые. Есть также правила, не записанные в драйвере.
Пиксельный формат имеет значение. Я столкнулся с подобной проблемой низкого fps и потратил некоторое время на тестирование с использованием моей программы на Go и C ++ с использованием API V4L2. Я обнаружил, что модуль Rpi Cam имеет хорошее ускорение с форматом пикселей H.264 / MJPG. Я могу легко получить 60 кадров в секунду при 640 * 480, так же, как несжатые форматы, такие как YUYV / RGB. Однако JPEG работает очень медленно. Я могу получить только 4fps даже при 320 * 240. И я также обнаружил, что ток выше (> 700 мА) с JPEG по сравнению с 500 мА с H.264 / MJPG.