3 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>
6 * Faculty of Computer Science, Mathematics and Natural Sciences,
7 * Leipzig University of Applied Sciences (HTWK Leipzig)
10 /* Compile with: gcc v4ltest.c `pkg-config --libs --cflags gtk+-2.0` */
11 /* ToDo: Clean code */
23 #include <sys/types.h>
26 #include <sys/ioctl.h>
27 #include <asm/types.h>
28 #include <linux/videodev2.h>
34 static GtkWidget
*window
;
35 static GtkWidget
*image
;
43 static void errno_exit(const char *s
)
45 fprintf(stderr
, "%s error %d, %s\n", s
, errno
, strerror(errno
));
49 static int xioctl(int fd
, int request
, void *arg
)
55 do r
= ioctl(fd
, request
, arg
);
56 while(r
< 0 && EINTR
== errno
);
61 #define CLIP(color) (unsigned char) (((color) > 0xFF) ? \
62 0xff : (((color) < 0) ? 0 : (color)))
64 void v4lconvert_yuyv_to_rgb24(const unsigned char *src
, unsigned char *dest
,
65 int width
, int height
)
67 /* From: Hans de Goede <j.w.r.degoede@hhs.nl> */
70 while(--height
>= 0) {
71 for(j
= 0; j
< width
; j
+= 2) {
74 int u1
= (((u
- 128) << 7) + (u
- 128)) >> 6;
75 int rg
= (((u
- 128) << 1) + (u
- 128) +
76 ((v
- 128) << 2) + ((v
- 128) << 1)) >> 3;
77 int v1
= (((v
- 128) << 1) + (v
- 128)) >> 1;
79 *dest
++ = CLIP(src
[0] + v1
);
80 *dest
++ = CLIP(src
[0] - rg
);
81 *dest
++ = CLIP(src
[0] + u1
);
83 *dest
++ = CLIP(src
[2] + v1
);
84 *dest
++ = CLIP(src
[2] - rg
);
85 *dest
++ = CLIP(src
[2] + u1
);
93 static void convert_v4l_image_and_display(unsigned char *img
, size_t len
)
95 unsigned char img2
[640*480*3] = {0};
96 v4lconvert_yuyv_to_rgb24(img
, img2
, 640, 480);
98 GdkPixbuf
*pb
= gdk_pixbuf_new_from_data(img2
, GDK_COLORSPACE_RGB
,
99 FALSE
, 24/3, 640, 480, 640*3,
102 gtk_container_remove(GTK_CONTAINER(window
), image
);
103 image
= gtk_image_new_from_pixbuf(pb
);
104 gtk_container_add(GTK_CONTAINER(window
), image
);
105 gtk_widget_show_all(window
);
108 static void process_v4l_image(unsigned char *img
, size_t len
)
111 convert_v4l_image_and_display(img
, len
);
115 static int read_v4l_frame(int fd
, unsigned int n_buffers
,
116 struct buffer
*buffers
)
118 struct v4l2_buffer buf
;
122 memset(&buf
, 0, sizeof(buf
));
124 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
125 buf
.memory
= V4L2_MEMORY_MMAP
;
127 if(xioctl(fd
, VIDIOC_DQBUF
, &buf
) < 0) {
133 errno_exit("VIDIOC_DQBUF");
137 assert(buf
.index
< n_buffers
);
139 process_v4l_image(buffers
[buf
.index
].start
, buffers
[buf
.index
].length
);
140 if(xioctl(fd
, VIDIOC_QBUF
, &buf
) < 0)
141 errno_exit("VIDIOC_QBUF");
146 static int open_v4l_device(const char *dev_name
)
152 dev_name
= "/dev/video0";
154 if(stat(dev_name
, &st
) < 0) {
155 fprintf(stderr
, "Cannot identify %s: %d, %s\n",
156 dev_name
, errno
, strerror(errno
));
160 if(!S_ISCHR(st
.st_mode
)) {
161 fprintf(stderr
, "%s is no device\n", dev_name
);
165 fd
= open (dev_name
, O_RDWR
| O_NONBLOCK
, 0);
167 fprintf(stderr
, "Cannot open %s: %d, %s\n",
168 dev_name
, errno
, strerror(errno
));
175 static void close_vl4_device(int fd
)
182 static int start_v4l_capturing(int fd
, unsigned int n_buffers
)
185 enum v4l2_buf_type type
;
187 for(i
= 0; i
< n_buffers
; ++i
) {
188 struct v4l2_buffer buf
;
190 memset(&buf
, 0, sizeof(buf
));
192 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
193 buf
.memory
= V4L2_MEMORY_MMAP
;
196 if(xioctl(fd
, VIDIOC_QBUF
, &buf
) < 0)
197 errno_exit("VIDIOC_QBUF");
200 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
202 if(xioctl(fd
, VIDIOC_STREAMON
, &type
) < 0)
203 errno_exit("VIDIOC_STREAMON");
208 static int stop_v4l_capturing(int fd
)
210 enum v4l2_buf_type type
;
212 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
214 if(xioctl(fd
, VIDIOC_STREAMOFF
, &type
) < 0)
215 errno_exit("VIDIOC_STREAMOFF");
220 static int init_mmap(int fd
, const char *dev_name
, unsigned int *n_buffers
,
221 struct buffer
**buffers
)
223 struct v4l2_requestbuffers req
;
229 memset(&req
, 0, sizeof(req
));
232 req
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
233 req
.memory
= V4L2_MEMORY_MMAP
;
235 if(xioctl(fd
, VIDIOC_REQBUFS
, &req
) < 0) {
236 if(EINVAL
== errno
) {
237 fprintf(stderr
, "%s does not support memory mapping\n",
241 errno_exit("VIDIOC_REQBUFS");
246 fprintf(stderr
, "Insufficient buffer memory on %s\n",
251 (*buffers
) = calloc(req
.count
, sizeof (**buffers
));
253 fprintf (stderr
, "Out of memory\n");
257 for((*n_buffers
) = 0; (*n_buffers
) < req
.count
; ++(*n_buffers
)) {
258 struct v4l2_buffer buf
;
260 memset(&buf
, 0, sizeof(buf
));
262 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
263 buf
.memory
= V4L2_MEMORY_MMAP
;
264 buf
.index
= (*n_buffers
);
266 if(xioctl(fd
, VIDIOC_QUERYBUF
, &buf
) < 0)
267 errno_exit("VIDIOC_QUERYBUF");
269 (*buffers
)[(*n_buffers
)].length
= buf
.length
;
270 (*buffers
)[(*n_buffers
)].start
= mmap(NULL
, buf
.length
,
271 PROT_READ
| PROT_WRITE
,
272 MAP_SHARED
, fd
, buf
.m
.offset
);
273 if(MAP_FAILED
== (*buffers
)[(*n_buffers
)].start
)
280 static int init_v4l_device(int fd
, const char *dev_name
, unsigned int *n_buffers
,
281 struct buffer
**buffers
)
285 struct v4l2_capability cap
;
286 struct v4l2_cropcap cropcap
;
287 struct v4l2_crop crop
;
288 struct v4l2_format fmt
;
290 if(xioctl(fd
, VIDIOC_QUERYCAP
, &cap
) < 0) {
291 if(EINVAL
== errno
) {
292 fprintf(stderr
, "%s is no V4L2 device\n", dev_name
);
295 errno_exit("VIDIOC_QUERYCAP");
299 if(!(cap
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
)) {
300 fprintf(stderr
, "%s is no video capture device\n", dev_name
);
304 if(!(cap
.capabilities
& V4L2_CAP_STREAMING
)) {
305 fprintf(stderr
, "%s does not support streaming i/o\n", dev_name
);
309 memset(&cropcap
, 0, sizeof(cropcap
));
311 cropcap
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
313 if(xioctl(fd
, VIDIOC_CROPCAP
, &cropcap
) == 0) {
314 crop
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
315 crop
.c
= cropcap
.defrect
;
317 if(xioctl(fd
, VIDIOC_S_CROP
, &crop
) < 0) {
320 /* Cropping not supported. */
323 /* Errors ignored. */
328 /* Errors ignored. */
331 memset(&fmt
, 0, sizeof(fmt
));
333 fmt
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
334 fmt
.fmt
.pix
.width
= 640;//640;
335 fmt
.fmt
.pix
.height
= 480;//480;
336 fmt
.fmt
.pix
.pixelformat
= V4L2_PIX_FMT_YUYV
;
337 fmt
.fmt
.pix
.field
= V4L2_FIELD_INTERLACED
;
339 if(xioctl(fd
, VIDIOC_S_FMT
, &fmt
) < 0)
340 errno_exit("VIDIOC_S_FMT");
342 /* XXX: VIDIOC_S_FMT may change width and height. */
344 /* Buggy driver paranoia. */
345 min
= fmt
.fmt
.pix
.width
* 2;
346 if(fmt
.fmt
.pix
.bytesperline
< min
)
347 fmt
.fmt
.pix
.bytesperline
= min
;
348 min
= fmt
.fmt
.pix
.bytesperline
* fmt
.fmt
.pix
.height
;
349 if(fmt
.fmt
.pix
.sizeimage
< min
)
350 fmt
.fmt
.pix
.sizeimage
= min
;
352 return init_mmap(fd
, dev_name
, n_buffers
, buffers
);
355 static int cleanup_v4l_device(int fd
, unsigned int n_buffers
,
356 struct buffer
*buffers
)
360 for(i
= 0; i
< n_buffers
; ++i
)
361 if(munmap(buffers
[i
].start
, buffers
[i
].length
) < 0)
362 errno_exit("munmap");
368 static void destroy(void) {
372 /* XXX: Remove here */
374 unsigned int v4l_n_buffers
;
375 struct buffer
*v4l_buffers
;
377 gboolean
update_v4l_image(gpointer null
)
379 read_v4l_frame(fd
, v4l_n_buffers
, v4l_buffers
);
383 int main(int argc
, char **argv
)
385 const char *dev_name
= "/dev/video0";
387 gtk_init(&argc
, &argv
);
389 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
390 gtk_window_set_title(GTK_WINDOW(window
), "netyack v4l player");
391 gtk_window_set_default_size(GTK_WINDOW(window
), 640, 480);
392 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_NONE
);
393 gtk_window_set_resizable(GTK_WINDOW(window
), FALSE
);
395 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
396 GTK_SIGNAL_FUNC(destroy
), NULL
);
398 fd
= open_v4l_device(dev_name
);
399 init_v4l_device(fd
, dev_name
, &v4l_n_buffers
, &v4l_buffers
);
400 start_v4l_capturing(fd
, v4l_n_buffers
);
402 gint func_ref
= g_timeout_add(10, update_v4l_image
, NULL
);
403 gtk_widget_show_all(window
);
405 g_source_remove(func_ref
);
407 stop_v4l_capturing(fd
);
408 cleanup_v4l_device(fd
, v4l_n_buffers
, v4l_buffers
);
409 close_vl4_device(fd
);