The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * Frame statistics (blank, scene change) etc.
 *
 */


#include "ad_frame.h"
#include "ts_advert.h"


//===========================================================================================================================
// CONSTANTS
//===========================================================================================================================

// Audio default Perl settings
#define FRAME_max_advert			(3*60*FPS)
#define FRAME_min_advert			(3*60*FPS)
#define FRAME_min_program			(5*60*FPS)
#define FRAME_start_pad				(2*60*FPS)
#define FRAME_end_pad				(2*60*FPS)
#define FRAME_min_frames 	 		2
#define FRAME_frame_window 	 		(4*60*FPS)
#define FRAME_max_gap 		 		(10*FPS)
#define FRAME_reduce_end			0
#define FRAME_reduce_min_gap	 	0

//#define DEBUG_STATE

//===========================================================================================================================
// MACROS
//===========================================================================================================================


// print debug if debug setting is high enough
#define frame_dbg_prt(LVL, ARGS)	\
		if (frame_settings->debug >= LVL)	printf ARGS


//===========================================================================================================================
// FUNCTIONS
//===========================================================================================================================



//---------------------------------------------------------------------------------------------------------------------------
#ifdef DEBUG_STATE
static void dump_histo(char *msg, unsigned *histo)
{
unsigned i;

	fprintf(stderr, "%s\n", msg) ;
	for (i=0; i < HISTOGRAM_BINS; )
	{
	unsigned j ;

		fprintf(stderr, "%4d: ", i) ;
		for (j=0; j<32; j++, i++)
		{
			fprintf(stderr, "%04x ", histo[i]) ;
		}
		fprintf(stderr, "\n") ;
	}
	fprintf(stderr, "\n") ;
}
#endif

//===========================================================================================================================
// Stats utils
//


//---------------------------------------------------------------------------------------------------------------------------
// Blank out buffer area
static void _remove_logo(int width, int height,
		int chroma_width, int chroma_height,
		uint8_t ** buf,
		struct Ad_frame_settings *frame_settings)
{
uint8_t *frame = buf[0] ;
uint8_t *Cr = buf[1] ;
uint8_t *Cb = buf[2] ;
uint8_t *pp_Cr, *pp_Cb, *pp ;
unsigned x, y ;

unsigned chroma_x1 = frame_settings->logo_x1 / 2 ;
unsigned chroma_y1 = frame_settings->logo_y1 / 2 ;
unsigned chroma_x2 = frame_settings->logo_x2 / 2 ;
unsigned chroma_y2 = frame_settings->logo_y2 / 2 ;


	// Chroma
	for(y = chroma_y1; (y <= chroma_y2); y++)
	{
	unsigned offset = (y * chroma_width) + chroma_x1 ;
	uint8_t blank_Cr=0 ;
	uint8_t blank_Cb=0 ;

		if (chroma_x1 > 0)
		{
			blank_Cr = Cr[offset-1];
			blank_Cb = Cb[offset-1];
		}

		pp_Cr = &Cr[offset] ;
		pp_Cb = &Cb[offset] ;

		for (x=chroma_x1; (x <= chroma_x2); x++)
		{
			*pp_Cr++ = blank_Cr ;
			*pp_Cb++ = blank_Cb ;
		}
	}

	// Frame
	for(y = frame_settings->logo_y1; (y <= frame_settings->logo_y2); y++)
	{
	unsigned offset = (y * width) + frame_settings->logo_x1 ;
	uint8_t blank = 0 ;

		if (frame_settings->logo_x1 > 0)
		{
			blank = frame[offset-1];
		}

		pp = &frame[offset] ;

		for (x=frame_settings->logo_x1; (x <= frame_settings->logo_x2); x++)
		{
			*pp++ = blank ;
		}
	}

}

//---------------------------------------------------------------------------------------------------------------------------
static unsigned _chroma_diff(
		int chroma_width, int chroma_height,
		uint8_t * const * buf, int framenum,
		struct Ad_frame_settings *frame_settings,
		struct Ad_frame_state *frame_state
		)
{
uint8_t *Cr = buf[1] ;
uint8_t *Cb = buf[2] ;
uint8_t *pp_Cr, *pp_Cb ;
unsigned x, y ;
unsigned max_diff = 0 ;

unsigned start_row ;
unsigned sample_height ;
unsigned start_col ;
unsigned sample_width ;
unsigned num_pixels ;
unsigned step ;


	// Chroma
	start_row = chroma_height * (100 - frame_settings->window_percent) / 200 ;
	sample_height = chroma_height * frame_settings->window_percent / 100 ;
	start_col = chroma_width * (100 - frame_settings->window_percent) / 200 ;
	sample_width = chroma_width * frame_settings->window_percent / 100 ;
	num_pixels = sample_height * sample_width ;
	step = 2;

	if (chroma_width > 800) step = 4;
	if (chroma_width < 400) step = 1;

	for(y = 0; (y < sample_height); y+=step)
	{
	unsigned offset = (start_row+y) * chroma_width + start_col ;

		pp_Cr = &Cr[offset] ;
		pp_Cb = &Cb[offset] ;

		for (x=0; (x < sample_width); x+=step)
		{
		unsigned diff = abs(*pp_Cr - *pp_Cb) ;

			if (diff > max_diff)
			{
				max_diff = diff;
			}
			pp_Cr += step;
			pp_Cb += step ;
		}
	}

	return max_diff ;
}

//---------------------------------------------------------------------------------------------------------------------------
static void _frame_state(
		int width, int height,
		uint8_t * const * buf, int framenum,
		struct Ad_frame_settings *frame_settings,
		struct Ad_frame_state *frame_state
		)
{
uint8_t *frame = buf[0] ;
unsigned x, y ;


unsigned	start_row = height * (100 - frame_settings->window_percent) / 200 ;
unsigned	sample_height = height * frame_settings->window_percent / 100 ;
unsigned	start_col = width * (100 - frame_settings->window_percent) / 200 ;
unsigned	sample_width = width * frame_settings->window_percent / 100 ;
unsigned	num_pixels = height * sample_width ;

int i ;
int uniform ;
int pixels = 0 ;
long similar = 0;
int		hasBright = 0;
int		dimCount = 0;
int		sceneChangePercent;

unsigned 	delta, hereBright ;
unsigned	brightCountminX = 0;
unsigned	brightCountminY = 0;
unsigned	brightCountmaxX = 0;
unsigned	brightCountmaxY = 0;

unsigned	minY = start_row;
unsigned	maxY = height - start_row;
unsigned	minX = start_col;
unsigned	maxX = width - start_col;
unsigned	brightCount = 0;
unsigned	step = 4;


frame_dbg_prt(4, ("_frame_state(w %d x h %d) %d%%\n", width, height, frame_settings->window_percent)) ;

	frame_state->max_bright = 0 ;

	if (width > 800) step = 8;
	if (width < 400) step = 2;

	memcpy(frame_state->lastHistogram, frame_state->histogram, sizeof(frame_state->histogram));

	// compare current frame with last frame here
	memset(frame_state->histogram, 0, sizeof(frame_state->histogram));

	delta = 0;

	//			minX						maxX
	//
	//	minY	+---------------------------+
	//			|							|
	//			|	+-------------------+	|
	//			|	|					|	|
	//			|	+-------------------+	|
	//			|							|
	//	maxY	+---------------------------+
	//

	brightCountminX = 0;
	brightCountminY = 0;
	brightCountmaxX = 0;
	brightCountmaxY = 0;
	while (1)
	{
		y = start_row + delta;
//fprintf(stderr, "1: Y=%d\n  X: ", y) ;
		for (x = start_col + delta; x <= width - start_col - delta; x += step) {
//fprintf(stderr, "%d,", x) ;
//			if (haslogo[y * width + x])
//				continue;
			hereBright = frame[y * width + x];
			frame_state->histogram[hereBright]++;

			if (hereBright > frame_state->max_bright) frame_state->max_bright = hereBright ;

			// update brightness count for minY
			if (hereBright > frame_settings->test_brightness)
				brightCountminY++;
		}
//fprintf(stderr, "\n") ;

		// keep moving minY down until brightness registers
		if (brightCountminY < 5) {
			minY = y;
		}


		y = height - start_row - delta;
//		fprintf(stderr, "2: Y=%d\n  X: ", y) ;
		for (x = start_col + delta; x <= width - start_col - delta; x += step) {
//			fprintf(stderr, "%d,", x) ;
//			if (haslogo[y * width + x])
//				continue;
			hereBright = frame[y * width + x];
			frame_state->histogram[hereBright]++;

			if (hereBright > frame_state->max_bright) frame_state->max_bright = hereBright ;

			// update brightness count for maxY
			if (hereBright > frame_settings->test_brightness)
				brightCountmaxY++;
		}
//		fprintf(stderr, "\n") ;

		// keep moving maxY up until brightness registers
		if (brightCountmaxY < 5) {
			maxY = y;
		}

		x = start_col + delta;
//		fprintf(stderr, "3: X=%d\n  Y: ", x) ;
		for (y = start_row + delta; y <= height - start_row - delta; y += step) {
//			fprintf(stderr, "%d,", y) ;
//			if (haslogo[y * width + x])
//				continue;
			hereBright = frame[y * width + x];
			frame_state->histogram[hereBright]++;

			if (hereBright > frame_state->max_bright) frame_state->max_bright = hereBright ;

			// update brightness count for minX
			if (hereBright > frame_settings->test_brightness)
				brightCountminX++;
		}
//		fprintf(stderr, "\n") ;

		// keep moving minX right until brightness registers
		if (brightCountminX < 5) {
			minX = x;
		}
//		fprintf(stderr, "brightCountmaxX=%d\n", brightCountmaxX) ;

		x = width - start_col - delta;

		// don't bother with brightness update if maxX brightness registers
		if (brightCountmaxX < 5) {
//			fprintf(stderr, "4: X=%d\n  Y: ", x) ;
			for (y = start_row + delta; y <= height - start_row - delta; y += step) {
//				if (haslogo[y * width + x])
//					continue;
				hereBright = frame[y * width + x];
				frame_state->histogram[hereBright]++;

				if (hereBright > frame_state->max_bright) frame_state->max_bright = hereBright ;

				// update brightness count for maxX
				if (hereBright > frame_settings->test_brightness)
					brightCountmaxX++;
			}
//			fprintf(stderr, "\n") ;

			// keep moving maxX left until brightness registers
			if (brightCountmaxX < 5) {
				maxX = x;
			}
		}

		delta += step;

		if (delta > width / 2 || delta > height / 2)
		{
			// ****** STOP ******
			break;
		}
	}

}

//---------------------------------------------------------------------------------------------------------------------------
void calc_frame_state(
		struct TS_frame_info *frameinfo,
		int width, int height,
		int chroma_width, int chroma_height,
		uint8_t * const * buf, int framenum,
		struct Ad_frame_settings *frame_settings,
		struct Ad_frame_state *frame_state,
		struct Ad_frame_results *results,
		struct Ad_frame_totals *totals
		)
{
int 		i ;
unsigned 	pixels = 0 ;
long		similar = 0;


	// remove logo area if required
	if (frame_settings->remove_logo && frame_settings->logo_set)
	{
		_remove_logo(width, height,
				chroma_width, chroma_height,
				(uint8_t **)buf,
				frame_settings) ;
	}

	// Chroma
	frame_state->max_diff = _chroma_diff(chroma_width, chroma_height, buf, framenum, frame_settings, frame_state) ;

	// Frame stats
	_frame_state(width, height, buf, framenum, frame_settings, frame_state) ;

	frame_state->last_brightness = frame_state->brightness;
	frame_state->brightness = 0;

	frame_state->hasBright = 0;
	frame_state->dimCount = 0;

#ifdef DEBUG_STATE
dump_histo("Last Histograme", frame_state->lastHistogram) ;
dump_histo("Current Histograme", frame_state->histogram) ;
#endif

	// pixels = SUM( num_pixels[pixel_val] )
	// brightness = SUM( pixel_val * num_pixels[pixel_val] ) / SUM( num_pixels[pixel_val] )
	//
	//
	for (i = 255; i > frame_settings->max_brightness; i--)
	{
		pixels += frame_state->histogram[i];
		frame_state->brightness += frame_state->histogram[i] * i;

		// ** > max **
		if (frame_state->histogram[i])
			frame_state->hasBright++;

		if (frame_state->histogram[i] < frame_state->lastHistogram[i])
			similar += frame_state->histogram[i];
		else
			similar += frame_state->lastHistogram[i];
	}

	for (i = frame_settings->max_brightness; i > frame_settings->test_brightness; i--)
	{
		pixels += frame_state->histogram[i];
		frame_state->brightness += frame_state->histogram[i] * i;

		// ** max .. test **
		frame_state->dimCount += frame_state->histogram[i];

		if (frame_state->histogram[i] < frame_state->lastHistogram[i])
			similar += frame_state->histogram[i];
		else
			similar += frame_state->lastHistogram[i];
	}

	for (i = frame_settings->test_brightness; i >= 0; i--)
	{
		pixels += frame_state->histogram[i];
		frame_state->brightness += frame_state->histogram[i] * i;

		if (frame_state->histogram[i] < frame_state->lastHistogram[i])
			similar += frame_state->histogram[i];
		else
			similar += frame_state->lastHistogram[i];
	}
	frame_state->brightness /= pixels;

	frame_state->dimCount = frame_state->dimCount * 100 / pixels ;

	//---------------
	frame_state->sceneChangePercent = 100 - (unsigned)(100.0 * similar / pixels);
	if (frame_state->sceneChangePercent < 0) frame_state->sceneChangePercent=0 ;
	if (frame_state->sceneChangePercent > 100) frame_state->sceneChangePercent=100 ;

	//------------------
	// uniform = SUM( (pixel_val  - brightness) * num_pixels[pixel_val] ) 			255 >= pixel_val > brightness + noise
	// uniform = SUM( (brightness - pixel_val)  * num_pixels[pixel_val] ) 			brightness - noise >= pixel_val >= 0
	//
#ifdef DEBUG_STATE
fprintf(stderr, "uniform calc: bright=%d, noise=%d, pixels=%d\n",
		frame_state->brightness, frame_settings->noise_level, pixels) ;
#endif

	frame_state->uniform = 0;
	for (i = 255; i > frame_state->brightness + frame_settings->noise_level; i--) {
		frame_state->uniform +=  frame_state->histogram[i] * (i - frame_state->brightness);
	}
#ifdef DEBUG_STATE
fprintf(stderr, " + uniform calc A: uniform=%d\n", frame_state->uniform) ;
#endif
	for (i = frame_state->brightness - frame_settings->noise_level; i >= 0; i--) {
		frame_state->uniform +=  frame_state->histogram[i] * (frame_state->brightness - i);
	}
#ifdef DEBUG_STATE
fprintf(stderr, " + uniform calc B: uniform=%d\n", frame_state->uniform) ;
#endif
//	frame_state->uniform = ((double)frame_state->uniform) * 730/pixels;
	frame_state->uniform = (unsigned)(((double)frame_state->uniform) * 100/pixels);
#ifdef DEBUG_STATE
fprintf(stderr, " + uniform calc C: uniform=%d\n", frame_state->uniform) ;
#endif

	if (frame_state->min_uniform > frame_state->uniform) frame_state->min_uniform = frame_state->uniform ;
	if (frame_state->max_uniform < frame_state->uniform) frame_state->max_uniform = frame_state->uniform ;

	//---------------
	if (abs(frame_state->brightness - frame_state->last_brightness) > frame_settings->brightness_jump)
	{
		results->black_frame = 1 ;

		frame_dbg_prt(1, ("Black Frame %6i [ %u ..  %u] - Black frame because large brightness change from %i to %i with uniform %i (chroma %u)\n",
				framenum, frameinfo->pesinfo.start_pkt, frameinfo->pesinfo.end_pkt, frame_state->last_brightness, frame_state->brightness, frame_state->uniform, frame_state->max_diff));
	}

	if (frame_state->max_bright <= frame_settings->max_black)
	{
		results->black_frame = 1 ;

		frame_dbg_prt(1, ("Black Frame %6i [ %u ..  %u] - Black frame because max bright %d < threshold (chroma %d)\n",
				framenum, frameinfo->pesinfo.start_pkt, frameinfo->pesinfo.end_pkt, frame_state->max_bright, frame_state->max_diff));
	}

	if (frame_state->sceneChangePercent > frame_settings->schange_cutlevel)
	{
		results->scene_frame = 1 ;

		frame_dbg_prt(1, ("Scene Frame %6i [ %u ..  %u] - Black frame because large scene change of %i, uniform %i\n",
				framenum, frameinfo->pesinfo.start_pkt, frameinfo->pesinfo.end_pkt, frame_state->sceneChangePercent, frame_state->uniform));
	}
	if ((int)frame_state->sceneChangePercent - (int)frame_state->prev_sceneChangePercent > (int)frame_settings->schange_jump)
	{
		results->scene_frame = 1 ;

		frame_dbg_prt(1, ("Scene Frame %6i [ %u ..  %u] - Black frame because large scene change of %i to %i, uniform %i\n",
				framenum, frameinfo->pesinfo.start_pkt, frameinfo->pesinfo.end_pkt, frame_state->prev_sceneChangePercent, frame_state->sceneChangePercent, frame_state->uniform));
	}

	frame_dbg_prt(1, ("# frame %5d : has %d, dim %d, brightness %d, similar %ld (pixels %d), scene change %d%%, uniform %d [%d .. %d]\n", framenum,
			frame_state->hasBright, frame_state->dimCount, frame_state->brightness,
			similar, pixels, frame_state->sceneChangePercent, frame_state->uniform,
			frame_state->min_uniform, frame_state->max_uniform)) ;

	results->screen_width = width ;
	results->screen_height = height ;
	results->brightness = frame_state->brightness ;
	results->uniform = frame_state->uniform ;
	results->dimCount = frame_state->dimCount ;
	results->sceneChangePercent = frame_state->sceneChangePercent ;

	// check for screen size change
	results->size_change = 0 ;
	if (frame_state->prev_screen_height && frame_state->prev_screen_width)
	{
		if (results->screen_width != frame_state->prev_screen_width) results->size_change = 1 ;
		if (results->screen_height != frame_state->prev_screen_height) results->size_change = 1 ;
	}

	//---------------
	frame_state->prev_sceneChangePercent = frame_state->sceneChangePercent ;
	frame_state->prev_screen_width = results->screen_width ;
	frame_state->prev_screen_height = results->screen_height ;

	// Totals
	if (results->black_frame) ++totals->num_black_frames ;
	if (results->scene_frame) ++totals->num_scene_frames ;
	if (results->size_change) ++totals->num_size_frames ;

}




//---------------------------------------------------------------------------------------------------------------------------
// Initialise logo area
void frame_set_logo_area(struct Ad_frame_settings *settings,
		unsigned	logo_set,
		unsigned	logo_y1,		// top left
		unsigned	logo_x1,
		unsigned	logo_y2,		// bottom right
		unsigned	logo_x2)
{
	settings->logo_set = logo_set ;
	if (logo_set)
	{
		settings->logo_x1 = logo_x1 ;
		settings->logo_y1 = logo_y1 ;
		settings->logo_x2 = logo_x2 ;
		settings->logo_y2 = logo_y1 ;
	}
	else
	{
		settings->logo_x1 = 0 ;
		settings->logo_y1 = 0 ;
		settings->logo_x2 = 0 ;
		settings->logo_y2 = 0 ;
	}
}


//---------------------------------------------------------------------------------------------------------------------------
// Initialise the user data
void frame_init_settings(struct Ad_frame_settings *settings)
{
	settings->debug = 0 ;

	settings->max_brightness = 60;				// frame not black if any pixels checked are greater than this (scale 0 to 255)
	settings->test_brightness = 40;				// frame not pure black if any pixels are greater than this, will check average

	settings->brightness_jump = 200;
	settings->schange_cutlevel = 85;
	settings->schange_jump = 30;

	settings->noise_level=5;

	settings->window_percent = WINDOW_PERCENT;

	// maximum pixel value considered as a black frame
	settings->max_black = MAX_BLACK ;

	// no logo removal
	settings->remove_logo = 0 ;
	frame_set_logo_area(settings, 0, 0,0, 0,0) ;

	// Perl settings
	// set_perl_settings(settings, mx_ad, mn_ad, mn_pr, s_pd, e_pd, mn_fr, fr_wn, mx_gp, r_en, r_mn_gp)
	set_perl_settings(settings,
		FRAME_max_advert,
		FRAME_min_advert,
		FRAME_min_program,
		FRAME_start_pad,
		FRAME_end_pad,
		FRAME_min_frames,
		FRAME_frame_window,
		FRAME_max_gap,
		FRAME_reduce_end,
		FRAME_reduce_min_gap
	) ;

}

//---------------------------------------------------------------------------------------------------------------------------
// Initialise the state data
void frame_init_state(struct Ad_frame_state *state)
{
	state->brightness = 0 ;
	state->last_brightness = 0 ;
	state->prevsimilar = 0 ;
	state->prev_sceneChangePercent = 0 ;
	memset(state->histogram, 0, HISTOGRAM_BINS*sizeof(unsigned)) ;
	memset(state->lastHistogram, 0, HISTOGRAM_BINS*sizeof(unsigned)) ;

	state->min_uniform = 100000 ;
	state->max_uniform = 0 ;

	state->prev_screen_height = 0 ;
	state->prev_screen_width = 0 ;
}

//---------------------------------------------------------------------------------------------------------------------------
// Initialise the results
void frame_init_results(struct Ad_frame_results *results)
{
	results->black_frame = 0 ;
	results->scene_frame = 0 ;
	results->size_change = 0 ;
	results->screen_width = 0 ;
	results->screen_height = 0 ;
	results->brightness = 0 ;
	results->uniform = 0 ;
	results->dimCount = 0 ;
	results->sceneChangePercent = 0 ;
}

//---------------------------------------------------------------------------------------------------------------------------
// Initialise the totals
void frame_init_totals(struct Ad_frame_totals *totals)
{
	totals->num_black_frames = 0 ;
	totals->num_scene_frames = 0 ;
	totals->num_size_frames = 0 ;
}


//---------------------------------------------------------------------------------------------------------------------------
// Initialise the detector
void frame_detector_init(struct Ad_frame_settings *settings, struct Ad_frame_state *state)
{
	frame_init_settings(settings) ;
	frame_init_state(state) ;
}

//---------------------------------------------------------------------------------------------------------------------------
// Free up data created by the detector
void frame_detector_free(struct Ad_frame_state *state)
{

}


//---------------------------------------------------------------------------------------------------------------------------
// Run the detector
void frame_detector_run(struct TS_reader *tsreader, struct TS_pidinfo *pidinfo, struct TS_frame_info *frameinfo, const mpeg2_info_t *info,
		struct Ad_frame_settings *settings, struct Ad_frame_state *state, struct Ad_frame_results *results, struct Ad_frame_totals *totals)
{
unsigned framenum = frameinfo->framenum ;

	// clear down results
	frame_init_results(results) ;

	// get frame statistics
	calc_frame_state(
			frameinfo,
			info->sequence->width, info->sequence->height,
			info->sequence->chroma_width, info->sequence->chroma_height,
			info->display_fbuf->buf, framenum,
			settings,
			state,
			results,
			totals) ;
}


#ifdef FRAME_STANDALONE

//---------------------------------------------------------------------------------------------------------------------------
// TS parsing

// For stand-alone running

//---------------------------------------------------------------------------------------------------------------------------
void mpeg2_stats_hook(struct TS_pidinfo *pidinfo, struct TS_frame_info *frameinfo, const mpeg2_info_t *info, void *hook_data)
{
struct Ad_user_data *user_data = (struct Ad_user_data *)hook_data ;

//	// init
//	if (framenum == 0)
//	{
//		user_data->frame_state.brightness = 0 ;
//		user_data->frame_state.last_brightness = 0 ;
//		user_data->frame_state.prevsimilar = 0 ;
//		user_data->frame_state.prev_sceneChangePercent = 0 ;
//	}

	// set pid
	if (user_data->pid < 0)
	{
		user_data->pid = pidinfo->pid ;
		if (user_data->debug) fprintf(stderr, "Locked down TS parsing just to video PID = %d\n", pidinfo->pid) ;
	}

if (user_data->debug >= 4) fprintf(stderr, "mpeg2_stats_hook(frame=%06d)\n", framenum);

	// Update last frame number
	user_data->last_framenum = framenum ;

	// get frame statistics
	calc_frame_state(
			frameinfo,
			info->sequence->width, info->sequence->height,
			info->sequence->chroma_width, info->sequence->chroma_height,
			info->display_fbuf->buf, framenum,
			&user_data->frame_settings,
			&user_data->frame_state,
			&user_data->frame_results) ;

	{
	uint64_t rel_pts, rel_dts ;
	unsigned pts_frame, dts_frame ;
	unsigned pts_secs, dts_secs ;

	rel_pts = frameinfo->pesinfo.end_pts - frameinfo->pesinfo.start_pts ;
	pts_frame = (unsigned)(rel_pts * 25 / 90000) ;
	pts_secs = (unsigned)(rel_pts / 90000) ;

	rel_dts = frameinfo->pesinfo.dts - frameinfo->pesinfo.start_dts ;
	dts_frame = (unsigned)(rel_dts * 25 / 90000) ;
	dts_secs = (unsigned)(rel_dts / 90000) ;

	fprintf(stderr, "# PTS video frame %5d : pts=%"PRIu64" [%"PRIu64" .. %"PRIu64" ] frm=%u sec=%u\n",
			framenum,
			frameinfo->pesinfo.dts,
			frameinfo->pesinfo.start_dts, frameinfo->pesinfo.end_dts,
			dts_frame, dts_secs
			) ;
	}
}


//============================================================================================
enum DVB_error run_stats_check(struct Ad_user_data *user_data,
		char *filename, unsigned num_pkts, unsigned skip)
{
struct TS_reader *tsreader ;

	tsreader = tsreader_new(filename) ;
    if (!tsreader)
    {
		fprintf(stderr,"ERROR %s: %s\n",filename,dvb_error_str(dvb_error_code));
		return(ERR_FILE);
    }

    fprintf(stderr, "Total Num packets=%u\n", tsreader->tsstate->total_pkts) ;

    tsreader->num_pkts = num_pkts ;
    tsreader->skip = skip ;
    tsreader->debug = 0 ;
    tsreader->user_data = user_data ;
    user_data->tsreader = tsreader ;

	tsreader->pid_hook = pid_hook ;
	tsreader->mpeg2_hook = mpeg2_stats_hook ;

    // process file
    tsreader_setpos(tsreader, 0, SEEK_SET, num_pkts) ;
    ts_parse(tsreader) ;

	fprintf(stderr, "Last frame=%u\n", user_data->last_framenum) ;

    // end
    tsreader_free(tsreader) ;

    return (ERR_NONE) ;
}
#endif