0
|
1 /* dummy.c
|
|
2 *
|
|
3 * Example program for using a videoloopback device in zero-copy mode.
|
|
4 * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
|
|
5 * Copyright 2005 by Angel Carpintero (ack@telefonica.net)
|
|
6 * This software is distributed under the GNU public license version 2
|
|
7 * See also the file 'COPYING'.
|
|
8 *
|
|
9 */
|
|
10
|
|
11 #include <unistd.h>
|
|
12 #include <stdlib.h>
|
|
13 #include <stdio.h>
|
|
14 #include <fcntl.h>
|
|
15 #include <string.h>
|
|
16 #include <errno.h>
|
|
17 #include <sys/ioctl.h>
|
|
18 #include <sys/mman.h>
|
|
19 #include <signal.h>
|
|
20 #include <sys/wait.h>
|
|
21 #include <sys/poll.h>
|
|
22 #include <dirent.h>
|
|
23 #include <sys/utsname.h>
|
|
24 #include <linux/videodev.h>
|
|
25
|
|
26 /* all seem reasonable, or not? */
|
|
27 #define MAXIOCTL 1024
|
|
28 #define MAXWIDTH 640
|
|
29 #define MAXHEIGHT 480
|
|
30 int width;
|
|
31 int height;
|
|
32 int fmt=0;
|
|
33 char ioctlbuf[MAXIOCTL];
|
|
34 int v4ldev;
|
|
35 char *image_out;
|
|
36
|
|
37
|
|
38 int get_frame(void)
|
|
39 {
|
|
40 int i;
|
|
41 char colour = 0;
|
|
42
|
|
43 memset(image_out, 0x128, width*height*3);
|
|
44
|
|
45 for (i=10; i<width-10; i++) {
|
|
46 image_out[10*width*3+i*3]=colour++;
|
|
47 image_out[10*width*3+i*3+1]=0;
|
|
48 image_out[10*width*3+i*3+2]=-colour;
|
|
49 }
|
|
50 for (i=10; i<width-10; i++) {
|
|
51 image_out[(height-10)*width*3+i*3]=colour;
|
|
52 image_out[(height-10)*width*3+i*3+1]=0;
|
|
53 image_out[(height-10)*width*3+i*3+2]=-colour++;
|
|
54 }
|
|
55 /*
|
|
56 */
|
|
57 usleep(500); /* BIG XXX */
|
|
58 return 0;
|
|
59 }
|
|
60
|
|
61 char *v4l_create (int dev, int memsize)
|
|
62 {
|
|
63 char *map;
|
|
64
|
|
65 map=mmap(0, memsize, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
|
|
66 if ((unsigned char *)-1 == (unsigned char *)map)
|
|
67 return NULL;
|
|
68 return map;
|
|
69 }
|
|
70
|
|
71 int v4l_ioctl(unsigned long int cmd, void *arg)
|
|
72 {
|
|
73 int i;
|
|
74 switch (cmd) {
|
|
75 case VIDIOCGCAP:
|
|
76 {
|
|
77 struct video_capability *vidcap=arg;
|
|
78
|
|
79 sprintf(vidcap->name, "Jeroen's dummy v4l driver");
|
|
80 vidcap->type= VID_TYPE_CAPTURE;
|
|
81 vidcap->channels=1;
|
|
82 vidcap->audios=0;
|
|
83 vidcap->maxwidth=MAXWIDTH;
|
|
84 vidcap->maxheight=MAXHEIGHT;
|
|
85 vidcap->minwidth=20;
|
|
86 vidcap->minheight=20;
|
|
87 return 0;
|
|
88 }
|
|
89 case VIDIOCGCHAN:
|
|
90 {
|
|
91 struct video_channel *vidchan= (struct video_channel *)arg;
|
|
92
|
|
93 printf("VIDIOCGCHAN called\n");
|
|
94 if (vidchan->channel!=0)
|
|
95 ;//return 1;
|
|
96 vidchan->channel=0;
|
|
97 vidchan->flags=0;
|
|
98 vidchan->tuners=0;
|
|
99 vidchan->norm=0;
|
|
100 vidchan->type=VIDEO_TYPE_CAMERA;
|
|
101 strcpy(vidchan->name, "Loopback");
|
|
102
|
|
103 return 0;
|
|
104 }
|
|
105 case VIDIOCSCHAN:
|
|
106 {
|
|
107 int *v=arg;
|
|
108
|
|
109 if (v[0]!=0)
|
|
110 return 1;
|
|
111 return 0;
|
|
112 }
|
|
113 case VIDIOCGTUNER:
|
|
114 {
|
|
115 struct video_tuner *v = arg;
|
|
116
|
|
117 if(v->tuner) {
|
|
118 printf("VIDIOCGTUNER: Invalid Tuner, was %d\n", v->tuner);
|
|
119 //return -EINVAL;
|
|
120 }
|
|
121 v->tuner=0;
|
|
122 strcpy(v->name, "Format");
|
|
123 v->rangelow=0;
|
|
124 v->rangehigh=0;
|
|
125 v->flags=0;
|
|
126 v->mode=VIDEO_MODE_AUTO;
|
|
127 return 1;
|
|
128 }
|
|
129 case VIDIOCGPICT:
|
|
130 {
|
|
131 struct video_picture *vidpic=arg;
|
|
132
|
|
133 vidpic->colour=0x8000;
|
|
134 vidpic->hue=0x8000;
|
|
135 vidpic->brightness=0x8000;
|
|
136 vidpic->contrast=0x8000;
|
|
137 vidpic->whiteness=0x8000;
|
|
138 vidpic->depth=0x8000;
|
|
139 vidpic->palette=fmt;
|
|
140 return 0;
|
|
141 }
|
|
142 case VIDIOCSPICT:
|
|
143 {
|
|
144 struct video_picture *vidpic=arg;
|
|
145
|
|
146 if (vidpic->palette!=fmt)
|
|
147 return 1;
|
|
148 return 0;
|
|
149 }
|
|
150 case VIDIOCGWIN:
|
|
151 {
|
|
152 struct video_window *vidwin=arg;
|
|
153
|
|
154 vidwin->x=0;
|
|
155 vidwin->y=0;
|
|
156 vidwin->width=width;
|
|
157 vidwin->height=height;
|
|
158 vidwin->chromakey=0;
|
|
159 vidwin->flags=0;
|
|
160 vidwin->clipcount=0;
|
|
161 return 0;
|
|
162 }
|
|
163 case VIDIOCSWIN:
|
|
164 {
|
|
165 struct video_window *vidwin=arg;
|
|
166
|
|
167 if (vidwin->width > MAXWIDTH ||
|
|
168 vidwin->height > MAXHEIGHT )
|
|
169 return 1;
|
|
170 if (vidwin->flags)
|
|
171 return 1;
|
|
172 width=vidwin->width;
|
|
173 height=vidwin->height;
|
|
174 printf("new size: %dx%d\n", width, height);
|
|
175 return 0;
|
|
176 }
|
|
177 case VIDIOCGMBUF:
|
|
178 {
|
|
179 struct video_mbuf *vidmbuf=arg;
|
|
180
|
|
181 vidmbuf->size=width*height*3;
|
|
182 vidmbuf->frames=1;
|
|
183 for (i=0; i<vidmbuf->frames; i++)
|
|
184 vidmbuf->offsets[i]=i*vidmbuf->size;
|
|
185 return 0;
|
|
186 }
|
|
187 case VIDIOCMCAPTURE:
|
|
188 {
|
|
189 struct video_mmap *vidmmap=arg;
|
|
190
|
|
191 //return 0;
|
|
192 if (vidmmap->height>MAXHEIGHT ||
|
|
193 vidmmap->width>MAXWIDTH ||
|
|
194 vidmmap->format!=fmt )
|
|
195 return 1;
|
|
196 if (vidmmap->height!=height ||
|
|
197 vidmmap->width!=width) {
|
|
198 height=vidmmap->height;
|
|
199 width=vidmmap->width;
|
|
200 printf("new size: %dx%d\n", width, height);
|
|
201 }
|
|
202 // check if 'vidmmap->frame' is valid
|
|
203 // initiate capture for 'vidmmap->frame' frames
|
|
204 return 0;
|
|
205 }
|
|
206 case VIDIOCSYNC:
|
|
207 {
|
|
208 //struct video_mmap *vidmmap=arg;
|
|
209
|
|
210 // check if frames are ready.
|
|
211 // wait until ready.
|
|
212 get_frame();
|
|
213 return 0;
|
|
214 }
|
|
215 default:
|
|
216 {
|
|
217 printf("unknown ioctl: %ld\n", cmd & 0xff);
|
|
218 return 1;
|
|
219 }
|
|
220 }
|
|
221 return 0;
|
|
222 }
|
|
223
|
|
224 #define VIDIOCSINVALID _IO('v',BASE_VIDIOCPRIVATE+1)
|
|
225
|
|
226 void sighandler(int signo)
|
|
227 {
|
|
228 int size, ret;
|
|
229 unsigned long int cmd;
|
|
230 struct pollfd ufds;
|
|
231
|
|
232 if (signo!=SIGIO)
|
|
233 return;
|
|
234 ufds.fd=v4ldev;
|
|
235 ufds.events=POLLIN;
|
|
236 ufds.revents=0;
|
|
237 poll(&ufds, 1, 1000);
|
|
238 if (!ufds.revents & POLLIN) {
|
|
239 printf("Received signal but got negative on poll?!?!?!?\n");
|
|
240 return;
|
|
241 }
|
|
242 size=read(v4ldev, ioctlbuf, MAXIOCTL);
|
|
243 if (size >= sizeof(unsigned long int)) {
|
|
244 memcpy(&cmd, ioctlbuf, sizeof(unsigned long int));
|
|
245 if (cmd==0) {
|
|
246 printf("Client closed device\n");
|
|
247 return;
|
|
248 }
|
|
249 ret=v4l_ioctl(cmd, ioctlbuf+sizeof(unsigned long int));
|
|
250 if (ret) {
|
|
251 memset(ioctlbuf+sizeof(unsigned long int), MAXIOCTL-sizeof(unsigned long int), 0xff);
|
|
252 printf("ioctl %lx unsuccesfull, lets issue VIDIOCSINVALID (%x)\n", cmd, VIDIOCSINVALID);
|
|
253 ioctl(v4ldev, VIDIOCSINVALID);
|
|
254 } else
|
|
255 ioctl(v4ldev, cmd, ioctlbuf+sizeof(unsigned long int));
|
|
256 }
|
|
257 return;
|
|
258 }
|
|
259
|
|
260 int open_vidpipe(void)
|
|
261 {
|
|
262 int pipe_fd = -1;
|
|
263 FILE *vloopbacks;
|
|
264 char pipepath[255];
|
|
265 char buffer[255];
|
|
266 char *loop;
|
|
267 char *input;
|
|
268 char *istatus;
|
|
269 char *output;
|
|
270 char *ostatus;
|
|
271 char *major;
|
|
272 char *minor;
|
|
273 struct utsname uts;
|
|
274
|
|
275 if (uname(&uts) < 0) {
|
|
276 printf("Unable to execute uname\nError[%s]\n",strerror(errno));
|
|
277 return -1;
|
|
278 }
|
|
279
|
|
280 major = strtok(uts.release, ".");
|
|
281 minor = strtok(NULL, ".");
|
|
282 if ((major == NULL) || (minor == NULL) || (strcmp(major, "2"))) {
|
|
283 printf("Unable to decipher OS version\n");
|
|
284 return -1;
|
|
285 }
|
|
286
|
|
287 if (strcmp(minor, "5") < 0) {
|
|
288
|
|
289 vloopbacks=fopen("/proc/video/vloopback/vloopbacks", "r");
|
|
290 if (!vloopbacks) {
|
|
291 printf ("Failed to open '/proc/video/vloopback/vloopbacks");
|
|
292 return -1;
|
|
293 }
|
|
294 /* Read vloopback version */
|
|
295 fgets(buffer, 255, vloopbacks);
|
|
296 printf("%s", buffer);
|
|
297 /* Read explaination line */
|
|
298 fgets(buffer, 255, vloopbacks);
|
|
299 while (fgets(buffer, 255, vloopbacks)) {
|
|
300 if (strlen(buffer)>1) {
|
|
301 buffer[strlen(buffer)-1]=0;
|
|
302 loop=strtok(buffer, "\t");
|
|
303 input=strtok(NULL, "\t");
|
|
304 istatus=strtok(NULL, "\t");
|
|
305 output=strtok(NULL, "\t");
|
|
306 ostatus=strtok(NULL, "\t");
|
|
307 if (istatus[0]=='-') {
|
|
308 sprintf(pipepath, "/dev/%s", input);
|
|
309 pipe_fd=open(pipepath, O_RDWR);
|
|
310 if (pipe_fd>=0) {
|
|
311 printf("Input: /dev/%s\n", input);
|
|
312 printf("Output: /dev/%s\n", output);
|
|
313 return pipe_fd;
|
|
314 }
|
|
315 }
|
|
316 }
|
|
317 }
|
|
318
|
|
319 }else{
|
|
320 DIR *dir;
|
|
321 struct dirent *dirp;
|
|
322 const char prefix[]="/sys/class/video4linux/";
|
|
323 char *ptr, *io;
|
|
324 int fd;
|
|
325 int low=9999;
|
|
326 int tfd;
|
|
327 int tnum;
|
|
328
|
|
329 if ((dir=opendir(prefix))== NULL) {
|
|
330 printf( "Failed to open '%s'", prefix);
|
|
331 return -1;
|
|
332 }
|
|
333
|
|
334 while ((dirp=readdir(dir)) != NULL) {
|
|
335 if (!strncmp(dirp->d_name, "video", 5)) {
|
|
336 strcpy(buffer, prefix);
|
|
337 strcat(buffer, dirp->d_name);
|
|
338 strcat(buffer, "/name");
|
|
339 if ((fd=open(buffer, O_RDONLY)) >= 0) {
|
|
340 if ((read(fd, buffer, sizeof(buffer)-1))<0) {
|
|
341 close(fd);
|
|
342 continue;
|
|
343 }
|
|
344 ptr = strtok(buffer, " ");
|
|
345 if (strcmp(ptr,"Video")) {
|
|
346 close(fd);
|
|
347 continue;
|
|
348 }
|
|
349 major = strtok(NULL, " ");
|
|
350 minor = strtok(NULL, " ");
|
|
351 io = strtok(NULL, " \n");
|
|
352 if (strcmp(major, "loopback") || strcmp(io, "input")) {
|
|
353 close(fd);
|
|
354 continue;
|
|
355 }
|
|
356 if ((ptr=strtok(buffer, " "))==NULL) {
|
|
357 close(fd);
|
|
358 continue;
|
|
359 }
|
|
360 tnum = atoi(minor);
|
|
361 if (tnum < low) {
|
|
362 strcpy(buffer, "/dev/");
|
|
363 strcat(buffer, dirp->d_name);
|
|
364 if ((tfd=open(buffer, O_RDWR))>=0) {
|
|
365 strcpy(pipepath, buffer);
|
|
366 if (pipe_fd>=0) {
|
|
367 close(pipe_fd);
|
|
368 }
|
|
369 pipe_fd = tfd;
|
|
370 low = tnum;
|
|
371 }
|
|
372 }
|
|
373 close(fd);
|
|
374 }
|
|
375 }
|
|
376 }
|
|
377
|
|
378
|
|
379 closedir(dir);
|
|
380 if (pipe_fd >= 0)
|
|
381 printf("Opened input of %s", pipepath);
|
|
382 }
|
|
383
|
|
384 return pipe_fd;
|
|
385 }
|
|
386
|
|
387 int main (int argc, char **argv)
|
|
388 {
|
|
389 char palette[10]={'\0'};
|
|
390
|
|
391 if (argc != 3) {
|
|
392 printf("dummy.c\n");
|
|
393 printf("A example for using a video4linux loopback in zero-copy mode\n");
|
|
394 printf("Written by Jeroen Vreeken, 2000\n");
|
|
395 printf("Updated to vloopback API v0.97\n\n");
|
|
396 printf("Usage:\n\n");
|
|
397 printf("dummy widthxheight rgb24|yuv420p\n\n");
|
|
398 printf("example: dummy 352x288 yuv420p\n\n");
|
|
399 exit(1);
|
|
400 }
|
|
401
|
|
402 sscanf(argv[1], "%dx%d", &width, &height);
|
|
403 sscanf(argv[2], "%s", palette);
|
|
404
|
|
405 if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24;
|
|
406 else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P;
|
|
407 else fmt = VIDEO_PALETTE_RGB24;
|
|
408
|
|
409 /* Default startup values, nothing special
|
|
410 width=352;
|
|
411 height=288;
|
|
412 */
|
|
413
|
|
414 v4ldev=open_vidpipe();
|
|
415 if (v4ldev < 0) {
|
|
416 printf ("Failed to open video loopback device\nError[%s]\n",strerror(errno));
|
|
417 exit(1);
|
|
418 }
|
|
419 image_out=v4l_create(v4ldev, MAXWIDTH*MAXHEIGHT*3);
|
|
420 if (!image_out) {
|
|
421 exit(1);
|
|
422 printf ("Failed to set device to zero-copy mode\nError[%s]\n",strerror(errno));
|
|
423 }
|
|
424
|
|
425 signal (SIGIO, sighandler);
|
|
426
|
|
427 printf("\nListening.\n");
|
|
428 while (1) {
|
|
429 sleep(1000);
|
|
430 }
|
|
431
|
|
432 close (v4ldev);
|
|
433 free(image_out);
|
|
434 exit(0);
|
|
435 }
|