/********** 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. // Interleaving of MP3 ADUs // Implementation #include "MP3ADUinterleaving.hh" #include "MP3ADUdescriptor.hh" #include #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; }