198 lines
6.5 KiB
C++
Executable File
198 lines
6.5 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.
|
|
// Vorbis Audio RTP Sources
|
|
// Implementation
|
|
|
|
#include "VorbisAudioRTPSource.hh"
|
|
#include "Base64.hh"
|
|
|
|
////////// VorbisBufferedPacket and VorbisBufferedPacketFactory //////////
|
|
|
|
class VorbisBufferedPacket: public BufferedPacket {
|
|
public:
|
|
VorbisBufferedPacket();
|
|
virtual ~VorbisBufferedPacket();
|
|
|
|
private: // redefined virtual functions
|
|
virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr,
|
|
unsigned dataSize);
|
|
};
|
|
|
|
class VorbisBufferedPacketFactory: public BufferedPacketFactory {
|
|
private: // redefined virtual functions
|
|
virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
|
|
};
|
|
|
|
|
|
///////// VorbisAudioRTPSource implementation ////////
|
|
|
|
VorbisAudioRTPSource*
|
|
VorbisAudioRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
|
|
unsigned char rtpPayloadFormat,
|
|
unsigned rtpTimestampFrequency) {
|
|
return new VorbisAudioRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency);
|
|
}
|
|
|
|
VorbisAudioRTPSource
|
|
::VorbisAudioRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
|
|
unsigned char rtpPayloadFormat,
|
|
unsigned rtpTimestampFrequency)
|
|
: MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency,
|
|
new VorbisBufferedPacketFactory),
|
|
fCurPacketIdent(0) {
|
|
}
|
|
|
|
VorbisAudioRTPSource::~VorbisAudioRTPSource() {
|
|
}
|
|
|
|
Boolean VorbisAudioRTPSource
|
|
::processSpecialHeader(BufferedPacket* packet,
|
|
unsigned& resultSpecialHeaderSize) {
|
|
unsigned char* headerStart = packet->data();
|
|
unsigned packetSize = packet->dataSize();
|
|
|
|
resultSpecialHeaderSize = 4;
|
|
if (packetSize < resultSpecialHeaderSize) return False; // packet was too small
|
|
|
|
// The first 3 bytes of the header are the "Ident" field:
|
|
fCurPacketIdent = (headerStart[0]<<16) | (headerStart[1]<<8) | headerStart[2];
|
|
|
|
// The 4th byte is F|VDT|numPkts.
|
|
// Reject any packet with VDT == 3:
|
|
if ((headerStart[3]&0x30) == 0x30) return False;
|
|
|
|
u_int8_t F = headerStart[3]>>6;
|
|
fCurrentPacketBeginsFrame = F <= 1; // "Not Fragmented" or "Start Fragment"
|
|
fCurrentPacketCompletesFrame = F == 0 || F == 3; // "Not Fragmented" or "End Fragment"
|
|
|
|
return True;
|
|
}
|
|
|
|
char const* VorbisAudioRTPSource::MIMEtype() const {
|
|
return "audio/VORBIS";
|
|
}
|
|
|
|
|
|
////////// VorbisBufferedPacket and VorbisBufferedPacketFactory implementation //////////
|
|
|
|
VorbisBufferedPacket::VorbisBufferedPacket() {
|
|
}
|
|
|
|
VorbisBufferedPacket::~VorbisBufferedPacket() {
|
|
}
|
|
|
|
unsigned VorbisBufferedPacket
|
|
::nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) {
|
|
if (dataSize < 2) {
|
|
// There's not enough space for a 2-byte header. TARFU! Just return the data that's left:
|
|
return dataSize;
|
|
}
|
|
|
|
unsigned frameSize = (framePtr[0]<<8) | framePtr[1];
|
|
framePtr += 2;
|
|
if (frameSize > dataSize - 2) return dataSize - 2; // inconsistent frame size => just return all the data that's left
|
|
|
|
return frameSize;
|
|
}
|
|
|
|
BufferedPacket* VorbisBufferedPacketFactory
|
|
::createNewPacket(MultiFramedRTPSource* /*ourSource*/) {
|
|
return new VorbisBufferedPacket();
|
|
}
|
|
|
|
|
|
////////// parseVorbisOrTheoraConfigStr() implementation //////////
|
|
|
|
#define ADVANCE(n) do { p += (n); rem -= (n); } while (0)
|
|
#define GET_ENCODED_VAL(n) do { u_int8_t byte; n = 0; do { if (rem == 0) break; byte = *p; n = (n*128) + (byte&0x7F); ADVANCE(1); } while (byte&0x80); } while (0); if (rem == 0) break
|
|
|
|
void parseVorbisOrTheoraConfigStr(char const* configStr,
|
|
u_int8_t*& identificationHdr, unsigned& identificationHdrSize,
|
|
u_int8_t*& commentHdr, unsigned& commentHdrSize,
|
|
u_int8_t*& setupHdr, unsigned& setupHdrSize,
|
|
u_int32_t& identField) {
|
|
identificationHdr = commentHdr = setupHdr = NULL; // default values, if an error occur
|
|
identificationHdrSize = commentHdrSize = setupHdrSize = 0; // ditto
|
|
identField = 0; // ditto
|
|
|
|
// Begin by Base64-decoding the configuration string:
|
|
unsigned configDataSize;
|
|
u_int8_t* configData = base64Decode(configStr, configDataSize);
|
|
u_int8_t* p = configData;
|
|
unsigned rem = configDataSize;
|
|
|
|
do {
|
|
if (rem < 4) break;
|
|
u_int32_t numPackedHeaders = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; ADVANCE(4);
|
|
if (numPackedHeaders == 0) break;
|
|
|
|
// Use the first 'packed header' only.
|
|
if (rem < 3) break;
|
|
identField = (p[0]<<16)|(p[1]<<8)|p[2]; ADVANCE(3);
|
|
|
|
if (rem < 2) break;
|
|
u_int16_t length = (p[0]<<8)|p[1]; ADVANCE(2);
|
|
|
|
unsigned numHeaders;
|
|
GET_ENCODED_VAL(numHeaders);
|
|
|
|
Boolean success = False;
|
|
for (unsigned i = 0; i < numHeaders+1 && i < 3; ++i) {
|
|
success = False;
|
|
unsigned headerSize;
|
|
if (i < numHeaders) {
|
|
// The header size is encoded:
|
|
GET_ENCODED_VAL(headerSize);
|
|
if (headerSize > length) break;
|
|
length -= headerSize;
|
|
} else {
|
|
// The last header is implicit:
|
|
headerSize = length;
|
|
}
|
|
|
|
// Allocate space for the header bytes; we'll fill it in later
|
|
if (i == 0) {
|
|
identificationHdrSize = headerSize;
|
|
identificationHdr = new u_int8_t[identificationHdrSize];
|
|
} else if (i == 1) {
|
|
commentHdrSize = headerSize;
|
|
commentHdr = new u_int8_t[commentHdrSize];
|
|
} else { // i == 2
|
|
setupHdrSize = headerSize;
|
|
setupHdr = new u_int8_t[setupHdrSize];
|
|
}
|
|
|
|
success = True;
|
|
}
|
|
if (!success) break;
|
|
|
|
// Copy the remaining config bytes into the appropriate 'header' buffers:
|
|
if (identificationHdr != NULL) {
|
|
memmove(identificationHdr, p, identificationHdrSize); ADVANCE(identificationHdrSize);
|
|
if (commentHdr != NULL) {
|
|
memmove(commentHdr, p, commentHdrSize); ADVANCE(commentHdrSize);
|
|
if (setupHdr != NULL) {
|
|
memmove(setupHdr, p, setupHdrSize); ADVANCE(setupHdrSize);
|
|
}
|
|
}
|
|
}
|
|
} while (0);
|
|
|
|
delete[] configData;
|
|
}
|