/********** 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. // 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; shiftBits(&toBuffer[toCount], 0, // to packet->data(), fromBV.curBitIndex(), // from frameSizeBits // num bits ); #ifdef DEBUG if (frameSizeBits > fromBV.numBitsRemaining()) { fprintf(stderr, "\tWarning: Unpacking frame %d of %d: want %d bits, but only %d are available!\n", i, tocSize, frameSizeBits, fromBV.numBitsRemaining()); } #endif 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; }