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