Работа с usb видеокамерой в Linux. Часть 2

от автора

   Привествую, Хабр!

   Продолжаем цикл статей про программирование видеокамеры в Linux. В первой части [1], мы рассмотрели механизм открытия и считывания первичных параметров видеоустройства. Была написана простенькая утилита catvd. Сегодня расширим функционал нашей маленькой программы, но сначала надо написать обертку для функции ioctl.

Код метода xioctl

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

Эта обертка позволяет прервать программу если была ошибка и показать сообщение.

&nbsp&nbspПопробуем считать картинку с камеры и сохранить в файл.

Код метода getFrame

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 [*].

&nbsp&nbspРазберем метод initMMAP

Код метода 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];	//всегда в ноль }; 

&nbsp&nbspПосле того, как буфер был проинициализирован, его надо отобразить на область памяти (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> 

&nbsp&nbspДалее необходимо переключить камеру в режим захвата.

Код метода startCapturing

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[↓] включает камеру в режим захвата.

&nbsp&nbspТеперь камера включена и захватывает изображения. Но картинку еще надо получить.

Код метода readFrame

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» видно — картинка скидывается в буфер довольно долго. Для ускорения можно использовать несколько буферов и вытаскивать картинку с первого свободного и т.д.

&nbsp&nbspСегодня была рассмотрена инициализация буфера памяти и чтения из него картинки. Изображение сохраняется в raw формате. Можно открыть программой Shotwell. В следующей статье будет рассмотрен вывод изображению в текстуру (через SDL2), затронуты некоторые форматы изображения и настройки камеры.

предидущий пост поставить закладку V4L2_CAP_STREAMING.

Ресурсы используемые в статье:

  1. Работа с usb видеокамерой в Linux. Часть 1
  2. video for Linux API
  3. подробнее про mmap()

Ссылки на используемые функции:

Исходный код

ссылка на оригинал статьи http://habrahabr.ru/post/212531/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *