2 * Copyright (C) 2010 Daniel Borkmann <daniel@netyack.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or (at
7 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
19 /* Compile with: gcc v4ltest.c `pkg-config --libs --cflags gtk+-2.0` */
21 /* TODO: clean code, use gtk_image_set_from_pixbuf() */
34 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <asm/types.h>
39 #include <linux/videodev2.h>
41 static GtkWidget
*window
;
42 static GtkWidget
*image
;
49 static void errno_exit(const char *s
)
51 fprintf(stderr
, "%s error %d, %s\n", s
, errno
, strerror(errno
));
55 static int xioctl(int fd
, int request
, void *arg
)
61 do r
= ioctl(fd
, request
, arg
);
62 while(r
< 0 && EINTR
== errno
);
67 #define CLIP(color) (unsigned char) (((color) > 0xFF) ? \
68 0xff : (((color) < 0) ? 0 : (color)))
70 void v4lconvert_yuyv_to_rgb24(const unsigned char *src
, unsigned char *dest
,
71 int width
, int height
)
73 /* From: Hans de Goede <j.w.r.degoede@hhs.nl> */
76 while(--height
>= 0) {
77 for(j
= 0; j
< width
; j
+= 2) {
80 int u1
= (((u
- 128) << 7) + (u
- 128)) >> 6;
81 int rg
= (((u
- 128) << 1) + (u
- 128) +
82 ((v
- 128) << 2) + ((v
- 128) << 1)) >> 3;
83 int v1
= (((v
- 128) << 1) + (v
- 128)) >> 1;
85 *dest
++ = CLIP(src
[0] + v1
);
86 *dest
++ = CLIP(src
[0] - rg
);
87 *dest
++ = CLIP(src
[0] + u1
);
89 *dest
++ = CLIP(src
[2] + v1
);
90 *dest
++ = CLIP(src
[2] - rg
);
91 *dest
++ = CLIP(src
[2] + u1
);
98 static void convert_v4l_image_and_display(unsigned char *img
, size_t len
)
100 unsigned char img2
[640*480*3] = {0};
101 v4lconvert_yuyv_to_rgb24(img
, img2
, 640, 480);
103 GdkPixbuf
*pb
= gdk_pixbuf_new_from_data(img2
, GDK_COLORSPACE_RGB
,
104 FALSE
, 24/3, 640, 480, 640*3,
107 gtk_container_remove(GTK_CONTAINER(window
), image
);
108 image
= gtk_image_new_from_pixbuf(pb
);
109 gtk_container_add(GTK_CONTAINER(window
), image
);
110 gtk_widget_show_all(window
);
113 static void process_v4l_image(unsigned char *img
, size_t len
)
116 convert_v4l_image_and_display(img
, len
);
119 static int read_v4l_frame(int fd
, unsigned int n_buffers
,
120 struct buffer
*buffers
)
122 struct v4l2_buffer buf
;
126 memset(&buf
, 0, sizeof(buf
));
128 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
129 buf
.memory
= V4L2_MEMORY_MMAP
;
131 if(xioctl(fd
, VIDIOC_DQBUF
, &buf
) < 0) {
137 errno_exit("VIDIOC_DQBUF");
141 assert(buf
.index
< n_buffers
);
143 process_v4l_image(buffers
[buf
.index
].start
, buffers
[buf
.index
].length
);
144 if(xioctl(fd
, VIDIOC_QBUF
, &buf
) < 0)
145 errno_exit("VIDIOC_QBUF");
150 static int open_v4l_device(const char *dev_name
)
157 if(stat(dev_name
, &st
) < 0) {
158 fprintf(stderr
, "Cannot identify %s: %d, %s\n",
159 dev_name
, errno
, strerror(errno
));
163 if(!S_ISCHR(st
.st_mode
)) {
164 fprintf(stderr
, "%s is no device\n", dev_name
);
168 fd
= open (dev_name
, O_RDWR
| O_NONBLOCK
, 0);
170 fprintf(stderr
, "Cannot open %s: %d, %s\n",
171 dev_name
, errno
, strerror(errno
));
178 static void close_vl4_device(int fd
)
185 static int start_v4l_capturing(int fd
, unsigned int n_buffers
)
188 enum v4l2_buf_type type
;
190 for(i
= 0; i
< n_buffers
; ++i
) {
191 struct v4l2_buffer buf
;
193 memset(&buf
, 0, sizeof(buf
));
195 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
196 buf
.memory
= V4L2_MEMORY_MMAP
;
199 if(xioctl(fd
, VIDIOC_QBUF
, &buf
) < 0)
200 errno_exit("VIDIOC_QBUF");
203 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
205 if(xioctl(fd
, VIDIOC_STREAMON
, &type
) < 0)
206 errno_exit("VIDIOC_STREAMON");
211 static int stop_v4l_capturing(int fd
)
213 enum v4l2_buf_type type
;
215 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
217 if(xioctl(fd
, VIDIOC_STREAMOFF
, &type
) < 0)
218 errno_exit("VIDIOC_STREAMOFF");
223 static int init_mmap(int fd
, const char *dev_name
, unsigned int *n_buffers
,
224 struct buffer
**buffers
)
226 struct v4l2_requestbuffers req
;
232 memset(&req
, 0, sizeof(req
));
235 req
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
236 req
.memory
= V4L2_MEMORY_MMAP
;
238 if(xioctl(fd
, VIDIOC_REQBUFS
, &req
) < 0) {
239 if(EINVAL
== errno
) {
240 fprintf(stderr
, "%s does not support memory mapping\n",
244 errno_exit("VIDIOC_REQBUFS");
249 fprintf(stderr
, "Insufficient buffer memory on %s\n",
254 (*buffers
) = calloc(req
.count
, sizeof (**buffers
));
256 fprintf (stderr
, "Out of memory\n");
260 for((*n_buffers
) = 0; (*n_buffers
) < req
.count
; ++(*n_buffers
)) {
261 struct v4l2_buffer buf
;
263 memset(&buf
, 0, sizeof(buf
));
265 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
266 buf
.memory
= V4L2_MEMORY_MMAP
;
267 buf
.index
= (*n_buffers
);
269 if(xioctl(fd
, VIDIOC_QUERYBUF
, &buf
) < 0)
270 errno_exit("VIDIOC_QUERYBUF");
272 (*buffers
)[(*n_buffers
)].length
= buf
.length
;
273 (*buffers
)[(*n_buffers
)].start
= mmap(NULL
, buf
.length
,
274 PROT_READ
| PROT_WRITE
,
275 MAP_SHARED
, fd
, buf
.m
.offset
);
276 if(MAP_FAILED
== (*buffers
)[(*n_buffers
)].start
)
283 static int init_v4l_device(int fd
, const char *dev_name
, unsigned int *n_buffers
,
284 struct buffer
**buffers
)
288 struct v4l2_capability cap
;
289 struct v4l2_cropcap cropcap
;
290 struct v4l2_crop crop
;
291 struct v4l2_format fmt
;
293 if(xioctl(fd
, VIDIOC_QUERYCAP
, &cap
) < 0) {
294 if(EINVAL
== errno
) {
295 fprintf(stderr
, "%s is no V4L2 device\n", dev_name
);
298 errno_exit("VIDIOC_QUERYCAP");
302 if(!(cap
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
)) {
303 fprintf(stderr
, "%s is no video capture device\n", dev_name
);
307 if(!(cap
.capabilities
& V4L2_CAP_STREAMING
)) {
308 fprintf(stderr
, "%s does not support streaming i/o\n", dev_name
);
312 memset(&cropcap
, 0, sizeof(cropcap
));
314 cropcap
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
316 if(xioctl(fd
, VIDIOC_CROPCAP
, &cropcap
) == 0) {
317 crop
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
318 crop
.c
= cropcap
.defrect
;
320 if(xioctl(fd
, VIDIOC_S_CROP
, &crop
) < 0) {
323 /* Cropping not supported. */
326 /* Errors ignored. */
331 /* Errors ignored. */
334 memset(&fmt
, 0, sizeof(fmt
));
336 fmt
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
337 fmt
.fmt
.pix
.width
= 640;//640;
338 fmt
.fmt
.pix
.height
= 480;//480;
339 fmt
.fmt
.pix
.pixelformat
= V4L2_PIX_FMT_YUYV
;
340 fmt
.fmt
.pix
.field
= V4L2_FIELD_INTERLACED
;
342 if(xioctl(fd
, VIDIOC_S_FMT
, &fmt
) < 0)
343 errno_exit("VIDIOC_S_FMT");
345 /* XXX: VIDIOC_S_FMT may change width and height. */
347 /* Buggy driver paranoia. */
348 min
= fmt
.fmt
.pix
.width
* 2;
349 if(fmt
.fmt
.pix
.bytesperline
< min
)
350 fmt
.fmt
.pix
.bytesperline
= min
;
351 min
= fmt
.fmt
.pix
.bytesperline
* fmt
.fmt
.pix
.height
;
352 if(fmt
.fmt
.pix
.sizeimage
< min
)
353 fmt
.fmt
.pix
.sizeimage
= min
;
355 return init_mmap(fd
, dev_name
, n_buffers
, buffers
);
358 static int cleanup_v4l_device(int fd
, unsigned int n_buffers
,
359 struct buffer
*buffers
)
363 for(i
= 0; i
< n_buffers
; ++i
)
364 if(munmap(buffers
[i
].start
, buffers
[i
].length
) < 0)
365 errno_exit("munmap");
370 static void destroy(void) {
374 /* XXX: Remove here */
376 unsigned int v4l_n_buffers
;
377 struct buffer
*v4l_buffers
;
379 gboolean
update_v4l_image(gpointer null
)
381 read_v4l_frame(fd
, v4l_n_buffers
, v4l_buffers
);
385 int main(int argc
, char **argv
)
387 const char *dev_name
= "/dev/video0";
389 gtk_init(&argc
, &argv
);
391 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
392 gtk_window_set_title(GTK_WINDOW(window
), "V4L Player");
393 gtk_window_set_default_size(GTK_WINDOW(window
), 640, 480);
394 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_NONE
);
395 gtk_window_set_resizable(GTK_WINDOW(window
), FALSE
);
397 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
398 GTK_SIGNAL_FUNC(destroy
), NULL
);
400 fd
= open_v4l_device(dev_name
);
401 init_v4l_device(fd
, dev_name
, &v4l_n_buffers
, &v4l_buffers
);
402 start_v4l_capturing(fd
, v4l_n_buffers
);
404 gint func_ref
= g_timeout_add(10, update_v4l_image
, NULL
);
405 gtk_widget_show_all(window
);
407 g_source_remove(func_ref
);
409 stop_v4l_capturing(fd
);
410 cleanup_v4l_device(fd
, v4l_n_buffers
, v4l_buffers
);
411 close_vl4_device(fd
);