2 * transsip - the telephony network
3 * By Daniel Borkmann <daniel@transsip.org>
4 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,
5 * Swiss federal institute of technology (ETH Zurich)
6 * Subject to the GPL, version 2.
10 /* Compile with: gcc v4ltest.c `pkg-config --libs --cflags gtk+-2.0` */
12 /* TODO: clean code, use gtk_image_set_from_pixbuf() */
25 #include <sys/types.h>
28 #include <sys/ioctl.h>
29 #include <asm/types.h>
30 #include <linux/videodev2.h>
32 static GtkWidget
*window
;
33 static GtkWidget
*image
;
40 static void errno_exit(const char *s
)
42 fprintf(stderr
, "%s error %d, %s\n", s
, errno
, strerror(errno
));
46 static int xioctl(int fd
, int request
, void *arg
)
52 do r
= ioctl(fd
, request
, arg
);
53 while(r
< 0 && EINTR
== errno
);
58 #define CLIP(color) (unsigned char) (((color) > 0xFF) ? \
59 0xff : (((color) < 0) ? 0 : (color)))
61 void v4lconvert_yuyv_to_rgb24(const unsigned char *src
, unsigned char *dest
,
62 int width
, int height
)
64 /* From: Hans de Goede <j.w.r.degoede@hhs.nl> */
67 while(--height
>= 0) {
68 for(j
= 0; j
< width
; j
+= 2) {
71 int u1
= (((u
- 128) << 7) + (u
- 128)) >> 6;
72 int rg
= (((u
- 128) << 1) + (u
- 128) +
73 ((v
- 128) << 2) + ((v
- 128) << 1)) >> 3;
74 int v1
= (((v
- 128) << 1) + (v
- 128)) >> 1;
76 *dest
++ = CLIP(src
[0] + v1
);
77 *dest
++ = CLIP(src
[0] - rg
);
78 *dest
++ = CLIP(src
[0] + u1
);
80 *dest
++ = CLIP(src
[2] + v1
);
81 *dest
++ = CLIP(src
[2] - rg
);
82 *dest
++ = CLIP(src
[2] + u1
);
89 static void convert_v4l_image_and_display(unsigned char *img
, size_t len
)
91 unsigned char img2
[640*480*3] = {0};
92 v4lconvert_yuyv_to_rgb24(img
, img2
, 640, 480);
94 GdkPixbuf
*pb
= gdk_pixbuf_new_from_data(img2
, GDK_COLORSPACE_RGB
,
95 FALSE
, 24/3, 640, 480, 640*3,
98 gtk_container_remove(GTK_CONTAINER(window
), image
);
99 image
= gtk_image_new_from_pixbuf(pb
);
100 gtk_container_add(GTK_CONTAINER(window
), image
);
101 gtk_widget_show_all(window
);
104 static void process_v4l_image(unsigned char *img
, size_t len
)
107 convert_v4l_image_and_display(img
, len
);
110 static int read_v4l_frame(int fd
, unsigned int n_buffers
,
111 struct buffer
*buffers
)
113 struct v4l2_buffer buf
;
117 memset(&buf
, 0, sizeof(buf
));
119 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
120 buf
.memory
= V4L2_MEMORY_MMAP
;
122 if(xioctl(fd
, VIDIOC_DQBUF
, &buf
) < 0) {
128 errno_exit("VIDIOC_DQBUF");
132 assert(buf
.index
< n_buffers
);
134 process_v4l_image(buffers
[buf
.index
].start
, buffers
[buf
.index
].length
);
135 if(xioctl(fd
, VIDIOC_QBUF
, &buf
) < 0)
136 errno_exit("VIDIOC_QBUF");
141 static int open_v4l_device(const char *dev_name
)
148 if(stat(dev_name
, &st
) < 0) {
149 fprintf(stderr
, "Cannot identify %s: %d, %s\n",
150 dev_name
, errno
, strerror(errno
));
154 if(!S_ISCHR(st
.st_mode
)) {
155 fprintf(stderr
, "%s is no device\n", dev_name
);
159 fd
= open (dev_name
, O_RDWR
| O_NONBLOCK
, 0);
161 fprintf(stderr
, "Cannot open %s: %d, %s\n",
162 dev_name
, errno
, strerror(errno
));
169 static void close_vl4_device(int fd
)
176 static int start_v4l_capturing(int fd
, unsigned int n_buffers
)
179 enum v4l2_buf_type type
;
181 for(i
= 0; i
< n_buffers
; ++i
) {
182 struct v4l2_buffer buf
;
184 memset(&buf
, 0, sizeof(buf
));
186 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
187 buf
.memory
= V4L2_MEMORY_MMAP
;
190 if(xioctl(fd
, VIDIOC_QBUF
, &buf
) < 0)
191 errno_exit("VIDIOC_QBUF");
194 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
196 if(xioctl(fd
, VIDIOC_STREAMON
, &type
) < 0)
197 errno_exit("VIDIOC_STREAMON");
202 static int stop_v4l_capturing(int fd
)
204 enum v4l2_buf_type type
;
206 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
208 if(xioctl(fd
, VIDIOC_STREAMOFF
, &type
) < 0)
209 errno_exit("VIDIOC_STREAMOFF");
214 static int init_mmap(int fd
, const char *dev_name
, unsigned int *n_buffers
,
215 struct buffer
**buffers
)
217 struct v4l2_requestbuffers req
;
223 memset(&req
, 0, sizeof(req
));
226 req
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
227 req
.memory
= V4L2_MEMORY_MMAP
;
229 if(xioctl(fd
, VIDIOC_REQBUFS
, &req
) < 0) {
230 if(EINVAL
== errno
) {
231 fprintf(stderr
, "%s does not support memory mapping\n",
235 errno_exit("VIDIOC_REQBUFS");
240 fprintf(stderr
, "Insufficient buffer memory on %s\n",
245 (*buffers
) = calloc(req
.count
, sizeof (**buffers
));
247 fprintf (stderr
, "Out of memory\n");
251 for((*n_buffers
) = 0; (*n_buffers
) < req
.count
; ++(*n_buffers
)) {
252 struct v4l2_buffer buf
;
254 memset(&buf
, 0, sizeof(buf
));
256 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
257 buf
.memory
= V4L2_MEMORY_MMAP
;
258 buf
.index
= (*n_buffers
);
260 if(xioctl(fd
, VIDIOC_QUERYBUF
, &buf
) < 0)
261 errno_exit("VIDIOC_QUERYBUF");
263 (*buffers
)[(*n_buffers
)].length
= buf
.length
;
264 (*buffers
)[(*n_buffers
)].start
= mmap(NULL
, buf
.length
,
265 PROT_READ
| PROT_WRITE
,
266 MAP_SHARED
, fd
, buf
.m
.offset
);
267 if(MAP_FAILED
== (*buffers
)[(*n_buffers
)].start
)
274 static int init_v4l_device(int fd
, const char *dev_name
, unsigned int *n_buffers
,
275 struct buffer
**buffers
)
279 struct v4l2_capability cap
;
280 struct v4l2_cropcap cropcap
;
281 struct v4l2_crop crop
;
282 struct v4l2_format fmt
;
284 if(xioctl(fd
, VIDIOC_QUERYCAP
, &cap
) < 0) {
285 if(EINVAL
== errno
) {
286 fprintf(stderr
, "%s is no V4L2 device\n", dev_name
);
289 errno_exit("VIDIOC_QUERYCAP");
293 if(!(cap
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
)) {
294 fprintf(stderr
, "%s is no video capture device\n", dev_name
);
298 if(!(cap
.capabilities
& V4L2_CAP_STREAMING
)) {
299 fprintf(stderr
, "%s does not support streaming i/o\n", dev_name
);
303 memset(&cropcap
, 0, sizeof(cropcap
));
305 cropcap
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
307 if(xioctl(fd
, VIDIOC_CROPCAP
, &cropcap
) == 0) {
308 crop
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
309 crop
.c
= cropcap
.defrect
;
311 if(xioctl(fd
, VIDIOC_S_CROP
, &crop
) < 0) {
314 /* Cropping not supported. */
317 /* Errors ignored. */
322 /* Errors ignored. */
325 memset(&fmt
, 0, sizeof(fmt
));
327 fmt
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
328 fmt
.fmt
.pix
.width
= 640;//640;
329 fmt
.fmt
.pix
.height
= 480;//480;
330 fmt
.fmt
.pix
.pixelformat
= V4L2_PIX_FMT_YUYV
;
331 fmt
.fmt
.pix
.field
= V4L2_FIELD_INTERLACED
;
333 if(xioctl(fd
, VIDIOC_S_FMT
, &fmt
) < 0)
334 errno_exit("VIDIOC_S_FMT");
336 /* XXX: VIDIOC_S_FMT may change width and height. */
338 /* Buggy driver paranoia. */
339 min
= fmt
.fmt
.pix
.width
* 2;
340 if(fmt
.fmt
.pix
.bytesperline
< min
)
341 fmt
.fmt
.pix
.bytesperline
= min
;
342 min
= fmt
.fmt
.pix
.bytesperline
* fmt
.fmt
.pix
.height
;
343 if(fmt
.fmt
.pix
.sizeimage
< min
)
344 fmt
.fmt
.pix
.sizeimage
= min
;
346 return init_mmap(fd
, dev_name
, n_buffers
, buffers
);
349 static int cleanup_v4l_device(int fd
, unsigned int n_buffers
,
350 struct buffer
*buffers
)
354 for(i
= 0; i
< n_buffers
; ++i
)
355 if(munmap(buffers
[i
].start
, buffers
[i
].length
) < 0)
356 errno_exit("munmap");
361 static void destroy(void) {
365 /* XXX: Remove here */
367 unsigned int v4l_n_buffers
;
368 struct buffer
*v4l_buffers
;
370 gboolean
update_v4l_image(gpointer null
)
372 read_v4l_frame(fd
, v4l_n_buffers
, v4l_buffers
);
376 int main(int argc
, char **argv
)
378 const char *dev_name
= "/dev/video0";
380 gtk_init(&argc
, &argv
);
382 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
383 gtk_window_set_title(GTK_WINDOW(window
), "V4L Player");
384 gtk_window_set_default_size(GTK_WINDOW(window
), 640, 480);
385 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_NONE
);
386 gtk_window_set_resizable(GTK_WINDOW(window
), FALSE
);
388 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
389 GTK_SIGNAL_FUNC(destroy
), NULL
);
391 fd
= open_v4l_device(dev_name
);
392 init_v4l_device(fd
, dev_name
, &v4l_n_buffers
, &v4l_buffers
);
393 start_v4l_capturing(fd
, v4l_n_buffers
);
395 gint func_ref
= g_timeout_add(10, update_v4l_image
, NULL
);
396 gtk_widget_show_all(window
);
398 g_source_remove(func_ref
);
400 stop_v4l_capturing(fd
);
401 cleanup_v4l_device(fd
, v4l_n_buffers
, v4l_buffers
);
402 close_vl4_device(fd
);