📄 amraudiortpsource.cpp
字号:
/**********This library is free software; you can redistribute it and/or modify it underthe terms of the GNU Lesser General Public License as published by theFree Software Foundation; either version 2.1 of the License, or (at youroption) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)This library is distributed in the hope that it will be useful, but WITHOUTANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESSFOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License formore details.You should have received a copy of the GNU Lesser General Public Licensealong with this library; if not, write to the Free Software Foundation, Inc.,59 Temple Place, Suite 330, Boston, MA 02111-1307 USA**********/// "liveMedia"// Copyright (c) 1996-2004 Live Networks, Inc. All rights reserved.// AMR Audio RTP Sources (RFC 3267)// Implementation#include "AMRAudioRTPSource.hh"#include "MultiFramedRTPSource.hh"#include "BitVector.hh"#include <string.h>#include <stdlib.h>// 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 pktprivate: 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 processSpecialHeader(BufferedPacket* packet, unsigned& resultSpecialHeaderSize); virtual char const* MIMEtype() const; virtual Boolean hasBeenSynchronizedUsingRTCP();private: Boolean fIsWideband, fIsOctetAligned, fIsInterleaved, fCRCsArePresent; unsigned char fILL, fILP; unsigned fTOCSize; unsigned char* fTOC; unsigned fFrameIndex, fNumSuccessiveSyncedPackets;};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();private: FramedSource* 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), fNumSuccessiveSyncedPackets(0) {}RawAMRRTPSource::~RawAMRRTPSource() { delete[] fTOC;}#define FT_SPEECH_LOST 14#define FT_NO_DATA 15static void unpackBandwidthEfficientData(BufferedPacket* packet, Boolean isWideband); // forwardBoolean 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(); // First, check whether this packet's RTP timestamp is synchronized: if (RTPSource::hasBeenSynchronizedUsingRTCP()) { ++fNumSuccessiveSyncedPackets; } else { fNumSuccessiveSyncedPackets = 0; } // 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-WB";}Boolean RawAMRRTPSource::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)(fILL+1)) { fNumSuccessiveSyncedPackets = fILL + 2; // prevents overflow return True; } return False;}///// AMRBufferedPacket and AMRBufferedPacketFactory implementationAMRBufferedPacket::AMRBufferedPacket(RawAMRRTPSource& ourSource) : fOurSource(ourSource) {}AMRBufferedPacket::~AMRBufferedPacket() {}// The mapping from the "FT" field to frame size.// Values of 65535 are invalid.#define FT_INVALID 65535static unsigned short 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 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -