Продолжаем цикл статей про программирование видеокамеры в Linux. В первой части [1], мы рассмотрели механизм открытия и считывания первичных параметров видеоустройства. Была написана простенькая утилита catvd. Сегодня расширим функционал нашей маленькой программы, но сначала надо написать обертку для функции ioctl.
int videodevice::xioctl(int fd, int request, void *arg) { int r; r = ioctl (fd, request, arg); if(r == -1) { if (errno == EAGAIN) return EAGAIN; stringstream ss; ss << "ioctl code " << request << " "; errno_exit(ss.str()); } return r; }
Эта обертка позволяет прервать программу если была ошибка и показать сообщение.
  Попробуем считать картинку с камеры и сохранить в файл.
void videodevice::getFrame(string file_name) { initMMAP(); startCapturing(); long int i = 0; for (;;) { if(readFrame(file_name)) break; i++; } cout << "iter == " << i << endl; stopCapturing(); freeMMAP(); }
метод readFrame — отвечает за чтение и обработку полученого изображения.
методы initMMAP(), freeMMAP() — создание/очистка буфера памяти устройства.
методы startCapturing(), stopCapturing() — включение/выключение режима streaming у видеоустройства. Наличие этих функций, у камеры, можно проверить флагом V4L2_CAP_STREAMING [*].
  Разберем метод initMMAP
void videodevice::initMMAP() { struct v4l2_requestbuffers req; req.count = 1; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; xioctl(fd, VIDIOC_REQBUFS, &req); devbuffer = (buffer*) calloc(req.count, sizeof(*devbuffer)); struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; xioctl(fd, VIDIOC_QUERYBUF, &buf); devbuffer->length = buf.length; devbuffer->start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (devbuffer->start == MAP_FAILED) errno_exit("mmap"); }
функция VIDIOC_REQBUFS [↓] позволяет проинициализировать буфер памяти внутри устройства. Структура v4l2_requestbuffers задает параметры инициализации
struct v4l2_requestbuffers { __u32 count; //количество буферов __u32 type; //тип или цель использования __u32 memory; //режим работы с памятью. __u32 reserved[2]; //всегда в ноль };
  После того, как буфер был проинициализирован, его надо отобразить на область памяти (mapping).
Функция VIDIOC_QUERYBUF [↓] позволяет считать параметры буфера, которые будут использоваться для создания memory-mapping области. Структура v4l2_buffer большая, опишу необходимые поля:
struct v4l2_buffer { //до выполнения VIDIOC_QUERYBUF устанавливаем следующие поля __u32 index; // ноль или номер буфера (если v4l2_requestbuffers.cout > 1) __u32 type; // тип (совпадает со значением v4l2_requestbuffers.type) //после выполнения VIDIOC_QUERYBUF используем эти поля в качестве параметров для memory-mapping union { __u32 offset; // смещение буфера относительно начала памяти устройства } m; __u32 length; // размер буфера };
системная функция mmap() [3] позволяет отображать файл или область памяти устройств в оперативную память. Для использования mmap() необходимо подключить
<sys/mman.h>
  Далее необходимо переключить камеру в режим захвата.
void videodevice::startCapturing() { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; xioctl(fd, VIDIOC_QBUF, &buf); enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; xioctl(fd, VIDIOC_STREAMON, &type); }
Функция VIDIOC_QBUF [↓] ставит буфер в очередь обработки драйвером устройства. Поля используются такие же, как и для VIDIOC_REQBUFS или VIDIOC_QUERYBUF.
Функция VIDIOC_STREAMON[↓] включает камеру в режим захвата.
  Теперь камера включена и захватывает изображения. Но картинку еще надо получить.
int videodevice::readFrame(string file_name) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (xioctl(fd, VIDIOC_DQBUF, &buf) == EAGAIN) return 0; buffer *temp = devbuffer; FILE *out_file = fopen(file_name.c_str(),"w"); fwrite(temp->start,temp->length,1,out_file); fclose(out_file); return 1; }
Функция VIDIOC_DQBUF[↓] освобождает буфер из очереди обработки драйвера. В результате можем получить ошибку EAGAIN. Ничего опасного в этом нет, надо еще раз вызвать VIDIOC_DQBUF. Это происходит потому, что драйвер еще обрабатывает запрос и не может освободить буфер из очереди. При успешном выполнении этой функции, мы получаем в «руки» нашу картинку. В самом начале статьи, в коде был добавлен итератор. Итератор позволяет проследить сколько итераций вхолостую проходит цикл до успешного выполнения VIDIOC_DQBUF.
Компиляция
$ cmake . $ make
Вывод программы следующий
$./getimage Open device /dev/video0 Init mmap Start capturing read frame from buffer and write to file iter == 831013 stop Capturing free mmap Close device /dev/video0
Из «iter == 831013» видно — картинка скидывается в буфер довольно долго. Для ускорения можно использовать несколько буферов и вытаскивать картинку с первого свободного и т.д.
  Сегодня была рассмотрена инициализация буфера памяти и чтения из него картинки. Изображение сохраняется в raw формате. Можно открыть программой Shotwell. В следующей статье будет рассмотрен вывод изображению в текстуру (через SDL2), затронуты некоторые форматы изображения и настройки камеры.
предидущий пост поставить закладку V4L2_CAP_STREAMING.
Ресурсы используемые в статье:
Ссылки на используемые функции:
ссылка на оригинал статьи http://habrahabr.ru/post/212531/
Добавить комментарий