blob: d0b6c609d4b3f07a647a21652f98026f03f2b91a [file] [log] [blame]
/**********
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 3 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-2020 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;
}