/*
   File: mpeg2player.cc

   Description:
   MPEG 2 Transport Stream Player invokes a Demultiplexer object
   which has its own thread of execution. The main program just
   waits until the demultiplexer has finished.

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

#define RELEASE "0.6  October 1999"

#include "athread.hh"

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

#include "error.hh"
#include "debug.hh"
#include "util.hh"
#include "network.hh"

#include "sync.hh"
#include "mpeg2const.hh"
#include "mpeg2buff.hh"
#include "mpeg2audio.hh"
#include "mpeg2video.hh"
#include "mpeg2demux.hh"

// socket to read from by demultiplexor

SocketMulti* sock=0;

// Globals for arguments of audio/video
const int arg_max=20;
int audio_argc=0;
char** audio_argv=0;
int video_argc=0;
char** video_argv=0;

// Default parameters

unsigned int asap     = 4000;
int time_stamp_qsize  = 10000;
int frame_stamp_qsize = 1000;
unsigned int MPEG2_TS_Packet_size = 188;
int aal_pdu_size      = 2*MPEG2_TS_Packet_size;
unsigned int vc       = 60; // Virtual Circuit (VP Vitual Path = 0)
int vstream            = 0; // -1=Select first ID in stream, 0, 1, ... = stream ID
int astream            = 0; // -1=Select first ID in stream, 0, 1, ... = stream ID
int audio             = 2;  // 0==Off, 1=MPEG, 2=AC3 
int video             = 1;  // 0= OFF, 1=MPEG-1 2=DVD Mode
int synchro_on        = 1;
int quiet_on          = 0;
int frame_rate        = 0;
int audio_buffer_size = 102400; // The length of these buffers should not matter
int video_buffer_size = 1024000; // for sync purposes  they only need to be big enough
/*
   Assuming 6 Mbps, 0.7 sec (700 msec) between PCR fields which needs to 
   be buffered. 

   6000000 x 0.7 = 4200000 Bits

   Assume 20 % audio, 80 % video (NB. no overhead is counted):
   audio buffer = 0.2 x 4200000 =  840000 bits = 105000 Bytes ~ 102400 Bytes
   video buffer = 0.8 x 4200000 = 3360000 bits = 420000 Bytes ~ 512000 Bytes
*/



void usage(const char* name){
  msg("usage: "); msg(name); message(" <options>\n");
  message("options:");
  message("\t-f <s>\t\tFilename <s>");
  message("\t-nt <n>\t\tNetwork ASAP <n>");
#ifdef FORE_ATM
  message("\t-vc n\t\tVitual Circuit (VC), Vitual Path (VP) is 0");
#endif
  message("\t-m <n>\t\tMapping of <n> MPEG packets per sdu");
  message("\t-vstream <n>\tSelect video stream <n> for playing");
  message("\t-astream <n>\tSelect audio stream <n> for playing");
  message("\t-mpeg\t\tSelect mpeg audio  for playing -- default is AC3");
  message("\t-q\t\tQuiet");
  message("\t-fq <n>\t\tQueue size for frame time stamps");
  message("\t-tq <n>\t\tQueue size for time stamps");
  message("\t-ab <n>\t\tAudio buffer size (in KBytes)");
  message("\t-vb <n>\t\tVideo buffer size (in KBytes)");
  message("\t-vob\t\tDVD playing mode");
  message("\t-na\t\tNo audio");
  message("\t-u\t\tuh=Headphones, us=Internal Speaker, ul=Line out");
  message("\t-l\t\tOnly decode left channel audio");
  message("\t-r\t\tOnly decode right channel audio");
  message("\t-nv\t\tNo video");
  message("\t-fr n\t\tNumber of frames per second (will result in B-frame skipping)")
//  message("\t-c n\t\tChunk size of buffer reading for video");
//  NOTE: be careful when using sychronization
  message("\t-ns <n>\t\tNo synchronization of audio and video");
  message("");
  message("\tVOB File Player Version 0.6\n");
  message("\tbased on");
  message("\tMPEG 2 Transport Stream Player");
  msg("\tVersion 1.2, November 1996"); 
  message("\tAlex Theo de Jong (e-mail: alex.dejong@nist.gov)");
  message("\tMulti-Media and Digital Video Group");
  message("\tNational Institute of Standards and Technology");
  message("\tGaithersburg, Md, U.S.A.");
  message("Original Audio Player code by:");
  message("\tTobias Bading");
  message("\tBerlin University of Technology, Germany");
  message("Original Video Player code by:");
  message("\tMPEG Software Simulation Group    &");
  message("\tStefan Eckart");
  message("\tFraunhofer-Institut fuer Festkoerpertechnologie, Germany");
}



int main(int argc, char** argv){
  String filename;
  Mpeg2Demux* mpeg2demux=0;

  // Create space for `arg_max' arguments for audio and video
  video_argv=new char*[arg_max];
  video_argv[video_argc++]=strdup("mpeg2player");     // parent
  video_argv[video_argc++]=strdup("buffer");          // "filename"
  String displayname("-d");
  displayname+="MPEG 2 Player, ";
  displayname+=RELEASE;
  video_argv[video_argc++]=strdup(displayname.chars());  // Display title
  audio_argv=new char*[arg_max];
  audio_argv[audio_argc++]=strdup("mpeg2player");     // parent
  audio_argv[audio_argc++]=strdup("buffer");          // "filename"

  // Check/parse arguments
  if (argc<3){
    usage(argv[0]);
    exit(0);
  }
  else {
    for (int i=1; i<argc; i++){
      if (strcmp(argv[i], "-f") == 0 && (i+1)<argc) filename=argv[++i];
// General options
      else if (strcmp(argv[i], "-vstream")==0 && (i+1)<argc) vstream=atoi(argv[++i]);
      else if (strcmp(argv[i], "-astream")==0 && (i+1)<argc) astream=atoi(argv[++i]);
      else if (strcmp(argv[i], "-nt")==0 && (i+1)<argc) asap=(unsigned int) atoi(argv[++i]);
#ifdef FORE_ATM
      else if (strcmp(argv[i], "-vc")==0 && (i+1)<argc) vc=atoi(argv[++i]);
#endif
      else if (strcmp(argv[i], "-m")==0 && (i+1)<argc)
        aal_pdu_size=atoi(argv[++i])*MPEG2_TS_Packet_size;
      else if (strcmp(argv[i], "-vob")==0) { MPEG2_TS_Packet_size = 0x800; aal_pdu_size = 0x20000;video=2;}
      else if (strcmp(argv[i], "-na")==0) audio=0;
      else if (strcmp(argv[i], "-ab")==0 && (i+1)<argc) audio_buffer_size=(atoi(argv[++i])*1024);
      else if (strcmp(argv[i], "-nv")==0) video=0;
      else if (strcmp(argv[i], "-vb")==0&& (i+1)<argc) video_buffer_size=(atoi(argv[++i])*1024);
      else if (strcmp(argv[i], "-ns")==0) synchro_on=0;
      else if (strcmp(argv[i], "-fq")==0 && (i+1)<argc) frame_stamp_qsize=atoi(argv[++i]);
      else if (strcmp(argv[i], "-tq")==0 && (i+1)<argc) time_stamp_qsize=atoi(argv[++i]);
      else if (strcmp(argv[i], "-q")==0) quiet_on=1;
      else if (strcmp(argv[i], "-mpeg")==0) audio=1;
// Video Options
      else if (strcmp(argv[i], "-fr")==0 && (i+1)<argc){
        char* arg=new char[5];
        sprintf(arg, "-f%s", argv[++i]);
        frame_rate=atoi(argv[i]);
        video_argv[video_argc++]=arg;
      }
      else if (strcmp(argv[i], "-c")==0 && (i+1)<argc){
        char* arg=new char[5];
        sprintf(arg, "-c%s", argv[++i]);
        video_argv[video_argc++]=arg;
      }
// Audio Options
      else if (strcmp(argv[i], "-uh")==0) audio_argv[audio_argc++]=strdup(argv[i]);
      else if (strcmp(argv[i], "-ul")==0) audio_argv[audio_argc++]=strdup(argv[i]);
      else if (strcmp(argv[i], "-us")==0) audio_argv[audio_argc++]=strdup(argv[i]);
      else if (strcmp(argv[i], "-l")==0) audio_argv[audio_argc++]=strdup(argv[i]);
      else if (strcmp(argv[i], "-r")==0) audio_argv[audio_argc++]=strdup(argv[i]);
      else {
        msg("unknown argument `"); msg(argv[i]); message("' - ignored");
      }
    }
  }
  if (quiet_on){
    audio_argv[audio_argc++]=strdup("-q");
    video_argv[video_argc++]=strdup("-q");
  }

  // show options
  message("---------------------------------------------");
  msg("Verbose\t\t\t\t\t"); if (!quiet_on){ message("On"); } else { message("Off"); }
  msg("File\t\t\t\t\t"); 
  if (filename.length()){ 
    message(filename.chars()); 
  }
  else {
    msg("asap "); message(itoa(asap)); 
#ifdef FORE_ATM
    msg("VPI/VCI\t\t\t\t\t0/"); message(itoa(vc)); 
#endif
  }
  msg("SDU size\t\t\t\t"); message(itoa(aal_pdu_size));
  msg("Video Stream ID\t\t\t\t"); message(itoa(vstream));
  msg("Audio Stream ID\t\t\t\t"); message(itoa(astream));
  msg("Frame rate\t\t\t\t"); if (frame_rate){ message(itoa(frame_rate)); } else { message("-"); }
  msg("Audio\t\t\t\t\t"); 
  if (audio){
    if (audio==2) {
      message("AC3");
    } else {
      message("MPEG");
    }
    msg("Audio buffer\t\t\t\t"); message(itoa(audio_buffer_size));
    msg("Audio Time Stamp buffer\t\t\t"); message(itoa(frame_stamp_qsize));
  }
  else { message("Off"); }
  msg("Video\t\t\t\t\t");
  if (video){
    message("On");
    msg("Video buffer\t\t\t\t"); message(itoa(video_buffer_size));
    msg("Video Time Stamp buffer\t\t\t"); message(itoa(frame_stamp_qsize));
  }
  else { message("Off"); }
  msg("Sync audio/video\t\t\t");
  if (synchro_on){
    message("On");
    msg("Time Stamp buffer\t\t\t"); message(itoa(time_stamp_qsize));
  }
  else { message("Off"); }
  message("---------------------------------------------");

  video_argv[video_argc]=0;  // termintate last arg with a 0
  audio_argv[audio_argc]=0;  // termintate last arg with a 0

  // Actual MPEG player

  message("Start MPEG 2 Player\n");
  
  if (filename.length()){
    // create a file socket
    sock=new SocketMulti(filename.chars());

    mpeg2demux=new Mpeg2Demux(aal_pdu_size, vstream, astream, audio, video, synchro_on, quiet_on);
    
    // the demultiplexer starts the players too!
    
#if (defined(SOLARIS) || defined(IRIX) ) // only with NATIVE Solaris Threads and Irix
    char* c=new char[50];
    do {
      msg("Type 'q' to quit: ");
      athr_yield();
      cin >> c;
      switch(c[0]){
      case '0' :
      case '1' :
      case '2' : mpeg2demux->select(atoi(&c[0])); break;
      case 'p' :
      case 'P' : mpeg2demux->pause();  break;
      case 'r' :
      case 'R' : mpeg2demux->resume(); break;
      case 'q' :
      case 'Q' : {
        mpeg2demux->resume();
        mpeg2demux->stop();
      } break;
      default  : break;
      }
    }
    while (c[0]!='q' && c[0]!='Q');
    delete c;
#endif // without pthreads
  }
  else {
    // a multi socket wait for 4 ports: tcpip, tcpipatm, atmspans, and atmpvc
    sock=new SocketMulti(asap, aal_pdu_size, vc); 
    
    mpeg2demux=new Mpeg2Demux(aal_pdu_size, vstream, astream, audio, video, synchro_on, quiet_on);
  }

  // wait for demux to finish before deleting stuff!!!
  while (!mpeg2demux->done()){
    sleep(2);
//    athr_yield();
  }

  // finish up; 
  delete mpeg2demux;
  int j;
  for (j=0; j<audio_argc; j++) 
    delete audio_argv[j];
  for (j=0; j<video_argc; j++) 
    delete video_argv[j];
  delete audio_argv;
  delete video_argv;

  if (sock){
    sock->close();
    delete sock;
  }

  message("\nMPEG 2 Player Finished\n");
  exit(0);
}
