2 * transsip - the telephony toolkit
3 * By Daniel Borkmann <daniel@transsip.org>
4 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>
5 * Subject to the GPL, version 2.
9 /* Compile with: gcc v4ltest.c `pkg-config --libs --cflags gtk+-2.0` */
11 /* TODO: clean code, use gtk_image_set_from_pixbuf() */
24 #include <sys/types.h>
27 #include <sys/ioctl.h>
28 #include <asm/types.h>
29 #include <linux/videodev2.h>
31 static GtkWidget
*window
;
32 static GtkWidget
*image
;
39 static void errno_exit(const char *s
)
41 fprintf(stderr
, "%s error %d, %s\n", s
, errno
, strerror(errno
));
45 static int xioctl(int fd
, int request
, void *arg
)
51 do r
= ioctl(fd
, request
, arg
);
52 while(r
< 0 && EINTR
== errno
);
57 #define CLIP(color) (unsigned char) (((color) > 0xFF) ? \
58 0xff : (((color) < 0) ? 0 : (color)))
60 void v4lconvert_yuyv_to_rgb24(const unsigned char *src
, unsigned char *dest
,
61 int width
, int height
)
63 /* From: Hans de Goede <j.w.r.degoede@hhs.nl> */
66 while(--height
>= 0) {
67 for(j
= 0; j
< width
; j
+= 2) {
70 int u1
= (((u
- 128) << 7) + (u
- 128)) >> 6;
71 int rg
= (((u
- 128) << 1) + (u
- 128) +
72 ((v
- 128) << 2) + ((v
- 128) << 1)) >> 3;
73 int v1
= (((v
- 128) << 1) + (v
- 128)) >> 1;
75 *dest
++ = CLIP(src
[0] + v1
);
76 *dest
++ = CLIP(src
[0] - rg
);
77 *dest
++ = CLIP(src
[0] + u1
);
79 *dest
++ = CLIP(src
[2] + v1
);
80 *dest
++ = CLIP(src
[2] - rg
);
81 *dest
++ = CLIP(src
[2] + u1
);
88 static void convert_v4l_image_and_display(unsigned char *img
, size_t len
)
90 unsigned char img2
[640*480*3] = {0};
91 v4lconvert_yuyv_to_rgb24(img
, img2
, 640, 480);
93 GdkPixbuf
*pb
= gdk_pixbuf_new_from_data(img2
, GDK_COLORSPACE_RGB
,
94 FALSE
, 24/3, 640, 480, 640*3,
97 gtk_container_remove(GTK_CONTAINER(window
), image
);
98 image
= gtk_image_new_from_pixbuf(pb
);
99 gtk_container_add(GTK_CONTAINER(window
), image
);
100 gtk_widget_show_all(window
);
103 static void process_v4l_image(unsigned char *img
, size_t len
)
106 convert_v4l_image_and_display(img
, len
);
109 static int read_v4l_frame(int fd
, unsigned int n_buffers
,
110 struct buffer
*buffers
)
112 struct v4l2_buffer buf
;
116 memset(&buf
, 0, sizeof(buf
));
118 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
119 buf
.memory
= V4L2_MEMORY_MMAP
;
121 if(xioctl(fd
, VIDIOC_DQBUF
, &buf
) < 0) {
127 errno_exit("VIDIOC_DQBUF");
131 assert(buf
.index
< n_buffers
);
133 process_v4l_image(buffers
[buf
.index
].start
, buffers
[buf
.index
].length
);
134 if(xioctl(fd
, VIDIOC_QBUF
, &buf
) < 0)
135 errno_exit("VIDIOC_QBUF");
140 static int open_v4l_device(const char *dev_name
)
147 if(stat(dev_name
, &st
) < 0) {
148 fprintf(stderr
, "Cannot identify %s: %d, %s\n",
149 dev_name
, errno
, strerror(errno
));
153 if(!S_ISCHR(st
.st_mode
)) {
154 fprintf(stderr
, "%s is no device\n", dev_name
);
158 fd
= open (dev_name
, O_RDWR
| O_NONBLOCK
, 0);
160 fprintf(stderr
, "Cannot open %s: %d, %s\n",
161 dev_name
, errno
, strerror(errno
));
168 static void close_vl4_device(int fd
)
175 static int start_v4l_capturing(int fd
, unsigned int n_buffers
)
178 enum v4l2_buf_type type
;
180 for(i
= 0; i
< n_buffers
; ++i
) {
181 struct v4l2_buffer buf
;
183 memset(&buf
, 0, sizeof(buf
));
185 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
186 buf
.memory
= V4L2_MEMORY_MMAP
;
189 if(xioctl(fd
, VIDIOC_QBUF
, &buf
) < 0)
190 errno_exit("VIDIOC_QBUF");
193 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
195 if(xioctl(fd
, VIDIOC_STREAMON
, &type
) < 0)
196 errno_exit("VIDIOC_STREAMON");
201 static int stop_v4l_capturing(int fd
)
203 enum v4l2_buf_type type
;
205 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
207 if(xioctl(fd
, VIDIOC_STREAMOFF
, &type
) < 0)
208 errno_exit("VIDIOC_STREAMOFF");
213 static int init_mmap(int fd
, const char *dev_name
, unsigned int *n_buffers
,
214 struct buffer
**buffers
)
216 struct v4l2_requestbuffers req
;
222 memset(&req
, 0, sizeof(req
));
225 req
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
226 req
.memory
= V4L2_MEMORY_MMAP
;
228 if(xioctl(fd
, VIDIOC_REQBUFS
, &req
) < 0) {
229 if(EINVAL
== errno
) {
230 fprintf(stderr
, "%s does not support memory mapping\n",
234 errno_exit("VIDIOC_REQBUFS");
239 fprintf(stderr
, "Insufficient buffer memory on %s\n",
244 (*buffers
) = calloc(req
.count
, sizeof (**buffers
));
246 fprintf (stderr
, "Out of memory\n");
250 for((*n_buffers
) = 0; (*n_buffers
) < req
.count
; ++(*n_buffers
)) {
251 struct v4l2_buffer buf
;
253 memset(&buf
, 0, sizeof(buf
));
255 buf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
256 buf
.memory
= V4L2_MEMORY_MMAP
;
257 buf
.index
= (*n_buffers
);
259 if(xioctl(fd
, VIDIOC_QUERYBUF
, &buf
) < 0)
260 errno_exit("VIDIOC_QUERYBUF");
262 (*buffers
)[(*n_buffers
)].length
= buf
.length
;
263 (*buffers
)[(*n_buffers
)].start
= mmap(NULL
, buf
.length
,
264 PROT_READ
| PROT_WRITE
,
265 MAP_SHARED
, fd
, buf
.m
.offset
);
266 if(MAP_FAILED
== (*buffers
)[(*n_buffers
)].start
)
273 static int init_v4l_device(int fd
, const char *dev_name
, unsigned int *n_buffers
,
274 struct buffer
**buffers
)
278 struct v4l2_capability cap
;
279 struct v4l2_cropcap cropcap
;
280 struct v4l2_crop crop
;
281 struct v4l2_format fmt
;
283 if(xioctl(fd
, VIDIOC_QUERYCAP
, &cap
) < 0) {
284 if(EINVAL
== errno
) {
285 fprintf(stderr
, "%s is no V4L2 device\n", dev_name
);
288 errno_exit("VIDIOC_QUERYCAP");
292 if(!(cap
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
)) {
293 fprintf(stderr
, "%s is no video capture device\n", dev_name
);
297 if(!(cap
.capabilities
& V4L2_CAP_STREAMING
)) {
298 fprintf(stderr
, "%s does not support streaming i/o\n", dev_name
);
302 memset(&cropcap
, 0, sizeof(cropcap
));
304 cropcap
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
306 if(xioctl(fd
, VIDIOC_CROPCAP
, &cropcap
) == 0) {
307 crop
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
308 crop
.c
= cropcap
.defrect
;
310 if(xioctl(fd
, VIDIOC_S_CROP
, &crop
) < 0) {
313 /* Cropping not supported. */
316 /* Errors ignored. */
321 /* Errors ignored. */
324 memset(&fmt
, 0, sizeof(fmt
));
326 fmt
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
327 fmt
.fmt
.pix
.width
= 640;//640;
328 fmt
.fmt
.pix
.height
= 480;//480;
329 fmt
.fmt
.pix
.pixelformat
= V4L2_PIX_FMT_YUYV
;
330 fmt
.fmt
.pix
.field
= V4L2_FIELD_INTERLACED
;
332 if(xioctl(fd
, VIDIOC_S_FMT
, &fmt
) < 0)
333 errno_exit("VIDIOC_S_FMT");
335 /* XXX: VIDIOC_S_FMT may change width and height. */
337 /* Buggy driver paranoia. */
338 min
= fmt
.fmt
.pix
.width
* 2;
339 if(fmt
.fmt
.pix
.bytesperline
< min
)
340 fmt
.fmt
.pix
.bytesperline
= min
;
341 min
= fmt
.fmt
.pix
.bytesperline
* fmt
.fmt
.pix
.height
;
342 if(fmt
.fmt
.pix
.sizeimage
< min
)
343 fmt
.fmt
.pix
.sizeimage
= min
;
345 return init_mmap(fd
, dev_name
, n_buffers
, buffers
);
348 static int cleanup_v4l_device(int fd
, unsigned int n_buffers
,
349 struct buffer
*buffers
)
353 for(i
= 0; i
< n_buffers
; ++i
)
354 if(munmap(buffers
[i
].start
, buffers
[i
].length
) < 0)
355 errno_exit("munmap");
360 static void destroy(void) {
364 /* XXX: Remove here */
366 unsigned int v4l_n_buffers
;
367 struct buffer
*v4l_buffers
;
369 gboolean
update_v4l_image(gpointer null
)
371 read_v4l_frame(fd
, v4l_n_buffers
, v4l_buffers
);
375 int main(int argc
, char **argv
)
377 const char *dev_name
= "/dev/video0";
379 gtk_init(&argc
, &argv
);
381 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
382 gtk_window_set_title(GTK_WINDOW(window
), "V4L Player");
383 gtk_window_set_default_size(GTK_WINDOW(window
), 640, 480);
384 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_NONE
);
385 gtk_window_set_resizable(GTK_WINDOW(window
), FALSE
);
387 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
388 GTK_SIGNAL_FUNC(destroy
), NULL
);
390 fd
= open_v4l_device(dev_name
);
391 init_v4l_device(fd
, dev_name
, &v4l_n_buffers
, &v4l_buffers
);
392 start_v4l_capturing(fd
, v4l_n_buffers
);
394 gint func_ref
= g_timeout_add(10, update_v4l_image
, NULL
);
395 gtk_widget_show_all(window
);
397 g_source_remove(func_ref
);
399 stop_v4l_capturing(fd
);
400 cleanup_v4l_device(fd
, v4l_n_buffers
, v4l_buffers
);
401 close_vl4_device(fd
);