1 /*
2  * next generation[tm] xawtv capture interfaces
3  *
4  * (c) 2001 Gerd Knorr <kraxel@bytesex.org>
5  *
6  */
7 
8 #define NG_PRIVATE
9 #include "config.h"
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <pthread.h>
16 #include <dirent.h>
17 #include <fnmatch.h>
18 #include <errno.h>
19 #include <ctype.h>
20 #include <inttypes.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 
24 #include <dlfcn.h>
25 #ifndef RTLD_NOW
26 # define RTLD_NOW RTLD_LAZY
27 #endif
28 
29 #include "get_media_devices.h"
30 #include "grab-ng.h"
31 
32 int  ng_debug          = 0;
33 int  ng_chromakey      = 0x00ff00ff;
34 int  ng_jpeg_quality   = 75;
35 int  ng_ratio_x        = 4;
36 int  ng_ratio_y        = 3;
37 
38 char ng_v4l_conf[256]  = "v4l-conf";
39 
40 /* --------------------------------------------------------------------- */
41 
42 const unsigned int ng_vfmt_to_depth[] = {
43     0,               /* unused   */
44     8,               /* RGB8     */
45     8,               /* GRAY8    */
46     16,              /* RGB15 LE */
47     16,              /* RGB16 LE */
48     16,              /* RGB15 BE */
49     16,              /* RGB16 BE */
50     24,              /* BGR24    */
51     32,              /* BGR32    */
52     24,              /* RGB24    */
53     32,              /* RGB32    */
54     16,              /* LUT2     */
55     32,              /* LUT4     */
56     16,		     /* YUYV     */
57     16,		     /* YUV422P  */
58     12,		     /* YUV420P  */
59     0,		     /* MJPEG    */
60     0,		     /* JPEG     */
61     16,		     /* UYVY     */
62 };
63 
64 const char* ng_vfmt_to_desc[] = {
65     "none",
66     "8 bit PseudoColor (dithering)",
67     "8 bit StaticGray",
68     "15 bit TrueColor (LE)",
69     "16 bit TrueColor (LE)",
70     "15 bit TrueColor (BE)",
71     "16 bit TrueColor (BE)",
72     "24 bit TrueColor (LE: bgr)",
73     "32 bit TrueColor (LE: bgr-)",
74     "24 bit TrueColor (BE: rgb)",
75     "32 bit TrueColor (BE: -rgb)",
76     "16 bit TrueColor (lut)",
77     "32 bit TrueColor (lut)",
78     "16 bit YUV 4:2:2 (packed, YUYV)",
79     "16 bit YUV 4:2:2 (planar)",
80     "12 bit YUV 4:2:0 (planar)",
81     "MJPEG (AVI)",
82     "JPEG (JFIF)",
83     "16 bit YUV 4:2:2 (packed, UYVY)",
84 };
85 
86 /* --------------------------------------------------------------------- */
87 
88 const unsigned int   ng_afmt_to_channels[] = {
89     0,  1,  2,  1,  2,  1,  2, 0
90 };
91 const unsigned int   ng_afmt_to_bits[] = {
92     0,  8,  8, 16, 16, 16, 16, 0
93 };
94 const char* ng_afmt_to_desc[] = {
95     "none",
96     "8bit mono",
97     "8bit stereo",
98     "16bit mono (LE)",
99     "16bit stereo (LE)",
100     "16bit mono (BE)",
101     "16bit stereo (BE)",
102     "mp3 compressed audio",
103 };
104 
105 /* --------------------------------------------------------------------- */
106 
107 const char* ng_attr_to_desc[] = {
108     "none",
109     "norm",
110     "input",
111     "volume",
112     "mute",
113     "audio mode",
114     "color",
115     "bright",
116     "hue",
117     "contrast",
118 };
119 
120 /* --------------------------------------------------------------------- */
121 
ng_init_video_buf(struct ng_video_buf * buf)122 void ng_init_video_buf(struct ng_video_buf *buf)
123 {
124     memset(buf,0,sizeof(*buf));
125     pthread_mutex_init(&buf->lock,NULL);
126     pthread_cond_init(&buf->cond,NULL);
127 }
128 
ng_release_video_buf(struct ng_video_buf * buf)129 void ng_release_video_buf(struct ng_video_buf *buf)
130 {
131     int release;
132 
133     pthread_mutex_lock(&buf->lock);
134     buf->refcount--;
135     release = (buf->refcount == 0);
136     pthread_mutex_unlock(&buf->lock);
137     if (release && NULL != buf->release)
138 	buf->release(buf);
139 }
140 
ng_wakeup_video_buf(struct ng_video_buf * buf)141 void ng_wakeup_video_buf(struct ng_video_buf *buf)
142 {
143     pthread_cond_signal(&buf->cond);
144 }
145 
ng_waiton_video_buf(struct ng_video_buf * buf)146 void ng_waiton_video_buf(struct ng_video_buf *buf)
147 {
148     pthread_mutex_lock(&buf->lock);
149     while (buf->refcount)
150 	pthread_cond_wait(&buf->cond, &buf->lock);
151     pthread_mutex_unlock(&buf->lock);
152 }
153 
ng_free_video_buf(struct ng_video_buf * buf)154 static void ng_free_video_buf(struct ng_video_buf *buf)
155 {
156     free(buf->data);
157     free(buf);
158 }
159 
160 struct ng_video_buf*
ng_malloc_video_buf(struct ng_video_fmt * fmt,int size)161 ng_malloc_video_buf(struct ng_video_fmt *fmt, int size)
162 {
163     struct ng_video_buf *buf;
164 
165     buf = malloc(sizeof(*buf));
166     if (NULL == buf)
167 	return NULL;
168     ng_init_video_buf(buf);
169     buf->fmt  = *fmt;
170     buf->size = size;
171     buf->data = malloc(size);
172     if (NULL == buf->data) {
173 	free(buf);
174 	return NULL;
175     }
176     buf->refcount = 1;
177     buf->release  = ng_free_video_buf;
178     return buf;
179 }
180 
181 /* --------------------------------------------------------------------- */
182 
183 struct ng_audio_buf*
ng_malloc_audio_buf(struct ng_audio_fmt * fmt,int size)184 ng_malloc_audio_buf(struct ng_audio_fmt *fmt, int size)
185 {
186     struct ng_audio_buf *buf;
187 
188     buf = malloc(sizeof(*buf)+size);
189     memset(buf,0,sizeof(*buf));
190     buf->fmt  = *fmt;
191     buf->size = size;
192     buf->data = (char*)buf + sizeof(*buf);
193     return buf;
194 }
195 
196 /* --------------------------------------------------------------------- */
197 
198 struct ng_attribute*
ng_attr_byid(struct ng_attribute * attrs,int id)199 ng_attr_byid(struct ng_attribute *attrs, int id)
200 {
201     if (NULL == attrs)
202 	return NULL;
203     for (;;) {
204 	if (NULL == attrs->name)
205 	    return NULL;
206 	if (attrs->id == id)
207 	    return attrs;
208 	attrs++;
209     }
210 }
211 
212 struct ng_attribute*
ng_attr_byname(struct ng_attribute * attrs,char * name)213 ng_attr_byname(struct ng_attribute *attrs, char *name)
214 {
215     if (NULL == attrs)
216 	return NULL;
217     for (;;) {
218 	if (NULL == attrs->name)
219 	    return NULL;
220 	if (0 == strcasecmp(attrs->name,name))
221 	    return attrs;
222 	attrs++;
223     }
224 }
225 
226 const char*
ng_attr_getstr(struct ng_attribute * attr,int value)227 ng_attr_getstr(struct ng_attribute *attr, int value)
228 {
229     int i;
230 
231     if (NULL == attr)
232 	return NULL;
233     if (attr->type != ATTR_TYPE_CHOICE)
234 	return NULL;
235 
236     for (i = 0; attr->choices[i].str != NULL; i++)
237 	if (attr->choices[i].nr == value)
238 	    return attr->choices[i].str;
239     return NULL;
240 }
241 
242 int
ng_attr_getint(struct ng_attribute * attr,char * value)243 ng_attr_getint(struct ng_attribute *attr, char *value)
244 {
245     int i,val;
246 
247     if (NULL == attr)
248 	return -1;
249     if (attr->type != ATTR_TYPE_CHOICE)
250 	return -1;
251 
252     for (i = 0; attr->choices[i].str != NULL; i++) {
253 	if (0 == strcasecmp(attr->choices[i].str,value))
254 	    return attr->choices[i].nr;
255     }
256 
257     if (isdigit(value[0])) {
258 	/* Hmm.  String not found, but starts with a digit.
259 	   Check if this is a valid number ... */
260 	val = atoi(value);
261 	for (i = 0; attr->choices[i].str != NULL; i++)
262 	    if (val == attr->choices[i].nr)
263 		return attr->choices[i].nr;
264 
265     }
266     return -1;
267 }
268 
269 void
ng_attr_listchoices(struct ng_attribute * attr)270 ng_attr_listchoices(struct ng_attribute *attr)
271 {
272     int i;
273 
274     fprintf(stderr,"valid choices for \"%s\": ",attr->name);
275     for (i = 0; attr->choices[i].str != NULL; i++)
276 	fprintf(stderr,"%s\"%s\"",
277 		i ? ", " : "",
278 		attr->choices[i].str);
279     fprintf(stderr,"\n");
280 }
281 
282 int
ng_attr_int2percent(struct ng_attribute * attr,int value)283 ng_attr_int2percent(struct ng_attribute *attr, int value)
284 {
285     int range,percent;
286 
287     range   = attr->max - attr->min;
288     percent = (value - attr->min) * 100 / range;
289     if (percent < 0)
290 	percent = 0;
291     if (percent > 100)
292 	percent = 100;
293     return percent;
294 }
295 
296 int
ng_attr_percent2int(struct ng_attribute * attr,int percent)297 ng_attr_percent2int(struct ng_attribute *attr, int percent)
298 {
299     int range,value;
300 
301     range = attr->max - attr->min;
302     value = percent * range / 100 + attr->min;
303     if (value < attr->min)
304 	value = attr->min;
305     if (value > attr->max)
306 	value = attr->max;
307     return value;
308 }
309 
310 int
ng_attr_parse_int(struct ng_attribute * attr,char * str)311 ng_attr_parse_int(struct ng_attribute *attr, char *str)
312 {
313     int value,n;
314 
315     if (0 == sscanf(str,"%d%n",&value,&n))
316 	/* parse error */
317 	return attr->defval;
318     if (str[n] == '%')
319 	value = ng_attr_percent2int(attr,value);
320     if (value < attr->min)
321 	value = attr->min;
322     if (value > attr->max)
323 	value = attr->max;
324     return value;
325 }
326 
327 /* --------------------------------------------------------------------- */
328 
329 void
ng_ratio_fixup(int * width,int * height,int * xoff,int * yoff)330 ng_ratio_fixup(int *width, int *height, int *xoff, int *yoff)
331 {
332     int h = *height;
333     int w = *width;
334 
335     if (0 == ng_ratio_x || 0 == ng_ratio_y)
336 	return;
337     if (w * ng_ratio_y < h * ng_ratio_x) {
338 	*height = *width * ng_ratio_y / ng_ratio_x;
339 	if (yoff)
340 	    *yoff  += (h-*height)/2;
341     } else if (w * ng_ratio_y > h * ng_ratio_x) {
342 	*width  = *height * ng_ratio_x / ng_ratio_y;
343 	if (yoff)
344 	    *xoff  += (w-*width)/2;
345     }
346 }
347 
348 void
ng_ratio_fixup2(int * width,int * height,int * xoff,int * yoff,int ratio_x,int ratio_y,int up)349 ng_ratio_fixup2(int *width, int *height, int *xoff, int *yoff,
350 		int ratio_x, int ratio_y, int up)
351 {
352     int h = *height;
353     int w = *width;
354 
355     if (0 == ratio_x || 0 == ratio_y)
356 	return;
357     if ((!up  &&  w * ratio_y < h * ratio_x) ||
358 	(up   &&  w * ratio_y > h * ratio_x)) {
359 	*height = *width * ratio_y / ratio_x;
360 	if (yoff)
361 	    *yoff  += (h-*height)/2;
362     } else if ((!up  &&  w * ratio_y > h * ratio_x) ||
363 	       (up   &&  w * ratio_y < h * ratio_x)) {
364 	*width  = *height * ratio_x / ratio_y;
365 	if (yoff)
366 	    *xoff  += (w-*width)/2;
367     }
368 }
369 
370 /* --------------------------------------------------------------------- */
371 
372 LIST_HEAD(ng_conv);
373 LIST_HEAD(ng_aconv);
374 LIST_HEAD(ng_filters);
375 LIST_HEAD(ng_writers);
376 LIST_HEAD(ng_readers);
377 LIST_HEAD(ng_vid_drivers);
378 LIST_HEAD(ng_dsp_drivers);
379 LIST_HEAD(ng_mix_drivers);
380 
ng_check_magic(int magic,char * plugname,char * type)381 static int ng_check_magic(int magic, char *plugname, char *type)
382 {
383     if (magic != NG_PLUGIN_MAGIC) {
384 	fprintf(stderr, "ERROR: plugin magic mismatch [xawtv=%d,%s=%d]\n",
385 		NG_PLUGIN_MAGIC,plugname,magic);
386 	return -1;
387     }
388 #if 0
389     if (ng_debug)
390 	fprintf(stderr,"plugins: %s registered by %s\n",type,plugname);
391 #endif
392     return 0;
393 }
394 
395 int
ng_conv_register(int magic,char * plugname,struct ng_video_conv * list,int count)396 ng_conv_register(int magic, char *plugname,
397 		 struct ng_video_conv *list, int count)
398 {
399     int n;
400 
401     if (0 != ng_check_magic(magic,plugname,"video converters"))
402 	return -1;
403     for (n = 0; n < count; n++)
404 	list_add_tail(&(list[n].list),&ng_conv);
405     return 0;
406 }
407 
408 int
ng_aconv_register(int magic,char * plugname,struct ng_audio_conv * list,int count)409 ng_aconv_register(int magic, char *plugname,
410 		  struct ng_audio_conv *list, int count)
411 {
412     int n;
413 
414     if (0 != ng_check_magic(magic,plugname,"audio converters"))
415 	return -1;
416     for (n = 0; n < count; n++)
417 	list_add_tail(&(list[n].list),&ng_aconv);
418     return 0;
419 }
420 
421 int
ng_filter_register(int magic,char * plugname,struct ng_filter * filter)422 ng_filter_register(int magic, char *plugname, struct ng_filter *filter)
423 {
424     if (0 != ng_check_magic(magic,plugname,"filter"))
425 	return -1;
426     list_add_tail(&filter->list,&ng_filters);
427     return 0;
428 }
429 
430 int
ng_writer_register(int magic,char * plugname,struct ng_writer * writer)431 ng_writer_register(int magic, char *plugname, struct ng_writer *writer)
432 {
433     if (0 != ng_check_magic(magic,plugname,"writer"))
434 	return -1;
435     list_add_tail(&writer->list,&ng_writers);
436     return 0;
437 }
438 
439 int
ng_reader_register(int magic,char * plugname,struct ng_reader * reader)440 ng_reader_register(int magic, char *plugname, struct ng_reader *reader)
441 {
442     if (0 != ng_check_magic(magic,plugname,"reader"))
443 	return -1;
444     list_add_tail(&reader->list,&ng_readers);
445     return 0;
446 }
447 
448 int
ng_vid_driver_register(int magic,char * plugname,struct ng_vid_driver * driver)449 ng_vid_driver_register(int magic, char *plugname, struct ng_vid_driver *driver)
450 {
451     if (0 != ng_check_magic(magic,plugname,"video drv"))
452 	return -1;
453     list_add_tail(&driver->list,&ng_vid_drivers);
454     return 0;
455 }
456 
457 int
ng_dsp_driver_register(int magic,char * plugname,struct ng_dsp_driver * driver)458 ng_dsp_driver_register(int magic, char *plugname, struct ng_dsp_driver *driver)
459 {
460     if (0 != ng_check_magic(magic,plugname,"dsp drv"))
461 	return -1;
462     list_add_tail(&driver->list,&ng_dsp_drivers);
463     return 0;
464 }
465 
466 int
ng_mix_driver_register(int magic,char * plugname,struct ng_mix_driver * driver)467 ng_mix_driver_register(int magic, char *plugname, struct ng_mix_driver *driver)
468 {
469     if (0 != ng_check_magic(magic,plugname,"mixer drv"))
470 	return -1;
471     list_add_tail(&driver->list,&ng_mix_drivers);
472     return 0;
473 }
474 
475 struct ng_video_conv*
ng_conv_find_to(unsigned int out,int * i)476 ng_conv_find_to(unsigned int out, int *i)
477 {
478     struct list_head *item;
479     struct ng_video_conv *ret;
480     int j = 0;
481 
482     list_for_each(item,&ng_conv) {
483 	if (j < *i) {
484 	    j++;
485 	    continue;
486 	}
487 	ret = list_entry(item, struct ng_video_conv, list);
488 #if 0
489 	fprintf(stderr,"\tconv to:  %-28s =>  %s\n",
490 		ng_vfmt_to_desc[ret->fmtid_in],
491 		ng_vfmt_to_desc[ret->fmtid_out]);
492 #endif
493 	if (ret->fmtid_out == out) {
494 	    (*i)++;
495 	    return ret;
496 	}
497 	(*i)++;
498 	j++;
499     }
500     return NULL;
501 }
502 
503 struct ng_video_conv*
ng_conv_find_from(unsigned int in,int * i)504 ng_conv_find_from(unsigned int in, int *i)
505 {
506     struct list_head *item;
507     struct ng_video_conv *ret;
508 
509     int j = 0;
510 
511     list_for_each(item,&ng_conv) {
512 	if (j < *i) {
513 	    j++;
514 	    continue;
515 	}
516 	ret = list_entry(item, struct ng_video_conv, list);
517 #if 0
518 	fprintf(stderr,"\tconv from:  %-28s =>  %s\n",
519 		ng_vfmt_to_desc[ret->fmtid_in],
520 		ng_vfmt_to_desc[ret->fmtid_out]);
521 #endif
522 	if (ret->fmtid_in == in) {
523 	    (*i)++;
524 	    return ret;
525 	}
526     }
527     return NULL;
528 }
529 
530 struct ng_video_conv*
ng_conv_find_match(unsigned int in,unsigned int out)531 ng_conv_find_match(unsigned int in, unsigned int out)
532 {
533     struct list_head *item;
534     struct ng_video_conv *ret = NULL;
535 
536     list_for_each(item,&ng_conv) {
537 	ret = list_entry(item, struct ng_video_conv, list);
538 	if (ret->fmtid_in  == in && ret->fmtid_out == out)
539 	    return ret;
540     }
541     return NULL;
542 }
543 
544 /* --------------------------------------------------------------------- */
545 
546 #ifdef __linux__ /* Because this depends on get_media_devices.c */
ng_vid_open_auto(struct ng_vid_driver * drv,char * devpath,int allow_grabber)547 static void *ng_vid_open_auto(struct ng_vid_driver *drv, char *devpath,
548 			      int allow_grabber)
549 {
550     void *md, *handle = NULL;
551     const char *device = NULL;
552     const char *scan_type = "an analog TV";
553 
554     *devpath = 0;
555     md = discover_media_devices();
556     if (!md)
557 	goto error;
558 
559     /* Step 1: try TV cards first */
560     while (1) {
561 	device = get_associated_device(md, device, MEDIA_V4L_VIDEO, NULL, NONE);
562 	if (!device)
563 	    break; /* No more video devices to try */
564 	snprintf(devpath, PATH_MAX, "/dev/%s", device);
565 	if (ng_debug)
566 	    fprintf(stderr,"vid-open-auto: trying: %s... \n", devpath);
567 	handle = (drv->open)(devpath, CAN_CAPTURE | CAN_TUNE);
568 	if (handle) {
569 		fprintf(stderr,"vid-open-auto: using analog TV device %s\n", devpath);
570 		break;
571 	}
572     }
573 
574     /* Step 2: try grabber devices and webcams */
575     if (!handle) {
576 	if (!allow_grabber)
577 	    goto error;
578 	scan_type = "a capture";
579 	device = NULL;
580 	while (1) {
581 	    device = get_associated_device(md, device, MEDIA_V4L_VIDEO, NULL, NONE);
582 	    if (!device)
583 		break; /* No more video devices to try */
584 	    snprintf(devpath, PATH_MAX, "/dev/%s", device);
585 	    if (ng_debug)
586 		fprintf(stderr,"vid-open-auto: trying: %s... \n", devpath);
587 	    handle = (drv->open)(devpath, CAN_CAPTURE);
588 	    if (handle) {
589 		fprintf(stderr,"vid-open-auto: using grabber/webcam device %s\n", devpath);
590 		break;
591 	    }
592 	}
593     }
594     free_media_devices(md);
595 
596 error:
597     if (!handle) {
598 	    fprintf(stderr, "vid-open-auto: failed to open %s device",
599 		    scan_type);
600 	    if (*devpath)
601 		    fprintf(stderr, " at %s\n", devpath);
602 	    else
603 		    fprintf(stderr, "\n");
604 
605 	    return NULL;
606     }
607 
608     if (handle && ng_debug)
609 	fprintf(stderr,"vid-open-auto: success, using: %s\n", devpath);
610 
611     return handle;
612 }
613 #endif
614 
615 const struct ng_vid_driver*
ng_vid_open(char ** device,char * driver,struct ng_video_fmt * screen,void * base,void ** handle)616 ng_vid_open(char **device, char *driver, struct ng_video_fmt *screen,
617 	    void *base, void **handle)
618 {
619     struct list_head *item;
620     struct ng_vid_driver *drv;
621 
622     if (!driver) {
623 	fprintf (stderr, "Video4linux driver is not specified\n");
624 	return NULL;
625     }
626 
627     /* check all grabber drivers */
628     list_for_each(item,&ng_vid_drivers) {
629 	drv = list_entry(item, struct ng_vid_driver, list);
630 	if (strcasecmp(driver, drv->name) == 0)
631 	    break;
632     }
633 
634     if (item == &ng_vid_drivers) {
635 	if (strcasecmp(driver, "help") != 0)
636 	    fprintf (stderr, "Cannot find %s video driver\n", driver);
637 	fprintf (stderr, "Available drivers:");
638 	list_for_each(item,&ng_vid_drivers) {
639 	    drv = list_entry(item, struct ng_vid_driver, list);
640 	    fprintf (stderr, " %s", drv->name);
641 	}
642 	fprintf (stderr, "\n");
643 
644 	return NULL;
645     }
646 
647 #ifndef __linux__
648     if (!strcmp(*device, "auto") || !strcmp(*device, "auto_tv"))
649 	*device = "/dev/bktr0";
650 #else
651     if (!strcmp(*device, "auto") || !strcmp(*device, "auto_tv")) {
652 	char devpath[PATH_MAX];
653 	*handle = ng_vid_open_auto(drv, devpath,
654 				   !strcmp(*device, "auto_tv") ? 0 : 1);
655 	if (*handle == NULL) {
656 	    fprintf(stderr, "vid-open: could not find a suitable videodev\n");
657 	    return NULL;
658 	}
659 	*device = strdup(devpath);
660     } else
661 #endif
662     {
663 	if (ng_debug)
664 	    fprintf(stderr,"vid-open: trying: %s... \n", drv->name);
665 	if (!(*handle = (drv->open)(*device, 0))) {
666 	    fprintf(stderr,"vid-open: failed: %s\n", drv->name);
667 	    return NULL;
668 	}
669 	if (ng_debug)
670 	    fprintf(stderr,"vid-open: ok: %s\n", drv->name);
671     }
672 
673     if (NULL != screen && drv->capabilities(*handle) & CAN_OVERLAY) {
674 #ifdef __linux__
675 	int l = strlen(ng_v4l_conf);
676 
677 	snprintf(ng_v4l_conf + l, sizeof(ng_v4l_conf) - l, " -c %s", *device);
678 
679 	if (ng_debug)
680 	    fprintf(stderr,"vid-open: closing dev to run v4lconf\n");
681 	drv->close(*handle);
682 	switch (system(ng_v4l_conf)) {
683 	case -1: /* can't run */
684 	    fprintf(stderr,"could'nt start v4l-conf\n");
685 	    break;
686 	case 0: /* ok */
687 	    break;
688 	default: /* non-zero return */
689 	    fprintf(stderr,"v4l-conf had some trouble, "
690 		    "trying to continue anyway\n");
691 	    break;
692 	}
693 	if (ng_debug)
694 	    fprintf(stderr,"vid-open: re-opening dev after v4lconf\n");
695 	if (!(*handle = (drv->open)(*device, 0))) {
696 	    fprintf(stderr,"vid-open: failed: %s\n", drv->name);
697 	    return NULL;
698 	}
699 	if (ng_debug)
700 	    fprintf(stderr,"vid-open: re-open ok\n");
701 #endif
702 	drv->setupfb(*handle,screen,base);
703     }
704 
705     return drv;
706 }
707 
708 const struct ng_dsp_driver*
ng_dsp_open(char * device,struct ng_audio_fmt * fmt,int record,void ** handle)709 ng_dsp_open(char *device, struct ng_audio_fmt *fmt, int record, void **handle)
710 {
711     struct list_head *item;
712     struct ng_dsp_driver *drv;
713 
714     /* check all dsp drivers */
715     list_for_each(item,&ng_dsp_drivers) {
716 	drv = list_entry(item, struct ng_dsp_driver, list);
717 	if (NULL == drv->name)
718 	    continue;
719 	if (record && NULL == drv->read)
720 	    continue;
721 	if (!record && NULL == drv->write)
722 	    continue;
723 	if (ng_debug)
724 	    fprintf(stderr,"dsp-open: trying: %s... \n", drv->name);
725 	if (NULL != (*handle = (drv->open)(device,fmt,record)))
726 	    break;
727 	if (ng_debug)
728 	    fprintf(stderr,"dsp-open: failed: %s\n", drv->name);
729     }
730     if (item == &ng_dsp_drivers)
731 	return NULL;
732     if (ng_debug)
733 	fprintf(stderr,"dsp-open: ok: %s\n",drv->name);
734     return drv;
735 }
736 
737 struct ng_attribute*
ng_mix_init(char * device,char * channel)738 ng_mix_init(char *device, char *channel)
739 {
740     struct list_head *item;
741     struct ng_mix_driver *drv = NULL;
742     struct ng_attribute *attrs = NULL;
743     void *handle;
744 
745     /* check all mixer drivers */
746     list_for_each(item, &ng_mix_drivers) {
747 	drv = list_entry(item, struct ng_mix_driver, list);
748 	if (ng_debug)
749 	    fprintf(stderr,"mix-init: trying: %s... \n", drv->name);
750 	if (NULL != (handle = (drv->open)(device))) {
751 	    if (NULL != (attrs = drv->volctl(handle,channel)))
752 		break;
753 	    drv->close(handle);
754 	}
755 	if (ng_debug)
756 	    fprintf(stderr,"mix-init: failed: %s\n",drv->name);
757     }
758     if (ng_debug && NULL != attrs)
759 	fprintf(stderr,"mix-init: ok: %s\n",drv->name);
760     return attrs;
761 }
762 
ng_find_reader(char * filename)763 struct ng_reader* ng_find_reader(char *filename)
764 {
765     struct list_head *item;
766     struct ng_reader *reader;
767     char blk[512];
768     FILE *fp;
769     int m;
770 
771     if (NULL == (fp = fopen(filename, "r"))) {
772 	fprintf(stderr,"open %s: %s\n",filename,strerror(errno));
773 	return NULL;
774     }
775     memset(blk,0,sizeof(blk));
776     fread(blk,1,sizeof(blk),fp);
777     fclose(fp);
778 
779     list_for_each(item,&ng_readers) {
780 	reader = list_entry(item, struct ng_reader, list);
781 	for (m = 0; m < 4 && reader->mlen[m] > 0; m++) {
782 	    if (0 == memcmp(blk+reader->moff[m],reader->magic[m],
783 			    reader->mlen[m]))
784 		return reader;
785 	}
786     }
787     if (ng_debug)
788 	fprintf(stderr,"%s: no reader found\n",filename);
789     return NULL;
790 }
791 
792 int64_t
ng_tofday_to_timestamp(struct timeval * tv)793 ng_tofday_to_timestamp(struct timeval *tv)
794 {
795     long long ts;
796 
797     ts  = tv->tv_sec;
798     ts *= 1000000;
799     ts += tv->tv_usec;
800     ts *= 1000;
801     return ts;
802 }
803 
804 int64_t
ng_get_timestamp()805 ng_get_timestamp()
806 {
807     struct timeval tv;
808 
809     gettimeofday(&tv,NULL);
810     return ng_tofday_to_timestamp(&tv);
811 }
812 
813 struct ng_video_buf*
ng_filter_single(struct ng_filter * filter,struct ng_video_buf * in)814 ng_filter_single(struct ng_filter *filter, struct ng_video_buf *in)
815 {
816     struct ng_video_buf *out = in;
817     void *handle;
818 
819     if (NULL != filter  &&  filter->fmts & (1 << in->fmt.fmtid)) {
820 	handle = filter->init(&in->fmt);
821 	out = filter->frame(handle,in);
822 	filter->fini(handle);
823     }
824     return out;
825 }
826 
827 /* --------------------------------------------------------------------- */
828 
clip_dump(char * state,struct OVERLAY_CLIP * oc,int count)829 static void clip_dump(char *state, struct OVERLAY_CLIP *oc, int count)
830 {
831     int i;
832 
833     fprintf(stderr,"clip: %s - %d clips\n",state,count);
834     for (i = 0; i < count; i++)
835 	fprintf(stderr,"clip:   %d: %dx%d+%d+%d\n",i,
836 		oc[i].x2 - oc[i].x1,
837 		oc[i].y2 - oc[i].y1,
838 		oc[i].x1, oc[i].y1);
839 }
840 
clip_drop(struct OVERLAY_CLIP * oc,int n,int * count)841 static void clip_drop(struct OVERLAY_CLIP *oc, int n, int *count)
842 {
843     (*count)--;
844     memmove(oc+n, oc+n+1, sizeof(struct OVERLAY_CLIP) * (*count-n));
845 }
846 
ng_check_clipping(int width,int height,int xadjust,int yadjust,struct OVERLAY_CLIP * oc,int * count)847 void ng_check_clipping(int width, int height, int xadjust, int yadjust,
848 		       struct OVERLAY_CLIP *oc, int *count)
849 {
850     int i,j;
851 
852     if (ng_debug > 1) {
853 	fprintf(stderr,"clip: win=%dx%d xa=%d ya=%d\n",
854 		width,height,xadjust,yadjust);
855 	clip_dump("init",oc,*count);
856     }
857     for (i = 0; i < *count; i++) {
858 	/* fixup coordinates */
859 	oc[i].x1 += xadjust;
860 	oc[i].x2 += xadjust;
861 	oc[i].y1 += yadjust;
862 	oc[i].y2 += yadjust;
863     }
864     if (ng_debug > 1)
865 	clip_dump("fixup adjust",oc,*count);
866 
867     for (i = 0; i < *count; i++) {
868 	/* fixup borders */
869 	if (oc[i].x1 < 0)
870 	    oc[i].x1 = 0;
871 	if (oc[i].x2 < 0)
872 	    oc[i].x2 = 0;
873 	if (oc[i].x1 > width)
874 	    oc[i].x1 = width;
875 	if (oc[i].x2 > width)
876 	    oc[i].x2 = width;
877 	if (oc[i].y1 < 0)
878 	    oc[i].y1 = 0;
879 	if (oc[i].y2 < 0)
880 	    oc[i].y2 = 0;
881 	if (oc[i].y1 > height)
882 	    oc[i].y1 = height;
883 	if (oc[i].y2 > height)
884 	    oc[i].y2 = height;
885     }
886     if (ng_debug > 1)
887 	clip_dump("fixup range",oc,*count);
888 
889     /* drop zero-sized clips */
890     for (i = 0; i < *count;) {
891 	if (oc[i].x1 == oc[i].x2 || oc[i].y1 == oc[i].y2) {
892 	    clip_drop(oc,i,count);
893 	    continue;
894 	}
895 	i++;
896     }
897     if (ng_debug > 1)
898 	clip_dump("zerosize done",oc,*count);
899 
900     /* try to merge clips */
901  restart_merge:
902     for (j = *count - 1; j >= 0; j--) {
903 	for (i = 0; i < *count; i++) {
904 	    if (i == j)
905 		continue;
906 	    if (oc[i].x1 == oc[j].x1 &&
907 		oc[i].x2 == oc[j].x2 &&
908 		oc[i].y1 <= oc[j].y1 &&
909 		oc[i].y2 >= oc[j].y1) {
910 		if (ng_debug > 1)
911 		    fprintf(stderr,"clip: merge y %d,%d\n",i,j);
912 		if (oc[i].y2 < oc[j].y2)
913 		    oc[i].y2 = oc[j].y2;
914 		clip_drop(oc,j,count);
915 		if (ng_debug > 1)
916 		    clip_dump("merge y done",oc,*count);
917 		goto restart_merge;
918 	    }
919 	    if (oc[i].y1 == oc[j].y1 &&
920 		oc[i].y2 == oc[j].y2 &&
921 		oc[i].x1 <= oc[j].x1 &&
922 		oc[i].x2 >= oc[j].x1) {
923 		if (ng_debug > 1)
924 		    fprintf(stderr,"clip: merge x %d,%d\n",i,j);
925 		if (oc[i].x2 < oc[j].x2)
926 		    oc[i].x2 = oc[j].x2;
927 		clip_drop(oc,j,count);
928 		if (ng_debug > 1)
929 		    clip_dump("merge x done",oc,*count);
930 		goto restart_merge;
931 	    }
932 	}
933     }
934     if (ng_debug)
935 	clip_dump("final",oc,*count);
936 }
937 
938 /* --------------------------------------------------------------------- */
939 
ng_plugins(char * dirname)940 static int ng_plugins(char *dirname)
941 {
942     struct dirent **list;
943     char filename[1024];
944     void *plugin;
945     void (*initcall)(void);
946     int i,n = 0,l = 0;
947 
948     n = scandir(dirname,&list,NULL,alphasort);
949     if (n <= 0)
950 	return 0;
951     for (i = 0; i < n; i++) {
952 	if (0 != fnmatch("*.so",list[i]->d_name,0))
953 	    continue;
954 	sprintf(filename,"%s/%s",dirname,list[i]->d_name);
955 	if (NULL == (plugin = dlopen(filename,RTLD_NOW))) {
956 	    fprintf(stderr,"dlopen: %s\n",dlerror());
957 	    continue;
958 	}
959 	if (NULL == (initcall = dlsym(plugin,"ng_plugin_init"))) {
960 	    if (NULL == (initcall = dlsym(plugin,"_ng_plugin_init"))) {
961 		fprintf(stderr,"dlsym[%s]: %s\n",filename,dlerror());
962 		continue;
963 	    }
964 	}
965 	initcall();
966 	l--;
967     }
968     for (i = 0; i < n; i++)
969 	free(list[i]);
970     free(list);
971     return l;
972 }
973 
974 void
ng_init(void)975 ng_init(void)
976 {
977     static int once=0;
978     int count=0;
979 
980     if (once++) {
981 	fprintf(stderr,"panic: ng_init called twice\n");
982 	exit(1);
983     }
984     ng_device_init();
985     ng_color_packed_init();
986     ng_color_yuv2rgb_init();
987     ng_writefile_init();
988 
989     count += ng_plugins(LIBDIR);
990     if (0 == count) {
991 	/* nice for development */
992 	count += ng_plugins("../libng/plugins");
993 	count += ng_plugins("../libng/contrib-plugins");
994     }
995     if (0 == count)
996 	fprintf(stderr,"WARNING: no plugins found [%s]\n",LIBDIR);
997 }
998