/**********
This library 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. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library 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 this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**********/
// Copyright (c) 1996-2007, Live Networks, Inc.  All rights reserved
// A test program that reads a MPEG-1 or 2 Program Stream file,
// splits it into Audio and Video Elementary Streams,
// and streams both using RTP
// main program

#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"

UsageEnvironment* env;
char const* inputFileName = "test.mpg";
char const* inputFileNamePcm = "test.wav";
WAVAudioFileSource* pcmSource;
ByteStreamFileSource* fileVideoSource;
FramedSource* audioSource;
FramedSource* videoSource;
RTPSink* audioSink;
RTPSink* videoSink;

MPEG1or2VideoStreamFramer* MpegSource;

char* mimeType;
unsigned char payloadFormatCode;
Boolean iFramesOnly = False;

void play(); // forward
void start(void *);

// To stream using "source-specific multicast" (SSM), uncomment the following:
//#define USE_SSM 1
#ifdef USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif

// To set up an internal RTSP server, uncomment the following:
#define IMPLEMENT_RTSP_SERVER 1
// (Note that this RTSP server works for multicast only)

// To stream *only* MPEG "I" frames (e.g., to reduce network bandwidth),
// change the following "False" to "True":

int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  env = BasicUsageEnvironment::createNew(*scheduler);

  pcmSource = WAVAudioFileSource::createNew(*env, inputFileNamePcm);
  if (pcmSource == NULL) {
    *env << "Unable to open file \"" << inputFileNamePcm
	 << "\" as a pcm audio file source: "
	 << env->getResultMsg() << "\n";
    exit(1);
  }

  // Get attributes of the audio source:
  unsigned char const bitsPerSample = pcmSource->bitsPerSample();
  if (bitsPerSample !=  16) {
    *env << "The input file contains " << bitsPerSample
	 << " bit-per-sample audio, which we don't handle\n";
    exit(1);
  }
  unsigned const samplingFrequency = pcmSource->samplingFrequency();
  unsigned char const numChannels = pcmSource->numChannels();
  unsigned bitsPerSecond = samplingFrequency*bitsPerSample*numChannels;
  *env << "Audio source parameters:\n\t" << samplingFrequency << " Hz, ";
  *env << bitsPerSample << " bits-per-sample, ";
  *env << numChannels << " channels => ";
  *env << bitsPerSecond << " bits-per-second\n";

  mimeType = "L16";
  if (samplingFrequency == 44100 && numChannels == 2) {
    payloadFormatCode = 10; // a static RTP payload type
  } else if (samplingFrequency == 44100 && numChannels == 1) {
    payloadFormatCode = 11; // a static RTP payload type
  } else {
    payloadFormatCode = 96; // a dynamic RTP payload type
  }
  *env << "Converting to network byte order for streaming\n";

  if ( samplingFrequency != 48000 || numChannels != 2 ) {
     *env << "BIG WARNING: The receiver client is hardcoded to PCM 48KHz 2 Channels\n";
     exit(1);
  }

  // Open the input file as a 'byte-stream file source':
  fileVideoSource = ByteStreamFileSource::createNew(*env, inputFileName);
  if (fileVideoSource == NULL) {
    *env << "Unable to open file mpeg video ES\"" << inputFileName
	 << "\" as a byte-stream file source\n";
    exit(1);
  }

  // Create 'groupsocks' for RTP and RTCP:
  char* destinationAddressStr
#ifdef USE_SSM
    = "232.255.42.42";
#else
    = "239.255.42.42";
  // Note: This is a multicast address.  If you wish to stream using
  // unicast instead, then replace this string with the unicast address
  // of the (single) destination.  (You may also need to make a similar
  // change to the receiver program.)
#endif
  const unsigned short rtpPortNumAudio = 6666;
  const unsigned short rtcpPortNumAudio = rtpPortNumAudio+1;
  const unsigned short rtpPortNumVideo = 8888;
  const unsigned short rtcpPortNumVideo = rtpPortNumVideo+1;
  const unsigned char ttl = 7; // low, in case routers don't admin scope
  const unsigned char rtpPayloadType = 96;

  struct in_addr destinationAddress;
  destinationAddress.s_addr = our_inet_addr(destinationAddressStr);
  const Port rtpPortAudio(rtpPortNumAudio);
  const Port rtcpPortAudio(rtcpPortNumAudio);
  const Port rtpPortVideo(rtpPortNumVideo);
  const Port rtcpPortVideo(rtcpPortNumVideo);

  Groupsock rtpGroupsockAudio(*env, destinationAddress, rtpPortAudio, ttl);
  Groupsock rtcpGroupsockAudio(*env, destinationAddress, rtcpPortAudio, ttl);
  Groupsock rtpGroupsockVideo(*env, destinationAddress, rtpPortVideo, ttl);
  Groupsock rtcpGroupsockVideo(*env, destinationAddress, rtcpPortVideo, ttl);
#ifdef USE_SSM
  rtpGroupsockAudio.multicastSendOnly();
  rtcpGroupsockAudio.multicastSendOnly();
  rtpGroupsockVideo.multicastSendOnly();
  rtcpGroupsockVideo.multicastSendOnly();
#endif

  // Create a 'MPEG Audio RTP' sink from the RTP 'groupsock':
//  audioSink = MPEG1or2AudioRTPSink::createNew(*env, &rtpGroupsockAudio);

   audioSink = SimpleRTPSink::createNew(*env, &rtpGroupsockAudio,
			       payloadFormatCode, samplingFrequency,
			       "audio", mimeType, numChannels);

  // Create (and start) a 'RTCP instance' for this RTP sink:
  const unsigned estimatedSessionBandwidthAudio = bitsPerSecond/1000; // in kbps; for RTCP b/w share
  const unsigned maxCNAMElen = 100;
  unsigned char CNAME[maxCNAMElen+1];
  gethostname((char*)CNAME, maxCNAMElen);
  CNAME[maxCNAMElen] = '\0'; // just in case
#ifdef IMPLEMENT_RTSP_SERVER
  RTCPInstance* audioRTCP =
#endif
    RTCPInstance::createNew(*env, &rtcpGroupsockAudio,
			    estimatedSessionBandwidthAudio, CNAME,
			    audioSink, NULL /* we're a server */, isSSM);
  // Note: This starts RTCP running automatically

  // Create a 'MPEG Video RTP' sink from the RTP 'groupsock':
  videoSink = MPEG1or2VideoRTPSink::createNew(*env, &rtpGroupsockVideo);

  // Create (and start) a 'RTCP instance' for this RTP sink:
  const unsigned estimatedSessionBandwidthVideo = 4500; // in kbps; for RTCP b/w share
#ifdef IMPLEMENT_RTSP_SERVER
  RTCPInstance* videoRTCP =
#endif
    RTCPInstance::createNew(*env, &rtcpGroupsockVideo,
			      estimatedSessionBandwidthVideo, CNAME,
			      videoSink, NULL /* we're a server */, isSSM);
  // Note: This starts RTCP running automatically

#ifdef IMPLEMENT_RTSP_SERVER
  RTSPServer* rtspServer = RTSPServer::createNew(*env);
  // Note that this (attempts to) start a server on the default RTSP server
  // port: 554.  To use a different port number, add it as an extra
  // (optional) parameter to the "RTSPServer::createNew()" call above.
  if (rtspServer == NULL) {
    *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    exit(1);
  }
  ServerMediaSession* sms
    = ServerMediaSession::createNew(*env, "testStream", inputFileName,
		   "Session streamed by \"testMPEG1or2AudioVideoStreamer\"",
					   isSSM);
  sms->addSubsession(PassiveServerMediaSubsession::createNew(*audioSink, audioRTCP));
  sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, videoRTCP));
  rtspServer->addServerMediaSession(sms);

  char* url = rtspServer->rtspURL(sms);
  *env << "Play this stream using the URL \"" << url << "\"\n";
  delete[] url;
#endif

  // Finally, start the streaming:
  *env << "Beginning streaming...\n";
  play();

  env->taskScheduler().doEventLoop(); // does not return

  return 0; // only to prevent compiler warning
}

void afterPlaying(void* clientData) {
  // One of the sinks has ended playing.
  // Check whether any of the sources have a pending read.  If so,
  // wait until its sink ends playing also:
  //  if (audioSource->isCurrentlyAwaitingData()
  //   || videoSource->isCurrentlyAwaitingData()) return;
  
  // Now that both sinks have ended, close both input sources,
  // and start playing again:
  *env << "...done reading from file\n";

  audioSink->stopPlaying();
  videoSink->stopPlaying();
      // ensures that both are shut down
  Medium::close(audioSource);
  Medium::close(videoSource);
  // Note: This also closes the input file that this source read from.

  fileVideoSource = ByteStreamFileSource::createNew(*env, inputFileName);
  pcmSource = WAVAudioFileSource::createNew(*env, inputFileNamePcm);

  // Start playing once again:
  play();
}

void play() {
  
  // We must demultiplex Audio and Video Elementary Streams
  // from the input source:
  FramedSource* videoES = fileVideoSource;
  // Create a framer for each Elementary Stream:
  MpegSource  = MPEG1or2VideoStreamFramer::createNew(*env, videoES, iFramesOnly);
  audioSource = EndianSwap16::createNew(*env, pcmSource);

  /*******************************************************************/
  /* Finally, start with some delay (3 seconds)                      */
  /* Ouch...This will create a GAP between audio and video !!!!!     */
  /* Change 3000000 to 0 and no GAP                                  */
  /*******************************************************************/

  env->taskScheduler().scheduleDelayedTask(3000000, start, NULL);
}


void start(void *) {
  *env << "Beginning to read from file...\n";
  
//MpegSource->flushInput();   // !!!!! Remove comment to solve delay problem
  audioSink->startPlaying(*audioSource, afterPlaying, audioSink);
  videoSink->startPlaying(*MpegSource, afterPlaying, videoSink);
}