/**********
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.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MP3 audio file.
// (Actually, any MPEG-1 or MPEG-2 audio file should work.)
// Implementation

#include "MP3AudioFileServerMediaSubsession.hh"
#include "MPEG1or2AudioRTPSink.hh"
#include "MP3ADURTPSink.hh"
#include "MP3FileSource.hh"
#include "MP3ADU.hh"

MP3AudioFileServerMediaSubsession* MP3AudioFileServerMediaSubsession
::createNew(UsageEnvironment& env, char const* fileName, Boolean reuseFirstSource,
	    Boolean generateADUs, Interleaving* interleaving) {
  return new MP3AudioFileServerMediaSubsession(env, fileName, reuseFirstSource,
					       generateADUs, interleaving);
}

MP3AudioFileServerMediaSubsession
::MP3AudioFileServerMediaSubsession(UsageEnvironment& env,
				    char const* fileName, Boolean reuseFirstSource,
				    Boolean generateADUs,
				    Interleaving* interleaving)
  : FileServerMediaSubsession(env, fileName, reuseFirstSource),
    fGenerateADUs(generateADUs), fInterleaving(interleaving), fFileDuration(0.0) {
}

MP3AudioFileServerMediaSubsession
::~MP3AudioFileServerMediaSubsession() {
  delete fInterleaving;
}

FramedSource* MP3AudioFileServerMediaSubsession
::createNewStreamSourceCommon(FramedSource* baseMP3Source, unsigned mp3NumBytes, unsigned& estBitrate) {
  FramedSource* streamSource;
  do {
    streamSource = baseMP3Source; // by default
    if (streamSource == NULL) break;

    // Use the MP3 file size, plus the duration, to estimate the stream's bitrate:
    if (mp3NumBytes > 0 && fFileDuration > 0.0) {
      estBitrate = (unsigned)(mp3NumBytes/(125*fFileDuration) + 0.5); // kbps, rounded
      if (estBitrate == 0) estBitrate = 128; // kbps, estimate
    } else {
      estBitrate = 128; // kbps, estimate
    }

    if (fGenerateADUs) {
      // Add a filter that converts the source MP3s to ADUs:
      streamSource = ADUFromMP3Source::createNew(envir(), streamSource);
      if (streamSource == NULL) break;

      if (fInterleaving != NULL) {
	// Add another filter that interleaves the ADUs before packetizing:
	streamSource = MP3ADUinterleaver::createNew(envir(), *fInterleaving,
						    streamSource);
	if (streamSource == NULL) break;
      }
    } else if (fFileDuration > 0.0) {
      // Because this is a seekable file, insert a pair of filters: one that
      // converts the input MP3 stream to ADUs; another that converts these
      // ADUs back to MP3.  This allows us to seek within the input stream without
      // tripping over the MP3 'bit reservoir':
      streamSource = ADUFromMP3Source::createNew(envir(), streamSource);
      if (streamSource == NULL) break;

      streamSource = MP3FromADUSource::createNew(envir(), streamSource);
      if (streamSource == NULL) break;
    }
  } while (0);

  return streamSource;
}

void MP3AudioFileServerMediaSubsession::getBaseStreams(FramedSource* frontStream,
						       FramedSource*& sourceMP3Stream, ADUFromMP3Source*& aduStream/*if any*/) {
  if (fGenerateADUs) {
    // There's an ADU stream.
    if (fInterleaving != NULL) {
      // There's an interleaving filter in front of the ADU stream.  So go back one, to reach the ADU stream:
      aduStream = (ADUFromMP3Source*)(((FramedFilter*)frontStream)->inputSource());
    } else {
      aduStream = (ADUFromMP3Source*)frontStream;
    }

    // Then, go back one more, to reach the MP3 source:
    sourceMP3Stream = (MP3FileSource*)(aduStream->inputSource());
  } else if (fFileDuration > 0.0) {
    // There are a pair of filters - MP3->ADU and ADU->MP3 - in front of the
    // original MP3 source.  So, go back one, to reach the ADU source:
    aduStream = (ADUFromMP3Source*)(((FramedFilter*)frontStream)->inputSource());

    // Then, go back one more, to reach the MP3 source:
    sourceMP3Stream = (MP3FileSource*)(aduStream->inputSource());
  } else {
    // There's no filter in front of the source MP3 stream (and there's no ADU stream):
    aduStream = NULL;
    sourceMP3Stream = frontStream;
  }
}


void MP3AudioFileServerMediaSubsession
::seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& /*numBytes*/) {
  FramedSource* sourceMP3Stream;
  ADUFromMP3Source* aduStream;
  getBaseStreams(inputSource, sourceMP3Stream, aduStream);

  if (aduStream != NULL) aduStream->resetInput(); // because we're about to seek within its source
  ((MP3FileSource*)sourceMP3Stream)->seekWithinFile(seekNPT, streamDuration);
}

void MP3AudioFileServerMediaSubsession
::setStreamSourceScale(FramedSource* inputSource, float scale) {

  FramedSource* sourceMP3Stream;
  ADUFromMP3Source* aduStream;
  getBaseStreams(inputSource, sourceMP3Stream, aduStream);

  if (aduStream == NULL) return; // because, in this case, the stream's not scalable

  int iScale = (int)scale;
  aduStream->setScaleFactor(iScale);
  ((MP3FileSource*)sourceMP3Stream)->setPresentationTimeScale(iScale);
}

FramedSource* MP3AudioFileServerMediaSubsession
::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {
  MP3FileSource* mp3Source = MP3FileSource::createNew(envir(), fFileName);
  if (mp3Source == NULL) return NULL;
  fFileDuration = mp3Source->filePlayTime();

  return createNewStreamSourceCommon(mp3Source, mp3Source->fileSize(), estBitrate);
}

RTPSink* MP3AudioFileServerMediaSubsession
::createNewRTPSink(Groupsock* rtpGroupsock,
		   unsigned char rtpPayloadTypeIfDynamic,
		   FramedSource* /*inputSource*/) {
  if (fGenerateADUs) {
    return MP3ADURTPSink::createNew(envir(), rtpGroupsock,
				    rtpPayloadTypeIfDynamic);
  } else {
    return MPEG1or2AudioRTPSink::createNew(envir(), rtpGroupsock);
  }
}

void MP3AudioFileServerMediaSubsession::testScaleFactor(float& scale) {
  if (fFileDuration <= 0.0) {
    // The file is non-seekable, so is probably a live input source.
    // We don't support scale factors other than 1
    scale = 1;
  } else {
    // We support any integral scale >= 1
    int iScale = (int)(scale + 0.5); // round
    if (iScale < 1) iScale = 1;
    scale = (float)iScale;
  }
}

float MP3AudioFileServerMediaSubsession::duration() const {
  return fFileDuration;
}
