505 lines
16 KiB
C++
Executable File
505 lines
16 KiB
C++
Executable File
/**********
|
|
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-2016 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;
|
|
}
|