433 lines
16 KiB
C++
433 lines
16 KiB
C++
|
/**********
|
||
|
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.
|
||
|
// RTP sink for a common kind of payload format: Those which pack multiple,
|
||
|
// complete codec frames (as many as possible) into each RTP packet.
|
||
|
// Implementation
|
||
|
|
||
|
#include "MultiFramedRTPSink.hh"
|
||
|
#include "GroupsockHelper.hh"
|
||
|
|
||
|
////////// MultiFramedRTPSink //////////
|
||
|
|
||
|
void MultiFramedRTPSink::setPacketSizes(unsigned preferredPacketSize,
|
||
|
unsigned maxPacketSize) {
|
||
|
if (preferredPacketSize > maxPacketSize || preferredPacketSize == 0) return;
|
||
|
// sanity check
|
||
|
|
||
|
delete fOutBuf;
|
||
|
fOutBuf = new OutPacketBuffer(preferredPacketSize, maxPacketSize);
|
||
|
fOurMaxPacketSize = maxPacketSize; // save value, in case subclasses need it
|
||
|
}
|
||
|
|
||
|
#ifndef RTP_PAYLOAD_MAX_SIZE
|
||
|
#define RTP_PAYLOAD_MAX_SIZE 1472
|
||
|
// Default max packet size (1500, minus allowance for IP, UDP, UMTP headers)
|
||
|
// (Also, make it a multiple of 4 bytes, just in case that matters.)
|
||
|
#endif
|
||
|
#ifndef RTP_PAYLOAD_PREFERRED_SIZE
|
||
|
#define RTP_PAYLOAD_PREFERRED_SIZE ((RTP_PAYLOAD_MAX_SIZE) < 1000 ? (RTP_PAYLOAD_MAX_SIZE) : 1000)
|
||
|
#endif
|
||
|
|
||
|
MultiFramedRTPSink::MultiFramedRTPSink(UsageEnvironment& env,
|
||
|
Groupsock* rtpGS,
|
||
|
unsigned char rtpPayloadType,
|
||
|
unsigned rtpTimestampFrequency,
|
||
|
char const* rtpPayloadFormatName,
|
||
|
unsigned numChannels)
|
||
|
: RTPSink(env, rtpGS, rtpPayloadType, rtpTimestampFrequency,
|
||
|
rtpPayloadFormatName, numChannels),
|
||
|
fOutBuf(NULL), fCurFragmentationOffset(0), fPreviousFrameEndedFragmentation(False),
|
||
|
fOnSendErrorFunc(NULL), fOnSendErrorData(NULL) {
|
||
|
setPacketSizes(1000, 8192);//(RTP_PAYLOAD_PREFERRED_SIZE), (RTP_PAYLOAD_MAX_SIZE));
|
||
|
}
|
||
|
|
||
|
MultiFramedRTPSink::~MultiFramedRTPSink() {
|
||
|
delete fOutBuf;
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink
|
||
|
::doSpecialFrameHandling(unsigned /*fragmentationOffset*/,
|
||
|
unsigned char* /*frameStart*/,
|
||
|
unsigned /*numBytesInFrame*/,
|
||
|
struct timeval framePresentationTime,
|
||
|
unsigned /*numRemainingBytes*/) {
|
||
|
// default implementation: If this is the first frame in the packet,
|
||
|
// use its presentationTime for the RTP timestamp:
|
||
|
if (isFirstFrameInPacket()) {
|
||
|
setTimestamp(framePresentationTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Boolean MultiFramedRTPSink::allowFragmentationAfterStart() const {
|
||
|
return False; // by default
|
||
|
}
|
||
|
|
||
|
Boolean MultiFramedRTPSink::allowOtherFramesAfterLastFragment() const {
|
||
|
return False; // by default
|
||
|
}
|
||
|
|
||
|
Boolean MultiFramedRTPSink
|
||
|
::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/,
|
||
|
unsigned /*numBytesInFrame*/) const {
|
||
|
return True; // by default
|
||
|
}
|
||
|
|
||
|
unsigned MultiFramedRTPSink::specialHeaderSize() const {
|
||
|
// default implementation: Assume no special header:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
unsigned MultiFramedRTPSink::frameSpecificHeaderSize() const {
|
||
|
// default implementation: Assume no frame-specific header:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
unsigned MultiFramedRTPSink::computeOverflowForNewFrame(unsigned newFrameSize) const {
|
||
|
// default implementation: Just call numOverflowBytes()
|
||
|
return fOutBuf->numOverflowBytes(newFrameSize);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::setMarkerBit() {
|
||
|
unsigned rtpHdr = fOutBuf->extractWord(0);
|
||
|
rtpHdr |= 0x00800000;
|
||
|
fOutBuf->insertWord(rtpHdr, 0);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::setTimestamp(struct timeval framePresentationTime) {
|
||
|
// First, convert the presentation time to a 32-bit RTP timestamp:
|
||
|
fCurrentTimestamp = convertToRTPTimestamp(framePresentationTime);
|
||
|
|
||
|
// Then, insert it into the RTP packet:
|
||
|
fOutBuf->insertWord(fCurrentTimestamp, fTimestampPosition);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::setSpecialHeaderWord(unsigned word,
|
||
|
unsigned wordPosition) {
|
||
|
fOutBuf->insertWord(word, fSpecialHeaderPosition + 4*wordPosition);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::setSpecialHeaderBytes(unsigned char const* bytes,
|
||
|
unsigned numBytes,
|
||
|
unsigned bytePosition) {
|
||
|
fOutBuf->insert(bytes, numBytes, fSpecialHeaderPosition + bytePosition);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::setFrameSpecificHeaderWord(unsigned word,
|
||
|
unsigned wordPosition) {
|
||
|
fOutBuf->insertWord(word, fCurFrameSpecificHeaderPosition + 4*wordPosition);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::setFrameSpecificHeaderBytes(unsigned char const* bytes,
|
||
|
unsigned numBytes,
|
||
|
unsigned bytePosition) {
|
||
|
fOutBuf->insert(bytes, numBytes, fCurFrameSpecificHeaderPosition + bytePosition);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::setFramePadding(unsigned numPaddingBytes) {
|
||
|
if (numPaddingBytes > 0) {
|
||
|
// Add the padding bytes (with the last one being the padding size):
|
||
|
unsigned char paddingBuffer[255]; //max padding
|
||
|
memset(paddingBuffer, 0, numPaddingBytes);
|
||
|
paddingBuffer[numPaddingBytes-1] = numPaddingBytes;
|
||
|
fOutBuf->enqueue(paddingBuffer, numPaddingBytes);
|
||
|
|
||
|
// Set the RTP padding bit:
|
||
|
unsigned rtpHdr = fOutBuf->extractWord(0);
|
||
|
rtpHdr |= 0x20000000;
|
||
|
fOutBuf->insertWord(rtpHdr, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Boolean MultiFramedRTPSink::continuePlaying() {
|
||
|
// Send the first packet.
|
||
|
// (This will also schedule any future sends.)
|
||
|
buildAndSendPacket(True);
|
||
|
return True;
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::stopPlaying() {
|
||
|
fOutBuf->resetPacketStart();
|
||
|
fOutBuf->resetOffset();
|
||
|
fOutBuf->resetOverflowData();
|
||
|
|
||
|
// Then call the default "stopPlaying()" function:
|
||
|
MediaSink::stopPlaying();
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket) {
|
||
|
fIsFirstPacket = isFirstPacket;
|
||
|
|
||
|
// Set up the RTP header:
|
||
|
unsigned rtpHdr = 0x80000000; // RTP version 2; marker ('M') bit not set (by default; it can be set later)
|
||
|
rtpHdr |= (fRTPPayloadType<<16);
|
||
|
rtpHdr |= fSeqNo; // sequence number
|
||
|
fOutBuf->enqueueWord(rtpHdr);
|
||
|
|
||
|
// Note where the RTP timestamp will go.
|
||
|
// (We can't fill this in until we start packing payload frames.)
|
||
|
fTimestampPosition = fOutBuf->curPacketSize();
|
||
|
fOutBuf->skipBytes(4); // leave a hole for the timestamp
|
||
|
|
||
|
fOutBuf->enqueueWord(SSRC());
|
||
|
|
||
|
// Allow for a special, payload-format-specific header following the
|
||
|
// RTP header:
|
||
|
fSpecialHeaderPosition = fOutBuf->curPacketSize();
|
||
|
fSpecialHeaderSize = specialHeaderSize();
|
||
|
fOutBuf->skipBytes(fSpecialHeaderSize);
|
||
|
|
||
|
// Begin packing as many (complete) frames into the packet as we can:
|
||
|
fTotalFrameSpecificHeaderSizes = 0;
|
||
|
fNoFramesLeft = False;
|
||
|
fNumFramesUsedSoFar = 0;
|
||
|
packFrame();
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::packFrame() {
|
||
|
// Get the next frame.
|
||
|
|
||
|
// First, skip over the space we'll use for any frame-specific header:
|
||
|
fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize();
|
||
|
fCurFrameSpecificHeaderSize = frameSpecificHeaderSize();
|
||
|
fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);
|
||
|
fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize;
|
||
|
|
||
|
// See if we have an overflow frame that was too big for the last pkt
|
||
|
if (fOutBuf->haveOverflowData()) {
|
||
|
// Use this frame before reading a new one from the source
|
||
|
unsigned frameSize = fOutBuf->overflowDataSize();
|
||
|
struct timeval presentationTime = fOutBuf->overflowPresentationTime();
|
||
|
unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds();
|
||
|
fOutBuf->useOverflowData();
|
||
|
|
||
|
afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);
|
||
|
} else {
|
||
|
// Normal case: we need to read a new frame from the source
|
||
|
if (fSource == NULL) return;
|
||
|
fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),
|
||
|
afterGettingFrame, this, ourHandleClosure, this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink
|
||
|
::afterGettingFrame(void* clientData, unsigned numBytesRead,
|
||
|
unsigned numTruncatedBytes,
|
||
|
struct timeval presentationTime,
|
||
|
unsigned durationInMicroseconds) {
|
||
|
MultiFramedRTPSink* sink = (MultiFramedRTPSink*)clientData;
|
||
|
sink->afterGettingFrame1(numBytesRead, numTruncatedBytes,
|
||
|
presentationTime, durationInMicroseconds);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink
|
||
|
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
|
||
|
struct timeval presentationTime,
|
||
|
unsigned durationInMicroseconds) {
|
||
|
if (fIsFirstPacket) {
|
||
|
// Record the fact that we're starting to play now:
|
||
|
gettimeofday(&fNextSendTime, NULL);
|
||
|
}
|
||
|
|
||
|
fMostRecentPresentationTime = presentationTime;
|
||
|
if (fInitialPresentationTime.tv_sec == 0 && fInitialPresentationTime.tv_usec == 0) {
|
||
|
fInitialPresentationTime = presentationTime;
|
||
|
}
|
||
|
|
||
|
if (numTruncatedBytes > 0) {
|
||
|
unsigned const bufferSize = fOutBuf->totalBytesAvailable();
|
||
|
envir() << "MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size ("
|
||
|
<< bufferSize << "). "
|
||
|
<< numTruncatedBytes << " bytes of trailing data was dropped! Correct this by increasing \"OutPacketBuffer::maxSize\" to at least "
|
||
|
<< OutPacketBuffer::maxSize + numTruncatedBytes << ", *before* creating this 'RTPSink'. (Current value is "
|
||
|
<< OutPacketBuffer::maxSize << ".)\n";
|
||
|
}
|
||
|
unsigned curFragmentationOffset = fCurFragmentationOffset;
|
||
|
unsigned numFrameBytesToUse = frameSize;
|
||
|
unsigned overflowBytes = 0;
|
||
|
|
||
|
// If we have already packed one or more frames into this packet,
|
||
|
// check whether this new frame is eligible to be packed after them.
|
||
|
// (This is independent of whether the packet has enough room for this
|
||
|
// new frame; that check comes later.)
|
||
|
if (fNumFramesUsedSoFar > 0) {
|
||
|
if ((fPreviousFrameEndedFragmentation
|
||
|
&& !allowOtherFramesAfterLastFragment())
|
||
|
|| !frameCanAppearAfterPacketStart(fOutBuf->curPtr(), frameSize)) {
|
||
|
// Save away this frame for next time:
|
||
|
numFrameBytesToUse = 0;
|
||
|
fOutBuf->setOverflowData(fOutBuf->curPacketSize(), frameSize,
|
||
|
presentationTime, durationInMicroseconds);
|
||
|
}
|
||
|
}
|
||
|
fPreviousFrameEndedFragmentation = False;
|
||
|
|
||
|
if (numFrameBytesToUse > 0) {
|
||
|
// Check whether this frame overflows the packet
|
||
|
if (fOutBuf->wouldOverflow(frameSize)) {
|
||
|
// Don't use this frame now; instead, save it as overflow data, and
|
||
|
// send it in the next packet instead. However, if the frame is too
|
||
|
// big to fit in a packet by itself, then we need to fragment it (and
|
||
|
// use some of it in this packet, if the payload format permits this.)
|
||
|
if (isTooBigForAPacket(frameSize)
|
||
|
&& (fNumFramesUsedSoFar == 0 || allowFragmentationAfterStart())) {
|
||
|
// We need to fragment this frame, and use some of it now:
|
||
|
overflowBytes = computeOverflowForNewFrame(frameSize);
|
||
|
numFrameBytesToUse -= overflowBytes;
|
||
|
fCurFragmentationOffset += numFrameBytesToUse;
|
||
|
} else {
|
||
|
// We don't use any of this frame now:
|
||
|
overflowBytes = frameSize;
|
||
|
numFrameBytesToUse = 0;
|
||
|
}
|
||
|
fOutBuf->setOverflowData(fOutBuf->curPacketSize() + numFrameBytesToUse,
|
||
|
overflowBytes, presentationTime, durationInMicroseconds);
|
||
|
} else if (fCurFragmentationOffset > 0) {
|
||
|
// This is the last fragment of a frame that was fragmented over
|
||
|
// more than one packet. Do any special handling for this case:
|
||
|
fCurFragmentationOffset = 0;
|
||
|
fPreviousFrameEndedFragmentation = True;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (numFrameBytesToUse == 0 && frameSize > 0) {
|
||
|
// Send our packet now, because we have filled it up:
|
||
|
sendPacketIfNecessary();
|
||
|
} else {
|
||
|
// Use this frame in our outgoing packet:
|
||
|
unsigned char* frameStart = fOutBuf->curPtr();
|
||
|
fOutBuf->increment(numFrameBytesToUse);
|
||
|
// do this now, in case "doSpecialFrameHandling()" calls "setFramePadding()" to append padding bytes
|
||
|
|
||
|
// Here's where any payload format specific processing gets done:
|
||
|
doSpecialFrameHandling(curFragmentationOffset, frameStart,
|
||
|
numFrameBytesToUse, presentationTime,
|
||
|
overflowBytes);
|
||
|
|
||
|
++fNumFramesUsedSoFar;
|
||
|
|
||
|
// Update the time at which the next packet should be sent, based
|
||
|
// on the duration of the frame that we just packed into it.
|
||
|
// However, if this frame has overflow data remaining, then don't
|
||
|
// count its duration yet.
|
||
|
if (overflowBytes == 0) {
|
||
|
fNextSendTime.tv_usec += durationInMicroseconds;
|
||
|
fNextSendTime.tv_sec += fNextSendTime.tv_usec/1000000;
|
||
|
fNextSendTime.tv_usec %= 1000000;
|
||
|
}
|
||
|
|
||
|
// Send our packet now if (i) it's already at our preferred size, or
|
||
|
// (ii) (heuristic) another frame of the same size as the one we just
|
||
|
// read would overflow the packet, or
|
||
|
// (iii) it contains the last fragment of a fragmented frame, and we
|
||
|
// don't allow anything else to follow this or
|
||
|
// (iv) one frame per packet is allowed:
|
||
|
if (fOutBuf->isPreferredSize()
|
||
|
|| fOutBuf->wouldOverflow(numFrameBytesToUse)
|
||
|
|| (fPreviousFrameEndedFragmentation &&
|
||
|
!allowOtherFramesAfterLastFragment())
|
||
|
|| !frameCanAppearAfterPacketStart(fOutBuf->curPtr() - frameSize,
|
||
|
frameSize) ) {
|
||
|
// The packet is ready to be sent now
|
||
|
sendPacketIfNecessary();
|
||
|
} else {
|
||
|
// There's room for more frames; try getting another:
|
||
|
packFrame();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static unsigned const rtpHeaderSize = 12;
|
||
|
|
||
|
Boolean MultiFramedRTPSink::isTooBigForAPacket(unsigned numBytes) const {
|
||
|
// Check whether a 'numBytes'-byte frame - together with a RTP header and
|
||
|
// (possible) special headers - would be too big for an output packet:
|
||
|
// (Later allow for RTP extension header!) #####
|
||
|
numBytes += rtpHeaderSize + specialHeaderSize() + frameSpecificHeaderSize();
|
||
|
return fOutBuf->isTooBigForAPacket(numBytes);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::sendPacketIfNecessary() {
|
||
|
if (fNumFramesUsedSoFar > 0) {
|
||
|
// Send the packet:
|
||
|
#ifdef TEST_LOSS
|
||
|
if ((our_random()%10) != 0) // simulate 10% packet loss #####
|
||
|
#endif
|
||
|
if (!fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize())) {
|
||
|
// if failure handler has been specified, call it
|
||
|
if (fOnSendErrorFunc != NULL) (*fOnSendErrorFunc)(fOnSendErrorData);
|
||
|
}
|
||
|
++fPacketCount;
|
||
|
fTotalOctetCount += fOutBuf->curPacketSize();
|
||
|
fOctetCount += fOutBuf->curPacketSize()
|
||
|
- rtpHeaderSize - fSpecialHeaderSize - fTotalFrameSpecificHeaderSizes;
|
||
|
|
||
|
++fSeqNo; // for next time
|
||
|
}
|
||
|
|
||
|
if (fOutBuf->haveOverflowData()
|
||
|
&& fOutBuf->totalBytesAvailable() > fOutBuf->totalBufferSize()/2) {
|
||
|
// Efficiency hack: Reset the packet start pointer to just in front of
|
||
|
// the overflow data (allowing for the RTP header and special headers),
|
||
|
// so that we probably don't have to "memmove()" the overflow data
|
||
|
// into place when building the next packet:
|
||
|
unsigned newPacketStart = fOutBuf->curPacketSize()
|
||
|
- (rtpHeaderSize + fSpecialHeaderSize + frameSpecificHeaderSize());
|
||
|
fOutBuf->adjustPacketStart(newPacketStart);
|
||
|
} else {
|
||
|
// Normal case: Reset the packet start pointer back to the start:
|
||
|
fOutBuf->resetPacketStart();
|
||
|
}
|
||
|
fOutBuf->resetOffset();
|
||
|
fNumFramesUsedSoFar = 0;
|
||
|
|
||
|
if (fNoFramesLeft) {
|
||
|
// We're done:
|
||
|
onSourceClosure();
|
||
|
} else {
|
||
|
// We have more frames left to send. Figure out when the next frame
|
||
|
// is due to start playing, then make sure that we wait this long before
|
||
|
// sending the next packet.
|
||
|
#if 0
|
||
|
struct timeval timeNow;
|
||
|
gettimeofday(&timeNow, NULL);
|
||
|
int secsDiff = fNextSendTime.tv_sec - timeNow.tv_sec;
|
||
|
int64_t uSecondsToGo = secsDiff*1000000 + (fNextSendTime.tv_usec - timeNow.tv_usec);
|
||
|
if (uSecondsToGo < 0 || secsDiff < 0) { // sanity check: Make sure that the time-to-delay is non-negative:
|
||
|
uSecondsToGo = 0;
|
||
|
}
|
||
|
#endif
|
||
|
// Delay this amount of time
|
||
|
//nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);
|
||
|
sendNext(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The following is called after each delay between packet sends:
|
||
|
void MultiFramedRTPSink::sendNext(void* firstArg) {
|
||
|
MultiFramedRTPSink* sink = (MultiFramedRTPSink*)firstArg;
|
||
|
sink->buildAndSendPacket(False);
|
||
|
}
|
||
|
|
||
|
void MultiFramedRTPSink::ourHandleClosure(void* clientData) {
|
||
|
MultiFramedRTPSink* sink = (MultiFramedRTPSink*)clientData;
|
||
|
// There are no frames left, but we may have a partially built packet
|
||
|
// to send
|
||
|
sink->fNoFramesLeft = True;
|
||
|
sink->sendPacketIfNecessary();
|
||
|
}
|