265 lines
7.8 KiB
C++
Executable File
265 lines
7.8 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.
|
|
// MPEG-4 audio, using LATM multiplexing
|
|
// Implementation
|
|
|
|
#include "MPEG4LATMAudioRTPSource.hh"
|
|
|
|
////////// LATMBufferedPacket and LATMBufferedPacketFactory //////////
|
|
|
|
class LATMBufferedPacket: public BufferedPacket {
|
|
public:
|
|
LATMBufferedPacket(Boolean includeLATMDataLengthField);
|
|
virtual ~LATMBufferedPacket();
|
|
|
|
private: // redefined virtual functions
|
|
virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr,
|
|
unsigned dataSize);
|
|
|
|
private:
|
|
Boolean fIncludeLATMDataLengthField;
|
|
};
|
|
|
|
class LATMBufferedPacketFactory: public BufferedPacketFactory {
|
|
private: // redefined virtual functions
|
|
virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
|
|
};
|
|
|
|
///////// MPEG4LATMAudioRTPSource implementation ////////
|
|
|
|
MPEG4LATMAudioRTPSource*
|
|
MPEG4LATMAudioRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
|
|
unsigned char rtpPayloadFormat,
|
|
unsigned rtpTimestampFrequency) {
|
|
return new MPEG4LATMAudioRTPSource(env, RTPgs, rtpPayloadFormat,
|
|
rtpTimestampFrequency);
|
|
}
|
|
|
|
MPEG4LATMAudioRTPSource
|
|
::MPEG4LATMAudioRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
|
|
unsigned char rtpPayloadFormat,
|
|
unsigned rtpTimestampFrequency)
|
|
: MultiFramedRTPSource(env, RTPgs,
|
|
rtpPayloadFormat, rtpTimestampFrequency,
|
|
new LATMBufferedPacketFactory),
|
|
fIncludeLATMDataLengthField(True) {
|
|
}
|
|
|
|
MPEG4LATMAudioRTPSource::~MPEG4LATMAudioRTPSource() {
|
|
}
|
|
|
|
void MPEG4LATMAudioRTPSource::omitLATMDataLengthField() {
|
|
fIncludeLATMDataLengthField = False;
|
|
}
|
|
|
|
Boolean MPEG4LATMAudioRTPSource
|
|
::processSpecialHeader(BufferedPacket* packet,
|
|
unsigned& resultSpecialHeaderSize) {
|
|
fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame;
|
|
// whether the *previous* packet ended a frame
|
|
|
|
// The RTP "M" (marker) bit indicates the last fragment of a frame:
|
|
fCurrentPacketCompletesFrame = packet->rtpMarkerBit();
|
|
|
|
// There is no special header
|
|
resultSpecialHeaderSize = 0;
|
|
return True;
|
|
}
|
|
|
|
char const* MPEG4LATMAudioRTPSource::MIMEtype() const {
|
|
return "audio/MP4A-LATM";
|
|
}
|
|
|
|
|
|
////////// LATMBufferedPacket and LATMBufferedPacketFactory implementation
|
|
|
|
LATMBufferedPacket::LATMBufferedPacket(Boolean includeLATMDataLengthField)
|
|
: fIncludeLATMDataLengthField(includeLATMDataLengthField) {
|
|
}
|
|
|
|
LATMBufferedPacket::~LATMBufferedPacket() {
|
|
}
|
|
|
|
unsigned LATMBufferedPacket
|
|
::nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) {
|
|
// Look at the LATM data length byte(s), to determine the size
|
|
// of the LATM payload.
|
|
unsigned resultFrameSize = 0;
|
|
unsigned i;
|
|
for (i = 0; i < dataSize; ++i) {
|
|
resultFrameSize += framePtr[i];
|
|
if (framePtr[i] != 0xFF) break;
|
|
}
|
|
++i;
|
|
if (fIncludeLATMDataLengthField) {
|
|
resultFrameSize += i;
|
|
} else {
|
|
framePtr += i;
|
|
dataSize -= i;
|
|
}
|
|
|
|
return (resultFrameSize <= dataSize) ? resultFrameSize : dataSize;
|
|
}
|
|
|
|
BufferedPacket* LATMBufferedPacketFactory
|
|
::createNewPacket(MultiFramedRTPSource* ourSource) {
|
|
MPEG4LATMAudioRTPSource* source = (MPEG4LATMAudioRTPSource*)ourSource;
|
|
return new LATMBufferedPacket(source->returnedFrameIncludesLATMDataLengthField());
|
|
}
|
|
|
|
|
|
////////// parseStreamMuxConfigStr() implementation //////////
|
|
|
|
static Boolean getNibble(char const*& configStr,
|
|
unsigned char& resultNibble) {
|
|
char c = configStr[0];
|
|
if (c == '\0') return False; // we've reached the end
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
resultNibble = c - '0';
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
resultNibble = 10 + c - 'A';
|
|
} else if (c >= 'a' && c <= 'f') {
|
|
resultNibble = 10 + c - 'a';
|
|
} else {
|
|
return False;
|
|
}
|
|
|
|
++configStr; // move to the next nibble
|
|
return True;
|
|
}
|
|
|
|
static Boolean getByte(char const*& configStr, unsigned char& resultByte) {
|
|
resultByte = 0; // by default, in case parsing fails
|
|
|
|
unsigned char firstNibble;
|
|
if (!getNibble(configStr, firstNibble)) return False;
|
|
resultByte = firstNibble<<4;
|
|
|
|
unsigned char secondNibble = 0;
|
|
if (!getNibble(configStr, secondNibble) && configStr[0] != '\0') {
|
|
// There's a second nibble, but it's malformed
|
|
return False;
|
|
}
|
|
resultByte |= secondNibble;
|
|
|
|
return True;
|
|
}
|
|
|
|
Boolean
|
|
parseStreamMuxConfigStr(char const* configStr,
|
|
// result parameters:
|
|
Boolean& audioMuxVersion,
|
|
Boolean& allStreamsSameTimeFraming,
|
|
unsigned char& numSubFrames,
|
|
unsigned char& numProgram,
|
|
unsigned char& numLayer,
|
|
unsigned char*& audioSpecificConfig,
|
|
unsigned& audioSpecificConfigSize) {
|
|
// Set default versions of the result parameters:
|
|
audioMuxVersion = False;
|
|
allStreamsSameTimeFraming = True;
|
|
numSubFrames = numProgram = numLayer = 0;
|
|
audioSpecificConfig = NULL;
|
|
audioSpecificConfigSize = 0;
|
|
|
|
do {
|
|
if (configStr == NULL) break;
|
|
|
|
unsigned char nextByte;
|
|
|
|
if (!getByte(configStr, nextByte)) break;
|
|
audioMuxVersion = (nextByte&0x80) != 0;
|
|
if (audioMuxVersion) break;
|
|
|
|
allStreamsSameTimeFraming = ((nextByte&0x40)>>6) != 0;
|
|
numSubFrames = (nextByte&0x3F);
|
|
|
|
if (!getByte(configStr, nextByte)) break;
|
|
numProgram = (nextByte&0xF0)>>4;
|
|
|
|
numLayer = (nextByte&0x0E)>>1;
|
|
|
|
// The one remaining bit, and the rest of the string,
|
|
// are used for "audioSpecificConfig":
|
|
unsigned char remainingBit = nextByte&1;
|
|
|
|
unsigned ascSize = (strlen(configStr)+1)/2 + 1;
|
|
audioSpecificConfig = new unsigned char[ascSize];
|
|
|
|
Boolean parseSuccess;
|
|
unsigned i = 0;
|
|
do {
|
|
nextByte = 0;
|
|
parseSuccess = getByte(configStr, nextByte);
|
|
audioSpecificConfig[i++] = (remainingBit<<7)|((nextByte&0xFE)>>1);
|
|
remainingBit = nextByte&1;
|
|
} while (parseSuccess);
|
|
if (i != ascSize) break; // part of the remaining string was bad
|
|
|
|
audioSpecificConfigSize = ascSize;
|
|
return True; // parsing succeeded
|
|
} while (0);
|
|
|
|
delete[] audioSpecificConfig;
|
|
return False; // parsing failed
|
|
}
|
|
|
|
unsigned char* parseStreamMuxConfigStr(char const* configStr,
|
|
// result parameter:
|
|
unsigned& audioSpecificConfigSize) {
|
|
Boolean audioMuxVersion, allStreamsSameTimeFraming;
|
|
unsigned char numSubFrames, numProgram, numLayer;
|
|
unsigned char* audioSpecificConfig;
|
|
|
|
if (!parseStreamMuxConfigStr(configStr,
|
|
audioMuxVersion, allStreamsSameTimeFraming,
|
|
numSubFrames, numProgram, numLayer,
|
|
audioSpecificConfig, audioSpecificConfigSize)) {
|
|
audioSpecificConfigSize = 0;
|
|
return NULL;
|
|
}
|
|
|
|
return audioSpecificConfig;
|
|
}
|
|
|
|
unsigned char* parseGeneralConfigStr(char const* configStr,
|
|
// result parameter:
|
|
unsigned& configSize) {
|
|
unsigned char* config = NULL;
|
|
do {
|
|
if (configStr == NULL) break;
|
|
configSize = (strlen(configStr)+1)/2;
|
|
|
|
config = new unsigned char[configSize];
|
|
if (config == NULL) break;
|
|
|
|
unsigned i;
|
|
for (i = 0; i < configSize; ++i) {
|
|
if (!getByte(configStr, config[i])) break;
|
|
}
|
|
if (i != configSize) break; // part of the string was bad
|
|
|
|
return config;
|
|
} while (0);
|
|
|
|
configSize = 0;
|
|
delete[] config;
|
|
return NULL;
|
|
}
|