/********** 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 .) 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. // RTP sink for Vorbis audio // Implementation #include "VorbisAudioRTPSink.hh" #include "Base64.hh" #include "VorbisAudioRTPSource.hh" // for parseVorbisOrTheoraConfigStr() VorbisAudioRTPSink* VorbisAudioRTPSink ::createNew(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels, u_int8_t* identificationHeader, unsigned identificationHeaderSize, u_int8_t* commentHeader, unsigned commentHeaderSize, u_int8_t* setupHeader, unsigned setupHeaderSize, u_int32_t identField) { return new VorbisAudioRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, numChannels, identificationHeader, identificationHeaderSize, commentHeader, commentHeaderSize, setupHeader, setupHeaderSize, identField); } VorbisAudioRTPSink* VorbisAudioRTPSink ::createNew(UsageEnvironment& env, Groupsock* RTPgs,u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels, char const* configStr) { // Begin by decoding and unpacking the configuration string: u_int8_t* identificationHeader; unsigned identificationHeaderSize; u_int8_t* commentHeader; unsigned commentHeaderSize; u_int8_t* setupHeader; unsigned setupHeaderSize; u_int32_t identField; parseVorbisOrTheoraConfigStr(configStr, identificationHeader, identificationHeaderSize, commentHeader, commentHeaderSize, setupHeader, setupHeaderSize, identField); VorbisAudioRTPSink* resultSink = new VorbisAudioRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, numChannels, identificationHeader, identificationHeaderSize, commentHeader, commentHeaderSize, setupHeader, setupHeaderSize, identField); delete[] identificationHeader; delete[] commentHeader; delete[] setupHeader; return resultSink; } VorbisAudioRTPSink ::VorbisAudioRTPSink(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels, u_int8_t* identificationHeader, unsigned identificationHeaderSize, u_int8_t* commentHeader, unsigned commentHeaderSize, u_int8_t* setupHeader, unsigned setupHeaderSize, u_int32_t identField) : AudioRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, "VORBIS", numChannels), fIdent(identField), fFmtpSDPLine(NULL) { if (identificationHeaderSize >= 28) { // Get the 'bitrate' values from this header, and use them to set our estimated bitrate: u_int32_t val; u_int8_t* p; p = &identificationHeader[16]; val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian int bitrate_maximum = (int)val; if (bitrate_maximum < 0) bitrate_maximum = 0; p = &identificationHeader[20]; val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian int bitrate_nominal = (int)val; if (bitrate_nominal < 0) bitrate_nominal = 0; p = &identificationHeader[24]; val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian int bitrate_minimum = (int)val; if (bitrate_minimum < 0) bitrate_minimum = 0; int bitrate = bitrate_nominal > 0 ? bitrate_nominal : bitrate_maximum > 0 ? bitrate_maximum : bitrate_minimum > 0 ? bitrate_minimum : 0; if (bitrate > 0) estimatedBitrate() = ((unsigned)bitrate)/1000; } // Generate a 'config' string from the supplied configuration headers: char* base64PackedHeaders = generateVorbisOrTheoraConfigStr(identificationHeader, identificationHeaderSize, commentHeader, commentHeaderSize, setupHeader, setupHeaderSize, identField); if (base64PackedHeaders == NULL) return; // Then use this 'config' string to construct our "a=fmtp:" SDP line: unsigned fmtpSDPLineMaxSize = 50 + strlen(base64PackedHeaders); // 50 => more than enough space fFmtpSDPLine = new char[fmtpSDPLineMaxSize]; sprintf(fFmtpSDPLine, "a=fmtp:%d configuration=%s\r\n", rtpPayloadType(), base64PackedHeaders); delete[] base64PackedHeaders; } VorbisAudioRTPSink::~VorbisAudioRTPSink() { delete[] fFmtpSDPLine; } char const* VorbisAudioRTPSink::auxSDPLine() { return fFmtpSDPLine; } void VorbisAudioRTPSink ::doSpecialFrameHandling(unsigned fragmentationOffset, unsigned char* frameStart, unsigned numBytesInFrame, struct timeval framePresentationTime, unsigned numRemainingBytes) { // Set the 4-byte "payload header", as defined in RFC 5215, section 2.2: u_int8_t header[4]; // The first three bytes of the header are our "Ident": header[0] = fIdent>>16; header[1] = fIdent>>8; header[2] = fIdent; // The final byte contains the "F", "VDT", and "numPkts" fields: u_int8_t F; // Fragment type if (numRemainingBytes > 0) { if (fragmentationOffset > 0) { F = 2<<6; // continuation fragment } else { F = 1<<6; // start fragment } } else { if (fragmentationOffset > 0) { F = 3<<6; // end fragment } else { F = 0<<6; // not fragmented } } u_int8_t const VDT = 0<<4; // Vorbis Data Type (always a "Raw Vorbis payload") u_int8_t numPkts = F == 0 ? (numFramesUsedSoFar() + 1): 0; // set to 0 when we're a fragment header[3] = F|VDT|numPkts; setSpecialHeaderBytes(header, sizeof header); // There's also a 2-byte 'frame-specific' header: The length of the Vorbis data: u_int8_t frameSpecificHeader[2]; frameSpecificHeader[0] = numBytesInFrame>>8; frameSpecificHeader[1] = numBytesInFrame; setFrameSpecificHeaderBytes(frameSpecificHeader, 2); // Important: Also call our base class's doSpecialFrameHandling(), // to set the packet's timestamp: MultiFramedRTPSink::doSpecialFrameHandling(fragmentationOffset, frameStart, numBytesInFrame, framePresentationTime, numRemainingBytes); } Boolean VorbisAudioRTPSink::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/, unsigned /*numBytesInFrame*/) const { // We allow more than one frame to be packed into an outgoing RTP packet, but no more than 15: return numFramesUsedSoFar() <= 15; } unsigned VorbisAudioRTPSink::specialHeaderSize() const { return 4; } unsigned VorbisAudioRTPSink::frameSpecificHeaderSize() const { return 2; } ////////// generateVorbisOrTheoraConfigStr() implementation ////////// char* generateVorbisOrTheoraConfigStr(u_int8_t* identificationHeader, unsigned identificationHeaderSize, u_int8_t* commentHeader, unsigned commentHeaderSize, u_int8_t* setupHeader, unsigned setupHeaderSize, u_int32_t identField) { // First, count how many headers (<=3) are included, and how many bytes will be used // to encode these headers' sizes: unsigned numHeaders = 0; unsigned sizeSize[2]; // The number of bytes used to encode the lengths of the first two headers (but not the length of the 3rd) sizeSize[0] = sizeSize[1] = 0; if (identificationHeaderSize > 0) { sizeSize[numHeaders++] = identificationHeaderSize < 128 ? 1 : identificationHeaderSize < 16384 ? 2 : 3; } if (commentHeaderSize > 0) { sizeSize[numHeaders++] = commentHeaderSize < 128 ? 1 : commentHeaderSize < 16384 ? 2 : 3; } if (setupHeaderSize > 0) { ++numHeaders; } else { sizeSize[1] = 0; // We have at most two headers, so the second one's length isn't encoded } if (numHeaders == 0) return NULL; // With no headers, we can't set up a configuration if (numHeaders == 1) sizeSize[0] = 0; // With only one header, its length isn't encoded // Then figure out the size of the packed configuration headers, and allocate space for this: unsigned length = identificationHeaderSize + commentHeaderSize + setupHeaderSize; // The "length" field in the packed headers if (length > (unsigned)0xFFFF) return NULL; // too big for a 16-bit field; we can't handle this unsigned packedHeadersSize = 4 // "Number of packed headers" field + 3 // "ident" field + 2 // "length" field + 1 // "n. of headers" field + sizeSize[0] + sizeSize[1] // "length1" and "length2" (if present) fields + length; u_int8_t* packedHeaders = new u_int8_t[packedHeadersSize]; if (packedHeaders == NULL) return NULL; // Fill in the 'packed headers': u_int8_t* p = packedHeaders; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; // "Number of packed headers": 1 *p++ = identField>>16; *p++ = identField>>8; *p++ = identField; // "Ident" (24 bits) *p++ = length>>8; *p++ = length; // "length" (16 bits) *p++ = numHeaders-1; // "n. of headers" if (numHeaders > 1) { // Fill in the "length1" header: unsigned length1 = identificationHeaderSize > 0 ? identificationHeaderSize : commentHeaderSize; if (length1 >= 16384) { *p++ = 0x80; // flag, but no more, because we know length1 <= 32767 } if (length1 >= 128) { *p++ = 0x80|((length1&0x3F80)>>7); // flag + the second 7 bits } *p++ = length1&0x7F; // the low 7 bits if (numHeaders > 2) { // numHeaders == 3 // Fill in the "length2" header (for the 'Comment' header): unsigned length2 = commentHeaderSize; if (length2 >= 16384) { *p++ = 0x80; // flag, but no more, because we know length2 <= 32767 } if (length2 >= 128) { *p++ = 0x80|((length2&0x3F80)>>7); // flag + the second 7 bits } *p++ = length2&0x7F; // the low 7 bits } } // Copy each header: if (identificationHeader != NULL) memmove(p, identificationHeader, identificationHeaderSize); p += identificationHeaderSize; if (commentHeader != NULL) memmove(p, commentHeader, commentHeaderSize); p += commentHeaderSize; if (setupHeader != NULL) memmove(p, setupHeader, setupHeaderSize); // Having set up the 'packed configuration headers', Base-64-encode this, for our result: char* base64PackedHeaders = base64Encode((char const*)packedHeaders, packedHeadersSize); delete[] packedHeaders; return base64PackedHeaders; }