1 /*
2 * interface to the v4l driver
3 *
4 * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
5 *
6 */
7 #include "config.h"
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <math.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <pthread.h>
18 #include <sys/types.h>
19 #include <sys/time.h>
20 #include <sys/ioctl.h>
21 #include <sys/stat.h>
22 #include <sys/mman.h>
23
24 #include "videodev.h"
25
26 #include "grab-ng.h"
27
28 #include "struct-dump.h"
29 #include "struct-v4l.h"
30
31 #define SYNC_TIMEOUT 5
32
33 /* ---------------------------------------------------------------------- */
34
35 /* open+close */
36 static void* v4l_open(char *device);
37 static int v4l_close(void *handle);
38
39 /* attributes */
40 static char* v4l_devname(void *handle);
41 static int v4l_flags(void *handle);
42 static struct ng_attribute* v4l_attrs(void *handle);
43 static int v4l_read_attr(struct ng_attribute*);
44 static void v4l_write_attr(struct ng_attribute*, int val);
45
46 /* overlay */
47 static int v4l_setupfb(void *handle, struct ng_video_fmt *fmt, void *base);
48 static int v4l_overlay(void *handle, struct ng_video_fmt *fmt, int x, int y,
49 struct OVERLAY_CLIP *oc, int count, int aspect);
50
51 /* capture video */
52 static int v4l_setformat(void *handle, struct ng_video_fmt *fmt);
53 static int v4l_startvideo(void *handle, int fps, unsigned int buffers);
54 static void v4l_stopvideo(void *handle);
55 static struct ng_video_buf* v4l_nextframe(void *handle);
56 static struct ng_video_buf* v4l_getimage(void *handle);
57
58 /* tuner */
59 static unsigned long v4l_getfreq(void *handle);
60 static void v4l_setfreq(void *handle, unsigned long freq);
61 static int v4l_tuned(void *handle);
62
63 /* ---------------------------------------------------------------------- */
64
65 static const char *device_cap[] = {
66 "capture", "tuner", "teletext", "overlay", "chromakey", "clipping",
67 "frameram", "scales", "monochrome", NULL
68 };
69
70 static const char *device_pal[] = {
71 "-", "grey", "hi240", "rgb16", "rgb24", "rgb32", "rgb15",
72 "yuv422", "yuyv", "uyvy", "yuv420", "yuv411", "raw",
73 "yuv422p", "yuv411p", "yuv420p", "yuv410p"
74 };
75 #define PALETTE(x) ((x < sizeof(device_pal)/sizeof(char*)) ? device_pal[x] : "UNKNOWN")
76
77 static struct STRTAB stereo[] = {
78 { 0, "auto" },
79 { VIDEO_SOUND_MONO, "mono" },
80 { VIDEO_SOUND_STEREO, "stereo" },
81 { VIDEO_SOUND_LANG1, "lang1" },
82 { VIDEO_SOUND_LANG2, "lang2" },
83 { -1, NULL },
84 };
85 static struct STRTAB norms_v4l[] = {
86 { VIDEO_MODE_PAL, "PAL" },
87 { VIDEO_MODE_NTSC, "NTSC" },
88 { VIDEO_MODE_SECAM, "SECAM" },
89 { VIDEO_MODE_AUTO, "AUTO" },
90 { -1, NULL }
91 };
92 static struct STRTAB norms_bttv[] = {
93 { VIDEO_MODE_PAL, "PAL" },
94 { VIDEO_MODE_NTSC, "NTSC" },
95 { VIDEO_MODE_SECAM, "SECAM" },
96 { 3, "PAL-NC" },
97 { 4, "PAL-M" },
98 { 5, "PAL-N" },
99 { 6, "NTSC-JP" },
100 { -1, NULL }
101 };
102
103 static unsigned short format2palette[VIDEO_FMT_COUNT] = {
104 [ VIDEO_RGB08 ] = VIDEO_PALETTE_HI240,
105 [ VIDEO_GRAY ] = VIDEO_PALETTE_GREY,
106 [ VIDEO_RGB15_LE ] = VIDEO_PALETTE_RGB555,
107 [ VIDEO_RGB16_LE ] = VIDEO_PALETTE_RGB565,
108 [ VIDEO_BGR24 ] = VIDEO_PALETTE_RGB24,
109 [ VIDEO_BGR32 ] = VIDEO_PALETTE_RGB32,
110 [ VIDEO_YUYV ] = VIDEO_PALETTE_YUV422,
111 [ VIDEO_UYVY ] = VIDEO_PALETTE_UYVY,
112 [ VIDEO_YUV422P ] = VIDEO_PALETTE_YUV422P,
113 [ VIDEO_YUV420P ] = VIDEO_PALETTE_YUV420P,
114 };
115
116 /* pass 0/1 by reference */
117 static int one = 1, zero = 0;
118
119 /* ---------------------------------------------------------------------- */
120
121 struct v4l_handle {
122 int fd;
123
124 /* general informations */
125 struct video_capability capability;
126 struct video_channel *channels;
127 struct video_tuner tuner;
128 struct video_audio audio;
129 struct video_picture pict;
130
131 /* attributes */
132 int nattr;
133 struct ng_attribute *attr;
134 int input;
135 int audio_mode;
136
137 /* overlay */
138 struct video_buffer fbuf;
139 struct video_window win;
140 int ov_error;
141 unsigned int ov_fmtid;
142 int ov_enabled;
143 int ov_on;
144
145 /* capture */
146 int use_read;
147 struct ng_video_fmt fmt;
148 long long start;
149 int fps;
150
151 /* capture via read() */
152 struct ng_video_fmt rd_fmt;
153 struct video_window rd_win;
154 unsigned int rd_fmtid;
155
156 /* capture to mmap()'ed buffers */
157 struct video_mbuf mbuf;
158 unsigned char *mmap;
159 unsigned int nbuf;
160 unsigned int queue;
161 unsigned int waiton;
162 int probe[VIDEO_FMT_COUNT];
163 struct video_mmap *buf_v4l;
164 struct ng_video_buf *buf_me;
165 };
166
167 struct ng_vid_driver v4l_driver = {
168 name: "v4l",
169 open: v4l_open,
170 close: v4l_close,
171
172 get_devname: v4l_devname,
173 capabilities: v4l_flags,
174 list_attrs: v4l_attrs,
175
176 setupfb: v4l_setupfb,
177 overlay: v4l_overlay,
178
179 setformat: v4l_setformat,
180 startvideo: v4l_startvideo,
181 stopvideo: v4l_stopvideo,
182 nextframe: v4l_nextframe,
183 getimage: v4l_getimage,
184
185 getfreq: v4l_getfreq,
186 setfreq: v4l_setfreq,
187 is_tuned: v4l_tuned,
188 };
189
190 /* ---------------------------------------------------------------------- */
191
192 static int alarms;
193
194 static void
195 sigalarm(int signal)
196 {
197 alarms++;
198 fprintf(stderr,"v4l: timeout (got SIGALRM), hardware/driver problems?\n");
199 }
200
201 static void
202 siginit(void)
203 {
204 struct sigaction act,old;
205
206 memset(&act,0,sizeof(act));
207 act.sa_handler = sigalarm;
208 sigemptyset(&act.sa_mask);
209 sigaction(SIGALRM,&act,&old);
210 }
211
212 /* ---------------------------------------------------------------------- */
213
214 #define PREFIX "ioctl: "
215
216 static int
217 xioctl(int fd, int cmd, void *arg)
218 {
219 int rc;
220
221 rc = ioctl(fd,cmd,arg);
222 if (0 == rc && ng_debug < 2)
223 return 0;
224 print_ioctl(stderr,ioctls_v4l1,PREFIX,cmd,arg);
225 fprintf(stderr,": %s\n",(rc == 0) ? "ok" : strerror(errno));
226 return rc;
227 }
228
229 /* ---------------------------------------------------------------------- */
230
231 static void
232 v4l_add_attr(struct v4l_handle *h, int id, int type,
233 int defval, struct STRTAB *choices)
234 {
235 h->attr = realloc(h->attr,(h->nattr+2) * sizeof(struct ng_attribute));
236 memset(h->attr+h->nattr,0,sizeof(struct ng_attribute)*2);
237 h->attr[h->nattr].id = id;
238 h->attr[h->nattr].type = type;
239 h->attr[h->nattr].defval = defval;
240 h->attr[h->nattr].choices = choices;
241 if (ATTR_TYPE_INTEGER == type) {
242 h->attr[h->nattr].min = 0;
243 h->attr[h->nattr].max = 65535;
244 }
245 if (id < ATTR_ID_COUNT)
246 h->attr[h->nattr].name = ng_attr_to_desc[id];
247
248 h->attr[h->nattr].read = v4l_read_attr;
249 h->attr[h->nattr].write = v4l_write_attr;
250 h->attr[h->nattr].handle = h;
251 h->nattr++;
252 }
253
254 static void*
255 v4l_open(char *device)
256 {
257 struct v4l_handle *h;
258 struct STRTAB *inputs;
259 struct STRTAB *norms;
260 unsigned int i;
261 int rc;
262
263 h = malloc(sizeof(*h));
264 if (NULL == h)
265 return NULL;
266 memset(h,0,sizeof(*h));
267
268 /* open device */
269 if (-1 == (h->fd = open(device,O_RDWR))) {
270 fprintf(stderr,"v4l: open %s: %s\n",device,strerror(errno));
271 goto err;
272 }
273 if (-1 == ioctl(h->fd,VIDIOCGCAP,&h->capability))
274 goto err;
275
276 if (ng_debug)
277 fprintf(stderr, "v4l: open: %s (%s)\n",device,h->capability.name);
278 fcntl(h->fd,F_SETFD,FD_CLOEXEC);
279 siginit();
280 if (ng_debug) {
281 fprintf(stderr," capabilities: ");
282 for (i = 0; device_cap[i] != NULL; i++)
283 if (h->capability.type & (1 << i))
284 fprintf(stderr," %s",device_cap[i]);
285 fprintf(stderr,"\n");
286 fprintf(stderr," size : %dx%d => %dx%d\n",
287 h->capability.minwidth,h->capability.minheight,
288 h->capability.maxwidth,h->capability.maxheight);
289 }
290
291 /* input sources */
292 if (ng_debug)
293 fprintf(stderr," channels: %d\n",h->capability.channels);
294 h->channels = malloc(sizeof(struct video_channel)*h->capability.channels);
295 memset(h->channels,0,sizeof(struct video_channel)*h->capability.channels);
296 inputs = malloc(sizeof(struct STRTAB)*(h->capability.channels+1));
297 memset(inputs,0,sizeof(struct STRTAB)*(h->capability.channels+1));
298 for (i = 0; i < h->capability.channels; i++) {
299 h->channels[i].channel = i;
300 xioctl(h->fd,VIDIOCGCHAN,&(h->channels[i]));
301 inputs[i].nr = i;
302 inputs[i].str = h->channels[i].name;
303 if (ng_debug)
304 fprintf(stderr," %s: %d %s%s %s%s\n",
305 h->channels[i].name,
306 h->channels[i].tuners,
307 (h->channels[i].flags & VIDEO_VC_TUNER) ? "tuner " : "",
308 (h->channels[i].flags & VIDEO_VC_AUDIO) ? "audio " : "",
309 (h->channels[i].type & VIDEO_TYPE_TV) ? "tv " : "",
310 (h->channels[i].type & VIDEO_TYPE_CAMERA) ? "camera " : "");
311 }
312 inputs[i].nr = -1;
313 inputs[i].str = NULL;
314 v4l_add_attr(h,ATTR_ID_INPUT,ATTR_TYPE_CHOICE,0,inputs);
315
316 /* audios */
317 if (ng_debug)
318 fprintf(stderr," audios : %d\n",h->capability.audios);
319 if (h->capability.audios) {
320 h->audio.audio = 0;
321 xioctl(h->fd,VIDIOCGAUDIO,&h->audio);
322 if (ng_debug) {
323 fprintf(stderr," %d (%s): ",i,h->audio.name);
324 if (h->audio.flags & VIDEO_AUDIO_MUTABLE)
325 fprintf(stderr,"muted=%s ",
326 (h->audio.flags&VIDEO_AUDIO_MUTE) ? "yes":"no");
327 if (h->audio.flags & VIDEO_AUDIO_VOLUME)
328 fprintf(stderr,"volume=%d ",h->audio.volume);
329 if (h->audio.flags & VIDEO_AUDIO_BASS)
330 fprintf(stderr,"bass=%d ",h->audio.bass);
331 if (h->audio.flags & VIDEO_AUDIO_TREBLE)
332 fprintf(stderr,"treble=%d ",h->audio.treble);
333 fprintf(stderr,"\n");
334 }
335 v4l_add_attr(h,ATTR_ID_MUTE,ATTR_TYPE_BOOL,0,NULL);
336 v4l_add_attr(h,ATTR_ID_AUDIO_MODE,ATTR_TYPE_CHOICE,0,stereo);
337 if (h->audio.flags & VIDEO_AUDIO_VOLUME)
338 v4l_add_attr(h,ATTR_ID_VOLUME,ATTR_TYPE_INTEGER,0,NULL);
339 }
340
341 /* tv norms / tuner */
342 norms = malloc(sizeof(norms_v4l));
343 memcpy(norms,norms_v4l,sizeof(norms_v4l));
344 if (h->capability.type & VID_TYPE_TUNER) {
345 /* have tuner */
346 xioctl(h->fd,VIDIOCGTUNER,&h->tuner);
347 if (ng_debug)
348 fprintf(stderr," tuner : %s %lu-%lu",
349 h->tuner.name,h->tuner.rangelow,h->tuner.rangehigh);
350 for (i = 0; norms[i].str != NULL; i++) {
351 if (h->tuner.flags & (1<<i)) {
352 if (ng_debug)
353 fprintf(stderr," %s",norms[i].str);
354 } else
355 norms[i].nr = -1;
356 }
357 if (ng_debug)
358 fprintf(stderr,"\n");
359 } else {
360 /* no tuner */
361 struct video_channel vchan;
362 memcpy(&vchan, &h->channels[0], sizeof(struct video_channel));
363 for (i = 0; norms[i].str != NULL; i++) {
364 vchan.norm = i;
365 if (-1 == xioctl(h->fd,VIDIOCSCHAN,&vchan))
366 norms[i].nr = -1;
367 else if (ng_debug)
368 fprintf(stderr," %s",norms[i].str);
369 }
370 /* restore settings after probe */
371 memcpy(&vchan, &h->channels[0], sizeof(struct video_channel));
372 xioctl(h->fd,VIDIOCSCHAN,&vchan);
373 if (ng_debug)
374 fprintf(stderr,"\n");
375 }
376
377 #if 1
378 #define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
379 /* dirty hack time / v4l design flaw -- works with bttv only
380 * this adds support for a few less common PAL versions */
381 if (-1 != (rc = ioctl(h->fd,BTTV_VERSION,&i))) {
382 norms = norms_bttv;
383 if (ng_debug || rc < 0x000700)
384 fprintf(stderr,"v4l: bttv version %d.%d.%d\n",
385 (rc >> 16) & 0xff,
386 (rc >> 8) & 0xff,
387 rc & 0xff);
388 if (rc < 0x000700)
389 fprintf(stderr,
390 "v4l: prehistoric bttv version found, please try to\n"
391 " upgrade the driver before mailing bug reports\n");
392 }
393 #endif
394 v4l_add_attr(h,ATTR_ID_NORM,ATTR_TYPE_CHOICE,0,norms);
395
396 /* frame buffer */
397 xioctl(h->fd,VIDIOCGFBUF,&h->fbuf);
398 if (ng_debug)
399 fprintf(stderr," fbuffer : base=0x%p size=%dx%d depth=%d bpl=%d\n",
400 h->fbuf.base, h->fbuf.width, h->fbuf.height,
401 h->fbuf.depth, h->fbuf.bytesperline);
402
403 /* picture parameters */
404 xioctl(h->fd,VIDIOCGPICT,&h->pict);
405 v4l_add_attr(h,ATTR_ID_BRIGHT, ATTR_TYPE_INTEGER,0,NULL);
406 v4l_add_attr(h,ATTR_ID_HUE, ATTR_TYPE_INTEGER,0,NULL);
407 v4l_add_attr(h,ATTR_ID_COLOR, ATTR_TYPE_INTEGER,0,NULL);
408 v4l_add_attr(h,ATTR_ID_CONTRAST,ATTR_TYPE_INTEGER,0,NULL);
409 if (ng_debug) {
410 fprintf(stderr,
411 " picture : brightness=%d hue=%d colour=%d contrast=%d\n",
412 h->pict.brightness, h->pict.hue,
413 h->pict.colour, h->pict.contrast);
414 fprintf(stderr,
415 " picture : whiteness=%d depth=%d palette=%s\n",
416 h->pict.whiteness, h->pict.depth, PALETTE(h->pict.palette));
417 }
418
419 if (h->capability.type & VID_TYPE_CAPTURE) {
420 /* map grab buffer */
421 if (0 == xioctl(h->fd,VIDIOCGMBUF,&h->mbuf)) {
422 if (ng_debug)
423 fprintf(stderr," mbuf: size=%d frames=%d\n",
424 h->mbuf.size,h->mbuf.frames);
425 h->mmap = mmap(0,h->mbuf.size,PROT_READ|PROT_WRITE,
426 MAP_SHARED,h->fd,0);
427 if ((unsigned char*)-1 == h->mmap)
428 perror("mmap");
429 } else {
430 h->mmap = (unsigned char*)-1;
431 }
432 if ((unsigned char*)-1 != h->mmap) {
433 if (ng_debug)
434 fprintf(stderr," v4l: using mapped buffers for capture\n");
435 h->use_read = 0;
436 h->nbuf = h->mbuf.frames;
437 h->buf_v4l = malloc(h->nbuf * sizeof(struct video_mmap));
438 memset(h->buf_v4l,0,h->nbuf * sizeof(struct video_mmap));
439 h->buf_me = malloc(h->nbuf * sizeof(struct ng_video_buf));
440 for (i = 0; i < h->nbuf; i++) {
441 ng_init_video_buf(h->buf_me+i);
442 h->buf_me[i].release = ng_wakeup_video_buf;
443 }
444 } else {
445 if (ng_debug)
446 fprintf(stderr," v4l: using read() for capture\n");
447 h->use_read = 1;
448 }
449 }
450
451 return h;
452
453 err:
454 if (h->fd != -1)
455 close(h->fd);
456 free(h);
457 return NULL;
458 }
459
460 static int
461 v4l_close(void *handle)
462 {
463 struct v4l_handle *h = handle;
464
465 if (ng_debug)
466 fprintf(stderr, "v4l: close\n");
467
468 if ((unsigned char*)-1 != h->mmap)
469 munmap(h->mmap,h->mbuf.size);
470
471 close(h->fd);
472 free(h);
473 return 0;
474 }
475
476 /* ---------------------------------------------------------------------- */
477
478 static char*
479 v4l_devname(void *handle)
480 {
481 struct v4l_handle *h = handle;
482 return h->capability.name;
483 }
484
485 static int v4l_flags(void *handle)
486 {
487 struct v4l_handle *h = handle;
488 int ret = 0;
489
490 if (h->capability.type & VID_TYPE_OVERLAY)
491 ret |= CAN_OVERLAY;
492 if (h->capability.type & VID_TYPE_CAPTURE &&
493 !h->ov_error)
494 ret |= CAN_CAPTURE;
495 if (h->capability.type & VID_TYPE_TUNER)
496 ret |= CAN_TUNE;
497 if (h->capability.type & VID_TYPE_CHROMAKEY)
498 ret |= NEEDS_CHROMAKEY;
499 return ret;
500 }
501
502 static struct ng_attribute* v4l_attrs(void *handle)
503 {
504 struct v4l_handle *h = handle;
505 return h->attr;
506 }
507
508 static int audio_mode_mask2bit(int mode)
509 {
510 if (mode & VIDEO_SOUND_STEREO)
511 return VIDEO_SOUND_STEREO;
512 if (mode & VIDEO_SOUND_LANG1)
513 return VIDEO_SOUND_LANG1;
514 if (mode & VIDEO_SOUND_LANG2)
515 return VIDEO_SOUND_LANG2;
516 if (mode & VIDEO_SOUND_MONO)
517 return VIDEO_SOUND_MONO;
518 return 0;
519 }
520
521 static int v4l_read_attr(struct ng_attribute *attr)
522 {
523 struct v4l_handle *h = attr->handle;
524
525 switch (attr->id) {
526 case ATTR_ID_INPUT:
527 return -1;
528 case ATTR_ID_NORM:
529 xioctl(h->fd, VIDIOCGCHAN, &h->channels[h->input]);
530 return h->channels[h->input].norm;
531 case ATTR_ID_MUTE:
532 xioctl(h->fd, VIDIOCGAUDIO, &h->audio);
533 return h->audio.flags & VIDEO_AUDIO_MUTE;
534 case ATTR_ID_VOLUME:
535 xioctl(h->fd, VIDIOCGAUDIO, &h->audio);
536 return h->audio.volume;
537 case ATTR_ID_AUDIO_MODE:
538 xioctl(h->fd, VIDIOCGAUDIO, &h->audio);
539 return audio_mode_mask2bit(h->audio.mode);
540 case ATTR_ID_COLOR:
541 xioctl(h->fd, VIDIOCGPICT, &h->pict);
542 return h->pict.colour;
543 case ATTR_ID_BRIGHT:
544 xioctl(h->fd, VIDIOCGPICT, &h->pict);
545 return h->pict.brightness;
546 case ATTR_ID_HUE:
547 xioctl(h->fd, VIDIOCGPICT, &h->pict);
548 return h->pict.hue;
549 case ATTR_ID_CONTRAST:
550 xioctl(h->fd, VIDIOCGPICT, &h->pict);
551 return h->pict.contrast;
552 }
553 return -1;
554 }
555
556 static void v4l_write_attr(struct ng_attribute *attr, int val)
557 {
558 struct v4l_handle *h = attr->handle;
559
560 /* read ... */
561 switch (attr->id) {
562 case ATTR_ID_INPUT:
563 /* nothing */
564 break;
565 case ATTR_ID_NORM:
566 xioctl(h->fd, VIDIOCGCHAN, &h->channels[h->input]);
567 break;
568 case ATTR_ID_MUTE:
569 case ATTR_ID_VOLUME:
570 case ATTR_ID_AUDIO_MODE:
571 xioctl(h->fd, VIDIOCGAUDIO, &h->audio);
572 break;
573 case ATTR_ID_COLOR:
574 case ATTR_ID_BRIGHT:
575 case ATTR_ID_HUE:
576 case ATTR_ID_CONTRAST:
577 xioctl(h->fd, VIDIOCGPICT, &h->pict);
578 break;
579 }
580
581 /* ... modify ... */
582 switch (attr->id) {
583 case ATTR_ID_INPUT:
584 h->input = val;
585 h->audio_mode = 0;
586 break;
587 case ATTR_ID_NORM:
588 h->channels[h->input].norm = val;
589 h->audio_mode = 0;
590 break;
591 case ATTR_ID_MUTE:
592 if (val)
593 h->audio.flags |= VIDEO_AUDIO_MUTE;
594 else
595 h->audio.flags &= ~VIDEO_AUDIO_MUTE;
596 break;
597 case ATTR_ID_VOLUME:
598 h->audio.volume = val;
599 break;
600 case ATTR_ID_AUDIO_MODE:
601 h->audio_mode = val;
602 break;
603 case ATTR_ID_COLOR:
604 h->pict.colour = val;
605 break;
606 case ATTR_ID_BRIGHT:
607 h->pict.brightness = val;
608 break;
609 case ATTR_ID_HUE:
610 h->pict.hue = val;
611 break;
612 case ATTR_ID_CONTRAST:
613 h->pict.contrast = val;
614 break;
615 }
616 /* have to set that all the time as read and write have
617 slightly different semantics:
618 read == bitmask with all available modes flagged
619 write == one bit set (for the selected mode, zero is autodetect)
620 */
621 h->audio.mode = h->audio_mode;
622
623 /* ... write */
624 switch (attr->id) {
625 case ATTR_ID_INPUT:
626 case ATTR_ID_NORM:
627 xioctl(h->fd, VIDIOCSCHAN, &h->channels[h->input]);
628 break;
629 case ATTR_ID_MUTE:
630 case ATTR_ID_VOLUME:
631 case ATTR_ID_AUDIO_MODE:
632 xioctl(h->fd, VIDIOCSAUDIO, &h->audio);
633 break;
634 case ATTR_ID_COLOR:
635 case ATTR_ID_BRIGHT:
636 case ATTR_ID_HUE:
637 case ATTR_ID_CONTRAST:
638 xioctl(h->fd, VIDIOCSPICT, &h->pict);
639 break;
640 }
641 }
642
643 static unsigned long
644 v4l_getfreq(void *handle)
645 {
646 struct v4l_handle *h = handle;
647 unsigned long freq;
648
649 xioctl(h->fd, VIDIOCGFREQ, &freq);
650 return freq;
651 }
652
653 static void
654 v4l_setfreq(void *handle, unsigned long freq)
655 {
656 struct v4l_handle *h = handle;
657
658 if (ng_debug)
659 fprintf(stderr,"v4l: freq: %.3f\n",(float)freq/16);
660 xioctl(h->fd, VIDIOCSFREQ, &freq);
661 h->audio_mode = 0;
662 }
663
664 static int
665 v4l_tuned(void *handle)
666 {
667 struct v4l_handle *h = handle;
668
669 /* usleep(10000); */
670 if (-1 == xioctl(h->fd,VIDIOCGTUNER,&h->tuner))
671 return 0;
672 return h->tuner.signal ? 1 : 0;
673 }
674
675
676 /* ---------------------------------------------------------------------- */
677 /* do overlay */
678
679 int
680 v4l_setupfb(void *handle, struct ng_video_fmt *fmt, void *base)
681 {
682 struct v4l_handle *h = handle;
683
684 /* overlay supported ?? */
685 if (!(h->capability.type & VID_TYPE_OVERLAY)) {
686 if (ng_debug)
687 fprintf(stderr,"v4l: device has no overlay support\n");
688 return -1;
689 }
690
691 /* double-check settings */
692 if (ng_debug)
693 fprintf(stderr,"v4l: %dx%d, %d bit/pixel, %d byte/scanline\n",
694 h->fbuf.width,h->fbuf.height,
695 h->fbuf.depth,h->fbuf.bytesperline);
696 if ((fmt->bytesperline > 0 &&
697 h->fbuf.bytesperline != fmt->bytesperline) ||
698 (h->fbuf.width != fmt->width) ||
699 (h->fbuf.height != fmt->height)) {
700 fprintf(stderr,
701 "WARNING: v4l and x11 disagree about the screen size\n"
702 "WARNING: Is v4l-conf installed correctly?\n");
703 h->ov_error = 1;
704 }
705 if (ng_vfmt_to_depth[fmt->fmtid] != ((h->fbuf.depth+7)&0xf8)) {
706 fprintf(stderr,
707 "WARNING: v4l and x11 disagree about the color depth\n"
708 "WARNING: fbuf.depth=%d, x11 depth=%d\n"
709 "WARNING: Is v4l-conf installed correctly?\n",
710 h->fbuf.depth,ng_vfmt_to_depth[fmt->fmtid]);
711 h->ov_error = 1;
712 }
713 if (NULL != base) {
714 /* XXX: minor differences are legal... (matrox problems) */
715 if ((void*)((unsigned long)h->fbuf.base & 0xfffff000) !=
716 (void*)((unsigned long)base & 0xfffff000)) {
717 fprintf(stderr,
718 "WARNING: v4l and dga disagree about the framebuffer base\n"
719 "WARNING: fbuf.base=%p, dga=%p\n"
720 "WARNING: Is v4l-conf installed correctly?\n",
721 h->fbuf.base,base);
722 h->ov_error = 1;
723 }
724 }
725 if (h->ov_error) {
726 fprintf(stderr,"WARNING: overlay mode disabled\n");
727 return -1;
728 }
729 return 0;
730 }
731
732 static void
733 v4l_overlay_set(struct v4l_handle *h, int state)
734 {
735 int rc;
736
737 if (0 == state) {
738 /* off */
739 if (0 == h->ov_on)
740 return;
741 xioctl(h->fd, VIDIOCCAPTURE, &zero);
742 h->ov_on = 0;
743 } else {
744 /* on */
745 h->pict.depth = ng_vfmt_to_depth[h->ov_fmtid];
746 h->pict.palette = GETELEM(format2palette,h->ov_fmtid,0);
747 xioctl(h->fd, VIDIOCSPICT, &h->pict);
748 rc = xioctl(h->fd, VIDIOCSWIN, &h->win);
749 if (0 == rc) {
750 if (0 != h->ov_on)
751 return;
752 xioctl(h->fd, VIDIOCCAPTURE, &one);
753 h->ov_on = 1;
754 } else {
755 /* disable overlay on SWIN failure */
756 xioctl(h->fd, VIDIOCCAPTURE, &zero);
757 h->ov_on = 0;
758 }
759 }
760 }
761
762 int
763 v4l_overlay(void *handle, struct ng_video_fmt *fmt, int x, int y,
764 struct OVERLAY_CLIP *oc, int count, int aspect)
765 {
766 struct v4l_handle *h = handle;
767 int i;
768
769 if (h->ov_error)
770 return -1;
771
772 if (NULL == fmt) {
773 if (ng_debug)
774 fprintf(stderr,"v4l: overlay off\n");
775 h->ov_enabled = 0;
776 v4l_overlay_set(h,h->ov_enabled);
777 return 0;
778 }
779
780 h->win.x = x;
781 h->win.y = y;
782 h->win.width = fmt->width;
783 h->win.height = fmt->height;
784 h->win.flags = 0;
785 h->win.chromakey = 0;
786
787 /* check against max. size */
788 xioctl(h->fd,VIDIOCGCAP,&h->capability);
789 if (h->win.width > h->capability.maxwidth) {
790 h->win.width = h->capability.maxwidth;
791 h->win.x += (fmt->width - h->win.width)/2;
792 }
793 if (h->win.height > h->capability.maxheight) {
794 h->win.height = h->capability.maxheight;
795 h->win.y += (fmt->height - h->win.height)/2;
796 }
797 if (aspect)
798 ng_ratio_fixup(&h->win.width,&h->win.height,&h->win.x,&h->win.y);
799
800 #if 0
801 /* pass aligned values -- the driver does'nt get it right yet */
802 h->win.width &= ~3;
803 h->win.height &= ~3;
804 h->win.x &= ~3;
805 if (h->win.x < x)
806 h->win.x += 4;
807 if (h->win.x+h->win.width > x+fmt->width)
808 h->win.width -= 4;
809 #endif
810
811 /* fixups */
812 ng_check_clipping(h->win.width, h->win.height,
813 x - h->win.x, y - h->win.y,
814 oc, &count);
815
816 /* handle clipping */
817 if (h->win.clips) {
818 free(h->win.clips);
819 h->win.clips = NULL;
820 }
821 h->win.clipcount = 0;
822 if (h->capability.type & VID_TYPE_CLIPPING && count > 0) {
823 h->win.clipcount = count;
824 h->win.clips = malloc(count * sizeof(struct video_clip));
825 for (i = 0; i < count; i++) {
826 h->win.clips[i].x = oc[i].x1;
827 h->win.clips[i].y = oc[i].y1;
828 h->win.clips[i].width = oc[i].x2-oc[i].x1;
829 h->win.clips[i].height = oc[i].y2-oc[i].y1;
830 }
831 }
832 if (h->capability.type & VID_TYPE_CHROMAKEY)
833 h->win.chromakey = ng_chromakey;
834 h->ov_enabled = 1;
835 h->ov_fmtid = fmt->fmtid;
836 v4l_overlay_set(h,h->ov_enabled);
837
838 if (ng_debug)
839 fprintf(stderr,"v4l: overlay win=%dx%d+%d+%d, %d clips\n",
840 fmt->width,fmt->height,x,y,count);
841 return 0;
842 }
843
844 /* ---------------------------------------------------------------------- */
845
846 static int
847 mm_queue(struct v4l_handle *h)
848 {
849 int frame = h->queue % h->nbuf;
850 int rc;
851
852 if (0 != h->buf_me[frame].refcount) {
853 if (0 != h->queue - h->waiton)
854 return -1;
855 fprintf(stderr,"v4l: waiting for a free buffer\n");
856 ng_waiton_video_buf(h->buf_me+frame);
857 }
858
859 rc = xioctl(h->fd,VIDIOCMCAPTURE,h->buf_v4l+frame);
860 if (0 == rc)
861 h->queue++;
862 return rc;
863 }
864
865 static void
866 mm_queue_all(struct v4l_handle *h)
867 {
868 for (;;) {
869 if (h->queue - h->waiton >= h->nbuf)
870 return;
871 if (0 != mm_queue(h))
872 return;
873 }
874 }
875
876 static int
877 mm_waiton(struct v4l_handle *h)
878 {
879 int frame = h->waiton % h->nbuf;
880 int rc;
881
882 if (0 == h->queue - h->waiton)
883 return -1;
884 h->waiton++;
885
886 alarms=0;
887 alarm(SYNC_TIMEOUT);
888
889 retry:
890 if (-1 == (rc = xioctl(h->fd,VIDIOCSYNC,h->buf_v4l+frame))) {
891 if (errno == EINTR && !alarms)
892 goto retry;
893 }
894 alarm(0);
895 if (-1 == rc)
896 return -1;
897 return frame;
898 }
899
900 static void
901 mm_clear(struct v4l_handle *h)
902 {
903 while (h->queue > h->waiton)
904 mm_waiton(h);
905 h->queue = 0;
906 h->waiton = 0;
907 }
908
909 static int
910 mm_probe(struct v4l_handle *h, unsigned int fmtid)
911 {
912 if (0 != h->probe[fmtid])
913 goto done;
914
915 if (ng_debug)
916 fprintf(stderr, "v4l: capture probe %s...\t",
917 ng_vfmt_to_desc[fmtid]);
918
919 h->buf_v4l[0].frame = 0;
920 h->buf_v4l[0].width = h->capability.minwidth;
921 h->buf_v4l[0].height = h->capability.minheight;
922 h->buf_v4l[0].format = GETELEM(format2palette,fmtid,0);
923 #if 1 /* bug compatibility: bttv up to 0.7.67 reports wrong minwidth */
924 if (h->buf_v4l[0].width == 32)
925 h->buf_v4l[0].width = 48;
926 #endif
927
928 if (0 == h->buf_v4l[0].format)
929 goto fail;
930 if (-1 == mm_queue(h))
931 goto fail;
932 if (-1 == mm_waiton(h))
933 goto fail;
934
935 if (ng_debug)
936 fprintf(stderr, "ok\n");
937 h->probe[fmtid] = 1;
938 goto done;
939
940 fail:
941 if (ng_debug)
942 fprintf(stderr, "failed\n");
943 h->probe[fmtid] = 2;
944
945 done:
946 mm_clear(h);
947 return h->probe[fmtid] == 1;
948 }
949
950 static int
951 mm_setparams(struct v4l_handle *h, struct ng_video_fmt *fmt)
952 {
953 unsigned int i;
954
955 /* buffers available ? */
956 if (h->mbuf.frames < 1)
957 return -1;
958
959 /* verify parameters */
960 xioctl(h->fd,VIDIOCGCAP,&h->capability);
961 if (fmt->width > h->capability.maxwidth)
962 fmt->width = h->capability.maxwidth;
963 if (fmt->height > h->capability.maxheight)
964 fmt->height = h->capability.maxheight;
965 fmt->bytesperline = fmt->width * ng_vfmt_to_depth[fmt->fmtid] / 8;
966
967 /* check if we can handle the format */
968 if (!mm_probe(h,fmt->fmtid))
969 return -1;
970
971 /* initialize everything */
972 h->nbuf = h->mbuf.frames;
973 for (i = 0; i < h->nbuf; i++) {
974 h->buf_v4l[i].format = GETELEM(format2palette,fmt->fmtid,0);
975 h->buf_v4l[i].frame = i;
976 h->buf_v4l[i].width = fmt->width;
977 h->buf_v4l[i].height = fmt->height;
978 h->buf_me[i].fmt = *fmt;
979 h->buf_me[i].data = h->mmap + h->mbuf.offsets[i];
980 h->buf_me[i].size = fmt->height * fmt->bytesperline;
981 }
982 return 0;
983 }
984
985 /* ---------------------------------------------------------------------- */
986
987 static int
988 read_setformat(struct v4l_handle *h, struct ng_video_fmt *fmt)
989 {
990 xioctl(h->fd,VIDIOCGCAP,&h->capability);
991 if (fmt->width > h->capability.maxwidth)
992 fmt->width = h->capability.maxwidth;
993 if (fmt->height > h->capability.maxheight)
994 fmt->height = h->capability.maxheight;
995 fmt->bytesperline = fmt->width * ng_vfmt_to_depth[fmt->fmtid] / 8;
996
997 h->rd_win.width = fmt->width;
998 h->rd_win.height = fmt->height;
999 h->rd_fmtid = fmt->fmtid;
1000
1001 h->pict.depth = ng_vfmt_to_depth[h->rd_fmtid];
1002 h->pict.palette = GETELEM(format2palette,h->rd_fmtid,0);
1003 if (-1 == xioctl(h->fd, VIDIOCSPICT, &h->pict))
1004 return -1;
1005 if (-1 == xioctl(h->fd, VIDIOCSWIN, &h->rd_win))
1006 return -1;
1007
1008 fmt->width = h->rd_win.width;
1009 fmt->height = h->rd_win.height;
1010 fmt->bytesperline = fmt->width * ng_vfmt_to_depth[fmt->fmtid] / 8;
1011 h->rd_fmt = *fmt;
1012 return 0;
1013 }
1014
1015 static struct ng_video_buf*
1016 read_getframe(struct v4l_handle *h)
1017 {
1018 struct ng_video_buf* buf;
1019 int size;
1020
1021 h->pict.depth = ng_vfmt_to_depth[h->rd_fmtid];
1022 h->pict.palette = GETELEM(format2palette,h->rd_fmtid,0);
1023 xioctl(h->fd, VIDIOCSPICT, &h->pict);
1024 xioctl(h->fd, VIDIOCSWIN, &h->rd_win);
1025 size = h->rd_fmt.bytesperline * h->rd_fmt.height;
1026 buf = ng_malloc_video_buf(&h->rd_fmt, size);
1027 if (NULL == buf)
1028 return NULL;
1029 if (size != read(h->fd,buf->data,size)) {
1030 ng_release_video_buf(buf);
1031 return NULL;
1032 }
1033 return buf;
1034 }
1035
1036 /* ---------------------------------------------------------------------- */
1037
1038 int
1039 v4l_setformat(void *handle, struct ng_video_fmt *fmt)
1040 {
1041 struct v4l_handle *h = handle;
1042 int rc;
1043
1044 #if 0
1045 /* for debugging color space conversion functions:
1046 force xawtv to capture some specific format */
1047 if (fmt->fmtid != VIDEO_YUV420P)
1048 return -1;
1049 #endif
1050
1051 if (ng_debug)
1052 fprintf(stderr,"v4l: setformat\n");
1053 if (h->use_read) {
1054 v4l_overlay_set(h,0);
1055 rc = read_setformat(h,fmt);
1056 v4l_overlay_set(h,h->ov_enabled);
1057 } else {
1058 if (h->queue != h->waiton)
1059 fprintf(stderr,"v4l: Huh? setformat: found queued buffers (%d %d)\n",
1060 h->queue, h->waiton);
1061 mm_clear(h);
1062 rc = mm_setparams(h,fmt);
1063 }
1064 return rc;
1065 }
1066
1067 int
1068 v4l_startvideo(void *handle, int fps, unsigned int buffers)
1069 {
1070 struct v4l_handle *h = handle;
1071
1072 if (ng_debug)
1073 fprintf(stderr,"v4l: startvideo\n");
1074 if (0 != h->fps)
1075 fprintf(stderr,"v4l: Huh? start: fps != 0\n");
1076 if (!h->use_read) {
1077 if (h->nbuf > buffers)
1078 h->nbuf = buffers;
1079 mm_queue_all(h);
1080 }
1081 h->start = ng_get_timestamp();
1082 h->fps = fps;
1083 return 0;
1084 }
1085
1086 void
1087 v4l_stopvideo(void *handle)
1088 {
1089 struct v4l_handle *h = handle;
1090
1091 if (ng_debug)
1092 fprintf(stderr,"v4l: stopvideo\n");
1093 if (0 == h->fps)
1094 fprintf(stderr,"v4l: Huh? stop: fps == 0\n");
1095 if (!h->use_read)
1096 mm_clear(h);
1097 h->fps = 0;
1098 }
1099
1100 struct ng_video_buf*
1101 v4l_nextframe(void *handle)
1102 {
1103 struct v4l_handle *h = handle;
1104 struct ng_video_buf* buf = NULL;
1105 int frame = 0;
1106
1107 if (ng_debug > 1)
1108 fprintf(stderr,"v4l: getimage\n");
1109
1110 if (0 == h->fps) {
1111 fprintf(stderr,"v4l: nextframe: fps == 0\n");
1112 return NULL;
1113 }
1114
1115 if (h->use_read) {
1116 if (buf)
1117 ng_release_video_buf(buf);
1118 v4l_overlay_set(h,0);
1119 buf = read_getframe(h);
1120 v4l_overlay_set(h,h->ov_enabled);
1121 if (NULL == buf)
1122 return NULL;
1123 memset(&buf->info,0,sizeof(buf->info));
1124 buf->info.ts = ng_get_timestamp() - h->start;
1125 return buf;
1126 } else {
1127 mm_queue_all(h);
1128 frame = mm_waiton(h);
1129 if (-1 == frame)
1130 return NULL;
1131 memset(&h->buf_me[frame].info,0,sizeof(h->buf_me[frame].info));
1132 h->buf_me[frame].refcount++;
1133 h->buf_me[frame].info.ts = ng_get_timestamp() - h->start;
1134 return h->buf_me+frame;
1135 }
1136 }
1137
1138 /* ---------------------------------------------------------------------- */
1139
1140 struct ng_video_buf*
1141 v4l_getimage(void *handle)
1142 {
1143 struct v4l_handle *h = handle;
1144 struct ng_video_buf* buf = NULL;
1145 int frame;
1146
1147 if (ng_debug)
1148 fprintf(stderr,"v4l: getimage\n");
1149
1150 if (0 != h->fps) {
1151 fprintf(stderr,"v4l: getimage: fps != 0\n");
1152 return NULL;
1153 }
1154 if (h->use_read) {
1155 v4l_overlay_set(h,0);
1156 buf = read_getframe(h);
1157 v4l_overlay_set(h,h->ov_enabled);
1158 return buf;
1159 } else {
1160 mm_queue(h);
1161 frame = mm_waiton(h);
1162 if (-1 == frame)
1163 return NULL;
1164 h->buf_me[frame].refcount++;
1165 return h->buf_me+frame;
1166 }
1167 }
1168
1169 /* ---------------------------------------------------------------------- */
1170
1171 extern void ng_plugin_init(void);
1172 void ng_plugin_init(void)
1173 {
1174 ng_vid_driver_register(NG_PLUGIN_MAGIC,__FILE__,&v4l_driver);
1175 }
1176
|
This page was automatically generated by the
LXR engine.
|