/********** 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 .) 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-2016 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 } 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; }