| /********** |
| 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 <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-2015 Live Networks, Inc. All rights reserved. |
| // Qualcomm "PureVoice" (aka. "QCELP") Audio RTP Sources |
| // Implementation |
| |
| #include "QCELPAudioRTPSource.hh" |
| #include "MultiFramedRTPSource.hh" |
| #include "FramedFilter.hh" |
| #include <string.h> |
| #include <stdlib.h> |
| |
| // This source is implemented internally by two separate sources: |
| // (i) a RTP source for the raw (interleaved) QCELP frames, and |
| // (ii) a deinterleaving filter that reads from this. |
| // Define these two new classes here: |
| |
| class RawQCELPRTPSource: public MultiFramedRTPSource { |
| public: |
| static RawQCELPRTPSource* createNew(UsageEnvironment& env, |
| Groupsock* RTPgs, |
| unsigned char rtpPayloadFormat, |
| unsigned rtpTimestampFrequency); |
| |
| unsigned char interleaveL() const { return fInterleaveL; } |
| unsigned char interleaveN() const { return fInterleaveN; } |
| unsigned char& frameIndex() { return fFrameIndex; } // index within pkt |
| |
| private: |
| RawQCELPRTPSource(UsageEnvironment& env, Groupsock* RTPgs, |
| unsigned char rtpPayloadFormat, |
| unsigned rtpTimestampFrequency); |
| // called only by createNew() |
| |
| virtual ~RawQCELPRTPSource(); |
| |
| private: |
| // redefined virtual functions: |
| virtual Boolean processSpecialHeader(BufferedPacket* packet, |
| unsigned& resultSpecialHeaderSize); |
| virtual char const* MIMEtype() const; |
| |
| virtual Boolean hasBeenSynchronizedUsingRTCP(); |
| |
| private: |
| unsigned char fInterleaveL, fInterleaveN, fFrameIndex; |
| unsigned fNumSuccessiveSyncedPackets; |
| }; |
| |
| class QCELPDeinterleaver: public FramedFilter { |
| public: |
| static QCELPDeinterleaver* createNew(UsageEnvironment& env, |
| RawQCELPRTPSource* inputSource); |
| |
| private: |
| QCELPDeinterleaver(UsageEnvironment& env, |
| RawQCELPRTPSource* inputSource); |
| // called only by "createNew()" |
| |
| virtual ~QCELPDeinterleaver(); |
| |
| 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: |
| class QCELPDeinterleavingBuffer* fDeinterleavingBuffer; |
| Boolean fNeedAFrame; |
| }; |
| |
| |
| ////////// QCELPAudioRTPSource implementation ////////// |
| |
| FramedSource* |
| QCELPAudioRTPSource::createNew(UsageEnvironment& env, |
| Groupsock* RTPgs, |
| RTPSource*& resultRTPSource, |
| unsigned char rtpPayloadFormat, |
| unsigned rtpTimestampFrequency) { |
| RawQCELPRTPSource* rawRTPSource; |
| resultRTPSource = rawRTPSource |
| = RawQCELPRTPSource::createNew(env, RTPgs, rtpPayloadFormat, |
| rtpTimestampFrequency); |
| if (resultRTPSource == NULL) return NULL; |
| |
| QCELPDeinterleaver* deinterleaver |
| = QCELPDeinterleaver::createNew(env, rawRTPSource); |
| if (deinterleaver == NULL) { |
| Medium::close(resultRTPSource); |
| resultRTPSource = NULL; |
| } |
| |
| return deinterleaver; |
| } |
| |
| |
| ////////// QCELPBufferedPacket and QCELPBufferedPacketFactory ////////// |
| |
| // A subclass of BufferedPacket, used to separate out QCELP frames. |
| |
| class QCELPBufferedPacket: public BufferedPacket { |
| public: |
| QCELPBufferedPacket(RawQCELPRTPSource& ourSource); |
| virtual ~QCELPBufferedPacket(); |
| |
| private: // redefined virtual functions |
| virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr, |
| unsigned dataSize); |
| private: |
| RawQCELPRTPSource& fOurSource; |
| }; |
| |
| class QCELPBufferedPacketFactory: public BufferedPacketFactory { |
| private: // redefined virtual functions |
| virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource); |
| }; |
| |
| |
| ///////// RawQCELPRTPSource implementation //////// |
| |
| RawQCELPRTPSource* |
| RawQCELPRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs, |
| unsigned char rtpPayloadFormat, |
| unsigned rtpTimestampFrequency) { |
| return new RawQCELPRTPSource(env, RTPgs, rtpPayloadFormat, |
| rtpTimestampFrequency); |
| } |
| |
| RawQCELPRTPSource::RawQCELPRTPSource(UsageEnvironment& env, |
| Groupsock* RTPgs, |
| unsigned char rtpPayloadFormat, |
| unsigned rtpTimestampFrequency) |
| : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, |
| rtpTimestampFrequency, |
| new QCELPBufferedPacketFactory), |
| fInterleaveL(0), fInterleaveN(0), fFrameIndex(0), |
| fNumSuccessiveSyncedPackets(0) { |
| } |
| |
| RawQCELPRTPSource::~RawQCELPRTPSource() { |
| } |
| |
| Boolean RawQCELPRTPSource |
| ::processSpecialHeader(BufferedPacket* packet, |
| unsigned& resultSpecialHeaderSize) { |
| unsigned char* headerStart = packet->data(); |
| unsigned packetSize = packet->dataSize(); |
| |
| // First, check whether this packet's RTP timestamp is synchronized: |
| if (RTPSource::hasBeenSynchronizedUsingRTCP()) { |
| ++fNumSuccessiveSyncedPackets; |
| } else { |
| fNumSuccessiveSyncedPackets = 0; |
| } |
| |
| // There's a 1-byte header indicating the interleave parameters |
| if (packetSize < 1) return False; |
| |
| // Get the interleaving parameters from the 1-byte header, |
| // and check them for validity: |
| unsigned char const firstByte = headerStart[0]; |
| unsigned char const interleaveL = (firstByte&0x38)>>3; |
| unsigned char const interleaveN = firstByte&0x07; |
| #ifdef DEBUG |
| fprintf(stderr, "packetSize: %d, interleaveL: %d, interleaveN: %d\n", packetSize, interleaveL, interleaveN); |
| #endif |
| if (interleaveL > 5 || interleaveN > interleaveL) return False; //invalid |
| |
| fInterleaveL = interleaveL; |
| fInterleaveN = interleaveN; |
| fFrameIndex = 0; // initially |
| |
| resultSpecialHeaderSize = 1; |
| return True; |
| } |
| |
| char const* RawQCELPRTPSource::MIMEtype() const { |
| return "audio/QCELP"; |
| } |
| |
| Boolean RawQCELPRTPSource::hasBeenSynchronizedUsingRTCP() { |
| // Don't report ourselves as being synchronized until we've received |
| // at least a complete interleave cycle of synchronized packets. |
| // This ensures that the receiver is currently getting a frame from |
| // a packet that was synchronized. |
| if (fNumSuccessiveSyncedPackets > (unsigned)(fInterleaveL+1)) { |
| fNumSuccessiveSyncedPackets = fInterleaveL+2; // prevents overflow |
| return True; |
| } |
| return False; |
| } |
| |
| |
| ///// QCELPBufferedPacket and QCELPBufferedPacketFactory implementation |
| |
| QCELPBufferedPacket::QCELPBufferedPacket(RawQCELPRTPSource& ourSource) |
| : fOurSource(ourSource) { |
| } |
| |
| QCELPBufferedPacket::~QCELPBufferedPacket() { |
| } |
| |
| unsigned QCELPBufferedPacket:: |
| nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) { |
| // The size of the QCELP frame is determined by the first byte: |
| if (dataSize == 0) return 0; // sanity check |
| unsigned char const firstByte = framePtr[0]; |
| |
| unsigned frameSize; |
| switch (firstByte) { |
| case 0: { frameSize = 1; break; } |
| case 1: { frameSize = 4; break; } |
| case 2: { frameSize = 8; break; } |
| case 3: { frameSize = 17; break; } |
| case 4: { frameSize = 35; break; } |
| default: { frameSize = 0; break; } |
| } |
| |
| #ifdef DEBUG |
| fprintf(stderr, "QCELPBufferedPacket::nextEnclosedFrameSize(): frameSize: %d, dataSize: %d\n", frameSize, dataSize); |
| #endif |
| if (dataSize < frameSize) return 0; |
| |
| ++fOurSource.frameIndex(); |
| return frameSize; |
| } |
| |
| BufferedPacket* QCELPBufferedPacketFactory |
| ::createNewPacket(MultiFramedRTPSource* ourSource) { |
| return new QCELPBufferedPacket((RawQCELPRTPSource&)(*ourSource)); |
| } |
| |
| ///////// QCELPDeinterleavingBuffer ///////// |
| // (used to implement QCELPDeinterleaver) |
| |
| #define QCELP_MAX_FRAME_SIZE 35 |
| #define QCELP_MAX_INTERLEAVE_L 5 |
| #define QCELP_MAX_FRAMES_PER_PACKET 10 |
| #define QCELP_MAX_INTERLEAVE_GROUP_SIZE \ |
| ((QCELP_MAX_INTERLEAVE_L+1)*QCELP_MAX_FRAMES_PER_PACKET) |
| |
| class QCELPDeinterleavingBuffer { |
| public: |
| QCELPDeinterleavingBuffer(); |
| virtual ~QCELPDeinterleavingBuffer(); |
| |
| void deliverIncomingFrame(unsigned frameSize, |
| unsigned char interleaveL, |
| unsigned char interleaveN, |
| unsigned char frameIndex, |
| unsigned short packetSeqNum, |
| struct timeval presentationTime); |
| Boolean retrieveFrame(unsigned char* to, unsigned maxSize, |
| unsigned& resultFrameSize, unsigned& resultNumTruncatedBytes, |
| struct timeval& resultPresentationTime); |
| |
| unsigned char* inputBuffer() { return fInputBuffer; } |
| unsigned inputBufferSize() const { return QCELP_MAX_FRAME_SIZE; } |
| |
| private: |
| class FrameDescriptor { |
| public: |
| FrameDescriptor(); |
| virtual ~FrameDescriptor(); |
| |
| unsigned frameSize; |
| unsigned char* frameData; |
| struct timeval presentationTime; |
| }; |
| |
| // Use two banks of descriptors - one for incoming, one for outgoing |
| FrameDescriptor fFrames[QCELP_MAX_INTERLEAVE_GROUP_SIZE][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; |
| }; |
| |
| |
| ////////// QCELPDeinterleaver implementation ///////// |
| |
| QCELPDeinterleaver* |
| QCELPDeinterleaver::createNew(UsageEnvironment& env, |
| RawQCELPRTPSource* inputSource) { |
| return new QCELPDeinterleaver(env, inputSource); |
| } |
| |
| QCELPDeinterleaver::QCELPDeinterleaver(UsageEnvironment& env, |
| RawQCELPRTPSource* inputSource) |
| : FramedFilter(env, inputSource), |
| fNeedAFrame(False) { |
| fDeinterleavingBuffer = new QCELPDeinterleavingBuffer(); |
| } |
| |
| QCELPDeinterleaver::~QCELPDeinterleaver() { |
| delete fDeinterleavingBuffer; |
| } |
| |
| static unsigned const uSecsPerFrame = 20000; // 20 ms |
| |
| void QCELPDeinterleaver::doGetNextFrame() { |
| // First, try getting a frame from the deinterleaving buffer: |
| if (fDeinterleavingBuffer->retrieveFrame(fTo, fMaxSize, |
| fFrameSize, fNumTruncatedBytes, |
| fPresentationTime)) { |
| // 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 QCELPDeinterleaver::doStopGettingFrames() { |
| fNeedAFrame = False; |
| fInputSource->stopGettingFrames(); |
| } |
| |
| void QCELPDeinterleaver |
| ::afterGettingFrame(void* clientData, unsigned frameSize, |
| unsigned /*numTruncatedBytes*/, |
| struct timeval presentationTime, |
| unsigned /*durationInMicroseconds*/) { |
| QCELPDeinterleaver* deinterleaver = (QCELPDeinterleaver*)clientData; |
| deinterleaver->afterGettingFrame1(frameSize, presentationTime); |
| } |
| |
| void QCELPDeinterleaver |
| ::afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) { |
| RawQCELPRTPSource* source = (RawQCELPRTPSource*)fInputSource; |
| |
| // First, put the frame into our deinterleaving buffer: |
| fDeinterleavingBuffer |
| ->deliverIncomingFrame(frameSize, source->interleaveL(), |
| source->interleaveN(), source->frameIndex(), |
| source->curPacketRTPSeqNum(), |
| presentationTime); |
| |
| // Then, try delivering a frame to the client (if he wants one): |
| if (fNeedAFrame) doGetNextFrame(); |
| } |
| |
| |
| ////////// QCELPDeinterleavingBuffer implementation ///////// |
| |
| QCELPDeinterleavingBuffer::QCELPDeinterleavingBuffer() |
| : fIncomingBankId(0), fIncomingBinMax(0), |
| fOutgoingBinMax(0), fNextOutgoingBin(0), |
| fHaveSeenPackets(False) { |
| fInputBuffer = new unsigned char[QCELP_MAX_FRAME_SIZE]; |
| } |
| |
| QCELPDeinterleavingBuffer::~QCELPDeinterleavingBuffer() { |
| delete[] fInputBuffer; |
| } |
| |
| void QCELPDeinterleavingBuffer |
| ::deliverIncomingFrame(unsigned frameSize, |
| unsigned char interleaveL, |
| unsigned char interleaveN, |
| unsigned char frameIndex, |
| unsigned short packetSeqNum, |
| struct timeval presentationTime) { |
| // First perform a sanity check on the parameters: |
| // (This is overkill, as the source should have already done this.) |
| if (frameSize > QCELP_MAX_FRAME_SIZE |
| || interleaveL > QCELP_MAX_INTERLEAVE_L || interleaveN > interleaveL |
| || frameIndex == 0 || frameIndex > QCELP_MAX_FRAMES_PER_PACKET) { |
| #ifdef DEBUG |
| fprintf(stderr, "QCELPDeinterleavingBuffer::deliverIncomingFrame() param sanity check failed (%d,%d,%d,%d)\n", frameSize, interleaveL, interleaveN, frameIndex); |
| #endif |
| return; |
| } |
| |
| // The input "presentationTime" was that of the first frame in this |
| // packet. Update it for the current frame: |
| unsigned uSecIncrement = (frameIndex-1)*(interleaveL+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)) { |
| // We've moved to a new interleave group |
| fHaveSeenPackets = True; |
| fLastPacketSeqNumForGroup = packetSeqNum + interleaveL - interleaveN; |
| |
| // 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 |
| = interleaveN + (frameIndex-1)*(interleaveL+1); |
| FrameDescriptor& inBin = fFrames[binNumber][fIncomingBankId]; |
| unsigned char* curBuffer = inBin.frameData; |
| inBin.frameData = fInputBuffer; |
| inBin.frameSize = frameSize; |
| inBin.presentationTime = presentationTime; |
| |
| if (curBuffer == NULL) curBuffer = new unsigned char[QCELP_MAX_FRAME_SIZE]; |
| fInputBuffer = curBuffer; |
| |
| if (binNumber >= fIncomingBinMax) { |
| fIncomingBinMax = binNumber + 1; |
| } |
| } |
| |
| Boolean QCELPDeinterleavingBuffer |
| ::retrieveFrame(unsigned char* to, unsigned maxSize, |
| unsigned& resultFrameSize, unsigned& resultNumTruncatedBytes, |
| struct timeval& resultPresentationTime) { |
| if (fNextOutgoingBin >= fOutgoingBinMax) return False; // none left |
| |
| FrameDescriptor& outBin = fFrames[fNextOutgoingBin][fIncomingBankId^1]; |
| unsigned char* fromPtr; |
| unsigned char fromSize = outBin.frameSize; |
| outBin.frameSize = 0; // for the next time this bin is used |
| |
| // Check whether this frame is missing; if so, return an 'erasure' frame: |
| unsigned char erasure = 14; |
| if (fromSize == 0) { |
| fromPtr = &erasure; |
| fromSize = 1; |
| |
| // 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: |
| fromPtr = outBin.frameData; |
| resultPresentationTime = outBin.presentationTime; |
| } |
| |
| fLastRetrievedPresentationTime = resultPresentationTime; |
| |
| if (fromSize > maxSize) { |
| resultNumTruncatedBytes = fromSize - maxSize; |
| resultFrameSize = maxSize; |
| } else { |
| resultNumTruncatedBytes = 0; |
| resultFrameSize = fromSize; |
| } |
| memmove(to, fromPtr, resultFrameSize); |
| |
| ++fNextOutgoingBin; |
| return True; |
| } |
| |
| QCELPDeinterleavingBuffer::FrameDescriptor::FrameDescriptor() |
| : frameSize(0), frameData(NULL) { |
| } |
| |
| QCELPDeinterleavingBuffer::FrameDescriptor::~FrameDescriptor() { |
| delete[] frameData; |
| } |