/*
 * MPEG1/2 demuxer
 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
 *
 * This file is part of Mpxplay/FFmpeg demuxers
 *
 * 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"
#include "ffutils.h"
#include <string.h>

#define PACK_START_CODE             ((unsigned int)0x000001ba)
#define SYSTEM_HEADER_START_CODE    ((unsigned int)0x000001bb)
#define SEQUENCE_END_CODE           ((unsigned int)0x000001b7)
#define PACKET_START_CODE_MASK      ((unsigned int)0xffffff00)
#define PACKET_START_CODE_PREFIX    ((unsigned int)0x00000100)
#define ISO_11172_END_CODE          ((unsigned int)0x000001b9)

#define PROGRAM_STREAM_MAP 0x1bc
#define PRIVATE_STREAM_1   0x1bd
#define PADDING_STREAM     0x1be
#define PRIVATE_STREAM_2   0x1bf

#define AUDIO_ID 0xc0
#define VIDEO_ID 0xe0
#define AC3_ID   0x80
#define DTS_ID   0x8a
#define LPCM_ID  0xa0
#define SUB_ID   0x20

#define STREAM_TYPE_VIDEO_MPEG1     0x01
#define STREAM_TYPE_VIDEO_MPEG2     0x02
#define STREAM_TYPE_AUDIO_MPEG1     0x03
#define STREAM_TYPE_AUDIO_MPEG2     0x04
#define STREAM_TYPE_PRIVATE_SECTION 0x05
#define STREAM_TYPE_PRIVATE_DATA    0x06
#define STREAM_TYPE_AUDIO_AAC       0x0f
#define STREAM_TYPE_VIDEO_MPEG4     0x10
#define STREAM_TYPE_VIDEO_H264      0x1b

#define STREAM_TYPE_AUDIO_AC3       0x81
#define STREAM_TYPE_AUDIO_DTS       0x8a

#define MAX_SYNC_SIZE 128000
#define FFMPG_LASTERROR_SEEK 1

typedef struct MpegDemuxContext {
 int sofdec;
 //uint32_t riffcdxa;
 uint32_t lasterror;
 uint32_t sync_bytes_skipped;
 unsigned char psm_es_type[256];
}MpegDemuxContext;

static const int lpcm_freq_tab[4] = { 48000, 96000, 44100, 32000 };
//static const uint8_t cdxa_sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00};

static long ffmpg_get_byte(AVFormatContext *s)
{
 uint8_t rbuf[4];
 if(s->fbfs->fread(s->fbds,&rbuf[0],1)!=1)
  return AVERROR(EIO);
 return (uint32_t)(rbuf[0]);
}

static long ffmpg_get_be16(AVFormatContext *s)
{
 uint8_t rbuf[4];
 if(s->fbfs->fread(s->fbds,&rbuf[0],2)!=2)
  return AVERROR(EIO);
 return (uint32_t)(PDS_GETB_BE16(rbuf));
}

static inline int64_t ff_parse_pes_pts(const uint8_t *buf)
{
 return (((int64_t)(buf[0]&0x0e)) << 29) | ((AV_RB16(buf+1) >> 1) << 15) | (AV_RB16(buf+3) >> 1);
}

static int64_t get_pts(AVFormatContext *s, int c)
{
 uint8_t buf[8];
#ifdef MPXPLAY_USE_DEBUGF
 //static int counter;
#endif

 if(c<0)
  c=ffmpg_get_byte(s);
 if(c<0)
  return c;
 buf[0] = c;
 if(s->fbfs->fread(s->fbds, buf+1, 4)!=4)
  return AVERROR(EIO);

 /*mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"%5.5d pts:%"PRIi64" buf:%2.2X%2.2X%2.2X%2.2X%2.2X",
  counter++,ff_parse_pes_pts(buf),
  (unsigned long)buf[0],(unsigned long)buf[1],(unsigned long)buf[2],(unsigned long)buf[3],
  (unsigned long)buf[4]);*/

 return ff_parse_pes_pts(buf);
}

static int32_t find_next_start_code(AVFormatContext *s, MpegDemuxContext *m)
{
 int blocksize,i,retcode=0;
 uint8_t readbuf[16];

 /*if(m->riffcdxa)
  i=12;
 else*/
  i=4;
 if(s->fbfs->fread(s->fbds,&readbuf[0],i)!=i)
  return AVERROR(EIO);

 if(s->flags&ADFMT_FLAG_AUTODETECT)
  blocksize=MAX_SYNC_SIZE/10;
 else if(s->flags&ADFMT_FLAG_PARSESTATUS)
  blocksize=MAX_SYNC_SIZE;
 else
  blocksize=MAX_SYNC_SIZE*10;

 i=blocksize;
 do{
  uint32_t val;
  /*if(m->riffcdxa){
   if(memcmp(readbuf,cdxa_sync_header,12)!=0){
    memcpy(&readbuf[0],&readbuf[1],11);
    if(s->fbfs->fread(s->fbds, &readbuf[11], 1)!=1)
     return AVERROR(EIO);
    continue;
   }

  }*/
  val=PDS_GETB_BE32(&readbuf[0]);
  if(!val){
   if(s->fbfs->fread(s->fbds, &readbuf[2], 2)!=2)
    return AVERROR(EIO);
  }else{
   if((val&PACKET_START_CODE_MASK)==PACKET_START_CODE_PREFIX){
    retcode=val;
    break;
   }
   PDS_PUTB_LE32(&readbuf[0],PDS_GETB_LE32(&readbuf[1]));
   if(s->fbfs->fread(s->fbds, &readbuf[3], 1)!=1)
    return AVERROR(EIO);
  }
 }while(--i);
 m->sync_bytes_skipped=blocksize-i;
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"nsc bs:%d i:%d r:%d",blocksize,i,retcode);
 if(!i && PDS_GETB_LE32(&readbuf[0])) // ??? checks the last block only
  return AVERROR(EINVAL);
 return retcode;
}

/**
 * Extract stream types from a program stream map
 * According to ISO/IEC 13818-1 ('MPEG-2 Systems') table 2-35
 *
 * @return number of bytes occupied by PSM in the bitstream
 */
static long mpegps_psm_parse(AVFormatContext *s,MpegDemuxContext *m)
{
 int psm_length, ps_info_length, es_map_length;

 if((psm_length=ffmpg_get_be16(s))<0)
  return psm_length;

 if(s->fbfs->fseek(s->fbds,2,SEEK_CUR)<0)
  return AVERROR(ESPIPE);
 if((ps_info_length=ffmpg_get_be16(s))<0)
  return ps_info_length;

 // skip program_stream_info
 if(s->fbfs->fseek(s->fbds,ps_info_length,SEEK_CUR)<0)
  return AVERROR(ESPIPE);
 if((es_map_length=ffmpg_get_be16(s))<0)
  return es_map_length;

 // at least one es available?
 while(es_map_length >= 4){
  long type,es_id,es_info_length;
  type = ffmpg_get_byte(s);
  if(type<0)
   return type;
  es_id = ffmpg_get_byte(s);
  if(es_id<0)
   return es_id;
  es_info_length = ffmpg_get_be16(s);
  if(es_info_length<0)
   return es_info_length;
  // remember mapping from stream id to stream type
  m->psm_es_type[es_id] = type;
  // skip program_stream_info
  if(s->fbfs->fseek(s->fbds,es_info_length,SEEK_CUR)<0)
   return AVERROR(ESPIPE);
  es_map_length -= 4 + es_info_length;
 }
 if(s->fbfs->fseek(s->fbds,4,SEEK_CUR)<0) // crc32
  return AVERROR(ESPIPE);
 return (2+psm_length);
}

static int mpegps_read_pes_header(AVFormatContext *s,MpegDemuxContext *m,
                                  int64_t *ppos, int *pstart_code,
                                  int64_t *ppts, int64_t *pdts)
{
 int len, startcode, c, flags, header_len;
 int pes_ext, ext2_len, id_ext, skip, rd_retry=(s->flags&ADFMT_FLAG_AUTODETECT)? 200:500;
 int64_t pts, dts;
 int64_t begin_sync,nsync;

redo:
 if((--rd_retry)<=0)
  return AVERROR(EINVAL);
 begin_sync=s->fbfs->ftell(s->fbds);
 startcode = find_next_start_code(s,m);
 if(startcode==AVERROR(EINVAL))
  return startcode;
 nsync = s->fbfs->ftell(s->fbds)-4;
 if(begin_sync<nsync)
  begin_sync=nsync;
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"startcode=%x pos=0x%"PRIx64" %"PRIu64" c:%d",startcode,begin_sync,begin_sync,rd_retry);
 if(startcode<0)
  goto err_out_read;
 m->lasterror=0;
 if(!startcode){
  if(s->flags&ADFMT_FLAG_PARSESTATUS) // !!!
   return AVERROR(EIO);
  else
   return AVERROR(EINVAL);
 }
 if(startcode == PACK_START_CODE)
  goto redo;
 if(startcode == SYSTEM_HEADER_START_CODE)
  goto redo;
 if(startcode == PADDING_STREAM) {
  long skipbytes=ffmpg_get_be16(s);
  if(skipbytes<0)
   goto err_out_read;
  if(s->fbfs->fseek(s->fbds, skipbytes,SEEK_CUR)<0)
   goto err_out_seek;
  goto redo;
 }
 if(startcode == PRIVATE_STREAM_2) {
  len = ffmpg_get_be16(s);
  if(len<0)
   goto err_out_read;
  if(!m->sofdec) {
   while(len-- >= 6) {
    int b=ffmpg_get_byte(s);
    if(b<0)
     goto err_out_read;
    if(b == 'S') {
     uint8_t buf[5];
     if(s->fbfs->fread(s->fbds, buf, sizeof(buf))!=sizeof(buf))
      goto err_out_read;
     m->sofdec = !memcmp(buf, "ofdec", 5);
     len -= sizeof(buf);
     break;
    }
   }
   m->sofdec -= !m->sofdec;
  }
  if(s->fbfs->fseek(s->fbds, len, SEEK_CUR)<0)
   goto err_out_seek;
  goto redo;
 }

 if(startcode == PROGRAM_STREAM_MAP){
  if(mpegps_psm_parse(s,m)<0)
   goto err_out_read;
  goto redo;
 }

 // find matching stream
 if(!((startcode >= 0x1c0 && startcode <= 0x1df) ||
      (startcode >= 0x1e0 && startcode <= 0x1ef) ||
      (startcode == 0x1bd) || (startcode == 0x1fd)))
  goto redo;

 *ppos = begin_sync;

 len = ffmpg_get_be16(s);
 if(len<0)
  goto err_out_read;
 pts = dts = AV_NOPTS_VALUE;

 // stuffing
 for(;;) {
  if(len < 1)
   goto err_out_data;
  c = ffmpg_get_byte(s);
  if(c<0)
   goto err_out_read;
  len--;
  // XXX: for mpeg1, should test only bit 7
  if(c != 0xff)
   break;
 }

 if((c&0xc0) == 0x40){
  // buffer scale & size
  if(ffmpg_get_byte(s)<0)
   goto err_out_read;
  c=ffmpg_get_byte(s);
  if(c<0)
   goto err_out_read;
  len-=2;
 }
 if((c & 0xe0) == 0x20){
  dts = pts = get_pts(s, c);
  if(dts<0)
   goto err_out_read;
  len -= 4;
  if(c & 0x10){
   dts = get_pts(s, -1);
   if(dts<0)
    goto err_out_read;
   len -= 5;
  }
 }else if ((c & 0xc0) == 0x80) {
  // mpeg 2 PES
#if 0 // some streams have this field set for no apparent reason
  if((c & 0x30) != 0) {
   // Encrypted multiplex not handled
   goto redo;
  }
#endif
  flags = ffmpg_get_byte(s);
  if(flags<0)
   goto err_out_read;
  header_len = ffmpg_get_byte(s);
  if(header_len<0)
   goto err_out_read;
  len -= 2;
  if(header_len > len)
   goto err_out_data;
  len -= header_len;
  if(flags & 0x80) {
   dts = pts = get_pts(s, -1);
   if(dts<0)
    goto err_out_read;
   header_len -= 5;
   if(flags & 0x40){
    dts = get_pts(s, -1);
    if(dts<0)
     goto err_out_read;
    header_len -= 5;
   }
  }
  if((flags&0x3f) && (header_len==0)){
   flags &= 0xC0;
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "Further flags set but no bytes left");
   goto err_out_data;
  }
  if(flags & 0x01) { // PES extension
   pes_ext = ffmpg_get_byte(s);
   if(pes_ext<0)
    goto err_out_read;
   header_len--;
   // Skip PES private data, program packet sequence counter and P-STD buffer
   skip = (pes_ext >> 4) & 0xb;
   skip += skip & 0x9;
   if((pes_ext&0x40) || (skip>header_len)){
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "pes_ext %X is invalid", pes_ext);
    goto err_out_data;
   }

   if(s->fbfs->fseek(s->fbds, skip, SEEK_CUR)<0)
    goto err_out_seek;

   header_len -= skip;

   if(pes_ext & 0x01) { // PES extension 2
    ext2_len = ffmpg_get_byte(s);
    if(ext2_len<0)
     goto err_out_read;
    header_len--;
    if((ext2_len & 0x7f) > 0) {
     id_ext = ffmpg_get_byte(s);
     if(id_ext<0)
      goto err_out_read;
     if((id_ext & 0x80) == 0)
      startcode = ((startcode & 0xff) << 8) | id_ext;
     header_len--;
    }
   }
  }

  if(header_len<0)
   goto err_out_data;
  if(s->fbfs->fseek(s->fbds, header_len, SEEK_CUR)<0)
   goto err_out_seek;

 }else if(c!=0xf)
  goto redo;

 if(startcode == PRIVATE_STREAM_1 && !m->psm_es_type[startcode & 0xff]) {
  startcode = ffmpg_get_byte(s);
  if(startcode<0)
   goto err_out_read;
  len--;
  if(startcode >= 0x80 && startcode <= 0xcf){
   // audio: skip header
   if(s->fbfs->fseek(s->fbds, 3, SEEK_CUR)<0)
    goto err_out_read;
   len -= 3;
   if(startcode >= 0xb0 && startcode <= 0xbf) {
    // MLP/TrueHD audio has a 4-byte header
    if(ffmpg_get_byte(s)<0)
     goto err_out_read;
    len--;
   }
  }
 }

 if(len<0)
  goto err_out_data;

 *pstart_code = startcode;
 *ppts = pts;
 *pdts = dts;
 return len;

err_out_read:
 if(!s->fbfs->eof(s->fbds) && (m->lasterror!=FFMPG_LASTERROR_SEEK) && !(s->flags&ADFMT_FLAG_PARSESTATUS))
  if(s->fbfs->fseek(s->fbds, begin_sync, SEEK_SET)<0)
   m->lasterror=FFMPG_LASTERROR_SEEK;
 return AVERROR(EIO);
err_out_seek:
 if(!s->fbfs->eof(s->fbds) && !(s->flags&ADFMT_FLAG_PARSESTATUS)){
  if(m->lasterror!=FFMPG_LASTERROR_SEEK)
   if(s->fbfs->fseek(s->fbds, begin_sync, SEEK_SET)<0)
    m->lasterror=FFMPG_LASTERROR_SEEK;
  return AVERROR(ESPIPE);
 } // else data-error
err_out_data:
 if(s->fbfs->fseek(s->fbds, (begin_sync+1), SEEK_SET)<0){
  m->lasterror=FFMPG_LASTERROR_SEEK;
  return AVERROR(ESPIPE);
 }
 return AVERROR(EINVAL);
}

static int mpegps_read_packet(AVFormatContext *s,
                              AVPacket *pkt)
{
 MpegDemuxContext *m = s->priv_data;
 AVStream *st;
 int len, startcode, i, es_type, dvdaudio_substream_type;
 enum CodecID codec_id = CODEC_ID_NONE;
 enum AVMediaType type;
 int64_t pts, dts, dummy_pos;
 mpxp_filesize_t lastgoodfilepos=s->fbfs->ftell(s->fbds);

 pkt->size=0;
 pkt->pts=AV_NOPTS_VALUE;
 pkt->dts=AV_NOPTS_VALUE;
 pkt->stream_index=100;
 m->sync_bytes_skipped=0;

 len = mpegps_read_pes_header(s,m, &dummy_pos, &startcode, &pts, &dts);
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"len:%d dp:%"PRIu64" sc:%x pts:%"PRIi64" dts:%"PRIi64"",len,dummy_pos,startcode,pts,dts);
 if(len<0){
  //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"len<0 fp:%d !!!!",(long)s->fbfs->ftell(s->fbds));
  return len;
 }
 if((m->sync_bytes_skipped>16) && (m->sync_bytes_skipped>(len>>1))){
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"sync skip: %d len:%d fp:%d",m->sync_bytes_skipped,len,(long)s->fbfs->ftell(s->fbds));
  goto skip;
 }

 if(startcode == 0x1bd) {
  dvdaudio_substream_type = ffmpg_get_byte(s);
  if(dvdaudio_substream_type<0)
   goto err_out_read;
  if(s->fbfs->fseek(s->fbds, 3, SEEK_CUR)<0)
   goto err_out_read;
  len -= 4;
 }

 // now find stream
 for(i=0;i<s->nb_streams;i++){
  st = s->streams[i];
  if(st->id == startcode)
   goto found;
 }

 es_type = m->psm_es_type[startcode & 0xff];
 if(es_type > 0 && es_type != STREAM_TYPE_PRIVATE_DATA){
  if(es_type == STREAM_TYPE_VIDEO_MPEG1){
   codec_id = CODEC_ID_MPEG2VIDEO;
   type = AVMEDIA_TYPE_VIDEO;
  }else if(es_type == STREAM_TYPE_VIDEO_MPEG2){
   codec_id = CODEC_ID_MPEG2VIDEO;
   type = AVMEDIA_TYPE_VIDEO;
  }else if(es_type == STREAM_TYPE_AUDIO_MPEG1 || es_type == STREAM_TYPE_AUDIO_MPEG2){
   codec_id = CODEC_ID_MP3;
   type = AVMEDIA_TYPE_AUDIO;
  }else if(es_type == STREAM_TYPE_AUDIO_AAC){
   codec_id = CODEC_ID_AAC;
   type = AVMEDIA_TYPE_AUDIO;
  }else if(es_type == STREAM_TYPE_VIDEO_MPEG4){
   codec_id = CODEC_ID_MPEG4;
   type = AVMEDIA_TYPE_VIDEO;
  }else if(es_type == STREAM_TYPE_VIDEO_H264){
   codec_id = CODEC_ID_H264;
   type = AVMEDIA_TYPE_VIDEO;
  }else if(es_type == STREAM_TYPE_AUDIO_AC3){
   codec_id = CODEC_ID_AC3;
   type = AVMEDIA_TYPE_AUDIO;
  }else{
   goto skip;
  }
 }else if (startcode >= 0x1e0 && startcode <= 0x1ef) {
  static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 };
  unsigned char buf[8];
  if(s->fbfs->fread(s->fbds, buf, 8)!=8)
   goto err_out_read;
  if(s->fbfs->fseek(s->fbds, -8, SEEK_CUR)<0)
   goto err_out_seek;
  if(!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1))
   codec_id = CODEC_ID_CAVS;
  else
   codec_id = CODEC_ID_PROBE;
  type = AVMEDIA_TYPE_VIDEO;
 }else if (startcode >= 0x1c0 && startcode <= 0x1df) {
  type = AVMEDIA_TYPE_AUDIO;
  codec_id = m->sofdec > 0 ? CODEC_ID_ADPCM_ADX : CODEC_ID_MP2;
 }else if (startcode >= 0x80 && startcode <= 0x87) {
  type = AVMEDIA_TYPE_AUDIO;
  codec_id = CODEC_ID_AC3;
 }else if((startcode >= 0x88 && startcode <= 0x8f) || ( startcode >= 0x98 && startcode <= 0x9f)){
  // 0x90 - 0x97 is reserved for SDDS in DVD specs
  type = AVMEDIA_TYPE_AUDIO;
  codec_id = CODEC_ID_DTS;
 }else if (startcode >= 0xa0 && startcode <= 0xaf){
  type = AVMEDIA_TYPE_AUDIO;
  // 16 bit form will be handled as CODEC_ID_PCM_S16BE
  codec_id = CODEC_ID_PCM_DVD;
 }else if (startcode >= 0xb0 && startcode <= 0xbf){
  type = AVMEDIA_TYPE_AUDIO;
  codec_id = CODEC_ID_TRUEHD;
 }else if (startcode >= 0xc0 && startcode <= 0xcf){
  // Used for both AC-3 and E-AC-3 in EVOB files
  type = AVMEDIA_TYPE_AUDIO;
  codec_id = CODEC_ID_AC3;
 }else if (startcode >= 0x20 && startcode <= 0x3f){
  type = AVMEDIA_TYPE_SUBTITLE;
  codec_id = CODEC_ID_DVD_SUBTITLE;
 }else if (startcode >= 0xfd55 && startcode <= 0xfd5f){
  type = AVMEDIA_TYPE_VIDEO;
  codec_id = CODEC_ID_VC1;
 }else if (startcode == 0x1bd){
  // check dvd audio substream type
  type = AVMEDIA_TYPE_AUDIO;
  switch(dvdaudio_substream_type & 0xe0) {
   case 0xa0:codec_id = CODEC_ID_PCM_DVD;
             break;
   case 0x80:if((dvdaudio_substream_type & 0xf8) == 0x88)
              codec_id = CODEC_ID_DTS;
             else
              codec_id = CODEC_ID_AC3;
             break;
   default:mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "Unknown 0x1bd sub-stream");
           goto skip;
  }
 }else{
skip: // skip packet
  if(s->fbfs->fseek(s->fbds, len, SEEK_CUR)<0)
   goto err_out_seek;
  return AVERROR(EINVAL);
 }
 // no stream found: add a new stream
 if(!(s->flags&ADFMT_FLAG_PARSESTATUS)) // !!! we are in demuxing status already, do not add/alloc new stream
  goto skip;
 st = av_new_stream(s, startcode);
 if(!st)
  goto skip;
 st->codec->codec_type = type;
 st->codec->codec_id = codec_id;
 if(codec_id != CODEC_ID_PCM_S16BE)
  st->need_parsing = AVSTREAM_PARSE_FULL;
found:
 if(st->discard >= AVDISCARD_ALL)
  goto skip;
 if((startcode>=0xa0 && startcode<=0xaf) || (startcode==0x1bd && ((dvdaudio_substream_type&0xe0)==0xa0))){
  int b1, freq;
  // for LPCM, we just skip the header and consider it is raw audio data
  if(len <= 3)
   goto skip;
  ffmpg_get_byte(s); // emphasis (1), muse(1), reserved(1), frame number(5)
  b1 = ffmpg_get_byte(s); // quant (2), freq(2), reserved(1), channels(3)
  if(b1<0)
   goto err_out_read;
  if(ffmpg_get_byte(s)<0) // dynamic range control (0x80 = off)
   goto err_out_read;
  len -= 3;
  freq = (b1 >> 4) & 3;
  st->codec->sample_rate = lpcm_freq_tab[freq];
  st->codec->channels = 1 + (b1 & 7);
  st->codec->bits_per_coded_sample = 16 + ((b1 >> 6) & 3) * 4;
  st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * st->codec->bits_per_coded_sample;
  if(st->codec->bits_per_coded_sample == 16)
   st->codec->codec_id = CODEC_ID_PCM_S16BE;
  else if(st->codec->bits_per_coded_sample == 28)
   return AVERROR(EINVAL);
 }

 if(len>pkt->bufsize)
  return 0;

 if(s->fbfs->fread(s->fbds, pkt->data, len)!=len)
  goto err_out_read;

 pkt->size= len;
 pkt->pts = pts;
 pkt->dts = dts;
 pkt->pos = dummy_pos;
 pkt->stream_index = st->index;
 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "si:%d pts=%0.3f dts=%0.3f size=%d",
 //       pkt->stream_index, pkt->pts / 90000.0, pkt->dts / 90000.0, pkt->size);

 return 0;

err_out_read:
err_out_seek:
 if(!s->fbfs->eof(s->fbds) && (m->lasterror!=FFMPG_LASTERROR_SEEK) && !(s->flags&ADFMT_FLAG_PARSESTATUS))
  if(s->fbfs->fseek(s->fbds, lastgoodfilepos, SEEK_SET)<0)
   m->lasterror=FFMPG_LASTERROR_SEEK;
 return AVERROR(EIO);
}

static int mpegps_read_header(AVFormatContext *s,AVPacket *pkt)
{
 int j,asc=-1;
 int prev_stnum=0;
 int retry_frame=(s->flags&ADFMT_FLAG_AUTODETECT)? 200:500;
 int retry_empty=2,retry_bad=(s->flags&ADFMT_FLAG_AUTODETECT)? 1:2;
 int retry_stream=150;
 unsigned int stream_counters[MAX_STREAMS];
 memset(stream_counters,0,sizeof(stream_counters));

 do{
  int retcode=mpegps_read_packet(s,pkt);
#ifdef MPXPLAY_USE_DEBUGF
  if(pkt->pts>=0 || pkt->dts>=0)
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"%4d pts:%6"PRIi64" dts:%6"PRIi64" fp:%6"PRIi64" rc:%d",retry_frame,pkt->pts,pkt->dts,pkt->pos,retcode);
  else
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"%4d fp:%6d rc:%d",retry_frame,(long)s->fbfs->ftell(s->fbds),retcode);
#endif
  if(retcode==AVERROR(EIO)){ // !!! means empty data here
   if(s->fbfs->eof(s->fbds))
    break;
   if(!(--retry_empty))
    break;
  }else if(retcode<0){
   if(!(--retry_bad))
    break;
   av_streams_and_programs_free(s);
   prev_stnum=0;
   retry_stream=150;
  }else if(pkt->size && (pkt->stream_index<s->nb_streams)){
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"nbs:%d si:%d id:%d rs:%d",s->nb_streams,pkt->stream_index,s->streams[pkt->stream_index]->id,retry_stream);
   stream_counters[pkt->stream_index]++;
   if(prev_stnum<s->nb_streams){
    prev_stnum=s->nb_streams;
    asc=-1;
    for(j=0;j<s->nb_streams;j++){
     AVStream *st = s->streams[j];
     if(st->codec->codec_type==AVMEDIA_TYPE_AUDIO){
      asc++;
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"cid:%X sid:%d rs:%d",st->codec->codec_id,st->id,retry_stream);
     }
    }
    if((asc>=0) && (s->flags&ADFMT_FLAG_LOADHEADONLY))
     break;
   }else if(asc>=0){
    for(j=0;j<s->nb_streams;j++)
     if(stream_counters[j]<3) // ??? we have to read/find all streams 3 times to be sure that there are no more streams
      break;
    if(j==s->nb_streams)
     break;
    if(!(--retry_stream))
     break;
   }
   retry_empty=2;
   retry_bad=2;
  }
 }while(--retry_frame);
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"asc:%d",asc);
 return asc;
}

// for testing only
/*static int mpegps_read_header(AVFormatContext *s,AVPacket *pkt)
{
 int i;

 for(i=0;i<1000;i++){
  if(mpegps_read_packet(s,pkt)<0)
   break;
  if(pkt->pts>=0 || pkt->dts>=0)
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"%4d si:%d ci:%8.8X pts:%6"PRIi64" dts:%6"PRIi64" fp:%6"PRIi64"",i,pkt->stream_index,s->streams[pkt->stream_index]->codec->codec_id,pkt->pts,pkt->dts,pkt->pos);
 }
 return -1;
}*/

AVInputFormat ff_mpegps_demuxer={
 sizeof(MpegDemuxContext),
 mpegps_read_header,
 mpegps_read_packet,
 NULL,
 NULL,
};
