The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * ts_parse.c
 *
 *  Created on: 28 Apr 2010
 *      Author: sdprice1
 */

// VERSION = 2.00

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>

#include "ts_parse.h"
#include "ts_bits.h"
#include "tables/parse_si.h"
#include "dvbsnoop/crc32.h"

/*=============================================================================================*/
// libmpeg2
void dump_state (FILE * f, mpeg2_state_t state, const mpeg2_info_t * info,
		 int offset, int verbose);

/*=============================================================================================*/

// Check for PTS/DTS wrap
#define MAX_TS_DIFF		(60 * TS_FREQ)
#define TS_WRAP			(1LL << 33)

// libmpeg2 states
static char *STATE_STRINGS[16] = {
			[0 ... 15]	= "UNKNOWN",
		    [STATE_BUFFER] = "STATE_BUFFER",
		    [STATE_SEQUENCE] = "STATE_SEQUENCE",
		    [STATE_SEQUENCE_REPEATED] = "STATE_SEQUENCE_REPEATED",
		    [STATE_GOP] = "STATE_GOP",
		    [STATE_PICTURE] = "STATE_PICTURE",
		    [STATE_SLICE_1ST] = "STATE_SLICE_1ST",
		    [STATE_PICTURE_2ND] = "STATE_PICTURE_2ND",
		    [STATE_SLICE] = "STATE_SLICE",
		    [STATE_END] = "STATE_END",
		    [STATE_INVALID] = "STATE_INVALID",
		    [STATE_INVALID_END] = "STATE_INVALID_END",
		    [STATE_SEQUENCE_MODIFIED] = "STATE_SEQUENCE_MODIFIED"
} ;



// mpeg2audio settings
#define AUDIOBUFFER		80000
#define STORAGE_SIZE 	100000
#define MIN_BUFFER 		4000


//define DEBUG_PTS

#define CHECK_MAGIC
#ifndef CHECK_MAGIC
#undef CHECK_TS_READER
#define CHECK_TS_READER(b)
#undef CHECK_TS_BUFF
#define CHECK_TS_BUFF(b)
#endif


/*=============================================================================================*/
#define FRAMEINFO_BLOCKSIZE		1024

//---------------------------------------------------------------------------------------------------------------------------
// Return the frame info array entry for this index. Allocates more memory as appropriate
struct TS_frame_info *frame_info_entry(struct TS_reader *tsreader, unsigned index)
{
	CHECK_TS_READER(tsreader) ;

	// Allocate if not already allocated
	if (!tsreader->mpeg2.frame_info_list)
	{
		tsreader->mpeg2.frame_info_list_size = FRAMEINFO_BLOCKSIZE ;
		tsreader->mpeg2.frame_info_list = (struct TS_frame_info *)malloc(tsreader->mpeg2.frame_info_list_size * sizeof(struct TS_frame_info)) ;
		memset(tsreader->mpeg2.frame_info_list, 0, tsreader->mpeg2.frame_info_list_size * sizeof(struct TS_frame_info)) ;
	}

	// If index > array size, expand
	if (index >= tsreader->mpeg2.frame_info_list_size)
	{
		tsreader->mpeg2.frame_info_list_size += FRAMEINFO_BLOCKSIZE ;
		tsreader->mpeg2.frame_info_list = (struct TS_frame_info *)realloc(tsreader->mpeg2.frame_info_list, tsreader->mpeg2.frame_info_list_size * sizeof(struct TS_frame_info)) ;
		memset(&tsreader->mpeg2.frame_info_list[tsreader->mpeg2.frame_info_list_size - FRAMEINFO_BLOCKSIZE], 0, FRAMEINFO_BLOCKSIZE * sizeof(struct TS_frame_info)) ;
	}

	return &tsreader->mpeg2.frame_info_list[index] ;
}

//---------------------------------------------------------------------------------------------------------------------------
// Free the frame info array
void free_frame_info_list(struct TS_reader *tsreader)
{
	CHECK_TS_READER(tsreader) ;
	if (tsreader->mpeg2.frame_info_list_size)
	{
		tsreader->mpeg2.frame_info_list_size = 0 ;
		free(tsreader->mpeg2.frame_info_list) ;
		tsreader->mpeg2.frame_info_list = NULL ;
	}
}


/*=============================================================================================*/

/* ----------------------------------------------------------------------- */
void dump_buff(const uint8_t *payload, unsigned payload_len, unsigned display_len)
{
unsigned byte ;

	if ((display_len == 0) || (display_len > payload_len))
		display_len = payload_len ;

	printf("---[ Len: %d  Displaying: %d ]------------------------------------------\n", payload_len, display_len) ;
	for (byte=0; byte < display_len; ++byte)
	{
		if (byte % 32 == 0)
		{
			printf("%04x: ", byte) ;
		}
		printf("%02x ", payload[byte]) ;
		if (byte % 8 == 7)
		{
			printf(" - ") ;
		}
		if (byte % 32 == 31)
		{
			printf("\n") ;
		}
	}
	if (display_len < payload_len)
	{
		printf("[") ;
		for (; (byte < display_len+3) && (byte < payload_len); ++byte)
		{
			printf("%02x ", payload[byte]) ;
			if (byte % 32 == 31)
			{
				printf("\n") ;
			}
		}
		printf("... ]") ;
	}
	printf("\n------------------------------------------------------------\n") ;
}


/*=============================================================================================*/
// TS_buff

/* ----------------------------------------------------------------------- */
void buffer_free(struct TS_buffer **buff)
{
struct TS_buffer *bp = *buff ;

//fprintf(stderr, "buffer_free(**TS_buffer=%p)\n", buff) ;

	if (bp)
	{
//		fprintf(stderr, " + **TS_buffer=%p [=> %p]\n", buff, *buff) ;
//		fprintf(stderr, " + buff_size=%d buff= %p\n", bp->buff_size, bp->buff) ;
		if (bp->buff_size)
		{
			free(bp->buff) ;
		}
		free(bp) ;
	}
	*buff = NULL ;
}

/* ----------------------------------------------------------------------- */
struct TS_buffer *buffer_new()
{
struct TS_buffer *bp ;

	// create struct
	bp = (struct TS_buffer *)malloc(sizeof(struct TS_buffer)) ;
	CLEAR_MEM(bp) ;

	bp->MAGIC = MAGIC_BUFF ;


	// create buffer
	bp->data_len = 0 ;
	bp->buff = (uint8_t *)malloc(TS_BUFFSIZE*sizeof(uint8_t)) ;
	bp->buff_size = TS_BUFFSIZE ;

//fprintf(stderr, "buffer_new() TS_buffer=%p [buff = %p])\n", bp, bp->buff) ;

	return bp ;
}

/* ----------------------------------------------------------------------- */
void buffer_clear(struct TS_buffer *bp)
{
	//CLEAR_MEM(bp)
	bp->data_len = 0 ;
}


/* ----------------------------------------------------------------------- */
uint8_t *buffer_data(struct TS_buffer **buff, const uint8_t *data, unsigned data_len)
{
struct TS_buffer *bp ;
unsigned new_len ;

//fprintf(stderr, "buffer_data(**TS_buffer=%p, data_len=%d)\n", buff, data_len) ;

	// check for first time
	if (!*buff)
	{
		// create struct
		*buff = buffer_new() ;
	}
	bp = *buff ;
	new_len = bp->data_len + data_len ;

//	fprintf(stderr, " + **TS_buffer=%p [=> %p] : buff=%p\n", buff, *buff, bp->buff) ;

	// able to add data?
	if (new_len >= bp->buff_size)
	{
//		fprintf(stderr, " + + realloc()\n") ;
		bp->buff_size += TS_BUFFSIZE ;
		bp->buff = realloc(bp->buff, bp->buff_size) ;
//		fprintf(stderr, " + + buff=%p\n", bp->buff) ;
	}
	else if (bp->buff_size - new_len > 2*TS_BUFFSIZE )
	{
//		fprintf(stderr, " + + downsize() data_len=%d, new_len=%d : curr size=%d\n", bp->data_len, new_len, bp->buff_size) ;
		// scale down if required
		bp->buff_size = ( ( (new_len + TS_BUFFSIZE-1) / TS_BUFFSIZE) + 1) * TS_BUFFSIZE ;
//		fprintf(stderr, " + + new size=%d\n", bp->buff_size) ;
		bp->buff = realloc(bp->buff, bp->buff_size) ;
//		fprintf(stderr, " + + buff=%p\n", bp->buff) ;
	}

//	fprintf(stderr, " + buff=%p .. %p : copying into %p .. %p\n", bp->buff, &bp->buff[bp->buff_size-1], &bp->buff[bp->data_len], &bp->buff[bp->data_len+data_len-1]) ;

	// copy data
	memcpy(&bp->buff[bp->data_len], data, data_len) ;
	bp->data_len += data_len ;

	return bp->buff ;
}


/*=============================================================================================*/
// TS_pid

/* ----------------------------------------------------------------------- */
// get existing or return created
static struct TS_pid* piditem_get(struct list_head *pid_list, struct TS_pidinfo *pidinfo)
{
struct TS_pid   *piditem;
struct list_head *item;

	list_for_each(item, pid_list)
	{
		piditem = list_entry(item, struct TS_pid, next);
		if (piditem->pidinfo.pid != pidinfo->pid)
			continue;

		return piditem;
    }

	piditem = malloc(sizeof(*piditem));
    CLEAR_MEM(piditem);
    piditem->MAGIC = MAGIC_PID ;

	//    struct TS_pid {
	//        struct list_head    next;
	//
	//        struct TS_pidinfo	pidinfo ;
	//        struct TS_buffer *	pes_buff ;
	//        enum TS_pesstate	pes_state ;
	//    };

    memcpy(&piditem->pidinfo, pidinfo, sizeof(*pidinfo)) ;
    piditem->pes_buff = buffer_new() ;
    piditem->pes_state = PES_SKIP ;
    list_add_tail(&piditem->next, pid_list);

    piditem->pesinfo.start_pts = UNSET_TS ;
    piditem->pesinfo.start_dts = UNSET_TS ;
    piditem->pesinfo.end_pts = UNSET_TS ;
    piditem->pesinfo.end_dts = UNSET_TS ;
    piditem->pesinfo.pts = UNSET_TS ;
    piditem->pesinfo.dts = UNSET_TS ;

    piditem->pesinfo.pes_psi = T_PES ;

    return piditem;
}

/* ----------------------------------------------------------------------- */
// get existing or return created
static void piditem_free(struct TS_pid * piditem)
{
	//    struct TS_pid {
	//        struct list_head    next;
	//
	//        struct TS_pidinfo	pidinfo ;
	//        struct TS_buffer *	pes_buff ;
	//        enum TS_pesstate	pes_state ;
	//    };

    buffer_free(&piditem->pes_buff) ;

    free(piditem);
}

/* ----------------------------------------------------------------------- */
// Start of new PES, reset flags/counters etc
static void pes_start(struct TS_pid * pid_item)
{
	pid_item->pes_buff->data_len = 0 ;
	pid_item->pes_state = PES_HEADER ;

	pid_item->pesinfo.psi_error = 0 ;
	pid_item->pesinfo.pes_error = 0 ;
	pid_item->pesinfo.ts_error = 0 ;
}


/*=============================================================================================*/
// TS_state

//struct TS_state {
//	unsigned MAGIC ;
//	struct TS_pidinfo 	pidinfo ;
//	struct TS_pid		*pid_item ;
//
//	// list of TS_pid
//    struct list_head    pid_list;
//
//    // Set to total number of packets
//    unsigned			total_pkts ;
//
//    // set to min/max pts/dts times
//	int64_t 			start_ts ;
//	int64_t 			end_ts ;
//
//	// Stop flag - set by callbacks to exit the loop
//	unsigned			stop_flag ;
//
////    // list of TS packets
////	unsigned			start_pktnum ;
////
////	// list of TS_pkt
////	struct list_head	pkt_list ;
//};

/* ----------------------------------------------------------------------- */
static void tsstate_free(struct TS_state *tsstate)
{
struct list_head  *item, *safe;
struct TS_pid    *piditem;
//struct TS_pkt    *pktitem;

	list_for_each_safe(item,safe,&tsstate->pid_list)
	{
		piditem = list_entry(item, struct TS_pid, next);
		list_del(&piditem->next);
		piditem_free(piditem);
	};
//	list_for_each_safe(item,safe,&tsstate->pkt_list)
//	{
//		pktitem = list_entry(item, struct TS_pkt, next);
//		list_del(&pktitem->next);
//		free(pktitem);
//	};

    free(tsstate) ;
}

/* ----------------------------------------------------------------------- */
static struct TS_state *tsstate_new()
{
struct TS_state *tsstate ;

	// create struct
	tsstate = (struct TS_state *)malloc(sizeof(struct TS_state)) ;
	CLEAR_MEM(tsstate) ;
	tsstate->MAGIC = MAGIC_STATE ;

	tsstate->pidinfo.pktnum = 0 ;
	tsstate->pidinfo.pid_error = 0 ;

	INIT_LIST_HEAD(&tsstate->pid_list);

	tsstate->start_ts = UNSET_TS ;
	tsstate->end_ts = UNSET_TS ;

	tsstate->pid_item = (struct TS_pid *)0 ;
	tsstate->total_pkts = 0 ;
	tsstate->stop_flag = 0 ;

//    // list of TS packets
//	INIT_LIST_HEAD(&tsstate->pkt_list);
//	tsstate->start_pktnum = 0LLU ;

	return tsstate ;
}


/*=============================================================================================*/


/* ----------------------------------------------------------------------- */
static int getbuff(int fh, uint8_t *buffer, int *count)
{
int rc ;
int status ;
int data_ready ;

	status = 0 ;

	rc = read(fh, buffer, *count);

	// return actual read amount
	*count = rc ;

	switch (rc) {
	case -1:
		RETURN_DVB_ERROR(ERR_READ) ;
	case 0:
		RETURN_DVB_ERROR(ERR_EOF) ;

	default:
		break;
	}
	return(status) ;
}


//---------------------------------------------------------------------------------------------------------------------------
unsigned mpeg2_frame_flags(struct TS_reader *tsreader, struct TS_state *tsstate, uint8_t *pesdata, unsigned pesdata_len)
{
uint8_t *p = pesdata ;
unsigned flags = 0 ;

	CHECK_TS_READER(tsreader) ;
	while (p && ((int)pesdata_len-(int)(p-pesdata) >= 4) && (p = memchr(p, 0, (int)pesdata_len-(int)(p-pesdata))) )
	{
		if ((int)pesdata_len-(int)(p-pesdata) >= 4)
		{
			if ( (p[0]==0) && (p[1]==0) && (p[2]==1) )
			{
				if ( p[3]==0 )
				{
					flags |= FRAME_FLAG_START ;

					tsparse_dbg_prt(200, (" @@ Video Start @@ pes start pkt %u : [at offset %d]\n",
							tsstate->pid_item->pesinfo.start_pkt, (int)(p-pesdata))) ;
				}
				else
				{
					char *codestr ;
					char tmp[256] ;

					int code = 0x100 + (int)p[3] ;
					switch (code)
					{
					case user_data_start_code:
						flags |= FRAME_FLAG_USER_DATA ;
						codestr = "USER DATA" ;
						break ;

					case sequence_header_code:
						flags |= FRAME_FLAG_SEQ_HEAD ;
						codestr = "SEQ HEAD" ;
						break ;

					case sequence_error_code:
						flags |= FRAME_FLAG_SEQ_ERROR ;
						codestr = "SEQ ERROR" ;
						break ;

					case extension_start_code:
						flags |= FRAME_FLAG_EXTENSION ;
						codestr = "EXTENSION" ;
						break ;

					case sequence_end_code:
						flags |= FRAME_FLAG_SEQ_END ;
						codestr = "SEQ END" ;
						break ;

					case group_start_code:
						flags |= FRAME_FLAG_GOP ;
						codestr = "GOP" ;
						break ;

					default:
						if ( (code >= slice_start_code_start) && (code <= slice_start_code_end) )
						{
							flags |= FRAME_FLAG_SLICE ;
							sprintf(tmp, "SLICE %d", code - slice_start_code_start + 1) ;
							codestr = tmp ;
						}
						else if ( (code >= system_start_code_start) && (code <= system_start_code_end) )
						{
							flags |= FRAME_FLAG_SYSTEM ;
							sprintf(tmp, "SYSTEM") ;
							codestr = tmp ;
						}
						else
						{
							flags |= FRAME_FLAG_RESERVED ;
							codestr = "" ;
						}
						break ;
					}

					tsparse_dbg_prt(200, ("    @#@ code 0x%02x %s @#@ pes start pkt %u : [at offset %d]\n",
							(int)p[3], codestr, tsstate->pid_item->pesinfo.start_pkt, (int)(p-pesdata))) ;
				}
				p+=3 ;
			}
		}

		if ((int)pesdata_len-(int)(p-pesdata) > 4)
		{
			++p ;
		}
		else
		{
			p = NULL ;
		}
	}

	return flags ;
}


//---------------------------------------------------------------------------------------------------------------------------
// NOTE: When skipping packets/frames, we need to have seen the previous GOP to get the frame for this GOP. Otherwise, we only
// get a frame out for the frame following the GOP
//
//               GOP                        GOP                        GOP
//   DATA:  ------|--------------------------|--------------------------|--------------------------|
//                 012345678901234567890123456012345678901234567890123456012345678901234567890123456
//
//   Skip=0 v
//           ------|--------------------------|--------------------------|--------------------------|
//                 |
//                 v
//                 First frame = Frame 0
//
//   Skip=2nd GOP
//                                           v
//          ------|--------------------------|--------------------------|--------------------------|
//                 012345678901234567890123456012345678901234567890123456012345678901234567890123456
//				                            |
//				                            v
//				                        First frame
//
//          i.e. first frame out = Frame 27 NOT Frame 26
//

#define MAX_STATE_CYCLE		100
static void process_mpeg2(struct TS_reader *tsreader, struct TS_state *tsstate, uint8_t *pesdata, unsigned pesdata_len)
{
mpeg2_state_t state;
unsigned state_cycle = 0 ;
unsigned frame_flags ;

// for debug
static int total_offset = 0;
const mpeg2_info_t * info;

	CHECK_TS_READER(tsreader) ;


	tsparse_dbg_prt(202, ("\n\n--[ video ]----------\n%d bytes\n", pesdata_len)) ;
	if (tsreader->debug >= 202)
		dump_buff(pesdata, pesdata_len, pesdata_len) ;

	// mainly looking for GOP start
	frame_flags = mpeg2_frame_flags(tsreader, tsstate, pesdata, pesdata_len) ;
	if (frame_flags & FRAME_FLAG_GOP)
	{
		tsreader->mpeg2.gop_pktnum = tsstate->pid_item->pesinfo.start_pkt ;
	}

	// check this is a video stream
	if ( (tsstate->pid_item->pesinfo.code & video_stream_mask) != video_stream)
	{
		return ;
	}

	// for debug
    info = mpeg2_info(tsreader->mpeg2.decoder);
    total_offset += pesdata_len ;

	do
	{
		state = mpeg2_parse (tsreader->mpeg2.decoder);


		/*
		 * With verbose=100, dump_state format:
		 *
		 *  %8x = file offset
		 *  %c%c = seq code, gop code S=seq, G=gop
		 *  %c%c%c = curr fbuff code, curr pic code, curr pic2 code (a-z or A-Z for new) 0..25
		 *  %c%c%c = disp fbuff code, disp pic code, disp pic2 code
		 *  %c = discard fbuf code
		 *  %s = State name
		 *
		 *
		 *  GOP = Group of 26 pictures
		 */
		tsparse_dbg_prt(200, ("---[ mpeg2 dump_state ]------------------------\nstate = %s [%d]\n", STATE_STRINGS[state], state));
		if (tsreader->debug >= 202)
			dump_state (stderr, state, info,
					total_offset - mpeg2_getpos(tsreader->mpeg2.decoder), 100 /* verbosity */);
		tsparse_dbg_prt(102, ("state = %s [%d]\n", STATE_STRINGS[state], state));

		switch (state) {
		case STATE_BUFFER:
			// HAVE TO COPY DATA TO NEW BUFFER SO THAT libmpeg2 HAS A COPY TO WORK ON IN THE MEANTIME!
			if (tsreader->mpeg2.video_buffer) free(tsreader->mpeg2.video_buffer) ;
			tsreader->mpeg2.video_buffer = (uint8_t *)malloc(pesdata_len * sizeof(uint8_t)) ;
			memcpy(tsreader->mpeg2.video_buffer, pesdata, pesdata_len) ;
			mpeg2_buffer (tsreader->mpeg2.decoder, tsreader->mpeg2.video_buffer, tsreader->mpeg2.video_buffer + pesdata_len);
			break;

		case STATE_SEQUENCE:
			if (tsreader->mpeg2.convert_rgb)
			{
				mpeg2_convert(tsreader->mpeg2.decoder, mpeg2convert_rgb24, NULL);
			}
			break;
		case STATE_SLICE:
		case STATE_END:
		case STATE_INVALID_END:
			if (tsreader->mpeg2.info->display_fbuf)
			{
				// call callback
//				if (tsreader->mpeg2_hook)
				{
					struct TS_frame_info *frame_info ;

					unsigned frame_info_index = (unsigned)tsreader->mpeg2.info->current_picture->tag ;
					frame_info = frame_info_entry(tsreader, frame_info_index) ;

					if (tsreader->mpeg2.convert_rgb)
					{
						tsreader->mpeg2_rgb_hook(&tsstate->pidinfo,
											 frame_info,
											 tsreader->mpeg2.info,
											 tsreader->user_data) ;
					}
					else
					{
						tsreader->mpeg2_hook(&tsstate->pidinfo,
											 frame_info,
											 tsreader->mpeg2.info,
											 tsreader->user_data) ;
					}
				}
				++tsreader->mpeg2.framenum ;
			}
			break;

		case STATE_INVALID:
			break ;
		case STATE_SEQUENCE_REPEATED:
			break ;
		case STATE_GOP:
			break ;
		case STATE_PICTURE:
			break ;
		case STATE_SLICE_1ST:
			break ;
		case STATE_PICTURE_2ND:
			break ;
		case STATE_SEQUENCE_MODIFIED:
			break ;

		default:
			break;
		}
	} while ( (state != STATE_BUFFER) && (state_cycle++ < MAX_STATE_CYCLE) ) ;

	// Use the tags to store:
	// tag = frame info index
	// tag2 = packet count of pes containing GOP (once every 26 frames)
	struct TS_frame_info *frame_info = frame_info_entry(tsreader, tsreader->mpeg2.frame_info_index) ;
	frame_info->framenum = tsreader->mpeg2.framenum ;
	frame_info->gop_pkt = tsreader->mpeg2.gop_pktnum ;
	memcpy(&frame_info->pesinfo, &tsstate->pid_item->pesinfo, sizeof(tsstate->pid_item->pesinfo)) ;
	memcpy(&frame_info->pidinfo, &tsstate->pid_item->pidinfo, sizeof(tsstate->pid_item->pidinfo)) ;

	mpeg2_tag_picture(tsreader->mpeg2.decoder, tsreader->mpeg2.frame_info_index, tsreader->mpeg2.gop_pktnum) ;
	tsreader->mpeg2.frame_info_index++ ;

	tsparse_dbg_prt(102, ("cycle=%d\n-------------------------------------\n\n", state_cycle));
}

//---------------------------------------------------------------------------------------------------------------------------
// mpeg2 decoder (again) but this time converts the image to RGB
//
static void process_mpeg2_rgb(struct TS_reader *tsreader, struct TS_state *tsstate, uint8_t *pesdata, unsigned pesdata_len)
{
mpeg2_state_t state;
unsigned state_cycle = 0 ;
unsigned frame_flags ;

// for debug
static int total_offset = 0;
const mpeg2_info_t * info;

	CHECK_TS_READER(tsreader) ;

	tsparse_dbg_prt(202, ("\n\n--[ rgb video ]----------\n%d bytes\n", pesdata_len)) ;
	if (tsreader->debug >= 202)
		dump_buff(pesdata, pesdata_len, pesdata_len) ;

	// mainly looking for GOP start
	frame_flags = mpeg2_frame_flags(tsreader, tsstate, pesdata, pesdata_len) ;
	if (frame_flags & FRAME_FLAG_GOP)
	{
		tsreader->mpeg2.gop_pktnum = tsstate->pid_item->pesinfo.start_pkt ;
	}

	// check this is a video stream
	if ( (tsstate->pid_item->pesinfo.code & video_stream_mask) != video_stream)
	{
		return ;
	}

	// for debug
    info = mpeg2_info(tsreader->mpeg2.decoder);
    total_offset += pesdata_len ;

	do
	{
		state = mpeg2_parse (tsreader->mpeg2.decoder);

		/*
		 * With verbose=100, dump_state format:
		 *
		 *  %8x = file offset
		 *  %c%c = seq code, gop code S=seq, G=gop
		 *  %c%c%c = curr fbuff code, curr pic code, curr pic2 code (a-z or A-Z for new) 0..25
		 *  %c%c%c = disp fbuff code, disp pic code, disp pic2 code
		 *  %c = discard fbuf code
		 *  %s = State name
		 *
		 *
		 *  GOP = Group of 26 pictures
		 */
		tsparse_dbg_prt(200, ("---[ rgb mpeg2 dump_state ]------------------------\nstate = %s [%d]\n", STATE_STRINGS[state], state));
		if (tsreader->debug >= 202)
			dump_state (stderr, state, info,
					total_offset - mpeg2_getpos(tsreader->mpeg2.decoder), 100 /* verbosity */);
		tsparse_dbg_prt(102, ("state = %s [%d]\n", STATE_STRINGS[state], state));

		switch (state) {
		case STATE_BUFFER:
			// HAVE TO COPY DATA TO NEW BUFFER SO THAT libmpeg2 HAS A COPY TO WORK ON IN THE MEANTIME!
			if (tsreader->mpeg2.video_buffer) free(tsreader->mpeg2.video_buffer) ;
			tsreader->mpeg2.video_buffer = (uint8_t *)malloc(pesdata_len * sizeof(uint8_t)) ;
			memcpy(tsreader->mpeg2.video_buffer, pesdata, pesdata_len) ;
			mpeg2_buffer (tsreader->mpeg2.decoder, tsreader->mpeg2.video_buffer, tsreader->mpeg2.video_buffer + pesdata_len);
			break;

		case STATE_SEQUENCE:
		    mpeg2_convert (tsreader->mpeg2.decoder, mpeg2convert_rgb24, NULL);
			break;

		case STATE_SLICE:
		case STATE_END:
		case STATE_INVALID_END:
			if (tsreader->mpeg2.info->display_fbuf)
			{
				// call callback
				if (tsreader->mpeg2_rgb_hook)
				{
					struct TS_frame_info *frame_info ;

					unsigned frame_info_index = (unsigned)tsreader->mpeg2.info->current_picture->tag ;
					frame_info = frame_info_entry(tsreader, frame_info_index) ;

					tsreader->mpeg2_rgb_hook(&tsstate->pidinfo,
										 frame_info,
										 tsreader->mpeg2.info,
										 tsreader->user_data) ;
				}
				++tsreader->mpeg2.framenum ;
			}
			break;

		case STATE_INVALID:
			break ;
		case STATE_SEQUENCE_REPEATED:
			break ;
		case STATE_GOP:
			break ;
		case STATE_PICTURE:
			break ;
		case STATE_SLICE_1ST:
			break ;
		case STATE_PICTURE_2ND:
			break ;
		case STATE_SEQUENCE_MODIFIED:
			break ;

		default:
			break;
		}
	} while ( (state != STATE_BUFFER) && (state_cycle++ < MAX_STATE_CYCLE) ) ;

	// Use the tags to store:
	// tag = frame info index
	// tag2 = packet count of pes containing GOP (once every 26 frames)
	struct TS_frame_info *frame_info = frame_info_entry(tsreader, tsreader->mpeg2.frame_info_index) ;
	frame_info->framenum = tsreader->mpeg2.framenum ;
	frame_info->gop_pkt = tsreader->mpeg2.gop_pktnum ;
	memcpy(&frame_info->pesinfo, &tsstate->pid_item->pesinfo, sizeof(tsstate->pid_item->pesinfo)) ;
	memcpy(&frame_info->pidinfo, &tsstate->pid_item->pidinfo, sizeof(tsstate->pid_item->pidinfo)) ;

	mpeg2_tag_picture(tsreader->mpeg2.decoder, tsreader->mpeg2.frame_info_index, tsreader->mpeg2.gop_pktnum) ;
	tsreader->mpeg2.frame_info_index++ ;

	tsparse_dbg_prt(102, ("cycle=%d\n-------------------------------------\n\n", state_cycle));
}



//---------------------------------------------------------------------------------------------------------------------------
static void process_audio(struct TS_reader *tsreader, struct TS_state *tsstate, uint8_t *pesdata, unsigned pesdata_len)
{
int done_bytes, avail_bytes, bytes;
int samples, audio_bytes;
mpeg2_audio_t audio_info ;

	CHECK_TS_READER(tsreader) ;

	// check this is an audio stream
	if ( (tsstate->pid_item->pesinfo.code & audio_stream_mask) != audio_stream)
	{
		return ;
	}

	if (&tsreader->mpeg2audio.write_ptr[pesdata_len] - tsreader->mpeg2audio.storage_buf > STORAGE_SIZE)
	{
		printf("ERROR: Audio buffer full\n");
		abort() ;
	}
	memcpy(tsreader->mpeg2audio.write_ptr, pesdata, pesdata_len);
	tsreader->mpeg2audio.write_ptr = &tsreader->mpeg2audio.write_ptr[pesdata_len];
	avail_bytes = tsreader->mpeg2audio.write_ptr - tsreader->mpeg2audio.read_ptr;

	// start with this PTS
	audio_info.pts = tsstate->pid_item->pesinfo.dts ;

	done_bytes = 1;
	while ( (avail_bytes > MIN_BUFFER) && (done_bytes > 0) )
	{
		audio_bytes = 0;
		tsparse_dbg_prt(102, ("avail_bytes=%d, done=%d\n", avail_bytes, done_bytes)) ;

		// Decode the frame in buffer
		done_bytes = decode_frame(tsreader->mpeg2audio.audio_buf, &audio_bytes, tsreader->mpeg2audio.read_ptr, (int)(tsreader->mpeg2audio.write_ptr - tsreader->mpeg2audio.read_ptr));

		// convert the audio buffer size in bytes, into the total number of samples (shorts). Samples per chan = samples / num_chans
		samples = audio_bytes / sizeof(short) ;

		tsreader->mpeg2audio.read_ptr = &tsreader->mpeg2audio.read_ptr[done_bytes];
		avail_bytes -= done_bytes;

		// Pass the samples to the user
		audio_info.sample_rate = get_samplerate() ;
		audio_info.channels = get_channels() ;
		audio_info.framesize = get_framesize() ;
		audio_info.audio = tsreader->mpeg2audio.audio_buf ;
		audio_info.samples = samples ;
		audio_info.audio_framenum = tsreader->mpeg2audio.framenum ;

		audio_info.samples_per_frame = audio_info.framesize * audio_info.channels ;

		// Calculate the PTS delta between audio frames
		audio_info.pts_delta = 90000 * audio_info.framesize / audio_info.sample_rate ;

		// call callback
		if ( (audio_info.samples_per_frame > 0) && tsreader->audio_hook )
		{
			tsreader->mpeg2audio.audio_samples += samples;
			while (tsreader->mpeg2audio.audio_samples >= audio_info.samples_per_frame)
			{
				// callback a frame at a time
				audio_info.samples = audio_info.samples_per_frame ;
				tsreader->audio_hook(&tsstate->pidinfo, &tsstate->pid_item->pesinfo,
						&audio_info, tsreader->user_data) ;

				tsreader->mpeg2audio.audio_samples -= audio_info.samples_per_frame;
				audio_info.audio -= audio_info.samples_per_frame ;

				// This is the audio frame count
				audio_info.audio_framenum++ ;

				// step to expected next DTS
				audio_info.pts += (int64_t)audio_info.pts_delta ;
			}
			tsreader->mpeg2audio.framenum = audio_info.audio_framenum ;

		}
	}

	// Shuffle any remaining data down to the bottom of the buffer
	bytes = 0 ;
	while (tsreader->mpeg2audio.read_ptr < tsreader->mpeg2audio.write_ptr) {
		tsreader->mpeg2audio.storage_buf[bytes++] = *tsreader->mpeg2audio.read_ptr++;
	}
	tsreader->mpeg2audio.write_ptr = &tsreader->mpeg2audio.storage_buf[bytes];
	tsreader->mpeg2audio.read_ptr = tsreader->mpeg2audio.storage_buf;

}


///* ----------------------------------------------------------------------- */
//static int process_psi(struct TS_reader *tsreader, struct TS_state *tsstate,
//			uint8_t *payload, unsigned payload_len)
//{
//	tsparse_dbg_prt(102, ("process_psi(pid %d) payload len %d\n",
//			tsstate->pidinfo.pid, payload_len)) ;
//
//	// Call table processor
//
//	return 0 ;
//}

/* ----------------------------------------------------------------------- */
static int process_pes(struct TS_reader *tsreader, struct TS_state *tsstate,
			uint8_t *payload, unsigned payload_len)
{
unsigned code ;
unsigned packet_length ;
unsigned byte ;
int64_t pts, dts ;
int i ;
unsigned set_ts = 0 ;
unsigned pts_dts ;

#ifdef DEBUG_PTS
unsigned dbg_bytes[32] ;
unsigned dbg_num ;
#endif

	CHECK_TS_READER(tsreader) ;

	tsparse_dbg_prt(102, ("process_pes(pid %d) payload len %d\n",
		tsstate->pidinfo.pid, payload_len)) ;

	if (payload_len < 7)
		return 0 ;

	//	packet_start_code_prefix				24 bslbf
	//	stream_id								8 uimsbf
	//	packet_length							16 uimsbf
	//
	code = 0x100 | payload[3] ;
	packet_length = ((payload[4])<<8) | payload[5] ;

	tsstate->pid_item->pesinfo.code = code ;

	tsparse_dbg_prt(102, ("PES code 0x%03x PES Len %d Data:\n", code, packet_length)) ;
	if (tsreader->debug >= 103)
		dump_buff(payload, payload_len, 32) ;

	byte = 6 ;

	//	ISO13818-1:
	//
	//	if (stream_id != program_stream_map
	//	&& stream_id != padding_stream
	//	&& stream_id != private_stream_2
	//	&& stream_id != ECM
	//	&& stream_id != EMM
	//	&& stream_id != program_stream_directory
	//	&& stream_id != DSMCC_stream
	//	&& stream_id != ITU-T Rec. H.222.1 type E stream) {
	if (
			code != program_stream_map
		&&	code != padding_stream
		&&	code != private_stream_2
		&&	code != ECM_stream
		&&	code != EMM_stream
		&&	code != program_stream_directory
		&&	code != DSMCC_stream
		&&	code != H2221_E_stream
	)
	{
	unsigned pes_header_len ;

		//		'10' 2 bslbf
		//		PES_scrambling_control 2 bslbf
		//		PES_priority 1 bslbf
		//		data_alignment_indicator 1 bslbf
		//		copyright 1 bslbf
		//		original_or_copy 1 bslbf
		++byte ;

		//		PTS_DTS_flags 2 bslbf
		//		ESCR_flag 1 bslbf
		//		ES_rate_flag 1 bslbf
		//		DSM_trick_mode_flag 1 bslbf
		//		additional_copy_info_flag 1 bslbf
		//		PES_CRC_flag 1 bslbf
		//		PES_extension_flag 1 bslbf
		pts_dts = (payload[byte] >> 6) & 0x3 ;
		++byte ;

		tsparse_dbg_prt(102, (" + decoding pes data (pts_dts=%d [0x%02x])...\n", pts_dts, payload[byte-1])) ;

		//		PES_header_data_length 8 uimsbf
		pes_header_len = payload[byte] ;
		++byte ;

		// point at just the PES data part
		tsstate->pid_item->pesinfo.pesdata_p = &payload[byte+pes_header_len] ;
		tsstate->pid_item->pesinfo.pesdata_len = payload_len - (byte+pes_header_len) ;

		tsparse_dbg_prt(102, (" + pes header len=%d...\n", pes_header_len)) ;

//		if (PTS_DTS_flags = = '10') {
//			'0010' 4 bslbf
//			PTS [32..30] 3 bslbf
//			marker_bit 1 bslbf
//			PTS [29..15] 15 bslbf
//			marker_bit 1 bslbf
//			PTS [14..0] 15 bslbf
//			marker_bit 1 bslbf
//		}
		if (pts_dts == 2)
		{
#ifdef DEBUG_PTS
dbg_bytes[0] = payload[byte] ;
dbg_bytes[1] = payload[byte+1] ;
dbg_bytes[2] = payload[byte+2] ;
dbg_bytes[3] = payload[byte+3] ;
dbg_bytes[4] = payload[byte+4] ;
dbg_num=5 ;
#endif

			if ( (payload[byte] & 0xf0) != 0x20)
			{
				// handle error
				tsstate->pid_item->pesinfo.pes_error++ ;
				tsstate->pidinfo.pid_error++ ;
				if (tsreader->error_hook)
				{
					SET_DVB_ERROR(ERR_PESHEAD) ;
					tsreader->error_hook(dvb_error_code, &tsstate->pidinfo, tsreader->user_data) ;
				}
			}
			pts = (int64_t)((payload[byte] >> 1) & 0x07) << 30;
			pts |= (((payload[byte+1]<<8) | payload[byte+2]) >> 1) << 15;
			pts |=  (((payload[byte+3]<<8) | payload[byte+4]) >> 1);

			tsparse_dbg_prt(100, ("PTS definition:\n")) ;
			if (tsreader->debug >= 100)
				dump_buff(&payload[byte], payload_len, 5) ;

			byte += 5 ;

			dts = pts ;
			++set_ts ;
		}

//		if (PTS_DTS_flags = = '11') {
//			'0011' 4 bslbf
//			PTS [32..30] 3 bslbf
//			marker_bit 1 bslbf

//			PTS [29..15] 15 bslbf
//			marker_bit 1 bslbf

//			PTS [14..0] 15 bslbf
//			marker_bit 1 bslbf

//			'0001' 4 bslbf
//			DTS [32..30] 3 bslbf
//			marker_bit 1 bslbf

//			DTS [29..15] 15 bslbf
//			marker_bit 1 bslbf

//			DTS [14..0] 15 bslbf
//			marker_bit 1 bslbf
//		}
		if (pts_dts == 3)
		{
#ifdef DEBUG_PTS
dbg_bytes[0] = payload[byte] ;
dbg_bytes[1] = payload[byte+1] ;
dbg_bytes[2] = payload[byte+2] ;
dbg_bytes[3] = payload[byte+3] ;
dbg_bytes[4] = payload[byte+4] ;
dbg_bytes[5] = payload[byte+5] ;
dbg_bytes[6] = payload[byte+6] ;
dbg_bytes[7] = payload[byte+7] ;
dbg_bytes[8] = payload[byte+8] ;
dbg_bytes[9] = payload[byte+9] ;
dbg_num=10 ;
#endif
			if ( (payload[byte] & 0xf0) != 0x30)
			{
				// handle error
				tsstate->pid_item->pesinfo.pes_error++ ;
				tsstate->pidinfo.pid_error++ ;
				if (tsreader->error_hook)
				{
					SET_DVB_ERROR(ERR_PESHEAD) ;
					tsreader->error_hook(dvb_error_code, &tsstate->pidinfo, tsreader->user_data) ;
				}
			}
		    pts = (int64_t)((payload[byte] >> 1) & 0x07) << 30;
		    pts |= (((payload[byte+1]<<8) | payload[byte+2]) >> 1) << 15;
		    pts |=  (((payload[byte+3]<<8) | payload[byte+4]) >> 1);

			tsparse_dbg_prt(100, ("PTS definition:\n")) ;
		    if (tsreader->debug >= 100)
				dump_buff(&payload[byte], payload_len, 5) ;

		    byte += 5 ;

		    dts = (int64_t)((payload[byte] >> 1) & 0x07) << 30;
		    dts |= (((payload[byte+1]<<8) | payload[byte+2]) >> 1) << 15;
		    dts |=  (((payload[byte+3]<<8) | payload[byte+4]) >> 1);

			tsparse_dbg_prt(100, ("DTS definition:\n")) ;
		    if (tsreader->debug >= 100)
				dump_buff(&payload[byte], payload_len, 5) ;

			byte += 5 ;

			++set_ts ;
		}


//		if (ESCR_flag = = '1') {
//			reserved 2 bslbf
//			ESCR_base[32..30] 3 bslbf
//			marker_bit 1 bslbf
//			ESCR_base[29..15] 15 bslbf
//			marker_bit 1 bslbf
//			ESCR_base[14..0] 15 bslbf
//			marker_bit 1 bslbf
//			ESCR_extension 9 uimsbf
//			marker_bit 1 bslbf
//		}
//		if (ES_rate_flag = = '1') {
//			marker_bit 1 bslbf
//			ES_rate 22 uimsbf
//			marker_bit 1 bslbf
//		}
//		if (DSM_trick_mode_flag = = '1') {
//			trick_mode_control 3 uimsbf
//			if ( trick_mode_control = = fast_forward ) {
//				field_id 2 bslbf
//				intra_slice_refresh 1 bslbf
//				frequency_truncation 2 bslbf
//			}
//			else if ( trick_mode_control = = slow_motion ) {
//				rep_cntrl 5 uimsbf
//			}
//			else if ( trick_mode_control = = freeze_frame ) {
//				field_id 2 uimsbf
//				reserved 3 bslbf
//			}
//			else if ( trick_mode_control = = fast_reverse ) {
//				field_id 2 bslbf
//				intra_slice_refresh 1 bslbf
//				frequency_truncation 2 bslbf
//			else if ( trick_mode_control = = slow_reverse ) {
//				rep_cntrl 5 uimsbf
//			}
//			else
//				reserved 5 bslbf
//		}
//		if ( additional_copy_info_flag = = '1') {
//			marker_bit 1 bslbf
//			additional_copy_info 7 bslbf
//		}
//		if ( PES_CRC_flag = = '1') {
//			previous_PES_packet_CRC 16 bslbf
//		}
//		if ( PES_extension_flag = = '1') {
//			PES_private_data_flag 1 bslbf
//			pack_header_field_flag 1 bslbf
//			program_packet_sequence_counter_flag 1 bslbf
//			P-STD_buffer_flag 1 bslbf
//			reserved 3 bslbf
//			PES_extension_flag_2 1 bslbf
//			if ( PES_private_data_flag = = '1') {
//				PES_private_data 128 bslbf
//			}
//			if (pack_header_field_flag = = '1') {
//				pack_field_length 8 uimsbf
//				pack_header()
//			}
//			if (program_packet_sequence_counter_flag = = '1') {
//				marker_bit 1 bslbf
//				program_packet_sequence_counter 7 uimsbf
//				marker_bit 1 bslbf
//				MPEG1_MPEG2_identifier 1 bslbf
//				original_stuff_length 6 uimsbf
//			}
//			if ( P-STD_buffer_flag = = '1') {
//				'01' 2 bslbf
//				P-STD_buffer_scale 1 bslbf
//				P-STD_buffer_size 13 uimsbf
//			}
//			if ( PES_extension_flag_2 = = '1') {
//				marker_bit 1 bslbf
//				PES_extension_field_length 7 uimsbf
//				for (i = 0; i < PES_extension_field_length; i++) {
//					reserved 8 bslbf
//				}
//			}
//		}
//		for (i = 0; i < N1; i++) {
//			stuffing_byte 8 bslbf
//		}
//		for (i = 0; i < N2; i++) {
//			PES_packet_data_byte 8 bslbf
//		}
//	}
	}


	// setting the pts/dts?
	if (set_ts)
	{
		// PTS
		if (tsstate->pid_item->pesinfo.start_pts == UNSET_TS)
		{
			tsstate->pid_item->pesinfo.start_pts = pts ;
		}
		else
		{
			// check for wrap
			if (pts+MAX_TS_DIFF < tsstate->pid_item->pesinfo.start_pts)
			{
				// wrapped, so adjust
				pts += TS_WRAP ;
			}
		}

		if (tsstate->pid_item->pesinfo.end_pts == UNSET_TS)
		{
			tsstate->pid_item->pesinfo.end_pts = pts ;
		}
		else
		{
			if (tsstate->pid_item->pesinfo.end_pts < pts)
				tsstate->pid_item->pesinfo.end_pts = pts ;
		}
		tsstate->pid_item->pesinfo.pts = pts ;

		// DTS
		if (tsstate->pid_item->pesinfo.start_dts == UNSET_TS)
		{
			tsstate->pid_item->pesinfo.start_dts = pts ;
		}
		else
		{
			// check for wrap
			if (dts+MAX_TS_DIFF < tsstate->pid_item->pesinfo.start_dts)
			{
				// wrapped, so adjust
				dts += TS_WRAP ;
			}
		}

		if (tsstate->pid_item->pesinfo.end_dts == UNSET_TS)
		{
			tsstate->pid_item->pesinfo.end_dts = pts ;
		}
		else
		{
			if (tsstate->pid_item->pesinfo.end_dts < dts)
				tsstate->pid_item->pesinfo.end_dts = dts ;
		}
		tsstate->pid_item->pesinfo.dts = dts ;

		tsparse_dbg_prt(102, ("TIMESTAMP: pid %d [0x%03x] rel ts=%d\n",
			tsstate->pidinfo.pid, code,
			(int)(dts - tsstate->pid_item->pesinfo.start_dts)
			));

#ifdef DEBUG_PTS
printf("DTS: pid %d [0x%03x] pts=%"PRId64" 0x%0"PRIx64"  (dts=%"PRId64") : pts_dts=%d [",
		tsstate->pidinfo.pid, code, pts, pts, dts, pts_dts);
for (i=0; i<dbg_num; ++i)
{
	printf("0x%02x ", dbg_bytes[i]) ;
}
printf("]\n") ;
#endif
	}

	return 0 ;
}




/* ----------------------------------------------------------------------- */
static int handle_payload(struct TS_reader *tsreader, struct TS_state *tsstate,
			uint8_t *payload, unsigned payload_len)
{
	CHECK_TS_READER(tsreader) ;
	if (tsstate->pidinfo.pid == NULL_PID)
		return 0 ;

	tsparse_dbg_prt(102, ("handle_payload(pid %d) : pkt %u : payload len %d  pes_start=%d\n",
		tsstate->pidinfo.pid, tsstate->pidinfo.pktnum,
		payload_len, tsstate->pidinfo.pes_start?1:0)) ;

    // process buffered data
	if (tsstate->pidinfo.pes_start)
	{
		if (tsstate->pid_item->pes_buff->data_len)
		{
		uint8_t *buff = tsstate->pid_item->pes_buff->buff ;
		unsigned buff_len = tsstate->pid_item->pes_buff->data_len ;

			tsparse_dbg_prt(102, ("handle_payload(pid %d) : call process_*() with existing buffer : buffered data len %d:\n",
					tsstate->pidinfo.pid, tsstate->pid_item->pes_buff->data_len)) ;

			if ((buff[0]==0) && (buff[1]==0) && (buff[2]==1))
			{
				// do something with the PES data
				tsstate->pid_item->pesinfo.pes_psi = T_PES ;
				process_pes(tsreader, tsstate, buff, buff_len) ;
			}

			//# PSI
			else
			{
				if (tsreader->debug >= 103)
					dump_buff(buff, buff_len, (tsreader->debug >= 104 ? buff_len : 31) ) ;

				// do something with the PSI data
				tsstate->pid_item->pesinfo.pes_psi = T_PSI ;
				parse_si(tsreader, tsstate, buff, buff_len) ;
			}

			// send to hook
			if (tsreader->pes_hook)
			{
				// Complete PES packet - Header + Data
				tsreader->pes_hook(&tsstate->pidinfo, &tsstate->pid_item->pesinfo,
						buff, buff_len,
						tsreader->user_data) ;
			}

			// send to hook
			if (tsreader->pes_data_hook)
			{
				// Just the PES data
				tsreader->pes_data_hook(&tsstate->pidinfo, &tsstate->pid_item->pesinfo,
						tsstate->pid_item->pesinfo.pesdata_p, tsstate->pid_item->pesinfo.pesdata_len,
						tsreader->user_data) ;
			}

			// send to hook
			if (tsreader->mpeg2.decoder)
			{
				// Just the PES data
				process_mpeg2(tsreader, tsstate, tsstate->pid_item->pesinfo.pesdata_p, tsstate->pid_item->pesinfo.pesdata_len) ;
			}

			// send to hook
			if (tsreader->audio_hook)
			{
				// Just the PES data
				process_audio(tsreader, tsstate, tsstate->pid_item->pesinfo.pesdata_p, tsstate->pid_item->pesinfo.pesdata_len) ;
			}
		}

		// reset state
		pes_start(tsstate->pid_item) ;
	}

    // process current data
	if (tsstate->pidinfo.pes_start)
	{
		tsstate->pid_item->pesinfo.start_pkt = tsreader->tsstate->pidinfo.pktnum ;
		tsstate->pid_item->pesinfo.end_pkt = tsreader->tsstate->pidinfo.pktnum ;

		tsparse_dbg_prt(102, ("handle_payload(pid %d) : calling process_* with new data : payload len = %d\n",
				tsstate->pidinfo.pid, payload_len)) ;

		//	#	Name 						Size 		Description
		//	#
		//	#	Packet start code prefix 	3 bytes 	0x000001
		//	#	Stream id 					1 byte 		Examples: Audio streams (0xC0-0xDF), Video streams (0xE0-0xEF) [2] [3] [4] [5]
		//	#											Note: The above 4 bytes is called the 32 bit start code.
		//	#	PES Packet length 			2 bytes 	Can be zero.If the PES packet length is set to zero, the PES packet can be of any length. A value of zero for the PES packet length can be used only when the PES packet payload is a video elementary stream.[6]
		//	#	Optional PES header 		variable length
		//	#	Stuffing bytes 				variable length
		//	#	Data 									See elementary stream. In the case of private streams the first byte of the payload is the sub-stream number.
		//	#
		if ((payload[0]==0) && (payload[1]==0) && (payload[2]==1))
		{
			// do something with the PES data
			process_pes(tsreader, tsstate, payload, payload_len) ;
		}

		//# PSI
		else
		{
			// do something with the PSI data
//SDP-just buffer until next start?//			process_psi(tsreader, tsstate, payload, payload_len) ;
		}
	}

	// buffer if required
	if (tsstate->pid_item->pes_state != PES_SKIP)
	{
		tsstate->pid_item->pesinfo.end_pkt = tsreader->tsstate->pidinfo.pktnum ;

		buffer_data(&tsstate->pid_item->pes_buff, payload, payload_len) ;

		tsparse_dbg_prt(102, ("<buffered> handle_payload(pid %d) : buffered - length now = %d\n",
				tsstate->pidinfo.pid, tsstate->pid_item->pes_buff->data_len)) ;
//		if (tsreader->debug >= 103)
//			dump_buff(tsstate->pid_item->pes_buff->buff, tsstate->pid_item->pes_buff->data_len, (tsreader->debug >= 104 ? tsstate->pid_item->pes_buff->data_len : 31) ) ;
	}

	return 0 ;
}

/* ----------------------------------------------------------------------- */
static int parse_ts_packet(struct TS_reader *tsreader, struct TS_state *tsstate,
		uint8_t *packet, unsigned packet_len)
{
unsigned pid_ok, pes_complete ;

uint8_t *payload ;
unsigned start ;
int payload_len ;

	CHECK_TS_READER(tsreader) ;
	tsstate->pid_item = NULL ;

	/*
		# ISO 13818-1
		#
		#	sync_byte 8 bslbf
		#
		#	transport_error_indicator 1 bslbf
		#	payload_unit_start_indicator 1 bslbf
		#	transport_priority 1 bslbf
		#	PID 13 uimsbf
		#
		#	transport_scrambling_control 2 bslbf
		#	adaptation_field_control 2 bslbf
		#	continuity_counter 4 uimsbf
		#
		#	if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){
		#		adaptation_field()
		#	}
		#	if(adaptation_field_control = = '01' || adaptation_field_control = = '11') {
		#		for (i = 0; i < N; i++){
		#			data_byte 8 bslbf
		#		}
		#	}
	*/
	tsstate->pidinfo.pid = (((packet[1] & 0x1f) << 8) | (packet[2] & 0xff)) & MAX_PID ;
	tsstate->pidinfo.err_flag = packet[1] & 0x80;
	tsstate->pidinfo.pes_start = packet[1] & 0x40;

    /* skip adaptation field */
	tsstate->pidinfo.afc = (packet[3] >> 4) & 3;
	start=4 ;
	payload = &packet[start] ;

	if (tsstate->pidinfo.afc == 0) /* reserved value */
        return 0;
    if (tsstate->pidinfo.afc == 2) /* adaptation field only */
        return 0;
    if (tsstate->pidinfo.afc == 3)
    {
        /* skip adaptation_field */
    	start += payload[0] + 1 ;
    }
    /* if past the end of packet, ignore */
    if (start >= packet_len)
        return 0;

	payload = &packet[start] ;
	payload_len = packet_len - start ;

if (tsreader->debug >= 100)
{
	printf ("PID %d (0x%03x) - start=%d, payload len=%d\n", tsstate->pidinfo.pid, tsstate->pidinfo.pid, start, payload_len) ;
	dump_buff(packet, packet_len, 31) ;
}


    // cumulative error
    tsstate->pidinfo.pid_error = 0 ;

	// look at pid?
	pid_ok=1;
	if (tsreader->pid_hook)
	{
		//# see if we want to process this pid
		pid_ok = tsreader->pid_hook(tsstate->pidinfo.pid, tsreader->user_data) ;
	}


	if (pid_ok)
	{
		// Update pid
		tsstate->pid_item = piditem_get(&tsstate->pid_list, &tsstate->pidinfo) ;

		//## Do checks for this packet

		//# sync check
		if (packet[0] != SYNC_BYTE)
		{
			++tsstate->pidinfo.pid_error ;
			if (tsreader->error_hook)
			{
				SET_DVB_ERROR(ERR_BADSYNC) ;
				tsreader->error_hook(dvb_error_code, &tsstate->pidinfo, tsreader->user_data) ;
			}
		}

		// # error check
		if (tsstate->pidinfo.err_flag)
		{
			++tsstate->pidinfo.pid_error ;
			if (tsreader->error_hook)
			{
				SET_DVB_ERROR(ERR_TSERR) ;
				tsreader->error_hook(dvb_error_code, &tsstate->pidinfo, tsreader->user_data) ;
			}
		}

		//    /* continuity check (currently not used) */
		//    cc = (packet[3] & 0xf);
		//    cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc));
		//    tss->last_cc = cc;

		tsstate->pid_item->pidinfo.pid_error = tsstate->pidinfo.pid_error ;
		tsstate->pid_item->pesinfo.ts_error += tsstate->pidinfo.pid_error ;
		if (tsstate->pidinfo.err_flag) ++tsstate->pid_item->pesinfo.ts_error ;

		if (tsreader->payload_hook)
		{
			tsreader->payload_hook(&tsstate->pidinfo, payload, payload_len, tsreader->user_data) ;
		}

		//# data may be a complete PES/PSI
		handle_payload(tsreader, tsstate, payload, payload_len) ;

		//## do something with raw packet
		if (tsreader->ts_hook)
		{
			tsreader->ts_hook(&tsstate->pidinfo, packet, packet_len, tsreader->user_data) ;
		}
	}

	return 0 ;
}

/*=============================================================================================*/
// PUBLIC
/*=============================================================================================*/

/* ----------------------------------------------------------------------- */
// Convert a TS packet into a NULL packet - overwrites the data
void ts_null_packet(uint8_t *packet, unsigned packet_len)
{

	/*
	 * For null packets the payload_unit_start_indicator shall be set to '0'.
	 * PID value 0x1FFF is reserved for null packets
	 * In the case of a null packet the value of the adaptation_field_control shall be set to '01'.
	 * In the case of a null packet the value of the continuity_counter is undefined.
	 * In the case of null packets with PID value 0x1FFF, data_bytes may be assigned any value.
	 *
	 *
		# ISO 13818-1
		#
		#	sync_byte 8 bslbf
		#
		#	transport_error_indicator 1 bslbf
		#	payload_unit_start_indicator 1 bslbf
		#	transport_priority 1 bslbf
		#	PID 13 uimsbf
		#
		#	transport_scrambling_control 2 bslbf
		#	adaptation_field_control 2 bslbf
		#	continuity_counter 4 uimsbf
		#
		#	if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){
		#		adaptation_field()
		#	}
		#	if(adaptation_field_control = = '01' || adaptation_field_control = = '11') {
		#		for (i = 0; i < N; i++){
		#			data_byte 8 bslbf
		#		}
		#	}
	*/
	// sync
	packet[0] = SYNC_BYTE ;

	// pid = 0x1fff
	// transport_error_indicator = 0
	// payload_unit_start_indicator = 0
	// transport_priority = 0
	packet[1] = (NULL_PID>>8) & 0x1f ;
	packet[2] = NULL_PID & 0xff ;

    /* adaptation field = 01 */
	packet[3] = (packet[3] & 0xcf) | 0x10 ;

}



/*=============================================================================================*/
// SI table decoding

/* ----------------------------------------------------------------------- */
int tsreader_register_section(struct TS_reader *tsreader,
		unsigned table_id, unsigned mask,
		Section_handler	handler, struct Section_decode_flags flags)
{
int updated = 0 ;
enum TS_section_ids masked_val ;
enum TS_section_ids id ;

	CHECK_TS_READER(tsreader) ;

	// add/remove handler entries from the table
	id = table_id & SECTION_MAX ;

	id &= mask ;
	masked_val = id ;
	for ( ; (id <= SECTION_MAX) && ( (id & mask) == masked_val); ++id )
	{
		tsreader->section_decode_table[id].flags = flags ;
		tsreader->section_decode_table[id].handler = handler ;
		++updated ;
	}
	return (updated) ;
}


/*=============================================================================================*/
// TS_reader

/* ----------------------------------------------------------------------- */
// Set the start & end points for whole video based on min/max times of the streams
void tsreader_set_timing(struct TS_reader *tsreader)
{
struct list_head  *item;
struct TS_pid    *piditem;

	CHECK_TS_READER(tsreader) ;
	tsparse_dbg_prt(102, ("tsreader_set_timing()\n")) ;

	list_for_each(item,&tsreader->tsstate->pid_list)
	{
		piditem = list_entry(item, struct TS_pid, next);

		tsparse_dbg_prt(102, ("Start=%"PRId64"  End=%"PRId64"\n", tsreader->tsstate->start_ts, tsreader->tsstate->end_ts)) ;
		tsparse_dbg_prt(102, ("PID %d\n", piditem->pidinfo.pid)) ;
		tsparse_dbg_prt(102, (" + PTS Start=%"PRId64"  End=%"PRId64" : DTS Start=%"PRId64"  End=%"PRId64"\n",
										piditem->pesinfo.start_pts, piditem->pesinfo.end_pts,
										piditem->pesinfo.start_dts, piditem->pesinfo.end_dts
										)) ;

		// start
		if (piditem->pesinfo.start_pts != UNSET_TS)
		{
			if  ((tsreader->tsstate->start_ts == UNSET_TS) || (piditem->pesinfo.start_pts < tsreader->tsstate->start_ts))
			{
				tsreader->tsstate->start_ts = piditem->pesinfo.start_pts ;
				tsparse_dbg_prt(102, (" + + Set start = pts\n")) ;
			}
		}
		if (piditem->pesinfo.start_dts != UNSET_TS)
		{
			if  ((tsreader->tsstate->start_ts == UNSET_TS) || (piditem->pesinfo.start_dts < tsreader->tsstate->start_ts))
			{
				tsreader->tsstate->start_ts = piditem->pesinfo.start_dts ;
				tsparse_dbg_prt(102, (" + + Set start = dts\n")) ;
			}
		}

		// end
		if (piditem->pesinfo.end_pts != UNSET_TS)
		{
			if  ((tsreader->tsstate->end_ts == UNSET_TS) || (piditem->pesinfo.end_pts > tsreader->tsstate->end_ts))
			{
				tsreader->tsstate->end_ts = piditem->pesinfo.end_pts ;
				tsparse_dbg_prt(102, (" + + Set end = pts\n")) ;
			}
		}
		if (piditem->pesinfo.end_dts != UNSET_TS)
		{
			if  ((tsreader->tsstate->end_ts == UNSET_TS) || (piditem->pesinfo.end_dts > tsreader->tsstate->end_ts))
			{
				tsreader->tsstate->end_ts = piditem->pesinfo.end_dts ;
				tsparse_dbg_prt(102, (" + + Set end = dts\n")) ;
			}
		}
	};

	tsparse_dbg_prt(102, ("Start=%"PRId64"  End=%"PRId64"\n", tsreader->tsstate->start_ts, tsreader->tsstate->end_ts)) ;
	tsparse_dbg_prt(102, ("tsreader_set_timing() - DONE\n")) ;
}


/* ----------------------------------------------------------------------- */
// Origin is as lseek ; skip_pkts can be -ve if origin is SEEK_END
int tsreader_setpos(struct TS_reader *tsreader, int skip_pkts, int origin, unsigned num_pkts)
{
off64_t rc = 0 ;
off64_t pos ;
int abs_skip ;
int skip_sign ;

	CHECK_TS_READER(tsreader) ;

	abs_skip = skip_pkts ;
	skip_sign = 1 ;
	if (skip_pkts < 0)
	{
		abs_skip = -skip_pkts ;
		skip_sign = -1 ;
	}

	// check for over-run
	if (abs_skip > tsreader->tsstate->total_pkts)
	{
		abs_skip = tsreader->tsstate->total_pkts ;
	}
	skip_pkts = skip_sign * abs_skip ;

	// set position
	tsreader->num_pkts = num_pkts ;
	tsreader->skip = abs_skip ;
	tsreader->origin = origin ;
	tsreader->tsstate->pidinfo.pktnum = 0 ;

	// skip if no file open
	if (!tsreader->file)
		return 0 ;

//TODO: check skip_pkts < num_pkts
//TODO: check against total pkts (tsreader->tsstate->total_pkts)


	// calc pos
	pos = (off64_t)(skip_pkts) * (off64_t)(TS_PACKET_LEN) ;

	tsparse_dbg_prt(100, ("tsreader_setpos(skip=%d, origin=%d) pos=%"PRId64"\n", skip_pkts, origin, (long long int)pos)) ;

	rc = lseek64(tsreader->file, pos, origin) ;

	tsparse_dbg_prt(100, ("lseek pos now = %"PRId64"\n", (long long int)rc)) ;

	if (rc == (off64_t)-1)
	{
		SET_DVB_ERROR(ERR_FILE_SEEK) ;
		if (tsreader->debug >= 100)
			perror("File seek error: ") ;

		// On error - reset to start of file
		lseek64(tsreader->file, (off64_t)0, SEEK_SET) ;

//TODO: on error seek to 0?
	}
	else
	{
		// set packet number
		tsreader->tsstate->pidinfo.pktnum = (unsigned)(rc / TS_PACKET_LEN) ;
	}

	return(dvb_error_code) ;
}

/* ----------------------------------------------------------------------- */
void tsreader_start_framenum(struct TS_reader *tsreader, unsigned framenum)
{
	CHECK_TS_READER(tsreader) ;
	tsreader->mpeg2.start_framenum = framenum ;
	tsreader->mpeg2.framenum = framenum ;
}


/* ----------------------------------------------------------------------- */
struct TS_reader *tsreader_new(char *filename)
{
int file = 0 ;
struct TS_reader *tsreader = NULL ;
off64_t size ;

	// open file iff specified
	if (filename)
	{
		file = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0666);
		if (-1 == file)
		{
			SET_DVB_ERROR(ERR_FILE) ;
			return(NULL);
		}
	}

	// create struct
	tsreader = (struct TS_reader *)malloc(sizeof(struct TS_reader)) ;
	CLEAR_MEM(tsreader) ;
	tsreader->MAGIC = MAGIC_READER ;

	tsreader->file = file ;
	tsreader->tsstate = tsstate_new() ;

	// work out total number of packets
	if (tsreader->file)
	{
		size = lseek64(tsreader->file, -1, SEEK_END) ;

		// size = -1 :
		// errorno=EINVAL for zero length file
		if (size < 0)
		{
			size = 0 ;
			SET_DVB_ERROR(ERR_FILE) ;
			if (errno == EINVAL)
			{
				SET_DVB_ERROR(ERR_FILE_ZERO) ;
			}
			tsreader_free(tsreader) ;
			return(NULL) ;
		}
		tsreader->tsstate->total_pkts = (unsigned)(size / (off64_t)TS_PACKET_LEN) ;
	}

	// set position
	tsreader_setpos(tsreader, 0, SEEK_SET, 0) ;

	return tsreader ;
}


/* ----------------------------------------------------------------------- */
struct TS_reader *tsreader_new_nofile()
{
	struct TS_reader *tsreader = tsreader_new( (char *)NULL ) ;

	return tsreader ;
}

/* ----------------------------------------------------------------------- */
// abort loop
void tsreader_stop(struct TS_reader *tsreader)
{
	CHECK_TS_READER(tsreader) ;
	tsreader->tsstate->stop_flag = 1 ;
}

/* ----------------------------------------------------------------------- */
void tsreader_free(struct TS_reader *tsreader)
{
	if (tsreader)
	{
		CHECK_TS_READER(tsreader) ;
		if (tsreader->file)
			close(tsreader->file) ;

		tsstate_free(tsreader->tsstate) ;

		// optionally clear out libmpeg2 settings
		if (tsreader->mpeg2.decoder)
			mpeg2_close (tsreader->mpeg2.decoder);
	    if (tsreader->mpeg2.video_buffer)
	    	free(tsreader->mpeg2.video_buffer) ;
	    free_frame_info_list(tsreader) ;

	    // optionally clear mpeg2audio settings
		if (tsreader->mpeg2audio.audio_buf)
			free(tsreader->mpeg2audio.audio_buf) ;

		if (tsreader->mpeg2audio.storage_buf)
			free(tsreader->mpeg2audio.storage_buf) ;

		free(tsreader) ;
	}
}

/* ----------------------------------------------------------------------- */
static void tsreader_set_mpeg2(struct TS_reader *tsreader)
{
	CHECK_TS_READER(tsreader) ;

	// optionally set up libmpeg2 settings
	if (tsreader->mpeg2_hook || tsreader->mpeg2_rgb_hook)
	{
		if (!tsreader->mpeg2.decoder)
		{
			tsreader->mpeg2.decoder = mpeg2_init ();
		    if (tsreader->mpeg2.decoder == NULL) {
				fprintf (stderr, "Could not allocate a decoder object.\n");
				exit (1);
		    }
		    tsreader->mpeg2.info = mpeg2_info (tsreader->mpeg2.decoder);
		    tsreader->mpeg2.framenum = 0;
		    tsreader->mpeg2.start_framenum = 0;
		    tsreader->mpeg2.gop_pktnum = 0;

		    tsreader->mpeg2.convert_rgb = 0;
		    if (tsreader->mpeg2_rgb_hook)
		    {
		    	// colour conversion
		    	tsreader->mpeg2.convert_rgb = 1;
		    }

		    tsreader->mpeg2.frame_info_list_size = 0 ;
			tsreader->mpeg2.frame_info_list = NULL ;
			tsreader->mpeg2.frame_info_index = 0 ;
		}
	}
}


/* ----------------------------------------------------------------------- */
static void tsreader_set_audio(struct TS_reader *tsreader)
{
	CHECK_TS_READER(tsreader) ;

	// optionally set up mpeg2audio settings
	if (tsreader->audio_hook)
	{
		if (!tsreader->mpeg2audio.audio_init)
		{
			decode_init();
		    tsreader->mpeg2audio.audio_init = 1 ;
		    tsreader->mpeg2audio.framenum = 0;
		    tsreader->mpeg2audio.audio_samples = 0;

		    tsreader->mpeg2audio.audio_buf = (short *)malloc(sizeof(short) * AUDIOBUFFER) ;
			CLEAR_MEM(tsreader->mpeg2audio.audio_buf) ;

		    tsreader->mpeg2audio.storage_buf = (uint8_t *)malloc(sizeof(uint8_t) * STORAGE_SIZE) ;
			CLEAR_MEM(tsreader->mpeg2audio.storage_buf) ;
			tsreader->mpeg2audio.write_ptr = tsreader->mpeg2audio.storage_buf ;
			tsreader->mpeg2audio.read_ptr = tsreader->mpeg2audio.storage_buf ;
		}
	}
}


/* ----------------------------------------------------------------------- */
// Only called when a file is specified
int ts_parse(struct TS_reader *tsreader)
{
uint8_t buffer[TS_BUFFSIZE];
int bytes_read ;
int status;

	CHECK_TS_READER(tsreader) ;

	status = 0 ;

	// check file open
	if (!tsreader->file)
	{
		RETURN_DVB_ERROR(ERR_FILE) ;
	}

	// initialise
	status = tsreader_data_start(tsreader) ;
	if (status) return (status) ;

    // main loop
    while (tsreader->buff_state.running > 0)
    {
		bytes_read = TS_BUFFSIZE_READ ;
    	status = getbuff(tsreader->file, buffer, &bytes_read) ;

    	// add packet and process
    	status = tsreader_data_add(tsreader, buffer, bytes_read) ;
    	if (status) return (status) ;

    } // while running

    // finish off
	status = tsreader_data_end(tsreader) ;

    return status;
}


/* ----------------------------------------------------------------------- */
int tsreader_data_start(struct TS_reader *tsreader)
{
	CHECK_TS_READER(tsreader) ;
	tsparse_dbg_prt(10, ("TS: tsreader_data_start()\n")) ;
	tsparse_dbg_prt(100, ("TS: # Total packets = %d\n", tsreader->tsstate->total_pkts)) ;

	tsreader->buff_state.bptr = tsreader->buff_state.buffer ;
	tsreader->buff_state.buffer_len = 0 ;
	tsreader->buff_state.running = 1 ;
	tsreader->buff_state.get_sync = 1 ;
	tsreader->buff_state.pktnum = 0 ;
	tsreader->buff_state.status = 0 ;


	// Ensure libmpeg2 info is correct
	tsreader_set_mpeg2(tsreader) ;

	// Ensure audio info is set up
	tsreader_set_audio(tsreader) ;

	// Progress required?
	if (tsreader->progress_hook)
	{
		tsreader->progress_info.scale = 1 ;
		if (tsreader->tsstate->total_pkts*100 > 0xffffffff)
		{
			tsreader->progress_info.scale = ( (tsreader->tsstate->total_pkts*100) / 0xffffffff) + 1 ;
		}
		tsreader->progress_info.step = tsreader->tsstate->total_pkts / (tsreader->progress_info.scale * 100) ;
		tsreader->progress_info.total = tsreader->tsstate->total_pkts / tsreader->progress_info.scale ;
		tsreader->progress_info.next_progress = tsreader->progress_info.step ;

		tsreader->progress_hook(PROGRESS_START, 0, tsreader->progress_info.total, tsreader->user_data) ;
	}

	return 0 ;
}



/* ----------------------------------------------------------------------- */
int tsreader_data_add(struct TS_reader *tsreader, uint8_t *data, unsigned data_len)
{
unsigned byte_num ;

tsparse_dbg_prt(10, ("TS: tsreader_data_add() running=%d data_len=%d : Current size = %d\n", tsreader->buff_state.running, data_len, tsreader->buff_state.buffer_len)) ;

	CHECK_TS_READER(tsreader) ;

	// skip if stopped
	if (!tsreader->buff_state.running)
		return 0 ;

	// skip if no data
	if (data_len == 0)
		return 0 ;

	// add new data
	if (tsreader->buff_state.buffer_len < TS_PACKET_LEN)
	{
    	tsreader->buff_state.bptr = tsreader->buff_state.buffer ;
    	tsreader->buff_state.buffer_len = data_len ;
    	memcpy(tsreader->buff_state.buffer, data, data_len*sizeof(uint8_t)) ;

    	tsreader->buff_state.get_sync = 1 ;

//fprintf(stderr, "bptr[0]=0x%02x\n", tsreader->buff_state.bptr[0]) ;
//fprintf(stderr, "bptr=%p\n", tsreader->buff_state.bptr) ;
//fprintf(stderr, "buffer_len=%d\n", tsreader->buff_state.buffer_len) ;

    	tsparse_dbg_prt(10, ("TS: Reload buffer : 0x%02x (bptr @ %p) %d bytes left\n",
    			tsreader->buff_state.bptr[0], tsreader->buff_state.bptr, tsreader->buff_state.buffer_len)) ;
	}
	// check overflow (should never happen!)
	else if (tsreader->buff_state.buffer_len + data_len >= TS_BUFFSIZE)
	{
		// restart
		data_len %= TS_BUFFSIZE ;
    	tsreader->buff_state.bptr = tsreader->buff_state.buffer ;
    	tsreader->buff_state.buffer_len = data_len ;
    	memcpy(tsreader->buff_state.buffer, data, data_len*sizeof(uint8_t)) ;

    	tsreader->buff_state.get_sync = 1 ;

fprintf(stderr, "\n**OVERFLOW**\n") ;

    	tsparse_dbg_prt(10, ("TS: *OVERFLOW* Reload buffer : 0x%02x (bptr @ %p) %d bytes left\n",
    			tsreader->buff_state.bptr[0], tsreader->buff_state.bptr, tsreader->buff_state.buffer_len)) ;
	}
	// append data
	else
	{
//		unsigned data_used = (tsreader->buff_state.bptr - tsreader->buff_state.buffer) ;
//		if (data_len + tsreader->buff_state.buffer_len + data_used >= TS_BUFFSIZE )
//		{
//			data_len = TS_BUFFSIZE - 1 - (tsreader->buff_state.buffer_len + data_used) ;
//		}

		// append data
    	memcpy(&tsreader->buff_state.bptr[tsreader->buff_state.buffer_len], data, data_len*sizeof(uint8_t)) ;
    	tsreader->buff_state.buffer_len += data_len ;

    	tsparse_dbg_prt(10, ("TS: Append buffer : 0x%02x (bptr @ %p) %d bytes left\n",
    			tsreader->buff_state.bptr[0], tsreader->buff_state.bptr, tsreader->buff_state.buffer_len)) ;
	}

	while (tsreader->buff_state.running && (tsreader->buff_state.buffer_len > 0) )
	{
		tsparse_dbg_prt(100, ("TS: Loop start...(len=%d) running=%d, get sync=%d\n",
				tsreader->buff_state.buffer_len,
				tsreader->buff_state.running,
				tsreader->buff_state.get_sync)) ;

		// check for sync if required
		if (tsreader->buff_state.get_sync)
		{
			tsparse_dbg_prt(10, ("TS: waiting for sync...\n")) ;

			// wait for sync byte, but abort if we've waited for at least 4 packets and not found it
			byte_num=0;
			while ( (tsreader->buff_state.buffer_len > 0) && (tsreader->buff_state.bptr[0] != SYNC_BYTE) && (byte_num < (4*TS_PACKET_LEN)) )
			{
				tsreader->buff_state.buffer_len-- ;
				tsreader->buff_state.bptr++ ;
				byte_num++ ;

				tsparse_dbg_prt(10, ("TS:  + byte[0]=0x%02x num=%d\n", tsreader->buff_state.bptr[0], byte_num)) ;
			}
			tsreader->buff_state.get_sync = 0 ;

			// did we find it?
			if  ((tsreader->buff_state.buffer_len == 0) || (tsreader->buff_state.bptr[0] != SYNC_BYTE))
			{
				// clear buffer
				tsreader->buff_state.get_sync = 1 ;
				tsreader->buff_state.buffer_len = 0 ;

				// return error
				RETURN_DVB_ERROR(ERR_NOSYNC) ;
			}
		}
		tsparse_dbg_prt(10, ("TS: handling TS packets...(buffer @ %p => 0x%02x)\n", tsreader->buff_state.buffer, tsreader->buff_state.bptr[0])) ;

		// handle rest of TS packet(s)
		while ( tsreader->buff_state.running && !tsreader->buff_state.get_sync && (tsreader->buff_state.buffer_len >= TS_PACKET_LEN) )
		{
			tsparse_dbg_prt(10, ("TS: Start data of loop : 0x%02x (bptr @ %p) %d bytes left : local pkt count = %u\n",
					tsreader->buff_state.bptr[0], tsreader->buff_state.bptr, tsreader->buff_state.buffer_len, tsreader->buff_state.pktnum)) ;
			tsparse_dbg_prt(10, ("TS: # pkt count = %u\n", tsreader->buff_state.pktnum)) ;

			// check sync byte
			if (tsreader->buff_state.bptr[0] != SYNC_BYTE)
			{
				// re-sync
				++tsreader->buff_state.get_sync ;

				tsparse_dbg_prt(10, ("TS: ! Resync required : 0x%02x (bptr @ %p)\n", tsreader->buff_state.bptr[0], tsreader->buff_state.bptr)) ;
			}
			else
			{
				// Do something with the packet
				parse_ts_packet(tsreader, tsreader->tsstate, tsreader->buff_state.bptr, TS_PACKET_LEN) ;

				// Progress required?
				if (tsreader->progress_hook)
				{
					if (tsreader->buff_state.pktnum == tsreader->progress_info.next_progress)
					{
						tsreader->progress_hook(PROGRESS_RUNNING,
								(unsigned)(tsreader->buff_state.pktnum / tsreader->progress_info.scale),
								tsreader->progress_info.total,
								tsreader->user_data) ;

						tsreader->progress_info.next_progress += tsreader->progress_info.step ;
					}
				}

				//== check for end ==
				++tsreader->buff_state.pktnum ;
				++tsreader->tsstate->pidinfo.pktnum ;

				// only compare with total packets if it's been set!
				if (tsreader->tsstate->total_pkts && (tsreader->buff_state.pktnum >= tsreader->tsstate->total_pkts) )
				{
					// reached end of file
					tsreader->buff_state.running = 0 ;
					tsparse_dbg_prt(100, ("TS: stop running total pkts (len=%d)\n", tsreader->buff_state.buffer_len)) ;
				}

				// check if max packets has been set
				if (tsreader->num_pkts && (tsreader->buff_state.pktnum >= tsreader->num_pkts) )
				{
					// reached requested number of packets
					tsreader->buff_state.running = 0 ;
					tsparse_dbg_prt(100, ("TS: stop running num pkts> (len=%d)\n", tsreader->buff_state.buffer_len)) ;
				}

				// check special STOP flag
				if (tsreader->tsstate->stop_flag)
				{
					// stop request
					tsreader->buff_state.running = 0 ;
					tsparse_dbg_prt(100, ("TS: stop running flag (len=%d)\n", tsreader->buff_state.buffer_len)) ;
				}

				// update buffer
				tsreader->buff_state.buffer_len -= TS_PACKET_LEN ;
				tsreader->buff_state.bptr += TS_PACKET_LEN ;

			} // if get_sync

			tsparse_dbg_prt(10, ("TS: End of data loop : 0x%02x (bptr @ %p) %d bytes left\n",
					tsreader->buff_state.bptr[0], tsreader->buff_state.bptr, tsreader->buff_state.buffer_len)) ;

			tsparse_dbg_prt(10, ("TS: running=%d, get sync=%d, buff len=%d\n",
					tsreader->buff_state.running, tsreader->buff_state.get_sync, tsreader->buff_state.buffer_len)) ;

		} // while in sync


		// process other bytes if still got data
		if ((tsreader->buff_state.buffer_len > 0) && (tsreader->buff_state.bptr[0] != SYNC_BYTE))
		{
			tsreader->buff_state.get_sync = 1 ;
		}

		tsparse_dbg_prt(100, ("TS: Loop end...(len=%d)\n", tsreader->buff_state.buffer_len)) ;

	} // while got data

	return 0 ;
}


/* ----------------------------------------------------------------------- */
int tsreader_data_end(struct TS_reader *tsreader)
{
	CHECK_TS_READER(tsreader) ;

	// Progress required?
	if (tsreader->progress_hook)
	{
		unsigned scaled_pktnum = (unsigned)(tsreader->buff_state.pktnum / tsreader->progress_info.scale) ;
		if (scaled_pktnum > tsreader->progress_info.total) scaled_pktnum = tsreader->progress_info.total ;

		if (tsreader->tsstate->stop_flag)
		{
			// premature stop
			tsreader->progress_hook(PROGRESS_STOPPED,
					scaled_pktnum,
					tsreader->progress_info.total,
					tsreader->user_data) ;
		}
		else
		{
			// run to end
			tsreader->progress_hook(PROGRESS_END,
					tsreader->progress_info.total,
					tsreader->progress_info.total,
					tsreader->user_data) ;
		}
	}

	tsparse_dbg_prt(10, ("TS: tsreader_data_end() - END\n")) ;

    return 0;
}