/********** 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 .) 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; }