/*
   File: mpeg2demux.hh

   By: Alex Theo de Jong
   Created: February 1996

   Description:
   MPEG 2 Transport Stream Demultiplexer. MPEG 2 Audio/Video players
   are invoked automatically when detected in the stream. Arguments
   are used to specify whether audio or video should be suppressed. 
*/

#ifndef __mpeg2demux_hh
#define __mpeg2demux_hh

extern unsigned int MPEG2_TS_Packet_size;

const int Pid_Max           = 20;        // Maximum number of PIDs in one stream

class Mpeg2Audio;
class Mpeg2Video;

/*
 *
 * MPEG 2 Transport Stream De-Multiplexer
 *
 */

class Mpeg2Demux {
  // input stream
  String filename;  // filename of file to be analyzed
  ifstream file;
  int bitrate;
  unsigned int file_size;  // maximum size of file to be analyzed (in bytes)
  int eof;
  athr_t thread_id; // demux thread id
  int terminate;    // action
  int terminated;   // indication

  struct timeval tstart, tstop;  // for runtime calculation

  // 2 MPEG Transport Packets (conform ATM Forum (N*MPEG TS / AAL)
  unsigned char* aalpdu, *aalpdu_max;
  int aalpdu_size, lastpdu_size;
  unsigned char* byteptr;
  unsigned char* pdu_mpeg_packet;  // offset within pdu for current Mpeg packet
  unsigned int bytecount;

  // for next/getbits operations
  unsigned char c;

  // output streams
  Mpeg2Buffer* audio_buffer;
  Mpeg2Buffer* video_buffer;
  // Synchronization for audio and video based on PCR
  Synchronization* sync;
  // counters for each pes packet
  int pes_audio_bytes;
  int pes_video_bytes;
  double pes_audio_time;
  double pes_video_time;
  // Pid
  int audio_pid, video_pid;
  int audio_on, video_on; // booleans to turn audio/video on/off
  int sync_on;
  // Audio and video objects
  Mpeg2Audio* audio;
  Mpeg2Video* video;

  int quiet;  // ie. no output messages
  int vstream;  // e.g. 0 or 1  videostream
  int astream;  // audiostream

  // various counters
  unsigned int counter;
  int stream_type, transport_packets, transport_packet_errors, 
      sync_byte_errors, lost_packets, continuity_counters[Pid_Max],
      program_association_tables,
      adaptation_fields, pes_packets, psi_packets, 
      audio_packets, video_packets;      

  // Transport Header Info
  int transport_error_indicator, payload_unit_start_indicator, 
      transport_priority, pid, adaptation_field_control,
      transport_scrambling_control, continuity_counter,
      pidtable[Pid_Max];

  // program associated table
  int table_id, transport_stream_id, section_length;
  
 protected:
  int copybytes(Mpeg2Buffer* out, int length);
  int skipbytes(int length);
  int nextpacket();
  int copy_ps_bytes(Mpeg2Buffer* out, unsigned int *header,int len);
  int skip_ps_bytes(unsigned int *header);
  unsigned int nextbits8(){
    if (bytecount+1>MPEG2_TS_Packet_size) { nextpacket(); }
    return byteptr[0];
  }
  unsigned int nextbits16(){
    if ((bytecount+2)>MPEG2_TS_Packet_size) warning("nextbits16 beyond packet length");
    return ((byteptr[0] << 8) | byteptr[1]); 
  }
  unsigned int nextbits24(){
    if ((bytecount+3)>MPEG2_TS_Packet_size) warning("nextbits24 beyond packet length");
    return ((byteptr[0] << 16) | (byteptr[1] << 8) | byteptr[2]);  
  }
  unsigned int nextbits32(){
    if ((bytecount+4)>MPEG2_TS_Packet_size) warning("nextbits32 beyond packet length");
    return ((byteptr[0] << 24) | (byteptr[1] << 16) | (byteptr[2] << 8) | byteptr[3]); 
  }
  unsigned int getbits8(){ c=nextbits8(); byteptr++; bytecount++; return c; }
#if defined(IRIX) && !defined(__GNUG__)   
  // Only with Native SGI C++ Compiler
  unsigned int value;
  unsigned int getbits16(){ return ((value=getbits8()<<8) |= getbits8()); }
  unsigned int getbits24(){ 
    return (((value=getbits8()<<16) |= getbits8()<<8) |= getbits8());
  }
  unsigned int getbits32(){ 
    return ((((value=getbits8()<<24) |= getbits8()<<16) |= getbits8()<<8) |= getbits8());
  }
#else
  unsigned int getbits16(){ return ((getbits8() << 8) | getbits8()); }
  unsigned int getbits24(){
    return ((getbits8() << 16) | (getbits8() << 8) | getbits8()); 
  }
  unsigned int getbits32(){
    return ((getbits8() << 24) | (getbits8() << 16) | (getbits8() << 8) | getbits8()); 
  }
#endif

  // MPEG 2 Transport Stream (TS)
  int transport_stream();
  int get_transport_packet();
  int get_sync_byte(){ return (nextbits8()==Sync_byte) ? 1 : 0; }
  int get_adaptation_field();
  int get_payload();
  int get_program_association_table();
  int get_pes_packet();
  int get_pes_packet_data(int stream_id);
  int get_pes_packet_header(unsigned long& pts, unsigned long& dts);
  int get_psi_packet();
  int get_audio_data();
  int get_video_data();
  int get_unknown_data();

  // MPEG 2 Program Stream (PS)
  int program_stream();  // with or without "pack" layer
  int get_program_pack();
  int get_ps_pes_packet(unsigned int *header);
  int get_pack_header(unsigned int *header);
  int get_system_header();

  // MPEG2 Packetized Elementary Streams (PES)
  int pes_stream_video();
  int pes_stream_audio();

  static void* init(Mpeg2Demux*);
 public:
  Mpeg2Demux(int pdu_size, int vstream=0, int astream = 0, int a_on=1, int v_on=1, int s_on=0, int q=0);
  ~Mpeg2Demux();
  int select(unsigned int stream_id){ return vstream=stream_id; }
  int pause(){ return (sync) ? sync->pause() : athr_suspend(thread_id); }
  int resume(){ return (sync) ? sync->resume() : athr_continue(thread_id); }
  int stop();
  int done(){ return terminated; }
};

#endif
