/**********
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-2024 Live Networks, Inc.  All rights reserved.
// Interleaving of MP3 ADUs
// Implementation

#include "MP3ADUinterleaving.hh"
#include "MP3ADUdescriptor.hh"

#include <string.h>

#ifdef TEST_LOSS
#include "GroupsockHelper.hh"
#endif

////////// Interleaving //////////

Interleaving::Interleaving(unsigned cycleSize,
			   unsigned char const* cycleArray)
  : fCycleSize(cycleSize) {
  for (unsigned i = 0; i < fCycleSize; ++i) {
    fInverseCycle[cycleArray[i]] = i;
  }
}

Interleaving::~Interleaving() {
}

////////// MP3ADUinterleaverBase //////////

MP3ADUinterleaverBase::MP3ADUinterleaverBase(UsageEnvironment& env,
					     FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}
MP3ADUinterleaverBase::~MP3ADUinterleaverBase() {
}

FramedSource* MP3ADUinterleaverBase::getInputSource(UsageEnvironment& env,
						    char const* inputSourceName) {
  FramedSource* inputSource;
  if (!FramedSource::lookupByName(env, inputSourceName, inputSource))
    return NULL;

  if (strcmp(inputSource->MIMEtype(), "audio/MPA-ROBUST") != 0) {
    env.setResultMsg(inputSourceName, " is not an MP3 ADU source");
    return NULL;
  }

  return inputSource;
}

void MP3ADUinterleaverBase::afterGettingFrame(void* clientData,
					      unsigned numBytesRead,
					      unsigned /*numTruncatedBytes*/,
					      struct timeval presentationTime,
					      unsigned durationInMicroseconds) {
  MP3ADUinterleaverBase* interleaverBase = (MP3ADUinterleaverBase*)clientData;
  // Finish up after reading:
  interleaverBase->afterGettingFrame(numBytesRead,
				     presentationTime, durationInMicroseconds);

  // Then, continue to deliver an outgoing frame:
  interleaverBase->doGetNextFrame();
}


////////// InterleavingFrames (definition) //////////

class InterleavingFrames {
public:
  InterleavingFrames(unsigned maxCycleSize);
  virtual ~InterleavingFrames();

  Boolean haveReleaseableFrame();
  void getIncomingFrameParams(unsigned char index,
			      unsigned char*& dataPtr,
			      unsigned& bytesAvailable);
  void getReleasingFrameParams(unsigned char index,
			       unsigned char*& dataPtr,
			       unsigned& bytesInUse,
			       struct timeval& presentationTime,
			       unsigned& durationInMicroseconds);
  void setFrameParams(unsigned char index,
		      unsigned char icc, unsigned char ii,
		      unsigned frameSize, struct timeval presentationTime,
		      unsigned durationInMicroseconds);
  unsigned nextIndexToRelease() {return fNextIndexToRelease;}
  void releaseNext();

private:
  unsigned fMaxCycleSize;
  unsigned fNextIndexToRelease;
  class InterleavingFrameDescriptor* fDescriptors;
};

////////// MP3ADUinterleaver //////////


MP3ADUinterleaver::MP3ADUinterleaver(UsageEnvironment& env,
				     Interleaving const& interleaving,
				     FramedSource* inputSource)
  : MP3ADUinterleaverBase(env, inputSource),
    fInterleaving(interleaving),
    fFrames(new InterleavingFrames(interleaving.cycleSize())),
    fII(0), fICC(0) {
}

MP3ADUinterleaver::~MP3ADUinterleaver() {
  delete fFrames;
}

MP3ADUinterleaver* MP3ADUinterleaver::createNew(UsageEnvironment& env,
						Interleaving const& interleaving,
						FramedSource* inputSource) {
  return new MP3ADUinterleaver(env, interleaving, inputSource);
}

void MP3ADUinterleaver::doGetNextFrame() {
  // If there's a frame immediately available, deliver it, otherwise get new
  // frames from the source until one's available:
  if (fFrames->haveReleaseableFrame()) {
    releaseOutgoingFrame();

    // 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 {
    fPositionOfNextIncomingFrame = fInterleaving.lookupInverseCycle(fII);
    unsigned char* dataPtr;
    unsigned bytesAvailable;
    fFrames->getIncomingFrameParams(fPositionOfNextIncomingFrame,
				    dataPtr, bytesAvailable);

    // Read the next incoming frame (asynchronously)
    fInputSource->getNextFrame(dataPtr, bytesAvailable,
			       &MP3ADUinterleaverBase::afterGettingFrame, this,
			       handleClosure, this);
  }
}

void MP3ADUinterleaver::releaseOutgoingFrame() {
  unsigned char* fromPtr;
  fFrames->getReleasingFrameParams(fFrames->nextIndexToRelease(),
				   fromPtr, fFrameSize,
				   fPresentationTime, fDurationInMicroseconds);

  if (fFrameSize > fMaxSize) {
    fNumTruncatedBytes = fFrameSize - fMaxSize;
    fFrameSize = fMaxSize;
  }
  memmove(fTo, fromPtr, fFrameSize);

  fFrames->releaseNext();
}

void MP3ADUinterleaver::afterGettingFrame(unsigned numBytesRead,
					  struct timeval presentationTime,
					  unsigned durationInMicroseconds) {
  // Set the (icc,ii) and frame size of the newly-read frame:
  fFrames->setFrameParams(fPositionOfNextIncomingFrame,
			  fICC, fII, numBytesRead,
			  presentationTime, durationInMicroseconds);

  // Prepare our counters for the next frame:
  if (++fII == fInterleaving.cycleSize()) {
    fII = 0;
    fICC = (fICC+1)%8;
  }
}

////////// DeinterleavingFrames (definition) //////////

class DeinterleavingFrames {
public:
  DeinterleavingFrames();
  virtual ~DeinterleavingFrames();

  Boolean haveReleaseableFrame();
  void getIncomingFrameParams(unsigned char*& dataPtr,
			      unsigned& bytesAvailable);
  void getIncomingFrameParamsAfter(unsigned frameSize,
				   struct timeval presentationTime,
				   unsigned durationInMicroseconds,
				   unsigned char& icc, unsigned char& ii);
  void getReleasingFrameParams(unsigned char*& dataPtr,
			       unsigned& bytesInUse,
			       struct timeval& presentationTime,
			       unsigned& durationInMicroseconds);
  void moveIncomingFrameIntoPlace();
  void releaseNext();
  void startNewCycle();

private:
  unsigned fNextIndexToRelease;
  Boolean fHaveEndedCycle;
  unsigned fIIlastSeen;
  unsigned fMinIndexSeen, fMaxIndexSeen; // actually, max+1
  class DeinterleavingFrameDescriptor* fDescriptors;
};

////////// MP3ADUdeinterleaver //////////

MP3ADUdeinterleaver::MP3ADUdeinterleaver(UsageEnvironment& env,
					 FramedSource* inputSource)
  : MP3ADUinterleaverBase(env, inputSource),
    fFrames(new DeinterleavingFrames),
    fIIlastSeen(~0), fICClastSeen(~0) {
}

MP3ADUdeinterleaver::~MP3ADUdeinterleaver() {
  delete fFrames;
}

MP3ADUdeinterleaver* MP3ADUdeinterleaver::createNew(UsageEnvironment& env,
						    FramedSource* inputSource) {
  return new MP3ADUdeinterleaver(env, inputSource);
}

void MP3ADUdeinterleaver::doGetNextFrame() {
  // If there's a frame immediately available, deliver it, otherwise get new
  // frames from the source until one's available:
  if (fFrames->haveReleaseableFrame()) {
    releaseOutgoingFrame();

    // 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 {
#ifdef TEST_LOSS
  NOTE: This code no longer works, because it uses synchronous reads,
  which are no longer supported.
    static unsigned const framesPerPacket = 3;
    static unsigned const frameCount = 0;
    static Boolean packetIsLost;
    while (1) {
      unsigned packetCount = frameCount/framesPerPacket;
      if ((frameCount++)%framesPerPacket == 0) {
	packetIsLost = (our_random()%10 == 0); // simulate 10% packet loss #####
      }

      if (packetIsLost) {
	// Read and discard the next input frame (that would be part of
	// a lost packet):
	unsigned char dummyBuf[2000];
	unsigned numBytesRead;
	struct timeval presentationTime;
	// (this works only if the source can be read synchronously)
	fInputSource->syncGetNextFrame(dummyBuf, sizeof dummyBuf,
				       numBytesRead, presentationTime);
      } else {
	break; // from while (1)
      }
    }
#endif
    unsigned char* dataPtr;
    unsigned bytesAvailable;
    fFrames->getIncomingFrameParams(dataPtr, bytesAvailable);

    // Read the next incoming frame (asynchronously)
    fInputSource->getNextFrame(dataPtr, bytesAvailable,
			       &MP3ADUinterleaverBase::afterGettingFrame, this,
			       handleClosure, this);
  }
}

void MP3ADUdeinterleaver::afterGettingFrame(unsigned numBytesRead,
					    struct timeval presentationTime,
					    unsigned durationInMicroseconds) {
  // Get the (icc,ii) and set the frame size of the newly-read frame:
  unsigned char icc, ii;
  fFrames->getIncomingFrameParamsAfter(numBytesRead,
				       presentationTime, durationInMicroseconds,
				       icc, ii);

  // Compare these to the values we saw last:
  if (icc != fICClastSeen || ii == fIIlastSeen) {
    // We've started a new interleave cycle
    // (or interleaving was not used).  Release all
    // pending ADU frames to the ADU->MP3 conversion step:
    fFrames->startNewCycle();
  } else {
    // We're still in the same cycle as before.
    // Move the newly-read frame into place, so it can be used:
    fFrames->moveIncomingFrameIntoPlace();
  }

  fICClastSeen = icc;
  fIIlastSeen = ii;
}

void MP3ADUdeinterleaver::releaseOutgoingFrame() {
  unsigned char* fromPtr;
  fFrames->getReleasingFrameParams(fromPtr, fFrameSize,
				   fPresentationTime, fDurationInMicroseconds);

  if (fFrameSize > fMaxSize) {
    fNumTruncatedBytes = fFrameSize - fMaxSize;
    fFrameSize = fMaxSize;
  }
  memmove(fTo, fromPtr, fFrameSize);

  fFrames->releaseNext();
}

////////// InterleavingFrames (implementation) //////////

#define MAX_FRAME_SIZE 2000 /* conservatively high */

class InterleavingFrameDescriptor {
public:
  InterleavingFrameDescriptor() {frameDataSize = 0;}

  unsigned frameDataSize; // includes ADU descriptor and (modified) MPEG hdr
  struct timeval presentationTime;
  unsigned durationInMicroseconds;
  unsigned char frameData[MAX_FRAME_SIZE]; // ditto
};

InterleavingFrames::InterleavingFrames(unsigned maxCycleSize)
  : fMaxCycleSize(maxCycleSize), fNextIndexToRelease(0),
    fDescriptors(new InterleavingFrameDescriptor[maxCycleSize]) {
}
InterleavingFrames::~InterleavingFrames() {
  delete[] fDescriptors;
}

Boolean InterleavingFrames::haveReleaseableFrame() {
  return fDescriptors[fNextIndexToRelease].frameDataSize > 0;
}

void InterleavingFrames::getIncomingFrameParams(unsigned char index,
						unsigned char*& dataPtr,
						unsigned& bytesAvailable) {
  InterleavingFrameDescriptor& desc = fDescriptors[index];
  dataPtr = &desc.frameData[0];
  bytesAvailable = MAX_FRAME_SIZE;
}

void InterleavingFrames::getReleasingFrameParams(unsigned char index,
						 unsigned char*& dataPtr,
						 unsigned& bytesInUse,
						 struct timeval& presentationTime,
						 unsigned& durationInMicroseconds) {
  InterleavingFrameDescriptor& desc = fDescriptors[index];
  dataPtr = &desc.frameData[0];
  bytesInUse = desc.frameDataSize;
  presentationTime = desc.presentationTime;
  durationInMicroseconds = desc.durationInMicroseconds;
}

void InterleavingFrames::setFrameParams(unsigned char index,
					unsigned char icc,
					unsigned char ii,
					unsigned frameSize,
					struct timeval presentationTime,
					unsigned durationInMicroseconds) {
  InterleavingFrameDescriptor& desc = fDescriptors[index];
  desc.frameDataSize = frameSize;
  desc.presentationTime = presentationTime;
  desc.durationInMicroseconds = durationInMicroseconds;

  // Advance over the ADU descriptor, to get to the MPEG 'syncword':
  unsigned char* ptr = &desc.frameData[0];
  (void)ADUdescriptor::getRemainingFrameSize(ptr);

  // Replace the next 11 bits with (ii,icc):
  *ptr++ = ii;
  *ptr &=~ 0xE0;
  *ptr |= (icc<<5);
}

void InterleavingFrames::releaseNext() {
  fDescriptors[fNextIndexToRelease].frameDataSize = 0;
  fNextIndexToRelease = (fNextIndexToRelease+1)%fMaxCycleSize;
}

////////// DeinterleavingFrames (implementation) //////////

class DeinterleavingFrameDescriptor {
public:
  DeinterleavingFrameDescriptor() {frameDataSize = 0; frameData = NULL;}
  virtual ~DeinterleavingFrameDescriptor() {delete[] frameData;}

  unsigned frameDataSize; // includes ADU descriptor and (modified) MPEG hdr
  struct timeval presentationTime;
  unsigned durationInMicroseconds;
  unsigned char* frameData;
};

DeinterleavingFrames::DeinterleavingFrames()
  : fNextIndexToRelease(0), fHaveEndedCycle(False),
    fMinIndexSeen(MAX_CYCLE_SIZE), fMaxIndexSeen(0),
    fDescriptors(new DeinterleavingFrameDescriptor[MAX_CYCLE_SIZE+1]) {
}
DeinterleavingFrames::~DeinterleavingFrames() {
  delete[] fDescriptors;
}

Boolean DeinterleavingFrames::haveReleaseableFrame() {
  if (!fHaveEndedCycle) {
    // Check just the next frame in the sequence
    return fDescriptors[fNextIndexToRelease].frameDataSize > 0;
  } else {
    // We've just ended a cycle, so we can skip over frames that didn't
    // get filled in (due to packet loss):
    if (fNextIndexToRelease < fMinIndexSeen) {
      fNextIndexToRelease = fMinIndexSeen;
    }
    while (fNextIndexToRelease < fMaxIndexSeen
	   && fDescriptors[fNextIndexToRelease].frameDataSize == 0) {
      ++fNextIndexToRelease;
    }
    if (fNextIndexToRelease >= fMaxIndexSeen) {
      // No more frames are available from the cycle that we just ended, so
      // clear out all previously stored frames, then make available
      // the last-read frame, and return false for now:
      for (unsigned i = fMinIndexSeen; i < fMaxIndexSeen; ++i) {
	fDescriptors[i].frameDataSize = 0;
      }

      fMinIndexSeen = MAX_CYCLE_SIZE; fMaxIndexSeen = 0;
      moveIncomingFrameIntoPlace();

      fHaveEndedCycle = False;
      fNextIndexToRelease = 0;
      return False;
    }

    return True;
  }
}

void DeinterleavingFrames::getIncomingFrameParams(unsigned char*& dataPtr,
						  unsigned& bytesAvailable) {
  // Use fDescriptors[MAX_CYCLE_SIZE] to store the incoming frame,
  // prior to figuring out its real position:
  DeinterleavingFrameDescriptor& desc = fDescriptors[MAX_CYCLE_SIZE];
  if (desc.frameData == NULL) {
    // There's no buffer yet, so allocate a new one:
    desc.frameData = new unsigned char[MAX_FRAME_SIZE];
  }
  dataPtr = desc.frameData;
  bytesAvailable = MAX_FRAME_SIZE;
}

void DeinterleavingFrames
::getIncomingFrameParamsAfter(unsigned frameSize,
			      struct timeval presentationTime,
			      unsigned durationInMicroseconds,
			      unsigned char& icc, unsigned char& ii) {
  DeinterleavingFrameDescriptor& desc = fDescriptors[MAX_CYCLE_SIZE];
  desc.frameDataSize = frameSize;
  desc.presentationTime = presentationTime;
  desc.durationInMicroseconds = durationInMicroseconds;

  // Advance over the ADU descriptor, to get to the MPEG 'syncword':
  unsigned char* ptr = desc.frameData;
  (void)ADUdescriptor::getRemainingFrameSize(ptr);

  // Read the next 11 bits into (ii,icc), and replace them with all-1s:
  fIIlastSeen = ii = *ptr; *ptr++ = 0xFF;
  icc = (*ptr&0xE0)>>5; *ptr |= 0xE0;
}

void DeinterleavingFrames::getReleasingFrameParams(unsigned char*& dataPtr,
						   unsigned& bytesInUse,
						   struct timeval& presentationTime,
						   unsigned& durationInMicroseconds) {
  DeinterleavingFrameDescriptor& desc = fDescriptors[fNextIndexToRelease];
  dataPtr = desc.frameData;
  bytesInUse = desc.frameDataSize;
  presentationTime = desc.presentationTime;
  durationInMicroseconds = desc.durationInMicroseconds;
}

void DeinterleavingFrames::moveIncomingFrameIntoPlace() {
  DeinterleavingFrameDescriptor& fromDesc = fDescriptors[MAX_CYCLE_SIZE];
  DeinterleavingFrameDescriptor& toDesc = fDescriptors[fIIlastSeen];

  toDesc.frameDataSize = fromDesc.frameDataSize;
  toDesc.presentationTime = fromDesc.presentationTime;

  // Move the data pointer into place by swapping the data pointers:
  unsigned char* tmp = toDesc.frameData;
  toDesc.frameData = fromDesc.frameData;
  fromDesc.frameData = tmp;

  if (fIIlastSeen < fMinIndexSeen) {
    fMinIndexSeen = fIIlastSeen;
  }
  if (fIIlastSeen + 1 > fMaxIndexSeen) {
    fMaxIndexSeen = fIIlastSeen + 1;
  }
}

void DeinterleavingFrames::releaseNext() {
  fDescriptors[fNextIndexToRelease].frameDataSize = 0;
  fNextIndexToRelease = (fNextIndexToRelease+1)%MAX_CYCLE_SIZE;
}

void DeinterleavingFrames::startNewCycle() {
  fHaveEndedCycle = True;
}
