/**********
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 3 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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2017 Live Networks, Inc.  All rights reserved.
// A filter that breaks up an AC3 audio elementary stream into frames
// Implementation

#include "AC3AudioStreamFramer.hh"
#include "StreamParser.hh"
#include <GroupsockHelper.hh>

////////// AC3AudioStreamParser definition //////////

class AC3FrameParams {
public:
  AC3FrameParams() : samplingFreq(0) {}
  // 8-byte header at the start of each frame:
  //  u_int32_t hdr0, hdr1;
  unsigned hdr0, hdr1;

  // parameters derived from the headers
  unsigned kbps, samplingFreq, frameSize;

  void setParamsFromHeader();
};

class AC3AudioStreamParser: public StreamParser {
public:
  AC3AudioStreamParser(AC3AudioStreamFramer* usingSource,
			FramedSource* inputSource);
  virtual ~AC3AudioStreamParser();

public:
  void testStreamCode(unsigned char ourStreamCode,
		      unsigned char* ptr, unsigned size);
  unsigned parseFrame(unsigned& numTruncatedBytes);
     // returns the size of the frame that was acquired, or 0 if none was

  void registerReadInterest(unsigned char* to, unsigned maxSize);

  AC3FrameParams const& currentFrame() const { return fCurrentFrame; }

  Boolean haveParsedAFrame() const { return fHaveParsedAFrame; }
  void readAndSaveAFrame();

private:
  static void afterGettingSavedFrame(void* clientData, unsigned frameSize,
				     unsigned numTruncatedBytes,
                                     struct timeval presentationTime,
				     unsigned durationInMicroseconds);
  void afterGettingSavedFrame1(unsigned frameSize);
  static void onSavedFrameClosure(void* clientData);
  void onSavedFrameClosure1();

private:
  AC3AudioStreamFramer* fUsingSource;
  unsigned char* fTo;
  unsigned fMaxSize;

  Boolean fHaveParsedAFrame;
  unsigned char* fSavedFrame;
  unsigned fSavedFrameSize;
  char fSavedFrameFlag;

  // Parameters of the most recently read frame:
  AC3FrameParams fCurrentFrame;
};


////////// AC3AudioStreamFramer implementation //////////

AC3AudioStreamFramer::AC3AudioStreamFramer(UsageEnvironment& env,
					   FramedSource* inputSource,
					   unsigned char streamCode)
  : FramedFilter(env, inputSource), fOurStreamCode(streamCode) {
  // Use the current wallclock time as the initial 'presentation time':
  gettimeofday(&fNextFramePresentationTime, NULL);

  fParser = new AC3AudioStreamParser(this, inputSource);
}

AC3AudioStreamFramer::~AC3AudioStreamFramer() {
  delete fParser;
}

AC3AudioStreamFramer*
AC3AudioStreamFramer::createNew(UsageEnvironment& env,
				FramedSource* inputSource,
				unsigned char streamCode) {
  // Need to add source type checking here???  #####
  return new AC3AudioStreamFramer(env, inputSource, streamCode);
}

unsigned AC3AudioStreamFramer::samplingRate() {
  if (!fParser->haveParsedAFrame()) {
    // Because we haven't yet parsed a frame, we don't yet know the input
    // stream's sampling rate.  So, we first need to read a frame
    // (into a special buffer that we keep around for later use).
    fParser->readAndSaveAFrame();
  }

  return fParser->currentFrame().samplingFreq;
}

void AC3AudioStreamFramer::flushInput() {
  fParser->flushInput();
}

void AC3AudioStreamFramer::doGetNextFrame() {
  fParser->registerReadInterest(fTo, fMaxSize);
  parseNextFrame();
}

#define MILLION 1000000

struct timeval AC3AudioStreamFramer::currentFramePlayTime() const {
  AC3FrameParams const& fr = fParser->currentFrame();
  unsigned const numSamples = 1536;
  unsigned const freq = fr.samplingFreq;

  // result is numSamples/freq
  unsigned const uSeconds = (freq == 0) ? 0
    : ((numSamples*2*MILLION)/freq + 1)/2; // rounds to nearest integer

  struct timeval result;
  result.tv_sec = uSeconds/MILLION;
  result.tv_usec = uSeconds%MILLION;
  return result;
}

void AC3AudioStreamFramer
::handleNewData(void* clientData, unsigned char* ptr, unsigned size,
		struct timeval /*presentationTime*/) {
  AC3AudioStreamFramer* framer = (AC3AudioStreamFramer*)clientData;
  framer->handleNewData(ptr, size);
}

void AC3AudioStreamFramer
::handleNewData(unsigned char* ptr, unsigned size) {
  fParser->testStreamCode(fOurStreamCode, ptr, size);

  parseNextFrame();
}

void AC3AudioStreamFramer::parseNextFrame() {
  unsigned acquiredFrameSize = fParser->parseFrame(fNumTruncatedBytes);
  if (acquiredFrameSize > 0) {
    // We were able to acquire a frame from the input.
    // It has already been copied to the reader's space.
    fFrameSize = acquiredFrameSize;

    // Also set the presentation time, and increment it for next time,
    // based on the length of this frame:
    fPresentationTime = fNextFramePresentationTime;

    struct timeval framePlayTime = currentFramePlayTime();
    fDurationInMicroseconds = framePlayTime.tv_sec*MILLION + framePlayTime.tv_usec;
    fNextFramePresentationTime.tv_usec += framePlayTime.tv_usec;
    fNextFramePresentationTime.tv_sec
      += framePlayTime.tv_sec + fNextFramePresentationTime.tv_usec/MILLION;
    fNextFramePresentationTime.tv_usec %= MILLION;

    // Call our own 'after getting' function.  Because we're not a 'leaf'
    // source, we can call this directly, without risking infinite recursion.
    afterGetting(this);
  } else {
    // We were unable to parse a complete frame from the input, because:
    // - we had to read more data from the source stream, or
    // - the source stream has ended.
  }
}


////////// AC3AudioStreamParser implementation //////////

static int const kbpsTable[] = {32,  40,  48,  56,  64,  80,  96, 112,
				128, 160, 192, 224, 256, 320, 384, 448,
				512, 576, 640};

void AC3FrameParams::setParamsFromHeader() {
  unsigned char byte4 = hdr1 >> 24;

  unsigned char kbpsIndex = (byte4&0x3E) >> 1;
  if (kbpsIndex > 18) kbpsIndex = 18;
  kbps = kbpsTable[kbpsIndex];

  unsigned char samplingFreqIndex = (byte4&0xC0) >> 6;
  switch (samplingFreqIndex) {
  case 0:
    samplingFreq = 48000;
    frameSize = 4*kbps;
    break;
  case 1:
    samplingFreq = 44100;
    frameSize = 2*(320*kbps/147 + (byte4&1));
    break;
  case 2:
  case 3: // not legal?
    samplingFreq = 32000;
    frameSize = 6*kbps;
  }
}

AC3AudioStreamParser
::AC3AudioStreamParser(AC3AudioStreamFramer* usingSource,
			FramedSource* inputSource)
  : StreamParser(inputSource, FramedSource::handleClosure, usingSource,
		 &AC3AudioStreamFramer::handleNewData, usingSource),
    fUsingSource(usingSource), fHaveParsedAFrame(False),
    fSavedFrame(NULL), fSavedFrameSize(0) {
}

AC3AudioStreamParser::~AC3AudioStreamParser() {
}

void AC3AudioStreamParser::registerReadInterest(unsigned char* to,
						 unsigned maxSize) {
  fTo = to;
  fMaxSize = maxSize;
}

void AC3AudioStreamParser
::testStreamCode(unsigned char ourStreamCode,
		 unsigned char* ptr, unsigned size) {
  if (ourStreamCode == 0) return; // we assume that there's no stream code at the beginning of the data

  if (size < 4) return;
  unsigned char streamCode = *ptr;

  if (streamCode == ourStreamCode) {
    // Remove the first 4 bytes from the stream:
    memmove(ptr, ptr + 4, size - 4);
    totNumValidBytes() = totNumValidBytes() - 4;
  } else {
    // Discard all of the data that was just read:
    totNumValidBytes() = totNumValidBytes() - size;
  }
}

unsigned AC3AudioStreamParser::parseFrame(unsigned& numTruncatedBytes) {
  if (fSavedFrameSize > 0) {
    // We've already read and parsed a frame.  Use it instead:
    memmove(fTo, fSavedFrame, fSavedFrameSize);
    delete[] fSavedFrame; fSavedFrame = NULL;
    unsigned frameSize = fSavedFrameSize;
    fSavedFrameSize = 0;
    return frameSize;
  }

  try {
    saveParserState();

    // We expect an AC3 audio header (first 2 bytes == 0x0B77) at the start:
    while (1) {
      unsigned next4Bytes = test4Bytes();
      if (next4Bytes>>16 == 0x0B77) break;
      skipBytes(1);
      saveParserState();
    }
    fCurrentFrame.hdr0 = get4Bytes();
    fCurrentFrame.hdr1 = test4Bytes();

    fCurrentFrame.setParamsFromHeader();
    fHaveParsedAFrame = True;

    // Copy the frame to the requested destination:
    unsigned frameSize = fCurrentFrame.frameSize;
    if (frameSize > fMaxSize) {
      numTruncatedBytes = frameSize - fMaxSize;
      frameSize = fMaxSize;
    } else {
      numTruncatedBytes = 0;
    }

    fTo[0] = fCurrentFrame.hdr0 >> 24;
    fTo[1] = fCurrentFrame.hdr0 >> 16;
    fTo[2] = fCurrentFrame.hdr0 >> 8;
    fTo[3] = fCurrentFrame.hdr0;
    getBytes(&fTo[4], frameSize-4);
    skipBytes(numTruncatedBytes);

    return frameSize;
  } catch (int /*e*/) {
#ifdef DEBUG
    fUsingSource->envir() << "AC3AudioStreamParser::parseFrame() EXCEPTION (This is normal behavior - *not* an error)\n";
#endif
    return 0;  // the parsing got interrupted
  }
}

void AC3AudioStreamParser::readAndSaveAFrame() {
  unsigned const maxAC3FrameSize = 4000;
  fSavedFrame = new unsigned char[maxAC3FrameSize];
  fSavedFrameSize = 0;

  fSavedFrameFlag = 0;
  fUsingSource->getNextFrame(fSavedFrame, maxAC3FrameSize,
			     afterGettingSavedFrame, this,
			     onSavedFrameClosure, this);
  fUsingSource->envir().taskScheduler().doEventLoop(&fSavedFrameFlag);
}

void AC3AudioStreamParser
::afterGettingSavedFrame(void* clientData, unsigned frameSize,
			 unsigned /*numTruncatedBytes*/,
			 struct timeval /*presentationTime*/,
			 unsigned /*durationInMicroseconds*/) {
  AC3AudioStreamParser* parser = (AC3AudioStreamParser*)clientData;
  parser->afterGettingSavedFrame1(frameSize);
}

void AC3AudioStreamParser
::afterGettingSavedFrame1(unsigned frameSize) {
  fSavedFrameSize = frameSize;
  fSavedFrameFlag = ~0;
}

void AC3AudioStreamParser::onSavedFrameClosure(void* clientData) {
  AC3AudioStreamParser* parser = (AC3AudioStreamParser*)clientData;
  parser->onSavedFrameClosure1();
}

void AC3AudioStreamParser::onSavedFrameClosure1() {
  delete[] fSavedFrame; fSavedFrame = NULL;
  fSavedFrameSize = 0;
  fSavedFrameFlag = ~0;
}
