1 /*****************************************************************
2  * gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 #include <sys/mman.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/ioctl.h>
26 #include <fcntl.h>
27 
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32 
33 #include <config.h>
34 #include <gmerlin/translation.h>
35 #include <gmerlin/plugin.h>
36 
37 #include <linux/videodev.h>
38 
39 #include <unistd.h>
40 #include <gmerlin/utils.h>
41 
42 #include <gmerlin/log.h>
43 #define LOG_DOMAIN "i_v4l"
44 
45 #include "pwc.h"
46 
47 /* Debugging stuff */
48 #if 0
49 
50 const struct
51   {
52   int id;
53   char const * const name;
54   }
55 palette_names[] =
56   {
57     { VIDEO_PALETTE_GREY,    "Linear greyscale" },
58     { VIDEO_PALETTE_HI240,   "High 240 cube (BT848)" },
59     { VIDEO_PALETTE_RGB565,  "565 16 bit RGB" },
60     { VIDEO_PALETTE_RGB24,   "24bit RGB" },
61     { VIDEO_PALETTE_RGB32,   "32bit RGB" },
62     { VIDEO_PALETTE_RGB555,  "555 15bit RGB" },
63     { VIDEO_PALETTE_YUV422,  "YUV422 capture" },
64     { VIDEO_PALETTE_YUYV,    "YUYV" },
65     { VIDEO_PALETTE_UYVY,    "UYVY" },
66     { VIDEO_PALETTE_YUV420,  "YUV 420" },
67     { VIDEO_PALETTE_YUV411,  "YUV 411" },
68     { VIDEO_PALETTE_RAW,     "RAW capture (BT848)" },
69     { VIDEO_PALETTE_YUV422P, "YUV 4:2:2 Planar" },
70     { VIDEO_PALETTE_YUV411P, "YUV 4:1:1 Planar" },
71     { VIDEO_PALETTE_YUV420P, "YUV 4:2:0 Planar" },
72     { VIDEO_PALETTE_YUV410P, "YUV 4:1:0 Planar" },
73   };
74 
75 static void dump_palette(int palette)
76   {
77   int i;
78   FILE * out = stderr;
79   for(i = 0; i < sizeof(palette_names)/sizeof(palette_names[0]); i++)
80     {
81     if(palette == palette_names[i].id)
82       {
83       fprintf(out, "%s", palette_names[i].name);
84       break;
85       }
86     }
87   }
88 
89 static void dump_video_picture(struct video_picture * p)
90   {
91   FILE * out = stderr;
92   fprintf(out , "Video picture:\n");
93   fprintf(out , "  Brightness: %d\n", p->brightness);
94   fprintf(out , "  Hue:        %d\n", p->hue);
95   fprintf(out , "  Colour:     %d\n", p->colour);
96   fprintf(out , "  Contrast:   %d\n", p->contrast);
97   fprintf(out , "  Whiteness:  %d\n", p->whiteness);
98   fprintf(out , "  Depth:      %d\n", p->depth);
99   fprintf(out , "  Palette:    ");
100   dump_palette(p->palette);
101   fprintf(out , "\n");
102   }
103 #endif
104 
105 /* Colorspace translation stuff */
106 
107 static const struct
108   {
109   int               v4l;
110   gavl_pixelformat_t gavl;
111   int depth;
112   }
113 pixelformats[] =
114   {
115     { VIDEO_PALETTE_GREY, GAVL_GRAY_8, 8 }, /* Linear greyscale */
116     // VIDEO_PALETTE_HI240     2       /* High 240 cube (BT848) */
117     { VIDEO_PALETTE_RGB565, GAVL_RGB_16, 16 },       /* 565 16 bit RGB */
118     { VIDEO_PALETTE_RGB24,  GAVL_BGR_24, 24 },       /* 24bit RGB */
119     { VIDEO_PALETTE_RGB32,  GAVL_RGB_32, 32 },       /* 32bit RGB */
120     { VIDEO_PALETTE_RGB555, GAVL_RGB_15, 15 },       /* 555 15bit RGB */
121     { VIDEO_PALETTE_YUV422, GAVL_YUY2,   24 },  /* YUV422 capture */
122     { VIDEO_PALETTE_YUYV,   GAVL_YUY2,   24 },
123     { VIDEO_PALETTE_UYVY,   GAVL_UYVY,   24 },  /* The great thing about standards is ... */
124     // VIDEO_PALETTE_YUV420    10
125     // VIDEO_PALETTE_YUV411    11      /* YUV411 capture */
126     // VIDEO_PALETTE_RAW       12      /* RAW capture (BT848) */
127     { VIDEO_PALETTE_YUV422P, GAVL_YUV_422_P, 24 },  /* YUV 4:2:2 Planar */
128     { VIDEO_PALETTE_YUV411P,  GAVL_YUV_411_P, 24 },  /* YUV 4:1:1 Planar */
129     //    { VIDEO_PALETTE_YUV420P, GAVL_YUV_420_P, 12 },  /* YUV 4:2:0 Planar */
130     { VIDEO_PALETTE_YUV420P, GAVL_YUV_420_P, 24 },  /* YUV 4:2:0 Planar */
131 
132     // VIDEO_PALETTE_YUV410P   16      /* YUV 4:1:0 Planar */
133 
134   };
135 
136 static const int
137 num_pixelformats = sizeof(pixelformats)/sizeof(pixelformats[0]);
138 
get_gavl_pixelformat(int csp)139 static gavl_pixelformat_t get_gavl_pixelformat(int csp)
140   {
141   int i;
142   for(i = 0; i < num_pixelformats; i++)
143     {
144     if(pixelformats[i].v4l == csp)
145       return pixelformats[i].gavl;
146     }
147   return GAVL_PIXELFORMAT_NONE;
148   }
149 
get_v4l_pixelformat(gavl_pixelformat_t csp)150 static int get_v4l_pixelformat(gavl_pixelformat_t csp)
151   {
152   int i;
153   for(i = 0; i < num_pixelformats; i++)
154     {
155     if(pixelformats[i].gavl == csp)
156       return pixelformats[i].v4l;
157     }
158   return -1;
159   }
160 
161 /* Input module */
162 
163 typedef struct
164   {
165   bg_parameter_info_t * parameters;
166 
167   int have_pwc;            /* True is we have a Phillips webcam */
168   int have_pwc_parameters; /* True is we have created parameters for
169                               a Phillips webcam */
170 
171   gavl_video_format_t format;
172 
173   void * pwc_priv;
174 
175   int fd;
176 
177   struct video_picture pic, cfg_pic;
178   struct video_window  win, cfg_win;
179 
180   int user_width;
181   int user_height;
182   int user_resolution;
183 
184   struct video_mbuf mbuf;
185 
186   struct video_mmap mmap_struct;
187 
188   int frame_index;
189   uint8_t * mmap_buf;
190 
191   gavl_video_frame_t * frame;
192   char * device;
193   } v4l_t;
194 
open_v4l(void * priv,gavl_audio_format_t * audio_format,gavl_video_format_t * format)195 static int open_v4l(void * priv,
196                     gavl_audio_format_t * audio_format,
197                     gavl_video_format_t * format)
198   {
199   v4l_t * v4l;
200   v4l = priv;
201 
202   /* Open device */
203 
204   if((v4l->fd = open(v4l->device, O_RDWR, 0)) < 0)
205     {
206     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot open device %s: %s",
207                  v4l->device, strerror(errno));
208     goto fail;
209     }
210   /* Check if we have a Phillips webcam */
211 
212   v4l->have_pwc = bg_pwc_probe(v4l->fd);
213 
214   if(v4l->have_pwc)
215     bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Phillips webcam detected");
216 
217   /* Set Picture */
218 
219   if(ioctl(v4l->fd, VIDIOCGPICT, &v4l->pic))
220     {
221     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "VIDIOCGPICT failed: %s",
222            strerror(errno));
223     goto fail;
224     }
225   format->pixelformat = get_gavl_pixelformat(v4l->pic.palette);
226   /* If we have a nonsupported pixelformat we try YUV420 */
227 
228   if(format->pixelformat == GAVL_PIXELFORMAT_NONE)
229     v4l->pic.palette = get_v4l_pixelformat(GAVL_YUV_420_P);
230 
231   /* Transfer values */
232 
233 #if 1
234   v4l->pic.brightness = v4l->cfg_pic.brightness;
235   v4l->pic.hue        = v4l->cfg_pic.hue;
236   v4l->pic.colour     = v4l->cfg_pic.colour;
237   v4l->pic.contrast   = v4l->cfg_pic.contrast;
238   v4l->pic.whiteness  = v4l->cfg_pic.whiteness;      /* Black and white only */
239 #endif
240 
241   //  dump_video_picture(&v4l->pic);
242 
243   if(ioctl(v4l->fd, VIDIOCSPICT, &v4l->pic))
244     {
245     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "VIDIOCSPICT failed: %s",
246                                     strerror(errno));
247     goto fail;
248     }
249   format->pixelformat = get_gavl_pixelformat(v4l->pic.palette);
250 
251   /* Set window */
252 
253   if(ioctl(v4l->fd, VIDIOCGWIN, &v4l->win))
254     {
255     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "VIDIOCGWIN failed: %s",
256            strerror(errno));
257     goto fail;
258     }
259   v4l->win.x = 0;
260   v4l->win.y = 0;
261 
262   v4l->win.width  = v4l->cfg_win.width;
263   v4l->win.height = v4l->cfg_win.height;
264   v4l->win.flags = 0;
265 
266   if(ioctl(v4l->fd, VIDIOCSWIN, &v4l->win))
267     {
268     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "VIDIOCSWIN failed: %s (invalaid picture dimensions?)",
269            strerror(errno));
270     goto fail;
271     }
272   /* Setup format */
273 
274   format->image_width  = v4l->win.width;
275   format->image_height = v4l->win.height;
276 
277   format->frame_width  = v4l->win.width;
278   format->frame_height = v4l->win.height;
279 
280   format->pixel_width  = 1;
281   format->pixel_height = 1;
282 
283   format->framerate_mode = GAVL_FRAMERATE_VARIABLE;
284   format->frame_duration = 0;
285   format->timescale = 1;
286 
287   gavl_video_format_copy(&v4l->format, format);
288 
289 
290   /* Setup frame */
291 
292   gavl_video_frame_set_strides(v4l->frame, format);
293 
294   /* Setup mmap */
295 
296   if(ioctl(v4l->fd, VIDIOCGMBUF, &v4l->mbuf))
297     {
298     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "VIDIOCGMBUF failed: %s",
299            strerror(errno));
300     goto fail;
301     }
302   v4l->mmap_buf = (uint8_t*)(mmap(0, v4l->mbuf.size, PROT_READ|PROT_WRITE,
303                                   MAP_SHARED,v4l->fd,0));
304   if((unsigned char*)-1 == v4l->mmap_buf)
305     {
306     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "mmap failed: %s", strerror(errno));
307     goto fail;
308     }
309   v4l->mmap_struct.width = format->image_width;
310   v4l->mmap_struct.height = format->image_height;
311   v4l->mmap_struct.format = v4l->pic.palette;
312 
313   v4l->frame_index = 0;
314 
315   gavl_video_format_copy(&v4l->format, format);
316 
317   return 1;
318   fail:
319   if(v4l->fd >= 0)
320     {
321     close(v4l->fd);
322     v4l->fd = -1;
323     }
324   return 0;
325 
326   }
327 
close_v4l(void * priv)328 static void close_v4l(void * priv)
329   {
330   v4l_t * v4l;
331   v4l = priv;
332 
333   if(v4l->fd >= 0)
334     {
335     close(v4l->fd);
336     if(v4l->mmap_buf)
337       munmap(v4l->mmap_buf, v4l->mbuf.size);
338     }
339   v4l->fd = -1;
340   }
341 
read_frame_v4l(void * priv,gavl_video_frame_t * frame,int stream)342 static int read_frame_v4l(void * priv, gavl_video_frame_t * frame, int stream)
343   {
344   v4l_t * v4l;
345   v4l = priv;
346 
347   v4l->mmap_struct.frame = v4l->frame_index;
348 
349   if(ioctl(v4l->fd, VIDIOCMCAPTURE, &v4l->mmap_struct))
350     return 0;
351 
352   if(ioctl(v4l->fd,VIDIOCSYNC, &v4l->frame_index))
353     return 0;
354 
355   gavl_video_frame_set_planes(v4l->frame,
356                               &v4l->format,
357                               &v4l->mmap_buf[v4l->mbuf.offsets[v4l->frame_index]]);
358 
359   gavl_video_frame_copy(&v4l->format, frame, v4l->frame);
360   //  if(ioctl(v4l->fd,VIDIOCSYNC, &v4l->mmap[v4l->frame_index]))
361 
362   v4l->frame_index++;
363   if(v4l->frame_index >= v4l->mbuf.frames)
364     v4l->frame_index = 0;
365 
366   return 1;
367   }
368 
create_v4l()369 static void * create_v4l()
370   {
371   v4l_t * v4l;
372 
373   v4l = calloc(1, sizeof(*v4l));
374 
375   v4l->frame = gavl_video_frame_create(NULL);
376   v4l->fd = -1;
377   return v4l;
378   }
379 
destroy_v4l(void * priv)380 static void  destroy_v4l(void * priv)
381   {
382   v4l_t * v4l;
383   v4l = priv;
384   gavl_video_frame_null(v4l->frame);
385   gavl_video_frame_destroy(v4l->frame);
386   close_v4l(priv);
387 
388   if(v4l->parameters)
389     bg_parameter_info_destroy_array(v4l->parameters);
390 
391   if(v4l->pwc_priv)
392     {
393     bg_pwc_destroy(v4l->pwc_priv);
394     v4l->pwc_priv = NULL;
395     }
396     free(v4l);
397 
398   }
399 
400 /* Configuration stuff */
401 
402 static const bg_parameter_info_t parameters[] =
403   {
404     {
405       .name =        "device_section",
406       .long_name =   TRS("Device"),
407       .type =        BG_PARAMETER_SECTION
408     },
409     {
410       .name =        "device",
411       .long_name =   TRS("V4L Device"),
412       .type =        BG_PARAMETER_DEVICE,
413       .val_default = { .val_str = "/dev/video0" },
414     },
415     {
416       .name =        "res",
417       .long_name =   TRS("Resolution"),
418       .type =        BG_PARAMETER_SECTION,
419     },
420     {
421       .name =      "resolution",
422       .long_name = TRS("Resolution"),
423       .type =      BG_PARAMETER_STRINGLIST,
424       .val_default = { .val_str = "QVGA (320x240)" },
425       .multi_names =     (char const *[]){ "QSIF (160x112)",
426                               "QCIF (176x144)",
427                               "QVGA (320x240)",
428                               "SIF(352x240)",
429                               "CIF (352x288)",
430                               "VGA (640x480)",
431                               "User defined",
432                               NULL },
433       .multi_labels =     (char const *[]){ TRS("QSIF (160x112)"),
434                                    TRS("QCIF (176x144)"),
435                                    TRS("QVGA (320x240)"),
436                                    TRS("SIF(352x240)"),
437                                    TRS("CIF (352x288)"),
438                                    TRS("VGA (640x480)"),
439                                    TRS("User defined"),
440                                    NULL },
441     },
442     {
443       .name =        "user_width",
444       .long_name =   TRS("User defined width"),
445       .type =        BG_PARAMETER_INT,
446       .val_default = { .val_i = 720 },
447       .val_min =     { .val_i = 160 },
448       .val_max =     { .val_i = 1024 },
449     },
450     {
451       .name =        "user_height",
452       .long_name =   TRS("User defined height"),
453       .type =        BG_PARAMETER_INT,
454       .val_default = { .val_i = 576 },
455       .val_min =     { .val_i = 112 },
456       .val_max =     { .val_i = 768 },
457     },
458     {
459       .name =        "settings",
460       .long_name =   TRS("Settings"),
461       .type =        BG_PARAMETER_SECTION,
462     },
463     {
464       .name =        "brightness",
465       .long_name =   TRS("Brightness"),
466       .type =        BG_PARAMETER_SLIDER_INT,
467       .flags =       BG_PARAMETER_SYNC,
468       .val_min =     { .val_i = 0 },
469       .val_max =     { .val_i = 65535 },
470       .val_default = { .val_i = 40000 },
471     },
472     {
473       .name =        "hue",
474       .long_name =   TRS("Hue"),
475       .type =        BG_PARAMETER_SLIDER_INT,
476       .flags =       BG_PARAMETER_SYNC,
477       .val_min =     { .val_i = 0 },
478       .val_max =     { .val_i = 65535 },
479       .val_default = { .val_i = 65535 },
480     },
481     {
482       .name =        "colour",
483       .long_name =   TRS("Colour"),
484       .type =        BG_PARAMETER_SLIDER_INT,
485       .flags =       BG_PARAMETER_SYNC,
486       .val_min =     { .val_i = 0 },
487       .val_max =     { .val_i = 65535 },
488       .val_default = { .val_i = 30000 },
489     },
490     {
491       .name =        "contrast",
492       .long_name =   TRS("Contrast"),
493       .type =        BG_PARAMETER_SLIDER_INT,
494       .flags =       BG_PARAMETER_SYNC,
495       .val_min =     { .val_i = 0 },
496       .val_max =     { .val_i = 65535 },
497       .val_default = { .val_i = 30000 },
498     },
499     {
500       .name =        "whiteness",
501       .long_name =   TRS("Whiteness"),
502       .type =        BG_PARAMETER_SLIDER_INT,
503       .flags =       BG_PARAMETER_SYNC,
504       .val_min =     { .val_i = 0 },
505       .val_max =     { .val_i = 65535 },
506       .val_default = { .val_i = 30000 },
507     },
508     { /* End of parameters */ }
509   };
510 
create_parameters(v4l_t * v4l)511 static void create_parameters(v4l_t * v4l)
512   {
513   if(v4l->parameters)
514     bg_parameter_info_destroy_array(v4l->parameters);
515   v4l->parameters = bg_parameter_info_copy_array(parameters);
516 
517 #if 0
518   if((v4l->fd < 0) &&
519      ((v4l->fd = open(v4l->device, O_RDWR, 0)) >= 0))
520     {
521     v4l->have_pwc = bg_pwc_probe(v4l->fd);
522     close(v4l->fd);
523     v4l->fd = -1;
524     }
525 #endif
526 
527   if(v4l->have_pwc)
528     {
529     v4l->pwc_priv = bg_pwc_get_parameters(v4l->fd, &v4l->parameters);
530     v4l->have_pwc_parameters = 1;
531     }
532   }
533 
get_parameters_v4l(void * priv)534 static const bg_parameter_info_t * get_parameters_v4l(void * priv)
535   {
536   v4l_t * v4l;
537   v4l = priv;
538 
539   if(!v4l->parameters ||
540      (v4l->have_pwc != v4l->have_pwc_parameters))
541     {
542     create_parameters(v4l);
543     }
544 
545   return v4l->parameters;
546   }
547 
set_parameter_v4l(void * priv,const char * name,const bg_parameter_value_t * val)548 static void set_parameter_v4l(void * priv, const char * name,
549                               const bg_parameter_value_t * val)
550   {
551   char * pos;
552   v4l_t * v4l;
553   v4l = priv;
554 
555   if(!name)
556     {
557     if(v4l->user_resolution)
558       {
559       v4l->cfg_win.width  = v4l->user_width;
560       v4l->cfg_win.height = v4l->user_height;
561       }
562     bg_pwc_set_parameter(v4l->fd, v4l->pwc_priv, NULL, val);
563     return;
564     }
565 
566   if(!strncmp(name, "pwc_", 4))
567     {
568     bg_pwc_set_parameter(v4l->fd, v4l->pwc_priv, name, val);
569     }
570   else if(!strcmp(name, "resolution"))
571     {
572     if(!strcmp(val->val_str, "QSIF (160x112)"))
573       {
574       v4l->cfg_win.width  = 160;
575       v4l->cfg_win.height = 112;
576       v4l->user_resolution = 0;
577       }
578     else if(!strcmp(val->val_str, "QCIF (176x144)"))
579       {
580       v4l->cfg_win.width  = 176;
581       v4l->cfg_win.height = 144;
582       v4l->user_resolution = 0;
583       }
584     else if(!strcmp(val->val_str, "QVGA (320x240)"))
585       {
586       v4l->cfg_win.width  = 320;
587       v4l->cfg_win.height = 240;
588       v4l->user_resolution = 0;
589       }
590     else if(!strcmp(val->val_str, "SIF(352x240)"))
591       {
592       v4l->cfg_win.width  = 352;
593       v4l->cfg_win.height = 240;
594       v4l->user_resolution = 0;
595       }
596     else if(!strcmp(val->val_str, "CIF (352x288)"))
597       {
598       v4l->cfg_win.width  = 352;
599       v4l->cfg_win.height = 288;
600       v4l->user_resolution = 0;
601       }
602     else if(!strcmp(val->val_str, "VGA (640x480)"))
603       {
604       v4l->cfg_win.width  = 640;
605       v4l->cfg_win.height = 480;
606       v4l->user_resolution = 0;
607       }
608     else if(!strcmp(val->val_str, "User defined"))
609       {
610       v4l->user_resolution = 1;
611       }
612     }
613   else if(!strcmp(name, "device"))
614     {
615     v4l->device = bg_strdup(v4l->device, val->val_str);
616     pos = strchr(v4l->device, ' '); if(pos) *pos = '\0';
617     }
618   else if(!strcmp(name, "user_width"))
619     {
620     v4l->user_width = val->val_i;
621     }
622   else if(!strcmp(name, "user_height"))
623     {
624     v4l->user_height = val->val_i;
625     }
626   else if(!strcmp(name, "brightness"))
627     {
628     v4l->cfg_pic.brightness = val->val_i;
629 
630     if(v4l->fd > 0)
631       {
632       if(ioctl(v4l->fd, VIDIOCGPICT, &v4l->pic))
633         return;
634       v4l->pic.brightness = val->val_i;
635       if(ioctl(v4l->fd, VIDIOCSPICT, &v4l->pic))
636         return;
637       }
638     }
639   else if(!strcmp(name, "hue"))
640     {
641     v4l->cfg_pic.hue = val->val_i;
642     if(v4l->fd > 0)
643       {
644       if(ioctl(v4l->fd, VIDIOCGPICT, &v4l->pic))
645         return;
646       v4l->pic.hue = val->val_i;
647       if(ioctl(v4l->fd, VIDIOCSPICT, &v4l->pic))
648         return;
649       }
650     }
651   else if(!strcmp(name, "colour"))
652     {
653     v4l->cfg_pic.colour = val->val_i;
654     if(v4l->fd > 0)
655       {
656       if(ioctl(v4l->fd, VIDIOCGPICT, &v4l->pic))
657         return;
658       v4l->pic.colour = val->val_i;
659       if(ioctl(v4l->fd, VIDIOCSPICT, &v4l->pic))
660         return;
661       }
662     }
663   else if(!strcmp(name, "contrast"))
664     {
665     v4l->cfg_pic.contrast = val->val_i;
666     if(v4l->fd > 0)
667       {
668       if(ioctl(v4l->fd, VIDIOCGPICT, &v4l->pic))
669         return;
670       v4l->pic.contrast = val->val_i;
671       if(ioctl(v4l->fd, VIDIOCSPICT, &v4l->pic))
672         return;
673       }
674 
675     }
676   else if(!strcmp(name, "whiteness"))
677     {
678     v4l->cfg_pic.whiteness = val->val_i;
679     if(v4l->fd > 0)
680       {
681       if(ioctl(v4l->fd, VIDIOCGPICT, &v4l->pic))
682         return;
683       v4l->pic.whiteness = val->val_i;
684       if(ioctl(v4l->fd, VIDIOCSPICT, &v4l->pic))
685         return;
686       }
687     }
688   }
689 
690 
691 const bg_recorder_plugin_t the_plugin =
692   {
693     .common =
694     {
695       BG_LOCALE,
696       .name =          "i_v4l",
697       .long_name =     TRS("V4L"),
698       .description =   TRS("video4linux recording plugin. Supports only video and no tuner decives."),
699       .type =          BG_PLUGIN_RECORDER_VIDEO,
700       .flags =         BG_PLUGIN_RECORDER,
701       .priority =      BG_PLUGIN_PRIORITY_MAX,
702       .create =        create_v4l,
703       .destroy =       destroy_v4l,
704 
705       .get_parameters = get_parameters_v4l,
706       .set_parameter =  set_parameter_v4l,
707     },
708 
709     .open =       open_v4l,
710     .close =      close_v4l,
711     .read_video = read_frame_v4l,
712 
713   };
714 
715 /* Include this into all plugin modules exactly once
716    to let the plugin loader obtain the API version */
717 BG_GET_PLUGIN_API_VERSION;
718