235 lines
7.8 KiB
C++
235 lines
7.8 KiB
C++
|
/**********
|
||
|
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.
|
||
|
// MPEG4-GENERIC ("audio", "video", or "application") RTP stream sources
|
||
|
// Implementation
|
||
|
|
||
|
#include "MPEG4GenericRTPSource.hh"
|
||
|
#include "BitVector.hh"
|
||
|
#include "MPEG4LATMAudioRTPSource.hh" // for parseGeneralConfigStr()
|
||
|
|
||
|
////////// MPEG4GenericBufferedPacket and MPEG4GenericBufferedPacketFactory
|
||
|
|
||
|
class MPEG4GenericBufferedPacket: public BufferedPacket {
|
||
|
public:
|
||
|
MPEG4GenericBufferedPacket(MPEG4GenericRTPSource* ourSource);
|
||
|
virtual ~MPEG4GenericBufferedPacket();
|
||
|
|
||
|
private: // redefined virtual functions
|
||
|
virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr,
|
||
|
unsigned dataSize);
|
||
|
private:
|
||
|
MPEG4GenericRTPSource* fOurSource;
|
||
|
};
|
||
|
|
||
|
class MPEG4GenericBufferedPacketFactory: public BufferedPacketFactory {
|
||
|
private: // redefined virtual functions
|
||
|
virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
|
||
|
};
|
||
|
|
||
|
|
||
|
////////// AUHeader //////////
|
||
|
struct AUHeader {
|
||
|
unsigned size;
|
||
|
unsigned index; // indexDelta for the 2nd & subsequent headers
|
||
|
};
|
||
|
|
||
|
|
||
|
///////// MPEG4GenericRTPSource implementation ////////
|
||
|
|
||
|
//##### NOTE: INCOMPLETE!!! Support more modes, and interleaving #####
|
||
|
|
||
|
MPEG4GenericRTPSource*
|
||
|
MPEG4GenericRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
|
||
|
unsigned char rtpPayloadFormat,
|
||
|
unsigned rtpTimestampFrequency,
|
||
|
char const* mediumName,
|
||
|
char const* mode,
|
||
|
unsigned sizeLength, unsigned indexLength,
|
||
|
unsigned indexDeltaLength
|
||
|
) {
|
||
|
return new MPEG4GenericRTPSource(env, RTPgs, rtpPayloadFormat,
|
||
|
rtpTimestampFrequency, mediumName,
|
||
|
mode, sizeLength, indexLength,
|
||
|
indexDeltaLength
|
||
|
);
|
||
|
}
|
||
|
|
||
|
MPEG4GenericRTPSource
|
||
|
::MPEG4GenericRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
|
||
|
unsigned char rtpPayloadFormat,
|
||
|
unsigned rtpTimestampFrequency,
|
||
|
char const* mediumName,
|
||
|
char const* mode,
|
||
|
unsigned sizeLength, unsigned indexLength,
|
||
|
unsigned indexDeltaLength
|
||
|
)
|
||
|
: MultiFramedRTPSource(env, RTPgs,
|
||
|
rtpPayloadFormat, rtpTimestampFrequency,
|
||
|
new MPEG4GenericBufferedPacketFactory),
|
||
|
fSizeLength(sizeLength), fIndexLength(indexLength),
|
||
|
fIndexDeltaLength(indexDeltaLength),
|
||
|
fNumAUHeaders(0), fNextAUHeader(0), fAUHeaders(NULL) {
|
||
|
unsigned mimeTypeLength =
|
||
|
strlen(mediumName) + 14 /* strlen("/MPEG4-GENERIC") */ + 1;
|
||
|
fMIMEType = new char[mimeTypeLength];
|
||
|
if (fMIMEType != NULL) {
|
||
|
sprintf(fMIMEType, "%s/MPEG4-GENERIC", mediumName);
|
||
|
}
|
||
|
|
||
|
fMode = strDup(mode);
|
||
|
// Check for a "mode" that we don't yet support: //#####
|
||
|
if (mode == NULL ||
|
||
|
(strcmp(mode, "aac-hbr") != 0 && strcmp(mode, "generic") != 0)) {
|
||
|
envir() << "MPEG4GenericRTPSource Warning: Unknown or unsupported \"mode\": "
|
||
|
<< mode << "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MPEG4GenericRTPSource::~MPEG4GenericRTPSource() {
|
||
|
delete[] fAUHeaders;
|
||
|
delete[] fMode;
|
||
|
delete[] fMIMEType;
|
||
|
}
|
||
|
|
||
|
Boolean MPEG4GenericRTPSource
|
||
|
::processSpecialHeader(BufferedPacket* packet,
|
||
|
unsigned& resultSpecialHeaderSize) {
|
||
|
unsigned char* headerStart = packet->data();
|
||
|
unsigned packetSize = packet->dataSize();
|
||
|
|
||
|
fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame;
|
||
|
// whether the *previous* packet ended a frame
|
||
|
|
||
|
// The RTP "M" (marker) bit indicates the last fragment of a frame:
|
||
|
fCurrentPacketCompletesFrame = packet->rtpMarkerBit();
|
||
|
|
||
|
// default values:
|
||
|
resultSpecialHeaderSize = 0;
|
||
|
fNumAUHeaders = 0;
|
||
|
fNextAUHeader = 0;
|
||
|
delete[] fAUHeaders; fAUHeaders = NULL;
|
||
|
|
||
|
if (fSizeLength > 0) {
|
||
|
// The packet begins with a "AU Header Section". Parse it, to
|
||
|
// determine the "AU-header"s for each frame present in this packet:
|
||
|
resultSpecialHeaderSize += 2;
|
||
|
if (packetSize < resultSpecialHeaderSize) return False;
|
||
|
|
||
|
unsigned AU_headers_length = (headerStart[0]<<8)|headerStart[1];
|
||
|
unsigned AU_headers_length_bytes = (AU_headers_length+7)/8;
|
||
|
if (packetSize
|
||
|
< resultSpecialHeaderSize + AU_headers_length_bytes) return False;
|
||
|
resultSpecialHeaderSize += AU_headers_length_bytes;
|
||
|
|
||
|
// Figure out how many AU-headers are present in the packet:
|
||
|
int bitsAvail = AU_headers_length - (fSizeLength + fIndexLength);
|
||
|
if (bitsAvail >= 0 && (fSizeLength + fIndexDeltaLength) > 0) {
|
||
|
fNumAUHeaders = 1 + bitsAvail/(fSizeLength + fIndexDeltaLength);
|
||
|
}
|
||
|
if (fNumAUHeaders > 0) {
|
||
|
fAUHeaders = new AUHeader[fNumAUHeaders];
|
||
|
// Fill in each header:
|
||
|
BitVector bv(&headerStart[2], 0, AU_headers_length);
|
||
|
fAUHeaders[0].size = bv.getBits(fSizeLength);
|
||
|
fAUHeaders[0].index = bv.getBits(fIndexLength);
|
||
|
|
||
|
for (unsigned i = 1; i < fNumAUHeaders; ++i) {
|
||
|
fAUHeaders[i].size = bv.getBits(fSizeLength);
|
||
|
fAUHeaders[i].index = bv.getBits(fIndexDeltaLength);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return True;
|
||
|
}
|
||
|
|
||
|
char const* MPEG4GenericRTPSource::MIMEtype() const {
|
||
|
return fMIMEType;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////// MPEG4GenericBufferedPacket
|
||
|
////////// and MPEG4GenericBufferedPacketFactory implementation
|
||
|
|
||
|
MPEG4GenericBufferedPacket
|
||
|
::MPEG4GenericBufferedPacket(MPEG4GenericRTPSource* ourSource)
|
||
|
: fOurSource(ourSource) {
|
||
|
}
|
||
|
|
||
|
MPEG4GenericBufferedPacket::~MPEG4GenericBufferedPacket() {
|
||
|
}
|
||
|
|
||
|
unsigned MPEG4GenericBufferedPacket
|
||
|
::nextEnclosedFrameSize(unsigned char*& /*framePtr*/, unsigned dataSize) {
|
||
|
// WE CURRENTLY DON'T IMPLEMENT INTERLEAVING. FIX THIS! #####
|
||
|
AUHeader* auHeader = fOurSource->fAUHeaders;
|
||
|
if (auHeader == NULL) return dataSize;
|
||
|
unsigned numAUHeaders = fOurSource->fNumAUHeaders;
|
||
|
|
||
|
if (fOurSource->fNextAUHeader >= numAUHeaders) {
|
||
|
fOurSource->envir() << "MPEG4GenericBufferedPacket::nextEnclosedFrameSize("
|
||
|
<< dataSize << "): data error ("
|
||
|
<< auHeader << "," << fOurSource->fNextAUHeader
|
||
|
<< "," << numAUHeaders << ")!\n";
|
||
|
return dataSize;
|
||
|
}
|
||
|
|
||
|
auHeader = &auHeader[fOurSource->fNextAUHeader++];
|
||
|
return auHeader->size <= dataSize ? auHeader->size : dataSize;
|
||
|
}
|
||
|
|
||
|
BufferedPacket* MPEG4GenericBufferedPacketFactory
|
||
|
::createNewPacket(MultiFramedRTPSource* ourSource) {
|
||
|
return new MPEG4GenericBufferedPacket((MPEG4GenericRTPSource*)ourSource);
|
||
|
}
|
||
|
|
||
|
|
||
|
////////// samplingFrequencyFromAudioSpecificConfig() implementation //////////
|
||
|
|
||
|
static unsigned const samplingFrequencyFromIndex[16] = {
|
||
|
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
|
||
|
16000, 12000, 11025, 8000, 7350, 0, 0, 0
|
||
|
};
|
||
|
|
||
|
unsigned samplingFrequencyFromAudioSpecificConfig(char const* configStr) {
|
||
|
unsigned char* config = NULL;
|
||
|
unsigned result = 0; // if returned, indicates an error
|
||
|
|
||
|
do {
|
||
|
// Begin by parsing the config string:
|
||
|
unsigned configSize;
|
||
|
config = parseGeneralConfigStr(configStr, configSize);
|
||
|
if (config == NULL) break;
|
||
|
|
||
|
if (configSize < 2) break;
|
||
|
unsigned char samplingFrequencyIndex = ((config[0]&0x07)<<1) | (config[1]>>7);
|
||
|
if (samplingFrequencyIndex < 15) {
|
||
|
result = samplingFrequencyFromIndex[samplingFrequencyIndex];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Index == 15 means that the actual frequency is next (24 bits):
|
||
|
if (configSize < 5) break;
|
||
|
result = ((config[1]&0x7F)<<17) | (config[2]<<9) | (config[3]<<1) | (config[4]>>7);
|
||
|
} while (0);
|
||
|
|
||
|
delete[] config;
|
||
|
return result;
|
||
|
}
|