/* Video4Linux operation for GEGL * * GEGL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * GEGL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GEGL; if not, see . * * Copyright 2004-2008 Øyvind Kolås * originally written for gggl */ #include "config.h" #include #ifdef GEGL_CHANT_PROPERTIES gegl_chant_file_path (path, _("Path"), "/dev/video0", _("Path to v4l device")) gegl_chant_int (width, _("Width"), 0, G_MAXINT, 320, _("Width for rendered image")) gegl_chant_int (height, _("Height"), 0, G_MAXINT, 240, _("Height for rendered image")) gegl_chant_int (frame, _("Frame"), 0, G_MAXINT, 0, _("current frame number, can be changed to trigger a reload of the image.")) gegl_chant_int (fps, _("FPS"), 0, G_MAXINT, 0, _("autotrigger reload this many times a second.")) #else #define GEGL_CHANT_TYPE_SOURCE #define GEGL_CHANT_C_FILE "v4l.c" #include "gegl-chant.h" #include "v4lutils/v4lutils.h" #include "v4lutils/v4lutils.c" typedef struct { gint active; gint w; gint h; gint w_stored; gint h_stored; gint frame; gint decode; v4ldevice *vd; } Priv; static void init (GeglChantO *o) { Priv *p = (Priv*)o->chant_data; if (p==NULL) { p = g_new0 (Priv, 1); o->chant_data = (void*) p; } p->w = 320; p->h = 240; } static GeglRectangle get_bounding_box (GeglOperation *operation) { GeglRectangle result ={0,0,320,200}; GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); result.width = o->width; result.height = o->height; return result; } static void prepare (GeglOperation *operation) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); Priv *p= (Priv*)o->chant_data; if (p == NULL) init (o); p = (Priv*)o->chant_data; gegl_operation_set_format (operation, "output", babl_format_new ( babl_model ("R'G'B'"), babl_type ("u8"), babl_component ("B'"), babl_component ("G'"), babl_component ("R'"), NULL)); p->w = o->width; p->h = o->height; if (!p->vd) { p->vd = g_malloc0 (sizeof (v4ldevice)); if (v4lopen (o->path, p->vd)) return; p->active = 1; if (v4lmmap (p->vd)) return; v4lsetdefaultnorm (p->vd, VIDEO_MODE_PAL); v4lgetcapability (p->vd); if (!(p->vd->capability.type & VID_TYPE_CAPTURE)) { g_warning ( "video_init: This device seems not to support video capturing.\n"); return; } } if (p->w != p->w_stored || p->h != p->h_stored) { if (p->w > p->vd->capability.maxwidth || p->h > p->vd->capability.maxheight) { p->w = p->vd->capability.maxwidth; p->h = p->vd->capability.maxheight; o->width = p->w; o->height = p->h; g_warning ( "capturing size is set to %dx%d.\n", p->w, p->h); } else if (p->w < p->vd->capability.minwidth || p->h < p->vd->capability.minheight) { p->w = p->vd->capability.minwidth; p->h = p->vd->capability.minheight; o->width = p->w; o->height = p->h; g_warning ( "capturing size is set to %dx%d.\n", p->w, p->h); } p->w_stored = p->w; p->h_stored = p->h; /* FIXME: try other palettes as well, do some profiling on the spca * based cameras to see what is the ideal format wrt performance */ if (!v4lsetpalette (p->vd, VIDEO_PALETTE_RGB24)) { p->decode=0; } else if (!v4lsetpalette (p->vd, VIDEO_PALETTE_YUV420P)) { p->decode=1; } else { g_warning ( "oops,. no usable v4l format found\n"); return; } v4lgrabinit (p->vd, p->w, p->h); v4lgrabf (p->vd); } } static void finalize (GObject *object) { GeglChantO *o = GEGL_CHANT_PROPERTIES (object); if (o->chant_data) { Priv *p = (Priv*)o->chant_data; if (p->vd) { v4lmunmap (p->vd); v4lclose (p->vd); g_free (p->vd); } g_free (o->chant_data); o->chant_data = NULL; } G_OBJECT_CLASS (gegl_chant_parent_class)->finalize (object); } static gboolean update (gpointer operation) { GeglRectangle bounds = gegl_operation_get_bounding_box (operation); gegl_operation_invalidate (operation, &bounds, FALSE); return TRUE; } static gboolean process (GeglOperation *operation, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); Priv *p = (Priv*)o->chant_data; guchar *capbuf; static gboolean inited = FALSE; if (!inited && o->fps != 0) { inited= TRUE; g_timeout_add (1000/o->fps, update, operation); } if (!p->active) return FALSE; v4lgrabf (p->vd); capbuf = v4lgetaddress (p->vd); v4lsyncf (p->vd); if (!capbuf) { g_warning ("no capbuf found"); return FALSE; } if (p->decode) { guchar foobuf[o->width*o->height*3]; /* XXX: foobuf is unneeded the conversions resets for every * scanline and could thus have been done in a line by line * manner an fed into the output buffer */ gint y; for (y = 0; y < p->h; y++) { gint x; guchar *dst = &foobuf[y*p->w*3]; guchar *ysrc = (guchar *) (capbuf + (y) * (p->w) * 1); guchar *usrc = (guchar *) (capbuf + (p->w) * (p->h) + (y) / 2 * (p->w) / 2); guchar *vsrc = (guchar *) (capbuf + (p->w) * (p->h) + ((p->w) * (p->h)) / 4 + (y) / 2 * (p->w) / 2); for (x = 0; x < p->w; x++) { gint R, G, B; #ifndef byteclamp #define byteclamp(j) do{if(j<0)j=0; else if(j>255)j=255;}while(0) #endif #define YUV82RGB8(Y,U,V,R,G,B)do{\ R= ((Y<<15) + 37355*(V-128))>>15;\ G= ((Y<<15) -12911* (U-128) - 19038*(V-128))>>15;\ B= ((Y<<15) +66454* (U-128) )>>15;\ byteclamp(R);\ byteclamp(G);\ byteclamp(B);\ }while(0) /* the format support for this code is not very good, but it * works for the devices I have tested with, conversion even * for chroma subsampled images is something we should let * babl handle. */ YUV82RGB8 (*ysrc, *usrc, *vsrc, R, G, B); dst[0] = B; dst[1] = G; dst[2] = R; dst += 3; ysrc++; if (x % 2) { usrc++; vsrc++; } } } gegl_buffer_set (output, NULL, 0, NULL, foobuf, GEGL_AUTO_ROWSTRIDE); } else { gegl_buffer_set (output, NULL, 0, NULL, capbuf, GEGL_AUTO_ROWSTRIDE); } return TRUE; } static GeglRectangle get_cached_region (GeglOperation *operation, const GeglRectangle *roi) { return get_bounding_box (operation); } static void gegl_chant_class_init (GeglChantClass *klass) { GeglOperationClass *operation_class; GeglOperationSourceClass *source_class; G_OBJECT_CLASS (klass)->finalize = finalize; operation_class = GEGL_OPERATION_CLASS (klass); source_class = GEGL_OPERATION_SOURCE_CLASS (klass); source_class->process = process; operation_class->get_bounding_box = get_bounding_box; operation_class->get_cached_region = get_cached_region; operation_class->prepare = prepare; gegl_operation_class_set_keys (operation_class, "name" , "gegl:v4l", "categories" , "input:video", "description" , _("Video4Linux input, webcams framegrabbers and similar devices."), NULL); } #endif