#include <unistd.h>
#include <features.h> /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <math.h>
#include <linux/types.h>

#include "videodev2.h"

#include "ivtv-ext-api.h"

/* Options */
#define OptSetCodec			(1L<<0)
#define OptListCodec		(1L<<1)
#define OptGetFormat		(1L<<2)
#define OptSetFormat		(1L<<3)
#define OptListRegisters	(1L<<4)
#define OptSetRegister		(1L<<5)
#define OptListCapability	(1L<<6)
#define OptListInputs		(1L<<7)
#define OptGetInput			(1L<<8)
#define OptSetInput			(1L<<9)
#define OptGetFreq			(1L<<10)
#define OptSetFreq			(1L<<11)
#define OptListStandards	(1L<<12)
#define OptGetStandard		(1L<<13)
#define OptSetStandard		(1L<<14)
#define OptSetIO			(1L<<15)
#define OptListCtrls		(1L<<16)
#define OptSetCtrl			(1L<<17)
#define OptFrameSync    (1L<<18)

/* Codec's specified */
#define CAspect			(1L<<1)
#define CBitrateMode	(1L<<2)
#define CBitrate		(1L<<3)
#define CBitratePeak	(1L<<4)
#define CStreamType		(1L<<5)
#define CFreamerate		(1L<<6)
#define CFramesPerGOP	(1L<<7)
#define CBFrames		(1L<<8)
#define CGOPClosure		(1L<<9)
#define CAudio			(1L<<10)
#define CDNRMode		(1L<<11)
#define CDNRType		(1L<<12)
#define CDNRSpatial		(1L<<13)
#define CDNRTemporal	(1L<<14)
#define CPullDown		(1L<<15)

/* fmts specified */
#define FMTWidth	(1L<<0)
#define FMTHeight	(1L<<1)


void usage(void) {
	printf("Usage:\n");
	printf("  -h, --help         display this help message\n");
	printf("  -d, --device=<dev> Use device <dev> instead of /dev/video0\n");
	printf("  -a, --all          display all information available\n");
	printf("  -C, --list-codec-params\n");
	printf("                     get codec parameters [IVTV_IOC_G_CODEC]\n");
	printf("  -c, --set-codec-params=[param],[param],...\n");
	printf("                     set codec parameters [IVTV_IOC_S_CODEC]\n");
	printf("     param:\n");
	printf("       aspect       =<#> aspect ratio (try 3)\n");
	printf("       audio        =<#> sets audio bitmask (try 0xE9)\n");
	printf("       bframes      =<#> number of B frames\n");
	printf("       bitrate      =<#> bitrate (DVD max = 9.6Mbps)\n");
	printf("       bitrate_peak =<#> peak bitrate (must be greater than bitrate)\n");
	printf("       dnr_mode     =<#> see API documentation\n");
	printf("       dnr_spatial  =<#> see API documentation\n");
	printf("       dnr_temporal =<#> see API documentation\n");
	printf("       dnr_type     =<#> see API documentation\n");
	printf("       framerate    =<#> try 0 (30fps) or 1 (25fps)\n");
	printf("       framespergop =<#> GOP size\n");
	printf("       gop_closure  =<#> open/closed gop\n");
	printf("       pulldown     =<#> inverse telecine on (1) / off (0)\n");
	printf("       stream_type  =<#> see ivtv.h IVTV_STREAM_* types (try 10 for DVD, or 0 for PS)\n");
	printf("  -e, --get-format   get the data format [VIDIOC_G_FMT]\n");
	printf("  -f, --set-format=width=<x>,height=<y>\n");
	printf("                     set the data format [VIDIOC_G_FMT]\n");
	printf("  -G, --list-registers\n");
	printf("                     dump SAA7115 registers [SAA7115_GET_REG]\n");
	printf("  -g, --set-register=reg=<reg>,val=<val>\n");
	printf("                     set SAA7115 registers [SAA7115_SET_REG]\n");
	printf("  -k, --sync\n");
	printf("                     test vsync  scapabilities [IVTV_IOC_FRAMESYNC]\n");
	printf("  -m, --list-capability\n");
	printf("                     query device capabilities [VIDIOC_QUERYCAP]\n");
	printf("  -n, --list-inputs  display video inputs [VIDIOC_ENUMINPUT]\n");
	printf("  -o, --get-input    query the current video input [VIDIOC_G_INPUT]\n");
	printf("  -p, --set-input=<num>\n");
	printf("                     set the current video input to <num> [VIDIOC_S_INPUT]\n");
	printf("  -q, --get-freq=<tuner>\n");
	printf("                     get frequency for <tuner> [VIDIOC_G_FREQUENCY]\n");
	printf("  -r, --set-freq=tuner=<tuner>,freq=<freq>\n");
	printf("                     sets frequency to <freq> for tuner <tuner> [VIDIOC_S_FREQUENCY]\n");
	printf("  -s, --list-standards\n");
	printf("                     display supported video standards [VIDIOC_ENUMSTD]\n");
	printf("  -t, --get-standard\n");
	printf("                     query the video standard of the current input [VIDIOC_G_STD]\n");
	printf("  -u, --set-standard=<num>\n");
	printf("                     sets standard to <num> [VIDIOC_S_STD]\n");
	printf("  -v --set-io=input=<in>,output=<out>\n");
	printf("                     sets the MSP34xx input/output mapping [MSP_SET_MATRIX]\n");
	printf("  -Y --list-ctrls    list all contrl's and their values [VIDIOC_QUERYCTRL]\n");
	printf("  -y --set-ctrl=[ctrl]=<val>\n");
	printf("                     sets the control to the value specified [VIDIOC_S_CTRL]\n");
	printf("     ctrl:\n");
	printf("      brightness     =<#> Picture brightness, or more precisely, the black level. [0 - 255]\n");
	printf("      hue            =<#> Hue or color balance. [-128 - 127]\n");
	printf("      saturation     =<#> Picture color saturation or chroma gain. [0 - 127]\n");
	printf("      contrast       =<#> Picture contrast or luma gain. [0 - 127]\n");
	printf("      volume         =<#> Overall audio volume. [0 - 65535]\n");
	printf("      mute           =<#> Mute audio, i. e. set the volume to zero [boolean]\n");
 	exit(0);
}


int printfmt(struct v4l2_format vfmt) {
	switch (vfmt.type) {
		case 1:
			printf("\tType   : Video Capture\n");
			printf("\tWidth  : %d\n", vfmt.fmt.pix.width);
			printf("\tHeight : %d\n", vfmt.fmt.pix.height);
			break;
		case 2:
			printf("\tType   : Video Output\n");
			break;
		case 3:
			printf("\tType   : Video Overlay\n");
			break;
		case 4:
			printf("\tType   : VBI Capture\n");
			break;
		case 5:
			printf("\tType   : VBI Output\n");
			break;
		case 0x80:
			printf("\tType   : Private\n");
			break;
		default:
			printf("\tType   : Unknown: %d\n", vfmt.type);
			return -1;
			break;
	}
	return 0;
}

char* pts_to_string(char* str, unsigned long pts) {
  static char buf[256];
  char* p = (str) ? str : buf;

  static const int MPEG_CLOCK_FREQ = 90000;
  int seconds = pts / MPEG_CLOCK_FREQ;
  int fracsec = pts % MPEG_CLOCK_FREQ;

  int minutes = seconds / 60;
  seconds = seconds % 60;

  int hours = minutes / 60;
  minutes = minutes % 60;

  float fps = 30;
  int frame = (int)ceilf(((float)fracsec / (float)MPEG_CLOCK_FREQ) * fps);

  snprintf(p, sizeof(buf), "%d:%02d:%02d:%d", hours, minutes, seconds, frame);
  return p;
}

int main(int argc, char **argv) {
	char *value, *subs;
	int i;
	char *subopts[] = {
		#define SUB_ASPECT			0
		"aspect",
		#define SUB_AUDIO			1
		"audio",
		#define SUB_BFRAMES			2
		"bframes",
		#define SUB_BITRATE_MODE	3
		"bitrate_mode",
		#define SUB_BITRATE			4
		"bitrate",
		#define SUB_BITRATE_PEAK	5
		"bitrate_peak",
		#define SUB_DNR_MODE		6
		"dnr_mode",
		#define SUB_DNR_SPATIAL		7
		"dnr_spatial",
		#define SUB_DNR_TEMPORAL	8
		"dnr_temporal",
		#define SUB_DNR_TYPE		9
		"dnr_type",
		#define SUB_FRAMERATE		10
		"framerate",
		#define SUB_FRAMESPERGOP	11
		"framespergop",
		#define SUB_FREQ			12
		"freq",
		#define SUB_GOP_CLOSURE		13
		"gop_closure",
		#define SUB_HEIGHT			14
 		"height",
		#define SUB_INPUT			15
		"input",
		#define SUB_OUTPUT			16
	 	"output",
		#define SUB_PULLDOWN		17
		"pulldown",
		#define SUB_STEAM_TYPE		18
		"stream_type",
		#define SUB_TUNER			19
		"tuner",
		#define SUB_WIDTH			20
		"width",
		#define SUB_REG				21
		"reg",
		#define SUB_VAL				22
		"val",
		#define SUB_BRIGHTNESS		23
		"brightness",
		#define SUB_CONTRAST		24
		"contrast",
		#define SUB_SATURATION		25
		"saturation",
		#define SUB_HUE				26
		"hue",
		#define SUB_VOLUME			27
		"volume",
		#define SUB_MUTE			28
		"mute",
		NULL
	};

	int fd = -1;

	/* command bitfield */
	unsigned int set_opts = 0;
	/* bitfield for OptSetCodec */
	unsigned int set_codecs = 0;
	/* bitfield for fmts */
	unsigned int set_fmts = 0;

	/* command args */
	char ch, *device = strdup("/dev/video0"); /* -d device */
	struct ivtv_ioctl_codec codec; /* set_codec/list_codec */
	struct v4l2_format vfmt;       /* set_format/get_format */
	struct v4l2_control ctrl;      /* set_ctrl/get_ctrls */
	struct {                       /* list_registers/set_regesters */
		unsigned char reg;
		unsigned char val;
	} saa7115_reg;
	struct v4l2_capability vcap; /* list_cap */
	struct v4l2_input vin;       /* list_inputs */
	int input;                   /* set_input/get_input */
	v4l2_std_id std;             /* get_std/set_std */
	struct v4l2_frequency vf;    /* get_freq/set_freq */
	struct v4l2_standard vs;     /* list_std */
	struct msp_matrix mspm;      /* set_io */

		if (argc==1) {
			usage();
			return 0;
		}
	while (1) {
		int option_index = 0;
		static struct option long_options[] = {
			{"help", 0, 0, 0},
			{"all", 0, 0, 0},
			{"device", 1, 0, 0},
			{"list-codec-params", 0, 0, 0},
			{"set-codec-params", 1, 0, 0},
			{"get-format", 0, 0, 0},
			{"set-format", 1, 0, 0},
			{"list-registers", 0, 0, 0},
			{"set-registers", 1, 0, 0},
			{"list-capability", 0, 0, 0},
			{"list-inputs", 0, 0, 0},
			{"get-input", 0, 0, 0},
			{"set-input", 1, 0, 0},
			{"get-freq", 1, 0, 0},
			{"set-freq", 1, 0, 0},
			{"list-standards", 0, 0, 0},
			{"get-standard", 0, 0, 0},
			{"set-standard", 1, 0, 0},
			{"set-io", 1, 0, 0},
			{"list-ctrls", 0, 0, 0},
			{"set-ctrl", 1, 0, 0},
			{"sync", 0, 0, 0},
			{0, 0, 0, 0}
		};
		ch = getopt_long(argc, argv, ":hc:Cd:ef:g:Gmnop:q:r:stu:v:aYy:",
				long_options, &option_index);
		if (ch == -1)
			break;
		if (ch == 0) {
			if (!strcmp(long_options[option_index].name, "help"))
				ch = 'h';
			else if (!strcmp(long_options[option_index].name, "all"))
				ch = 'a';
			else if (!strcmp(long_options[option_index].name, "device"))
				ch = 'd';
			else if (!strcmp(long_options[option_index].name, "list-codec-params"))
				ch = 'C';
			else if (!strcmp(long_options[option_index].name, "set-codec-params"))
				ch = 'c';
			else if (!strcmp(long_options[option_index].name, "get-format"))
				ch = 'e';
			else if (!strcmp(long_options[option_index].name, "set-format"))
				ch = 'f';
			else if (!strcmp(long_options[option_index].name, "list-registers"))
				ch = 'G';
			else if (!strcmp(long_options[option_index].name, "set-register"))
				ch = 'g';
			if (!strcmp(long_options[option_index].name, "sync"))
				ch = 'k';
			else if (!strcmp(long_options[option_index].name, "list-capability"))
				ch = 'm';
			else if (!strcmp(long_options[option_index].name, "list-inputs"))
				ch = 'n';
			else if (!strcmp(long_options[option_index].name, "get-input"))
				ch = 'o';
			else if (!strcmp(long_options[option_index].name, "set-input"))
				ch = 'p';
			else if (!strcmp(long_options[option_index].name, "get-freq"))
				ch = 'q';
			else if (!strcmp(long_options[option_index].name, "set-freq"))
				ch = 'r';
			else if (!strcmp(long_options[option_index].name, "list-standards"))
				ch = 's';
			else if (!strcmp(long_options[option_index].name, "get-standard"))
				ch = 't';
			else if (!strcmp(long_options[option_index].name, "set-standard"))
				ch = 'u';
			else if (!strcmp(long_options[option_index].name, "set-io"))
				ch = 'v';
			else if (!strcmp(long_options[option_index].name, "list-ctrls"))
				ch = 'Y';
			else if (!strcmp(long_options[option_index].name, "set-ctrl"))
				ch = 'y';
		}

		switch (ch) {
			case 'h':
				usage();
				return 0;
			case 'd':
				device = strdup(optarg);
				break;
			case 'a':
				/* XXX Anyway to find out if the card supports saa7115_reg */
				set_opts |= OptListCodec|OptGetFormat|OptListCtrls|
						OptListCapability|OptListInputs|OptGetInput|
						OptListStandards|OptGetStandard|OptGetFreq;
				vf.tuner = 0;
				break;
			case 'c':
				set_opts |= OptSetCodec;
				subs = optarg;
				while(*subs != '\0') {
					switch(getsubopt(&subs,subopts,&value)) {
						case SUB_ASPECT:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <aspect>\n");
								usage();
								close(fd);
								return 1;
							}
							codec.aspect = strtol(value, 0L, 0);
							set_codecs |= CAspect;
							break;
						case SUB_BITRATE_MODE:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <bitrate_mode>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.bitrate_mode = strtol(value, 0L, 0);
							set_codecs |= CBitrateMode;
							break;
						case SUB_BITRATE:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <bitrate>\n");
								usage();
								close(fd);
								return 1;
							}
							codec.bitrate = strtol(value, 0L, 0);
							set_codecs |= CBitrate;
							break;
						case SUB_BITRATE_PEAK:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <bitrate_peak>\n");
								usage();
								close(fd);
								return 1;
							}
							codec.bitrate_peak = strtol(value, 0L, 0);
							set_codecs |= CBitratePeak;
							break;
						case SUB_STEAM_TYPE:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <stream_type>\n");
								usage();
								close(fd);
								return 1;
							}
							codec.stream_type = strtol(value, 0L, 0);
							set_codecs |= CStreamType;
							break;
						case SUB_FRAMERATE:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <framerate>\n");
								usage();
								close(fd);
								return 1;
							}
							codec.framerate = strtol(value, 0L, 0);
							set_codecs |= CFreamerate;
							break;
						case SUB_FRAMESPERGOP:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <framespergop>\n");
								usage();
								close(fd);
								return 1;
							}
							codec.framespergop = strtol(value, 0L, 0);
							set_codecs |= CFramesPerGOP;
							break;
						case SUB_BFRAMES:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <bframes>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.bframes = strtol(value, 0L, 0);
							set_codecs |= CBFrames;
							break;
						case SUB_GOP_CLOSURE:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <gop_closure>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.gop_closure = strtol(value, 0L, 0);
							set_codecs |= CGOPClosure;
							break;
						case SUB_AUDIO:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <audio>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.audio_bitmask = strtol(value, 0L, 0);
							set_codecs |= CAudio;
							break;
						case SUB_DNR_MODE:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <dnr_mode>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.dnr_mode = strtol(value, 0L, 0);
							set_codecs |= CDNRMode;
							break;
						case SUB_DNR_TYPE:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <dnr_type>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.dnr_type = strtol(value, 0L, 0);
							set_codecs |= CDNRType;
							break;
						case SUB_DNR_SPATIAL:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <dnr_spatial>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.dnr_spatial = strtol(value, 0L, 0);
							set_codecs |= CDNRSpatial;
							break;
						case SUB_DNR_TEMPORAL:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <dnr_temporal>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.dnr_temporal = strtol(value, 0L, 0);
							set_codecs |= CDNRTemporal;
							break;
						case SUB_PULLDOWN:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <pulldown>\n");
								usage();
								close(fd);
								exit(1);
							}
							codec.pulldown = strtol(value, 0L, 0);
							set_codecs |= CPullDown;
							break;
						default:
							fprintf(stderr, "Invalid suboptions specified\n");
							usage();
							close(fd);
							exit(1);
							break;
						}
				}

				break;
			case 'C':
				set_opts |= OptListCodec;
				break;
			case 'e':
				set_opts |= OptGetFormat;
				break;
			case 'f':
				set_opts |= OptSetFormat;
				subs = optarg;
				while(*subs != '\0') {
					switch(getsubopt(&subs,subopts,&value) ) {
						case SUB_WIDTH:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <width>\n");
								usage();
								close(fd);
								exit(1);
							}
							vfmt.fmt.pix.width = strtol(value, 0L, 0);
							set_fmts |= FMTWidth;
							break;
						case SUB_HEIGHT:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <height>\n");
								usage();
								close(fd);
								exit(1);
							}
							vfmt.fmt.pix.height = strtol(value, 0L, 0);
							set_fmts |= FMTHeight;
							break;
						default:
							fprintf(stderr, "Invalid suboptions specified\n");
							usage();
							close(fd);
							exit(1);
							break;
					}
				}

				break;
			case 'g':
				set_opts |= OptSetRegister;
				subs = optarg;
				while(*subs != '\0') {
					switch(getsubopt(&subs,subopts,&value)) {
						case SUB_REG:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <reg>\n");
								usage();
								close(fd);
								exit(1);
							}
							saa7115_reg.reg = (unsigned char)strtol(value, 0L, 0);
							break;
						case SUB_VAL:
							if (value == NULL) {
								fprintf(stderr, "No value given to suboption <val>\n");
								usage();
								close(fd);
								exit(1);
							}
							saa7115_reg.val = (unsigned char)strtol(value, 0L, 0);
							break;
						default:
							fprintf(stderr, "Invalid suboptions specified\n");
							usage();
							close(fd);
							exit(1);
							break;
					}
				}
				break;
			case 'G':
				set_opts |= OptListRegisters;
				break;
			case 'm':
				set_opts |= OptListCapability;
				break;
			case 'k':
				set_opts |= OptFrameSync;
				break;
			case 'n':
				set_opts |= OptListInputs;
				break;
			case 'o':
				set_opts |= OptGetInput;
				break;
			case 'p':
				set_opts |= OptSetInput;
				input = strtol(optarg, 0L, 0);
				break;
			case 'q':
				set_opts |= OptGetFreq;
				vf.tuner = strtol(optarg, 0L, 0);
				break;
			case 'r':
				set_opts |= OptSetFreq;
				subs = optarg;
				while(*subs != '\0') {
					switch(getsubopt(&subs,subopts,&value) ) {
						case SUB_TUNER:
							if (value == NULL) {
								printf("No value given to suboption <tuner>\n");
								usage();
							}
							vf.tuner = strtol(value, 0L, 0);
							break;
						case SUB_FREQ:
							if (value == NULL) {
								printf("No value given to suboption <freq>\n");
								usage();
							}
							vf.frequency = strtol(value, 0L, 0);
							break;
						default:
							printf("Invalid suboptions specified\n");
							usage();
							break;
					}
				}
				break;
			case 's':
				set_opts |= OptListStandards;
				break;
			case 't':
				set_opts |= OptGetStandard;
				break;
			case 'u':
				set_opts |= OptSetStandard;
				std = strtol(optarg, 0L, 0);
				break;
			case 'v':
				set_opts |= OptSetIO;
				subs = optarg;
				while(*subs != '\0') {
					switch(getsubopt(&subs,subopts,&value) ) {
						case SUB_INPUT:
							if (value == NULL) {
								printf("No value given to suboption <input>\n");
								usage();
							}
							mspm.input = strtol(value, 0L, 0);
							break;
						case SUB_OUTPUT:
							if (value == NULL) {
								printf("No value given to suboption <output>\n");
								usage();
							}
							mspm.output = strtol(value, 0L, 0);
							break;
						default:
							printf("Invalid suboptions specified\n");
							usage();
							break;
					}
				}
				break;
			case 'Y':
				set_opts |= OptListCtrls;
				break;
			case 'y':
				set_opts |= OptSetCtrl;
				subs = optarg;
				while(*subs != '\0') {
					switch(getsubopt(&subs,subopts,&value)) {
						case SUB_BRIGHTNESS:
							ctrl.id = V4L2_CID_BRIGHTNESS;
							ctrl.value = strtol(value, 0L, 0);
							break;
						case SUB_CONTRAST:
							ctrl.id = V4L2_CID_CONTRAST;
							ctrl.value = strtol(value, 0L, 0);
							break;
						case SUB_SATURATION:
							ctrl.id = V4L2_CID_SATURATION;
							ctrl.value = strtol(value, 0L, 0);
							break;
						case SUB_HUE:
							ctrl.id = V4L2_CID_HUE;
							ctrl.value = strtol(value, 0L, 0);
							break;
						case SUB_VOLUME:
							ctrl.id = V4L2_CID_AUDIO_VOLUME;
							ctrl.value = strtol(value, 0L, 0);
							break;
						case SUB_MUTE:
							ctrl.id = V4L2_CID_AUDIO_MUTE;
							ctrl.value = strtol(value, 0L, 0);
							break;
						default:
							fprintf(stderr, "Invalid suboptions specified\n");
							usage();
							close(fd);
							exit(1);
							break;
					}
				}
				break;
			case ':':
				fprintf(stderr, "Option `%s' requires a value\n", argv[optind]);
				usage();
				return 1;
			case '?':
				fprintf(stderr, "Unknown argument `%s'\n", argv[optind]);
				usage();
				return 1;
		}
	}
	if (optind < argc) {
		printf("unknown arguments: ");
		while (optind < argc)
			printf("%s ", argv[optind++]);
		printf("\n");
		usage();
		return 1;
	}

	if ( (fd = open(device, O_RDWR) ) < 0 ) {
		fprintf(stderr, "Failed to open %s: %s\n", device, strerror(errno));
		exit(1);
	}
	free(device);

	/* Setting Opts */

	if (set_opts & OptSetCodec) {
		struct ivtv_ioctl_codec in_codec;
		printf("ioctl: IVTV_IOC_S_CODEC\n");
		if (ioctl(fd, IVTV_IOC_G_CODEC, &in_codec) < 0)
			fprintf(stderr, "ioctl: IVTV_IOC_G_CODEC failed\n");
		else {
			if (set_codecs & CAspect)       in_codec.aspect       = codec.aspect;
			if (set_codecs & CBitrateMode)  in_codec.bitrate_mode = codec.bitrate_mode;
			if (set_codecs & CBitrate)      in_codec.bitrate      = codec.bitrate;
			if (set_codecs & CBitratePeak)  in_codec.bitrate_peak = codec.bitrate_peak;
			if (set_codecs & CStreamType)   in_codec.stream_type  = codec.stream_type;
			if (set_codecs & CFreamerate)   in_codec.framerate    = codec.framerate;
			if (set_codecs & CFramesPerGOP) in_codec.framespergop = codec.framespergop;
			if (set_codecs & CBFrames)      in_codec.bframes      = codec.bframes;
			if (set_codecs & CGOPClosure)   in_codec.gop_closure  = codec.gop_closure;
			if (set_codecs & CAudio)        in_codec.audio_bitmask= codec.audio_bitmask;
			if (set_codecs & CDNRMode)      in_codec.dnr_mode     = codec.dnr_mode;
			if (set_codecs & CDNRType)      in_codec.dnr_type     = codec.dnr_type;
			if (set_codecs & CDNRSpatial)   in_codec.dnr_spatial  = codec.dnr_spatial;
			if (set_codecs & CDNRTemporal)  in_codec.dnr_temporal = codec.dnr_temporal;
			if (set_codecs & CPullDown)     in_codec.pulldown     = codec.pulldown;
			if (ioctl(fd, IVTV_IOC_S_CODEC, &in_codec) < 0)
				fprintf(stderr, "ioctl: IVTV_IOC_S_CODEC failed\n");
			else
				printf("ioctl: IVTV_IOC_S_CODEC ok\n");
		}
	}

	if (set_opts & OptSetFormat) {
		struct v4l2_format in_vfmt;
		printf("ioctl: VIDIOC_S_FMT\n");
		if (ioctl(fd, VIDIOC_G_FMT, &in_vfmt) < 0)
			fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n");
		else {
			printf("\tBefore:\n");
			if (printfmt(in_vfmt) != 0)
				fprintf(stderr, "error printing result\n");
			if (set_fmts & FMTWidth)  in_vfmt.fmt.pix.width  = vfmt.fmt.pix.width;
			if (set_fmts & FMTHeight) in_vfmt.fmt.pix.height = vfmt.fmt.pix.height;
			if (ioctl(fd, VIDIOC_S_FMT, &in_vfmt) < 0)
				fprintf(stderr, "ioctl: VIDIOC_S_FMT failed\n");
			if (ioctl(fd, VIDIOC_G_FMT, &vfmt) < 0)
				fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n");
			else {
				printf("\n\tAfter:\n");
				if (printfmt(vfmt) != 0)
					fprintf(stderr, "error printing result\n");
			}
		}
	}

	if (set_opts & OptSetRegister) {
		printf("ioctl: SAA7115_SET_REG\n");
		printf("set SAA7115 register 0x%02X to 0x%02X\n", saa7115_reg.reg, saa7115_reg.val);
		if (ioctl(fd, SAA7115_SET_REG, &saa7115_reg) < 0)
			fprintf(stderr, "ioctl: SAA7115_SET_REG failed\n");
		else
			printf("ioctl: SAA7115_SET_REG ok\n");
	}

	if (set_opts & OptSetInput) {
		printf("ioctl: VIDIOC_S_INPUT\n");
		if (ioctl(fd, VIDIOC_S_INPUT, &input) < 0)
			fprintf(stderr, "ioctl: VIDIOC_S_INPUT failed\n");
		else
			printf("Input set to %d\n", input);
	}

	if (set_opts & OptSetFreq) {
		printf("ioctl: VIDIOC_S_FREQUENCY\n");
		if (ioctl(fd, VIDIOC_S_FREQUENCY, &vf) < 0)
			fprintf(stderr, "ioctl: VIDIOC_S_FREQUENCY failed\n");
		else
			printf("Frequency set to %d\n", vf.frequency);
	}

	if (set_opts & OptSetStandard) {
		printf("ioctl: VIDIOC_S_STD\n");
		if (ioctl(fd, VIDIOC_S_STD, &std) < 0)
			fprintf(stderr, "ioctl: VIDIOC_S_STD failed\n");
		else
			printf("Standard set to %08llx\n", std);
	}

	if (set_opts & OptSetIO) {
		printf("ioctl: MSP_SET_MATRIX\n");
		if ( (mspm.input < 9) & (mspm.output < 3) ) {
			if (ioctl(fd, MSP_SET_MATRIX, &mspm) < 0)
				fprintf(stderr, "ioctl: MSP_SET_MATRIX failed\n");
		}
		else
			fprintf(stderr, "Invalid input/output setting %d/%d\n", mspm.input, mspm.output);
	}

	if (set_opts & OptSetCtrl) {
		printf("ioctl: VIDIOC_S_CTRL\n");
		if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) < 0)
			fprintf(stderr, "ioctl: VIDIOC_S_CTRL failed\n");
		else
			printf("ioctl: VIDIOC_S_CTRL success\n");
	}

	/* informational opts */

	if (set_opts & OptListCodec) {
		printf("ioctl: IVTV_IOC_G_CODEC\n");
		if (ioctl(fd, IVTV_IOC_G_CODEC, &codec) < 0)
			fprintf(stderr, "ioctl: IVTV_IOC_G_CODEC failed\n");
		else {
			printf("Codec parameters\n");
			printf("aspect      : %d\n", codec.aspect);
			printf("audio       : 0x%04x\n", codec.audio_bitmask);
			printf("bframes     : %d\n", codec.bframes);
			printf("bitrate_mode: %d\n", codec.bitrate_mode);
			printf("bitrate     : %d\n", codec.bitrate);
			printf("bitrate_peak: %d\n", codec.bitrate_peak);
			printf("dnr_mode    : %d\n", codec.dnr_mode);
			printf("dnr_spatial : %d\n", codec.dnr_spatial);
			printf("dnr_temporal: %d\n", codec.dnr_temporal);
			printf("dnr_type    : %d\n", codec.dnr_type);
			printf("framerate   : %d\n", codec.framerate);
			printf("framespergop: %d\n", codec.framespergop);
			printf("gop_closure : %d\n", codec.gop_closure);
			printf("pulldown    : %d\n", codec.pulldown);
			printf("stream_type : %d\n", codec.stream_type);
		}
	}

	if (set_opts & OptGetFormat) {
		printf("ioctl: VIDIOC_G_FMT\n");
		if (ioctl(fd, VIDIOC_G_FMT, &vfmt) < 0)
			fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n");
		else
			if (printfmt(vfmt) != 0)
				fprintf(stderr, "error printing result\n");
	}

	if (set_opts & OptListRegisters) {
		printf("ioctl: SAA7115_GET_REG\n");
		for(i=0; i<256; i++) {
			saa7115_reg.reg=(unsigned char)i;
			if(ioctl(fd, SAA7115_GET_REG, &saa7115_reg) < 0)
				fprintf(stderr,"ioctl: SAA7115_GET_REG failed\n");
			else {
				if ((i&0xf)==0) printf("\n%04x: ",i);
				printf("%02x ", saa7115_reg.val);
			}
		}
		printf("\n");
	}

	if (set_opts & OptListCapability) {
		printf("ioctl: VIDIOC_QUERYCAP\n");
		if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) < 0)
			fprintf(stderr, "ioctl: VIDIOC_QUERYCAP failed\n");
		else {
			printf("\tDriver name   : %s\n", vcap.driver);
			printf("\tCard type     : %s\n", vcap.card);
			printf("\tBus info      : %s\n", vcap.bus_info);
			printf("\tDriver version: %d\n", vcap.version);
			printf("\tCapabilities  : 0x%08X\n", vcap.capabilities);
		}
	}

	if (set_opts & OptFrameSync) {
		printf("ioctl: IVTV_IOC_FRAMESYNC\n");
    struct ivtv_ioctl_framesync frameinfo;

    for (;;) {
      if (ioctl(fd, IVTV_IOC_FRAMESYNC, &frameinfo) < 0) {
        fprintf(stderr, "ioctl: IVTV_IOC_FRAMESYNC failed\n");
        break;
      } else {
        struct timeval tv;
        gettimeofday(&tv, NULL);
        double timestamp = (double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0);

        char ptsstr[64];
        char scrstr[64];
        pts_to_string(ptsstr, frameinfo.pts);
        pts_to_string(scrstr, frameinfo.scr);
        printf("%10.6f: pts %-20s, scr %-20s, %d frames\n", timestamp, ptsstr, scrstr, frameinfo.frame);
      }
    }
	}

	if (set_opts & OptListInputs) {
		vin.index = 0;
		printf("ioctl: VIDIOC_ENUMINPUT\n");
		while (ioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) {
			printf("\tName    : %s\n", vin.name);
			printf("\tType    : 0x%08X\n", vin.type);
			printf("\tAudioset: 0x%08X\n", vin.audioset);
			printf("\tTuner   : 0x%08X\n", vin.tuner);
			printf("\tStandard: 0x%16"PRIX64" ( ", vin.std);
			if (vin.std&0x000FFF) printf("PAL ");		// hack
			if (vin.std&0x00F000) printf("NTSC ");		// hack
			if (vin.std&0x7F0000) printf("SECAM ");		// hack
			printf(")\n");
			printf("\tStatus  : %d\n", vin.status);
			vin.index++;
		}
	}

	if (set_opts & OptGetInput) {
		printf("ioctl: VIDIOC_G_INPUT\n");
		if (ioctl(fd, VIDIOC_G_INPUT, &input) < 0)
			fprintf(stderr, "ioctl: VIDIOC_G_INPUT failed\n");
		else
			printf("Input = %d\n", input);
	}

	if (set_opts & OptGetFreq) {
		printf("ioctl: VIDIOC_G_FREQUENCY\n");
		if (ioctl(fd, VIDIOC_G_FREQUENCY, &vf) < 0)
			fprintf(stderr, "ioctl: VIDIOC_G_FREQUENCY failed\n");
		else
			printf("Frequency = %d\n", vf.frequency);
	}

	if (set_opts & OptListStandards) {
		printf("ioctl: VIDIOC_ENUMSTD\n");
		vs.index = 0;
		while(ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) {
			printf("\tID          : 0x%16"PRIX64"\n", vs.id);
			printf("\tName        : %s\n", vs.name);
			printf("\tFrame period: %d/%d\n",
				vs.frameperiod.numerator, vs.frameperiod.denominator);
			printf("\tFrame lines : %d\n", vs.framelines);
			vs.index++;
		}
	}

	if (set_opts & OptGetStandard) {
		printf("ioctl: VIDIOC_G_STD\n");
		if (ioctl(fd, VIDIOC_G_STD, &std) < 0)
			fprintf(stderr, "ioctl: VIDIOC_G_STD failed\n");
		else
			printf("Standard = 0x%08llx\n", std);
	}

	if (set_opts & OptListCtrls) {
		struct v4l2_queryctrl queryctrl;
		printf("ioctl: VIDIOC_QUERYCTRL\n");
		for (queryctrl.id = V4L2_CID_BASE;
			 queryctrl.id < V4L2_CID_LASTP1;
			 queryctrl.id++) {
			if (ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl) == 0) {
				if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
					continue;
				ctrl.id = queryctrl.id;
				if (ioctl(fd, VIDIOC_G_CTRL, &ctrl) == 0)
					printf("%s = %d\n", queryctrl.name, ctrl.value);
				else
					printf("error getting ctrl %s\n", queryctrl.name);
			}
			else if (errno == EINVAL)
				continue;
			else {
				perror("VIDIOC_QUERYCTRL");
				exit(1);
			}
		}
	}

	close(fd);
	exit(0);
}
