Home | History | Annotate | Download | only in v4l2capture
      1 // python-v4l2capture
      2 // Python extension to capture video with video4linux2
      3 //
      4 // 2009, 2010, 2011 Fredrik Portstrom
      5 //
      6 // I, the copyright holder of this file, hereby release it into the
      7 // public domain. This applies worldwide. In case this is not legally
      8 // possible: I grant anyone the right to use this work for any
      9 // purpose, without any conditions, unless such conditions are
     10 // required by law.
     11 
     12 #include <Python.h>
     13 #include <fcntl.h>
     14 #include <linux/videodev2.h>
     15 #include <sys/mman.h>
     16 
     17 // LIBV4L is not needed for MJPEG capture.
     18 #undef USE_LIBV4L
     19 
     20 #ifdef USE_LIBV4L
     21 #include <libv4l2.h>
     22 #else
     23 #include <sys/ioctl.h>
     24 #define v4l2_close close
     25 #define v4l2_ioctl ioctl
     26 #define v4l2_mmap mmap
     27 #define v4l2_munmap munmap
     28 #define v4l2_open open
     29 #endif
     30 
     31 #define ASSERT_OPEN if(self->fd < 0)                                    \
     32     {                                                                   \
     33       PyErr_SetString(PyExc_ValueError,                                 \
     34           "I/O operation on closed file");                              \
     35       return NULL;                                                      \
     36     }
     37 
     38 struct buffer {
     39   void *start;
     40   size_t length;
     41 };
     42 
     43 typedef struct {
     44   PyObject_HEAD
     45   int fd;
     46   struct buffer *buffers;
     47   int buffer_count;
     48 } Video_device;
     49 
     50 struct capability {
     51   int id;
     52   const char *name;
     53 };
     54 
     55 static struct capability capabilities[] = {
     56   { V4L2_CAP_ASYNCIO, "asyncio" },
     57   { V4L2_CAP_AUDIO, "audio" },
     58   { V4L2_CAP_HW_FREQ_SEEK, "hw_freq_seek" },
     59   { V4L2_CAP_RADIO, "radio" },
     60   { V4L2_CAP_RDS_CAPTURE, "rds_capture" },
     61   { V4L2_CAP_READWRITE, "readwrite" },
     62   { V4L2_CAP_SLICED_VBI_CAPTURE, "sliced_vbi_capture" },
     63   { V4L2_CAP_SLICED_VBI_OUTPUT, "sliced_vbi_output" },
     64   { V4L2_CAP_STREAMING, "streaming" },
     65   { V4L2_CAP_TUNER, "tuner" },
     66   { V4L2_CAP_VBI_CAPTURE, "vbi_capture" },
     67   { V4L2_CAP_VBI_OUTPUT, "vbi_output" },
     68   { V4L2_CAP_VIDEO_CAPTURE, "video_capture" },
     69   { V4L2_CAP_VIDEO_OUTPUT, "video_output" },
     70   { V4L2_CAP_VIDEO_OUTPUT_OVERLAY, "video_output_overlay" },
     71   { V4L2_CAP_VIDEO_OVERLAY, "video_overlay" }
     72 };
     73 
     74 static int my_ioctl(int fd, int request, void *arg)
     75 {
     76   // Retry ioctl until it returns without being interrupted.
     77 
     78   for(;;)
     79     {
     80       int result = v4l2_ioctl(fd, request, arg);
     81 
     82       if(!result)
     83         {
     84           return 0;
     85         }
     86 
     87       if(errno != EINTR)
     88         {
     89           PyErr_SetFromErrno(PyExc_IOError);
     90           return 1;
     91         }
     92     }
     93 }
     94 
     95 static void Video_device_unmap(Video_device *self)
     96 {
     97   int i;
     98 
     99   for(i = 0; i < self->buffer_count; i++)
    100     {
    101       v4l2_munmap(self->buffers[i].start, self->buffers[i].length);
    102     }
    103   free(self->buffers);
    104   self->buffers = NULL;
    105 }
    106 
    107 static void Video_device_dealloc(Video_device *self)
    108 {
    109   if(self->fd >= 0)
    110     {
    111       if(self->buffers)
    112         {
    113           Video_device_unmap(self);
    114         }
    115 
    116       v4l2_close(self->fd);
    117     }
    118 
    119   self->ob_type->tp_free((PyObject *)self);
    120 }
    121 
    122 static int Video_device_init(Video_device *self, PyObject *args,
    123     PyObject *kwargs)
    124 {
    125   const char *device_path;
    126 
    127   if(!PyArg_ParseTuple(args, "s", &device_path))
    128     {
    129       return -1;
    130     }
    131 
    132   int fd = v4l2_open(device_path, O_RDWR | O_NONBLOCK);
    133 
    134   if(fd < 0)
    135     {
    136       PyErr_SetFromErrnoWithFilename(PyExc_IOError, (char *)device_path);
    137       return -1;
    138     }
    139 
    140   self->fd = fd;
    141   self->buffers = NULL;
    142   self->buffer_count = 0;
    143   return 0;
    144 }
    145 
    146 static PyObject *Video_device_close(Video_device *self)
    147 {
    148   if(self->fd >= 0)
    149     {
    150       if(self->buffers)
    151         {
    152           Video_device_unmap(self);
    153         }
    154 
    155       v4l2_close(self->fd);
    156       self->fd = -1;
    157     }
    158 
    159   Py_RETURN_NONE;
    160 }
    161 
    162 static PyObject *Video_device_fileno(Video_device *self)
    163 {
    164   ASSERT_OPEN;
    165   return PyInt_FromLong(self->fd);
    166 }
    167 
    168 static PyObject *Video_device_get_info(Video_device *self)
    169 {
    170   ASSERT_OPEN;
    171   struct v4l2_capability caps;
    172 
    173   if(my_ioctl(self->fd, VIDIOC_QUERYCAP, &caps))
    174     {
    175       return NULL;
    176     }
    177 
    178   PyObject *set = PySet_New(NULL);
    179 
    180   if(!set)
    181     {
    182       return NULL;
    183     }
    184 
    185   struct capability *capability = capabilities;
    186 
    187   while((void *)capability < (void *)capabilities + sizeof(capabilities))
    188     {
    189       if(caps.capabilities & capability->id)
    190         {
    191           PyObject *s = PyString_FromString(capability->name);
    192 
    193           if(!s)
    194             {
    195               Py_DECREF(set);
    196               return NULL;
    197             }
    198 
    199           PySet_Add(set, s);
    200         }
    201 
    202       capability++;
    203     }
    204 
    205   return Py_BuildValue("sssO", caps.driver, caps.card, caps.bus_info, set);
    206 }
    207 
    208 static PyObject *Video_device_set_format(Video_device *self, PyObject *args)
    209 {
    210   int size_x;
    211   int size_y;
    212   if(!PyArg_ParseTuple(args, "ii", &size_x, &size_y))
    213     {
    214       return NULL;
    215     }
    216 
    217   struct v4l2_format format;
    218   format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    219   format.fmt.pix.width = size_x;
    220   format.fmt.pix.height = size_y;
    221   format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    222   format.fmt.pix.field = V4L2_FIELD_NONE;
    223   format.fmt.pix.bytesperline = 0;
    224 
    225   if(my_ioctl(self->fd, VIDIOC_S_FMT, &format))
    226     {
    227       return NULL;
    228     }
    229 
    230   return Py_BuildValue("ii", format.fmt.pix.width, format.fmt.pix.height);
    231 }
    232 
    233 static PyObject *Video_device_set_fps(Video_device *self, PyObject *args)
    234 {
    235   int fps;
    236   if(!PyArg_ParseTuple(args, "i", &fps))
    237     {
    238       return NULL;
    239     }
    240   struct v4l2_streamparm setfps;
    241   memset(&setfps, 0, sizeof(struct v4l2_streamparm));
    242   setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    243   setfps.parm.capture.timeperframe.numerator = 1;
    244   setfps.parm.capture.timeperframe.denominator = fps;
    245   if(my_ioctl(self->fd, VIDIOC_S_PARM, &setfps)){
    246         return NULL;
    247   }
    248   return Py_BuildValue("i",setfps.parm.capture.timeperframe.denominator);
    249 }
    250 
    251 static PyObject *Video_device_start(Video_device *self)
    252 {
    253   ASSERT_OPEN;
    254   enum v4l2_buf_type type;
    255   type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    256 
    257   if(my_ioctl(self->fd, VIDIOC_STREAMON, &type))
    258     {
    259       return NULL;
    260     }
    261 
    262   Py_RETURN_NONE;
    263 }
    264 
    265 static PyObject *Video_device_stop(Video_device *self)
    266 {
    267   ASSERT_OPEN;
    268   enum v4l2_buf_type type;
    269   type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    270 
    271   if(my_ioctl(self->fd, VIDIOC_STREAMOFF, &type))
    272     {
    273       return NULL;
    274     }
    275 
    276   Py_RETURN_NONE;
    277 }
    278 
    279 static PyObject *Video_device_create_buffers(Video_device *self, PyObject *args)
    280 {
    281   int buffer_count;
    282 
    283   if(!PyArg_ParseTuple(args, "I", &buffer_count))
    284     {
    285       return NULL;
    286     }
    287 
    288   ASSERT_OPEN;
    289 
    290   if(self->buffers)
    291     {
    292       PyErr_SetString(PyExc_ValueError, "Buffers are already created");
    293       return NULL;
    294     }
    295 
    296   struct v4l2_requestbuffers reqbuf;
    297   reqbuf.count = buffer_count;
    298   reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    299   reqbuf.memory = V4L2_MEMORY_MMAP;
    300 
    301   if(my_ioctl(self->fd, VIDIOC_REQBUFS, &reqbuf))
    302     {
    303       return NULL;
    304     }
    305 
    306   if(!reqbuf.count)
    307     {
    308       PyErr_SetString(PyExc_IOError, "Not enough buffer memory");
    309       return NULL;
    310     }
    311 
    312   self->buffers = malloc(reqbuf.count * sizeof(struct buffer));
    313 
    314   if(!self->buffers)
    315     {
    316       PyErr_NoMemory();
    317       return NULL;
    318     }
    319 
    320   int i;
    321 
    322   for(i = 0; i < reqbuf.count; i++)
    323     {
    324       struct v4l2_buffer buffer;
    325       buffer.index = i;
    326       buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    327       buffer.memory = V4L2_MEMORY_MMAP;
    328 
    329       if(my_ioctl(self->fd, VIDIOC_QUERYBUF, &buffer))
    330         {
    331           return NULL;
    332         }
    333 
    334       self->buffers[i].length = buffer.length;
    335       self->buffers[i].start = v4l2_mmap(NULL, buffer.length,
    336           PROT_READ | PROT_WRITE, MAP_SHARED, self->fd, buffer.m.offset);
    337 
    338       if(self->buffers[i].start == MAP_FAILED)
    339         {
    340           PyErr_SetFromErrno(PyExc_IOError);
    341           Video_device_unmap(self);
    342           return NULL;
    343         }
    344       ++self->buffer_count;
    345     }
    346 
    347   Py_RETURN_NONE;
    348 }
    349 
    350 static PyObject *Video_device_queue_all_buffers(Video_device *self)
    351 {
    352   if(!self->buffers)
    353     {
    354       ASSERT_OPEN;
    355       PyErr_SetString(PyExc_ValueError, "Buffers have not been created");
    356       return NULL;
    357     }
    358 
    359   int i;
    360   int buffer_count = self->buffer_count;
    361 
    362   for(i = 0; i < buffer_count; i++)
    363     {
    364       struct v4l2_buffer buffer;
    365       buffer.index = i;
    366       buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    367       buffer.memory = V4L2_MEMORY_MMAP;
    368 
    369       if(my_ioctl(self->fd, VIDIOC_QBUF, &buffer))
    370         {
    371           return NULL;
    372         }
    373     }
    374 
    375   Py_RETURN_NONE;
    376 }
    377 
    378 static PyObject *Video_device_read_internal(Video_device *self, int queue)
    379 {
    380   if(!self->buffers)
    381     {
    382       ASSERT_OPEN;
    383       PyErr_SetString(PyExc_ValueError, "Buffers have not been created");
    384       return NULL;
    385     }
    386 
    387   struct v4l2_buffer buffer;
    388   buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    389   buffer.memory = V4L2_MEMORY_MMAP;
    390 
    391   if(my_ioctl(self->fd, VIDIOC_DQBUF, &buffer))
    392     {
    393       return NULL;
    394     }
    395 
    396   PyObject *result = PyString_FromStringAndSize(
    397       self->buffers[buffer.index].start, buffer.bytesused);
    398 
    399   if(!result)
    400     {
    401       return NULL;
    402     }
    403 
    404   if(queue && my_ioctl(self->fd, VIDIOC_QBUF, &buffer))
    405     {
    406       return NULL;
    407     }
    408 
    409   return result;
    410 }
    411 
    412 static PyObject *Video_device_read(Video_device *self)
    413 {
    414   return Video_device_read_internal(self, 0);
    415 }
    416 
    417 static PyObject *Video_device_read_and_queue(Video_device *self)
    418 {
    419   return Video_device_read_internal(self, 1);
    420 }
    421 
    422 static PyMethodDef Video_device_methods[] = {
    423   {"close", (PyCFunction)Video_device_close, METH_NOARGS,
    424        "close()\n\n"
    425        "Close video device. Subsequent calls to other methods will fail."},
    426   {"fileno", (PyCFunction)Video_device_fileno, METH_NOARGS,
    427        "fileno() -> integer \"file descriptor\".\n\n"
    428        "This enables video devices to be passed select.select for waiting "
    429        "until a frame is available for reading."},
    430   {"get_info", (PyCFunction)Video_device_get_info, METH_NOARGS,
    431        "get_info() -> driver, card, bus_info, capabilities\n\n"
    432        "Returns three strings with information about the video device, and one "
    433        "set containing strings identifying the capabilities of the video "
    434        "device."},
    435   {"set_format", (PyCFunction)Video_device_set_format, METH_VARARGS,
    436        "set_format(size_x, size_y) -> size_x, size_y\n\n"
    437        "Request the video device to set image size and format. The device may "
    438        "choose another size than requested and will return its choice. The "
    439        "image format will be MJPEG."},
    440   {"set_fps", (PyCFunction)Video_device_set_fps, METH_VARARGS,
    441        "set_fps(fps) -> fps \n\n"
    442        "Request the video device to set frame per seconds.The device may "
    443        "choose another frame rate than requested and will return its choice. " },
    444   {"start", (PyCFunction)Video_device_start, METH_NOARGS,
    445        "start()\n\n"
    446        "Start video capture."},
    447   {"stop", (PyCFunction)Video_device_stop, METH_NOARGS,
    448        "stop()\n\n"
    449        "Stop video capture."},
    450   {"create_buffers", (PyCFunction)Video_device_create_buffers, METH_VARARGS,
    451        "create_buffers(count)\n\n"
    452        "Create buffers used for capturing image data. Can only be called once "
    453        "for each video device object."},
    454   {"queue_all_buffers", (PyCFunction)Video_device_queue_all_buffers,
    455        METH_NOARGS,
    456        "queue_all_buffers()\n\n"
    457        "Let the video device fill all buffers created."},
    458   {"read", (PyCFunction)Video_device_read, METH_NOARGS,
    459        "read() -> string\n\n"
    460        "Reads image data from a buffer that has been filled by the video "
    461        "device. The image data is in MJPEG format. "
    462        "The buffer is removed from the queue. Fails if no buffer "
    463        "is filled. Use select.select to check for filled buffers."},
    464   {"read_and_queue", (PyCFunction)Video_device_read_and_queue, METH_NOARGS,
    465        "read_and_queue()\n\n"
    466        "Same as 'read', but adds the buffer back to the queue so the video "
    467        "device can fill it again."},
    468   {NULL}
    469 };
    470 
    471 static PyTypeObject Video_device_type = {
    472   PyObject_HEAD_INIT(NULL)
    473       0, "v4l2capture.Video_device", sizeof(Video_device), 0,
    474       (destructor)Video_device_dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    475       0, Py_TPFLAGS_DEFAULT, "Video_device(path)\n\nOpens the video device at "
    476       "the given path and returns an object that can capture images. The "
    477       "constructor and all methods except close may raise IOError.", 0, 0, 0,
    478       0, 0, 0, Video_device_methods, 0, 0, 0, 0, 0, 0, 0,
    479       (initproc)Video_device_init
    480 };
    481 
    482 static PyMethodDef module_methods[] = {
    483   {NULL}
    484 };
    485 
    486 PyMODINIT_FUNC initv4l2capture(void)
    487 {
    488   Video_device_type.tp_new = PyType_GenericNew;
    489 
    490   if(PyType_Ready(&Video_device_type) < 0)
    491     {
    492       return;
    493     }
    494 
    495   PyObject *module = Py_InitModule3("v4l2capture", module_methods,
    496       "Capture video with video4linux2.");
    497 
    498   if(!module)
    499     {
    500       return;
    501     }
    502 
    503   Py_INCREF(&Video_device_type);
    504   PyModule_AddObject(module, "Video_device", (PyObject *)&Video_device_type);
    505 }
    506