/*
   File: mpeg2demux.cc

   Description:
   The MPEG 2 demultiplexer class reads MPEG 2 TS, PS and PES data 
   and demultiplexes a Transport Stream or Program Stream in its 
   Packetized Elementary Streams. Elementary Streams are just passed 
   through.

   Created: February 1996, Alex Theo de Jong, NIST
*/

#include "athread.hh"

#include <stdio.h>
#include <stdlib.h>
#include <String.h>
#include <fstream.h>
#include <sys/errno.h>
#ifdef IRIX
#include <dmedia/audio.h>
#endif
#ifdef SOLARIS
#include <sys/audioio.h>
#endif

// network stuff 
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <errno.h>

#include "error.hh"
#include "debug.hh"
#include "util.hh"
#include "sync.hh"
#include "mpeg2const.hh"
#include "mpeg2buff.hh"
#include "mpeg2audio.hh"
#include "mpeg2video.hh"
#include "mpeg2demux.hh"
#include "network.hh"

// Options for Audio and Video player

extern int audio_argc;
extern char** audio_argv;
extern int video_argc;
extern char** video_argv;

extern int time_stamp_qsize;
extern int frame_stamp_qsize;
extern int audio_buffer_size;
extern int video_buffer_size;

// Input socket

extern SocketMulti* sock;

#ifdef TRACE
Mpeg2Buffer* ab=0;
Mpeg2Buffer* vb=0;
Synchronization* ds=0;
#endif

#define DELIMITER   0x001B8


/*
 *
 * De-multiplexor
 *
 */

Mpeg2Demux::Mpeg2Demux(int pdu_size, int sel_vstream, int sel_astream, int a_on, int v_on, int s_on, int q) :
  pes_audio_bytes(0),
  pes_video_bytes(0),
  pes_audio_time(0.0),
  pes_video_time(0.0),
  audio_on(a_on),
  video_on(v_on),
  sync_on(s_on),
  quiet(q),
  vstream(sel_vstream),
  astream(sel_astream)
{
  audio_buffer=new Mpeg2Buffer(audio_buffer_size);
  video_buffer=new Mpeg2Buffer(video_buffer_size);
#ifdef TRACE
  ::ab=audio_buffer;
  ::vb=video_buffer;
#endif

  aalpdu_size=pdu_size;
  aalpdu=new unsigned char[aalpdu_size+1];
  aalpdu_max=aalpdu+aalpdu_size;

  // input counters
  bytecount=0;
  byteptr=0;
  pdu_mpeg_packet=aalpdu_max; // set to max for proper init
  // MPEG packet counters 
  counter=0;
  transport_packets=0;
  transport_packet_errors=0;
  sync_byte_errors=0;
  lost_packets=0;
  adaptation_fields=0;
  program_association_tables=0;
  pes_packets=0;
  psi_packets=0;
  audio_packets=0;
  video_packets=0;
  // transport packet header information
  transport_error_indicator=0;
  payload_unit_start_indicator=0;
  pid=0;
  audio_pid=-1;
  video_pid=-1;
  adaptation_field_control=0;

  audio=0;
  video=0;
  sync=0;

  int err;
#ifdef EOFEXIT
  Mpeg2Demux::init(this);
#else
  if ((err=athr_create((void*(*)(void*))Mpeg2Demux::init, this, &thread_id))<0){
    error("could not create thread");
  }
  sched_param param;
  int policy;
  if ((err=athr_getschedparam(thread_id, &policy, &param))<0){
    warning("could not get thread prio - ignored");
  }
  else {
#ifdef LINUX
    param.sched_priority+=1;
//    policy = SCHED_RR;
    TRACER("TIMERPRIORITY=" << param.sched_priority << "(" << param.sched_priority-1 << ")");
#else
    param.prio+=1;
    TRACER("TIMERPRIORITY=" << param.prio << "(" << param.prio-1 << ")");
#endif
    if ((err=athr_setschedparam(thread_id, policy, &param))<0){
      warning("could not set thread prio - ignored");
    }
  }

//  TRACER("DEMUXPRIORITY=" << param.prio);
#endif
  terminated=0;
}


Mpeg2Demux::~Mpeg2Demux(){
  TRACER("Mpeg2Demux::~Mpeg2Demux()");
  if (!terminated){ // check if thread is still alive
    TRACER("waiting for demux thread to terminate ...");
    athr_join(thread_id);
  }

  TRACER("delete video ...");
  delete video;
  TRACER("video deleted\ndelete audio ...");
  delete audio;
  TRACER("audio deleted\ndelete sync ...");
  delete sync;
  TRACER("sync deleted\ndemux deleted");
  delete aalpdu;
  delete audio_buffer;
  delete video_buffer;
}


int Mpeg2Demux::stop(){
  terminate=1;
  if (!terminated){    // check to see if thread is available
    athr_join(thread_id);
    gettimeofday(&tstop,(struct timezone *)NULL); 
    int runtime = 1000*(tstop.tv_sec-tstart.tv_sec) + (tstop.tv_usec-tstart.tv_usec)/1000;
    msg("Bitrate: "); 
    msg(dtoa(counter*MPEG2_TS_Packet_size*8/runtime));
    message(" Kbps");
  }
  return terminated; 
}


void* Mpeg2Demux::init(Mpeg2Demux* base){
  TRACER("void* Mpeg2Demux::init(Mpeg2Demux* base)");
  base->terminate=0;
  base->terminated=0;

  if (base->sync_on){
    if (base->video_on && base->audio_on)
      base->sync=new Synchronization(0, time_stamp_qsize, frame_stamp_qsize); // sync video/audio
    if (base->video_on && !base->audio_on)
      base->sync=new Synchronization(1, time_stamp_qsize, frame_stamp_qsize); // just video
    if (!base->video_on && base->audio_on)
      base->sync=new Synchronization(2, time_stamp_qsize, frame_stamp_qsize); // just audio
  }
  else base->sync=0;

  if (base->video_on)
    base->video=new Mpeg2Video(base->video_buffer, base->sync, video_argc, video_argv);
  else base->video=0;

  if (base->audio_on)
    base->audio=new Mpeg2Audio(base->audio_buffer, base->sync, base->audio_on, audio_argc, audio_argv);
  else base->audio=0;

  // wait for network connection or file to be ready!
  sock->accept();
  if (0){
    error("could not receive connection!");
    athr_exit(0);
  }

  if (base->nextpacket()<=0){  // init first pdu
    error("could not read first packet");
    athr_exit(0);
  }
  unsigned int bits=base->nextbits32();

  gettimeofday(&base->tstart,(struct timezone *)NULL); 

  if (((bits >> 24) & 0xff)==Sync_byte){
    message("Playing MPEG 2 TS Audio/Video");
#ifdef TRACE
    ::ds=base->sync;
#endif      
    base->transport_stream();
  }
  else if (bits == Pack_start_code){
    // Create players and start
    message("Playing MPEG 2 PS Audio/Video");
    base->program_stream();
  }
  else if (base->audio_on && (bits & 0xfff00000)==0xfff00000){  // just plain audio
    message("Playing MPEG 2 PES Audio");
    base->pes_stream_audio();
  }
  else if (base->video_on && bits==Sequence_start_code){ // just video
    message("Playing MPEG 2 PES Video");
    base->pes_stream_video();
  }
  else error("Stream is not valid MPEG 2 Stream (TS, PS, Audio ES, Video ES)");
  base->stop();
  base->terminated=1;
  TRACER("demux thread done");

#ifdef EOFEXIT
  exit(0);  // stop everything right here (after EOF!)
#else
  athr_exit(0);
#endif
  return 0;
}


int Mpeg2Demux::copybytes(Mpeg2Buffer* output, int length){
  if (bytecount+length>MPEG2_TS_Packet_size){
    warning("copying bytes beyond transport packet length");
    length=MPEG2_TS_Packet_size-bytecount;
  }
  if (output->write(byteptr, length)!=length){
    error("failed to copy input to output");    // write to output
  }
  bytecount+=length;
  byteptr+=length;
  return length;
}


int Mpeg2Demux::skipbytes(int length){
  int i = length;
  while(i--)
    getbits8();
  return length;

  if (bytecount+length>MPEG2_TS_Packet_size){
    warning("skipping bytes beyond transport packet length");
    length=MPEG2_TS_Packet_size - bytecount;
  }
  bytecount+=length;
  byteptr+=length;
  return length;
}

int Mpeg2Demux::copy_ps_bytes(Mpeg2Buffer* output, unsigned int *header,int len){
  unsigned char c;
//    if (video_on == 2) {
      if (output->write(byteptr, len)!=len){
        error("failed to copy input to output");    // write to output
      }
      if (!nextpacket()) {
        *header = 0;
        return 1;
      }
      *header = getbits32();
#if 0
    }
    else {
      *header = getbits32();
      while ((*header <= DELIMITER) || (*header > 0x1ff)) {
        c = *header >> 24;
        *header = (*header << 8) | getbits8();
        if (output->write(&c, 1)!=1){
          error("failed to copy input to output");    // write to output
        }
      }
    }
#endif
  return 0;
}


int Mpeg2Demux::skip_ps_bytes(unsigned int *header){
//  if (video_on==2) {
    if (!nextpacket()) {
      *header = 0;
      return 1;
    }
    *header = getbits32();
    return 0;
#if 0
  }
  else {
    *header = getbits32();
    while ((*header <= DELIMITER) || (*header >  0x1ff)){
      *header = (*header << 8) |  getbits8();
    }
  }
#endif
  return 0;
}



int Mpeg2Demux::nextpacket(){  

  DEBUGGER("int Mpeg2Demux::nextpacket()");

  if (pdu_mpeg_packet>=aalpdu_max){
    DEBUGGER("sock->recv()");
    if ((lastpdu_size=sock->recv(aalpdu, aalpdu_size))!=aalpdu_size){
      if (lastpdu_size==0){
       
//	if (audio_buffer)
//          while (audio_buffer->used() > 10000 )  sleep(1);
//	if (video_buffer) 
//          while (video_buffer->used() > 30000 ) sleep(1);	
	sleep(1);
        terminate=1;
        if (sync) sync->stop();
        if (audio) audio->stop();
        if (video) video->stop();
	sleep(1);
        if (sync) sync->stop();
        TRACER("EOF!");
        return 0; // eof
      }
      error("invalid pdu size (" << itoa(lastpdu_size) << ")");
      return -1;
    }
    byteptr=pdu_mpeg_packet=aalpdu;
    pdu_mpeg_packet+=MPEG2_TS_Packet_size;
    bytecount=0;
  }
  else {
    byteptr=pdu_mpeg_packet;
    pdu_mpeg_packet+=MPEG2_TS_Packet_size;
    bytecount=0;
  }
  return 1;
}

/*
 *
 * Mpeg Transport Stream
 *
 */

int Mpeg2Demux::transport_stream(){
  do {
    counter++;

    if (!get_transport_packet()){
      if (!quiet){
        String err("incorrect packet ");
        err+=itoa(counter);
        error(err.chars());
      }
    }

    if (!quiet){
      if ((counter % 100)==0) msg(".");
      if ((counter % 5000)==0){
        message(itoa(counter));
        athr_yield();
      }
    }

    if (terminate){
      if ((!sync || sync->stop()) && (!audio || audio->stop()) && (!video || video->stop()))
        break;  // continue until all threads terminated
    }
  }
  while (nextpacket());

  TRACER("closing audio buffer ...");
  audio_buffer->close();
  TRACER("closed audio buffer\nclosing video buffer ...");
  video_buffer->close();
  TRACER("closed video buffer");
  file.close();

  return (counter==0) ? 0 : 1;
}


int Mpeg2Demux::get_transport_packet(){  
  if (get_sync_byte()){              // align with sync
    transport_packets++;

    unsigned int bits=(getbits24() & 0x0000ffff);  // drop sync byte
    // printf("First 2 bytes: %d\n", bits);
    // exit(0);
    transport_error_indicator=bits >> 15;
    payload_unit_start_indicator=(bits >> 14) & 1;
    pid=bits & 0x00001fff;
    transport_scrambling_control=(nextbits8() >> 6)&0x3;
    adaptation_field_control=(nextbits8() >> 4)&0x3;
    continuity_counter=(getbits8()&0xf);

    if (transport_error_indicator){
      transport_packet_errors++;
      return 0;  // error set!
    }

    if (pid==0x1fff){
      return 1;  // padding; just go to next
    }
    int i;
    for (i=0; pidtable[i]!=0 && pid!=pidtable[i]; i++); // get pid
    if (pidtable[i]==0){  // not in table yet
      pidtable[i]=pid;
      continuity_counters[i]=continuity_counter;  // init
      pidtable[i+1]=0;
    }
    
    if (pid!=(int)Program_Association_Table && pid!=(int)Conditional_Access_Table
        && (adaptation_field_control==1 || adaptation_field_control==3)){
      // Check counters
      if (continuity_counters[i]!=continuity_counter){
        if (!quiet){
          String err("lost MPEG ");
          err+=itoa(continuity_counter-continuity_counters[i]);
          err+=" packet(s)";
          error(err.chars());
        }
        continuity_counters[i]=continuity_counter; // reset
        lost_packets++;
      }
      if (++continuity_counters[i]>15) continuity_counters[i]=0;
    }

    if (adaptation_field_control==2 || adaptation_field_control==3)
      get_adaptation_field();
    if (adaptation_field_control==1 || adaptation_field_control==3)
      get_payload();

  }
  else {
    sync_byte_errors++;
    return 0;
  }
  return 1;
}


int Mpeg2Demux::get_adaptation_field(){
  adaptation_fields++;
  int length=getbits8();                        // get adaptation field length
// int discontinuity_indicator=(nextbits8() >> 7);
// int random_access_indicator=(nextbits8() >> 6) & 1;
// int elem_prio_indicator=(nextbits8() >> 5) & 1; // not used at this point;
  int pcr_flag=(getbits8() >> 4) & 1;           // get first byte

  if (pcr_flag){
    unsigned long clk_ref_base=getbits32();
    unsigned int clk_ref_ext=getbits16();
    if (clk_ref_base>0x7fffffff){   // correct for invalid numbers
      clk_ref_base=0;               // ie. longer than 32 bits when multiplied by 2
      clk_ref_ext=0;                // multiplied by 2 corresponds to shift left 1 (<<=1)
    }
    else {
      clk_ref_base<<=1; // Create space for bit
      clk_ref_base|=(clk_ref_ext >> 15);          // Take bit
      clk_ref_ext&=0x01ff;                        // Only lower 9 bits
    }
    
    double time=clk_ref_base + clk_ref_ext/300;
    TRACER("Time: " << dtoa(time));
    if (sync) sync->put(time);  // id=0, Timer

    if (length) skipbytes(length - 7);
  }
  else skipbytes(length - 1);

  return 1;
}


int Mpeg2Demux::get_payload(){
  if (payload_unit_start_indicator){
    if (pid==0) get_program_association_table();
    else if (nextbits24()==Packet_start_code_prefix) get_pes_packet();
    else skipbytes(MPEG2_TS_Packet_size - bytecount);  // get_psi_packet();
  }
  else {
    if (pid==audio_pid) get_audio_data();
    else if (pid==video_pid) get_video_data();
    else skipbytes(MPEG2_TS_Packet_size - bytecount);
  }

  return 1;
}

int Mpeg2Demux::get_program_association_table(){
  program_association_tables++;

  table_id=getbits8();
  section_length=getbits16() & 0xfff;   // last 12 bits
  transport_stream_id=getbits16();
  
  skipbytes(MPEG2_TS_Packet_size-bytecount);
/*  
  for (int i=0; i<section_length-9; i+=4) getbits32();
  i-=4;

  if ((i+9)>(MPEG2_TS_Packet_size-4)) error("too many bytes in program association table");
*/
  return 1;
}


int Mpeg2Demux::get_pes_packet_data(int stream_id){
  unsigned long pts(0), dts(0);

  if ((stream_id >> 4)==12 || (stream_id >> 4)==13){
    // Just pick the first available stream if no ID is set
    if (astream==-1)
      astream=(stream_id & 0x0f);

    if ((stream_id & 0x0f)==astream && audio_on){
      if (sync && pes_audio_bytes){
    TRACER("AudioTime: " << dtoa(pes_audio_time) << " " << itoa(pes_audio_bytes));
        sync->put(2, pes_audio_time, pes_audio_bytes); // id=2, Audio
        pes_audio_time=0;
        pes_audio_bytes=0;
      }
      get_pes_packet_header(pts, dts);
      pes_audio_time=pts;
      audio_pid=pid;
      return get_audio_data();
    }
  }
  else if ((stream_id >> 4)==14){
    // Just pick the first available stream if no ID is set
    if (vstream==-1)
      vstream=(stream_id & 0x0f);

    if ((stream_id & 0x0f)==vstream && video_on){
      if (sync && pes_video_bytes){
    TRACER("VideoTime: " << dtoa(pes_video_time) << " " << itoa(pes_video_bytes));
        sync->put(1, pes_video_time, pes_video_bytes); // id=1, Video
        pes_video_time=0;
        pes_video_bytes=0;
      }
      get_pes_packet_header(pts, dts);
      pes_video_time=pts;
      video_pid=pid;
      return get_video_data();
    }
  }
  else {
    return get_unknown_data();
  }
  skipbytes(MPEG2_TS_Packet_size - bytecount);

  return 1;
}

int Mpeg2Demux::get_pes_packet_header(unsigned long& pts, unsigned long& dts){
  unsigned int pes_header_bytes(0);

  getbits8();  // drop first 8 bits
  short PTS_DTS_flags=(getbits8() >> 6) & 0x3;
  int PES_header_data_length=getbits8();

  // Get Presentation Time stamps and Decoding Time Stamps
  if (PTS_DTS_flags==2){
    pts=(getbits8() >> 1) & 7;  // Only low 4 bits (7==1111)
    pts<<=15;
    pts|=(getbits16() >> 1);
    pts<<=15;
    pts|=(getbits16() >> 1);
    pes_header_bytes+=5;
  }
  else if (PTS_DTS_flags==3){      
    pts=(getbits8() >> 1) & 7;  // Only low 4 bits (7==1111)
    pts<<=15;
    pts|=(getbits16() >> 1);
    pts<<=15;
    pts|=(getbits16() >> 1);
    dts=(getbits8() >> 1) & 7;  // Only low 4 bits (7==1111)
    dts<<=15;
    dts|=(getbits16() >> 1);
    dts<<=15;
    dts|=(getbits16() >> 1);
    pes_header_bytes+=10;
  }
  // extract other stuff here!
  
  skipbytes(PES_header_data_length - pes_header_bytes);
  return 1;
}

int Mpeg2Demux::get_pes_packet(){
  pes_packets++;

  getbits24();     // skip startcode
  unsigned int stream_id=getbits8();

//  int pes_packet_length=   // Not used at this point; just drop
  getbits16();

  if (stream_id!=Private_stream_2 && stream_id!=Padding_stream){
    return get_pes_packet_data(stream_id);
  }
  else if (stream_id==Private_stream_2){
    // Dump private data!
    error("private stream");
    skipbytes(MPEG2_TS_Packet_size - bytecount);
  }
  else if (stream_id==Padding_stream){
    skipbytes(MPEG2_TS_Packet_size - bytecount);
    // Nothing; the next search for a sync byte will just skip the stuffing bytes
  }
  else {
    error("unknown stream_id in pes packet");
    skipbytes(MPEG2_TS_Packet_size - bytecount);
  }

  return 1;
}


int Mpeg2Demux::get_psi_packet(){
  psi_packets++;
  table_id=getbits8();
  section_length=getbits16() & 0x0fff;

  skipbytes(MPEG2_TS_Packet_size - bytecount);
  return 1;
}


int Mpeg2Demux::get_audio_data(){
  audio_packets++;
  pes_audio_bytes+=copybytes(audio_buffer, MPEG2_TS_Packet_size - bytecount);
  return 1;
}

int Mpeg2Demux::get_video_data(){
  video_packets++;
  pes_video_bytes+=copybytes(video_buffer, MPEG2_TS_Packet_size - bytecount);
  return 1;
}

int Mpeg2Demux::get_unknown_data(){
  warning("unknown data in PSI or PES packet");
  return skipbytes(MPEG2_TS_Packet_size - bytecount);
}

/*
 *
 * MPEG Program Stream
 *
 */


int Mpeg2Demux::program_stream(){

  if (!get_program_pack()){
    error("failure in programm pack");
    return 0;
  }
  audio_buffer->close();
  video_buffer->close();
  return 1;
}

int Mpeg2Demux::get_program_pack(){
static unsigned int header;

  header = getbits32();

  while (1) {

    if (header == Pack_start_code) {
 	get_pack_header(&header);
    }
    else if ((header >> 8) == Packet_start_code_prefix) {
       counter++;
       get_ps_pes_packet(&header);
    }
    else { 
      return 0;
    }

    if (!quiet){
      if ((counter % 100)==0) msg(".");
      if ((counter % 5000)==0){
        message(itoa(counter));
      }
    }
    
    if (terminate){
//      if ((!sync || sync->stop()) && (!audio && audio->stop()) && (!video && video->stop()))
        break;
    }
  }

  return 1;
}


int Mpeg2Demux::get_ps_pes_packet(unsigned int *header){
  static unsigned long pts(0),dts(0);
  int PES_header_data_length;
  pes_packets++;
  int stream_id=*header & 0xff;
  unsigned int pes_packet_length=getbits16();
  if (stream_id!=(int)Private_stream_2 && stream_id!=(int)Padding_stream){

pts = 0;
    if ((nextbits8() & 0xc0) == 0x40) {
      pes_packet_length -= 12;
        skipbytes(12);
    } 
    else {
      int pes_header_bytes=0;
      int scrambling = (getbits8() >> 4 ) & 3;
//      if (scrambling) {
//        message("scrambled data");
//        skip_ps_bytes(header);            
//        return 1;
//      }
      int PTS_DTS_flags=(getbits8() >> 6) & 0x3;
      PES_header_data_length=getbits8();
  // Get Presentation Time stamps and Decoding Time Stamps
      if (PTS_DTS_flags==2){
        pts=(getbits8() >> 1) & 7;  // Only low 4 bits (7==1111)
        pts<<=15;
        pts|=(getbits16() >> 1);
        pts<<=15;
        pts|=(getbits16() >> 1);
        pes_header_bytes+=5;
      }
      else if (PTS_DTS_flags==3){
        pts=(getbits8() >> 1) & 7;  // Only low 4 bits (7==1111)
        pts<<=15;
        pts|=(getbits16() >> 1);
        pts<<=15;
        pts|=(getbits16() >> 1);
        dts=(getbits8() >> 1) & 7;  // Only low 4 bits (7==1111)
        dts<<=15;
        dts|=(getbits16() >> 1);
        dts<<=15;
        dts|=(getbits16() >> 1);
        pes_header_bytes+=10;
      }
     // extract other stuff here!


      pes_packet_length -=3;
      if (PES_header_data_length) {
        pes_packet_length -= PES_header_data_length; 
        skipbytes (PES_header_data_length - pes_header_bytes);
      }
    }

    if ((stream_id >> 4)==12 || (stream_id >> 4)==13){
      if ((stream_id & 0x0f)== astream && audio_on==1){
        if (sync && pes_audio_bytes && pts ){
          TRACER("AudioTime: " << dtoa(pes_audio_time));
          sync->put(2, pes_audio_time, pes_audio_bytes); // id=2, Audio
          pes_audio_time=0;
          pes_audio_bytes=0;
	  	
        }
	if (pts)
          pes_audio_time=pts;
        pes_audio_bytes+=pes_packet_length;
        copy_ps_bytes(audio_buffer, header,pes_packet_length);            
      }
      else {
        skip_ps_bytes(header);            
      }
    }
    else if ((stream_id >> 4)==14){
      if ((stream_id & 0x0f)== vstream && video_on){
        if (sync &&  pes_video_bytes && pts){
          TRACER("VideoTime: " << dtoa(pes_video_time) << " " << itoa(pes_video_bytes));
          sync->put(1, pes_video_time,pes_video_bytes); // id=1, Video
          pes_video_time=0;
          pes_video_bytes=0;
        }
        if (pts)
          pes_video_time=pts;
        pes_video_bytes += pes_packet_length;
        copy_ps_bytes(video_buffer, header,pes_packet_length);            
      }
      else {
        skip_ps_bytes(header);            
      }
    }
    else if (stream_id == 0xbd){
      if ( (*byteptr == (0x80+astream) ) && audio_on==2){  /* AC-3 Data */
        if (sync &&  pes_audio_bytes && pts){
          TRACER("AudioTime: " << dtoa(pes_audio_time) << " " << itoa(pes_audio_bytes));
          sync->put(2, pes_audio_time, pes_audio_bytes); // id=2, Audio
          pes_audio_time=0;
          pes_audio_bytes=0;
        }
        if (pts)
          pes_audio_time=pts;
        pes_audio_bytes+=pes_packet_length-4;
	skipbytes(4);
        copy_ps_bytes(audio_buffer, header,pes_packet_length-4);
      }
      else {
        skip_ps_bytes(header);
      }
    }
    else if (stream_id == 0xbc){
      skip_ps_bytes(header);            
    }
    else {
      skip_ps_bytes(header);            
    }
  }
  else if (stream_id==(int)Private_stream_2){
    skip_ps_bytes(header);            
  }
  else if (stream_id == (int)Padding_stream){
    skip_ps_bytes(header);            
  }
  else error("unknown stream_id in pes packet");

  return 1;
}


int Mpeg2Demux::get_pack_header(unsigned int *header){
static double old_time = 0.0;
unsigned long i,j;
unsigned long clock_ref,clock_ref_ext;
  i = getbits32();
  j = getbits16();
  if (i & 0x40000000) {
    clock_ref =  ((i & 0x31000000) << 3);
    clock_ref |= ((i & 0x03fff800) << 4);
    clock_ref |= ((i & 0x000003ff) << 5);
    clock_ref |= ((j & 0xf800) >> 11);
    clock_ref_ext = (j >> 1) & 0x1ff;

    double time=clock_ref + clock_ref_ext/300;
    TRACER("Time: " << dtoa(time));
    if (sync && (old_time + 6000 < time)) {
       sync->put(time);  // id=0, Timer
       old_time = time;
    }
   
    skipbytes(3);
    i = getbits8() & 0x7;
    while (i--) 
      getbits8();		// stuffing 
  }
  else
    skipbytes(2);
  *header = getbits32();
  
  if (*header == System_start_code) {
    get_system_header();
    *header = getbits32();
  }
  return 1;
}

int Mpeg2Demux::get_system_header(){
int i;
  i = getbits16();		// get length
  while (i--)
    getbits8();
  return 1;
}


/*
 *  Packetized Elementary Stream (PES)
 */


int Mpeg2Demux::pes_stream_audio(){
  do {
    counter++;

    get_audio_data();

    if (!quiet){
      if ((counter % 100)==0) msg(".");
      if ((counter % 5000)==0){
        message(itoa(counter));
      }
    } 
  }
  while (nextpacket());

  audio_buffer->close();

  return 1;
}
 
int Mpeg2Demux::pes_stream_video(){
  do {
    counter++;

    get_video_data();

    if (!quiet){
      if ((counter % 100)==0) msg(".");
      if ((counter % 5000)==0){
        message(itoa(counter));
      }
    }
  }
  while (nextpacket());

  video_buffer->close();

  return 1;
}
