/*

dvbstream - RTP-ize a DVB transport stream.
(C) Dave Chapman <dave@dchapman.com> 2001, 2002.
(C) Terry Hardie <terryh@orcas.net> 2009 - 2011.

Changelog:

0.8.2:
- Made -cr (code rate) work for DVB-S
- Added -m switch to set modulation type for DVB-S/S2
- Added -V switch to turn off voltage output (useful for amateur radio DVB-S on 1.3GHz)
- Added ncurses detection for terminal resize
- Fixed huged memory usage and crash if disk can't keep up on high bitrate stream.

0.8.1:
- Removed STDOUT option. Will add it back later when I clean up the curses implementation
- ncurses cleanup

The following addtions made by Terry Hardie:
- DVB-S2 support
- Multi LNB type support
- DiSeqC positioner support
- Continuity checking
- Multi file splitting (on size or continuity error)
- curses interface
- Multi-threaded high performance file writer (Tested to 80Mbit streams)

The latest version can be found at http://www.orcas.net/dvbstream

Copyright notice:

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., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#define VERSION "0.8.2"

// Linux includes:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <resolv.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <values.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <pthread.h>
#ifdef USE_CURSES
#include <curses.h>
#else
#define wprintw(window, format, args...) printf(format, ## args)
#define mvwprintw(pStatusWin, arg...) NULL
#define WINDOW void
#define wrefresh(window) NULL
#define refresh(window) NULL
#define endwin(window) NULL
#define newwin(window, a, b, c) NULL
#define scrollok(a, b) NULL
#define LINES 25
#define COLS 80
#endif

// DVB includes:
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>

#include "rtp.h"
#include "mpegtools/transform.h"
#include "mpegtools/remux.h"

#include "tune.h"

// The default telnet port.
#define DEFAULT_PORT 12345

#define USAGE "\nUSAGE: dvbstream tpid1 tpid2 tpid3 .. tpid8\n\n"
#define PACKET_SIZE 188

// How often (in seconds) to update the "now" variable
#define ALARM_TIME 5

/* Thanks to Giancarlo Baracchino for this fix */
#define MTU 1500
#define IP_HEADER_SIZE 20
#define UDP_HEADER_SIZE 8
#define RTP_HEADER_SIZE 12

#define LARGE_BUF_SIZE (1 * 1024 * 1024)

#define MAX_RTP_SIZE (MTU-IP_HEADER_SIZE-UDP_HEADER_SIZE-RTP_HEADER_SIZE)

// There seems to be a limit of 16 simultaneous filters in the driver
#define MAX_CHANNELS 16

#define writes(f,x) write((f),(x),strlen(x))

/* Signal handling code shamelessly copied from VDR by Klaus Schmidinger
- see http://www.cadsoft.de/people/kls/vdr/index.htm */

char* frontenddev[4]={"/dev/dvb/adapter0/frontend0","/dev/dvb/adapter1/frontend0","/dev/dvb/adapter2/frontend0","/dev/dvb/adapter3/frontend0"};
char* dvrdev[4]={"/dev/dvb/adapter0/dvr0","/dev/dvb/adapter1/dvr0","/dev/dvb/adapter2/dvr0","/dev/dvb/adapter3/dvr0"};
char* demuxdev[4]={"/dev/dvb/adapter0/demux0","/dev/dvb/adapter1/demux0","/dev/dvb/adapter2/demux0","/dev/dvb/adapter3/demux0"};

WINDOW *pMainWindow;
WINDOW *pStatusWin;

int card=0;
long now;
long real_start_time;
int Interrupted=0;
fe_spectral_inversion_t specInv=INVERSION_AUTO;
int tone=0;
fe_modulation_t modulation=CONSTELLATION_DEFAULT;
fe_transmit_mode_t TransmissionMode=TRANSMISSION_MODE_DEFAULT;
fe_bandwidth_t bandWidth=BANDWIDTH_DEFAULT;
fe_guard_interval_t guardInterval=GUARD_INTERVAL_DEFAULT;
fe_code_rate_t HP_CodeRate=HP_CODERATE_DEFAULT, LP_CodeRate=LP_CODERATE_DEFAULT;
fe_hierarchy_t hier=HIERARCHY_DEFAULT;
unsigned char diseqc=0;
char pol=0;
int streamtype = RTP;
int nCheckingCont = 0;
int nDropNullPid = 0;
int nTotalContErrors = 0;
int nPacketsSinceLastFileChange = 0;
unsigned long long nTotalPackets = 0ULL;

typedef struct {
	char *filename;
	int fd;
	int pids[MAX_CHANNELS];
	int num;
	int pid_cnt;
	int nCurrentSeq;
	long start_time; // in seconds
	long end_time;   // in seconds
	int socket;
	struct rtpheader hdr;
	struct sockaddr_in sOut;
	unsigned char buf[MTU];
	unsigned char net[20];
	int pos;
	int port;
} pids_map_t;

pids_map_t *pids_map;
int map_cnt;



int open_fe(int* fd_frontend) {
	if((*fd_frontend = open(frontenddev[card],O_RDWR | O_NONBLOCK)) < 0){
		wprintw(pMainWindow,"FRONTEND DEVICE:  - Error %d(%s)\n", errno, strerror(errno));
		refresh();
		return -1;
	}
	return 1;
}

static void SignalHandler(int signum) {
	struct timeval tv;

	if (signum == SIGALRM) {
		gettimeofday(&tv,(struct timezone*) NULL);
		now=tv.tv_sec-real_start_time;
		alarm(ALARM_TIME);
	} else if (signum != SIGPIPE) {
		Interrupted=signum;
	}
	signal(signum,SignalHandler);
}

long getmsec() {
	struct timeval tv;
	gettimeofday(&tv,(struct timezone*) NULL);
	return(tv.tv_sec%1000000)*1000 + tv.tv_usec/1000;
}


void set_ts_filt(int fd,uint16_t pid, dmx_pes_type_t pestype)
{
	struct dmx_pes_filter_params pesFilterParams;

	//wprintw(pMainWindow,"Setting filter for PID %d\n",pid);
	//refresh();
	pesFilterParams.pid     = pid;
	pesFilterParams.input   = DMX_IN_FRONTEND;
	pesFilterParams.output  = DMX_OUT_TS_TAP;
	pesFilterParams.pes_type = pestype;
	pesFilterParams.flags   = DMX_IMMEDIATE_START;

	if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0)  {
		wprintw(pMainWindow,"Failed setting filter for pid %i: ",pid);
		wprintw(pMainWindow,"DMX SET PES FILTER - Error %d(%s)\n", errno, strerror(errno));
		refresh();
	}
}

void make_nonblock(int f) {
	int oldflags;

	if ((oldflags=fcntl(f,F_GETFL,0)) < 0) {
		wprintw(pMainWindow,"F_GETFL - Error %d(%s)\n", errno, strerror(errno));
		refresh();
	}
	oldflags|=O_NONBLOCK;
	if (fcntl(f,F_SETFL,oldflags) < 0) {
		wprintw(pMainWindow,"F_SETFL - Error %d(%s)\n", errno, strerror(errno));
		refresh();
	}
}

typedef enum {STREAM_ON,STREAM_OFF} state_t;


int socketIn, ns;
int pids[MAX_CHANNELS];
int pestypes[MAX_CHANNELS];
unsigned char hi_mappids[8192];
unsigned char lo_mappids[8192];
int fd_frontend;
int pid,pid2;
int connectionOpen;
int fromlen;
char hostname[64];
char in_ch;
struct hostent *hp;
struct sockaddr_in name, fsin;
int ReUseAddr=1;
int oldflags;
int npids = 0;
unsigned long long nFileSizeRotate = 0ULL;
int fd[MAX_CHANNELS];
int to_stdout = 0; /* to stdout instead of rtp stream */

/* rtp */
struct rtpheader hdr;
struct sockaddr_in sOut;
int socketOut;

ipack pa, pv;

#define IPACKS 2048
#define TS_SIZE 188
#define IN_SIZE TS_SIZE

pthread_mutex_t muDataToFile;
pthread_cond_t muconDataToFile;

#define MAX_EXCHANGE_ENTRIES 204800

struct EXCHANGE_BUFFER {
	int nEntries;
	unsigned char pszBuffer[MAX_EXCHANGE_ENTRIES][TS_SIZE];
};

struct EXCHANGE_BUFFER *pExchangeBuffer = NULL;
int nGlobalHighWaterMark = 0;
int nGlobalTEI = 0;

struct MESSAGE {
	char szMessage[128];
	struct MESSAGE *pNext;
};

struct MESSAGE *pMessageExchange = NULL;


void *FileWriterFunc(void *pszArg) {
	struct EXCHANGE_BUFFER *pszLocalBuffer;

	unsigned char nCurrentCont[8192];
	int i;

	/* Initialise PID map */
	for (i=0;i<8192;i++) {
		nCurrentCont[i] = 0xff;
	}
	int counter;
	int nHighWaterMark = 0;
	int nMessages = 0;
	int nTEI = 0;
	struct MESSAGE *pNewMessage, *pMessageHead = NULL, *pMessageTail = NULL;
	while (1) {
		pthread_mutex_lock(&muDataToFile);
		pthread_cond_wait(&muconDataToFile, &muDataToFile);
		pszLocalBuffer = pExchangeBuffer;
		pExchangeBuffer = NULL;
		if (NULL == pMessageExchange) {
			pMessageExchange = pMessageHead;
			pMessageHead = NULL;
			pMessageTail = NULL;
			nMessages = 0;
		}
		nGlobalTEI = nTEI;
		nGlobalHighWaterMark = nHighWaterMark;
		pthread_mutex_unlock(&muDataToFile);
		if (pszLocalBuffer != NULL) {
			if (pszLocalBuffer->nEntries > nHighWaterMark) {
				nHighWaterMark = pszLocalBuffer->nEntries;
			}
			for (counter = 0; counter < pszLocalBuffer->nEntries; counter++) {
				unsigned char *buf = pszLocalBuffer->pszBuffer[counter];
				if(buf[0] == 0x47) {
					int pid, i, j, cont, tei, conterror = 0;

					pid = ((buf[1] & 0x1f) << 8) | buf[2];
					cont = buf[3] & 0xf;
					tei = (buf[1] & 0x80) >> 7;
					if (tei) {
						nTEI++;
						continue;
					}
					if (nCurrentCont[pid] == 0xff) {
						nCurrentCont[pid] = cont;
						pNewMessage = malloc(sizeof(struct MESSAGE));
						pNewMessage->pNext = NULL;
						sprintf(pNewMessage->szMessage, "Set first continuity for pid %d to %d\n", pid, cont);
						if (pMessageHead == NULL) {
							pMessageHead = pNewMessage;
							pMessageTail = pNewMessage;
						} else {
							pMessageTail->pNext = pNewMessage;
							pMessageTail = pNewMessage;
						}
						nMessages++;
					} else {
						if (((nCurrentCont[pid] + 1) % 0x10) != cont && ((nCurrentCont[pid]) % 0x10) != cont&& pid != 0x1fff) { // Aparently getting the same cont in a row is OK.
							// Continuity error
							pNewMessage = malloc(sizeof(struct MESSAGE));
							pNewMessage->pNext = NULL;
							sprintf(pNewMessage->szMessage, "Continuity error on PID %d(0x%04x). Jump from %d to %d\n", pid, pid, nCurrentCont[pid], cont);
							if (pMessageHead == NULL) {
								pMessageHead = pNewMessage;
								pMessageTail = pNewMessage;
							} else {
								pMessageTail->pNext = pNewMessage;
								pMessageTail = pNewMessage;
							}
							nMessages++;
							conterror = 1;
							nTotalContErrors++;
						}
						nCurrentCont[pid] = cont;
					}
					nPacketsSinceLastFileChange++;
					if (pid != 0x1fff || (pid == 0x1fff && nDropNullPid == 0)) {
						if (pids_map != NULL) {
							for (i = 0; i < map_cnt; i++) {
								if ( ((pids_map[i].start_time==-1) || (pids_map[i].start_time <= now))
									&& ((pids_map[i].end_time==-1) || (pids_map[i].end_time >= now))) {
										for (j = 0; j < MAX_CHANNELS; j++) {
											if (pids_map[i].pids[j] == pid || pids_map[i].pids[j] == 8192) {
												errno = 0;
												if ((conterror && nCheckingCont) || (nCheckingCont && nFileSizeRotate != 0 && nPacketsSinceLastFileChange * 188ULL > nFileSizeRotate)) {
													FILE *f;

													if (nPacketsSinceLastFileChange > 10000) {
														pids_map[i].nCurrentSeq++;
														char *pszTempFilename = malloc(strlen(pids_map[i].filename) + 5);
														nPacketsSinceLastFileChange = 0;
														sprintf(pszTempFilename, pids_map[i].filename, pids_map[i].nCurrentSeq);
														pNewMessage = malloc(sizeof(struct MESSAGE));
														pNewMessage->pNext = NULL;
														sprintf(pNewMessage->szMessage, "Closing old file and opening file %s\n", pszTempFilename);
														if (pMessageHead == NULL) {
															pMessageHead = pNewMessage;
															pMessageTail = pNewMessage;
														} else {
															pMessageTail->pNext = pNewMessage;
															pMessageTail = pNewMessage;
														}
														nMessages++;
														close(pids_map[i].fd);
														f = fopen(pszTempFilename, "w+b");
														free(pszTempFilename);
														pids_map[i].fd = fileno(f);
														make_nonblock(pids_map[i].fd);
													} else {
														char *pszTempFilename = malloc(strlen(pids_map[i].filename) + 5);
														sprintf(pszTempFilename, pids_map[i].filename, pids_map[i].nCurrentSeq);
														nPacketsSinceLastFileChange = 0;
														lseek(pids_map[i].fd, 0, SEEK_SET);
														pNewMessage = malloc(sizeof(struct MESSAGE));
														pNewMessage->pNext = NULL;
														sprintf(pNewMessage->szMessage, "Truncating file %s\n", pszTempFilename);
														if (pMessageHead == NULL) {
															pMessageHead = pNewMessage;
															pMessageTail = pNewMessage;
														} else {
															pMessageTail->pNext = pNewMessage;
															pMessageTail = pNewMessage;
														}
														nMessages++;
														free(pszTempFilename);
													}
												}
												write(pids_map[i].fd, pszLocalBuffer->pszBuffer[counter], TS_SIZE);

												//write(pids_map[i].fd, buf, TS_SIZE);
											}
										}
								}
							}
						}
					}
				} else {
					pNewMessage = malloc(sizeof(struct MESSAGE));
					pNewMessage->pNext = NULL;
					sprintf(pNewMessage->szMessage, "NON 0X47\n");
					if (pMessageHead == NULL) {
						pMessageHead = pNewMessage;
						pMessageTail = pNewMessage;
					} else {
						pMessageTail->pNext = pNewMessage;
						pMessageTail = pNewMessage;
					}
					nMessages++;
				}
			}
		}
		free(pszLocalBuffer);
	}
	pthread_exit(NULL);
}


int process_telnet() {
	char cmd[1024];
	int cmd_i=0;
	int i;
	char* ch;
	dmx_pes_type_t pestype;
	unsigned long freq=0;
	unsigned long srate=0;

	/* Open a new telnet session if a client is trying to connect */
	if (ns==-1) {
		if ((ns = accept(socketIn, (struct sockaddr *)&fsin, &fromlen)) > 0) {
			make_nonblock(ns);
			cmd_i=0;
			cmd[0]=0;
			printf("Opened connection\n");
			writes(ns,"220-DVBSTREAM - ");
			writes(ns,hostname);
			writes(ns,"\r\nDONE\r\n");
			connectionOpen=1;
		}
	}

	/* If a telnet session is open, receive and process any input */
	if (connectionOpen) {
		/* Read in at most a line of text - any ctrl character ends the line */
		while (read(ns,&in_ch,1)>0) {
			if (in_ch < 32) break;
			/* Prevent buffer overflows */
			if (cmd_i < 1024-1) {
				cmd[cmd_i++]=in_ch;
				cmd[cmd_i]=0;
			}
		}
		if (in_ch > 0) {
			if (cmd_i > 0) {
				printf("CMD: \"%s\"\n",cmd);
				if (strcasecmp(cmd,"QUIT")==0) {
					writes(ns,"DONE\r\n");
					close(ns);
					ns=-1;
					connectionOpen=0;
					printf("Closed connection\n");
				} else if (strcasecmp(cmd,"STOP")==0) {
					writes(ns,"STOP\n");
					for (i=0;i<npids;i++) {
						if (ioctl(fd[i], DMX_STOP) < 0)  {
							wprintw(pMainWindow,"DMX_STOP - Error %d(%s)\n", errno, strerror(errno));
							wrefresh(pMainWindow);
						}
					}
					for (i=0;i<8192;i++) {
						hi_mappids[i]=(i >> 8);
						lo_mappids[i]=(i&0xff);
					}
					writes(ns,"DONE\r\n");
				} else if (strncasecmp(cmd,"ADD",3)==0) {
					i=4;
					if ((cmd[3]=='V') || (cmd[3]=='v')) pestype=DMX_PES_VIDEO;
					else if ((cmd[3]=='A') || (cmd[3]=='a')) pestype=DMX_PES_AUDIO;
					else if ((cmd[3]=='T') || (cmd[3]=='t')) pestype=DMX_PES_TELETEXT;
					else { pestype=DMX_PES_OTHER; i=3; }
					while (cmd[i]==' ') i++;
					if ((ch=(char*)strstr(&cmd[i],":"))!=NULL) {
						pid2=atoi(&ch[1]);
						ch[0]=0;
					} else {
						pid2=-1;
					}
					pid=atoi(&cmd[i]);
					if (pid) {
						if (npids == MAX_CHANNELS) {
							wprintw(pMainWindow,"\nsorry, you can only set up to 8 filters.\n\n");
							wrefresh(pMainWindow);
							endwin();
							return(-1);
						} else {
							pestypes[npids]=pestype;
							pestype=DMX_PES_OTHER;
							pids[npids]=pid;
							if (pid2!=-1) {
								hi_mappids[pid]=pid2>>8;
								lo_mappids[pid]=pid2&0xff;
								wprintw(pMainWindow,"Mapping %d to %d\n",pid,pid2);
								wrefresh(pMainWindow);
							}

							if((fd[npids] = open(demuxdev[card],O_RDWR|O_NONBLOCK)) < 0){
								wprintw(pMainWindow,"FD %i: ",i);
								wprintw(pMainWindow,"DEMUX DEVICE:  - Error %d(%s)\n", errno, strerror(errno));
								wrefresh(pMainWindow);
							} else {
								set_ts_filt(fd[npids],pids[npids],pestypes[npids]);
								npids++;
							}
						}
					}
					writes(ns,"DONE\r\n");
				} else if (strcasecmp(cmd,"START")==0) {
					writes(ns,"START\n");
					for (i=0;i<npids;i++) {
						set_ts_filt(fd[i],pids[i],pestypes[i]);
					}
					writes(ns,"DONE\r\n");
				} else if (strncasecmp(cmd,"TUNE",4)==0) {
					for (i=0;i<8192;i++) {
						hi_mappids[i]=(i >> 8);
						lo_mappids[i]=(i&0xff);
					}
					for (i=0;i<npids;i++) {
						if (ioctl(fd[i], DMX_STOP) < 0)  {
							wprintw(pMainWindow,"DMX_STOP - Error %d(%s)\n", errno, strerror(errno));
							wrefresh(pMainWindow);
							close(fd[i]);
						}
					}
					npids=0;
					i=4;
					while (cmd[i]==' ') i++;
					freq=atoi(&cmd[i]);
					while ((cmd[i]!=' ') && (cmd[i]!=0)) i++;
					if (cmd[i]!=0) {
						while (cmd[i]==' ') i++;
						pol=cmd[i];
						while ((cmd[i]!=' ') && (cmd[i]!=0)) i++;
						if (cmd[i]!=0) {
							while (cmd[i]==' ') i++;
							srate=atoi(&cmd[i])*1000UL;
							wprintw(pMainWindow,"Tuning to %ld,%ld,%c\n",freq,srate,pol);
							wrefresh(pMainWindow);
							tune_it(pMainWindow, fd_frontend,freq,srate,pol,tone,specInv,diseqc,modulation,HP_CodeRate,TransmissionMode,guardInterval,bandWidth, LP_CodeRate, hier, SYS_DVBS2, 0, 0, 0, 0, PILOT_AUTO, 0, 0);
						}
					}
				}
				cmd_i=0;
				cmd[0]=0;
				writes(ns,"DONE\r\n");
			}
		}
	}
	return(0);
}


/* The output routine for sending a PS */
void my_write_out(uint8_t *buf, int count,void  *p)
{
	/* to fix: change this buffer size and check for overflow */
	static uint8_t out_buffer[1000000];
	static int out_buffer_n=0;
	int i;

	if (to_stdout) {
		/* This one is easy. */

		write(STDOUT_FILENO, buf, count);
	} else { /* We are streaming it. */
		/* Copy data to write to the end of out_buffer */

		memcpy(&out_buffer[out_buffer_n],buf,count);
		out_buffer_n+=count;

		/* Send as many full packets as possible */

		i=0;
		while ((i + MAX_RTP_SIZE) < out_buffer_n) {
			hdr.timestamp = getmsec()*90;
			sendrtp2(socketOut,&sOut,&hdr,&out_buffer[i],MAX_RTP_SIZE);
			i+=MAX_RTP_SIZE;
		}

		/* Move whatever data is left to the start of the buffer */

		memmove(&out_buffer[0],&out_buffer[i],out_buffer_n-i);
		out_buffer_n-=i;
	}
}

void my_ts_to_ps( uint8_t* buf, uint16_t pida, uint16_t pidv)
{
	uint16_t pid;
	ipack *p;
	uint8_t off = 0;

	pid = get_pid(buf+1);
	if (!(buf[3]&0x10)) // no payload?
		return;
	if (pid == pidv){
		p = &pv;
	} else {
		if (pid == pida){
			p = &pa;
		} else return;
	}

	if ( buf[1]&0x40) {
		if (p->plength == MMAX_PLENGTH-6){
			p->plength = p->found-6;
			p->found = 0;
			send_ipack(p);
			reset_ipack(p);
		}
	}

	if ( buf[3] & 0x20) {  // adaptation field?
		off = buf[4] + 1;
	}

	instant_repack(buf+4+off, TS_SIZE-4-off, p);
}


int main(int argc, char **argv)
{
	//  state_t state=STREAM_OFF;
#ifdef ENABLE_TELNET
	unsigned short int port=DEFAULT_PORT;
#endif
	int fd_dvr;
	int i,j;
	unsigned char buf[MTU];
	unsigned char *pszLargeBuf;
	int nBytesLeftOver = 0;
	struct pollfd pfds[2];  // DVR device and Telnet connection
	unsigned int secs = -1;
	unsigned long freq=0;
	unsigned long srate=0;
	int count;
	char* ch;
	dmx_pes_type_t pestype;
	int bytes_read;
	int do_analyse=0;
	int disable_power = 0;
	unsigned char* free_bytes;
	int output_type=RTP_TS;
	int64_t counts[8192];
	double f;
	long start_time=-1;
	long end_time=-1;
	int nPositioner = 0;
	int nSearch = 0;
	struct timeval tv;
	int found;
	unsigned int delivery_system = SYS_DVBS;
	int nStrength, nBER, nSNR, nUncorrected, nTEI = 0, nTotalUncorrected = 0;
	int nLNB = 0;

	/* Output: {uni,multi,broad}cast socket */
	char ipOut[20];
	int portOut;
	int ttl = 2;
	int nMove = 0;
	int nStore = 0;
	int nPilot = PILOT_AUTO;
	int nTuneCont = 0;
	struct stat stat_buf;

	pids_map = NULL;
	map_cnt = 0;

#ifdef USE_CURSES

	WINDOW *win = initscr();
	nodelay(win, 1);
	cbreak();
	noecho();
	scrollok(win, 1);
	refresh();
#else
	void *win = NULL;
#endif

	if (argc!=1) {
		int nLines = LINES / 4 < 9 ? 9 : LINES / 4;
		pStatusWin = newwin(nLines, COLS, 0, 0);
		wrefresh(pStatusWin);
		pMainWindow = newwin(LINES - nLines, COLS, nLines, 0);
		wrefresh(pMainWindow);
		scrollok(pMainWindow, 1);
	} else {
		pMainWindow = win;
	}

	wprintw(pMainWindow,"dvbstream v%s - (C) Dave Chapman 2001-2004\n", VERSION);
	wprintw(pMainWindow,"Additional features by Terry Hardie (C) 2009-2011\n");
	wprintw(pMainWindow,"Released under the GPL.\n");
	wprintw(pMainWindow,"Latest version available from http://www.orcas.net/dvbstream\n");
	wrefresh(pMainWindow);

	/* Initialise PID map */
	for (i=0;i<8192;i++) {
		hi_mappids[i]=(i >> 8);
		lo_mappids[i]=(i&0xff);
		counts[i]=0;
	}

	/* Set default IP and port */
	strcpy(ipOut,"224.0.1.2");
	portOut = 5004;

	if (argc==1) {
		wprintw(pMainWindow,"Usage: dvbtune [OPTIONS] pid1 pid2 ... pid8\n\n");
		wprintw(pMainWindow,"-i          IP multicast address\n");
		wprintw(pMainWindow,"-r          IP multicast port\n");
		wprintw(pMainWindow,"-net ip:prt IP address:port combination to be followed by pids list. Can be repeated to generate multiple RTP streams\n");
		wprintw(pMainWindow,"-o:file.ts  Stream to named file instead of network\n");
		wprintw(pMainWindow,"-oc:file%%d.ts Stream to named file, substituing %%d with a sequence number, which will be incrememted every time a continuity error is detected\n");
		wprintw(pMainWindow,"-F size[m]  Roll to next file after size. [m] can be [k,M,G]. Only works with -oc mode.\n");
		wprintw(pMainWindow,"-n secs     Stop after secs seconds\n");
		wprintw(pMainWindow,"-from n     Start saving the file previously specified with -o: syntax in n minutes time\n");
		wprintw(pMainWindow,"-to n       Stop saving the file previously specified with -o: syntax in n minutes time\n");
		wprintw(pMainWindow,"-ps         Convert stream to Program Stream format (needs exactly 2 pids)\n");
		wprintw(pMainWindow,"-v vpid     Decode video PID (full cards only)\n");
		wprintw(pMainWindow,"-a apid     Decode audio PID (full cards only)\n");
		wprintw(pMainWindow,"-d          Drop NULL pid\n");
		wprintw(pMainWindow,"-t ttpid    Decode teletext PID (full cards only)\n");
		wprintw(pMainWindow,"\nStandard tuning options:\n\n");
		wprintw(pMainWindow,"-f freq     absolute Frequency (DVB-S in Hz or DVB-T in Hz)\n");
		wprintw(pMainWindow,"            or L-band Frequency (DVB-S in Hz or DVB-T in Hz)\n");
		wprintw(pMainWindow,"-p [H,V]    Polarity (DVB-S only)\n");
		wprintw(pMainWindow,"-s N        Symbol rate (DVB-S or DVB-C)\n");
		wprintw(pMainWindow,"-S          Use DVB-S2 instead of DVB-S\n");
		wprintw(pMainWindow,"-V          Disable voltage output\n");
		wprintw(pMainWindow,"-m [Q,8]    Use QPSK or 8PSK modulation\n");
		wprintw(pMainWindow,"-T          Send 22khz tone for DVB-S\n");
		wprintw(pMainWindow,"-L [0,1]    Select LNB type. 0 = 5150/10750 LO, 1 = 9750/10600\n");
		wprintw(pMainWindow,"-P n        Pilot mode for DVB-S2. 0 = Off, 1 = On, 2 = Auto\n");
		wprintw(pMainWindow,"-Pg n       Move DiSEqC positioner to stored position n\n");
		wprintw(pMainWindow,"-Pe n       Move DiSEqC positioner n steps east\n");
		wprintw(pMainWindow,"-Pw n       Move DiSEqC positioner n steps west\n");
		wprintw(pMainWindow,"-Pfe n      Move DiSEqC positioner east, up to n steps to search for signal\n");
		wprintw(pMainWindow,"-Pfw n      Move DiSEqC positioner west, up to n steps to search for signal\n");
		wprintw(pMainWindow,"-Ps n       Store current position in position n\n");

		wprintw(pMainWindow,"\nAdvanced tuning options:\n\n");
		wprintw(pMainWindow,"-C          Tune continuously (don't give up)\n");
		wprintw(pMainWindow,"-c [0-3]    Use DVB card #[0-3]\n");
		wprintw(pMainWindow,"-D [0-4AB]  DiSEqC command (0=none)\n\n");
		wprintw(pMainWindow,"-I [0|1|2]  0=Spectrum Inversion off, 1=Spectrum Inversion on, 2=auto\n");
		wprintw(pMainWindow,"-qam X      DVB-T/C and ATSC modulation - 16%s, 32%s, 64%s, 128%s or 256%s\n",(CONSTELLATION_DEFAULT==QAM_16 ? " (default)" : ""),(CONSTELLATION_DEFAULT==QAM_32 ? " (default)" : ""),(CONSTELLATION_DEFAULT==QAM_64 ? " (default)" : ""),(CONSTELLATION_DEFAULT==QAM_128 ? " (default)" : ""),(CONSTELLATION_DEFAULT==QAM_256 ? " (default)" : ""));
#ifdef DVB_ATSC
		wprintw(pMainWindow,"-vsb X      ATSC modulation - 8, 16\n");
#endif
		wprintw(pMainWindow,"-gi N       DVB-T guard interval 1_N (N=32%s, 16%s, 8%s or 4%s)\n",(GUARD_INTERVAL_DEFAULT==GUARD_INTERVAL_1_32 ? " (default)" : ""),(GUARD_INTERVAL_DEFAULT==GUARD_INTERVAL_1_16 ? " (default)" : ""),(GUARD_INTERVAL_DEFAULT==GUARD_INTERVAL_1_8 ? " (default)" : ""),(GUARD_INTERVAL_DEFAULT==GUARD_INTERVAL_1_4 ? " (default)" : ""));
		wprintw(pMainWindow,"-cr N       DVB-T/C/S code rate. N=AUTO%s, 1_2%s, 2_3%s, 3_4%s, 5_6%s, 7_8%s, 8_9%s, 9_10%s\n",(HP_CODERATE_DEFAULT==FEC_AUTO ? " (default)" : ""),(HP_CODERATE_DEFAULT==FEC_1_2 ? " (default)" : ""),(HP_CODERATE_DEFAULT==FEC_2_3 ? " (default)" : ""),(HP_CODERATE_DEFAULT==FEC_3_4 ? " (default)" : ""),(HP_CODERATE_DEFAULT==FEC_5_6 ? " (default)" : ""),(HP_CODERATE_DEFAULT==FEC_7_8 ? " (default)" : ""),(HP_CODERATE_DEFAULT==FEC_8_9 ? " (default)" : ""),(HP_CODERATE_DEFAULT==FEC_9_10 ? " (default)" : ""));
		wprintw(pMainWindow,"-crlp N     DVB-T code rate LP. N=AUTO%s, 1_2%s, 2_3%s, 3_4%s, 5_6%s, 7_8%s\n",(LP_CODERATE_DEFAULT==FEC_AUTO ? " (default)" : ""),(LP_CODERATE_DEFAULT==FEC_1_2 ? " (default)" : ""),(LP_CODERATE_DEFAULT==FEC_2_3 ? " (default)" : ""),(LP_CODERATE_DEFAULT==FEC_3_4 ? " (default)" : ""),(LP_CODERATE_DEFAULT==FEC_5_6 ? " (default)" : ""),(LP_CODERATE_DEFAULT==FEC_7_8 ? " (default)" : ""));
		wprintw(pMainWindow,"-bw N       DVB-T bandwidth (Mhz) - N=6%s, 7%s or 8%s\n",(BANDWIDTH_DEFAULT==BANDWIDTH_6_MHZ ? " (default)" : ""),(BANDWIDTH_DEFAULT==BANDWIDTH_7_MHZ ? " (default)" : ""),(BANDWIDTH_DEFAULT==BANDWIDTH_8_MHZ ? " (default)" : ""));
		wprintw(pMainWindow,"-tm N       DVB-T transmission mode - N=2%s or 8%s\n",(TRANSMISSION_MODE_DEFAULT==TRANSMISSION_MODE_2K ? " (default)" : ""),(TRANSMISSION_MODE_DEFAULT==TRANSMISSION_MODE_8K ? " (default)" : ""));
		wrefresh(pMainWindow);
		wprintw(pMainWindow,"-hy N       DVB-T hierarchy - N=1%s, 2%s, 4%s, NONE%s or AUTO%s\n",(HIERARCHY_DEFAULT==HIERARCHY_1 ? " (default)" : ""),(HIERARCHY_DEFAULT==HIERARCHY_2 ? " (default)" : ""),(HIERARCHY_DEFAULT==HIERARCHY_4 ? " (default)" : ""),(HIERARCHY_DEFAULT==HIERARCHY_NONE ? " (default)" : ""),(HIERARCHY_DEFAULT==HIERARCHY_AUTO ? " (default)" : ""));
		wprintw(pMainWindow,"-ttl N      Sets TTL to N (default: 2) when streaming in RTP\n");
		wprintw(pMainWindow,"-rtp        Sets output type to RTP (default when using network out)\n");
		wprintw(pMainWindow,"-udp        Sets output type to UDP \n");

		wprintw(pMainWindow,"\n-analyse    Perform a simple analysis of the bitrates of the PIDs in the transport stream\n");

		wprintw(pMainWindow,"\n");
		wprintw(pMainWindow,"NOTE: Use pid1=8192 to broadcast whole TS stream from a budget card\n");
		wprintw(pMainWindow,"\n");
		wprintw(pMainWindow,"Keys available after successful tune:\n");
		wprintw(pMainWindow,"< or ,   : Pan positioner 1 step west\n");
		wprintw(pMainWindow,"> or .   : Pan positioner 1 step east\n");
		wprintw(pMainWindow,"q        : Exit cleanly\n");
		wrefresh(pMainWindow);
		endwin();
		return(-1);
	} else {
		npids=0;
		pestype=DMX_PES_OTHER;  // Default PES type
		for (i=1;i<argc;i++) {
			if (strcmp(argv[i],"-ps")==0) {
				output_type=RTP_PS;
			} else if(!strcmp(argv[i],"-rtp")) {
				streamtype = RTP;
			} else if(!strcmp(argv[i],"-udp")) {
				streamtype = UDP;
			} else if (strcmp(argv[i],"-analyse")==0) {
				do_analyse=1;
				output_type=RTP_NONE;
				if (secs==-1) { secs=10; }
			} else if (strcmp(argv[i],"-d")==0) {
				nDropNullPid = 1;
			} else if (strcmp(argv[i],"-i")==0) {
				if(pids_map != NULL) {
					wprintw(pMainWindow, "ERROR! -i and -r can't be used with -o and -net.  Use -net instead\n");
					wrefresh(pMainWindow);
					endwin();
					exit(1);
				}
				i++;
				strcpy(ipOut,argv[i]);
			} else if(strcmp(argv[i],"-auto")==0) {
				modulation = QAM_AUTO;
				TransmissionMode = TRANSMISSION_MODE_AUTO;
				guardInterval = GUARD_INTERVAL_AUTO;
				HP_CodeRate = FEC_AUTO;
				LP_CodeRate = FEC_AUTO;
				hier = HIERARCHY_AUTO;
				specInv = INVERSION_AUTO;
			}
			else if (strcmp(argv[i],"-r")==0) {
				if(pids_map != NULL) {
					wprintw(pMainWindow, "ERROR! -i and -r can't be used with -o and -net.  Use -net instead\n");
					wrefresh(pMainWindow);
					endwin();
					exit(1);
				}
				i++;
				portOut=atoi(argv[i]);
			} else if (strcmp(argv[i],"-net")==0) {
				char *tmp;
				i++;
				tmp = strchr(argv[i], ':');
				if((tmp == NULL) || (tmp - argv[i] > 19)) {
					wprintw(pMainWindow, "No valid IP:port found after -net switch, discarding\n");
					wrefresh(pMainWindow);
				} else {
					int len = (int) (tmp - argv[i]);
					char addr[20];
					int port;
					strncpy(ipOut, argv[i], len);
					ipOut[len] = 0;
					strncpy(addr, argv[i], len);
					addr[len] = 0;
					port = portOut = atoi(tmp+1);


					pids_map = (pids_map_t*) realloc(pids_map, sizeof(pids_map_t) * (map_cnt+1));
					if(pids_map != NULL) {
						map_cnt++;
						pids_map[map_cnt-1].pid_cnt = 0;
						pids_map[map_cnt-1].start_time=start_time;
						pids_map[map_cnt-1].end_time=end_time;
						for(j=0; j < MAX_CHANNELS; j++) pids_map[map_cnt-1].pids[j] = -1;
						pids_map[map_cnt-1].filename = NULL;
						strncpy(pids_map[map_cnt-1].net, addr, len);
						pids_map[map_cnt-1].net[len] = 0;
						pids_map[map_cnt-1].port = port;
						pids_map[map_cnt-1].pos = 0;

						pids_map[map_cnt-1].socket = makesocket(addr,port,ttl,&(pids_map[map_cnt-1].sOut));
						initrtp(&(pids_map[map_cnt-1].hdr),(output_type==RTP_TS ? 33 : 34), streamtype);
						output_type = MAP_TS;
					} else
						wprintw(pMainWindow, "Couldn't alloc enough entry for this %s:%d address\n", addr, port);
						wrefresh(pMainWindow);
				}
			} else if (strcmp(argv[i],"-f")==0) {
				i++;
				freq=atoi(argv[i]);
			} else if (strcmp(argv[i],"-p")==0) {
				i++;
				if (argv[i][1]==0) {
					if (tolower(argv[i][0])=='v') {
						pol='V';
					} else if (tolower(argv[i][0])=='h') {
						pol='H';
					}
				}
			}
			else if (strcmp(argv[i],"-s")==0) {
				i++;
				srate=atoi(argv[i])*1000UL;
			}
			else if (strcmp(argv[i],"-L")==0) {
				i++;
				nLNB=atoi(argv[i]);
			}
			else if (strcmp(argv[i],"-P")==0) {
				i++;
				switch (atoi(argv[i])) {
					case 0:
						nPilot = PILOT_OFF;
						wprintw(pMainWindow, "Setting DVB-S2 Pilot OFF\n");
						wrefresh(pMainWindow);
						break;
					case 1:
						nPilot = PILOT_ON;
						wprintw(pMainWindow, "Setting DVB-S2 Pilot ON\n");
						wrefresh(pMainWindow);
						break;
					case 2:
						nPilot = PILOT_AUTO;
						wprintw(pMainWindow, "Setting DVB-S2 Pilot AUTO\n");
						wrefresh(pMainWindow);
						break;
				}
			}
			else if (strcmp(argv[i],"-Pg")==0) {
				i++;
				nPositioner=atoi(argv[i]);
			}
			else if (strcmp(argv[i],"-Pe")==0) {
				i++;
				nMove=atoi(argv[i]);
			}
			else if (strcmp(argv[i],"-Pw")==0) {
				i++;
				nMove=-(atoi(argv[i]));
			}
			else if (strcmp(argv[i],"-Pfe")==0) {
				i++;
				nSearch=atoi(argv[i]);
			}
			else if (strcmp(argv[i],"-Pfw")==0) {
				i++;
				nSearch=-(atoi(argv[i]));
			}
			else if (strcmp(argv[i],"-Ps")==0) {
				i++;
				nStore=atoi(argv[i]);
			}
			else if (strcmp(argv[i],"-D")==0)
			{
				i++;
				diseqc = argv[i][0];
				if(toupper(diseqc) == 'A')
					diseqc = 'A';
				else if(toupper(diseqc) == 'B')
					diseqc = 'B';
				else if(diseqc >= '0' && diseqc <= '4') {
					diseqc=diseqc - '0';
				}
				else {
					wprintw(pMainWindow,"DiSEqC must be between 0 and 4 or A | B\n");
					wrefresh(pMainWindow);
					endwin();
					exit(-1);
				}
			} else if (strcmp(argv[i],"-I")==0) {
				i++;
				if (atoi(argv[i])==0)
					specInv = INVERSION_OFF;
				else if (atoi(argv[i])==1)
					specInv = INVERSION_ON;
				else
					specInv = INVERSION_AUTO;
			} else if (strcmp(argv[i],"-S")==0) {
				delivery_system = SYS_DVBS2;
				wprintw(pMainWindow,"Tuning DVB-S2...\n");
				wrefresh(pMainWindow);
			} else if (strcmp(argv[i],"-V")==0) {
				disable_power = 1;
				wprintw(pMainWindow,"Set power off...\n");
				wrefresh(pMainWindow);
			} else if (strcmp(argv[i],"-m")==0) {
				i++;
				if (argv[i][1]==0) {
					if (tolower(argv[i][0])=='8') {
						modulation = PSK_8;
						wprintw(pMainWindow,"Setting modulation to 8PSK...\n");
						wrefresh(pMainWindow);
					} else if (tolower(argv[i][0])=='q') {
						modulation = QPSK;
						wprintw(pMainWindow,"Setting modulation to QPSK...\n");
						wrefresh(pMainWindow);
					}
				}
			} else if (strcmp(argv[i],"-C")==0) {
				nTuneCont = 1;
			} else if (strcmp(argv[i],"-T")==0) {
				tone = 1;
			} else if (strcmp(argv[i],"-n")==0) {
				i++;
				secs=atoi(argv[i]);
			} else if (strcmp(argv[i],"-c")==0) {
				i++;
				card=atoi(argv[i]);
				if ((card < 0) || (card > 3)) {
					wprintw(pMainWindow,"ERROR: card parameter must be between 0 and 4\n");
					wrefresh(pMainWindow);
				}
			} else if (strcmp(argv[i],"-v")==0) {
				pestype=DMX_PES_VIDEO;
			} else if (strcmp(argv[i],"-a")==0) {
				pestype=DMX_PES_AUDIO;
			} else if (strcmp(argv[i],"-t")==0) {
				pestype=DMX_PES_TELETEXT;
			} else if (strcmp(argv[i],"-qam")==0) {
				i++;
				switch(atoi(argv[i])) {
		  case 16:  modulation=QAM_16; break;
		  case 32:  modulation=QAM_32; break;
		  case 64:  modulation=QAM_64; break;
		  case 128: modulation=QAM_128; break;
		  case 256: modulation=QAM_256; break;
		  default:
			  wprintw(pMainWindow,"Invalid QAM rate: %s\n",argv[i]);
			  endwin();
			  exit(0);
				}
			}
#ifdef DVB_ATSC
			else if(strcmp(argv[i],"-vsb")==0) {
				i++;
				switch(atoi(argv[i])) {
	  case 8:  modulation=VSB_8; break;
	  case 16:  modulation=VSB_16; break;
	  default:
		  wprintw(pMainWindow,"Invalid ATSC VSB modulation: %s\n",argv[i]);
		  endwin();
		  exit(0);
				}
			}
#endif
			else if (strcmp(argv[i],"-gi")==0) {
				i++;
				switch(atoi(argv[i])) {
		  case 32:  guardInterval=GUARD_INTERVAL_1_32; break;
		  case 16:  guardInterval=GUARD_INTERVAL_1_16; break;
		  case 8:   guardInterval=GUARD_INTERVAL_1_8; break;
		  case 4:   guardInterval=GUARD_INTERVAL_1_4; break;
		  default:
			  wprintw(pMainWindow,"Invalid Guard Interval: %s\n",argv[i]);
			  endwin();
			  exit(0);
				}
			} else if (strcmp(argv[i],"-tm")==0) {
				i++;
				switch(atoi(argv[i])) {
		  case 8:   TransmissionMode=TRANSMISSION_MODE_8K; break;
		  case 2:   TransmissionMode=TRANSMISSION_MODE_2K; break;
		  default:
			  wprintw(pMainWindow,"Invalid Transmission Mode: %s\n",argv[i]);
			  endwin();
			  exit(0);
				}
			} else if (strcmp(argv[i],"-bw")==0) {
				i++;
				switch(atoi(argv[i])) {
		  case 8:   bandWidth=BANDWIDTH_8_MHZ; break;
		  case 7:   bandWidth=BANDWIDTH_7_MHZ; break;
		  case 6:   bandWidth=BANDWIDTH_6_MHZ; break;
		  default:
			  wprintw(pMainWindow,"Invalid DVB-T bandwidth: %s\n",argv[i]);
			  endwin();
			  exit(0);
				}
			} else if (strcmp(argv[i],"-cr")==0) {
				i++;
				if (!strcmp(argv[i],"AUTO")) {
					HP_CodeRate=FEC_AUTO;
					wprintw(pMainWindow,"FEC set to Auto...\n");
					wrefresh(pMainWindow);
				} else if (!strcmp(argv[i],"1_2")) {
					HP_CodeRate=FEC_1_2;
					wprintw(pMainWindow,"FEC set to 1/2...\n");
					wrefresh(pMainWindow);
				} else if (!strcmp(argv[i],"2_3")) {
					HP_CodeRate=FEC_2_3;
					wprintw(pMainWindow,"FEC set to 2/3...\n");
					wrefresh(pMainWindow);
				} else if (!strcmp(argv[i],"3_4")) {
					HP_CodeRate=FEC_3_4;
					wprintw(pMainWindow,"FEC set to 3/4...\n");
					wrefresh(pMainWindow);
				} else if (!strcmp(argv[i],"5_6")) {
					HP_CodeRate=FEC_5_6;
					wprintw(pMainWindow,"FEC set to 5/6...\n");
					wrefresh(pMainWindow);
				} else if (!strcmp(argv[i],"7_8")) {
					HP_CodeRate=FEC_7_8;
					wprintw(pMainWindow,"FEC set to 7/8...\n");
					wrefresh(pMainWindow);
				} else if (!strcmp(argv[i],"8_9")) {
					HP_CodeRate=FEC_8_9;
					wprintw(pMainWindow,"FEC set to 8/9...\n");
					wrefresh(pMainWindow);
				} else if (!strcmp(argv[i],"9_10")) {
					HP_CodeRate=FEC_9_10;
					wprintw(pMainWindow,"FEC set to 9/10...\n");
					wrefresh(pMainWindow);
				} else {
					wprintw(pMainWindow,"Invalid Code Rate: %s\n",argv[i]);
					wrefresh(pMainWindow);
					endwin();
					exit(0);
				}
			} else if (strcmp(argv[i],"-crlp")==0) {
				i++;
				if (!strcmp(argv[i],"AUTO")) {
					LP_CodeRate=FEC_AUTO;
				} else if (!strcmp(argv[i],"1_2")) {
					LP_CodeRate=FEC_1_2;
				} else if (!strcmp(argv[i],"2_3")) {
					LP_CodeRate=FEC_2_3;
				} else if (!strcmp(argv[i],"3_4")) {
					LP_CodeRate=FEC_3_4;
				} else if (!strcmp(argv[i],"5_6")) {
					LP_CodeRate=FEC_5_6;
				} else if (!strcmp(argv[i],"7_8")) {
					LP_CodeRate=FEC_7_8;
				} else {
					wprintw(pMainWindow,"Invalid Code Rate LP: %s\n",argv[i]);
					wrefresh(pMainWindow);
					endwin();
					exit(0);
				}
			} else if (strcmp(argv[i],"-hier")==0) {
				i++;
				if (!strcmp(argv[i],"AUTO")) {
					hier=HIERARCHY_AUTO;
				} else if (!strcmp(argv[i],"1")) {
					hier=HIERARCHY_2;
				} else if (!strcmp(argv[i],"4")) {
					hier=HIERARCHY_4;
				} else if (!strcmp(argv[i],"NONE")) {
					hier=HIERARCHY_NONE;
				} else {
					wprintw(pMainWindow,"Invalid HIERARCHY: %s\n",argv[i]);
					wrefresh(pMainWindow);
					endwin();
					exit(0);
				}
			} else if (strcmp(argv[i],"-ttl")==0) {
				i++;
				ttl = atoi(argv[i]);
			} else if (strcmp(argv[i],"-from")==0) {
				i++;
				if (map_cnt) {
					pids_map[map_cnt-1].start_time=atoi(argv[i])*60;
				} else {
					start_time=atoi(argv[i])*60;
				}
			} else if (strcmp(argv[i],"-to")==0) {
				i++;
				if (map_cnt) {
					pids_map[map_cnt-1].end_time=atoi(argv[i])*60;
				} else {
					end_time=atoi(argv[i])*60;
					secs=end_time;
				}
			} else if (strcmp(argv[i], "-F") == 0) {
				i++;
				char szLocalSize[32];
				memcpy(szLocalSize, argv[i], strlen(argv[i]) - 1);
				szLocalSize[strlen(argv[i]) - 1] = '\0';
				nFileSizeRotate = atoi(szLocalSize);
				switch (argv[i][strlen(argv[i]) - 1]) {
					case 'G':
						nFileSizeRotate *= 1024ULL;
						// fallthrough
					case 'M':
						nFileSizeRotate *= 1024ULL;
						// fallthrough
					case 'k':
						nFileSizeRotate *= 1024ULL;
						break;
					default:
						nFileSizeRotate = atoi(argv[i]);
						break;
				}
				wprintw(pMainWindow, "Rotating file ever %llu bytes\n", nFileSizeRotate);
				wrefresh(pMainWindow);
			} else if (strstr(argv[i], "-o:")==argv[i] || strstr(argv[i], "-O:")==argv[i] || strstr(argv[i], "-oc:")==argv[i] || strstr(argv[i], "-Oc:")==argv[i]) {
				if (strlen(argv[i]) > 3) {
					char * fname;
					fname = (char *) malloc(strlen(argv[i]) + 5);
					if(fname == NULL) {
						wprintw(pMainWindow, "Couldn't alloc enough memory for this -o: entry, discarding\n");
						wrefresh(pMainWindow);
					} else {
						if (strstr(argv[i], "-oc:")==argv[i] || strstr(argv[i], "-Oc:")==argv[i]) {
							char *pszTest = malloc(strlen(argv[i]) + 5);
							nCheckingCont = 1;
							strcpy(fname, &argv[i][4]);
							sprintf(pszTest, fname, 0);
							if (strcmp(pszTest, fname) == 0) {
								wprintw(pMainWindow, "Filename for -oc switch doesn't have the correct formatter.\n");
								wrefresh(pMainWindow);
								endwin();
								return -1;
							}
							wprintw(pMainWindow, "Test file: %s\n", pszTest);
							wrefresh(pMainWindow);
							if (stat(pszTest, &stat_buf) != -1 && (strstr(argv[i], "-o:")==argv[i] || strstr(argv[i], "-oc:")==argv[i]) && stat_buf.st_size != 0) {
								// file exists
								wprintw(pMainWindow, "File %s already exists. Remove, or use -Oc:\n", pszTest);
								wrefresh(pMainWindow);
								endwin();
								return -1;
							}
							free(pszTest);
						} else {
							strcpy(fname, &argv[i][3]);
							if (stat(fname, &stat_buf) != -1 && (strstr(argv[i], "-o:")==argv[i] || strstr(argv[i], "-oc:")==argv[i]) && stat_buf.st_size != 0) {
								// file exists
								wprintw(pMainWindow, "File %s already exists. Remove, or use -O:\n", fname);
								wrefresh(pMainWindow);
								endwin();
								return -1;
							}
						}
						if (strstr(argv[i], "-oc:")==argv[i] || strstr(argv[i], "-Oc:")==argv[i]) {
							nCheckingCont = 1;
						}
						pids_map = (pids_map_t*) realloc(pids_map, sizeof(pids_map_t) * (map_cnt+1));
						if(pids_map != NULL) {
							map_cnt++;
							pids_map[map_cnt-1].pid_cnt = 0;
							pids_map[map_cnt-1].start_time=start_time;
							pids_map[map_cnt-1].end_time=end_time;
							for(j=0; j < MAX_CHANNELS; j++) pids_map[map_cnt-1].pids[j] = -1;
							pids_map[map_cnt-1].filename = fname;
							pids_map[map_cnt-1].nCurrentSeq = 0;
							output_type = MAP_TS;
						} else
							wprintw(pMainWindow, "Couldn't alloc enough memory for file %s: entry, discarding\n", fname);
							wrefresh(pMainWindow);
					}
				}
			} else {
				if ((ch=(char*)strstr(argv[i],":"))!=NULL) {
					pid2=atoi(&ch[1]);
					ch[0]=0;
				} else {
					pid2=-1;
				}
				if (strlen(argv[i]) > 2 && argv[i][0] == '0' && argv[i][1] == 'x') {
					if (1 != sscanf(argv[i], "0x%x", &pid)) {
						pid=atoi(argv[i]);
					}
				} else {
					pid=atoi(argv[i]);
				}

				// If we are currently processing a "-o:" option:
				if (map_cnt) {
					// block for the map
					found = 0;
					for (j=0;j<MAX_CHANNELS;j++) {
						if(pids_map[map_cnt-1].pids[j] == pid) found = 1;
					}
					if (found == 0) {
						pids_map[map_cnt-1].pids[pids_map[map_cnt-1].pid_cnt] = pid;
						pids_map[map_cnt-1].pid_cnt++;
					}
				}

				// block for the list of pids to demux
				found = 0;
				for (j=0;j<npids;j++) {
					if(pids[j] == pid) found = 1;
				}
				if (found==0) {
					if (npids == MAX_CHANNELS) {
						wprintw(pMainWindow,"\nSorry, you can only set up to %d filters.\n\n",MAX_CHANNELS);
						wrefresh(pMainWindow);
						endwin();
						return(-1);
					} else {
						pestypes[npids]=pestype;
						pestype=DMX_PES_OTHER;
						pids[npids++]=pid;
						if (pid2!=-1) {
							hi_mappids[pid]=pid2>>8;
							lo_mappids[pid]=pid2&0xff;
							wprintw(pMainWindow,"Mapping %d to %d\n",pid,pid2);
							wrefresh(pMainWindow);
						}
					}
				}
			}
		}
	}

	if ((output_type==RTP_PS) && (npids!=2)) {
		wprintw(pMainWindow,"ERROR: PS requires exactly two PIDS - video and audio.\n");
		wrefresh(pMainWindow);
		endwin();
		exit(1);
	}

	for (i=0;i<map_cnt;i++) {
		if(pids_map[i].filename) {
			FILE *f;
			if (nCheckingCont) {
				char *pszTempFilename = malloc(strlen(pids_map[i].filename) + 5);
				sprintf(pszTempFilename, pids_map[i].filename, pids_map[i].nCurrentSeq);
				f = fopen(pszTempFilename, "w+b");
				free(pszTempFilename);
			} else {
				f = fopen(pids_map[i].filename, "w+b");
			}
			if (f != NULL) {
				pids_map[i].fd = fileno(f);
				make_nonblock(pids_map[i].fd);
				wprintw(pMainWindow, "Open file %s\n", pids_map[i].filename);
				wrefresh(pMainWindow);
			} else {
				pids_map[i].fd = -1;
				wprintw(pMainWindow, "Couldn't open file %s, errno:%d\n", pids_map[map_cnt-1].filename, errno);
				wrefresh(pMainWindow);
			}
		}
	}

	if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);
	if (signal(SIGINT, SignalHandler) == SIG_IGN) signal(SIGINT, SIG_IGN);
	if (signal(SIGTERM, SignalHandler) == SIG_IGN) signal(SIGTERM, SIG_IGN);
	if (signal(SIGALRM, SignalHandler) == SIG_IGN) signal(SIGALRM, SIG_IGN);
	alarm(ALARM_TIME);

	if (freq!=0) {
		if (open_fe(&fd_frontend)) {
			switch (nLNB) {
				case 0:
					if (freq >= 3700 && freq <= 4300) {
						freq = (5150 - freq) * 1000;
					} else if (freq > 11000 && freq < 13500) {
						freq = (freq - 10750) * 1000;
						tone = 1;
					}
					break;
				case 1:
					if (freq >= 11550 && freq < 13500) {
						freq = (freq - 10600) * 1000;
						tone = 1;
					} else if (freq > 10700 && freq < 11550) {
						freq = (freq - 9750) * 1000;
					}
					break;
			}
			wprintw(pMainWindow,"Tuning to %ld Hz\n",freq);
			wrefresh(pMainWindow);
			i=tune_it(pMainWindow, fd_frontend,freq,srate,pol,tone,specInv,diseqc,modulation,HP_CodeRate,TransmissionMode,guardInterval,bandWidth, LP_CodeRate, hier, delivery_system, nPositioner, nMove, nStore, nSearch, nPilot, nTuneCont, disable_power);
		}
	}

	//  if (i<0) { exit(i); }

	if(map_cnt > 0)
		wprintw(pMainWindow, "\n");
	for (i=0;i<map_cnt;i++) {
		if ((secs==-1) || (secs < pids_map[i].end_time)) { secs=pids_map[i].end_time; }
		if(pids_map[i].filename != NULL)
			wprintw(pMainWindow,"MAP %d, file %s: From %ld secs, To %ld secs, %d PIDs - ",i,pids_map[i].filename,pids_map[i].start_time,pids_map[i].end_time,pids_map[i].pid_cnt);
		else
			wprintw(pMainWindow,"MAP %d, addr %s:%d From %ld secs, To %ld secs, %d PIDs - ",i,pids_map[i].net,pids_map[i].port,pids_map[i].start_time,pids_map[i].end_time,pids_map[i].pid_cnt);
		for (j=0;j<MAX_CHANNELS;j++) { if (pids_map[i].pids[j]!=-1) wprintw(pMainWindow," %d",pids_map[i].pids[j]); }
		wprintw(pMainWindow,"\n");
	}

	wprintw(pMainWindow,"dvbstream will stop after %d seconds (%d minutes)\n",secs,secs/60);
	wrefresh(pMainWindow);

	for (i=0;i<npids;i++) {
		if((fd[i] = open(demuxdev[card],O_RDWR|O_NONBLOCK)) < 0){
			wprintw(pMainWindow,"FD %i: ",i);
			wprintw(pMainWindow,"DEMUX DEVICE:  - Error %d(%s)\n", errno, strerror(errno));
			wrefresh(pMainWindow);
			endwin();
			return -1;
		}
//		if (ioctl(fd[i], DMX_SET_BUFFER_SIZE, LARGE_BUF_SIZE) < 0)  {
//			wprintw(pMainWindow,"Failed to set demuxer buffer size. Error %d\n",errno);
//		} else {
//			wprintw(pMainWindow,"Set buffer size for device %s to 20MB\n",demuxdev[card]);
//		}

	}
	pszLargeBuf = malloc(LARGE_BUF_SIZE);
	if (NULL == pszLargeBuf) {
		printf("Failed to allocate large buffer.\n");
		exit(1);
	}

	/* Now we set the filters */
	wprintw(pMainWindow, "Setting filter for pid: ");
	for (i=0;i<npids;i++) {
		set_ts_filt(fd[i],pids[i],pestypes[i]);
		wprintw(pMainWindow, "%d ", pids[i]);
		// Start dmuxer
		if (ioctl(fd[i], DMX_START) < 0) {
			wprintw(pMainWindow,"Cannot start demuxer -  %d(%s)\n", errno, strerror(errno));
			wrefresh(pMainWindow);
			endwin();
			return -1;

		}
	}
	wrefresh(pMainWindow);

	if((fd_dvr = open(dvrdev[card],O_RDONLY)) < 0){
		wprintw(pMainWindow,"DVR DEVICE:  - Error %d(%s)\n", errno, strerror(errno));
		wrefresh(pMainWindow);
		endwin();
		return -1;
	}


	gettimeofday(&tv,(struct timezone*) NULL);
	real_start_time=tv.tv_sec;
	now=0;

	if (do_analyse) {
		wprintw(pMainWindow,"Analysing PIDS\n");
		wrefresh(pMainWindow);
	} else {
		if (to_stdout) {
			wprintw(pMainWindow,"Output to stdout\n");
			wrefresh(pMainWindow);
		}
		else if(! map_cnt) {
			ttl = 2;
			wprintw(pMainWindow,"Using %s:%d:%d\n",ipOut,portOut,ttl);
			wrefresh(pMainWindow);

			/* Init RTP */
			socketOut = makesocket(ipOut,portOut,ttl,&sOut);
#warning WHAT SHOULD THE PAYLOAD TYPE BE FOR "MPEG-2 PS" ?
			initrtp(&hdr,(output_type==RTP_TS ? 33 : 34), streamtype);
			wprintw(pMainWindow,"version=%X\n",hdr.b.v);
		}
		wprintw(pMainWindow,"Streaming %d stream%s\n",npids,(npids==1 ? "" : "s"));
		wrefresh(pMainWindow);
	}

	if (output_type==RTP_PS) {
		init_ipack(&pa, IPACKS,my_write_out, 1);
		init_ipack(&pv, IPACKS,my_write_out, 1);
	}

	/* Read packets */
	free_bytes = buf;

#ifdef ENABLE_TELNET
	/* Setup socket to accept input from a client */
	gethostname(hostname, sizeof(hostname));
	if ((hp = gethostbyname(hostname)) == NULL) {
		wprintw(pMainWindow, "%s: host unknown.\n", hostname);
		wrefresh(pMainWindow);
		endwin();
		exit(1);
	}
	if ((socketIn = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
		wprintw(pMainWindow,"server: socket - Error %d(%s)\n", errno, strerror(errno));
		wrefresh(pMainWindow);
		endwin();
		exit(1);
	}
	setsockopt(socketIn,SOL_SOCKET,SO_REUSEADDR,&ReUseAddr,sizeof(ReUseAddr));

	name.sin_family = AF_INET;
	name.sin_port = htons(port);
	name.sin_addr.s_addr = htonl(INADDR_ANY);
	if (bind(socketIn,(struct sockaddr* )&name,sizeof(name)) < 0) {
		wprintw(pMainWindow,"server: bind - Error %d(%s)\n", errno, strerror(errno));
		wrefresh(pMainWindow);
		endwin();
		exit(1);
	}

	make_nonblock(socketIn);

	if (listen(socketIn, 1) < 0) {
		wprintw(pMainWindow,"server: listen - Error %d(%s)\n", errno, strerror(errno));
		wrefresh(pMainWindow);
		endwin();
		exit(1);
	}
#endif

	connectionOpen=0;
	ns=-1;
	pfds[0].fd=fd_dvr;
	pfds[0].events=POLLIN|POLLPRI;
	pfds[1].events=POLLIN|POLLPRI;

	pthread_t hFileWriterThread;
	pthread_attr_t ThreadAttr;


	if (-1 == pthread_mutex_init(&muDataToFile, NULL)) {
		printf("Failed to initialize mutex for sending data to file. Error %d\n", errno);
		exit(1);
	}
	if (-1 == pthread_cond_init(&muconDataToFile, NULL)) {
		printf("Failed to initialize condition for sending data to file. Error %d\n", errno);
		exit(1);
	}

	pthread_attr_init(&ThreadAttr);
	//pthread_attr_setdetachstate(&ThreadAttr, PTHREAD_CREATE_JOINABLE);
	pthread_attr_setscope(&ThreadAttr, PTHREAD_SCOPE_SYSTEM);
	pthread_create(&hFileWriterThread, &ThreadAttr, FileWriterFunc, NULL);

	time_t nLast = 0;
	int nHighWaterMark = 0;

	while ( !Interrupted) {
		/* Poll the open file descriptors */
		if (ns==-1) {
			poll(pfds,1,500);
		} else {
			pfds[1].fd=ns;  // This can change
			poll(pfds,2,500);
		}
		if (time(NULL) != nLast) {
			time(&nLast);
			enum fe_status ev;
			if (ioctl(fd_frontend, FE_READ_STATUS, &ev) == -1) {
				// no answer, consider it as not locked situation
				ev = 0;
			}

			mvwprintw(pStatusWin, 0, 0, "Tuner: %s %s %s %s %s\n", ev & 0x1 ? "SIGNAL": "signal", ev & 0x2 ? "CARRIER": "carrier", ev & 0x4 ? "VITERBI": "viterbi", ev & 0x8 ? "SYNC": "sync", ev & 0x10 ? "LOCK": "lock");
			if (0 == get_signal(fd_frontend, &nStrength, &nBER, &nSNR, &nUncorrected)) {
				wprintw(pStatusWin, "Signal stength: %d (%.01f%%)\nBit Error Rate: %d\nSignal to Noise Ratio: %d (%.01f%%)\nUncorrected Errors/Total: %d/%d\nContinuity Errors: %d\nTransport Error Indicator: %d (Packets discarded)\nBuffer High Water Mark: %d\nTotal Packets: %llu", nStrength, nStrength / 655.35, nBER, nSNR, nSNR / 655.35, nUncorrected, nTotalUncorrected, nTotalContErrors, nTEI, nHighWaterMark, nTotalPackets);
				wrefresh(pStatusWin);
				if (nBER != 0) {
					nTotalUncorrected += nUncorrected;
				}

			}
		}
#ifdef USE_CURSES
		int ch = getch();
		if (ERR != ch) {
			switch (ch) {
				case '<':
				case ',':
					diseqc_move(pMainWindow, fd_frontend, -1, tone);
					break;
				case '>':
				case '.':
					diseqc_move(pMainWindow, fd_frontend, 1, tone);
					break;
				case 'q':
					Interrupted = 1;
					continue;
				case KEY_RESIZE:
					wprintw(pMainWindow, "Got resize. Lines: %d, Cols: %d\n", LINES, COLS);
					resizeterm(LINES,COLS);
					wrefresh(pStatusWin);
					wrefresh(pMainWindow);
					break;
			}
		}
#endif

		process_telnet();  // See if there is an incoming telnet connection

		if (output_type==RTP_TS) {
			/* Attempt to read 188 bytes from /dev/ost/dvr */
			if ((bytes_read = read(fd_dvr,free_bytes,PACKET_SIZE)) > 0) {
				if (bytes_read!=PACKET_SIZE) {
					wprintw(pMainWindow,"No bytes left to read - aborting\n");
					wrefresh(pMainWindow);
					break;
				}

				pid=((free_bytes[1]&0x1f) << 8) | (free_bytes[2]);
				free_bytes[1]=(free_bytes[1]&0xe0)|hi_mappids[pid];
				free_bytes[2]=lo_mappids[pid];
				free_bytes+=bytes_read;

				// If there isn't enough room for 1 more packet, then send it.
				if ((free_bytes+PACKET_SIZE-buf)>MAX_RTP_SIZE) {
					hdr.timestamp = getmsec()*90;
					if (to_stdout) {
						write(1, buf, free_bytes-buf);
					} else {
						sendrtp2(socketOut,&sOut,&hdr,buf,free_bytes-buf);
					}
					free_bytes = buf;
				}
				count++;
			}
		} else if (output_type==RTP_PS) {
			if (read(fd_dvr,buf,TS_SIZE) > 0) {
				my_ts_to_ps((uint8_t*)buf, pids[0], pids[1]);
			}
		} else if(output_type==MAP_TS) {
			int bytes_read;
			//printf("Reading from DVR...(into buffer 0x%p offset %d)\n", pszLargeBuf, nBytesLeftOver);
			if (nCheckingCont) {
				bytes_read = read(fd_dvr, &pszLargeBuf[nBytesLeftOver], 50 * TS_SIZE);
			} else {
				bytes_read = read(fd_dvr, &pszLargeBuf[nBytesLeftOver], 4096);
				for (i = 0; i < map_cnt; i++) {
					if(pids_map[i].filename) {
						write(pids_map[i].fd, &pszLargeBuf[nBytesLeftOver], bytes_read);
					}
				}
				continue;
			}
			//printf("Got %d bytes.\n", bytes_read);
			unsigned char *buf;
			int nOffset = 0;
			if(bytes_read >= TS_SIZE) {
				int nBytesToProcess = bytes_read;
				bytes_read = TS_SIZE;
				while (nBytesToProcess - nOffset >= TS_SIZE) {
					buf = &pszLargeBuf[nOffset];
					for (i = 0; i < map_cnt; i++) {
						if(pids_map[i].filename) {
							struct EXCHANGE_BUFFER *pszLocalBuffer;
							struct MESSAGE *pLocalMessages;
							pthread_mutex_lock(&muDataToFile);
							nHighWaterMark = nGlobalHighWaterMark;
							nTEI = nGlobalTEI;
							pLocalMessages = pMessageExchange;
							pMessageExchange = NULL;
							if (pExchangeBuffer != NULL) {
								pszLocalBuffer = pExchangeBuffer;
								pExchangeBuffer = NULL;
								pthread_mutex_unlock(&muDataToFile);
							} else {
								pthread_mutex_unlock(&muDataToFile);
								pszLocalBuffer = malloc(sizeof(struct EXCHANGE_BUFFER));
								if (NULL == pszLocalBuffer) {
									printf("Failed to allocate memory for local buffer.\n");
									exit(1);
								}
								pszLocalBuffer->nEntries = 0;
							}
							if (pszLocalBuffer->nEntries == MAX_EXCHANGE_ENTRIES - 1) {
								wprintw(pMainWindow, "Buffer overflow\n");
								wrefresh(pMainWindow);
							} else {
								memcpy(pszLocalBuffer->pszBuffer[pszLocalBuffer->nEntries], buf, TS_SIZE);
								nTotalPackets++;
								pszLocalBuffer->nEntries++;
								pthread_mutex_lock(&muDataToFile);
								if (pExchangeBuffer != NULL) {
									wprintw(pMainWindow, "INTERNAL ERROR: Duplicate buffer appeared\n");
									wrefresh(pMainWindow);
									endwin();
									exit(1);
								}
								pExchangeBuffer = pszLocalBuffer;
								pthread_cond_signal(&muconDataToFile);
								pthread_mutex_unlock(&muDataToFile);
							}
							struct MESSAGE *pMessageLoop = pLocalMessages;
							if (pLocalMessages != NULL) {
								while (pMessageLoop != NULL) {
									wprintw(pMainWindow, pMessageLoop->szMessage);
									//wrefresh(pMainWindow);
									struct MESSAGE *pFree = pMessageLoop;
									pMessageLoop = pMessageLoop->pNext;
									free(pFree);
								}
								wrefresh(pMainWindow);
							}
						} else {
							if((pids_map[i].pos + PACKET_SIZE) > MAX_RTP_SIZE) {
								hdr.timestamp = getmsec()*90;
								sendrtp2(pids_map[i].socket, &(pids_map[i].sOut), &(pids_map[i].hdr), pids_map[i].buf, pids_map[i].pos);
								pids_map[i].pos = 0;
							}

							memcpy(&(pids_map[i].buf[pids_map[i].pos]), buf, bytes_read);
							pids_map[i].pos += bytes_read;
						}
					}
					nOffset += TS_SIZE;
				}
				if (nBytesToProcess - nOffset != 0) {
					memmove(pszLargeBuf, &pszLargeBuf[nOffset], nBytesToProcess - nOffset);
					nBytesLeftOver = nBytesToProcess - nOffset;
				} else {
					nBytesLeftOver = 0;
				}
			} else if (bytes_read  < 0) {
				wprintw(pMainWindow, "Got error %d on read\n", errno);
				wrefresh(pMainWindow);
			} else if (bytes_read < TS_SIZE) {
				nBytesLeftOver = bytes_read;
			}
		} else {
			if (do_analyse) {
				if (read(fd_dvr,buf,TS_SIZE) > 0) {
					pid=((buf[1]&0x1f) << 8) | (buf[2]);
					counts[pid]++;
				}
			}
		}
		if ((secs!=-1) && (secs <=now)) { Interrupted=1; }
	}

	if (Interrupted) {
		wprintw(pMainWindow,"Caught signal %d - closing cleanly.\n",Interrupted);
		wrefresh(pMainWindow);
	}
	wprintw(pMainWindow, "Total uncorrected: %d\n", nTotalUncorrected);
	wrefresh(pMainWindow);
	endwin();

	if (ns!=-1) close(ns);
	close(socketIn);

	if (!to_stdout && !map_cnt) close(socketOut);
	for (i=0;i<npids;i++) close(fd[i]);
	close(fd_dvr);
	close(fd_frontend);

	if (do_analyse) {
		for (i=0;i<8192;i++) {
			if (counts[i]) {
				f=(counts[i]*184.0*8.0)/(secs*1024.0*1024.0);
				if (f >= 1.0) {
					fprintf(stdout,"%d,%.3f Mbit/s\n",i,f);
				} else {
					fprintf(stdout,"%d,%.0f kbit/s\n",i,f*1024);
				}
			}
		}
	}

	return(0);
}
