/*
 * MPEG2 transport stream (aka DVB) demuxer
 * Copyright (c) 2002-2003 Fabrice Bellard
 *
 * This file is part of Mpxplay/FFmpeg
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_OUTPUT stdout

#include "mpxplay.h"

#ifdef MPXPLAY_LINK_INFILE_FFMPG

#include "ffutils.h"
#include <string.h>

#define TS_FEC_PACKET_SIZE 204
#define TS_DVHS_PACKET_SIZE 192
#define TS_PACKET_SIZE 188
#define TS_MAX_PACKET_SIZE 204

#define NB_PID_MAX 8192
#define MAX_SECTION_SIZE 4096

// pids
#define PAT_PID   0x0000
//#define SDT_PID   0x0011

// table ids
#define PAT_TID   0x00
#define PMT_TID   0x02
//#define SDT_TID   0x42

#define STREAM_TYPE_PRIVATE_DATA    0x06

#define MAX_RESYNC_SIZE 65536
#define MAX_PES_PAYLOAD 200*1024

// enough for PES header + length
#define PES_START_SIZE  6
#define PES_HEADER_SIZE 9
#define MAX_PES_HEADER_SIZE (9 + 255)
//#define MAX_PIDS_PER_PROGRAM 64

enum MpegTSFilterType{
 MPEGTS_SECTION = 1,
 MPEGTS_PES,
};

enum MpegTSState{
 MPEGTS_HEADER = 0,
 MPEGTS_PESHEADER,
 MPEGTS_PESHEADER_FILL,
 MPEGTS_PAYLOAD,
 MPEGTS_SKIP,
};

typedef struct MpegTSFilter MpegTSFilter;

typedef int PESCallback(struct MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos);

struct MpegTSPESFilter{
 PESCallback *pes_cb;
 void *opaque;
};

typedef void SectionCallback(struct MpegTSFilter *f, const uint8_t *buf, int len);

typedef void SetServiceCallback(void *opaque, int ret);

struct MpegTSSectionFilter{
 int section_index;
 int section_h_size;
 uint8_t *section_buf;
 SectionCallback *section_cb;
 void *opaque;
 uint8_t check_crc;
 uint8_t end_of_section_reached;
};

struct MpegTSFilter{
 int pid;
 int last_cc; // last cc code (-1 if first packet)
 enum MpegTSFilterType type;
 union{
  struct MpegTSPESFilter pes_filter;
  struct MpegTSSectionFilter section_filter;
 }u;
};

struct SectionHeader {
 uint16_t id;
 uint8_t tid;
 uint8_t version;
 uint8_t sec_num;
 uint8_t last_sec_num;
};

struct MpegTSContext {
 struct AVFormatContext *stream;
 mpxp_filesize_t packet_filepos;
 int raw_packet_size;
 int auto_guess;
 int mpeg2ts_compute_pcr;
 int64_t cur_pcr;
 int pcr_incr;

 unsigned int stop_parse;
 AVPacket *pkt;
 mpxp_filesize_t last_pos;

 struct MpegTSFilter *pids[NB_PID_MAX];
};

struct PESContext{
 int pid;
 int pcr_pid; // if -1 then all packets containing PCR are considered
 int stream_type;
 struct MpegTSContext *ts;
 AVFormatContext *stream;
 AVStream *st;
 AVStream *sub_st; // stream for the embedded AC3 stream in HDMV TrueHD
 enum MpegTSState state;
 int data_index;
 int total_size;
 int pes_header_size;
 int extended_stream_id;
 int64_t pts, dts;
 int64_t ts_packet_pos; // position of first TS packet of this PES packet
 uint8_t *buffer;
 int bufsize; // !!! by PDS
 uint8_t header[MAX_PES_HEADER_SIZE];
};

struct StreamType{
 uint32_t stream_type;
 enum AVMediaType codec_type;
 enum CodecID codec_id;
};

static const struct StreamType ISO_types[] = {
#ifdef MPXPLAY_LINK_VIDEO
 { 0x01, AVMEDIA_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO },
 { 0x02, AVMEDIA_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO },
#endif
 { 0x03, AVMEDIA_TYPE_AUDIO,        CODEC_ID_MP2 },
 { 0x04, AVMEDIA_TYPE_AUDIO,        CODEC_ID_MP2 },
 { 0x0f, AVMEDIA_TYPE_AUDIO,        CODEC_ID_AAC },
#ifdef MPXPLAY_LINK_VIDEO
 { 0x10, AVMEDIA_TYPE_VIDEO,      CODEC_ID_MPEG4 },
#endif
 { 0x11, AVMEDIA_TYPE_AUDIO,   CODEC_ID_AAC_LATM },
#ifdef MPXPLAY_LINK_VIDEO
 { 0x1b, AVMEDIA_TYPE_VIDEO,       CODEC_ID_H264 },
 { 0xd1, AVMEDIA_TYPE_VIDEO,      CODEC_ID_DIRAC },
 { 0xea, AVMEDIA_TYPE_VIDEO,        CODEC_ID_VC1 },
#endif
 { 0,0,0 },
};

static const struct StreamType HDMV_types[] = {
 { 0x80, AVMEDIA_TYPE_AUDIO, CODEC_ID_PCM_BLURAY },
 { 0x81, AVMEDIA_TYPE_AUDIO, CODEC_ID_AC3 },
 { 0x82, AVMEDIA_TYPE_AUDIO, CODEC_ID_DTS },
 { 0x83, AVMEDIA_TYPE_AUDIO, CODEC_ID_TRUEHD },
 { 0x84, AVMEDIA_TYPE_AUDIO, CODEC_ID_EAC3 },
#ifdef MPXPLAY_LINK_VIDEO
 { 0x90, AVMEDIA_TYPE_SUBTITLE, CODEC_ID_HDMV_PGS_SUBTITLE },
#endif
 { 0,0,0 },
};

static const struct StreamType MISC_types[] = {
 { 0x81, AVMEDIA_TYPE_AUDIO, CODEC_ID_AC3 },
 { 0x85, AVMEDIA_TYPE_AUDIO, CODEC_ID_DTS }, // HDMV types? (no matter)
 { 0x86, AVMEDIA_TYPE_AUDIO, CODEC_ID_DTS }, //
 { 0x8a, AVMEDIA_TYPE_AUDIO, CODEC_ID_DTS },
 { 0,0,0 },
};

static const struct StreamType REGD_types[] = {
#ifdef MPXPLAY_LINK_VIDEO
 { PDS_GET4C_LE32('d','r','a','c'), AVMEDIA_TYPE_VIDEO, CODEC_ID_DIRAC },
#endif
 { PDS_GET4C_LE32('A','C','-','3'), AVMEDIA_TYPE_AUDIO, CODEC_ID_AC3 },
 { 0,0,0 },
};

static const struct StreamType DESC_types[] = {
 { 0x6a, AVMEDIA_TYPE_AUDIO, CODEC_ID_AC3 },
 { 0x7a, AVMEDIA_TYPE_AUDIO, CODEC_ID_EAC3 },
 { 0x7b, AVMEDIA_TYPE_AUDIO, CODEC_ID_DTS },
#ifdef MPXPLAY_LINK_VIDEO
 { 0x56, AVMEDIA_TYPE_SUBTITLE, CODEC_ID_DVB_TELETEXT },
 { 0x59, AVMEDIA_TYPE_SUBTITLE, CODEC_ID_DVB_SUBTITLE },
#endif
 { 0,0,0 },
};

#ifdef __DOS__
extern unsigned int intsoundcontrol;
#endif

// Assemble PES packets out of TS packets, and then call the "section_cb" function when they are complete.
static void write_section_data(struct MpegTSFilter *tss1,const uint8_t *buf, int buf_size, int is_start)
{
 struct MpegTSSectionFilter *tss = &tss1->u.section_filter;
 int len;

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"write_section_data 1 size:%d hsize:%d s:%d",buf_size,tss->section_h_size,is_start);

 if(is_start){
  memcpy(tss->section_buf, buf, buf_size);
  tss->section_index = buf_size;
  tss->section_h_size = -1;
  tss->end_of_section_reached = 0;
 }else{
  if(tss->end_of_section_reached)
   return;
  len = 4096 - tss->section_index;
  if(buf_size < len)
   len = buf_size;
  memcpy(tss->section_buf + tss->section_index, buf, len);
  tss->section_index += len;
 }

 if(tss->section_h_size == -1 && tss->section_index >= 3) {
  len = (AV_RB16(tss->section_buf + 1) & 0xfff) + 3;
  if (len > 4096)
   return;
  tss->section_h_size = len;
 }

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"write_section_data 2 size:%d hsize:%d %4.4X s:%d",buf_size,tss->section_h_size,tss->section_h_size,is_start);

 if((tss->section_h_size!=-1) && (tss->section_index>=tss->section_h_size)){
  tss->end_of_section_reached = 1;
  if(!tss->check_crc || av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1,tss->section_buf, tss->section_h_size) == 0)
   tss->section_cb(tss1, tss->section_buf, tss->section_h_size);
 }
}

static struct MpegTSFilter *mpegts_open_section_filter(struct MpegTSContext *ts, unsigned int pid,
                                         SectionCallback *section_cb, void *opaque,
                                         int check_crc)

{
 struct MpegTSFilter *filter;
 struct MpegTSSectionFilter *sec;

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "Section_filter: pid=0x%x ts:%8.8X ts->pids[pid]:%8.8X", pid,(unsigned long)ts,(unsigned long)ts->pids[pid]);

 if((pid>=NB_PID_MAX) || ts->pids[pid])
  return NULL;
 filter = calloc(1,sizeof(struct MpegTSFilter));
 if(!filter)
  return NULL;
 filter->type = MPEGTS_SECTION;
 filter->pid = pid;
 filter->last_cc = -1;
 sec = &filter->u.section_filter;
 sec->section_cb = section_cb;
 sec->opaque = opaque;
 sec->check_crc = check_crc;
 sec->section_buf=malloc(MAX_SECTION_SIZE);
 if(!sec->section_buf) {
  free(filter);
  return NULL;
 }
 ts->pids[pid] = filter;
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Section_filter: OK pid=0x%4.4x ts->pids[pid]:%8.8X",pid,(unsigned long)ts->pids[pid]);
 return filter;
}

static struct MpegTSFilter *mpegts_open_pes_filter(struct MpegTSContext *ts, unsigned int pid,
                                     PESCallback *pes_cb,
                                     void *opaque)
{
 struct MpegTSFilter *filter;
 struct MpegTSPESFilter *pes;

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "Pes_filter: pid=0x%x ts:%8.8X ts->pids[pid]:%8.8X", pid,(unsigned long)ts,(unsigned long)ts->pids[pid]);

 if((pid>=NB_PID_MAX) || ts->pids[pid])
  return NULL;
 filter = calloc(1,sizeof(struct MpegTSFilter));
 if(!filter)
  return NULL;
 ts->pids[pid] = filter;
 filter->type = MPEGTS_PES;
 filter->pid = pid;
 filter->last_cc = -1;
 pes = &filter->u.pes_filter;
 pes->pes_cb = pes_cb;
 pes->opaque = opaque;
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Pes_filter: OK pid:0x%4.4x ts->pids[pid]:%8.8X",pid,(unsigned long)ts->pids[pid]);
 return filter;
}

static void mpegts_close_filter(struct MpegTSContext *ts, struct MpegTSFilter *filter)
{
 if(filter){
  struct PESContext *pes;
  int pid;
  switch(filter->type){
   case MPEGTS_SECTION:av_freep(&filter->u.section_filter.section_buf);break;
   case MPEGTS_PES:
    pes=filter->u.pes_filter.opaque;
    if(pes){
     av_freep(&pes->buffer);
     pes->bufsize=0;
     if(!pes->st)
      av_freep(&filter->u.pes_filter.opaque);
    }
  }
  pid=filter->pid;
  if(pid<NB_PID_MAX)
   ts->pids[pid]=NULL;
  free(filter);
 }
}

#define GET8_FAST(p) (*p++)

static inline int get8(const uint8_t **pp, const uint8_t *p_end)
{
 const uint8_t *p;
 int c;

 p = *pp;
 if(p >= p_end)
  return -1;
 c=GET8_FAST(p);
 *pp = p;
 return c;
}

static int get16(const uint8_t **pp, const uint8_t *p_end)
{
 const uint8_t *p;
 int c;

 p = *pp;
 if((p + 1) >= p_end)
  return -1;
 c = AV_RB16(p);
 p += 2;
 *pp = p;
 return c;
}

static int parse_section_header(struct SectionHeader *h,const uint8_t **pp, const uint8_t *p_end)
{
 const uint8_t *p=*pp;

 if((p+8)>=p_end)
  return -1;

 h->tid = GET8_FAST(p);
 p += 2;
 h->id = AV_RB16(p);p+=2;
 h->version = (GET8_FAST(p) >> 1) & 0x1f;
 h->sec_num = GET8_FAST(p);
 h->last_sec_num = GET8_FAST(p);
 *pp=p;
 return 0;
}

static int mpegts_find_stream_type(AVStream *st,uint32_t stream_type, const struct StreamType *types)
{
 for(; types->stream_type; types++) {
  if (stream_type == types->stream_type) {
   st->codec->codec_type = types->codec_type;
   st->codec->codec_id   = types->codec_id;
   return 0;
  }
 }
 return -1;
}

static int mpegts_set_stream_info(AVStream *st, struct PESContext *pes,uint32_t stream_type, uint32_t prog_reg_desc)
{
 st->priv_data = pes;
 st->codec->codec_type = AVMEDIA_TYPE_DATA;
 st->codec->codec_id   = CODEC_ID_NONE;
 st->need_parsing = AVSTREAM_PARSE_FULL;
 pes->st = st;
 pes->stream_type = stream_type;

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"set st:%4.4X prd:%8.8X cid:%8.8X",
 // stream_type,prog_reg_desc,st->codec->codec_id);

 st->codec->codec_tag = pes->stream_type;

 mpegts_find_stream_type(st, pes->stream_type, ISO_types);
 if((prog_reg_desc==AV_RL32("HDMV")) && (st->codec->codec_id==CODEC_ID_NONE)){
  mpegts_find_stream_type(st, pes->stream_type, HDMV_types);
  //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"SET st:%4.4X prd:%8.8X h:%8.8X cid:%8.8X",
  // stream_type,prog_reg_desc,AV_RL32("HDMV"),st->codec->codec_id);
  if((pes->stream_type == 0x83) && pes->ts->auto_guess){
   // HDMV TrueHD streams also contain an AC3 coded version of the audio track - add a second stream for this
   AVStream *sub_st;
   struct PESContext *sub_pes = malloc(sizeof(*sub_pes));
   if(!sub_pes)
    return AVERROR(ENOMEM);
   memcpy(sub_pes, pes, sizeof(*sub_pes));

   sub_st=av_new_stream(pes->stream, pes->pid);
   if(!sub_st){
    free(sub_pes);
    return AVERROR(ENOMEM);
   }

   sub_st->priv_data = sub_pes;
   sub_st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
   sub_st->codec->codec_id   = CODEC_ID_AC3;
   sub_st->need_parsing = AVSTREAM_PARSE_FULL;
   sub_pes->sub_st = pes->sub_st = sub_st;
  }
 }
 if(st->codec->codec_id == CODEC_ID_NONE)
  mpegts_find_stream_type(st, pes->stream_type, MISC_types);

 return 0;
}

static int64_t get_pts(const uint8_t *p)
{
 int64_t pts = (int64_t)((p[0] >> 1) & 0x07) << 30;
 pts |= (AV_RB16(p + 1) >> 1) << 15;
 pts |=  AV_RB16(p + 3) >> 1;
 return pts;
}

static void new_pes_packet(struct PESContext *pes, AVPacket *pkt)
{
 av_new_packet(pkt,pes->data_index);

 memcpy(pkt->data,pes->buffer,pes->data_index);
 memset(pkt->data+pes->data_index, 0, FF_INPUT_BUFFER_PADDING_SIZE);

 // Separate out the AC3 substream from an HDMV combined TrueHD/AC3 PID
 if(pes->sub_st && pes->stream_type == 0x83 && pes->extended_stream_id == 0x76)
  pkt->stream_index = pes->sub_st->index;
 else
  pkt->stream_index = pes->st->index;
 pkt->pts = pes->pts;
 pkt->dts = pes->dts;
 pkt->pos = pes->ts_packet_pos;

 pes->pts = AV_NOPTS_VALUE;
 pes->dts = AV_NOPTS_VALUE;
 pes->data_index = 0;
}

static int mpegts_push_data(struct MpegTSFilter *filter,const uint8_t *buf, int buf_size, int is_start,int64_t pos)
{
 struct PESContext *pes = filter->u.pes_filter.opaque;
 struct MpegTSContext *ts = pes->ts;
 const uint8_t *p;
 int len, code;

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mpegts_push_data bs:%d is:%d p:%d ts:%8.8X pkt:%8.8X",
 //    buf_size,is_start,(long)pos,(long)ts,(long)ts->pkt);

 if(!ts->pkt)
  return 0;

 if(is_start) {
  if((pes->state==MPEGTS_PAYLOAD) && (pes->data_index>0)){
   new_pes_packet(pes, ts->pkt);
   ts->stop_parse = 1;
  }
  pes->state = MPEGTS_HEADER;
  pes->data_index = 0;
  pes->ts_packet_pos = pos;
 }

 p = buf;
 while(buf_size>0){
  switch(pes->state){
   case MPEGTS_HEADER:
    len = PES_START_SIZE - pes->data_index;
    if(len > buf_size)
     len = buf_size;
    memcpy(pes->header + pes->data_index, p, len);
    pes->data_index += len;
    p += len;
    buf_size -= len;
    if(pes->data_index == PES_START_SIZE) {
     // we got all the PES or section header. We can now decide
     if(pes->header[0] == 0x00 && pes->header[1] == 0x00 &&
      pes->header[2] == 0x01) {
      code = pes->header[3] | 0x100;

      //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "pid=%x pes_code=%#x", pes->pid, code);

      if((pes->st && pes->st->discard == AVDISCARD_ALL) || (code == 0x1be)) // padding_stream
       goto skip;

      //stream not present in PMT
      if(!pes->st){
       if(!ts->auto_guess)
        goto skip;
       pes->st = av_new_stream(ts->stream, pes->pid);
       if(!pes->st)
        return AVERROR(ENOMEM);
       mpegts_set_stream_info(pes->st, pes, 0, 0);
      }

      pes->total_size = AV_RB16(pes->header + 4);
      // NOTE: a zero total size means the PES size is unbounded
      if(!pes->total_size)
       pes->total_size = MAX_PES_PAYLOAD;

      if(pes->bufsize<pes->total_size){
#ifdef __DOS__
       if(!ts->auto_guess && (intsoundcontrol&INTSOUND_DECODER))
        goto skip;
#endif
       if(pes->buffer)
        free(pes->buffer);
       pes->buffer = malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE);
       if(!pes->buffer){
        pes->bufsize=0;
        return AVERROR(ENOMEM);
       }
       pes->bufsize=pes->total_size;
      }

      if(code != 0x1bc && code != 0x1bf && // program_stream_map, private_stream_2
         code != 0x1f0 && code != 0x1f1 && // ECM, EMM
         code != 0x1ff && code != 0x1f2 && // program_stream_directory, DSMCC_stream
         code != 0x1f8                     // ITU-T Rec. H.222.1 type E stream
      ){
       pes->state = MPEGTS_PESHEADER;
       if(pes->st->codec->codec_id == CODEC_ID_NONE){
        //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "pid=%x code:%4.4X stream_type=%x probing",pes->pid,code,pes->stream_type);
        pes->st->codec->codec_id = CODEC_ID_PROBE;
       }
      }else{
       pes->state = MPEGTS_PAYLOAD;
       pes->data_index = 0;
      }
     }else{
      // otherwise, it should be a table
     skip:
      pes->state = MPEGTS_SKIP;
      continue;
     }
    }
    break;
   case MPEGTS_PESHEADER:
    len = PES_HEADER_SIZE - pes->data_index;
    if(len < 0)
     return -1;
    if(len > buf_size)
     len = buf_size;
    memcpy(pes->header + pes->data_index, p, len);
    pes->data_index += len;
    p += len;
    buf_size -= len;
    if(pes->data_index == PES_HEADER_SIZE){
     pes->pes_header_size = pes->header[8] + 9;
     pes->state = MPEGTS_PESHEADER_FILL;
    }
    break;
   case MPEGTS_PESHEADER_FILL:
    len = pes->pes_header_size - pes->data_index;
    if(len < 0)
     return -1;
    if(len > buf_size)
     len = buf_size;
    memcpy(pes->header + pes->data_index, p, len);
    pes->data_index += len;
    p += len;
    buf_size -= len;
    if(pes->data_index == pes->pes_header_size){
     const uint8_t *r;
     unsigned int flags, pes_ext, skip;

     flags = pes->header[7];
     r = pes->header + 9;
     pes->pts = AV_NOPTS_VALUE;
     pes->dts = AV_NOPTS_VALUE;
     if((flags & 0xc0) == 0x80){
      pes->dts = pes->pts = get_pts(r);
      r += 5;
     }else if((flags & 0xc0) == 0xc0){
      pes->pts = get_pts(r);
      r += 5;
      pes->dts = get_pts(r);
      r += 5;
     }
     pes->extended_stream_id = -1;
     if(flags & 0x01){ // PES extension
      pes_ext = *r++;
      // Skip PES private data, program packet sequence counter and P-STD buffer
      skip = (pes_ext >> 4) & 0xb;
      skip += skip & 0x9;
      r += skip;
      if((pes_ext & 0x41) == 0x01 && (r + 2) <= (pes->header + pes->pes_header_size)){
       // PES extension 2
       if((r[0] & 0x7f) > 0 && (r[1] & 0x80) == 0)
        pes->extended_stream_id = r[1];
      }
     }

     // we got the full header. We parse it and get the payload
     pes->state = MPEGTS_PAYLOAD;
     pes->data_index = 0;
    }
    break;
   case MPEGTS_PAYLOAD:
    if((buf_size>0) && pes->buffer){
     if(pes->data_index+buf_size > pes->total_size) {
      new_pes_packet(pes, ts->pkt);
      pes->total_size = MAX_PES_PAYLOAD;
      if(pes->bufsize<pes->total_size){
#ifdef __DOS__
       if(!ts->auto_guess && (intsoundcontrol&INTSOUND_DECODER)){
        pes->state = MPEGTS_SKIP;
        continue;
       }
#endif
       free(pes->buffer);
       pes->buffer = malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE);
       if(!pes->buffer){
        pes->bufsize=0;
        return AVERROR(ENOMEM);
       }
       pes->bufsize=pes->total_size;
      }
      ts->stop_parse = 1;
     }
     memcpy(pes->buffer+pes->data_index, p, buf_size);
     pes->data_index += buf_size;
    }
    buf_size = 0;
    if((pes->total_size<MAX_PES_PAYLOAD) && ((pes->pes_header_size+pes->data_index)==(pes->total_size+6))){
     new_pes_packet(pes, ts->pkt);
     ts->stop_parse = 1;
    }
    break;
   case MPEGTS_SKIP:
    buf_size = 0;
    break;
  }
 }

 return 0;
}

static struct PESContext *add_pes_stream(struct MpegTSContext *ts, int pid, int pcr_pid)
{
 struct MpegTSFilter *tss;
 struct PESContext *pes;

 pes = calloc(1,sizeof(struct PESContext));
 if(!pes)
  return 0;
 pes->ts = ts;
 pes->stream = ts->stream;
 pes->pid = pid;
 pes->pcr_pid = pcr_pid;
 pes->state = MPEGTS_SKIP;
 pes->pts = AV_NOPTS_VALUE;
 pes->dts = AV_NOPTS_VALUE;
 tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes);
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mpegts_open_pes_filter end: %8.8X",(int)tss);
 if(!tss){
  free(pes);
  return NULL;
 }
 return pes;
}

static void pmt_cb(struct MpegTSFilter *filter, const uint8_t *section, int section_len)
{
 struct MpegTSContext *ts = filter->u.section_filter.opaque;
 struct SectionHeader h1, *h = &h1;
 struct PESContext *pes;
 AVStream *st;
 const uint8_t *p, *p_end, *pi_end, *desc_list_end, *desc_end;
 int program_info_length, pcr_pid, pid, stream_type;
 int desc_list_len, desc_len, desc_tag;
 uint32_t prog_reg_desc = 0;
 //char language[4];

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "PMT: len %i", section_len);

 p_end = section + section_len - 4;
 p = section;
 if(parse_section_header(h, &p, p_end) < 0)
  return;

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "PMT len:%d tid:%4.4X sid=0x%x sec_num=%d/%d",section_len,h->tid,h->id, h->sec_num, h->last_sec_num);

 if(h->tid != PMT_TID)
  return;

 pcr_pid = get16(&p, p_end);
 if(pcr_pid < 0)
  return;
 pcr_pid&=0x1fff;

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "pcr_pid=0x%x", pcr_pid);

 program_info_length = get16(&p, p_end);
 if(program_info_length<0)
  return;
 program_info_length&=0xfff;
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "program_info_length: %d remain:%d", program_info_length,(long)(p_end-p));
 pi_end=p+program_info_length;
 while(program_info_length >= 2){
  int tag, len;
  tag = get8(&p, p_end);
  if(tag<0)
   break;
  len = get8(&p, p_end);
  if(len<0)
   break;
  //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"program tag:%2.2X len:%d desc:%4.4s",tag,len,(char *)p);
  if(len>(program_info_length-2))
   break;
  program_info_length -= len+2;
  if((tag==0x05) && (len>=4)){ // registration descriptor
   prog_reg_desc = PDS_GETB_LE32(p);
  }
  p += len;
 }
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "p-p_end: %d ", (long)(p_end-p));
 p=pi_end;
 if(p >= p_end)
  return;

 // stop parsing after pmt, we found header
 if(!ts->stream->nb_streams)
  ts->stop_parse = 1;

 for(;;){
  st = NULL;
  stream_type = get8(&p, p_end);
  //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "stream_type: %d leftb:%d", stream_type,(long)(p_end-p));
  if(stream_type<0)
   break;
  pid = get16(&p, p_end);
  if(pid<0)
   break;
  pid&=0x1fff;

  if(ts->pids[pid] && (ts->pids[pid]->type==MPEGTS_PES)){
   pes = ts->pids[pid]->u.pes_filter.opaque;
  }else{
   if(!ts->auto_guess)
    return;
   if(ts->pids[pid])
    mpegts_close_filter(ts, ts->pids[pid]); //wrongly added sdt filter probably
   pes = add_pes_stream(ts, pid, pcr_pid);
   if(!pes)
    return;
  }
  if(!pes->st){
   if(!ts->auto_guess)
    return;
   pes->st = av_new_stream(pes->stream, pes->pid);
  }
  st=pes->st;
  //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"st:%8.8X p:%d pst:%4.4X st:%8.8X pes:%8.8X left:%d",
  // stream_type,pid,pes->stream_type,(long)st,(long)pes,(long)(p_end-p));

  if(!st)
   return;

  if(!pes->stream_type)
   mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc);

  desc_list_len = get16(&p, p_end);
  if(desc_list_len<0)
   break;
  desc_list_len&=0xfff;
  desc_list_end = p + desc_list_len;
  if(desc_list_end>p_end)
   break;

  for(;;){
   desc_tag = get8(&p, desc_list_end);
   if(desc_tag<0)
    break;
   desc_len = get8(&p, desc_list_end);
   if(desc_len<0)
    break;
   desc_end = p + desc_len;
   if(desc_end>desc_list_end)
    break;

   //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "desc_tag: 0x%02x len:%d st:%4.4X",desc_tag, desc_len,stream_type);

   if((st->codec->codec_id==CODEC_ID_NONE) && (stream_type==STREAM_TYPE_PRIVATE_DATA))
    mpegts_find_stream_type(st, desc_tag, DESC_types);

   switch(desc_tag){
#if 0
    case 0x56: // DVB teletext descriptor
     language[0] = get8(&p, desc_end);
     language[1] = get8(&p, desc_end);
     language[2] = get8(&p, desc_end);
     language[3] = 0;
     //av_metadata_set2(&st->metadata, "language", language, 0);
     break;
    case 0x59: // subtitling descriptor
     language[0] = get8(&p, desc_end);
     language[1] = get8(&p, desc_end);
     language[2] = get8(&p, desc_end);
     language[3] = 0;
     get8(&p, desc_end);
     //if(!st->codec->extradata){
     // st->codec->extradata = malloc(4 + FF_INPUT_BUFFER_PADDING_SIZE);
     // if(st->codec->extradata) {
     //  st->codec->extradata_size = 4;
     //  memcpy(st->codec->extradata, p, 4);
     // }
     //}
     p += 4;
     //av_metadata_set2(&st->metadata, "language", language, 0);
     break;
    case 0x0a: // ISO 639 language descriptor
     language[0] = get8(&p, desc_end);
     language[1] = get8(&p, desc_end);
     language[2] = get8(&p, desc_end);
     language[3] = 0;
     //av_metadata_set2(&st->metadata, "language", language, 0);
     break;
#endif
    case 0x05: // registration descriptor
     st->codec->codec_tag = PDS_GETB_LE32(p);
     //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "reg_desc=%.4s st:%4.4X", (char*)&st->codec->codec_tag,stream_type);
     if((st->codec->codec_id==CODEC_ID_NONE) && (stream_type == STREAM_TYPE_PRIVATE_DATA))
      mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types);
     break;
   }

   p = desc_end;

   if((prog_reg_desc==AV_RL32("HDMV")) && (stream_type==0x83) && pes->sub_st){
    pes->sub_st->codec->codec_tag = st->codec->codec_tag;
   }
  }
  p = desc_list_end;
 }
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"pmt_cb end sl:%d dll:%d dt:%4.4X dl:%d cid:%8.8X pl:%d",
 // section_len,desc_list_len,desc_tag,desc_len,((pes && pes->st && pes->st->codec)? pes->st->codec->codec_id:-1),(long)(p-section));
}

static void pat_cb(struct MpegTSFilter *filter, const uint8_t *section, int section_len)
{
 struct MpegTSContext *ts = filter->u.section_filter.opaque;
 struct SectionHeader h1, *h = &h1;
 const uint8_t *p, *p_end;
 int sid, pmt_pid;

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "PAT: sect:%8.8X len:%d",(unsigned long)section,section_len);

 p_end = section + section_len - 4;
 p = section;
 if(parse_section_header(h, &p, p_end)<0)
  return;
 if(h->tid!=PAT_TID)
  return;

 for(;;){
  sid=get16(&p, p_end);
  if(sid<0)
   break;
  pmt_pid=get16(&p, p_end);
  if(pmt_pid<0)
   break;
  pmt_pid&=0x1fff;
  //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "sid=0x%x pid=0x%x", sid, pmt_pid);

  if(sid && ts->auto_guess){ // ! NIT info
   av_new_program(ts->stream, sid);
   if(!ts->pids[pmt_pid])
    mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
  }
 }
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"pat_cb end");
}

static int handle_packet(struct MpegTSContext *ts, const uint8_t *packet)
{
 struct MpegTSFilter *tss;
 int len, pid, cc, cc_ok, afc, is_start;
 const uint8_t *p, *p_end;

 pid = AV_RB16(packet + 1) & 0x1fff;
 is_start = packet[1] & 0x40;
 tss = ts->pids[pid];
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"handle packet p:%4.4X is:%d ag:%d tss:%8.8X pos:%"PRIi64"",
 //               pid,is_start,ts->auto_guess,(long)tss,(mpxp_int64_t)ts->packet_filepos);
 if(!tss)
  return 0;

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

 // skip adaptation field
 afc = (packet[3] >> 4) & 3;
 p = packet + 4;
 if(afc == 0) // reserved value
  return 0;
 if(afc == 2) // adaptation field only
  return 0;
 if(afc == 3) // skip adapation field
  p += p[0] + 1;

 p_end = packet + TS_PACKET_SIZE;
 if(p >= p_end)
  return 0;

 if(tss->type == MPEGTS_SECTION){
  if(is_start){ // pointer field present
   len = *p++;
   if((p + len) >= p_end)
    return 0;
   if(len && cc_ok){
    write_section_data(tss,p, len, 0);
    if(!ts->pids[pid])
     return 0;
   }
   p += len;
   if(p < p_end)
    write_section_data(tss,p, p_end - p, 1);
  }else{
   if(cc_ok)
    write_section_data(tss,p, p_end - p, 0);
  }
 }else{
  int ret;
  if((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start,ts->packet_filepos)) < 0)
   return ret;
 }

 return 0;
}

static int read_packet(struct MpegTSContext *ts,char *buf,unsigned int sync_only)
{
 AVFormatContext *s = ts->stream;
 mpxp_filesize_t filepos=s->fbfs->ftell(s->fbds);
 int retry=((s->flags&ADFMT_FLAG_PARSESTATUS) && !(s->flags&ADFMT_FLAG_AUTODETECT))? (MAX_RESYNC_SIZE*5):MAX_RESYNC_SIZE;
 int retcode=AVERROR(EIO);

 if(s->fbfs->fread(s->fbds,buf,4)!=4)
  goto err_out_read;

 do{
  if(!PDS_GETB_LE32(&buf[0])){
   if(s->fbfs->fread(s->fbds,buf,4)!=4)
    goto err_out_read;
   filepos+=4;
  }else{
   if((buf[0]==0x47)){ // && !(buf[1]&0x80) && (buf[3]&0x30)){
    ts->packet_filepos=filepos;
    if(sync_only)
     return 0;
    if(s->fbfs->fread(s->fbds,(buf+4),(TS_PACKET_SIZE-4))!=(TS_PACKET_SIZE-4))
     goto err_out_read;
    s->fbfs->fseek(s->fbds,ts->raw_packet_size-TS_PACKET_SIZE,SEEK_CUR);
    return 0;
   }
   PDS_PUTB_LE32(&buf[0],PDS_GETB_LE32(&buf[1]));
   if(s->fbfs->fread(s->fbds, &buf[3], 1)!=1)
    goto err_out_read;
   filepos++;
  }
 }while(--retry);
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"retry:%d",retry);
 return AVERROR(EINVAL);

err_out_read:
 if(!s->fbfs->eof(s->fbds))
  s->fbfs->fseek(s->fbds,filepos,SEEK_SET);
 return retcode;
}

static int analyze_packetsize(struct MpegTSContext *ts)
{
 struct AVFormatContext *s = ts->stream;
 int i,retcode,retry=2;
 const int packet_sizes[3]={TS_PACKET_SIZE,TS_DVHS_PACKET_SIZE,TS_FEC_PACKET_SIZE};// 188,192,204
 uint8_t buf[6*TS_MAX_PACKET_SIZE];

 do{
  retcode=read_packet(ts,buf,1);
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ps:%lld ret:%d try:%d",(int64_t)ts->packet_filepos,retcode,retry);
  if(retcode!=0)
   return retcode;
  if(s->fbfs->fread(s->fbds,&buf[4],5*TS_MAX_PACKET_SIZE-4)!=(5*TS_MAX_PACKET_SIZE-4))
   return AVERROR(EIO);
  for(i=0;i<3;i++){
   uint8_t *bp=&buf[0];
   int p,ps=packet_sizes[i];
   for(p=0;p<5;p++){
    if((bp[0]!=0x47))// || (bp[1]&0x80) || !(bp[3]&0x30))
     break;
    bp+=ps;
   }
   if(p==5){
    ts->raw_packet_size=ps;
    s->fbfs->fseek(s->fbds,ts->packet_filepos,SEEK_SET);
    return 0;
   }
  }
 }while(--retry);
 return AVERROR(EINVAL);
}

static int handle_packets(struct MpegTSContext *ts, int nb_packets)
{
 uint8_t packet[TS_MAX_PACKET_SIZE*2]; // !!! mem leak bug somewhere
 int packet_num=0, ret=0;

 ts->stop_parse = 0;
 do{
  packet_num++;
  if(nb_packets && (packet_num>=nb_packets)){
   ret=AVERROR(EINVAL);
   break;
  }
  ret=read_packet(ts, packet, 0);
  if(ret!=0)
   break;
  ret=handle_packet(ts, packet);
  //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"handle packet %d ret:%d sp:%d",packet_num,ret,ts->stop_parse);
  if(ret!=0)
   break;
 }while(!ts->stop_parse);
 return ret;
}

static int mpegts_read_header(AVFormatContext *s,AVPacket *pkt)
{
 struct MpegTSContext *ts;
 int retcode=AVERROR(EINVAL);
 mpxp_filesize_t pos;

 if(!s || !s->priv_data)
  goto fail;

 ts=s->priv_data;
 ts->stream=s;

 if((retcode=analyze_packetsize(ts))!=0)
  goto fail;

 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"raw_packet_size: %d",ts->raw_packet_size);

 pos=ts->packet_filepos;

 ts->pkt=pkt;
 ts->auto_guess=1;

 mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);

 handle_packets(ts, 2500);

 ts->auto_guess=0;

 s->fbfs->fseek(s->fbds, pos, SEEK_SET);
 return 0;

fail:
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mpegts_read_header failed");
 return retcode;
}

static int mpegts_read_packet(AVFormatContext *s,AVPacket *pkt)
{
 struct MpegTSContext *ts = s->priv_data;
 int ret, i;

 if(s->flags&ADFMT_FLAG_PARSESTATUS)
  ts->auto_guess=1;
 else
  ts->auto_guess=0;

 if(s->fbfs->ftell(s->fbds)!=ts->last_pos){
  // seek detected, clear pes buffer
  for(i=0; i<NB_PID_MAX; i++){
   struct MpegTSFilter *tss=ts->pids[i];
   if(tss){
    tss->last_cc=-1;
    if(tss->type==MPEGTS_PES){
     struct PESContext *pes=tss->u.pes_filter.opaque;
     pes->data_index=0;
     pes->state=MPEGTS_SKIP;
    }
   }
  }
 }

 ts->pkt=pkt;
 ret = handle_packets(ts, 2500);
 if((ret<0) && s->fbfs->eof(s->fbds)){
  // flush pes data left
  for(i=0; i<NB_PID_MAX; i++) {
   if(ts->pids[i] && (ts->pids[i]->type==MPEGTS_PES)){
    struct PESContext *pes=ts->pids[i]->u.pes_filter.opaque;
    if((pes->state==MPEGTS_PAYLOAD) && (pes->data_index>0)){
     new_pes_packet(pes, pkt);
     pes->state=MPEGTS_SKIP;
     ret=0;
     break;
    }
   }
  }
 }

 ts->last_pos = s->fbfs->ftell(s->fbds);

 return ret;
}

static int mpegts_read_close(AVFormatContext *s)
{
 struct MpegTSContext *ts = s->priv_data;
 int i;

 if(!ts)
  return 0;

 for(i=0;i<NB_PID_MAX;i++)
  if(ts->pids[i])
   mpegts_close_filter(ts, ts->pids[i]);

 return 0;
}

AVInputFormat ff_mpegts_demuxer = {
    sizeof(struct MpegTSContext),
    mpegts_read_header,
    mpegts_read_packet,
    mpegts_read_close,
    NULL
};

#endif //MPXPLAY_LINK_INFILE_FFMPG
