/*
 * ts_parse.h
 *
 *  Created on: 29 Apr 2010
 *      Author: sdprice1
 */

#ifndef TS_PARSE_H_
#define TS_PARSE_H_

#include <inttypes.h>

//-- config --
#include "config.h"

// dvb_lib
#include "list.h"
#include "dvb_error.h"

// libmpeg2
#include "mpeg2.h"
#include "mpeg2convert.h"

// mpeg2audio
#include "mpegaudio.h"

/*=============================================================================================*/
// CONSTANTS
/*=============================================================================================*/

#define NULL_PID		0x1fff
#define MAX_PID			NULL_PID
#define ALL_PID			(MAX_PID+1)

// ISO 13818-1
#define SYNC_BYTE			0x47
#define TS_PACKET_LEN		188
#define MAX_SECTION_LEN 	1021
#define TS_FREQ				90000

// create a buffer that is a number of packets long
// (this is approx 4k)
#define TS_BUFFSIZE		(22 * TS_PACKET_LEN)


// clear memory
#define CLEAR_MEM(mem)	memset(mem, 0, sizeof(*mem))


// PTS, timing etc
#define UNSET_TS			((int64_t)-1)
#define FPS					25
#define VIDEO_PTS_DELTA		(TS_FREQ / FPS)


/*=============================================================================================*/
// STRUCTURES
/*=============================================================================================*/

enum TS_pes_type {
	// ISO 11172-2 Video
	picture_start_code			= 0x100,
	// slice_start_codes (including slice_vertical_positions)	0x101 .. 0x1AF
	slice_start_code_start		= 0x101,
	slice_start_code_end		= 0x1AF,

	// reserved1				= 0x1B0,
	// reserved2				= 0x1B1,
	user_data_start_code		= 0x1B2,
	sequence_header_code		= 0x1B3,
	sequence_error_code			= 0x1B4,
	extension_start_code		= 0x1B5,
	// reserved3				= 0x1B6,
	sequence_end_code			= 0x1B7,
	group_start_code			= 0x1B8,

	// system start codes (see note)	0x1B9 .. 0x1FF
	system_start_code_start		= 0x1B9,
	system_start_code_end		= 0x1FF,

	// ISO 13818-1
	program_stream_map			= 0x1bc,
	private_stream_1			= 0x1bd,
	padding_stream				= 0x1be,
	private_stream_2			= 0x1bf,

	// 0x1C0 .. 0x1DF (audio stream 00..1F)
	audio_stream				= 0x1c0,
	audio_stream_mask			= 0x1e0,

	// 0x1E0 .. 0x1EF (video stream 0..F)
	video_stream				= 0x1e0,
	video_stream_mask			= 0x1f0,

	ECM_stream					= 0x1f0,
	EMM_stream					= 0x1f1,
	DSMCC_stream				= 0x1f2,
	a13522_stream				= 0x1f3,
	H2221_A_stream				= 0x1f4,
	H2221_B_stream				= 0x1f5,
	H2221_C_stream				= 0x1f6,
	H2221_D_stream				= 0x1f7,
	H2221_E_stream				= 0x1f8,
	ancillary_stream			= 0x1f9,
	program_stream_directory	= 0x1ff
};

enum TS_frame_flags {

	FRAME_FLAG_START			= 0x0001,
	FRAME_FLAG_SLICE			= 0x0002,
	FRAME_FLAG_USER_DATA		= 0x0004,
	FRAME_FLAG_SEQ_HEAD			= 0x0008,
	FRAME_FLAG_SEQ_ERROR		= 0x0010,
	FRAME_FLAG_EXTENSION		= 0x0020,
	FRAME_FLAG_SEQ_END			= 0x0040,
	FRAME_FLAG_GOP				= 0x0080,
	FRAME_FLAG_SYSTEM			= 0x0100,
	FRAME_FLAG_RESERVED			= 0x8000,
} ;


typedef struct mpeg2_audio {
	unsigned 		sample_rate	;
	unsigned 		channels ;
	unsigned 		samples_per_frame ;
	short			*audio ;
	unsigned 		samples ;
	unsigned 		audio_framenum ;
	unsigned 		framesize ;
	unsigned		pts_delta ;
	int64_t			pts ;
} mpeg2_audio_t ;

enum TS_progress_state {
	PROGRESS_START,
	PROGRESS_RUNNING,
	PROGRESS_END,
	PROGRESS_STOPPED
};



//----------------------------------------------------------------------------------------------
// Callbacks

struct TS_state ;
struct TS_buffer ;
struct TS_pidinfo ;
struct TS_pesinfo ;
struct TS_frame_info ;

typedef unsigned (*tsparse_pid_hook)(unsigned, void *) ;
typedef void (*tsparse_error_hook)(enum DVB_error, struct TS_pidinfo *, void *) ;
typedef void (*tsparse_payload_hook)(struct TS_pidinfo *, uint8_t *, unsigned, void *) ;
typedef void (*tsparse_ts_hook)(struct TS_pidinfo *, uint8_t *, unsigned, void *) ;
typedef void (*tsparse_pes_hook)(struct TS_pidinfo *, struct TS_pesinfo *, uint8_t *, unsigned, void *) ;
typedef void (*tsparse_pes_data_hook)(struct TS_pidinfo *, struct TS_pesinfo *, uint8_t *, unsigned, void *) ;
typedef void (*tsparse_progress_hook)(enum TS_progress_state state, unsigned progress, unsigned total, void *) ;
typedef void (*tsparse_mpeg2_hook)(struct TS_pidinfo *, struct TS_frame_info *, const mpeg2_info_t *, void *) ;
typedef void (*tsparse_mpeg2_rgb_hook)(struct TS_pidinfo *, struct TS_frame_info *, const mpeg2_info_t *, void *) ;
typedef void (*tsparse_audio_hook)(struct TS_pidinfo *, struct TS_pesinfo *, const mpeg2_audio_t *, void *) ;


//----------------------------------------------------------------------------------------------
// Generic buffer

#define MAGIC_BUFF	0x5344500B
struct TS_buffer {
	unsigned	MAGIC ;
	unsigned	buff_size ;
	unsigned	data_len ;
	uint8_t *	buff ;
};


//----------------------------------------------------------------------------------------------
// PID information

struct TS_pidinfo {
	unsigned pid ;
	unsigned err_flag ;
	unsigned pes_start ;
	unsigned afc ;
	unsigned pid_error ;

	unsigned pktnum ;
};

//----------------------------------------------------------------------------------------------
// Current PES state

enum TS_pes_psi {
	T_PSI,
	T_PES,
};

struct TS_pesinfo {
	unsigned code ;

	unsigned start_pkt ;
	unsigned end_pkt ;

	int64_t start_dts ;
	int64_t start_pts ;
	int64_t end_dts ;
	int64_t end_pts ;
	int64_t dts ;
	int64_t pts ;

	enum TS_pes_psi	pes_psi ;
	unsigned psi_error ;
	unsigned pes_error ;
	unsigned ts_error ;

	// reference to the data portion of the PES packet (if it has one)
	uint8_t * 	pesdata_p ;
	unsigned 	pesdata_len ;

};


//----------------------------------------------------------------------------------------------
// Information relating to the current video frame

struct TS_frame_info {
	unsigned framenum ;
	unsigned gop_pkt ;

	// copy of PES info for this frame
	struct TS_pesinfo pesinfo ;

	// copy of PID info for this frame
	struct TS_pidinfo pidinfo ;
};



//----------------------------------------------------------------------------------------------
// Current PID state

enum TS_pesstate {
	PES_SKIP,
	PES_HEADER,
	PES_PAYLOAD
};
#define MAGIC_PID		0x53445001
struct TS_pid {
    struct list_head    next;

	unsigned MAGIC ;
    struct TS_pidinfo	pidinfo ;
    struct TS_pesinfo	pesinfo ;
    struct TS_buffer *	pes_buff ;
    enum TS_pesstate	pes_state ;
};

#if 0
//----------------------------------------------------------------------------------------------
// Each TS packet consists of data and a state that is either collecting (i.e. filling a PES packet before
// checking), has been marked as an error, or has been marked as being good
// Once it's marked as good/bad, then all packets making up the PES can be processed
enum TS_pkstate {
	PKT_SKIP,
	PKT_FILLING,
	PKT_OK,
	PKT_ERR
};
struct TS_pkt {
    struct list_head    next;

	uint8_t			ts_packet[TS_PACKET_LEN] ;
	unsigned		pktnum;
	enum TS_pkstate pkt_state ;
};
#endif

//----------------------------------------------------------------------------------------------
// Current parse state
#define MAGIC_STATE		0x53445002
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 ;
};

//----------------------------------------------------------------------------------------------
// Current buffer state
struct TS_buff_state {
	uint8_t buffer[TS_BUFFSIZE];
	int buffer_len ;

	uint8_t *bptr ;
	int bytes_left ;

	int status;
	unsigned get_sync ;
	int running ;

	unsigned pktnum ;
};


//----------------------------------------------------------------------------------------------
// TS reader

#define MAGIC_READER	0x5344500F
typedef struct TS_reader {
	// set by user
	int						file ;
	unsigned 				debug ;
	unsigned				num_pkts ;
	int64_t					skip ;
	int						origin ;
	void 					*user_data ;

	tsparse_pid_hook		pid_hook ;
	tsparse_error_hook		error_hook ;
	tsparse_payload_hook	payload_hook ;
	tsparse_ts_hook			ts_hook ;
	tsparse_pes_hook		pes_hook ;
	tsparse_pes_data_hook	pes_data_hook ;
	tsparse_mpeg2_hook		mpeg2_hook ;
	tsparse_mpeg2_rgb_hook	mpeg2_rgb_hook ;
	tsparse_audio_hook		audio_hook ;
	tsparse_progress_hook	progress_hook ;

	// internally set
	struct TS_state			*tsstate ;
	struct TS_buff_state	buff_state ;
	unsigned				MAGIC ;

	struct {
		unsigned				step ;
		unsigned				scale ;
		unsigned				next_progress ;
		unsigned				total ;
	}						progress_info ;

	// Optional libmpeg2
	struct {
		mpeg2dec_t 				*decoder;
		const mpeg2_info_t 		*info;
		unsigned				start_framenum;
		unsigned				framenum;
		unsigned				gop_pktnum;
		uint8_t 				*video_buffer ;
		unsigned				convert_rgb ;

		struct TS_frame_info	*frame_info_list ;
		unsigned				frame_info_list_size ;
		unsigned				frame_info_index ;
	} 						mpeg2 ;

	// Optional mpeg2audio
	struct {
		unsigned				audio_init ;
		unsigned				framenum;
		short					*audio_buf ;
		uint8_t					*storage_buf ;
		uint8_t					*write_ptr ;
		uint8_t					*read_ptr ;
		unsigned 				audio_samples ;
	} 						mpeg2audio ;

} TSReader ;


/*=============================================================================================*/
// GLOBAL FUNCTIONS
/*=============================================================================================*/

// utility
void dump_buff(const uint8_t *payload, unsigned payload_len, unsigned display_len) ;

void buffer_free(struct TS_buffer **buff);
struct TS_buffer *buffer_new();
void buffer_clear(struct TS_buffer *bp);
uint8_t *buffer_data(struct TS_buffer **buff, const uint8_t *data, unsigned data_len);

// TS utils
void ts_null_packet(uint8_t *packet, unsigned packet_len) ;

// TS parsing
int tsreader_setpos(struct TS_reader *tsreader, int skip_pkts, int origin, unsigned num_pkts) ;
void tsreader_start_framenum(struct TS_reader *tsreader, unsigned framenum) ;
struct TS_reader *tsreader_new(char *filename) ;
struct TS_reader *tsreader_new_nofile() ;
void tsreader_free(struct TS_reader *) ;
int ts_parse(struct TS_reader *tsreader) ;
void tsreader_stop(struct TS_reader *tsreader) ;

int tsreader_data_start(struct TS_reader *tsreader) ;
int tsreader_data_add(struct TS_reader *tsreader, uint8_t *data, unsigned data_len) ;
int tsreader_data_end(struct TS_reader *tsreader) ;


#endif /* TS_PARSE_H_ */
