/***************************************************************************
                          dvbstream.cpp  -  description
                             -------------------
    begin                : Mon Jan 20 2003
    copyright            : (C) 2003-2005 by Christophe Thommeret
    email                : hftom@free.fr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/time.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 <errno.h>

#include <linux/dvb/dmx.h>

#include <qdir.h>

#include <klocale.h>
#include <kapplication.h>

#include "dvbstream.h"
#include "dvbevents.h"
#include "gdvb.h"

#define MAXTUNETRY 1



DvbStream::DvbStream( int anum, int tnum, fe_type_t type, const QString &src, const QString &charset )
{
	isRunning = false;
	timeShifting = false;
	waitPause = 0;
	delOut = 0;
	adapter = anum;
	tuner = tnum;
	setSources( src );
	fdFrontend = fdDvr = 0;
	ndmx = 0;
	currentTransponder = Transponder();
	frontendName = QString("/dev/dvb/adapter%1/frontend%2").arg( adapter ).arg( tuner );
	dvrName = QString("/dev/dvb/adapter%1/dvr%2").arg( adapter ).arg( tuner );
	demuxName = QString("/dev/dvb/adapter%1/demux%2").arg( adapter ).arg( tuner );
	dvbType = type;

	out.setAutoDelete( true );

	bool bok;
	dvbEvents = new DVBevents( &bok, adapter, tuner, charset );

	connect( &statusTimer, SIGNAL(timeout()), this, SLOT(checkStatus()) );
}



void DvbStream::setLnb( int pos, LNB b )
{
	lnb[pos] = b;
}



void DvbStream::setSources( const QString &src )
{
	QString s;
	int pos, i;

	s = src;
	source.clear();
	pos = s.find("|");
	s = s.right( s.length()-pos-1 );
	for ( i=0; i<src.contains("|"); i++ ) {
		pos = s.find("|");
		source.append( s.left(pos) );
		s = s.right( s.length()-pos-1 );
	}
}



QStringList DvbStream::getSources()
{
	return source;
}



bool DvbStream::canSource( ChannelDesc *chan )
{
	if ( chan->tp.type!=dvbType ) {
		return false;
	}
	int i = getSatPos( chan->tp.source );
	if ( i!=-1 )
		return true;
	else
		return false;
}



int DvbStream::getSatPos( const QString &src )
{
	int i;

	for ( i=0; i<(int)source.count(); i++ )
		if ( source[i]==src )
			return i;
	return -1;
}



bool DvbStream::openFe()
{
	if ( fdFrontend ) {
		fprintf(stderr,"openFe: fdFrontend != 0\n");
		return false;
	}
	fdFrontend = open( frontendName.ascii(), O_RDWR );
	if ( fdFrontend<0 ) {
		perror("openFe:");
		fdFrontend = 0;
		return false;
	}
	return true;
}



bool DvbStream::closeFe()
{
	if ( !fdFrontend )
		return true;
	if ( close( fdFrontend )<0 ) {
		perror("closeFe: ");
		return false;
	}
	fprintf(stderr,"Frontend closed\n");
	fdFrontend = 0;
	currentTransponder = Transponder();
	return true;
}



void DvbStream::connectStatus( bool con )
{
	if ( con )
		statusTimer.start( 1000 );
	else
		statusTimer.stop();
}



ChannelDesc DvbStream::getLiveChannel()
{
	int i;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( out.at(i)->hasLive() )
			return out.at(i)->channel;
	}
	return ChannelDesc();
}



bool DvbStream::hasLive()
{
	int i;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( out.at(i)->hasLive() )
			return true;
	}
	return false;
}



bool DvbStream::hasRec()
{
	int i;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( out.at(i)->hasRec() )
			return true;
	}
	return false;
}



bool DvbStream::hasBroadcast()
{
	int i;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( out.at(i)->hasBroadcast() )
			return true;
	}
	return false;
}



bool DvbStream::liveIsRecording()
{
	int i;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( out.at(i)->hasLive() ) {
			if ( out.at(i)->hasRec() )
				return true;
			else
				return false;
		}
	}
	return false;
}



Transponder DvbStream::getCurrentTransponder()
{
	return currentTransponder;
}



bool DvbStream::isTuned()
{
	if ( fdFrontend )
		return true;
	else
		return false;
}



bool DvbStream::tuneDvb( ChannelDesc *chan, bool dvr )
{
	unsigned long lof=0;
	int res, hiband=0;
	struct dvb_frontend_parameters feparams;
	struct dvb_frontend_info fe_info;
	fe_status_t status;
	unsigned long freq=chan->tp.freq;
	unsigned long srate=chan->tp.sr;
	int lnbPos = getSatPos( chan->tp.source );

	closeFe();
	if ( !openFe() )
		return false;

	if ( (res = ioctl( fdFrontend, FE_GET_INFO, &fe_info ) < 0) ) {
		perror("FE_GET_INFO: ");
		return false;
	}

	fprintf(stderr, "Using DVB device %d:%d \"%s\"\n", adapter, tuner, fe_info.name);

	freq*=1000;
	srate*=1000;

	switch( fe_info.type ) {
		case FE_OFDM : {
			if (freq < 1000000)
				freq*=1000UL;
			feparams.frequency=freq;
			feparams.u.ofdm.bandwidth=chan->tp.bandwidth;
			feparams.u.ofdm.code_rate_HP=chan->tp.coderateH;
			feparams.u.ofdm.code_rate_LP=chan->tp.coderateL;
			feparams.u.ofdm.constellation=chan->tp.modulation;
			feparams.u.ofdm.transmission_mode=chan->tp.transmission;
			feparams.u.ofdm.guard_interval=chan->tp.guard;
			feparams.u.ofdm.hierarchy_information=chan->tp.hierarchy;
			fprintf(stderr,"tuning DVB-T to %lu Hz\n", freq);
			fprintf(stderr,"inv:%d bw:%d fecH:%d fecL:%d mod:%d tm:%d gi:%d hier:%d\n", chan->tp.inversion,
				chan->tp.bandwidth, chan->tp.coderateH, chan->tp.coderateL, chan->tp.modulation,
				chan->tp.transmission, chan->tp.guard, chan->tp.hierarchy );
			break;
		}
		case FE_QAM : {
			fprintf(stderr,"tuning DVB-C to %lu\n", freq);
			feparams.frequency=freq;
			feparams.u.qam.symbol_rate = srate;
			feparams.u.qam.fec_inner = chan->tp.coderateH;
			feparams.u.qam.modulation = chan->tp.modulation;
			fprintf(stderr,"inv:%d sr:%lu fecH:%d mod:%d\n", chan->tp.inversion,
				srate, chan->tp.coderateH, chan->tp.modulation );
			break;
		}
		case FE_QPSK : {
			fprintf(stderr,"tuning DVB-S to %lu %c %lu\n", freq, chan->tp.pol, srate);
			if (freq > 2200000) {
				if ( lnb[lnbPos].switchFreq ) {
					if ( freq<(lnb[lnbPos].switchFreq*1000) ) {
						lof = lnb[lnbPos].loFreq*1000;
					}
					else {
						lof = lnb[lnbPos].hiFreq*1000;
						hiband = 1;
					}
				}
				else {
					lof = (lnb[lnbPos].loFreq*1000);
				}
				feparams.frequency = ( freq-lof );
			}
			else
				feparams.frequency=freq;

			feparams.u.qpsk.symbol_rate=srate;
			feparams.u.qpsk.fec_inner=chan->tp.coderateH;
			fprintf(stderr,"inv:%d fecH:%d\n", chan->tp.inversion, chan->tp.coderateH );
			if ( setupSwitch( lnbPos, (chan->tp.pol=='h') || (chan->tp.pol=='H'), hiband)!=0 ) {
				closeFe();
				return false;
			}
			break;
		}
	}
	if ( chan->tp.inversion==INVERSION_AUTO ) {
		if ( fe_info.caps & FE_CAN_INVERSION_AUTO )
			feparams.inversion=chan->tp.inversion;
		else {
			fprintf(stderr,"Can NOT inversion_auto\n");
			feparams.inversion=INVERSION_OFF;
		}
	}
	else
		feparams.inversion=chan->tp.inversion;

	if (ioctl(fdFrontend,FE_SET_FRONTEND,&feparams) < 0) {
		perror("ERROR tuning \n");
		closeFe();
		return false;
	}

	for ( int i=0; i<30; i++ ) {
		usleep( 100000 );
		fprintf( stderr, "." );
		if ( ioctl( fdFrontend, FE_READ_STATUS, &status )==-1 ) {
			perror( "FE_READ_STATUS" );
			break;
		}
		if ( status & FE_HAS_LOCK ) {
			fprintf(stderr," LOCKED.\n");
			break;
		}
	}

	if ( !( status & FE_HAS_LOCK ) ) {
		fprintf( stderr, "\nNot able to lock to the signal on the given frequency\n" );
		closeFe();
		return false;
	}

	if ( !dvr )
		return true;

	if ( ( fdDvr = open( dvrName.ascii(), O_RDONLY|O_NONBLOCK)) < 0 ) {
		perror("DVR DEVICE: ");
		closeFe();
		fdDvr = 0;
		return false;
	}
	pfd.fd=fdDvr;
	pfd.events=POLLIN|POLLPRI;

	currentTransponder = chan->tp;
	return true;
}



int DvbStream::setupSwitch( int switchPos, int voltage18, int hiband )
{
	struct diseqc_cmd switch_cmds[] = {
		{ { { 0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf2, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf1, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf3, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf4, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf6, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf5, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf7, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf8, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xfa, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xf9, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xfb, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xfc, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xfe, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xfd, 0x00, 0x00 }, 4 }, 0 },
		{ { { 0xe0, 0x10, 0x38, 0xff, 0x00, 0x00 }, 4 }, 0 }
	};
	struct diseqc_cmd *cmd[2] = { NULL, NULL };
	int i = 4 * switchPos + 2 * hiband + (voltage18 ? 1 : 0);

	fprintf( stderr, "DiSEqC: switch pos %i, %sV, %sband (index %d)\n", switchPos, voltage18 ? "18" : "13", hiband ? "hi" : "lo", i );
	if ( i < 0 || i >= (int)(sizeof(switch_cmds)/sizeof(struct diseqc_cmd)) )
		return -EINVAL;
	cmd[0] = &switch_cmds[i];
	return diseqc( i % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13, cmd,
		(i/2) % 2 ? SEC_TONE_ON : SEC_TONE_OFF, (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A );
}



int DvbStream::diseqc( fe_sec_voltage_t v, struct diseqc_cmd **cmd, fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b)
{
	int err;

	if ((err = ioctl(fdFrontend, FE_SET_TONE, SEC_TONE_OFF))) {
		perror("FE_SET_TONE failed");
		//return err;
	}
	if ((err = ioctl(fdFrontend, FE_SET_VOLTAGE, v))) {
		perror("FE_SET_VOLTAGE failed");
		//return err;
	}
	usleep(15*1000);
	while (*cmd) {
		fprintf( stderr, "DiSEqC: %02x %02x %02x %02x %02x %02x\n", (*cmd)->cmd.msg[0], (*cmd)->cmd.msg[1],
			(*cmd)->cmd.msg[2], (*cmd)->cmd.msg[3], (*cmd)->cmd.msg[4], (*cmd)->cmd.msg[5]);
		if ((err = ioctl(fdFrontend, FE_DISEQC_SEND_MASTER_CMD, &(*cmd)->cmd))) {
			perror("FE_DISEQC_SEND_MASTER_CMD failed");
			//return err;
		}
		usleep( (*cmd)->wait*1000 );
		cmd++;
	}
	usleep(15*1000);
	if ((err = ioctl(fdFrontend, FE_DISEQC_SEND_BURST, b))) {
		perror("FE_DISEQC_SEND_BURST failed");
		//return err;
	}
	usleep(15*1000);
	if ((err = ioctl(fdFrontend, FE_SET_TONE, t))) {
		perror("FE_SET_TONE failed");
		//return err;
	};
	return 0;
}



bool DvbStream::setPids( DVBout *o, int napid )
{
	int i, dmx;
	struct dmx_pes_filter_params pesFilterParams;
	dmx_pes_type_t pestype = DMX_PES_OTHER;
	QValueList<int> pidList;

	if ( o->channel.vpid )
		pidList.append( o->channel.vpid );
	pidList.append( o->channel.apid[napid].pid );
	if ( o->channel.subpid[0].pid )
		pidList.append( o->channel.subpid[0].pid );

	/*if ( (ndmx+pidList.count())>MAX_CHANNELS )
		return false;*/

	for ( i=0; i<(int)pidList.count(); i++ ) {
		if ( ( dmx = open( demuxName.ascii(), O_RDWR)) < 0 ) {
			fprintf(stderr,"FD %i: ",i);
			perror("DEMUX DEVICE: ");
			return false;
		}
		else
			o->dmx.append(dmx);
	}

	for ( i=0; i<(int)pidList.count(); i++ ) {
		pesFilterParams.pid = pidList[i];
		pesFilterParams.input = DMX_IN_FRONTEND;
		pesFilterParams.output = DMX_OUT_TS_TAP;
		pesFilterParams.pes_type = pestype;
		pesFilterParams.flags = DMX_IMMEDIATE_START;
		if ( ioctl( o->dmx[i], DMX_SET_PES_FILTER, &pesFilterParams) < 0)  {
			fprintf( stderr, "FILTER %i: ", pidList[i] );
			perror("DMX SET PES FILTER");
		}
	}

	ndmx +=pidList.count();

	return true;
}



void DvbStream::removePids( DVBout *o )
{
	int i;

	for ( i=0; i<(int)o->dmx.count(); i++ )
		close( o->dmx[i] );
	ndmx -=o->dmx.count();
}



void DvbStream::removeOut( DVBout *o )
{
	disconnect( o, SIGNAL(endRecord(DVBout*,RecTimer*,bool)), this, SLOT(recordEnded(DVBout*,RecTimer*,bool)) );
	disconnect( o, SIGNAL(playDvb()), this, SLOT(receivePlayDvb()) );
	disconnect( o, SIGNAL(shifting(bool)), this, SLOT(receiveShifting(bool)) );
	delOut = o;
	while ( delOut )
		usleep(100);
}



bool DvbStream::checkStatus()
{
	int32_t strength;
	fe_status_t festatus;
	bool ret=true;

       strength=0;
	ioctl(fdFrontend,FE_READ_SIGNAL_STRENGTH,&strength);
	emit signalStatus(strength*100/65535);

	strength=0;
	ioctl(fdFrontend,FE_READ_SNR,&strength);
	emit snrStatus(strength*100/65535);

	memset( &festatus, 0, sizeof(festatus) );
	ioctl(fdFrontend,FE_READ_STATUS,&festatus);

	if (festatus & FE_HAS_LOCK)
		emit lockStatus( true );
	else {
		emit lockStatus( false );
		ret = false;
	}

	return ret;
}



bool DvbStream::hasVideo()
{
	if ( getLiveChannel().vpid )
		return true;
	return false;
}



void DvbStream::run()
{
	unsigned char buf[188];
	int n, i, thWrite=0;
	int WSIZE=188*8;
	DVBout *o=0;

	signal( SIGPIPE, SIG_IGN );

	while ( isRunning ) {
		poll( &pfd, 1, 100 );
		n = read( fdDvr, buf, 188 );
		if ( n==188 ) {
			memcpy( thBuf+thWrite, buf, n );
			thWrite+=n;
			if ( waitPause>0 ) {
				o = 0;
				for ( i=0; i<(int)out.count(); i++ )
					if ( out.at(i)->hasLive() )
						o = out.at(i);
				if ( o ) {
					if ( o->doPause( timeShiftFileName ) )
						waitPause = 0;
					else
						waitPause = -1;
				}
				else
					waitPause = -1;
			}
			if ( thWrite==WSIZE ) {
				for ( i=0; i<(int)out.count(); i++ )
					out.at(i)->process( thBuf, WSIZE );
				thWrite = 0;
			}
		}
		else
			usleep(1000);

		if ( delOut ) {
			out.remove( delOut );
			delOut = 0;
		}
	}

	fprintf(stderr,"dvbstream::run() end\n");
}



bool DvbStream::doPause( const QString &name )
{
	//if ( !hasVideo() ) return false;

	if ( !timeShifting ) {
		timeShiftFileName = name;
		waitPause = 1;
		while ( waitPause>0 )
			usleep(100);
		if ( waitPause<0 )
			return false;
		else
			receiveShifting( true );
		return true;
	}
	return false;
}



bool DvbStream::timeShiftMode()
{
	return timeShifting;
}



void DvbStream::receiveShifting( bool b )
{
	timeShifting = b;
	emit shifting( b );
}



bool DvbStream::running() const
{
	return isRunning;
}



void DvbStream::receivePlayDvb()
{
	//KApplication::kApplication()->postEvent( videoWin, new QTimerEvent( 500 ) );
}



void DvbStream::recordEnded( DVBout *o, RecTimer* t, bool kill )
{
	if ( kill ) {
		removePids( o );
		removeOut( o );
		if ( out.count()==0 )
			stop();
	}
	recordingState();
	if ( t )
		emit timerEnded( t );
}



void DvbStream::recordingState()
{
	int i;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( out.at(i)->hasRec() ) {
			emit isRecording( true );
			return;
		}
	}
	emit isRecording( false );
}



void DvbStream::updateTimer( RecTimer *t, int ms )
{
	int i;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( out.at(i)->recTimer==t ) {
			if ( ms )
				out.at(i)->changeTimer( ms );
			else
				out.at(i)->stopRec();
			return;
		}
	}
	if ( !ms )
		emit timerEnded( t ); // not running
}



void DvbStream::stopBroadcast()
{
	int i;
	DVBout *o=0;
	QPtrList<DVBout> p;

	for ( i=0; i<(int)out.count(); i++ ) {
		o = out.at(i);
		o->stopBroadcast();
		if ( !o->hasLive() && !o->hasRec() )
			p.append( o );
	}
	for ( i=0; i<(int)p.count(); i++ ) {
		removePids( p.at(i) );
		removeOut( p.at(i) );
	}
	if ( out.count()==0 )
		stop();
	emit isBroadcasting( false );
}



int DvbStream::canStartBroadcast( bool &live, ChannelDesc *chan )
{
	int i, ret=0;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( (chan->tp!=out.at(i)->channel.tp) && out.at(i)->hasRec() )
			return ErrIsRecording;
		if ( out.at(i)->hasLive() && chan->tp!=out.at(i)->channel.tp )
			live = true;
	}
	return ret;
}



bool DvbStream::startBroadcast( QPtrList<ChannelDesc> *list, Ts2Rtp *rtp )
{
	int i, j;
	bool stop=false, newout;
	DVBout *o=0;
	int no=0;
	ChannelDesc *chan=list->at(0);

	if ( !isTuned() ) {
		for ( i=0; i<MAXTUNETRY; i++ ) {
			if ( tuneDvb( chan ) ) {
				i = -1;
				break;
			}
			else
				usleep(100000);
		}
		if ( i<0 )
			stop = true;
		else
			return false;
	}

	for ( i=0; i<(int)list->count(); i++ ) {
		newout = false;
		o = 0;
		for ( j=0; j<(int)out.count(); j++ ) {
			if ( out.at(j)->channel.name==list->at(i)->name )
				o = out.at(j);
		}

		if ( !o ) {
			o = new DVBout( *list->at(i), adapter, tuner );
			connect( o, SIGNAL(endRecord(DVBout*,RecTimer*,bool)), this, SLOT(recordEnded(DVBout*,RecTimer*,bool)) );
			connect( o, SIGNAL(playDvb()), this, SLOT(receivePlayDvb()) );
			connect( o, SIGNAL(shifting(bool)), this, SLOT(receiveShifting(bool)) );
			out.append( o );
			if ( !setPids( o, 0 ) ) {
				removePids( o );
				removeOut( o );
				o = 0;
			}
			else
				newout = true;
		}

		if ( o ) {
			if ( !o->goBroadcast( rtp ) ) {
				if ( newout ) {
					removePids( o );
					removeOut( o );
				}
			}
			else
				no++;
		}
	}

	if ( !no && stop )
		stopFrontend();

	fprintf(stderr,"NOUT: %d\n", out.count() );
	fprintf(stderr,"NO: %d\n", no );

	startReading();

	if ( !no )
		return false;
	emit isBroadcasting( true );
	return true;
}



int DvbStream::canStartTimer( bool &live, ChannelDesc *chan )
{
	int i, ret=0;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( (chan->tp!=out.at(i)->channel.tp) && out.at(i)->hasRec() )
			return ErrIsRecording;
		if ( (out.at(i)->channel.name==chan->name) && out.at(i)->hasRec() )
			return ErrIsRecording;
		if ( out.at(i)->hasLive() ) {
			if ( chan->tp!=out.at(i)->channel.tp )
				live = true;
			else if ( (out.at(i)->channel.name==chan->name) && (out.at(i)->currentAudioPid()!=0) )
				live = true;
		}
	}
	return ret;
}



bool DvbStream::startTimer( ChannelDesc *chan, QString path, RecTimer *t )
{
	int i;
	bool stop=false, newout=false;
	DVBout *o=0;
	QPtrList<DVBout> p;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( out.at(i)->channel.name==chan->name )
			o = out.at(i);
		else {
			if ( out.at(i)->hasBroadcast() && out.at(i)->channel.tp!=chan->tp ) {
				p.append( out.at(i) );
			}
		}
	}

	for ( i=0; i<(int)p.count(); i++ ) {
		p.at(i)->stopBroadcast();
		removePids( p.at(i) );
		removeOut( p.at(i) );
	}

	if ( p.count() ) {
		emit isBroadcasting( false );
		this->stop();
	}

	if ( !isTuned() ) {
		for ( i=0; i<MAXTUNETRY; i++ ) {
			if ( tuneDvb( chan ) ) {
				i = -1;
				break;
			}
			else
				usleep(100000);
		}
		if ( i<0 )
			stop = true;
		else
			return false;
	}

	if ( !o ) {
		o = new DVBout( *chan, adapter, tuner );
		connect( o, SIGNAL(endRecord(DVBout*,RecTimer*,bool)), this, SLOT(recordEnded(DVBout*,RecTimer*,bool)) );
		connect( o, SIGNAL(playDvb()), this, SLOT(receivePlayDvb()) );
		connect( o, SIGNAL(shifting(bool)), this, SLOT(receiveShifting(bool)) );
		out.append( o );
		if ( !setPids( o, 0 ) ) {
			if ( stop )
				stopFrontend();
			removePids( o );
			removeOut( o );
			return false;
		}
		newout = true;
	}

	QString name = path+t->name;
	if ( t->mode )
		name = name+"-"+t->begin.date().toString( Qt::ISODate );
	if ( !o->goRec( t->filetype, name, t ) ) {
		if ( stop )
			stopFrontend();
		if ( newout ) {
			removePids( o );
			removeOut( o );
		}
		return false;
	}

	fprintf(stderr,"NOUT: %d\n", out.count() );

	startReading();

	recordingState();
	return true;
}



int DvbStream::goLive( ChannelDesc *chan, const QString &pipeName, int &napid )
{
	int i;
	bool stop=false;
	DVBout *o=0;

	for ( i=0; i<(int)out.count(); i++ ) {
		if ( (chan->tp!=out.at(i)->channel.tp) && out.at(i)->hasRec() )
			return ErrIsRecording;
		if ( (chan->tp!=out.at(i)->channel.tp) && out.at(i)->hasBroadcast() )
			return ErrIsBroadcasting;
		if ( out.at(i)->channel.name==chan->name )
			o = out.at(i);
	}

	if ( !isTuned() ) {
		for ( i=0; i<MAXTUNETRY; i++ ) {
			if ( tuneDvb( chan ) ) {
				i = -1;
				break;
			}
			else
				usleep(100000);
		}
		if ( i<0 )
			stop = true;
		else
			return ErrCantTune;
	}

	if ( !o ) {
		o = new DVBout( *chan, adapter, tuner );
		connect( o, SIGNAL(endRecord(DVBout*,RecTimer*,bool)), this, SLOT(recordEnded(DVBout*,RecTimer*,bool)) );
		connect( o, SIGNAL(playDvb()), this, SLOT(receivePlayDvb()) );
		connect( o, SIGNAL(shifting(bool)), this, SLOT(receiveShifting(bool)) );
		out.append( o );
		if ( !setPids( o, napid ) ) {
			if ( stop )
				stopFrontend();
			removePids( o );
			removeOut( o );
			return ErrCantSetPids;
		}
	}

	if ( o->hasRec() ) {
		napid = o->currentAudioPid();
		i = ErrDontSwitchAudio;
	}
	else i = 0;

	o->goLive( pipeName, napid );
	fprintf(stderr,"NOUT: %d\n", out.count() );

	startReading();

	return i;
}



void DvbStream::preStopLive()
{
	int i;
	DVBout *o;

	for ( i=0; i<(int)out.count(); i++ ) {
		o = out.at(i);
		if ( o->hasLive() ) {
			o->preStopLive();
			break;
		}
	}
}



void DvbStream::stopLive( ChannelDesc *chan )
{
	int i;
	DVBout *o;
	QPtrList<DVBout> p;

	fprintf(stderr,"Asked to stop\n");
	for ( i=0; i<(int)out.count(); i++ ) {
		o = out.at(i);
		if ( o->hasLive() ) {
			o->stopLive();
			if ( !o->hasRec() && !o->hasBroadcast() ) {
				p.append( o );
			}
			break;
		}
	}
	for ( i=0; i<(int)p.count(); i++ ) {
		removePids( p.at(i) );
		removeOut( p.at(i) );
	}
	fprintf(stderr,"Live stopped\n");
	if ( out.count()==0 && chan->tp!=currentTransponder )
		stop();
}



void DvbStream::startReading()
{
	if ( !isRunning ) {
		isRunning = true;
		start();
		dvbEvents->go( currentTransponder.source, true );
	}
}



void DvbStream::stop()
{
	isRunning = false;
	if ( !wait(1000) ) {
		terminate();
		wait();
		fprintf(stderr,"dvbstream::run() terminated\n");
	}
	dvbEvents->stop();
	stopFrontend();
}



void DvbStream::stopFrontend()
{
	if ( fdDvr ) {
		close( fdDvr );
		fprintf(stderr,"fdDvr closed\n");
		fdDvr = 0;
	}
	closeFe();
}



void DvbStream::setScanning( bool b )
{
	connectStatus( b );
}



void DvbStream::stopScan()
{
	closeFe();
}



DvbStream::~DvbStream()
{
	stop();
	delete dvbEvents;
}

#include "dvbstream.moc"
