/* V4L module for the GyachE-Broadcaster program:
   This application is used to send webcam streams to
    Yahoo. Right now this program has only been tested
    with a single Video4Linux device: An OV511 DLink
    C100 USB webcam.  The program uses Video4Linux-1
    for image capture and the libJasper library for Jpeg-2000
    image conversions.  */

/* This program borrows alot of code from both Ayttm and 
    Gyach-E itself, as well as the old 'videodog' program
    for a few decent V4L usage examples.

    It is designed for simplicity, speed, 
    memory-friendliness, and stability: It runs as an EXTERNAL 
    program to Gyach Enhanced, so that if it DOES crash, it 
    crashes ALONE, rather than taking down an entire chat program
    with it. It is a clean, efficient SINGLE-THREADED application 
*/

/*****************************************************************************
 * gyachewebcam.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 * Copyright (C) 2003-2005 Erica Andrews (Phrozensmoke ['at'] yahoo.com)
 * Released under the terms of the GPL.
 * *NO WARRANTY*
 *****************************************************************************/
/*****************************************************************************
 * 20060118 Video4Linux Version 2 support 
 * 20060208 Video4Linux Version 1 improvements
 * 
 * Stefan Sikora
 * <hoshy['at']schrauberstube['dot']de>
 ****************************************************************************/


#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "gyacheupload.h"
#include "v4l-fmtconv.h"
#include <glib.h>

#if defined (USE_PTHREAD_CREATE)
#include <pthread.h>
#endif

int exit_on_error=0;
int cam_is_open=0;
int x=320, y=240, w=3;
unsigned char *v_device; /* device */

// Supported palette
#define PAL_JPEG    (0)
#define PAL_RGB24   (1)
#define PAL_RGB32   (2)
#define PAL_YUV420P (3)
#define PAL_YUV422P (4)
#define PAL_RGB565  (5)
#define PAL_RGB555  (6)
#define PAL_GREY    (7)
#define PAL_DEFAULT PAL_RGB24

#ifdef V4L2
	struct v4l2_capability grab_cap;
	struct v4l2_format grab_fmt;

	struct buffer {
        void *start;
        size_t length;
	};

	struct buffer *buffers = NULL;
	static unsigned int n_buffers = 0;
#else // V4L2

typedef struct {
	char    *name;
	uint16_t palette;
	uint16_t depth;
} webcam_palette_t;


webcam_palette_t wpalette[] = {
	{ "JPEG",    VIDEO_PALETTE_RGB24,   24 },
	{ "RGB24",   VIDEO_PALETTE_RGB24,   24 },
	{ "RGB32",   VIDEO_PALETTE_RGB32,   32 },
	{ "YUV420P", VIDEO_PALETTE_YUV420P, 12 },
	{ "YUV422P", VIDEO_PALETTE_YUV422P, 12 },
	{ "RGB565",  VIDEO_PALETTE_RGB565,  16 },
	{ "RGB555",  VIDEO_PALETTE_RGB555,  16 },
	{ "GREY",    VIDEO_PALETTE_GREY,    8 },
	{ "GRAY",    VIDEO_PALETTE_GREY,    8 },
	{ NULL, 0, 0 }
};

 struct video_picture grab_pic;
 struct video_capability grab_cap;
 struct video_mmap grab_buf;
 struct video_mbuf grab_mbuf;
#endif // V4L2

 int grab_fd, grab_size;
 int grab_palette = PAL_RGB24;
 unsigned char *grab_data;
 char *pnm_buf=NULL;
 int pnm_size;
 unsigned char *rgb_buf;

static char webcamrc_path[256];
extern int app_debugger;

int hue=47104, contrast=65280, brightness=65280, colour=17152, whiteness=32768;
int fix_color=0;

#define CLEAR(x) memset (&(x), 0, sizeof (x))

#define dir_exists(x) (access((x), R_OK) == 0)

unsigned char* grab_one(int *, int *);
int grab_init();
void set_picture();


void fix_colour(char *image, int x, int y)
{
   int i;
   char tmp;
   i = x * y;
   while(--i) {
      tmp = image[0];
      image[0] = image[2];
      image[2] = tmp;
      image += 3;
   }
}

void init_pnm_buf()
{
	char hdr_buf[50];
	int hdr_size;

	if (pnm_buf) {
		return;
	}

	/* The PNM header never changes, so create it only once. */
	hdr_size=sprintf(hdr_buf, "P6\n%d %d\n255\n", x, y);

	/* Allocate space for the header + the image. */
	pnm_size=grab_size+hdr_size;
	pnm_buf=malloc(pnm_size);
	if (!pnm_buf) {
		show_error_dialog("Not enough memory available.");
		return;
	}

	/* Copy the header into the buffer, and set rgb_buf to point
	 * just past the header.
	 */
	memcpy(pnm_buf, hdr_buf, hdr_size);
	rgb_buf = pnm_buf + hdr_size;
}

#if defined(G_THREADS_ENABLED) || defined(USE_PTHREAD_CREATE)
int uploadstatus = 1;

/* create a PNM for Jasper, convert to jpeg-2000 and send */
void *uploadthread(void *arg) {
    unsigned char *my_jasper=NULL;

    init_pnm_buf();
    if (app_debugger) {printf("Jasper-conversion-1\n"); fflush(stdout);}
    my_jasper= image_2_jpg(pnm_buf, pnm_size);

    if (my_jasper) {
        if (app_debugger) {printf("Jasper-conversion-2..sending.\n"); fflush(stdout);}
	webcam_send_image(my_jasper, packet_size);
	if (app_debugger) {printf("Jasper-conversion-3..sent\n"); fflush(stdout);}
	free(my_jasper); 
	my_jasper=NULL;
    }
    
    uploadstatus = 1;
#if defined(G_THREADS_ENABLED)
    g_thread_exit(0);
#elif defined(USE_PTHREAD_CREATE)
    pthread_exit(0);
#endif
    return NULL; /* satisfy compiler */
}
#endif

void update_cam() {
    unsigned char *grabit=grab_one(&x, &y);
#if defined(G_THREADS_ENABLED)
    GThread *tid;
#elif defined(USE_PTHREAD_CREATE)
    pthread_t tid;
#endif
    
    if (!grabit) {
        show_error_dialog("There was an error reading the webcam image.");
	return;
    } 

    current_pixbuf=gdk_pixbuf_new_from_data(grabit,GDK_COLORSPACE_RGB,FALSE,8,x,y,x*24/8,NULL,NULL);
    gtk_image_set_from_pixbuf(GTK_IMAGE(current_image), current_pixbuf);
    gdk_pixbuf_unref(current_pixbuf);
    current_pixbuf=NULL;

#if defined(G_THREADS_ENABLED)
    if (webcam_connected && image_need_update && uploadstatus) {
        uploadstatus = 0;
	tid=g_thread_create(uploadthread, NULL, FALSE, NULL );
    }
#elif defined(USE_PTHREAD_CREATE)
    if (webcam_connected && image_need_update && uploadstatus) {
        uploadstatus = 0;
        if (!pthread_create(&tid, NULL, uploadthread, NULL)) {
            pthread_detach(tid);
	}
    }
#else
    unsigned char *my_jasper=NULL;

    while( gtk_events_pending()) {
        gtk_main_iteration();
    }

    if (webcam_connected && image_need_update) {

        init_pnm_buf();
        if (rgb_buf != grabit) memcpy(rgb_buf, grabit, grab_size);
        if (app_debugger) {printf("Jasper-conversion-1\n"); fflush(stdout);}
        my_jasper= image_2_jpg(pnm_buf, pnm_size);

        while( gtk_events_pending()) {
            gtk_main_iteration();
        }

        if (my_jasper) {
            if (app_debugger) {printf("Jasper-conversion-2..sending.\n"); fflush(stdout);}
            webcam_send_image(my_jasper, packet_size);
            if (app_debugger) {printf("Jasper-conversion-3..sent\n"); fflush(stdout);}
            free(my_jasper); 
            my_jasper=NULL;
        }
    }
#endif
}

/* signal handler */
void _sighandler (int sig) {
	switch (sig){			
		case SIGINT: /* ctrl+x */
			cleanup_v4l();
			show_error_dialog("Application interrupted: Shutting down.");
			break;
			}
}

void set_video_device(char *myvdev) {
	v_device=strdup(myvdev);
}

/* read webcamrc configuration file */
void read_webcamrc () {
	FILE *f;
	char line[256];

	snprintf(webcamrc_path, sizeof(webcamrc_path), 
			 "%s/.yahoorc/gyach/webcamrc", getenv("HOME"));
	
	if ((f = fopen(webcamrc_path, "r")) == NULL) {
		/* noconfig available */
		return;
	}
	while (fgets(line, sizeof(line), f)) {
		char *p, *key, *value;
		/* strip comments */
		if ((p = strchr(line, '#'))) *p = '\0';
		if ( (key = strtok(line, "=")) && (value = strtok(NULL, "\n"))) {
			if (strcmp(key,"width")==0) 
				x = atoi(value);

			else if (strcmp(key,"height") == 0)
				y = atoi(value);

            else if (strcmp(key, "fix_color") == 0)
                fix_color = atoi(value);

			else if (strcmp(key, "hue") == 0)
				hue = atoi(value);

			else if (strcmp(key, "contrast") == 0)
				contrast = atoi(value);

			else if (strcmp(key, "brightness") == 0)
				brightness = atoi(value);

			else if (strcmp(key, "colour") == 0)
				colour = atoi(value);

			else if (strcmp(key, "whiteness") == 0)
				whiteness = atoi(value);

		} else {
			fprintf(stderr, "Problems in %s\n", webcamrc_path);
		}		
	}
	fclose(f);
}

void write_webcamrc() {
	FILE *f;
	
	/* create ~/.yahoorc/gyach dir if it does not exist (it should) */
	snprintf(webcamrc_path, sizeof(webcamrc_path), "%s/.yahoorc", 
			 getenv("HOME"));
	if (!dir_exists(webcamrc_path))
		mkdir(webcamrc_path, 0700);

	snprintf(webcamrc_path, sizeof(webcamrc_path), "%s/.yahoorc/gyach", 
			 getenv("HOME"));
	if (!dir_exists(webcamrc_path))
		mkdir(webcamrc_path, 0700);

	snprintf(webcamrc_path, sizeof(webcamrc_path), 
			 "%s/.yahoorc/gyach/webcamrc", getenv("HOME"));

	if ((f = fopen(webcamrc_path, "w")) == NULL) {
		fprintf(stderr, "Problems when writing configuration to %s\n",
				webcamrc_path);
		return;
	}
	fprintf(f, "width=%i\n"
			"height=%i\n"
			"fix_color=%i\n"
			"hue=%i\n"
			"contrast=%i\n"
			"brightness=%i\n"
			"colour=%i\n"
			"whiteness=%i\n",
			x, y, fix_color, hue, contrast, brightness, colour, whiteness);
	fclose(f);
}


void init_cam () {
	signal (SIGINT, _sighandler);
	if (!v_device) {
			show_error_dialog("No Video4Linux device was specified.");
			return;
			}
	if (strlen(v_device)<1) {
			show_error_dialog("No Video4Linux device was specified.");
			return;
			}
	read_webcamrc();
	grab_init();
	set_picture();
}


#ifdef V4L2
int grab_init() {
    struct v4l2_buffer buf;
	struct v4l2_requestbuffers req;
    enum v4l2_buf_type type;
	unsigned int min;
	unsigned int i;
	
        if ((grab_fd = open(v_device,O_RDWR | O_NONBLOCK, 0)) == -1 ) {
		show_error_dialog("Could not open Video4Linux device.\nThe device may already be in use.");
		return 0; 
	}
	
	if (ioctl(grab_fd,VIDIOC_QUERYCAP,&grab_cap) == -1) {
		show_error_dialog("An error occurred at 'ioctl VIDIOC_QUERYCAP'.\nNot a V4L2 device.");
		return 0; 
	}
	
	if (!(grab_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        	show_error_dialog("Fatal: grab device does not handle capture\n");
		return 0;
	}
	
	if (!(grab_cap.capabilities & V4L2_CAP_STREAMING)) {
        	show_error_dialog("Fatal: grab device does not support streaming i/o\n");
		return 0;
    	}
	

	memset(webcam_description, '\0', sizeof(webcam_description));
	snprintf(webcam_description, sizeof(webcam_description), "%s %s",
			 grab_cap.card, " V4L2");

        /* Select video input, video standard and tune here. */
	
        CLEAR(grab_fmt);

        grab_fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        grab_fmt.fmt.pix.width       = x; 
        grab_fmt.fmt.pix.height      = y;
        grab_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
        grab_fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

        if (ioctl (grab_fd, VIDIOC_S_FMT, &grab_fmt) == -1) {
			show_error_dialog("Fatal: video format not supported by grab device\n");
			return 0;
		}

        /* Note VIDIOC_S_FMT may change width and height. */

//		CLEAR(grab_fmt);
//        grab_fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//        if (ioctl (grab_fd, VIDIOC_G_FMT, &grab_fmt) == -1) {
//			show_error_dialog("Could not get format information of grab device\n");
//			return 0;
//		}
//
//		long int a = grab_fmt.fmt.pix.pixelformat;
//		char atxt[4] = "AAAA";
//		atxt[3]=(a>>24)&0xFF;
//		atxt[2]=(a>>16)&0xFF;
//		atxt[1]=(a>>8)&0xFF;
//		atxt[0]=a&0xFF;
//		fprintf(stderr,"pixelformat: %s\n",atxt);
//		fprintf(stderr,"x: %i\n",grab_fmt.fmt.pix.width);
//		fprintf(stderr,"y: %i\n",grab_fmt.fmt.pix.height);
//		fprintf(stderr,"field: %i\n",grab_fmt.fmt.pix.field);

	/* Buggy driver paranoia. */
	min = grab_fmt.fmt.pix.width * 2;
	if (grab_fmt.fmt.pix.bytesperline < min) grab_fmt.fmt.pix.bytesperline = min;
	min = grab_fmt.fmt.pix.bytesperline * grab_fmt.fmt.pix.height;
	if (grab_fmt.fmt.pix.sizeimage < min) grab_fmt.fmt.pix.sizeimage = min;

        CLEAR(req);

        req.count               = 2;	// Doublebuffering
        req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory              = V4L2_MEMORY_MMAP;

	if (ioctl (grab_fd, VIDIOC_REQBUFS, &req) == -1) {
			show_error_dialog("Fatal: memory mapping not supported by grab device\n");
			return 0;
		}

        if (req.count < 2) {
			   show_error_dialog("Insufficient buffer memory\n");
			   return 0;
        }

        buffers = calloc (req.count, sizeof (*buffers));

        if (!buffers) {
			   show_error_dialog("GRAB: Out of memory\n");
			   return 0;
        }

        for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
        		CLEAR(buf);

                buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      = V4L2_MEMORY_MMAP;
                buf.index       = n_buffers;

                if (ioctl (grab_fd, VIDIOC_QUERYBUF, &buf) == -1) {
					show_error_dialog("VIDIOC_QUERYBUF\n");
					return 0;
				}
				
                buffers[n_buffers].length = buf.length;
                buffers[n_buffers].start =
                        mmap (NULL /* start anywhere */,
                              buf.length,
                              PROT_READ | PROT_WRITE /* required */,
                              MAP_SHARED /* recommended */,
                              grab_fd, buf.m.offset);

                if (buffers[n_buffers].start == MAP_FAILED) {
					show_error_dialog("Error mmap\n");
					return 0;
				}
        }
        
	grab_size = x * y * w;
	init_pnm_buf();

	/* Now turn on video streaming */

		for (i = 0; i < n_buffers; ++i) {
        		CLEAR(buf);

        		buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        		buf.memory      = V4L2_MEMORY_MMAP;
        		buf.index       = i;

        		if (ioctl (grab_fd, VIDIOC_QBUF, &buf) == -1) {
					show_error_dialog("VIDIOC_QBUF\n");
					return(0);
				}
				
		}
		
		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

		if (ioctl (grab_fd, VIDIOC_STREAMON, &type) == -1) {
			show_error_dialog("VIDIOC_STREAMON\n");
			return(0);
		}
	return(1);
}

void cleanup_v4l() {
    enum v4l2_buf_type type;
    unsigned int i;

	cam_is_open=0;
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (ioctl (grab_fd, VIDIOC_STREAMOFF, &type)) {
		show_error_dialog("VIDIOC_STREAMOFF\n");
		return;
	}

	for (i = 0; i < n_buffers; ++i) munmap (buffers[i].start, buffers[i].length);
	free (buffers);

	if (v_device) {free (v_device); v_device=NULL;}
	if (pnm_buf) {free(pnm_buf); pnm_buf=NULL;}
	if (grab_fd>-1) {
		close (grab_fd);
		grab_fd=-1;
	}
}

int get_control_v4l2(unsigned int id) {
	struct v4l2_queryctrl grab_ctrl;

	grab_ctrl.id = id;
	if (ioctl(grab_fd,VIDIOC_QUERYCTRL,&grab_ctrl) != -1) {
		fprintf(stderr, "ctrl %i:%s min:%i max:%i def:%i\n", id, grab_ctrl.name, grab_ctrl.minimum, grab_ctrl.maximum,
		grab_ctrl.default_value);
		return(0);
	}
	return(1);
}

void set_v4l2control(unsigned int id, int value) {
    struct v4l2_control propcontrol;
	
    if(!get_control_v4l2(id)) {
	propcontrol.id = id;
    	propcontrol.value= value;
    	ioctl (grab_fd, VIDIOC_S_CTRL, &propcontrol);
    }
}

void set_picture() {
	if (hue > -1) set_v4l2control(V4L2_CID_HUE, hue);
	if (contrast > -1) set_v4l2control(V4L2_CID_CONTRAST, 3); //contrast);
	if (brightness > -1) set_v4l2control(V4L2_CID_BRIGHTNESS, brightness);
	if (colour > -1) set_v4l2control(V4L2_CID_SATURATION, colour);
	if (whiteness > -1) set_v4l2control(V4L2_CID_WHITENESS, whiteness);
}

unsigned char* grab_one(int *width, int *height) {
    struct v4l2_buffer buf;

	CLEAR(buf);

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    if (ioctl (grab_fd, VIDIOC_DQBUF, &buf) == -1) {
    	switch (errno) {
        	case EAGAIN:
				return rgb_buf;
			default:
				show_error_dialog("grab_one: VIDIOC_DQBUF\n");
				return;
    		}
    }

    assert (buf.index < n_buffers);
			
	grab_data=buffers[buf.index].start;

	if (ioctl (grab_fd, VIDIOC_QBUF, &buf) == -1) {
		show_error_dialog("VIDIOC_QBUF\n");
		return;
	}

	switch(grab_fmt.fmt.pix.pixelformat) {
	    case V4L2_PIX_FMT_SBGGR8:	bayer2rgb24 (rgb_buf, grab_data, x, y); break;
	    case V4L2_PIX_FMT_RGB332:   break;
	    case V4L2_PIX_FMT_RGB555: 	rgb555_to_rgb24(x, y, rgb_buf, grab_data); break;
	    case V4L2_PIX_FMT_RGB565:  	rgb565_to_rgb24(x, y, rgb_buf, grab_data); break;
	    case V4L2_PIX_FMT_RGB555X:  break;
	    case V4L2_PIX_FMT_RGB565X:  break;
	    case V4L2_PIX_FMT_BGR24:  
	    case V4L2_PIX_FMT_RGB24:  	rgb24_to_rgb24(x, y, rgb_buf, grab_data); break;
	    case V4L2_PIX_FMT_BGR32:  
	    case V4L2_PIX_FMT_RGB32:   	rgb32_to_rgb24(x, y, rgb_buf, grab_data); break;
	    case V4L2_PIX_FMT_GREY:		grey_to_rgb24(x, y, rgb_buf, grab_data); break;
	    case V4L2_PIX_FMT_YVU410:   break;
	    case V4L2_PIX_FMT_YVU420:   break;
	    case V4L2_PIX_FMT_YUYV:     break;
	    case V4L2_PIX_FMT_UYVY:     break;
	    case V4L2_PIX_FMT_YUV422P:  yuv422p_to_rgb24(x, y, rgb_buf, grab_data); break;
	    case V4L2_PIX_FMT_YUV411P:  break;
	    case V4L2_PIX_FMT_Y41P:     break;
	    case V4L2_PIX_FMT_NV12:     break;
	    case V4L2_PIX_FMT_NV21:     break;
	    case V4L2_PIX_FMT_YUV410:   break;
	    case V4L2_PIX_FMT_YUV420:  	yuv420p_to_rgb24(x, y, rgb_buf, grab_data); break;
	    case V4L2_PIX_FMT_YYUV:     break;
	    case V4L2_PIX_FMT_HI240:    break;
	    case V4L2_PIX_FMT_MJPEG:    break;
	    case V4L2_PIX_FMT_JPEG:     break;
	    case V4L2_PIX_FMT_DV:       break;
	    case V4L2_PIX_FMT_MPEG:     break;
	    case V4L2_PIX_FMT_WNVA:     break;
	    case V4L2_PIX_FMT_SN9C10X:  break;

	    default:
	}

	if(fix_color) fix_colour(rgb_buf, x, y);
	return rgb_buf;
}

#else // V4L2

// Here begin functions for the Video4Linux Api 1
int grab_init() {
	if ((grab_fd = open(v_device,O_RDWR)) == -1 ) {
	show_error_dialog("Could not open Video4Linux device.\nThe device may already be in use.");
	return 0; 
	}

	if (ioctl(grab_fd,VIDIOCGCAP,&grab_cap) == -1) {
	show_error_dialog("An error occurred at 'ioctl VIDIOCGCAP'.\nWrong device.");
	return 0; 
	}

	if (!grab_cap.type & VID_TYPE_CAPTURE) {
	show_error_dialog("Device does not support capturing.");
	}

	if((grab_cap.minwidth && (grab_cap.minwidth > x)) ||
		(grab_cap.minheight && (grab_cap.minheight > y))) {
			x = grab_cap.minwidth;
			y = grab_cap.minheight;
	}
		
	if((grab_cap.maxwidth && (grab_cap.maxwidth < x)) ||
		(grab_cap.maxheight && (grab_cap.maxheight < y))) {
			x = grab_cap.maxwidth;
			y = grab_cap.maxheight;
	}
		
	memset (&grab_pic, 0, sizeof(struct video_picture));
	snprintf(webcam_description, 3, "%s",  "");
	strncpy(webcam_description, grab_cap.name ,28);
	strcat(webcam_description, " V4L1");

	if (ioctl (grab_fd, VIDIOCGPICT, &grab_pic) == -1) {
		show_error_dialog("Error getting picture information.");
		return 0; 
	}

	set_picture();

	// Find out how many buffers are available
	if(ioctl(grab_fd, VIDIOCGMBUF, &grab_mbuf) < 0)	{
		show_error_dialog("Error while querying mmap-buffers.");
        return 0;
	}
	
	/* mmap all available buffers. */
	grab_data = mmap(0, grab_mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, grab_fd, 0);
	if(grab_data == MAP_FAILED) {
		show_error_dialog("Error mmap'ing all available buffers.");
		return 0;
	}
	
	/* Setup capture options. */
	grab_buf.format = wpalette[grab_palette].palette;
	grab_buf.width  = x;
	grab_buf.height = y;
	grab_buf.frame  = 0;
	grab_size = x * y * w;
	
	/* Request the maximum number of frames the device can handle. */
	if(ioctl(grab_fd, VIDIOCMCAPTURE, &grab_buf) < 0) {
		show_error_dialog("Error while capturing initial image.");
		return 0;
	}

	init_pnm_buf();
	return(1);
}

void cleanup_v4l() {
	cam_is_open=0;
	if (v_device) {free (v_device); v_device=NULL;}
	if (pnm_buf) {free(pnm_buf); pnm_buf=NULL;}
	if (grab_fd>-1) {
		close (grab_fd);
		grab_fd=-1;
		munmap(grab_data, grab_mbuf.size);
	}
}

void set_picture() {
	if (hue > -1) grab_pic.hue=hue;
	if (contrast > -1) grab_pic.contrast=contrast;
	if (brightness > -1) grab_pic.brightness=brightness;
	if (colour > -1) grab_pic.colour=colour;
	if (whiteness > -1) grab_pic.whiteness=whiteness;

	// special case for MJPEG-devices
	if(grab_cap.type & VID_TYPE_MJPEG_ENCODER) grab_palette = PAL_JPEG;
	
	// Try each palette 
	while(wpalette[grab_palette].name != NULL)
	{
		grab_pic.palette = wpalette[grab_palette].palette;
		grab_pic.depth   = wpalette[grab_palette].depth;

		if(!ioctl(grab_fd, VIDIOCSPICT, &grab_pic))
		{
			return;
		}
		
		grab_palette++;
	}

	show_error_dialog("An error occurred at 'ioctl VIDIOCSPICT'.\nCould not set camera properties.");
}	

unsigned char* grab_one(int *width, int *height) {
	set_picture();
	
	/* Wait for the frame to be captured. */
	if(ioctl(grab_fd, VIDIOCSYNC, &grab_buf) < 0) {
		show_error_dialog("An error occurred at 'ioctl VIDIOCSYNC'.");
		return NULL;
	}
	
	if(ioctl(grab_fd, VIDIOCMCAPTURE, &grab_buf) < 0) {
		show_error_dialog("Error while capturing an image.");
		return NULL;
	}

	switch(grab_palette) {
//notready		case PAL_JPEG:			jpeg_to_rgb24(x, y, rgb_buf, grab_data, framelen); break;
		case PAL_RGB32:			rgb32_to_rgb24(x, y, rgb_buf, grab_data);	break;
		case PAL_RGB24:			rgb24_to_rgb24(x, y, rgb_buf, grab_data); break;
		case PAL_YUV420P:		yuv420p_to_rgb24(x, y, rgb_buf, grab_data); break;
		case PAL_YUV422P:		yuv422p_to_rgb24(x, y, rgb_buf, grab_data); break;
//old		case PAL_YUV420P:		yuv420p_to_rgb(grab_data, rgb_buf, grab_pic.depth, x, y); break;
		case PAL_RGB565:		rgb565_to_rgb24(x, y, rgb_buf, grab_data); break;
		case PAL_RGB555:		rgb555_to_rgb24(x, y, rgb_buf, grab_data); break;
		case PAL_GREY:			grey_to_rgb24(x, y, rgb_buf, grab_data); break;
		default:				return NULL;
	}

	if(fix_color) fix_colour(rgb_buf, x, y);
	return rgb_buf;
}

#endif //V4L2

void set_vid_properties(int thue, int tcontrast, int tcolor, int tbrightness, int tfc) {
	hue=thue;
	contrast=tcontrast;
	colour=tcolor;
	brightness=tbrightness;
	fix_color=tfc;
	set_picture();
	write_webcamrc();
}

void  start_cam()
{
   current_image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
   init_cam();
   gtk_widget_set_usize(current_image,x,y);
}
