├── Makefile ├── capture.lua ├── README ├── gtk-v4l2.lua ├── core.h ├── v4l_lua.c └── core.c /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -Wall -pedantic -fPIC v4l_lua.c core.c -shared -o v4l.so -I /usr/include/lua5.1/ -llua5.1 -lv4lconvert 3 | 4 | clean: 5 | rm *.so 6 | -------------------------------------------------------------------------------- /capture.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local camera = arg[1] or "/dev/video0" 4 | local dev = require "v4l".open(camera) 5 | 6 | local function saveimg(img, w, h) 7 | file = io.open("image.ppm", "w+") 8 | file:write("P3\n".. w .. " " .. h .."\n255\n") -- RGB IMAGE 9 | for i = 1, #img do 10 | local p = img[i] .. "\n" 11 | file:write(p) 12 | end 13 | file:close() 14 | end 15 | 16 | local w, h = dev:width(), dev:height() 17 | 18 | print(camera .. ": " ..w .. "x" .. h) 19 | 20 | local a 21 | for i = 1, 10 do -- take 10 pics to get a better image 22 | a = dev:getframe() 23 | -- print(a[i] .. " " .. a[i+1] .. " ".. a[i+2]) 24 | end 25 | 26 | saveimg(a, w, h) 27 | 28 | local fd = dev:fd() 29 | dev:close() 30 | print("File descriptor closed: " .. fd) 31 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | v4l-lua is a small library binding V4L2 CAMERA API to Lua. 2 | See the example "capture.lua" to see how easy is to capture from V4L2 cameras :D 3 | To compile v4l-lua, you'll need the v4lconvert and Lua development files. 4 | 5 | WARNING: THIS LIBRARY STILL IS IN ALPHA STAGE! Plese, report bugs ;) 6 | 7 | Usage: 8 | 9 | local v4l = require 'v4l' 10 | 11 | dev = v4l.open(device_name, width, height) 12 | -- opens a v4l camera, where: device_name is usually '/dev/video0', and the 13 | -- optional params width and height can be used to suggest a resolution, 14 | -- although a similar one may be chosen... 15 | 16 | array = dev:getframe() 17 | -- returns a table with the rgb values for the pixels from left to right 18 | -- top to bottom, in other words the the pixel (x,y) will appear at 19 | -- array[c + 3 * (x + y * dev:width())], assuming (0,0) to be the 20 | -- top left corner, and c=1 for red, c=2 for green and c=3 for blue. 21 | 22 | str = dev:getframestr() 23 | -- similar to getframe, but the values will be returned via a string 24 | -- instead of a table; you can use the string.byte for fast access. 25 | 26 | w = dev:width() -- return effective width 27 | h = dev:height() -- and effective height 28 | fd = dev:fd() -- file descriptor number, if you need it for concurrent I/O with other files 29 | dev:close() -- closes the camera freeing any resources 30 | -------------------------------------------------------------------------------- /gtk-v4l2.lua: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env lua 2 | 3 | --[[ 4 | This example depends on the lgob (a set of bindings of GObject-based libraries, like GTK+ and WebKitGtk, and some others like Cairo, for Lua). 5 | I've installed the 32 bits version of lgob from here: http://downloads.tuxfamily.org/oproj/bin/ubuntu32/ 6 | You have the choice to install it from the sources, found at: http://oproj.tuxfamily.org/wiki/doku.php?id=lgob 7 | Enjoy! 8 | ]] -- 9 | 10 | 11 | 12 | v4l = require "v4l" 13 | require('lgob.gdk') 14 | require('lgob.gtk') 15 | 16 | 17 | function saveimg(name, img) 18 | file = io.open(name, "w+") 19 | file:write("P3\n".. w .. " " .. h .."\n255\n") 20 | for i=1,#img do 21 | local p = a[i] .. "\n" 22 | file:write(p) 23 | end 24 | file:close() 25 | end 26 | 27 | 28 | camera = #arg 29 | 30 | if camera < 1 then 31 | camera = "/dev/video0" 32 | else 33 | camera = arg[1] 34 | end 35 | 36 | dev = v4l:open(camera) 37 | 38 | w, h = dev:width(), dev:height() 39 | 40 | print(camera .. ": " ..w .. "x" .. h) 41 | 42 | -- saveimg(a) 43 | 44 | for i=1,3 do -- take 3 pics to get a better image 45 | a = dev:getframe() 46 | end 47 | 48 | img = "P3\n" .. w .. " " .. h .. "\n255\n" .. table.concat(a, "\n") -- formats the image to pixbuf format 49 | 50 | -- saveimg(a) 51 | 52 | -- img = io.open("image.ppm", "r"):read("*a") 53 | -- print(img) 54 | 55 | 56 | function runDialog(dialog) 57 | dialog:run() 58 | dialog:hide() 59 | 60 | names = dialog:get_filenames() 61 | file = table.concat(names) 62 | print(file) 63 | saveimg(file, a) 64 | end 65 | 66 | 67 | 68 | loader = gdk.PixbufLoader.new() 69 | loader:write(img, img:len()) 70 | loader:close() 71 | pixbuf = loader:get_pixbuf() 72 | 73 | window = gtk.Window.new() 74 | hbox = gtk.VBox.new(false, 10) 75 | 76 | image = gtk.Image.new_from_pixbuf(pixbuf) 77 | button = gtk.Button.new_with_label("Save") 78 | 79 | dialog = gtk.FileChooserDialog.new("Select a name to save", window, gtk.FILE_CHOOSER_ACTION_SAVE, 80 | "gtk-cancel", gtk.RESPONSE_CANCEL, "gtk-ok", gtk.RESPONSE_OK) 81 | 82 | filter = gtk.FileFilter.new() 83 | filter:add_pattern("*.ppm") 84 | filter:set_name("PPM Images") 85 | dialog:add_filter(filter) 86 | dialog:set("select-multiple", true) 87 | 88 | hbox:add(image, button) 89 | 90 | window:add(hbox) 91 | 92 | window:set('title', "Camera photo " .. camera .. " ".. w .. "x" .. h, 'window-position', gtk.WIN_POS_CENTER) 93 | window:connect('delete-event', gtk.main_quit) 94 | button:connect('clicked', runDialog, dialog) 95 | window:show_all() 96 | gtk.main() 97 | window:show_all() 98 | 99 | 100 | img = nil 101 | a = nil 102 | 103 | dev:close() 104 | -------------------------------------------------------------------------------- /core.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011 Gabriel Duarte 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef CORE_H 24 | #define CORE_H 25 | 26 | #define WITH_V4L2_LIB 1 /* v4l library */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include /* low-level i/o */ 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include /* for videodev2.h */ 45 | #include 46 | #include 47 | 48 | struct buffer { 49 | void * start; 50 | size_t length; 51 | }; 52 | 53 | struct device { 54 | int fd; 55 | char * path; 56 | int w, h; 57 | double timeout_secs; 58 | struct v4l2_format src_fmt, fmt; 59 | struct v4lconvert_data * v4lconvert_data; 60 | unsigned char * dst_buf; 61 | struct buffer * buffers; 62 | int n_buffers; 63 | void * errorfn_data; 64 | void (*errorfn)(void * errorfn_data, char * fmt, ...); 65 | }; 66 | 67 | /* Refactored functions: on error return a negative value: */ 68 | int open_device(struct device * dev, const char * path, 69 | void * errorfn_data, 70 | void (*errorfn)(void * errorfn_data, char * fmt, ...)); 71 | int init_device(struct device * dev, int w, int h); 72 | void close_device(struct device * dev); 73 | 74 | int start_capturing(struct device * dev); 75 | void set_timout(struct device * dev, double secs); 76 | /* timeout on newframe can be detected via errorfn being reported the "timeout" string */ 77 | unsigned char * newframe(struct device * dev); 78 | 79 | #endif /*CORE_H*/ 80 | /* vi: set et sw=4: */ 81 | -------------------------------------------------------------------------------- /v4l_lua.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018 Francisco Castro 3 | Copyright (c) 2011 Gabriel Duarte 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | /* Lua herders */ 29 | #include "lua.h" 30 | #include "lauxlib.h" 31 | #include "luaconf.h" 32 | #include "lualib.h" 33 | /* V4L2 */ 34 | #include "core.h" 35 | 36 | #define V4L_MT "v4l-lua device" 37 | 38 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 39 | /* Compatibility for Lua 5.1. 40 | * 41 | * luaL_setfuncs() is used to create a module table where the functions have 42 | * json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */ 43 | static void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup) { 44 | int i; 45 | 46 | luaL_checkstack(l, nup, "too many upvalues"); 47 | for (; reg->name != NULL; reg++) { /* fill the table with given functions */ 48 | for (i = 0; i < nup; i++) /* copy upvalues to the top */ 49 | lua_pushvalue(l, -nup); 50 | lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */ 51 | lua_setfield(l, -(nup + 2), reg->name); 52 | } 53 | lua_pop(l, nup); /* remove upvalues */ 54 | } 55 | #endif 56 | 57 | static struct device * device_check(lua_State * L, int narg) { 58 | struct device * dev = luaL_checkudata(L, narg, V4L_MT); 59 | if (dev->fd < 0) 60 | luaL_error(L, "device already closed"); 61 | return dev; 62 | } 63 | 64 | static int v4l_device_getframe(lua_State * L) { 65 | struct device * dev = device_check(L, 1); 66 | int i, imgsize = dev->w * dev->h * 3; 67 | unsigned char * img; 68 | 69 | img = newframe(dev); 70 | 71 | lua_createtable(L, imgsize, 0); 72 | 73 | for (i = 0; i < imgsize; ++i) { 74 | lua_pushnumber(L, img[i]); 75 | lua_rawseti(L, -2, i+1); 76 | } 77 | 78 | return 1; 79 | } 80 | 81 | static int v4l_device_getframestr(lua_State * L) { 82 | struct device * dev = device_check(L, 1); 83 | lua_pushlstring(L, (char*)newframe(dev), dev->w * dev->h * 3); 84 | return 1; 85 | } 86 | 87 | static int v4l_device_close(lua_State * L) { 88 | close_device(device_check(L, 1)); 89 | return 0; 90 | } 91 | 92 | static int v4l_device_width(lua_State * L) { 93 | lua_pushnumber(L, device_check(L, 1)->w); 94 | return 1; 95 | } 96 | 97 | static int v4l_device_height(lua_State * L) { 98 | lua_pushnumber(L, device_check(L, 1)->h); 99 | return 1; 100 | } 101 | 102 | static int v4l_device_fd(lua_State * L) { 103 | lua_pushnumber(L, device_check(L, 1)->fd); 104 | return 1; 105 | } 106 | 107 | static int v4l_device___gc(lua_State * L) { 108 | close_device(luaL_checkudata(L, 1, V4L_MT)); 109 | return 0; 110 | } 111 | 112 | static void errorfn_handler(void * errorfn_data, char * fmt, ...) { 113 | va_list args; 114 | va_start(args, fmt); 115 | lua_pushvfstring((lua_State *)errorfn_data, fmt, args); 116 | va_end(args); 117 | lua_error((lua_State *)errorfn_data); 118 | } 119 | 120 | static int v4l_open(lua_State * L) { 121 | const char * path; 122 | struct device * dev = lua_newuserdata(L, sizeof (struct device)); 123 | int w, h; 124 | 125 | path = luaL_checkstring(L, 1); 126 | w = luaL_optinteger(L, 2, 720); 127 | h = luaL_optinteger(L, 3, 480); 128 | 129 | if (open_device(dev, path, L, errorfn_handler) < 0) 130 | return luaL_error(L, "device error"); 131 | 132 | luaL_getmetatable(L, V4L_MT); 133 | lua_setmetatable(L, -2); 134 | 135 | init_device(dev, w, h); 136 | start_capturing(dev); 137 | return 1; 138 | } 139 | 140 | int LUA_API luaopen_v4l(lua_State *L) { 141 | const luaL_Reg driver[] = { 142 | {"open", v4l_open}, 143 | {NULL, NULL}, 144 | }; 145 | 146 | if (luaL_newmetatable(L, V4L_MT)) { 147 | const luaL_Reg methods[] = { 148 | {"getframe", v4l_device_getframe}, 149 | {"getframestr", v4l_device_getframestr}, 150 | {"close", v4l_device_close}, 151 | {"width", v4l_device_width}, 152 | {"height", v4l_device_height}, 153 | {"fd", v4l_device_fd}, 154 | {"__gc", v4l_device___gc}, 155 | {NULL, NULL}, 156 | }; 157 | luaL_setfuncs(L, methods, 0); 158 | lua_pushvalue(L, -1); 159 | lua_setfield(L, -2, "__index"); 160 | } 161 | lua_pop(L, 1); /* discard metatable */ 162 | 163 | lua_createtable(L, sizeof driver / sizeof driver[0], 0); 164 | luaL_setfuncs(L, driver, 0); 165 | 166 | return 1; 167 | } 168 | /* vi: set et sw=4: */ 169 | -------------------------------------------------------------------------------- /core.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011 Gabriel Duarte 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include "core.h" 24 | 25 | static const int N_BUFFERS = 4; 26 | 27 | static int xioctl(int fd, int request, void * arg) { 28 | int r; 29 | 30 | do { 31 | r = ioctl(fd, request, arg); 32 | } while (r < 0 && EINTR == errno); 33 | return r; 34 | } 35 | 36 | static int process_image(struct device * dev, unsigned char * p, int len) { 37 | 38 | if (v4lconvert_convert(dev->v4lconvert_data, 39 | &dev->src_fmt, 40 | &dev->fmt, 41 | p, len, 42 | dev->dst_buf, 43 | dev->fmt.fmt.pix.sizeimage) < 0) { 44 | if(errno != EAGAIN) { 45 | dev->errorfn(dev->errorfn_data, "v4l_convert: %s", v4lconvert_get_error_message(dev->v4lconvert_data)); 46 | return -1; 47 | } 48 | } 49 | return 0; 50 | } 51 | 52 | static int read_frame(struct device * dev, struct v4l2_buffer * buf) { 53 | int res; 54 | memset(buf, 0, sizeof *buf); 55 | buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 56 | buf->memory = V4L2_MEMORY_MMAP; 57 | 58 | if (xioctl(dev->fd, VIDIOC_DQBUF, buf) < 0) { 59 | switch (errno) { 60 | case EAGAIN: 61 | return 0; 62 | break; 63 | case EIO: 64 | /* Could ignore EIO, see spec. */ 65 | /* fall through */ 66 | default: 67 | dev->errorfn(dev->errorfn_data, "VIDIOC_DQBUF: %s", strerror(errno)); 68 | return -1; 69 | } 70 | } 71 | 72 | assert(buf->index < dev->n_buffers); 73 | res = process_image(dev, dev->buffers[buf->index].start, buf->bytesused); 74 | 75 | if (xioctl(dev->fd, VIDIOC_QBUF, buf) < 0) { 76 | dev->errorfn(dev->errorfn_data, "VIDIOC_QBUF: %s", strerror(errno)); 77 | return -1; 78 | } 79 | 80 | return res; 81 | } 82 | 83 | static int get_frame(struct device * dev) { 84 | fd_set fds; 85 | struct timeval tv; 86 | int r; 87 | 88 | FD_ZERO(&fds); 89 | FD_SET(dev->fd, &fds); 90 | 91 | /* Timeout. */ 92 | tv.tv_sec = dev->timeout_secs; 93 | tv.tv_usec = fmod(dev->timeout_secs, 1.0) * 1e6; 94 | 95 | do { 96 | r = select(dev->fd + 1, &fds, NULL, NULL, &tv); 97 | } while (r < 0 && errno == EINTR); 98 | 99 | if (r < 0) { 100 | dev->errorfn(dev->errorfn_data, "select: %s", strerror(errno)); 101 | return -1; 102 | } else if (r == 0) { 103 | dev->errorfn(dev->errorfn_data, "timeout"); 104 | return -1; 105 | } 106 | 107 | return 0; 108 | } 109 | 110 | unsigned char * newframe(struct device * dev) { 111 | struct v4l2_buffer buf; 112 | 113 | if (get_frame(dev) < 0) 114 | return NULL; 115 | if (read_frame(dev, &buf) < 0) 116 | return NULL; 117 | return dev->dst_buf; 118 | } 119 | 120 | void set_timout(struct device * dev, double secs) { 121 | dev->timeout_secs = secs; 122 | } 123 | 124 | int start_capturing(struct device * dev) { 125 | int i; 126 | enum v4l2_buf_type type; 127 | struct v4l2_buffer buf; 128 | 129 | /*printf("mmap method\n");*/ 130 | 131 | for (i = 0; i < dev->n_buffers; i++) { 132 | memset(&buf, 0, sizeof buf); 133 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 134 | buf.memory = V4L2_MEMORY_MMAP; 135 | buf.index = i; 136 | 137 | if (xioctl(dev->fd, VIDIOC_QBUF, &buf) < 0) { 138 | dev->errorfn(dev->errorfn_data, "VIDIOC_QBUF: %s", strerror(errno)); 139 | return -1; 140 | } 141 | } 142 | 143 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 144 | 145 | if (xioctl(dev->fd, VIDIOC_STREAMON, &type) < 0) { 146 | dev->errorfn(dev->errorfn_data, "VIDIOC_STREAMON: %s", strerror(errno)); 147 | return -1; 148 | } 149 | return 0; 150 | } 151 | 152 | static int init_mmap(struct device * dev) { 153 | struct v4l2_requestbuffers req; 154 | struct v4l2_buffer buf; 155 | int i; 156 | 157 | memset(&(req), 0, sizeof(req)); 158 | req.count = N_BUFFERS; 159 | req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 160 | req.memory = V4L2_MEMORY_MMAP; 161 | 162 | /* call to VIDIOC_REQBUFS may change req.count */ 163 | if (xioctl(dev->fd, VIDIOC_REQBUFS, &req) < 0) { 164 | if (EINVAL == errno) { 165 | dev->errorfn(dev->errorfn_data, "%s does not support memory mapping", dev->path); 166 | } else { 167 | dev->errorfn(dev->errorfn_data, "VIDIOC_REQBUFS: %s", strerror(errno)); 168 | } 169 | return -1; 170 | } 171 | 172 | if (req.count < 2) { 173 | dev->errorfn(dev->errorfn_data, "Insufficient buffer memory on %s", dev->path); 174 | return -1; 175 | } 176 | 177 | dev->buffers = (struct buffer *) calloc(req.count, sizeof (struct buffer)); 178 | dev->n_buffers = req.count; 179 | 180 | if (!dev->buffers) { 181 | dev->errorfn(dev->errorfn_data, "Out of memory: %s", strerror(errno)); 182 | return -1; 183 | } 184 | 185 | for (i = 0; i < dev->n_buffers; i++) { 186 | memset(&buf, 0, sizeof buf); 187 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 188 | buf.memory = V4L2_MEMORY_MMAP; 189 | buf.index = i; 190 | 191 | if (xioctl(dev->fd, VIDIOC_QUERYBUF, &buf) < 0) { 192 | dev->errorfn(dev->errorfn_data, "VIDIOC_QUERYBUF: %s", strerror(errno)); 193 | return -1; 194 | } 195 | 196 | dev->buffers[i].length = buf.length; 197 | dev->buffers[i].start = mmap(NULL, /* start anywhere */ 198 | buf.length, 199 | PROT_READ | PROT_WRITE, /* required */ 200 | MAP_SHARED, /* recommended */ 201 | dev->fd, buf.m.offset); 202 | 203 | if (MAP_FAILED == dev->buffers[i].start) { 204 | dev->buffers[i].start = NULL; 205 | dev->errorfn(dev->errorfn_data, "mmap: %s", strerror(errno)); 206 | return -1; 207 | } 208 | } 209 | return 0; 210 | } 211 | 212 | int init_device(struct device * dev, int w, int h) { 213 | struct v4l2_capability cap; 214 | int ret; 215 | 216 | if (xioctl(dev->fd, VIDIOC_QUERYCAP, &cap) < 0) { 217 | if (EINVAL == errno) { 218 | dev->errorfn(dev->errorfn_data, "%s is no V4L2 device", dev->path); 219 | return -1; 220 | } else { 221 | dev->errorfn(dev->errorfn_data, "VIDIOC_QUERYCAP: %s", strerror(errno)); 222 | return -1; 223 | } 224 | } 225 | 226 | if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 227 | dev->errorfn(dev->errorfn_data, "%s is no video capture device", dev->path); 228 | return -1; 229 | } 230 | 231 | memset(&dev->fmt, 0, sizeof dev->fmt); 232 | dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 233 | dev->fmt.fmt.pix.width = w; 234 | dev->fmt.fmt.pix.height = h; 235 | dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; 236 | dev->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 237 | 238 | dev->v4lconvert_data = v4lconvert_create(dev->fd); 239 | 240 | if (!dev->v4lconvert_data) { 241 | dev->errorfn(dev->errorfn_data, "v4lconvert_create failure: %s", strerror(errno)); 242 | return -1; 243 | } 244 | 245 | if (v4lconvert_try_format(dev->v4lconvert_data, &dev->fmt, &dev->src_fmt) != 0) { 246 | /*errno_exit("v4lconvert_try_format");*/ 247 | dev->errorfn(dev->errorfn_data, "v4lconvert_try_format: %s", v4lconvert_get_error_message(dev->v4lconvert_data)); 248 | return -1; 249 | } 250 | 251 | dev->w = dev->fmt.fmt.pix.width; 252 | dev->h = dev->fmt.fmt.pix.height; 253 | 254 | ret = xioctl(dev->fd, VIDIOC_S_FMT, &dev->src_fmt); 255 | dev->dst_buf = (unsigned char *)malloc(dev->fmt.fmt.pix.sizeimage); 256 | 257 | #ifdef DEBUG 258 | printf("raw pixfmt: %c%c%c%c %dx%d\n", 259 | dev->src_fmt.fmt.pix.pixelformat & 0xff, 260 | (dev->src_fmt.fmt.pix.pixelformat >> 8) & 0xff, 261 | (dev->src_fmt.fmt.pix.pixelformat >> 16) & 0xff, 262 | (dev->src_fmt.fmt.pix.pixelformat >> 24) & 0xff, 263 | dev->src_fmt.fmt.pix.width, dev->src_fmt.fmt.pix.height); 264 | #endif 265 | 266 | if (ret < 0) { 267 | dev->errorfn(dev->errorfn_data, "VIDIOC_S_FMT"); 268 | return -1; 269 | } 270 | 271 | #ifdef DEBUG 272 | printf("pixfmt: %c%c%c%c %dx%d\n", 273 | dev->fmt.fmt.pix.pixelformat & 0xff, 274 | (dev->fmt.fmt.pix.pixelformat >> 8) & 0xff, 275 | (dev->fmt.fmt.pix.pixelformat >> 16) & 0xff, 276 | (dev->fmt.fmt.pix.pixelformat >> 24) & 0xff, 277 | dev->fmt.fmt.pix.width, dev->fmt.fmt.pix.height); 278 | 279 | /* Note VIDIOC_S_FMT may change width and height. */ 280 | #endif 281 | 282 | w = dev->fmt.fmt.pix.width; 283 | h = dev->fmt.fmt.pix.height; 284 | 285 | return init_mmap(dev); 286 | } 287 | 288 | void close_device(struct device * dev) { 289 | int i; 290 | 291 | if (dev->path) { 292 | free(dev->path); 293 | dev->path = NULL; 294 | } 295 | if (dev->v4lconvert_data) { 296 | v4lconvert_destroy(dev->v4lconvert_data); 297 | dev->v4lconvert_data = NULL; 298 | } 299 | if (dev->fd >= 0) { 300 | close(dev->fd); 301 | dev->fd = -1; 302 | } 303 | if (dev->dst_buf) { 304 | free(dev->dst_buf); 305 | dev->dst_buf = NULL; 306 | } 307 | if (dev->buffers) { 308 | for (i = 0; i < dev->n_buffers; i++) 309 | if (dev->buffers[i].start) 310 | munmap(dev->buffers[i].start, dev->buffers[i].length); 311 | free(dev->buffers); 312 | dev->buffers = NULL; 313 | } 314 | } 315 | 316 | static void default_errorfn(void * errorfn_data, char * fmt, ...) { 317 | va_list args; 318 | (void)errorfn_data; 319 | 320 | va_start(args, fmt); 321 | vfprintf(stderr, fmt, args); 322 | va_end(args); 323 | putc('\n', stderr); 324 | } 325 | 326 | int open_device(struct device * dev, const char * path, 327 | void * errorfn_data, 328 | void (*errorfn)(void * errorfn_data, char * fmt, ...)) { 329 | struct stat st; 330 | 331 | dev->fd = -1; 332 | dev->path = NULL; 333 | dev->timeout_secs = 2; 334 | dev->v4lconvert_data = NULL; 335 | dev->dst_buf = NULL; 336 | dev->buffers = NULL; 337 | dev->n_buffers = 0; 338 | dev->errorfn_data = errorfn_data; 339 | dev->errorfn = errorfn ? errorfn : &default_errorfn; 340 | 341 | if (stat(path, &st) < 0) { 342 | dev->errorfn(dev->errorfn_data, "Cannot identify '%s': %d, %s", path, errno, strerror(errno)); 343 | return -1; 344 | } 345 | 346 | if (!S_ISCHR(st.st_mode)) { 347 | dev->errorfn(dev->errorfn_data, "%s is no device", path); 348 | return -1; 349 | } 350 | 351 | dev->fd = open(path, O_RDWR /* required */ | O_NONBLOCK, 0); 352 | 353 | if (dev->fd < 0) { 354 | dev->errorfn(dev->errorfn_data, "Cannot open '%s': %d, %s", path, errno, strerror(errno)); 355 | return -1; 356 | } 357 | 358 | dev->path = strcpy(malloc(strlen(path) + 1), path); 359 | 360 | return dev->fd; 361 | } 362 | 363 | /* vi: set et sw=4: */ 364 | --------------------------------------------------------------------------------