#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev.h>
#include <sys/mman.h>
#include "videoInput.h"


videoInput::videoInput(const char *devname, int width, int height)
    {
    if (!devname)
        devname = "/dev/video";
    deviceName_ = strdup(devname);
    width_ = width;
    height_ = height;
    frameNumber_ = 0;
    data_ = 0;
    frames_ = 1;
    offsets_ = 0;

    fd_ = open(deviceName_, O_RDWR);
    if (fd_ == -1)
        {
        perror(deviceName_);
        return;
        }
    
    struct video_channel channel;
    channel.channel = 1;
    channel.norm = VIDEO_MODE_NTSC;
    if (ioctl(fd_,VIDIOCSCHAN,&channel) == -1)
        {
        perror("VIDIOCSCHAN");
        exit(1);
        } 

    struct video_picture pic;
    pic.depth = 24;
    pic.palette = VIDEO_PALETTE_RGB24;
    pic.brightness = pic.hue = pic.colour = pic.contrast = pic.whiteness = 32767;
    ioctl(fd_, VIDIOCSPICT, &pic);
    
    struct video_mbuf m_buf;
    ioctl(fd_, VIDIOCGMBUF, &m_buf);
    frames_ = m_buf.frames;
    if (frames_ <= 0)
        {
        fprintf(stderr,"WARNING: videoInput(%s) got bogus number of frames"
                " for video buffer: %d\n", deviceName_, frames_);
        frames_ = 1;
        }
    else
        {
        offsets_ = (int *) malloc(frames_ * sizeof(int));
        for (int i=0; i < frames_; i++)
            offsets_[i] = m_buf.offsets[i];
        }
        
    data_ = (unsigned char *)mmap(0, m_buf.size, PROT_READ, MAP_SHARED, fd_, 0);

    struct video_mmap vmap;
    vmap.format = VIDEO_PALETTE_RGB24;
    vmap.frame  = (frameNumber_ + 1) % frames_;
    vmap.width  = width_;
    vmap.height = height_;
    if (ioctl(fd_, VIDIOCMCAPTURE, &vmap) == -1)
        perror("VIDIOCMCAPTURE");
    }

videoInput::~videoInput(void)
    {
    if (fd_ != -1)
        close(fd_);
    fd_ = -1;
    }


void videoInput::grab(void)
    {
    if (fd_ == -1)
        return;

    frameNumber_ = (frameNumber_ + 1) % frames_;
    
    struct video_mmap vmap;
    vmap.format = VIDEO_PALETTE_RGB24;
    vmap.frame  = (frameNumber_ + 1) % frames_;
    vmap.width  = width_;
    vmap.height = height_;
    if (ioctl(fd_,VIDIOCMCAPTURE,&vmap) == -1)
        perror("VIDIOCMCAPTURE");
    
    vmap.format = VIDEO_PALETTE_RGB24;
    vmap.frame  = frameNumber_;
    vmap.width  = width_;
    vmap.height = height_;
    if (ioctl(fd_,VIDIOCSYNC,&vmap) == -1)
        perror("VIDIOCSYNC");
    }


unsigned char * videoInput::currentFrame(void)
    {
    if ((data_) && (offsets_))
        return data_ + offsets_[frameNumber_];
    else
        return 0;
    }

