/********** 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 .) 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-2020 Live Networks, Inc. All rights reserved. // AMR Audio RTP Sources (RFC 4867) // Implementation #include "AMRAudioRTPSource.hh" #include "MultiFramedRTPSource.hh" #include "BitVector.hh" #include #include // This source is implemented internally by two separate sources: // (i) a RTP source for the raw (and possibly interleaved) AMR frames, and // (ii) a deinterleaving filter that reads from this. // Define these two new classes here: class RawAMRRTPSource: public MultiFramedRTPSource { public: static RawAMRRTPSource* createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, Boolean isWideband, Boolean isOctetAligned, Boolean isInterleaved, Boolean CRCsArePresent); Boolean isWideband() const { return fIsWideband; } unsigned char ILL() const { return fILL; } unsigned char ILP() const { return fILP; } unsigned TOCSize() const { return fTOCSize; } // total # of frames in the last pkt unsigned char* TOC() const { return fTOC; } // FT+Q value for each TOC entry unsigned& frameIndex() { return fFrameIndex; } // index of frame-block within pkt Boolean& isSynchronized() { return fIsSynchronized; } private: RawAMRRTPSource(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, Boolean isWideband, Boolean isOctetAligned, Boolean isInterleaved, Boolean CRCsArePresent); // called only by createNew() virtual ~RawAMRRTPSource(); private: // redefined virtual functions: virtual Boolean hasBeenSynchronizedUsingRTCP(); virtual Boolean processSpecialHeader(BufferedPacket* packet, unsigned& resultSpecialHeaderSize); virtual char const* MIMEtype() const; private: Boolean fIsWideband, fIsOctetAligned, fIsInterleaved, fCRCsArePresent; unsigned char fILL, fILP; unsigned fTOCSize; unsigned char* fTOC; unsigned fFrameIndex; Boolean fIsSynchronized; }; class AMRDeinterleaver: public AMRAudioSource { public: static AMRDeinterleaver* createNew(UsageEnvironment& env, Boolean isWideband, unsigned numChannels, unsigned maxInterleaveGroupSize, RawAMRRTPSource* inputSource); private: AMRDeinterleaver(UsageEnvironment& env, Boolean isWideband, unsigned numChannels, unsigned maxInterleaveGroupSize, RawAMRRTPSource* inputSource); // called only by "createNew()" virtual ~AMRDeinterleaver(); static void afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds); void afterGettingFrame1(unsigned frameSize, struct timeval presentationTime); private: // Redefined virtual functions: void doGetNextFrame(); virtual void doStopGettingFrames(); private: RawAMRRTPSource* fInputSource; class AMRDeinterleavingBuffer* fDeinterleavingBuffer; Boolean fNeedAFrame; }; ////////// AMRAudioRTPSource implementation ////////// #define MAX_NUM_CHANNELS 20 // far larger than ever expected... #define MAX_INTERLEAVING_GROUP_SIZE 1000 // far larger than ever expected... AMRAudioSource* AMRAudioRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs, RTPSource*& resultRTPSource, unsigned char rtpPayloadFormat, Boolean isWideband, unsigned numChannels, Boolean isOctetAligned, unsigned interleaving, Boolean robustSortingOrder, Boolean CRCsArePresent) { // Perform sanity checks on the input parameters: if (robustSortingOrder) { env << "AMRAudioRTPSource::createNew(): 'Robust sorting order' was specified, but we don't yet support this!\n"; return NULL; } else if (numChannels > MAX_NUM_CHANNELS) { env << "AMRAudioRTPSource::createNew(): The \"number of channels\" parameter (" << numChannels << ") is much too large!\n"; return NULL; } else if (interleaving > MAX_INTERLEAVING_GROUP_SIZE) { env << "AMRAudioRTPSource::createNew(): The \"interleaving\" parameter (" << interleaving << ") is much too large!\n"; return NULL; } // 'Bandwidth-efficient mode' precludes some other options: if (!isOctetAligned) { if (interleaving > 0 || robustSortingOrder || CRCsArePresent) { env << "AMRAudioRTPSource::createNew(): 'Bandwidth-efficient mode' was specified, along with interleaving, 'robust sorting order', and/or CRCs, so we assume 'octet-aligned mode' instead.\n"; isOctetAligned = True; } } Boolean isInterleaved; unsigned maxInterleaveGroupSize; // in frames (not frame-blocks) if (interleaving > 0) { isInterleaved = True; maxInterleaveGroupSize = interleaving*numChannels; } else { isInterleaved = False; maxInterleaveGroupSize = numChannels; } RawAMRRTPSource* rawRTPSource; resultRTPSource = rawRTPSource = RawAMRRTPSource::createNew(env, RTPgs, rtpPayloadFormat, isWideband, isOctetAligned, isInterleaved, CRCsArePresent); if (resultRTPSource == NULL) return NULL; AMRDeinterleaver* deinterleaver = AMRDeinterleaver::createNew(env, isWideband, numChannels, maxInterleaveGroupSize, rawRTPSource); if (deinterleaver == NULL) { Medium::close(resultRTPSource); resultRTPSource = NULL; } return deinterleaver; } ////////// AMRBufferedPacket and AMRBufferedPacketFactory ////////// // A subclass of BufferedPacket, used to separate out AMR frames. class AMRBufferedPacket: public BufferedPacket { public: AMRBufferedPacket(RawAMRRTPSource& ourSource); virtual ~AMRBufferedPacket(); private: // redefined virtual functions virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize); private: RawAMRRTPSource& fOurSource; }; class AMRBufferedPacketFactory: public BufferedPacketFactory { private: // redefined virtual functions virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource); }; ///////// RawAMRRTPSource implementation //////// RawAMRRTPSource* RawAMRRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, Boolean isWideband, Boolean isOctetAligned, Boolean isInterleaved, Boolean CRCsArePresent) { return new RawAMRRTPSource(env, RTPgs, rtpPayloadFormat, isWideband, isOctetAligned, isInterleaved, CRCsArePresent); } RawAMRRTPSource ::RawAMRRTPSource(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, Boolean isWideband, Boolean isOctetAligned, Boolean isInterleaved, Boolean CRCsArePresent) : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, isWideband ? 16000 : 8000, new AMRBufferedPacketFactory), fIsWideband(isWideband), fIsOctetAligned(isOctetAligned), fIsInterleaved(isInterleaved), fCRCsArePresent(CRCsArePresent), fILL(0), fILP(0), fTOCSize(0), fTOC(NULL), fFrameIndex(0), fIsSynchronized(False) { } RawAMRRTPSource::~RawAMRRTPSource() { delete[] fTOC; } #define FT_SPEECH_LOST 14 #define FT_NO_DATA 15 static void unpackBandwidthEfficientData(BufferedPacket* packet, Boolean isWideband); // forward Boolean RawAMRRTPSource ::processSpecialHeader(BufferedPacket* packet, unsigned& resultSpecialHeaderSize) { // If the data is 'bandwidth-efficient', first unpack it so that it's // 'octet-aligned': if (!fIsOctetAligned) unpackBandwidthEfficientData(packet, fIsWideband); unsigned char* headerStart = packet->data(); unsigned packetSize = packet->dataSize(); // There's at least a 1-byte header, containing the CMR: if (packetSize < 1) return False; resultSpecialHeaderSize = 1; if (fIsInterleaved) { // There's an extra byte, containing the interleave parameters: if (packetSize < 2) return False; // Get the interleaving parameters, and check them for validity: unsigned char const secondByte = headerStart[1]; fILL = (secondByte&0xF0)>>4; fILP = secondByte&0x0F; if (fILP > fILL) return False; // invalid ++resultSpecialHeaderSize; } #ifdef DEBUG fprintf(stderr, "packetSize: %d, ILL: %d, ILP: %d\n", packetSize, fILL, fILP); #endif fFrameIndex = 0; // initially // Next, there's a "Payload Table of Contents" (one byte per entry): unsigned numFramesPresent = 0, numNonEmptyFramesPresent = 0; unsigned tocStartIndex = resultSpecialHeaderSize; Boolean F; do { if (resultSpecialHeaderSize >= packetSize) return False; unsigned char const tocByte = headerStart[resultSpecialHeaderSize++]; F = (tocByte&0x80) != 0; unsigned char const FT = (tocByte&0x78) >> 3; #ifdef DEBUG unsigned char Q = (tocByte&0x04)>>2; fprintf(stderr, "\tTOC entry: F %d, FT %d, Q %d\n", F, FT, Q); #endif ++numFramesPresent; if (FT != FT_SPEECH_LOST && FT != FT_NO_DATA) ++numNonEmptyFramesPresent; } while (F); #ifdef DEBUG fprintf(stderr, "TOC contains %d entries (%d non-empty)\n", numFramesPresent, numNonEmptyFramesPresent); #endif // Now that we know the size of the TOC, fill in our copy: if (numFramesPresent > fTOCSize) { delete[] fTOC; fTOC = new unsigned char[numFramesPresent]; } fTOCSize = numFramesPresent; for (unsigned i = 0; i < fTOCSize; ++i) { unsigned char const tocByte = headerStart[tocStartIndex + i]; fTOC[i] = tocByte&0x7C; // clear everything except the F and Q fields } if (fCRCsArePresent) { // 'numNonEmptyFramesPresent' CRC bytes will follow. // Note: we currently don't check the CRCs for validity ##### resultSpecialHeaderSize += numNonEmptyFramesPresent; #ifdef DEBUG fprintf(stderr, "Ignoring %d following CRC bytes\n", numNonEmptyFramesPresent); #endif if (resultSpecialHeaderSize > packetSize) return False; } #ifdef DEBUG fprintf(stderr, "Total special header size: %d\n", resultSpecialHeaderSize); #endif return True; } char const* RawAMRRTPSource::MIMEtype() const { return fIsWideband ? "audio/AMR-WB" : "audio/AMR"; } Boolean RawAMRRTPSource::hasBeenSynchronizedUsingRTCP() { return fIsSynchronized; } ///// AMRBufferedPacket and AMRBufferedPacketFactory implementation AMRBufferedPacket::AMRBufferedPacket(RawAMRRTPSource& ourSource) : fOurSource(ourSource) { } AMRBufferedPacket::~AMRBufferedPacket() { } // The mapping from the "FT" field to frame size. // Values of 65535 are invalid. #define FT_INVALID 65535 static unsigned short const frameBytesFromFT[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, FT_INVALID, FT_INVALID, FT_INVALID, FT_INVALID, FT_INVALID, FT_INVALID, 0 }; static unsigned short const frameBytesFromFTWideband[16] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, FT_INVALID, FT_INVALID, FT_INVALID, FT_INVALID, 0, 0 }; unsigned AMRBufferedPacket:: nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) { if (dataSize == 0) return 0; // sanity check // The size of the AMR frame is determined by the corresponding 'FT' value // in the packet's Table of Contents. unsigned const tocIndex = fOurSource.frameIndex(); if (tocIndex >= fOurSource.TOCSize()) return 0; // sanity check unsigned char const tocByte = fOurSource.TOC()[tocIndex]; unsigned char const FT = (tocByte&0x78) >> 3; // ASSERT: FT < 16 unsigned short frameSize = fOurSource.isWideband() ? frameBytesFromFTWideband[FT] : frameBytesFromFT[FT]; if (frameSize == FT_INVALID) { // Strange TOC entry! fOurSource.envir() << "AMRBufferedPacket::nextEnclosedFrameSize(): invalid FT: " << FT << "\n"; frameSize = 0; // This probably messes up the rest of this packet, but... } #ifdef DEBUG fprintf(stderr, "AMRBufferedPacket::nextEnclosedFrameSize(): frame #: %d, FT: %d, isWideband: %d => frameSize: %d (dataSize: %d)\n", tocIndex, FT, fOurSource.isWideband(), frameSize, dataSize); #endif ++fOurSource.frameIndex(); if (dataSize < frameSize) return 0; return frameSize; } BufferedPacket* AMRBufferedPacketFactory ::createNewPacket(MultiFramedRTPSource* ourSource) { return new AMRBufferedPacket((RawAMRRTPSource&)(*ourSource)); } ///////// AMRDeinterleavingBuffer ///////// // (used to implement AMRDeinterleaver) #define AMR_MAX_FRAME_SIZE 60 class AMRDeinterleavingBuffer { public: AMRDeinterleavingBuffer(unsigned numChannels, unsigned maxInterleaveGroupSize); virtual ~AMRDeinterleavingBuffer(); void deliverIncomingFrame(unsigned frameSize, RawAMRRTPSource* source, struct timeval presentationTime); Boolean retrieveFrame(unsigned char* to, unsigned maxSize, unsigned& resultFrameSize, unsigned& resultNumTruncatedBytes, u_int8_t& resultFrameHeader, struct timeval& resultPresentationTime, Boolean& resultIsSynchronized); unsigned char* inputBuffer() { return fInputBuffer; } unsigned inputBufferSize() const { return AMR_MAX_FRAME_SIZE; } private: unsigned char* createNewBuffer(); class FrameDescriptor { public: FrameDescriptor(); virtual ~FrameDescriptor(); unsigned frameSize; unsigned char* frameData; u_int8_t frameHeader; struct timeval presentationTime; Boolean fIsSynchronized; }; unsigned fNumChannels, fMaxInterleaveGroupSize; FrameDescriptor* fFrames[2]; unsigned char fIncomingBankId; // toggles between 0 and 1 unsigned char fIncomingBinMax; // in the incoming bank unsigned char fOutgoingBinMax; // in the outgoing bank unsigned char fNextOutgoingBin; Boolean fHaveSeenPackets; u_int16_t fLastPacketSeqNumForGroup; unsigned char* fInputBuffer; struct timeval fLastRetrievedPresentationTime; unsigned fNumSuccessiveSyncedFrames; unsigned char fILL; }; ////////// AMRDeinterleaver implementation ///////// AMRDeinterleaver* AMRDeinterleaver ::createNew(UsageEnvironment& env, Boolean isWideband, unsigned numChannels, unsigned maxInterleaveGroupSize, RawAMRRTPSource* inputSource) { return new AMRDeinterleaver(env, isWideband, numChannels, maxInterleaveGroupSize, inputSource); } AMRDeinterleaver::AMRDeinterleaver(UsageEnvironment& env, Boolean isWideband, unsigned numChannels, unsigned maxInterleaveGroupSize, RawAMRRTPSource* inputSource) : AMRAudioSource(env, isWideband, numChannels), fInputSource(inputSource), fNeedAFrame(False) { fDeinterleavingBuffer = new AMRDeinterleavingBuffer(numChannels, maxInterleaveGroupSize); } AMRDeinterleaver::~AMRDeinterleaver() { delete fDeinterleavingBuffer; Medium::close(fInputSource); } static unsigned const uSecsPerFrame = 20000; // 20 ms void AMRDeinterleaver::doGetNextFrame() { // First, try getting a frame from the deinterleaving buffer: if (fDeinterleavingBuffer->retrieveFrame(fTo, fMaxSize, fFrameSize, fNumTruncatedBytes, fLastFrameHeader, fPresentationTime, fInputSource->isSynchronized())) { // Success! fNeedAFrame = False; fDurationInMicroseconds = uSecsPerFrame; // Call our own 'after getting' function. Because we're not a 'leaf' // source, we can call this directly, without risking // infinite recursion afterGetting(this); return; } // No luck, so ask our source for help: fNeedAFrame = True; if (!fInputSource->isCurrentlyAwaitingData()) { fInputSource->getNextFrame(fDeinterleavingBuffer->inputBuffer(), fDeinterleavingBuffer->inputBufferSize(), afterGettingFrame, this, FramedSource::handleClosure, this); } } void AMRDeinterleaver::doStopGettingFrames() { fNeedAFrame = False; fInputSource->stopGettingFrames(); } void AMRDeinterleaver ::afterGettingFrame(void* clientData, unsigned frameSize, unsigned /*numTruncatedBytes*/, struct timeval presentationTime, unsigned /*durationInMicroseconds*/) { AMRDeinterleaver* deinterleaver = (AMRDeinterleaver*)clientData; deinterleaver->afterGettingFrame1(frameSize, presentationTime); } void AMRDeinterleaver ::afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) { RawAMRRTPSource* source = (RawAMRRTPSource*)fInputSource; // First, put the frame into our deinterleaving buffer: fDeinterleavingBuffer->deliverIncomingFrame(frameSize, source, presentationTime); // Then, try delivering a frame to the client (if he wants one): if (fNeedAFrame) doGetNextFrame(); } ////////// AMRDeinterleavingBuffer implementation ///////// AMRDeinterleavingBuffer ::AMRDeinterleavingBuffer(unsigned numChannels, unsigned maxInterleaveGroupSize) : fNumChannels(numChannels), fMaxInterleaveGroupSize(maxInterleaveGroupSize), fIncomingBankId(0), fIncomingBinMax(0), fOutgoingBinMax(0), fNextOutgoingBin(0), fHaveSeenPackets(False), fNumSuccessiveSyncedFrames(0), fILL(0) { // Use two banks of descriptors - one for incoming, one for outgoing fFrames[0] = new FrameDescriptor[fMaxInterleaveGroupSize]; fFrames[1] = new FrameDescriptor[fMaxInterleaveGroupSize]; fInputBuffer = createNewBuffer(); } AMRDeinterleavingBuffer::~AMRDeinterleavingBuffer() { delete[] fInputBuffer; delete[] fFrames[0]; delete[] fFrames[1]; } void AMRDeinterleavingBuffer ::deliverIncomingFrame(unsigned frameSize, RawAMRRTPSource* source, struct timeval presentationTime) { fILL = source->ILL(); unsigned char const ILP = source->ILP(); unsigned frameIndex = source->frameIndex(); unsigned short packetSeqNum = source->curPacketRTPSeqNum(); // First perform a sanity check on the parameters: // (This is overkill, as the source should have already done this.) if (ILP > fILL || frameIndex == 0) { #ifdef DEBUG fprintf(stderr, "AMRDeinterleavingBuffer::deliverIncomingFrame() param sanity check failed (%d,%d,%d,%d)\n", frameSize, fILL, ILP, frameIndex); #endif source->envir().internalError(); } --frameIndex; // because it was incremented by the source when this frame was read u_int8_t frameHeader; if (frameIndex >= source->TOCSize()) { // sanity check frameHeader = FT_NO_DATA<<3; } else { frameHeader = source->TOC()[frameIndex]; } unsigned frameBlockIndex = frameIndex/fNumChannels; unsigned frameWithinFrameBlock = frameIndex%fNumChannels; // The input "presentationTime" was that of the first frame-block in this // packet. Update it for the current frame: unsigned uSecIncrement = frameBlockIndex*(fILL+1)*uSecsPerFrame; presentationTime.tv_usec += uSecIncrement; presentationTime.tv_sec += presentationTime.tv_usec/1000000; presentationTime.tv_usec = presentationTime.tv_usec%1000000; // Next, check whether this packet is part of a new interleave group if (!fHaveSeenPackets || seqNumLT(fLastPacketSeqNumForGroup, packetSeqNum + frameBlockIndex)) { // We've moved to a new interleave group #ifdef DEBUG fprintf(stderr, "AMRDeinterleavingBuffer::deliverIncomingFrame(): new interleave group\n"); #endif fHaveSeenPackets = True; fLastPacketSeqNumForGroup = packetSeqNum + fILL - ILP; // Switch the incoming and outgoing banks: fIncomingBankId ^= 1; unsigned char tmp = fIncomingBinMax; fIncomingBinMax = fOutgoingBinMax; fOutgoingBinMax = tmp; fNextOutgoingBin = 0; } // Now move the incoming frame into the appropriate bin: unsigned const binNumber = ((ILP + frameBlockIndex*(fILL+1))*fNumChannels + frameWithinFrameBlock) % fMaxInterleaveGroupSize; // the % is for sanity #ifdef DEBUG fprintf(stderr, "AMRDeinterleavingBuffer::deliverIncomingFrame(): frameIndex %d (%d,%d) put in bank %d, bin %d (%d): size %d, header 0x%02x, presentationTime %lu.%06ld\n", frameIndex, frameBlockIndex, frameWithinFrameBlock, fIncomingBankId, binNumber, fMaxInterleaveGroupSize, frameSize, frameHeader, presentationTime.tv_sec, presentationTime.tv_usec); #endif FrameDescriptor& inBin = fFrames[fIncomingBankId][binNumber]; unsigned char* curBuffer = inBin.frameData; inBin.frameData = fInputBuffer; inBin.frameSize = frameSize; inBin.frameHeader = frameHeader; inBin.presentationTime = presentationTime; inBin.fIsSynchronized = ((RTPSource*)source)->RTPSource::hasBeenSynchronizedUsingRTCP(); if (curBuffer == NULL) curBuffer = createNewBuffer(); fInputBuffer = curBuffer; if (binNumber >= fIncomingBinMax) { fIncomingBinMax = binNumber + 1; } } Boolean AMRDeinterleavingBuffer ::retrieveFrame(unsigned char* to, unsigned maxSize, unsigned& resultFrameSize, unsigned& resultNumTruncatedBytes, u_int8_t& resultFrameHeader, struct timeval& resultPresentationTime, Boolean& resultIsSynchronized) { if (fNextOutgoingBin >= fOutgoingBinMax) return False; // none left FrameDescriptor& outBin = fFrames[fIncomingBankId^1][fNextOutgoingBin]; unsigned char* fromPtr = outBin.frameData; unsigned char fromSize = outBin.frameSize; outBin.frameSize = 0; // for the next time this bin is used resultIsSynchronized = False; // by default; can be changed by: if (outBin.fIsSynchronized) { // Don't consider the outgoing frame to be synchronized until we've received at least a complete interleave cycle of // synchronized frames. This ensures that the receiver will be getting all synchronized frames from now on. if (++fNumSuccessiveSyncedFrames > fILL) { resultIsSynchronized = True; fNumSuccessiveSyncedFrames = fILL+1; // prevents overflow } } else { fNumSuccessiveSyncedFrames = 0; } // Check whether this frame is missing; if so, return a FT_NO_DATA frame: if (fromSize == 0) { resultFrameHeader = FT_NO_DATA<<3; // Compute this erasure frame's presentation time via extrapolation: resultPresentationTime = fLastRetrievedPresentationTime; resultPresentationTime.tv_usec += uSecsPerFrame; if (resultPresentationTime.tv_usec >= 1000000) { ++resultPresentationTime.tv_sec; resultPresentationTime.tv_usec -= 1000000; } } else { // Normal case - a frame exists: resultFrameHeader = outBin.frameHeader; resultPresentationTime = outBin.presentationTime; } fLastRetrievedPresentationTime = resultPresentationTime; if (fromSize > maxSize) { resultNumTruncatedBytes = fromSize - maxSize; resultFrameSize = maxSize; } else { resultNumTruncatedBytes = 0; resultFrameSize = fromSize; } memmove(to, fromPtr, resultFrameSize); #ifdef DEBUG fprintf(stderr, "AMRDeinterleavingBuffer::retrieveFrame(): from bank %d, bin %d: size %d, header 0x%02x, presentationTime %lu.%06ld\n", fIncomingBankId^1, fNextOutgoingBin, resultFrameSize, resultFrameHeader, resultPresentationTime.tv_sec, resultPresentationTime.tv_usec); #endif ++fNextOutgoingBin; return True; } unsigned char* AMRDeinterleavingBuffer::createNewBuffer() { return new unsigned char[inputBufferSize()]; } AMRDeinterleavingBuffer::FrameDescriptor::FrameDescriptor() : frameSize(0), frameData(NULL) { } AMRDeinterleavingBuffer::FrameDescriptor::~FrameDescriptor() { delete[] frameData; } // Unpack bandwidth-aligned data to octet-aligned: static unsigned short const frameBitsFromFT[16] = { 95, 103, 118, 134, 148, 159, 204, 244, 39, 0, 0, 0, 0, 0, 0, 0 }; static unsigned short const frameBitsFromFTWideband[16] = { 132, 177, 253, 285, 317, 365, 397, 461, 477, 40, 0, 0, 0, 0, 0, 0 }; static void unpackBandwidthEfficientData(BufferedPacket* packet, Boolean isWideband) { #ifdef DEBUG fprintf(stderr, "Unpacking 'bandwidth-efficient' payload (%d bytes):\n", packet->dataSize()); for (unsigned j = 0; j < packet->dataSize(); ++j) { fprintf(stderr, "%02x:", (packet->data())[j]); } fprintf(stderr, "\n"); #endif BitVector fromBV(packet->data(), 0, 8*packet->dataSize()); unsigned const toBufferSize = 2*packet->dataSize(); // conservatively large unsigned char* toBuffer = new unsigned char[toBufferSize]; unsigned toCount = 0; // Begin with the payload header: unsigned CMR = fromBV.getBits(4); toBuffer[toCount++] = CMR << 4; // Then, run through and unpack the TOC entries: while (1) { unsigned toc = fromBV.getBits(6); toBuffer[toCount++] = toc << 2; if ((toc&0x20) == 0) break; // the F bit is 0 } // Then, using the TOC data, unpack each frame payload: unsigned const tocSize = toCount - 1; for (unsigned i = 1; i <= tocSize; ++i) { unsigned char tocByte = toBuffer[i]; unsigned char const FT = (tocByte&0x78) >> 3; unsigned short frameSizeBits = isWideband ? frameBitsFromFTWideband[FT] : frameBitsFromFT[FT]; unsigned short frameSizeBytes = (frameSizeBits+7)/8; if (frameSizeBits > fromBV.numBitsRemaining()) { #ifdef DEBUG fprintf(stderr, "\tWarning: Unpacking frame %d of %d: want %d bits, but only %d are available!\n", i, tocSize, frameSizeBits, fromBV.numBitsRemaining()); #endif break; } shiftBits(&toBuffer[toCount], 0, // to packet->data(), fromBV.curBitIndex(), // from frameSizeBits // num bits ); fromBV.skipBits(frameSizeBits); toCount += frameSizeBytes; } #ifdef DEBUG if (fromBV.numBitsRemaining() > 7) { fprintf(stderr, "\tWarning: %d bits remain unused!\n", fromBV.numBitsRemaining()); } #endif // Finally, replace the current packet data with the unpacked data: packet->removePadding(packet->dataSize()); // throws away current packet data packet->appendData(toBuffer, toCount); delete[] toBuffer; }