/*
 * replex.c
 *        
 *
 * Copyright (C) 2003 - 2006
 *                    Marcus Metzler <mocm@metzlerbros.de>
 *                    Metzler Brothers Systementwicklung GbR
 *           (C) 2006 Reel Multimedia
 *
 * 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
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 *
 */
//#define IN_DEBUG 


#include <stdlib.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#include "replex.h"
#include "pes.h"

static int replex_all_set(struct replex *rx);

void overflow_exit(struct replex *rx)
{
	rx->overflows++;

	if (rx->max_overflows && 
	    rx->overflows > rx->max_overflows){
		fprintf(stderr,"exiting after %d overflows  last video PTS: ", rx->overflows);
		printpts(rx->last_vpts);
		fprintf(stderr,"\n");
		exit(1);
	}
}

int replex_check_id(struct replex *rx, uint16_t id)
{
	int i;

	if (id==rx->vpid)
		return 0;

	for (i=0; i<rx->apidn; i++)
		if (id==rx->apid[i])
			return i+1;

	for (i=0; i<rx->ac3n; i++)
		if (id==rx->ac3_id[i])
			return i+0x80;

	return -1;
}


static int audio_jump(struct replex *rx)
{
	int i;

	for (i=0; i<rx->apidn; i++)
		if (rx->audio_jump[i])
			return 1;

	for (i=0; i<rx->ac3n; i++)
		if (rx->ac3_jump[i])
			return 1;

	return 0;
}


static int jump_finished(struct replex *rx)
{
	int i;

	if (!rx->video_jump) return 0;
	for (i=0; i<rx->apidn; i++)
		if (!rx->audio_jump[i])
			return 0;

	for (i=0; i<rx->ac3n; i++)
		if (!rx->ac3_jump[i])
			return 0;

	return 1;
}


static void clear_jump(struct replex *rx)
{
	int i;

	rx->video_jump = 0;
	for (i=0; i<rx->apidn; i++)
		rx->audio_jump[i] = 0;

	for (i=0; i<rx->ac3n; i++)
		rx->ac3_jump[i] = 0;
}


static void create_fillframe1(ringbuffer *rbuf, int off, int *fsize, 
			      int type, uint8_t *fillframe, struct replex *rx)
{
	int fs = *fsize;
	
	if (fs > MAXFRAME) return;

	if ( type == MPEG_AUDIO){
		audio_frame_t afr;
		get_audio_info(rbuf, &afr, off, fs,0);
		fs = afr.framesize;
		off += (*fsize-fs); 
	}
	if (rx->fillzero && type == MPEG_AUDIO) 
		memset(fillframe+4, 0, MAXFRAME-4);
	else 
		ring_peek (rbuf, fillframe, fs, off);
	*fsize = fs;
}

static void create_fillframe2(ringbuffer *rbuf, int off, int *fsize, 
			      int type, uint8_t *fillframe, struct replex *rx)
{
	int fs = *fsize;

	if (fs > MAXFRAME) return;

	if ( type == MPEG_AUDIO){
		audio_frame_t afr;
		get_audio_info(rbuf, &afr, off, fs,0);
		fs = afr.framesize;
	}
	if (rx->fillzero && type == MPEG_AUDIO) 
		memset(fillframe+4, 0, MAXFRAME-4);
	else 
		ring_peek (rbuf, fillframe, fs, off);

	*fsize = fs;
}


static void fill_in_frames(ringbuffer *index_buf, int fc, audio_frame_t *aframe, 
			   uint64_t *acount, uint8_t *fillframe, int fsize, struct replex *rx)
{								
	index_unit iu;
	int f;

	for (f=0; f < fc; f++){
		init_index(&iu);
		iu.active = 1;
		iu.pts = add_pts_audio(0, aframe,*acount);
		iu.framesize = fsize;
		iu.length = fsize;
		iu.fillframe = fillframe;
		iu.err = DUMMY_ERR;
		if (ring_write(index_buf, (uint8_t *)&iu, sizeof(index_unit)) < 0){
			fprintf(stderr,"audio ring buffer overrun error\n");
			overflow_exit(rx);
		}
		*acount += 1;
	}
}

static int analyze_audio_loop( pes_in_t *p, struct replex *rx, int type, 
			       audio_frame_t *aframe, index_unit *iu, 
			       ringbuffer *rbuf, ringbuffer *index_buf,
			       uint64_t *acount, uint64_t *fpts, 
			       uint64_t *lpts, int bsize, int *apes_abort,
			       uint64_t *ajump, uint64_t *aoff,
			       uint64_t adelay, int n, int off,
			       int c, int len, int pos, int *first, int *filled)
{
	int re=0;
	uint8_t *fillframe=NULL;
	
	switch( type ){
	case AC3:
		fillframe = rx->ac3fillframe[n];
		break;
	case MPEG_AUDIO:
		fillframe = rx->afillframe[n];
		break;
	}
	if (!aframe->set){
		switch( type ){
		case AC3:
			re = get_ac3_info(rbuf, aframe, 
					  pos+c+off,
					  len-c-pos,1);
			break;
		case MPEG_AUDIO:
			re = get_audio_info(rbuf, aframe, 
					    pos+c+off,
					    len-c-pos, 1);
			break;
		}
		if ( re == -2){
			*apes_abort = len -c;
			return c;
		}
		if (re < 0) return c;
		
		if (!rx->ignore_pts){
			if ((p->flag2 & PTS_ONLY)){
				*fpts = trans_pts_dts(p->pts);
				fprintf(stderr,
					"starting audio PTS: ");
				printpts(*fpts);
				fprintf(stderr,"\n");
			} else {
				aframe->set = 0;
				ring_skip(rbuf,pos+c+off+re);
			}
		}
		
		if (aframe->set && *first)
			ring_skip(rbuf,pos+c);
	} else {
		int diff = ring_posdiff(rbuf, iu->start, 
					p->ini_pos + pos+c);
		
		if ( (re =check_audio_header(rbuf, aframe, 
					     pos+c+off,len-c-pos,
					     type)) < 0){
			
			if ( re == -2){
				*apes_abort = len -c;
				return c;
			}
			
			if (aframe->framesize > diff){
				if ( re == -3){
					c+= pos+1;
					return c;
				}
				
				c += pos+2;
#ifdef IN_DEBUG
				fprintf(stderr,"WRONG HEADER1 %d\n", diff);
#endif
				return c;
			}
		}
		if (aframe->framesize > diff){
			c += pos+2;
			//fprintf(stderr,"WRONG HEADER2 %d\n", diff);
			return c;
		}
	}
	
	
	if (aframe->set){
		if(iu->active){
			iu->length = ring_posdiff(rbuf, 
						  iu->start, 
						  p->ini_pos + 
						  pos+c);

			if (fillframe && !*filled){
				int fsize = aframe->framesize;
				int pdiff = ring_posdiff(rbuf, ring_rpos(rbuf),iu->start);
				
				create_fillframe2(rbuf, pdiff, &fsize, type, fillframe, rx);
				*filled = 1;
			}

			if (iu->length != aframe->framesize){
				iu->err= FRAME_ERR;
				fprintf(stderr,"Wrong audio frame size: %d (%d)\n", 
					iu->length, aframe->framesize);
				*acount -= 1;
			}
			
			if (ring_write(index_buf, (uint8_t *)iu, sizeof(index_unit)) < 0){
				fprintf(stderr,"audio ring buffer overrun error\n");
				overflow_exit(rx);
			}
			if (iu->err == JUMP_ERR) *acount -= 1;
			*acount += 1;
		} 
		
		init_index(iu);
		iu->active = 1;
		iu->pts = add_pts_audio(0, aframe,*acount);
		iu->framesize = aframe->framesize;
		
		if (!rx->ignore_pts && *first && (p->flag2 & PTS_ONLY)){
			int64_t dpts;
			int64_t diff;
			
			dpts = ptsdiff(trans_pts_dts(p->pts), *fpts);
			diff = ptsdiff(dpts, iu->pts);
			
			if (rx->allow_jump &&
			    (int)diff > rx->allow_jump){
				
				if (!(*ajump) && rx->video_jump){
					int fc=0;
					int fsize = 0;
					uint64_t oldfpts  = 0;
					uint64_t ndpts = 0;
			
					int pdiff = ring_posdiff(rbuf, ring_rpos(rbuf),p->ini_pos + 
								 pos+c );
					
					*ajump = diff; 
					oldfpts = *fpts;
					*fpts += rx->video_jump;

					fprintf(stderr,"found jump in audio PTS\n");
					printpts(iu->pts);
					printpts(diff);
					fprintf(stderr,"\n");
					
					
					ndpts = uptsdiff(trans_pts_dts(p->pts),*fpts);
					fsize = aframe->framesize;
					if (fillframe){
						create_fillframe1(rbuf, pdiff-fsize, &fsize, 
								  type, fillframe, rx);
						*filled = 1;
					}
					
					if (iu->pts < ndpts){
						fc = cfix_audio_count(aframe, ndpts, iu->pts);
						
						if ( fc > 100) {
							*acount+= fc;
							fprintf(stderr,"need to fill in %d audio frames\n",fc);
							fprintf(stderr,"this is too much, try re-cutting\n");
							*fpts = oldfpts;
							fc = 0; // just too broken
							rx->video_jump=0;
						} else fill_in_frames(index_buf, fc, aframe, 
								      acount, fillframe, aframe->framesize, rx);
						init_index(iu);
						iu->active = 1;
						iu->pts = add_pts_audio(0, aframe,*acount);
						iu->framesize = aframe->framesize;
						
						//iu->pts = uptsdiff(trans_pts_dts(p->pts),
						//		   *fpts);
						printpts(iu->pts);
						fprintf(stderr,"  fixed %d frames\n", fc);
						
						diff = 0;
						
					} else {
						fc = cfix_audio_count(aframe, iu->pts, ndpts);
						fprintf(stderr,"need to drop %d audio frames: ",fc);
						diff = ptsdiff(ndpts, iu->pts);
						printpts(diff);
						fprintf(stderr,"\n");
						iu->pts = add_pts_audio(0, aframe,*acount);
					}
					
				}

				if (!rx->video_jump && diff > 0){ 
					uint64_t vadiff = ptsdiff(dpts, rx->last_vpts);
					iu->err = JUMP_ERR;
					*ajump = diff;
					
					fprintf(stderr,"found jump in audio PTS without video jump\n");
					printpts(iu->pts);
					printpts(diff);
					fprintf(stderr,"\n");

					if (abs(vadiff) < 600 * CLOCK_MS){
						int fc=0;
						int fsize = 0;
			
						int pdiff = ring_posdiff(rbuf, ring_rpos(rbuf),p->ini_pos + 
									 pos+c );
					
						*ajump = diff; 
						*fpts += rx->video_jump;
						
						fprintf(stderr,"filling in audio frames\n");
						printpts(iu->pts);
						printpts(diff);
						fprintf(stderr,"\n");
					
					
						fc = cfix_audio_count(aframe,
								      uptsdiff(trans_pts_dts(p->pts),
									       *fpts), iu->pts);
						fsize = aframe->framesize;
						if (fillframe){
							create_fillframe1(rbuf, pdiff-fsize, &fsize, 
									  type, fillframe, rx);
							*filled = 1;
						}
					
						fill_in_frames(index_buf, fc, aframe, 
							       acount, fillframe, fsize, rx);
						
						init_index(iu);
						iu->active = 1;
						iu->pts = add_pts_audio(0, aframe,*acount);
						iu->framesize = aframe->framesize;
						fprintf(stderr,"  fixed %d frames\n", fc);
	
						diff = 0;
					}
				}

				if (jump_finished(rx)) clear_jump(rx);
			}
						

			if ( diff < 0){
				fprintf(stderr,"drop audio frame\n");
				init_index(iu);
				iu->err = JUMP_ERR;
				iu->active = 1;
				iu->pts = add_pts_audio(0, aframe,*acount);
				iu->framesize = aframe->framesize;
			}

			if( iu->err!= JUMP_ERR && !rx->keep_pts && diff > 40*CLOCK_MS){
					if (!rx->allow_jump || abs((int)diff) > rx->allow_jump){
						fprintf(stderr,"audio PTS inconsistent: ");
						printpts(dpts);
						printpts(iu->pts);
						fprintf(stderr,"diff: ");
						printpts(diff);
						fprintf(stderr,"\n");
					} 
			}	
			if (rx->keep_pts){
				fix_audio_count(acount, aframe, 
						uptsdiff(trans_pts_dts(
								 p->pts),
							 *fpts),
						iu->pts);
				iu->pts = uptsdiff(trans_pts_dts(p->pts),
						   *fpts);
				if (*lpts && ptsdiff(iu->pts,*lpts)<0) 
					fprintf(stderr, 
						"Warning negative audio PTS increase!\n");
				*lpts = iu->pts;
			}
			*first = 0;
		}
		if (rx->analyze >1){
			if ((p->flag2 & PTS_ONLY)){
				iu->pts = trans_pts_dts(p->pts);
			} else {
				iu->pts = 0;
			}
		}
		iu->start = (p->ini_pos+pos+c)%bsize;
	}
	c += pos;
	if (c + aframe->framesize > len){
//				fprintf(stderr,"SHORT %d\n", len -c);
		c = len;
	} else {
		c += aframe->framesize;
	}
	return c;
}				



void analyze_audio( pes_in_t *p, struct replex *rx, int len, int num, int type)
{
	int c=0;
	int pos=0;
	audio_frame_t *aframe = NULL;
	index_unit *iu = NULL;
	ringbuffer *rbuf = NULL, *index_buf = NULL;
	uint64_t *acount=NULL;
	uint64_t *fpts=NULL;
	uint64_t *lpts=NULL;
	int bsize = 0;
	uint64_t *ajump=NULL;
	uint64_t *aoff=NULL;
	uint8_t buf[7];
	int off=0;
	int *apes_abort=NULL;
	uint64_t adelay=0;
	int first = 1;
	int *filled=NULL;
	
	switch ( type ){
	case AC3:
#ifdef IN_DEBUG
		fprintf(stderr, "AC3\n");
#endif
		aframe = &rx->ac3frame[num];	
		iu = &rx->current_ac3index[num];
		rbuf = &rx->ac3rbuffer[num];
		index_buf = &rx->index_ac3rbuffer[num];
		acount = &rx->ac3frame_count[num];
		fpts = &rx->first_ac3pts[num];
		lpts = &rx->last_ac3pts[num];
		bsize = rx->ac3buf;
		apes_abort = &rx->ac3pes_abort[num];
		ajump = &rx->ac3_jump[num];
		aoff = &rx->ac3pts_off[num];
		adelay = rx->ac3pts_off[num];
		filled = &rx->ac3filled[num];
		break;

	case MPEG_AUDIO:
#ifdef IN_DEBUG
		fprintf(stderr, "MPEG AUDIO\n");
#endif
		aframe = &rx->aframe[num];	
		iu = &rx->current_aindex[num];
		rbuf = &rx->arbuffer[num];
		index_buf = &rx->index_arbuffer[num];
		acount = &rx->aframe_count[num];
		fpts = &rx->first_apts[num];
		lpts = &rx->last_apts[num];
		bsize = rx->audiobuf;
		apes_abort = &rx->apes_abort[num];
		ajump = &rx->audio_jump[num];
		aoff = &rx->apts_off[num];
		adelay = rx->apts_off[num];
	        filled = &rx->afilled[num];
		break;
	}
	
	*apes_abort = 0;
	off = ring_rdiff(rbuf, p->ini_pos);
	while (c < len){
		if ( (pos = find_audio_sync(rbuf, buf, c+off, type, len-c) ) 
		     >= 0 ){
			c = analyze_audio_loop( p, rx, type, aframe, iu, 
						rbuf, index_buf, acount, fpts, 
						lpts, bsize, apes_abort,
						ajump, aoff, adelay, num, off,
						c, len, pos, &first, filled);
		} else {
			*apes_abort = len-c;
			c=len;
		}
	}	
}


void analyze_video( pes_in_t *p, struct replex *rx, int len)
{
	uint8_t buf[8];
	int c=0;
	int pos=0;
	uint8_t head;
	int seq_end = 0;
	uint8_t frame = 0;
	uint64_t newpts = 0;
	uint64_t newdts = 0;
	index_unit *iu;
	int off=0;
	ringbuffer *rbuf;
	ringbuffer *index_buf;
	sequence_t *s;
	int i;

	uint16_t tempref = 0;
	int seq_h = 0;
	int gop = 0;
	int frame_off = 0;
	int gop_off = 0;
	int seq_p = 0;
	int flush=0;
	int keep_now = 0;

	rbuf = &rx->vrbuffer;
	index_buf = &rx->index_vrbuffer;
	iu = &rx->current_vindex;
	s = &rx->seq_head;

	rx->vpes_abort = 0;
	off = ring_rdiff(rbuf, p->ini_pos);
#ifdef IN_DEBUG
	fprintf(stderr, " ini pos %d\n",
		(p->ini_pos)%rx->videobuf);
#endif

	
//	fprintf(stderr, "len %d  %d\n",len,off);
	while (c < len){
		if ((pos = ring_find_any_header( rbuf, &head, c+off, len-c)) 
		    >=0 ){
			switch(head){
			case SEQUENCE_HDR_CODE:
#ifdef IN_DEBUG
				fprintf(stderr, " seq headr %d\n",
					(p->ini_pos+c+pos)%rx->videobuf);
#endif

				seq_h = 1;
				seq_p = c+pos;
				
				
				if (!s->set){ 
					int re=0;
					re = get_video_info(rbuf, &rx->seq_head, 
							    pos+c+off, len -c -pos);

#ifdef IN_DEBUG
					fprintf(stderr, " seq headr result %d\n",re);
#endif
					if (re == -2){
						rx->vpes_abort = len -(c+pos-1);
						return;
					}
					if (s->set){
						ring_skip(rbuf, pos+c);
						off -= pos+c;
					}
					if (!rx->ignore_pts && 
					    !(p->flag2 & PTS_ONLY)) s->set = 0;
				}
				if (s->set){
					flush = 1;
				}
				break;
				
			case EXTENSION_START_CODE:{
				int ext_id = 0;

#ifdef IN_DEBUG
				fprintf(stderr," seq ext headr %d\n",
					(p->ini_pos+c+pos)+rx->videobuf);
#endif
				ext_id = get_video_ext_info(rbuf, 
							    &rx->seq_head, 
							    pos+c+off, 
							    len -c -pos);
				if (ext_id == -2){
					rx->vpes_abort = len - (pos-1+c);
					return;
				}
					

				if(ext_id == PICTURE_CODING_EXTENSION 
				   && s->ext_set && iu->active){
					if (!rx->ignore_pts && 
					    !rx->vframe_count &&
					    !rx->first_vpts){
						rx->first_vpts = trans_pts_dts(
							p->pts);
						
						for (i = 0; i< s->current_tmpref;
						     i++) ptsdec(&rx->first_vpts,
								 SEC_PER);
						fprintf(stderr,"starting with video PTS: ");
						printpts(rx->first_vpts);
						fprintf(stderr,"\n");
					}

					newpts = 0;
#ifdef IN_DEBUG
					
					fprintf(stderr,"fcount %d  gcount %d  tempref %d  %d\n",
						(int)rx->vframe_count, (int)rx->vgroup_count, 
						(int)s->current_tmpref,
						(int)(s->current_tmpref - rx->vgroup_count 
						      + rx->vframe_count));
#endif
					newdts = next_ptsdts_video(
						&newpts,s, rx->vframe_count, 
						rx->vgroup_count);
					
					if (!rx->ignore_pts &&
					    (p->flag2 & PTS_ONLY)){
						int64_t diff;

						diff = ptsdiff(iu->pts, 
							       rx->first_vpts 
							       + newpts);
						
						if (rx->allow_jump &&  
						    abs(diff) > rx->allow_jump)
						{
							if (audio_jump(rx)){
								fprintf(stderr,"AUDIO JUMPED\n");
								clear_jump(rx);
							}
							fprintf(stderr,"found jump in video PTS\n");
							printpts(iu->pts);
							printpts(diff);
							fprintf(stderr,"\n");
							rx->video_jump = diff;
							rx->first_vpts += diff;
							diff = 0;
						} else {
							keep_now = 1;
						}

						if (!rx->keep_pts &&
						    abs((int)(diff)) > 3*SEC_PER/2){
							fprintf(stderr,"video PTS inconsistent: ");
							printpts(trans_pts_dts(p->pts));
							printpts(iu->pts);
							printpts(newpts+rx->first_vpts);
							printpts(newpts);
							fprintf(stderr," diff: ");
							printpts(diff);
							fprintf(stderr,"\n");
						}
					}

					if (!rx->ignore_pts &&
					    (p->flag2 & PTS_DTS) == PTS_DTS){ 
						uint64_t diff;
						diff = ptsdiff(iu->dts, 
							       newdts + 
							       rx->first_vpts); 
						if (!rx->keep_pts && !keep_now &&
						    abs((int)diff) > 3*SEC_PER/2){
							fprintf(stderr,"video DTS inconsistent: ");
							printpts(trans_pts_dts(p->dts));
							printpts(iu->dts);
							printpts(newdts+rx->first_vpts);
							printpts(newdts);
							fprintf(stderr,"diff: ");
							printpts(diff);
							fprintf(stderr,"\n");
						}
					}
					if (!rx->keep_pts && !keep_now){
						iu->pts = newpts;
						iu->dts = newdts;
					} else {
						if (p->flag2 & PTS_DTS){
							ptsdec(&iu->pts, 
							       rx->first_vpts);

							if ((p->flag2 & PTS_DTS) == PTS_DTS){
								ptsdec(&iu->dts,
								       rx->first_vpts);
							} else 	iu->dts = newdts;

							fix_video_count(s,&rx->vframe_count,
									iu->pts,newpts,
									iu->dts,newdts);
							
						} else {
							iu->pts = newpts;
							iu->dts = newdts;
						}
					}
					if (rx->last_vpts && 
					    ptsdiff(iu->dts, rx->last_vpts) <0)
						fprintf(stderr,
							"Warning negative video PTS increase!\n");
					rx->last_vpts = iu->dts;
				}

				if (rx->analyze){ 
					if ((p->flag2 & PTS_DTS) == PTS_DTS){
						iu->pts = trans_pts_dts(p->pts);
						iu->dts = trans_pts_dts(p->dts);
					}  else if (p->flag2 & PTS_ONLY){
						iu->pts = trans_pts_dts(p->pts);
						iu->dts = 0;
					}  else {
						iu->pts = 0;
						iu->dts = 0;
					}
				}


				break;
			}

			case SEQUENCE_END_CODE:
#ifdef IN_DEBUG
				fprintf(stderr, " seq end %d\n",
					(p->ini_pos+c+pos)%rx->videobuf);
#endif
				if (s->set)
					seq_end = 1;
				
				break;

			case GROUP_START_CODE:{
				int hour, min, sec;
//#define ANA
#ifdef ANA
				fprintf(stderr,"  %d", (int)rx->vgroup_count);
				fprintf(stderr,"\n");

#endif
				if (s->set){
					if (!seq_h) flush = 1;
				}
				gop = 1;
				gop_off = c+pos - seq_p;
				
				if (ring_peek(rbuf, buf, 7, off+c+pos) < 0){
					rx->vpes_abort = len -(c+pos-1);
					return;
				}				
				hour = (int)((buf[4]>>2)& 0x1F);
				min  = (int)(((buf[4]<<4)& 0x30)| 
					     ((buf[5]>>4)& 0x0F));
				sec  = (int)(((buf[5]<<3)& 0x38)|
					     ((buf[6]>>5)& 0x07));
#ifdef IN_DEBUG
				fprintf(stderr,	" gop %02d:%02d.%02d %d\n",
					hour,min,sec, 
					(p->ini_pos+c+pos)%rx->videobuf);
#endif
				rx->vgroup_count = 0;

				break;
			}

			case PICTURE_START_CODE:{		
				if (len-(c+pos) < 14){
					rx->vpes_abort = len - (pos-1+c);
					return;
				}
				
				if (ring_peek(rbuf, buf, 6, off+c+pos) < 0) 
					return;


				frame = ((buf[5]&0x38) >>3);
				
				if (frame == I_FRAME){
					if( !rx->first_iframe){
						if (s->set){
							rx->first_iframe = 1;
						} else {
							s->set=0;
							flush = 0;
							break;
						}
					}
				}

				frame_off = c+pos - seq_p;
				if (s->set){
					if (!seq_h && !gop) flush = 1;
				}
				tempref = (buf[5]>>6) & 0x03;
				tempref |= buf[4] << 2;

				switch (frame){
				case I_FRAME:
#ifdef ANA
				fprintf(stderr,"I");
#endif
#ifdef IN_DEBUG
					fprintf(stderr, " I-frame %d\n",
						(p->ini_pos+c+pos)%rx->videobuf);
#endif
					break;
				case B_FRAME:
#ifdef ANA
				fprintf(stderr,"B");
#endif
#ifdef IN_DEBUG
					fprintf(stderr, " B-frame %d\n",
						(p->ini_pos+c+pos)%rx->videobuf);
#endif
					break;
				case P_FRAME:
#ifdef ANA
				fprintf(stderr,"P");
#endif
#ifdef IN_DEBUG
					fprintf(stderr,  " P-frame %d\n",
						(p->ini_pos+c+pos)%rx->videobuf);
#endif
					break;
				}
				s->current_frame = frame;
				s->current_tmpref = tempref;
				
				break;		
			}
			default:
#ifdef IN_DEBUG
				//fprintf(stderr,"other header 0x%02x (%d+%d)\n",head,c,pos);
#endif
			  break;
			}
			
			if (flush && s->set && rx->first_iframe){
				if(iu->active){
					rx->vframe_count++;
					rx->vgroup_count++;

					iu->length = ring_posdiff(rbuf, 
								  iu->start, 
								  p->ini_pos+
								  pos+c-frame_off);

					if ( ring_write(index_buf, (uint8_t *)
							 &rx->current_vindex,
							 sizeof(index_unit))<0){
						fprintf(stderr,"video ring buffer overrun error 1\n");
						overflow_exit(rx);

					}
				} 
				init_index(&rx->current_vindex);
				flush = 0;
				iu->active = 1;
				if (!rx->ignore_pts){
					if ((p->flag2 & PTS_DTS) == PTS_DTS){
						iu->pts = trans_pts_dts(p->pts);
						iu->dts = trans_pts_dts(p->dts);
					}  else if (p->flag2 & PTS_ONLY){
						iu->pts = trans_pts_dts(p->pts);
					}  
				}
				iu->start =  (p->ini_pos+pos+c-frame_off)
					%rx->videobuf;
#ifdef IN_DEBUG
				fprintf(stderr,"START %d\n", iu->start);
#endif
			}

			if (s->set){
				if (frame)
					iu->frame = (frame&0xFF);
				if (seq_h)
					iu->seq_header = 1; 
				if (seq_end)
					iu->seq_end = 1; 
				if (gop)
					iu->gop = 1; 
				if (gop_off) 
					iu->gop_off = gop_off; 
				if (frame_off) 
					iu->frame_off = frame_off;
			} 
			c+=pos+4;
		} else {
			if (pos == -2){
				rx->vpes_abort = 4;
					
			}
			c = len;
		}
	}
}

void es_out(pes_in_t *p)
{

	struct replex *rx;
	char t[80];
	int len;

	len = p->plength-3-p->hlength;

	rx = (struct replex *) p->priv;

	switch(p->type)
	{
	case 0xE0: {
		sprintf(t, "Video ");
		if (rx->vpes_abort){
			p->ini_pos = (p->ini_pos - rx->vpes_abort)%rx->vrbuffer.size;
			len += rx->vpes_abort;
		}
		analyze_video(p, rx, len);
		if (!rx->seq_head.set){
			ring_skip(&rx->vrbuffer, len);
		}
		break;
	}

	case 1 ... 32:{
		int l;
		l = p->type - 1;
		sprintf(t, "Audio%d ", l);
		if (rx->apes_abort[l]){
			p->ini_pos = (p->ini_pos - rx->apes_abort[l])
			  %rx->arbuffer[l].size;
			len += rx->apes_abort[l];
		}
		analyze_audio(p, rx, len, l, MPEG_AUDIO);
		if (!rx->aframe[l].set)
			ring_skip(&rx->arbuffer[l], len);
		
		break;
	}

	case 0x80 ... 0x87:{
		int l;
		l = p->type - 0x80;
		sprintf(t, "AC3 %d ", p->type);
		if (rx->ac3pes_abort[l]){
			p->ini_pos = (p->ini_pos - rx->ac3pes_abort[l])
				%rx->ac3rbuffer[l].size;
			len += rx->ac3pes_abort[l];
		}
		analyze_audio(p, rx, len, l, AC3);
		if (!rx->ac3frame[l].set)
			ring_skip(&rx->ac3rbuffer[l], len);
		break;
	}

	default:
		return;


	}

#ifdef IN_DEBUG
	fprintf(stderr,"%s PES\n", t);
#endif
}

void pes_es_out(pes_in_t *p)
{

	struct replex *rx = NULL;
	char t[80];
	int len = 0, i =0;
	int l=0;

	len = p->plength-3-p->hlength;
	rx = (struct replex *) p->priv;

	switch(p->cid){
	case VIDEO_STREAM_S ... VIDEO_STREAM_E:
		if (rx->vpid != p->cid) break;
		p->type = 0xE0;
		p->ini_pos = ring_wpos(&rx->vrbuffer);

		if (ring_write(&rx->vrbuffer, p->buf+9+p->hlength, len)<0){
			fprintf(stderr,"video ring buffer overrun error 2\n");
			overflow_exit(rx);
		}
		if (rx->vpes_abort){
			p->ini_pos = (p->ini_pos - rx->vpes_abort)%rx->vrbuffer.size;
			len += rx->vpes_abort;
		}
		sprintf(t, "Video ");
		analyze_video(p, rx, len);
		if (!rx->seq_head.set){
			ring_skip(&rx->vrbuffer, len);
		}
		break;
		
	case AUDIO_STREAM_S ... AUDIO_STREAM_E:
		p->type = p->cid - 0xc0 + 1;
		l = -1;
		for (i=0; i<rx->apidn; i++)
			if (p->cid == rx->apid[i])
				l = i;
		if (l < 0) break;
		p->ini_pos = ring_wpos(&rx->arbuffer[l]);
		if (ring_write(&rx->arbuffer[l], p->buf+9+p->hlength, len)<0){
			fprintf(stderr,"audio ring buffer overrun error\n");
			overflow_exit(rx);
		}
		if (rx->apes_abort[l]){
			p->ini_pos = (p->ini_pos - rx->apes_abort[l])
			  %rx->arbuffer[l].size;
			len += rx->apes_abort[l];
		}

		sprintf(t, "Audio%d ", l);
		analyze_audio(p, rx, len, l, MPEG_AUDIO);
		if (!rx->aframe[l].set)
			ring_skip(&rx->arbuffer[l], len);
		
		break;
		
	case PRIVATE_STREAM1:{
		int hl=4;
		if (rx->vdr){
			hl=0;
			p->type=0x80;
			l = 0;
		} else {
			uint16_t fframe;
			
			fframe=0;
			fframe = p->buf[9+p->hlength+3];
			fframe |= (p->buf[9+p->hlength+2]<<8);
		
			if (fframe > p->plength) break;
			
			p->type = p->buf[9+p->hlength];
			l = -1;
			for (i=0; i<rx->ac3n; i++)
				if (p->type == rx->ac3_id[i])
					l = i;
			if (l < 0) break;
		}
		len -= hl;
		p->ini_pos = ring_wpos(&rx->ac3rbuffer[l]);
	
		if (ring_write(&rx->ac3rbuffer[l], p->buf+9+hl+p->hlength, len)<0){
			fprintf(stderr,"ac3 ring buffer overrun error\n");
			overflow_exit(rx);
		}
		if (rx->ac3pes_abort[l]){
			p->ini_pos = (p->ini_pos - rx->ac3pes_abort[l])
				%rx->ac3rbuffer[l].size;
			len += rx->ac3pes_abort[l];
		}

		sprintf(t, "AC3 %d ", p->type);
		analyze_audio(p, rx, len, l, AC3);
		sprintf(t,"%d",rx->ac3frame[l].set);
		if (!rx->ac3frame[l].set)
			ring_skip(&rx->ac3rbuffer[l], len);
	}
		break;
		
	default:
		return;
	}
	
#ifdef IN_DEBUG
	fprintf(stderr,"%s PES %d\n", t,len);
#endif
}

void avi_es_out(pes_in_t *p)
{

	struct replex *rx;
	char t[80];
	int len;

	len = p->plength;

	rx = (struct replex *) p->priv;


	switch(p->type)
	{
	case 0xE0: {
		sprintf(t, "Video ");
		if (!len){
			rx->vframe_count++;
			break;
		}
		analyze_video(p, rx, len);
		if (!rx->seq_head.set){
			ring_skip(&rx->vrbuffer, len);
		}
		break;
	}

	case 1 ... 32:{
		int l;
		l = p->type - 1;
		sprintf(t, "Audio%d ", l);
		if (!len){
			rx->aframe_count[l]++;
			break;
		}
		analyze_audio(p, rx, len, l, MPEG_AUDIO);
		if (!rx->aframe[l].set)
			ring_skip(&rx->arbuffer[l], len);
		
		break;
	}

	case 0x80 ... 0x87:{
		int l;

		l = p->type - 0x80;
		sprintf(t, "AC3 %d ", p->type);
		if (!len){
			rx->ac3frame_count[l]++;
			break;
		}
		analyze_audio(p, rx, len, l, AC3);
		if (!rx->ac3frame[l].set)
			ring_skip(&rx->ac3rbuffer[l], len);
		break;
	}

	default:
		return;


	}
#ifdef IN_DEBUG
	fprintf(stderr,"%s PES\n", t);
#endif

}


int replex_tsp(struct replex *rx, uint8_t *tsp)
{
	uint16_t pid;
	int type;
	int off=0;
	pes_in_t *p=NULL;

	pid = get_pid(tsp+1);

	if ((type=replex_check_id(rx, pid))<0)
		return 0;
	
	switch(type){
	case 0:
		p = &rx->pvideo;
		break;

	case 1 ... 32:
		p = &rx->paudio[type-1];
		break;

	case 0x80 ... 0x87:
		p = &rx->pac3[type-0x80];
		break;
	default:
		return 0;
		
	}

	
	if ( tsp[1] & PAY_START){
		if (p->plength == MMAX_PLENGTH-6){
			p->plength = p->found-6;
			es_out(p);
			init_pes_in(p, p->type, NULL, 0);
		}
	}

	if ( tsp[3] & ADAPT_FIELD){  // adaptation field?
		off = tsp[4] + 1;
		if (off+4 >= TS_SIZE) return 0;
	}
        
	get_pes(p, tsp+4+off, TS_SIZE-4-off, es_out);
	
	return 0;
}


ssize_t save_read(struct replex *rx, void *buf, size_t count)
{
	ssize_t neof = 1;
	size_t re = 0;
	int fd = rx->fd_in;

	if (rx->itype== REPLEX_AVI){
		int l = rx->inflength - rx->finread;
		if ( l <= 0) return 0;
		if ( count > l) count = l;
	}
	while(neof >= 0 && re < count){
		neof = read(fd, buf+re, count - re);
		if (neof > 0) re += neof;
		else break;
	}
	rx->finread += re;
#ifndef OUT_DEBUG
	if (rx->inflength){
		uint8_t per=0;
		
		per = (uint8_t)(rx->finread*100/rx->inflength);
		if (rx->lastper < per){
			fprintf(stderr,"read %3d%%\r", (int)per);
			rx->lastper = per;
		}
		if (rx->finread >= rx->inflength && rx->inputFiles && rx->inputFiles[rx->inputIdx + 1]) {
			close(rx->fd_in);
			rx->inputIdx ++;
			if ((rx->fd_in = open(rx->inputFiles[rx->inputIdx] ,O_RDONLY| O_LARGEFILE)) < 0) {
				fprintf(stderr,"Error opening input file %s",rx->inputFiles[rx->inputIdx] );
				exit(1);
			}
			fprintf(stderr,"Reading from %s\n", rx->inputFiles[rx->inputIdx]);
			rx->inflength = lseek(rx->fd_in, 0, SEEK_END);
			fprintf(stderr,"Input file length: %.2f MB\n",rx->inflength/1024./1024.);
			lseek(rx->fd_in,0,SEEK_SET);
			rx->lastper = 0;
			rx->finread = 0;
		}
	} else fprintf(stderr,"read %.2f MB\r", rx->finread/1024./1024.);
#endif
	if (neof < 0 && re == 0) return neof;
	else return re;
}

int guess_fill( struct replex *rx)
{
	int vavail, aavail, ac3avail, i, fill;

	vavail = 0;
	ac3avail =0;
	aavail = 0;
	fill =0;
	
#define LIMIT 3
	if ((vavail = ring_avail(&rx->index_vrbuffer)/sizeof(index_unit))
	    < LIMIT) 
		fill = ring_free(&rx->vrbuffer);
	
	for (i=0; i<rx->apidn;i++){
		if ((aavail = ring_avail(&rx->index_arbuffer[i])
		     /sizeof(index_unit)) < LIMIT)
			if (fill < ring_free(&rx->arbuffer[i]))
				fill = ring_free(&rx->arbuffer[i]);
	}

	for (i=0; i<rx->ac3n;i++){
		if ((ac3avail = ring_avail(&rx->index_ac3rbuffer[i])
		     /sizeof(index_unit)) < LIMIT)
			if (fill < ring_free(&rx->ac3rbuffer[i]))
				fill = ring_free(&rx->ac3rbuffer[i]);
	}

//	fprintf(stderr,"free %d  %d %d %d\n",fill, vavail, aavail, ac3avail);

	if (!fill){ 
		if(vavail && (aavail || ac3avail)) return 0;
		else return -1;
	}

	return fill/2;
}



#define IN_SIZE (1000*TS_SIZE)
void find_pids_file(struct replex *rx)
{
	uint8_t buf[IN_SIZE];
	int afound=0;
	int vfound=0;
	int count=0;
	int re=0;
	uint16_t vpid=0, apid=0, ac3pid=0;
	
	fprintf(stderr,"Trying to find PIDs\n");
	while (!afound && !vfound && count < rx->inflength){
		if (rx->vpid) vfound = 1;
		if (rx->apidn) afound = 1;
		if ((re = save_read(rx,buf,IN_SIZE))<0)
			perror("reading");
		else
			count += re;
		if ( (re = find_pids(&vpid, &apid, &ac3pid, buf, re))){
			if (!rx->vpid && vpid){
				rx->vpid = vpid;
				fprintf(stderr,"vpid 0x%04x  \n",
					(int)rx->vpid);
				vfound++;
			}
			if (!rx->apidn && apid){
				rx->apid[0] = apid;
				fprintf(stderr,"apid 0x%04x  \n",
					(int)rx->apid[0]);
				rx->apidn++;
				afound++;
			}
			if (!rx->ac3n && ac3pid){
				rx->ac3_id[0] = ac3pid;
				fprintf(stderr,"ac3pid 0x%04x  \n",
					(int)rx->ac3_id[0]);
				rx->ac3n++;
				afound++;
			}
			
		}
		
	}
	
	lseek(rx->fd_in,0,SEEK_SET);
	if (!afound || !vfound){
		fprintf(stderr,"Couldn't find all pids\n");
		exit(1);
	}
	
}

#define MAXVPID 16
#define MAXAPID 32
#define MAXAC3PID 16
void find_all_pids_file(struct replex *rx)
{
	uint8_t buf[IN_SIZE];
	int count=0;
	int j;
	int re=0;
	uint16_t vpid[MAXVPID], apid[MAXAPID], ac3pid[MAXAC3PID];
	int vn=0, an=0,ac3n=0;
	uint16_t vp,ap,cp;
	int vpos, apos, cpos;
	
	memset (vpid , 0 , MAXVPID*sizeof(uint16_t));
	memset (apid , 0 , MAXAPID*sizeof(uint16_t));
	memset (ac3pid , 0 , MAXAC3PID*sizeof(uint16_t));
	
	fprintf(stderr,"Trying to find PIDs\n");
	while (count < rx->inflength-IN_SIZE){
		if ((re = save_read(rx,buf,IN_SIZE))<0)
			perror("reading");
		else
			count += re;
		if ( (re = find_pids_pos(&vp, &ap, &cp, buf, re,
					 &vpos, &apos, &cpos))){
			if (vp){
				int old=0;
				for (j=0; j < vn; j++){
//					printf("%d. %d\n",j+1,vpid[j]);
					if (vpid[j] == vp){
						old = 1;
						break;
					}
				}
				if (!old){
					vpid[vn]=vp;
					
					printf("vpid %d: 0x%04x (%d)  PES ID: 0x%02x\n",
					       vn+1,
					       (int)vpid[vn], (int)vpid[vn],
					       buf[vpos]);
					if (vn+1 < MAXVPID) vn++;
				}
			}
			
			if (ap){
				int old=0;
				for (j=0; j < an; j++)
					if (apid[j] == ap){
						old = 1;
						break;
					}
				if (!old){
					apid[an]=ap;
					printf("apid %d: 0x%04x (%d)  PES ID: 0x%02x\n",
					       an +1,
					       (int)apid[an],(int)apid[an],
					       buf[apos]);
					if (an+1 < MAXAPID) an++;
				}
			}
			
			if (cp){
				int old=0;
				for (j=0; j < ac3n; j++)
					if (ac3pid[j] == cp){
						old = 1;
						break;
					}
				if (!old){
					ac3pid[ac3n]=cp;
					printf("ac3pid %d: 0x%04x (%d) \n",
					       ac3n+1,
					       (int)ac3pid[ac3n],
					       (int)ac3pid[ac3n]);
					if (ac3n+1< MAXAC3PID) ac3n++;
				}
			}
		}
		
	}
	
	lseek(rx->fd_in,0,SEEK_SET);
}

void find_pids_stdin(struct replex *rx, uint8_t *buf, int len)
{
	int afound=0;
	int vfound=0;
	uint16_t vpid=0, apid=0, ac3pid=0;
	
	if (rx->vpid) vfound = 1;
	if (rx->apidn) afound = 1;
	fprintf(stderr,"Trying to find PIDs\n");
	if ( find_pids(&vpid, &apid, &ac3pid, buf, len) ){
		if (!rx->vpid && vpid){
			rx->vpid = vpid;
			vfound++;
		}
		if (!rx->apidn && apid){
			rx->apid[0] = apid;
			rx->apidn++;
			afound++;
			ring_init(&rx->arbuffer[0], rx->audiobuf);
			init_pes_in(&rx->paudio[0], 1, &rx->arbuffer[0], 0);
			rx->paudio[0].priv = (void *) rx;
			ring_init(&rx->index_arbuffer[0], INDEX_BUF);
			memset(&rx->aframe[0], 0, sizeof(audio_frame_t));
			init_index(&rx->current_aindex[0]);
			rx->aframe_count[0] = 0;
			rx->first_apts[0] = 0;
		}
		
		if (!rx->ac3n && ac3pid){
			rx->ac3_id[0] = ac3pid;
			rx->ac3n++;
			afound++;
			ring_init(&rx->ac3rbuffer[0], rx->ac3buf);
			init_pes_in(&rx->pac3[0], 0x80, &rx->ac3rbuffer[0],0);
			rx->pac3[0].priv = (void *) rx;
			ring_init(&rx->index_ac3rbuffer[0], INDEX_BUF);
			memset(&rx->ac3frame[0], 0, sizeof(audio_frame_t));
			init_index(&rx->current_ac3index[0]);
			rx->ac3frame_count[0] = 0;
			rx->first_ac3pts[0] = 0;
		}
		
	}
	
	if (afound && vfound){
		fprintf(stderr,"found ");
		if (rx->vpid) fprintf(stderr,"vpid %d (0x%04x)  ",
				      rx->vpid, rx->vpid);
		if (rx->apidn) fprintf(stderr,"apid %d (0x%04x)  ",
				       rx->apid[0], rx->apid[0]);
		if (rx->ac3n) fprintf(stderr,"ac3pid %d (0x%04x)  ",
				      rx->ac3_id[0], rx->ac3_id[0]);
		fprintf(stderr,"\n");
	}
	else {
		fprintf(stderr,"Couldn't find pids\n");
		exit(1);
	}
	
}


void pes_id_out(pes_in_t *p)
{
	
	struct replex *rx;
	int len;
	
	len = p->plength-3-p->hlength;
	rx = (struct replex *) p->priv;
	
	rx->scan_found=0;
	switch (p->cid){
	case VIDEO_STREAM_S ... VIDEO_STREAM_E:
		rx->scan_found=p->cid;
		break;
		
	case AUDIO_STREAM_S ... AUDIO_STREAM_E:
		rx->scan_found=p->cid;
		break;
		
	case PRIVATE_STREAM1:
		if (rx->vdr){
			rx->scan_found = 0x80;
			break;
		}
		else {
			
			uint8_t id = p->buf[9+p->hlength];
			switch (id){
			case 0x80 ... 0x8f:
			{
				int c=0;
				uint16_t fframe;
				
				fframe=0;
				fframe = p->buf[9+p->hlength+3];
				fframe |= (p->buf[9+p->hlength+2]<<8);
				
				if (fframe < p->plength){
					if ((c=find_audio_s(p->buf,
							    9+p->hlength+4+fframe,
							    AC3, p->plength+6)) >= 0){
						rx->scan_found = id;
						//fprintf(stderr,"0x%04x  0x%04x \n",
						//c-9-p->hlength-4,fframe);
//					if (id>0x80)show_buf(p->buf+9+p->hlength,8);
						//				if (id>0x80)show_buf(p->buf+c,8);
					}
				}
				break;
			}
			}
			break;
		}
	default:
		p->cid = 0;
		p->type = 0;
		rx->scan_found=0;
		memset(p->buf,0,MAX_PLENGTH*sizeof(uint8_t));
		return;
	}
}

void find_pes_ids(struct replex *rx)
{
	uint8_t buf[IN_SIZE];
	int count=0;
	int j;
	int re=0;
	uint16_t vpid[MAXVPID], apid[MAXAPID], ac3pid[MAXAC3PID];
	int vn=0, an=0,ac3n=0;
	
	memset (vpid , 0 , MAXVPID*sizeof(uint16_t));
	memset (apid , 0 , MAXAPID*sizeof(uint16_t));
	memset (ac3pid , 0 , MAXAC3PID*sizeof(uint16_t));
	
	fprintf(stderr,"Trying to find PES IDs\n");
	rx->scan_found=0;
	rx->pvideo.priv = rx ;
	while (count < 50000000 && count < rx->inflength-IN_SIZE){
		if ((re = save_read(rx,buf,IN_SIZE))<0)
			perror("reading");
		else
			count += re;
		
		get_pes(&rx->pvideo, buf, re, pes_id_out);
		
		if ( rx->scan_found ){
			
			switch (rx->scan_found){
				
			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
			{
				int old=0;
				for (j=0; j < vn; j++){
//					printf("%d. %d\n",j+1,vpid[j]);
					if (vpid[j] == rx->scan_found){
						old = 1;
						break;
					}
				}
				if (!old){
					vpid[vn]=rx->scan_found;
					
					printf("MPEG VIDEO %d: 0x%02x (%d)\n",
					       vn+1,
					       (int)vpid[vn], (int)vpid[vn]);
					if (vn+1 < MAXVPID) vn++;
				}
			}
			break;
			
			
			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
			{
				int old=0;
				for (j=0; j < an; j++)
					if (apid[j] == rx->scan_found){
						old = 1;
						break;
					}
				if (!old){
					apid[an] = rx->apid[an] = rx->scan_found;
					printf("MPEG AUDIO %d: 0x%02x (%d)\n",
					       an +1,
					       (int)apid[an],(int)apid[an]);
					if (an+1 < MAXAPID) an++;
				}
			}
			break;
			
			case 0x80 ... 0x8f:
			{
				int old=0;
				for (j=0; j < ac3n; j++)
					if (ac3pid[j] == rx->scan_found){
						old = 1;
						break;
					}
				if (!old){
					ac3pid[ac3n]=rx->ac3_id[ac3n]=rx->scan_found;
					if (rx->vdr){
						printf("possible AC3 AUDIO with private stream 1 pid (0xbd) \n");
					}
					else {
						printf("AC3 AUDIO %d: 0x%02x (%d) \n",
						       ac3n+1,
						       (int)ac3pid[ac3n],
						       (int)ac3pid[ac3n]);
					}
					if (ac3n+1< MAXAC3PID) ac3n++;
				}
				rx->scan_found = 0;
			}
			break;
			}
			rx->scan_found = 0;
		}
	}
	rx->ac3n = ac3n;
	rx->apidn = an;
}



void replex_finish(struct replex *rx)
{
	
	fprintf(stderr,"\n");
	if (!replex_all_set(rx)){
		fprintf(stderr,"Can't find all required streams\n");
		if (rx->itype == REPLEX_PS){
			fprintf(stderr,"Please check if audio and video have standard IDs (0xc0 or 0xe0)\n");
		}
		exit(1);
	}
	
	if (!rx->demux)
		finish_mpg((multiplex_t *)rx->priv);
	exit(0);
}

int replex_fill_buffers(struct replex *rx, uint8_t *mbuf)
{
	uint8_t buf[IN_SIZE];
	int i,j;
	int count=0;
	int fill;
	int re;
	int rsize;
	int tries = 0;

	if (rx->finish) return 0;
	fill =  guess_fill(rx);
	//fprintf(stderr,"trying to fill buffers with %d\n",fill);
	if (fill < 0) return -1;

	memset(buf, 0, IN_SIZE);
	
	switch(rx->itype){
	case REPLEX_TS:
		if (fill < IN_SIZE){
			rsize = fill - (fill%188);
		} else rsize = IN_SIZE;
		
//	fprintf(stderr,"filling with %d\n",rsize);
		
		if (!rsize) return 0;
		
		memset(buf, 0, IN_SIZE);
		
		if ( mbuf ){
			for ( i = 0; i < 188 ; i++){
				if ( mbuf[i] == 0x47 ) break;
			}
			
			if ( i == 188){
				fprintf(stderr,"Not a TS\n");
				return -1;
			} else {
				memcpy(buf,mbuf+i,2*TS_SIZE-i);
				if ((count = save_read(rx,mbuf,i))<0)
					perror("reading");
				memcpy(buf+2*TS_SIZE-i,mbuf,i);
				i = 188;
			}
		} else i=0;

	
#define MAX_TRIES 5
		while (count < rsize && tries < MAX_TRIES){
			if ((re = save_read(rx,buf+i,rsize-i)+i)<0)
				perror("reading");
			else 
				count += re;
			tries++;
			
			if (!rx->vpid || !(rx->apidn || rx->ac3n)){
				find_pids_stdin(rx, buf, re);
			}

			for( j = 0; j < re; j+= TS_SIZE){
				
				if ( re - j < TS_SIZE) break;
				
				if ( replex_tsp( rx, buf+j) < 0){
					fprintf(stderr, "Error reading TS\n");
					exit(1);
				}
			}
			i=0;
		}
		
		if (tries == MAX_TRIES)
			replex_finish(rx);
		return 0;
		break;
		
	case REPLEX_PS:
		rsize = fill;
		if (fill > IN_SIZE) rsize = IN_SIZE; 
		if (mbuf)
			get_pes(&rx->pvideo, mbuf, 2*TS_SIZE, pes_es_out);
		
		while (count < rsize && tries < MAX_TRIES){
			if ((re = save_read(rx, buf, rsize))<0)
				perror("reading PS");
			else 
				count += re;
	
			get_pes(&rx->pvideo, buf, re, pes_es_out);
			
			tries++;
			
		}
		
		if (tries == MAX_TRIES)
			replex_finish(rx);
		return 0;
		break;


	case REPLEX_AVI:
		rsize = fill;
		if (fill > IN_SIZE) rsize = IN_SIZE; 
		
		if (!(rx->ac.avih_flags & AVI_HASINDEX)){

			if (mbuf){
				get_avi(&rx->pvideo, mbuf, rx->avi_rest, avi_es_out);
			}

			while (count < rsize && tries < MAX_TRIES){
				if ((re = save_read(rx, buf, rsize))<0)
					perror("reading AVI");
				else 
					count += re;
				
				get_avi(&rx->pvideo, buf, re, avi_es_out);
				
				tries++;
			}
		} else {
			if (get_avi_from_index(&rx->pvideo, rx->fd_in,
					       &rx->ac, avi_es_out, rsize) < 0)
				tries = MAX_TRIES;
		}

		if (tries == MAX_TRIES)
			replex_finish(rx);
		return 0;
		break;
	}
	
	return -1;
}

int fill_buffers(void *r, int finish)
{
	struct replex *rx = (struct replex *)r;
	
	rx->finish = finish;

	return replex_fill_buffers(rx, NULL);
}


void init_index(index_unit *iu)
{
	memset(iu,0, sizeof(index_unit));
	iu->frame_start = 1;
}

static int replex_all_set(struct replex *rx)
{
	int i;
	int set=0;

	for (i=0;  i < rx->ac3n ;i++){
		set += rx->ac3frame[i].set;
	}
	for (i=0; i<rx->apidn;i++){
		set += rx->aframe[i].set;
	}
	set += rx->seq_head.set;

	if (set == (rx->ac3n+ rx->apidn + 1)) return 1;
	return 0;
}


int check_stream_type(struct replex *rx, uint8_t * buf, int len)
{
	int c=0;
	avi_context ac;
	uint8_t head;

	if (rx->itype != REPLEX_TS) return rx->itype;

	if (len< 2*TS_SIZE){
		fprintf(stderr,"cannot determine streamtype");
		exit(1);
	}

	fprintf(stderr, "Checking for TS: ");
	while (c < len && buf[c]!=0x47) c++;
	if (c<len && len-c>=TS_SIZE){
		if (buf[c+TS_SIZE] == 0x47){
			fprintf(stderr,"confirmed\n");
			return REPLEX_TS;
		} else  fprintf(stderr,"failed\n");
	} else  fprintf(stderr,"failed\n");

	fprintf(stderr, "Checking for AVI: ");
	if (check_riff(&ac, buf, len)>=0){
		fprintf(stderr,"confirmed\n");
		rx->itype = REPLEX_AVI;
		rx->vpid = 0xE0;
		rx->apidn = 1;
		rx->apid[0] = 0xC0;
		rx->ignore_pts =1;
		return REPLEX_AVI;
	} else fprintf(stderr,"failed\n");

	fprintf(stderr, "Checking for PS: ");
	if (find_any_header(&head, buf, len) >= 0){
		fprintf(stderr,"confirmed(maybe)\n");
	} else {
		fprintf(stderr,"failed, trying it anyway\n");
	}
	rx->itype=REPLEX_PS;
	if (!rx->vpid) rx->vpid = 0xE0;
	if (!(rx->apidn || rx->ac3n)){
		rx->apidn = 1;
		rx->apid[0] = 0xC0;
	}
	return REPLEX_PS;
}


void init_replex(struct replex *rx,int bufsize)
{
	int i;
	uint8_t mbuf[2*TS_SIZE];
	
	int VIDEO_BUF, AUDIO_BUF, AC3_BUF;

	VIDEO_BUF = bufsize;
	AUDIO_BUF = (VIDEO_BUF/10);
	AC3_BUF   = (VIDEO_BUF/10);

	rx->analyze=0;

	if (save_read(rx, mbuf, 2*TS_SIZE)<0)
		perror("reading");
	
	check_stream_type(rx, mbuf, 2*TS_SIZE);
	if (rx->itype == REPLEX_TS){
		if (!rx->vpid || !(rx->apidn || rx->ac3n)){
			if (rx->inflength){
				find_pids_file(rx);
			}
		}
	}	

	if (rx->otype==REPLEX_HDTV){
		rx->videobuf = 4*VIDEO_BUF;
	} else {
		rx->videobuf = VIDEO_BUF;
	}
	rx->audiobuf = AUDIO_BUF;
	rx->ac3buf = AC3_BUF;

	if (rx->itype == REPLEX_AVI){
		rx->videobuf = 4*VIDEO_BUF;
		rx->audiobuf = 2*rx->videobuf;
	}
	
	rx->vpes_abort = 0;
	rx->first_iframe = 0;
	ring_init(&rx->vrbuffer, rx->videobuf);
	if (rx->itype == REPLEX_TS || rx->itype == REPLEX_AVI)
		init_pes_in(&rx->pvideo, 0xE0, &rx->vrbuffer, 0);
	else if (rx->itype == REPLEX_PS){
		init_pes_in(&rx->pvideo, 0, NULL, 1);
//		find_pes_in(rx);
	} else init_pes_in(&rx->pvideo, 0, NULL, 1);
	
	rx->pvideo.priv = (void *) rx;
	ring_init(&rx->index_vrbuffer, INDEX_BUF);
	memset(&rx->seq_head, 0, sizeof(sequence_t));
	init_index(&rx->current_vindex);
	rx->vgroup_count = 0;
	rx->vframe_count = 0;
	rx->first_vpts = 0;
	rx->video_state = S_SEARCH;
	rx->last_vpts = 0;

	for (i=0; i<rx->apidn;i++){
		
		rx->apes_abort[i] = 0;
		rx->audio_state[i] = S_SEARCH;
		ring_init(&rx->arbuffer[i], rx->audiobuf);

		if (rx->itype == REPLEX_TS){
			init_pes_in(&rx->paudio[i], i+1, 
				    &rx->arbuffer[i], 0);
			rx->paudio[i].priv = (void *) rx;
		}
		ring_init(&rx->index_arbuffer[i], INDEX_BUF);	
		memset(&rx->aframe[i], 0, sizeof(audio_frame_t));
		init_index(&rx->current_aindex[i]);
		rx->aframe_count[i] = 0;
		rx->first_apts[i] = 0;
		rx->last_apts[i] = 0;
	}

	for (i=0; i<rx->ac3n;i++){
		rx->ac3pes_abort[i] = 0;
		rx->ac3_state[i] = S_SEARCH;
		ring_init(&rx->ac3rbuffer[i], AC3_BUF);
		if (rx->itype == REPLEX_TS){
			init_pes_in(&rx->pac3[i], 0x80+i, 
				    &rx->ac3rbuffer[i],0);
			rx->pac3[i].priv = (void *) rx;
		}
		ring_init(&rx->index_ac3rbuffer[i], INDEX_BUF);
		memset(&rx->ac3frame[i], 0, sizeof(audio_frame_t));
		init_index(&rx->current_ac3index[i]);
		rx->ac3frame_count[i] = 0;
		rx->first_ac3pts[i] = 0;
		rx->last_ac3pts[i] = 0;
	}	
	
	if (rx->itype == REPLEX_TS){
		if (replex_fill_buffers(rx, mbuf)< 0){
			fprintf(stderr,"error filling buffer\n");
			exit(1);
		}
	} else if ( rx->itype == REPLEX_AVI){
#define AVI_S 1024
		avi_context *ac;
		uint8_t buf[AVI_S];
		int re=0;
		
		lseek(rx->fd_in, 0, SEEK_SET);
		ac = &rx->ac;
		memset(ac, 0, sizeof(avi_context));
		save_read(rx, buf, 12);
		
		if (check_riff(ac, buf, 12) < 0){
			fprintf(stderr, "Wrong RIFF header\n");
			exit(1);
		} else {
			fprintf(stderr,"Found RIFF header\n");
		}
		
		memset(ac, 0, sizeof(avi_context));
		re = read_avi_header(ac, rx->fd_in);
		if (avi_read_index(ac,rx->fd_in) < 0){
			fprintf(stderr, "Error reading index\n");
			exit(1);
		}
//		rx->aframe_count[0] = ac->ai[0].initial_frames;
		rx->vframe_count = ac->ai[0].initial_frames*ac->vi.fps/
			ac->ai[0].fps;

		rx->inflength = lseek(rx->fd_in, 0, SEEK_CUR)+ac->movi_length;

		fprintf(stderr,"AVI initial frames %d\n",
			(int)rx->vframe_count);
		if (!ac->done){
			fprintf(stderr,"Error reading AVI header\n");
			exit(1);
		}

		if (replex_fill_buffers(rx, buf+re)< 0){
			fprintf(stderr,"error filling buffer\n");
			exit(1);
		}
	} else {
		if (replex_fill_buffers(rx, mbuf)< 0){
			fprintf(stderr,"error filling buffer\n");
			exit(1);
		}
	}

}


void fix_audio(struct replex *rx, multiplex_t *mx)
{
	int i;
	index_unit aiu;
	int size;

	size = sizeof(index_unit);

	for ( i = 0; i < rx->apidn; i++){
		do {
			while (ring_avail(&rx->index_arbuffer[i]) < 
			       sizeof(index_unit)){
				if (replex_fill_buffers(rx, 0)< 0){
					fprintf(stderr,
						"error in fix audio\n");
					exit(1);
				}	
			}
			ring_peek(&rx->index_arbuffer[i], (uint8_t *)&aiu, 
				  size, 0);
			if ( ptscmp(aiu.pts + rx->first_apts[i], rx->first_vpts) < 0){
				ring_skip(&rx->index_arbuffer[i], size);
				ring_skip(&rx->arbuffer[i], aiu.length);
			} else break;

		} while (1);
		mx->apts_off[i] = aiu.pts;
		rx->apts_off[i] = aiu.pts;
		mx->aframes[i] = aiu.framesize;
		
		fprintf(stderr,"Audio%d  offset: ",i);
		printpts(mx->apts_off[i]);
		printpts(rx->first_apts[i]+mx->apts_off[i]);
		fprintf(stderr,"\n");
	}
			  
	for ( i = 0; i < rx->ac3n; i++){
		do {
			while (ring_avail(&rx->index_ac3rbuffer[i]) < 
			       sizeof(index_unit)){
				if (replex_fill_buffers(rx, 0)< 0){
					fprintf(stderr,
						"error in fix audio\n");
					exit(1);
				}	
			}
			ring_peek(&rx->index_ac3rbuffer[i],(uint8_t *) &aiu, 
				  size, 0);
			if ( ptscmp (aiu.pts+rx->first_ac3pts[i], rx->first_vpts) < 0){
				ring_skip(&rx->index_ac3rbuffer[i], size);
				ring_skip(&rx->ac3rbuffer[i], aiu.length);
			} else break;
		} while (1);
		mx->ac3pts_off[i] = aiu.pts;
		rx->ac3pts_off[i] = aiu.pts;
		
		fprintf(stderr,"AC3%d  offset: ",i);
		printpts(mx->ac3pts_off[i]);
		printpts(rx->first_ac3pts[i]+mx->ac3pts_off[i]);
		fprintf(stderr,"\n");

	}
}



static int get_next_video_unit(struct replex *rx, index_unit *viu)
{
	if (ring_avail(&rx->index_vrbuffer)){
		ring_read(&rx->index_vrbuffer, (uint8_t *)viu, 
			  sizeof(index_unit));
		return 1;
	}
	return 0;
}

static int get_next_audio_unit(struct replex *rx, index_unit *aiu, int i)
{
	if(ring_avail(&rx->index_arbuffer[i])){
		ring_read(&rx->index_arbuffer[i], (uint8_t *)aiu, 
			  sizeof(index_unit));
		return 1;
	}
	return 0;
}

static int get_next_ac3_unit(struct replex *rx, index_unit *aiu, int i)
{
	if (ring_avail(&rx->index_ac3rbuffer[i])){
		ring_read(&rx->index_ac3rbuffer[i], (uint8_t *)aiu, 
			  sizeof(index_unit));
		
		return 1;
	} 
	return 0;
}


void do_analyze(struct replex *rx)
{
	index_unit dummy;
	index_unit dummy2;
	int i;
	uint64_t lastvpts;
	uint64_t lastvdts;
	uint64_t lastapts[N_AUDIO];
	uint64_t lastac3pts[N_AC3];
	int av;
	
	av = rx->analyze-1;

	lastvpts = 0;
	lastvdts = 0;
	memset(lastapts, 0, N_AUDIO*sizeof(uint64_t));
	memset(lastac3pts, 0, N_AC3*sizeof(uint64_t));
	
	fprintf(stderr,"STARTING ANALYSIS\n");
	
	
	while(!rx->finish){
		if (replex_fill_buffers(rx, 0)< 0){
			fprintf(stderr,"error in get next video unit\n");
			return;
		}
		for (i=0; i< rx->apidn; i++){
			while(get_next_audio_unit(rx, &dummy2, i)){
				ring_skip(&rx->arbuffer[i], 
					  dummy2.length);
				if (av>=1){
					fprintf(stdout,
						"MPG2 Audio%d unit: ",
						i);
					fprintf(stdout,
						" length %d  PTS ",
						dummy2.length);
					printptss(dummy2.pts);
					
					if (lastapts[i]){
						fprintf(stdout,"  diff:");
						printptss(ptsdiff(dummy2.pts,lastapts[i]));
					}
					lastapts[i] = dummy2.pts;
					
					fprintf(stdout,"\n");
				}
			}
		}
		
		for (i=0; i< rx->ac3n; i++){
			while(get_next_ac3_unit(rx, &dummy2, i)){
				ring_skip(&rx->ac3rbuffer[i], 
					  dummy2.length);
				if (av>=1){
					fprintf(stdout,
						"AC3 Audio%d unit: ",
						i);
					fprintf(stdout,
						" length %d  PTS ",
						dummy2.length);
					printptss(dummy2.pts);
					if (lastac3pts[i]){
						fprintf(stdout,"  diff:");
						printptss(ptsdiff(dummy2.pts, lastac3pts[i]));
					}
					lastac3pts[i] = dummy2.pts;
					
					fprintf(stdout,"\n");
				}
			}
		}
		
		while (get_next_video_unit(rx, &dummy)){
			ring_skip(&rx->vrbuffer,
				  dummy.length);
			if (av==0 || av==2){
				fprintf(stdout,
					"Video unit: ");
	                        if (dummy.seq_header){
					fprintf(stdout,"Sequence header ");
				}
				if (dummy.gop){
					fprintf(stdout,"GOP header ");
				}
				switch (dummy.frame){
				case I_FRAME:
					fprintf(stdout, "I-frame");
					break;
				case B_FRAME:
					fprintf(stdout, "B-frame");
					break;
				case P_FRAME:
					fprintf(stdout, "P-frame");
					break;
				}
				fprintf(stdout,
					" length %d  PTS ",
					dummy.length);
				printptss(dummy.pts);
				if (lastvpts){
					fprintf(stdout,"  diff:");
					printptss(ptsdiff(dummy.pts, lastvpts));
				}
				lastvpts = dummy.pts;
					
				fprintf(stdout,
					"  DTS ");
				printptss(dummy.dts);
				if (lastvdts){
					fprintf(stdout,"  diff:");
					printptss(ptsdiff(dummy.dts,lastvdts));
				}
				lastvdts = dummy.dts;
					
				fprintf(stdout,"\n");
			}
		}
	}


}

void do_scan(struct replex *rx)
{
	uint8_t mbuf[2*TS_SIZE];
	
	rx->analyze=0;

	if (save_read(rx, mbuf, 2*TS_SIZE)<0)
		perror("reading");
	
	fprintf(stderr,"STARTING SCAN\n");
	
	check_stream_type(rx, mbuf, 2*TS_SIZE);

	switch(rx->itype){
	case REPLEX_TS:
		find_all_pids_file(rx);
		break;
		
	case REPLEX_PS:
		init_pes_in(&rx->pvideo, 0, NULL, 1);
		find_pes_ids(rx);
		break;

	case REPLEX_AVI:
		break;
	}
	
}

void do_demux(struct replex *rx)
{
	index_unit dummy;
	index_unit dummy2;
	int i;
	multiplex_t mx;
	fprintf(stderr,"STARTING DEMUX\n");


	while (!replex_all_set(rx)){
		if (replex_fill_buffers(rx, 0)< 0){
			fprintf(stderr,"error filling buffer\n");
			exit(1);
		}
	}

	fix_audio(rx, &mx);
	
	while(!rx->finish){
		if (replex_fill_buffers(rx, 0)< 0){
			fprintf(stderr,"error in get next video unit\n");
			return;
		}
		for (i=0; i< rx->apidn; i++){
			while(get_next_audio_unit(rx, &dummy2, i)){
				switch(dummy2.err){
				case JUMP_ERR:
					ring_skip(&rx->arbuffer[i],dummy2.length);
					break;
				case DUMMY_ERR:
					write(rx->dmx_out[i+1],dummy.fillframe,dummy2.length);
					break; 
				default:
					ring_read_file(&rx->arbuffer[i], 
						       rx->dmx_out[i+1], 
						       dummy2.length);
				}
			}
		}
		
		for (i=0; i< rx->ac3n; i++){
			while(get_next_ac3_unit(rx, &dummy2, i)){
				switch(dummy2.err){
				case JUMP_ERR:
					ring_skip(&rx->ac3rbuffer[i],dummy2.length);
					break;
				case DUMMY_ERR:
					write(rx->dmx_out[i+1+rx->apidn],dummy.fillframe,dummy2.length);
					break; 
				default:
					ring_read_file(&rx->ac3rbuffer[i], 
						       rx->dmx_out[i+1+rx->apidn], 
						       dummy2.length);
				}
			}
		}
		
		while (get_next_video_unit(rx, &dummy)){
			ring_read_file(&rx->vrbuffer, rx->dmx_out[0], 
				       dummy.length);
		}
	}
}


void do_replex(struct replex *rx)
{
	int video_ok = 0;
	int audio_ok[N_AUDIO];
	int ac3_ok[N_AC3];
	int start=1;
	multiplex_t mx;
	int done = 0;


	fprintf(stderr,"STARTING REPLEX\n");
	memset(&mx, 0, sizeof(mx));
	memset(audio_ok, 0, N_AUDIO*sizeof(int));
	memset(ac3_ok, 0, N_AC3*sizeof(int));

	while (!replex_all_set(rx)){
		if (replex_fill_buffers(rx, 0)< 0){
			fprintf(stderr,"error filling buffer\n");
			exit(1);
		}
	}

	mx.priv = (void *) rx;
	rx->priv = (void *) &mx;
	init_multiplex(&mx, &rx->seq_head, rx->aframe, rx->ac3frame, 
		       rx->apidn, rx->ac3n, rx->video_delay, 
		       rx->audio_delay, rx->fd_out, fill_buffers,
		       &rx->vrbuffer, &rx->index_vrbuffer,	
		       rx->arbuffer, rx->index_arbuffer,
		       rx->ac3rbuffer, rx->index_ac3rbuffer, rx->otype);

	if (!rx->ignore_pts){ 
		fix_audio(rx, &mx);
	}
	setup_multiplex(&mx);

	do {
		check_times( &mx, &video_ok, audio_ok, ac3_ok, &start);

		write_out_packs( &mx, video_ok, audio_ok, ac3_ok);
	
		if (mx.max_reached) done = 1;
		if (mx.zero_write_count >100){
			fprintf(stderr,"Can`t continue, check input file\n");
			done=1;
		}
	} while (!done);
	
}


void usage(char *progname)
{
        printf ("usage: %s [options] <input files>\n\n",progname);
        printf ("options:\n");
        printf ("  --help,             -h            :  print help message\n");
        printf ("\n");
        printf ("  --audio_pid,        -a <integer>  :  audio PID for TS stream (also used for PS id, default 0xc0)\n");
        printf ("  --ac3_id,           -c <integer>  :  ID of AC3 audio for demux (also used for PS id, i.e. 0x80)\n");
        printf ("  --video_delay,      -d <integer>  :  video delay in ms\n");
        printf ("  --audio_delay,      -e <integer>  :  audio delay in ms\n");
        printf ("  --ignore_PTS,       -f            :  ignore all PTS information of original\n");
	printf ("  --larger_buffer     -g <integer>  :  video buffer in MB\n"); 
        printf ("  --input_stream,     -i <string>   :  set input stream type (string = TS(default), PS, AVI)\n");
        printf ("  --allow_jump,       -j            :  allow jump in the PTS and try repair\n");
        printf ("  --keep_PTS,         -k            :  keep and don't correct PTS information of original\n");
	printf ("  --min_jump,         -l <integer>  :  don't try to fix jumps in PTS larger than <int> but treat them as a cut (default 100ms)\n");
        printf ("  --of,               -o <filename> :  set output file\n");
	printf ("  --fillzero          -p            :  fill audio frames with zeros (only MPEG AUDIO)\n");
	printf ("  --max_overflow      -q <integer>  :  max_number of overflows allowed (default: 100, 0=no restriction)\n");
        printf ("  --scan,             -s            :  scan for streams\n");
        printf ("  --type,             -t <string>   :  set output type (string = MPEG2, DVD, HDTV)\n");
        printf ("  --video_pid,        -v <integer>  :  video PID for TS stream (also used for PS id, default 0xe0)\n");
        printf ("  --vdr,              -x            :  handle AC3 for vdr input file\n");
        printf ("  --analyze,          -y <integer>  :  analyze (0=video,1=audio, 2=both)\n");
        printf ("  --demux,            -z            :  demux only (-o is basename)\n");
        exit(1);
}

int main(int argc, char **argv)
{
        int c;
	int analyze=0;
	int scan =0;
        char *filename = NULL;
        char *type = "SVCD";
        char *inpt = "TS";
	int bufsize = 6*1024*1024;
	uint64_t min_jump=0;
	int fillzero = 0;

	struct replex rx;

	fprintf(stderr,"replex version %s\n", VERSION);

	memset(&rx, 0, sizeof(struct replex));
	rx.max_overflows = 100;

        while (1){
                int option_index = 0;
                static struct option long_options[] = {
			{"audio_pid", required_argument, NULL, 'a'},
			{"ac3_id", required_argument, NULL, 'c'},
			{"video_delay", required_argument, NULL, 'd'},
			{"audio_delay", required_argument, NULL, 'e'},
			{"ignore_PTS",required_argument, NULL, 'f'},
			{"larger_buffer",required_argument, NULL, 'g'},
			{"help", no_argument , NULL, 'h'},
			{"input_stream", required_argument, NULL, 'i'},
			{"allow_jump",required_argument, NULL, 'j'},
			{"keep_PTS",required_argument, NULL, 'k'},
			{"min_jump",required_argument, NULL, 'l'},
			{"of",required_argument, NULL, 'o'},
			{"fillzero",required_argument, NULL, 'p'},
			{"max_overflow",required_argument, NULL, 'q'},
			{"scan",required_argument, NULL, 's'},
			{"type", required_argument, NULL, 't'},
			{"video_pid", required_argument, NULL, 'v'},
			{"vdr",required_argument, NULL, 'x'},
			{"analyze",required_argument, NULL, 'y'},
			{"demux",no_argument, NULL, 'z'},
			{0, 0, 0, 0}
		};
                c = getopt_long (argc, argv, 
				 "a:c:d:e:fg:hi:jkl:o:pq:st:v:xy:z",
                                 long_options, &option_index);
                if (c == -1)
                        break;

                switch (c){
                case 'a':
			if (rx.apidn==N_AUDIO){
				fprintf(stderr,"Too many audio PIDs\n");
				exit(1);
			}
                        rx.apid[rx.apidn] = strtol(optarg,(char **)NULL, 0);
			rx.apidn++;
                        break;
                case 'c':
			if (rx.ac3n==N_AC3){
				fprintf(stderr,"Too many audio PIDs\n");
				exit(1);
			}
                        rx.ac3_id[rx.ac3n] = strtol(optarg,(char **)NULL, 0);
			rx.ac3n++;
                        break;
		case 'd':
			rx.video_delay = strtol(optarg,(char **)NULL, 0) 
				*CLOCK_MS;
			break;
		case 'e':
			rx.audio_delay = strtol(optarg,(char **)NULL, 0) 
				*CLOCK_MS;
			break;
		case 'f':
			rx.ignore_pts =1;
			break;
		case 'g':
			bufsize = strtol(optarg,(char **)NULL, 0) *1024*1024; 
			break;
                case 'i':
                        inpt = optarg;
                        break;
                case 'j':
                        rx.allow_jump = MIN_JUMP;
                        break;
		case 'k':
			rx.keep_pts =1;
			break;
		case 'l':
			min_jump = strtol(optarg,(char **)NULL, 0) *CLOCK_MS; 
			break;
                case 'o':
                        filename = optarg;
                        break;
		case 'p':
			fillzero = 1;
			break;
		case 'q':
			rx.max_overflows = strtol(optarg,(char **)NULL, 0); 
			break;
		case 's':
			scan = 1;
			break;
                case 't':
                        type = optarg;
                        break;
                case 'v':
                        rx.vpid = strtol(optarg,(char **)NULL, 0);
                        break;
		case 'x':
			rx.vdr=1;
			break;
		case 'y':
			analyze = strtol(optarg,(char **)NULL, 0);
			if (analyze>2) usage(argv[0]);
			analyze++;
			break;
		case 'z':
			rx.demux = 1;
			break;
                case 'h':
                case '?':
                default:
                        usage(argv[0]);
                }
        }

	if (rx.allow_jump && min_jump) rx.allow_jump = min_jump;

	if (fillzero) rx.fillzero = 1;
	rx.inputFiles = NULL;
        if (optind < argc){
		int i = 0;
		rx.inputFiles = calloc( sizeof(char * ), argc - optind + 1);
		while (optind < argc) {
			if ((rx.fd_in = open(argv[optind] ,O_RDONLY| O_LARGEFILE)) < 0){
				fprintf(stderr,"Error opening input file %s",argv[optind] );
				exit(1);
			}
			close(rx.fd_in);
			rx.inputFiles[i] = argv[optind];
			i++;
			optind++;
		}
		rx.inputFiles[i] = NULL;
		rx.inputIdx = 0;
		if ((rx.fd_in = open(rx.inputFiles[0] ,O_RDONLY| O_LARGEFILE)) < 0) {
			fprintf(stderr,"Error opening input file %s",argv[optind] );
			exit(1);
		}

                fprintf(stderr,"Reading from %s\n", argv[optind]);
		rx.inflength = lseek(rx.fd_in, 0, SEEK_END);
		fprintf(stderr,"Input file length: %.2f MB\n",rx.inflength/1024./1024.);
		lseek(rx.fd_in,0,SEEK_SET);
		rx.lastper = 0;
		rx.finread = 0;
        } else {
		fprintf(stderr,"using stdin as input\n");
		rx.fd_in = STDIN_FILENO;
		rx.inflength = 0;
        }

	if (!rx.demux){
		if (filename){
			if ((rx.fd_out = open(filename,O_WRONLY|O_CREAT
					      |O_TRUNC|O_LARGEFILE,
					      S_IRUSR|S_IWUSR|S_IRGRP|
					      S_IWGRP|
					      S_IROTH|S_IWOTH)) < 0){
				perror("Error opening output file");
				exit(1);
			}
			fprintf(stderr,"Output File is: %s\n", 
				filename);
		} else {
			rx.fd_out = STDOUT_FILENO;
			fprintf(stderr,"using stdout as output\n");
		}
	}
	if (scan){
		if (rx.fd_in == STDIN_FILENO){
			fprintf(stderr,"Can`t scan from pipe\n");
			exit(1);
		}
		do_scan(&rx);
		exit(0);
	}

        if (!strncmp(type,"MPEG2",6))
		rx.otype=REPLEX_MPEG2;
	else if (!strncmp(type,"DVD",4))
		rx.otype=REPLEX_DVD;
	else if (!strncmp(type,"HDTV",4))
		rx.otype=REPLEX_HDTV;
        else if (!rx.demux && !analyze)
                usage(argv[0]);
	
        if (!strncmp(inpt,"TS",3)){
		rx.itype=REPLEX_TS;
	} else if (!strncmp(inpt,"PS",3)){
		rx.itype=REPLEX_PS;
		if (!rx.vpid) rx.vpid = 0xE0;
		if (!(rx.apidn || rx.ac3n)){
			rx.apidn = 1;
			rx.apid[0] = 0xC0;
		}
	} else if (!strncmp(inpt,"AVI",4)){
		rx.itype=REPLEX_AVI;
		rx.vpid = 0xE0;
		rx.apidn = 1;
		rx.apid[0] = 0xC0;
		rx.ignore_pts =1;
	} else {
                usage(argv[0]);
	}

	init_replex(&rx, bufsize);
	rx.analyze= analyze;

	if (rx.demux){
		int i;
		char fname[256];
		if (!filename){
			filename = malloc(4);
			strcpy(filename,"out");
		}
		if (strlen(filename) > 250){
			fprintf(stderr,"Basename too long\n");
			exit(0);
		}
			
		snprintf(fname,256,"%s.mv2",filename);
		if ((rx.dmx_out[0] = open(fname,O_WRONLY|
					  O_CREAT|O_TRUNC|
					  O_LARGEFILE,
					  S_IRUSR|S_IWUSR|
					  S_IRGRP|S_IWGRP|
					  S_IROTH|S_IWOTH)) 
		    < 0){
			perror("Error opening output file");
			exit(1);
		}
		fprintf(stderr,"Video output File is: %s\n", 
			fname);
		
		for (i=0; i < rx.apidn; i++){
			snprintf(fname,256,"%s%d.mp2",filename
				 ,i);
			if ((rx.dmx_out[i+1] = 
			     open(fname,O_WRONLY|
				  O_CREAT|O_TRUNC|
				  O_LARGEFILE,
				  S_IRUSR|S_IWUSR|
				  S_IRGRP|S_IWGRP|
				  S_IROTH|S_IWOTH)) 
			    < 0){
				perror("Error opening output file");
				exit(1);
			}
			fprintf(stderr,"Audio%d output File is: %s\n",i,fname);
		}
		
		
		for (i=0; i < rx.ac3n; i++){
			snprintf(fname,256,"%s%d.ac3",filename
				 ,i);
			if ((rx.dmx_out[i+1+rx.apidn] = 
			     open(fname,O_WRONLY|
				  O_CREAT|O_TRUNC|
				  O_LARGEFILE,
				  S_IRUSR|S_IWUSR|
				  S_IRGRP|S_IWGRP|
				  S_IROTH|S_IWOTH)) 
			    < 0){
				perror("Error opening output file");
				exit(1);
			}
			fprintf(stderr,"AC3%d output File is: %s\n",i,fname);
		}
		do_demux(&rx);
	} else if (analyze){
		rx.demux=1;
		do_analyze(&rx);
	} else {
		do_replex(&rx);
	}
	
	return 0;
}
