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