

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#include "tvi_v4l2.h"
#include "errormsg.h"
#include "video2mem.h"
#include "config.h"

#include "img_format.h"


void *priv = NULL;

void exit_video2mem(void);
int inited_flags=1;
int exitflg=0;

static char *configfile = NULL;
static char *pidfile = NULL;
static int mode = 0;
static int logmode = 2;
static int loglevel = 0;
static int fps;
static int width;
static int height;
static int format;
static int input;
static float freq;
static int daemonflg;
static config_t *def = NULL;
static config_t *item = NULL;

static int loadarg(int argc, char** argv);
static void usage(char *fname);
static void sample(void);

static void exit_sighandler(int x){
  static int sig_count=0;
  ++sig_count;
  if(inited_flags==0 && sig_count>1) exit(1);
  if(sig_count==5)
    {
      exit(1);
    }
  if(sig_count==6) exit(1);
  if(sig_count<=1)
  switch(x){
  case SIGINT:
  case SIGQUIT:
  case SIGTERM:
  case SIGKILL:
      break;  // killed from keyboard (^C) or killed [-9]
  case SIGILL:
  case SIGFPE:
  case SIGSEGV:
  default:
    {}
  }
  exitflg=1;
  exit_video2mem();
  inited_flags=0;
}


void exit_video2mem(void)
{
int i;
config_t *temp;

for(i=0;i<17;i++)
  {
  if (NULL!=(temp=seekconfigid(i)))
    {
    video_uninit(&(temp->priv));
    if(temp->name) free(temp->name);
    temp->name=NULL;
    if(temp->device) free(temp->device);
    temp->device=NULL;
    }
  }
if(configfile) free(configfile);
configfile=NULL;
if(pidfile) {unlink(pidfile); free(pidfile);}
pidfile=NULL;
if(item) free(item);
item=NULL;
if(def) free(def);
def=NULL;
if(format==-1) format=IMGFMT_I420;
}

int loadarg(int argc, char** argv)
{
int i;
int flg = 0;
int flgact = 0;
for(i=1;i<argc;i++)
  {
  if (strlcmp(argv[i],"-c") || strlcmp(argv[i],"--config-file")) flg=1;
  if (strlcmp(argv[i],"-m") || strlcmp(argv[i],"--mode")) flg=2;
  if (strlcmp(argv[i],"-l") || strlcmp(argv[i],"--log-mode")) flg=3;
  if (strlcmp(argv[i],"-L") || strlcmp(argv[i],"--log-level")) flg=4;
  if (strlcmp(argv[i],"-f") || strlcmp(argv[i],"--fps")) flg=5;
  if (strlcmp(argv[i],"-g") || strlcmp(argv[i],"--geometry")) flg=6;
  if (strlcmp(argv[i],"-i") || strlcmp(argv[i],"--input")) flg=7;
  if (strlcmp(argv[i],"-d") || strlcmp(argv[i],"--daemon")) flgact=8;
  if (strlcmp(argv[i],"-p") || strlcmp(argv[i],"--pid-file")) flgact=9;
  if (strlcmp(argv[i],"-o") || strlcmp(argv[i],"--out-format")) flgact=11;
  if (strlcmp(argv[i],"-h") || strlcmp(argv[i],"--help")) flgact=10;
  if (!flg) {
    switch (flgact) {
      case 1 : if(strlcmp(argv[i],"help"))
    		 {
		 sample();
		 return 0;
		 }
    	       if(!configfile) free(configfile);
    	       configfile=strdup(argv[i]);
	       flgact=0;
	       break;
      case 2 : mode=atoi(argv[i]);
               flgact=0;
               break;
      case 3 : logmode=atoi(argv[i]);
    	       flgact=0;
	       break;
      case 4 : loglevel=atoi(argv[i]);
    	       flgact=0;
	       break;
      case 5 : fps=atoi(argv[i]);
    	       flgact=0;
	       break;
      case 6 : sscanf(argv[i], "%d:%d", &width, &height);
    	       flgact=0;
    	       break;
      case 7 : sscanf(argv[i], "%d:%f", &input, &freq);
    	       flgact=0;
    	       break;
      case 8 : daemonflg=1;
    	       flgact=0;
	       break;
      case 9 : if(!pidfile) free(pidfile);
    	       pidfile=strdup(argv[i]);
	       flgact=0;
	       break;
      case 11 : if (strlcmp(argv[i],"help"))
    	         {
		 listformat();
		 return 0;
		 }
	       format=get_format(argv[i]);
	       break;
      case 10 : usage(argv[0]);
    	       return 0;
	       break;
      }
    }
  if (flg) flgact=flg;
  flg=0;
  }
if (!daemonflg && !configfile) configfile=strdup(DEFAULT_CONFIG);
if (daemonflg && !configfile) configfile=strdup(DEFAULT_DCONFIG);
if (daemonflg && !pidfile) pidfile=strdup(DEFAULT_PID);
if (!daemonflg && logmode==-1) logmode=4;
if (daemonflg && logmode==-1) logmode=2;
if (!daemonflg && loglevel==-1) loglevel=0;
if (daemonflg && loglevel==-1) loglevel=1;
return 1;
}

static void usage(char *fname)
{
printf("The video4vl2 grabing to shared memory\n");
printf("Created by: 2006, Otvos Attila\n");
printf("Usage: %s [-d][-h] [-c <config file>] [-m <mode>] [-l <log mode>] [-L <log level>]\n",fname);
printf("\t-d|--daemon                 : set daemon mode\n");
printf("\t-m|--mode                   : set mode\n");
printf("\t\t0 : normal mode (default)\n");
printf("\t\t1 : force mode\n");
printf("\t-c|--config-file <file name>: set config file\n");
printf("\t\thelp: print sample config file\n");
printf("\t\tdefault normal: video2mem.conf\n");
printf("\t\tdefault daemon: /usr/local/etc/video2mem.conf\n");
printf("\t-p|--pid-file <file name>: set pid file\n");
printf("\t\tdefault normal: no settings\n");
printf("\t\tdefault daemon: /var/run/video2mem.pid\n");
printf("\t-l|--log-mode <log mode>    : set log mode\n");
printf("\t\t0 : no log\n");
printf("\t\t1 : log file (/var/log/video2mem.c)\n");
printf("\t\t2 : log system messages (daemon default)\n");
printf("\t\t3 : log file and system messages\n");
printf("\t\t4 : log to stderr (normal default)\n");
printf("\t\t5 : log file and stderr\n");
printf("\t\t6 : log system messages and stderr\n");
printf("\t\t7 : log all\n");
printf("\t-L|--log-level <log level>  : set log level\n");
printf("\t\t0 : only error (daemon default)\n");
printf("\t\t1 : info (normal default)\n");
printf("\t\t2 : verbose\n");
printf("\t-f|--fps <fps>              : set default fps (25)\n");
printf("\t-g|--geometry <width:height>: set geometry\n");
printf("\t\tdefault: 640:480\n");
printf("\t\t-i|--input <input:freq>   : set input channel and tuner freq\n");
printf("\t\tdefault: 0:175.250\n");
printf("\t-o|--outformat <name>       : set garb format\n");
printf("\t\tdefault: i420\n");
printf("\t\thelp: list all formats\n");
printf("\t-h|--help: this list\n");
return;
}

void sample(void)
{
printf("Sample config file:\n");
printf("logmode = 4\n");
printf("loglevel = 2\n");
printf("#tvfps = 25\n");
printf("tvfps = 10\n");
printf("tvinput = 1\n");
printf("tvfreq = 175.250\n");
printf("tvwidth = 640\n");
printf("tvheight = 480\n");
printf("tvformat = i420\n");
printf("\n");
printf("[Cam1]	// 1 camera\n");
printf("tvdevice = /dev/video0\n");
printf("tvsmemid = 1000\n");
printf("\n");
printf("[Cam2]\n");
printf("tvdevice = /dev/video1\n");
printf("tvsmemid = 1001\n");
printf("\n");
printf("[Cam3]\n");
printf("tvdevice = /dev/video2\n");
printf("tvsmemid = 1002\n");
return;
}


int main(int argc, char** argv)
{
config_t *temp;
int i,n;

  signal(SIGTERM,exit_sighandler); // kill
  signal(SIGHUP,exit_sighandler);  // kill -HUP  /  xterm closed
  signal(SIGINT,exit_sighandler);  // Interrupt from keyboard
  signal(SIGQUIT,exit_sighandler); // Quit from keyboard
  signal(SIGPIPE,exit_sighandler); // Some window managers cause this
#ifdef ENABLE_SIGHANDLER
  // fatal errors:
  signal(SIGBUS,exit_sighandler);  // bus error
  signal(SIGSEGV,exit_sighandler); // segfault
  signal(SIGILL,exit_sighandler);  // illegal instruction
  signal(SIGFPE,exit_sighandler);  // floating point exc.
  signal(SIGABRT,exit_sighandler); // abort()
#endif

configfile = NULL;
pidfile = NULL;
mode = 0;
logmode = -1;
loglevel = -1;
fps=DEFAULT_FPS;
format=-1;
width=DEFAULT_WIDTH;
height=DEFAULT_HEIGHT;
input=DEFAULT_INPUT;
freq=DEFAULT_FREQ;
daemonflg=0;
if (!loadarg(argc,argv)) return 0;
setlogmode(logmode);
setloglevel(loglevel);
loadconfig(configfile);
if (pidfile) {
  FILE *fhpid;
  if (NULL!=(fhpid=fopen(pidfile,"w"))) {
    fprintf(fhpid,"%d\n",getpid());  
    fclose(fhpid);
    } else {
    logprint(LEVEL_ERR,"pid file (%s) open error: %s\n",pidfile,strerror(errno));
    free(pidfile);
    pidfile=NULL;
    }
}
if (NULL==(item=malloc(sizeof(config_t)))) {logprint(LEVEL_ERR,"malloc config buffers failed %s",
	strerror(errno)); return 0;}
memset(item,0,sizeof(config_t));
if (NULL==(def=malloc(sizeof(config_t)))) {logprint(LEVEL_ERR,"malloc config buffers failed %s",
	strerror(errno)); free(def); return 0;}
memset(def,0,sizeof(config_t));
configclear(def);
def->name=strdup("default");
if (NULL!=(temp=seekconfigname("default")))
  {
  *def=*temp;
  if(temp->name) def->name=strdup(temp->name);  
  if(temp->device) def->device=strdup(temp->device);  
  }
if(def->logmode!=-1) setlogmode(def->logmode);
if(def->loglevel!=-1) setloglevel(def->loglevel);
int nodefault=0;
for(i=0;i<17;i++)
  {
  if (NULL!=(temp=seekconfigid(i)))
    {
    memset(item,0,sizeof(config_t));
    configclear(item);
    item->name=strdup(temp->name);
    configmix(item,def);
    configmix(item,temp);
    if (strlcmp(item->name,"default")) nodefault=1;    
    }
  }
int isopen;
int openok = 0;
for(i=0;i<17;i++)
  {
  if (NULL!=(temp=seekconfigid(i)))
    {
    memset(item,0,sizeof(config_t));
    configclear(item);
    item->name=strdup(temp->name);
    configmix(item,def);
    configmix(item,temp);
    if (strlcmp(item->name,"default")) isopen=!nodefault; else isopen=1;
    if (isopen)
      {
      if(mode) {
        for(n=0;n<TRYCOUNT;n++)
	  {
          if (NULL!=(priv=video_init(item->name,item->device, item->smemid))) break;
	  usleep(1000000);
	  }
	}
	else
        priv=video_init(item->name,item->device, item->smemid);
      if (priv)
        {
	if(item->width>0 && item->height>0)
	  video_size(priv,item->width,item->height);
	  else
	  video_size(priv,width,height);
	if(item->input!=-1 || item->freq!=-1.0)
	  video_input(priv,item->input,item->freq);
	  else
	  video_input(priv,input,freq);
	if(item->fps!=-1)
	  video_fps(priv,item->fps);
	  else
	  video_fps(priv,fps);
	if(item->format>0)
	  video_format(priv,item->format);
	  else
	  video_format(priv,format);
	if (video_start(priv))
	  {
	  logprint(LEVEL_VERBOSE,"%s: grab start device=%s width=%i height=%i input=%i freq=%8.3f fps=%i format=%x\n",
	    item->name,
	    item->device,
	    item->width,
	    item->height,
	    item->input,
	    item->freq,
	    item->fps,
	    item->format);
	  openok=1;
	  temp->priv=priv;
	  }
	}      
      }
    }
  }
if (!openok) return 1;
while (1){
  for(i=0;i<17;i++)
    {
    if (NULL!=(temp=seekconfigid(i)))
      {
      if(NULL!=temp->priv) {
        if (!video_process(temp->priv)) if(mode==0) exitflg=1;
	}
      }
    }
  if(exitflg) break;
  } 
for(i=0;i<17;i++)
  {
  if (NULL!=(temp=seekconfigid(i)))
    {
    video_uninit(&(temp->priv));
    }
  }
exit_video2mem();
return 0;
}
