blob: dd288f2b444264652fdc2d3c256731bd1c52a087 [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.
// RTP Sinks
// Implementation
#include "RTPSink.hh"
#include "GroupsockHelper.hh"
////////// RTPSink //////////
Boolean RTPSink::lookupByName(UsageEnvironment& env, char const* sinkName,
RTPSink*& resultSink) {
resultSink = NULL; // unless we succeed
MediaSink* sink;
if (!MediaSink::lookupByName(env, sinkName, sink)) return False;
if (!sink->isRTPSink()) {
env.setResultMsg(sinkName, " is not a RTP sink");
return False;
}
resultSink = (RTPSink*)sink;
return True;
}
Boolean RTPSink::isRTPSink() const {
return True;
}
RTPSink::RTPSink(UsageEnvironment& env,
Groupsock* rtpGS, unsigned char rtpPayloadType,
unsigned rtpTimestampFrequency,
char const* rtpPayloadFormatName,
unsigned numChannels)
: MediaSink(env), fRTPInterface(this, rtpGS),
fRTPPayloadType(rtpPayloadType),
fPacketCount(0), fOctetCount(0), fTotalOctetCount(0),
fTimestampFrequency(rtpTimestampFrequency), fNextTimestampHasBeenPreset(False), fEnableRTCPReports(True),
fNumChannels(numChannels), fEstimatedBitrate(0) {
fRTPPayloadFormatName
= strDup(rtpPayloadFormatName == NULL ? "???" : rtpPayloadFormatName);
gettimeofday(&fCreationTime, NULL);
fTotalOctetCountStartTime = fCreationTime;
resetPresentationTimes();
fSeqNo = (u_int16_t)our_random();
fSSRC = our_random32();
fTimestampBase = our_random32();
fTransmissionStatsDB = new RTPTransmissionStatsDB(*this);
}
RTPSink::~RTPSink() {
delete fTransmissionStatsDB;
delete[] (char*)fRTPPayloadFormatName;
fRTPInterface.forgetOurGroupsock();
// so that the "fRTPInterface" destructor doesn't turn off background read handling (in case
// its 'groupsock' is being shared with something else that does background read handling).
}
u_int32_t RTPSink::convertToRTPTimestamp(struct timeval tv) {
// Begin by converting from "struct timeval" units to RTP timestamp units:
u_int32_t timestampIncrement = (fTimestampFrequency*tv.tv_sec);
timestampIncrement += (u_int32_t)(fTimestampFrequency*(tv.tv_usec/1000000.0) + 0.5); // note: rounding
// Then add this to our 'timestamp base':
if (fNextTimestampHasBeenPreset) {
// Make the returned timestamp the same as the current "fTimestampBase",
// so that timestamps begin with the value that was previously preset:
fTimestampBase -= timestampIncrement;
fNextTimestampHasBeenPreset = False;
}
u_int32_t const rtpTimestamp = fTimestampBase + timestampIncrement;
#ifdef DEBUG_TIMESTAMPS
fprintf(stderr, "fTimestampBase: 0x%08x, tv: %lu.%06ld\n\t=> RTP timestamp: 0x%08x\n",
fTimestampBase, tv.tv_sec, tv.tv_usec, rtpTimestamp);
fflush(stderr);
#endif
return rtpTimestamp;
}
u_int32_t RTPSink::presetNextTimestamp() {
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
u_int32_t tsNow = convertToRTPTimestamp(timeNow);
if (!groupsockBeingUsed().hasMultipleDestinations()) {
// Don't adjust the timestamp stream if we already have another destination ongoing
fTimestampBase = tsNow;
fNextTimestampHasBeenPreset = True;
}
return tsNow;
}
void RTPSink::getTotalBitrate(unsigned& outNumBytes, double& outElapsedTime) {
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
outNumBytes = fTotalOctetCount;
outElapsedTime = (double)(timeNow.tv_sec-fTotalOctetCountStartTime.tv_sec)
+ (timeNow.tv_usec-fTotalOctetCountStartTime.tv_usec)/1000000.0;
fTotalOctetCount = 0;
fTotalOctetCountStartTime = timeNow;
}
void RTPSink::resetPresentationTimes() {
fInitialPresentationTime.tv_sec = fMostRecentPresentationTime.tv_sec = 0;
fInitialPresentationTime.tv_usec = fMostRecentPresentationTime.tv_usec = 0;
}
char const* RTPSink::sdpMediaType() const {
return "data";
// default SDP media (m=) type, unless redefined by subclasses
}
char* RTPSink::rtpmapLine() const {
if (rtpPayloadType() >= 96) { // the payload format type is dynamic
char* encodingParamsPart;
if (numChannels() != 1) {
encodingParamsPart = new char[1 + 20 /* max int len */];
sprintf(encodingParamsPart, "/%d", numChannels());
} else {
encodingParamsPart = strDup("");
}
char const* const rtpmapFmt = "a=rtpmap:%d %s/%d%s\r\n";
unsigned rtpmapFmtSize = strlen(rtpmapFmt)
+ 3 /* max char len */ + strlen(rtpPayloadFormatName())
+ 20 /* max int len */ + strlen(encodingParamsPart);
char* rtpmapLine = new char[rtpmapFmtSize];
sprintf(rtpmapLine, rtpmapFmt,
rtpPayloadType(), rtpPayloadFormatName(),
rtpTimestampFrequency(), encodingParamsPart);
delete[] encodingParamsPart;
return rtpmapLine;
} else {
// The payload format is staic, so there's no "a=rtpmap:" line:
return strDup("");
}
}
char const* RTPSink::auxSDPLine() {
return NULL; // by default
}
////////// RTPTransmissionStatsDB //////////
RTPTransmissionStatsDB::RTPTransmissionStatsDB(RTPSink& rtpSink)
: fOurRTPSink(rtpSink),
fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
fNumReceivers=0;
}
RTPTransmissionStatsDB::~RTPTransmissionStatsDB() {
// First, remove and delete all stats records from the table:
RTPTransmissionStats* stats;
while ((stats = (RTPTransmissionStats*)fTable->RemoveNext()) != NULL) {
delete stats;
}
// Then, delete the table itself:
delete fTable;
}
void RTPTransmissionStatsDB
::noteIncomingRR(u_int32_t SSRC, struct sockaddr_in const& lastFromAddress,
unsigned lossStats, unsigned lastPacketNumReceived,
unsigned jitter, unsigned lastSRTime, unsigned diffSR_RRTime) {
RTPTransmissionStats* stats = lookup(SSRC);
if (stats == NULL) {
// This is the first time we've heard of this SSRC.
// Create a new record for it:
stats = new RTPTransmissionStats(fOurRTPSink, SSRC);
if (stats == NULL) return;
add(SSRC, stats);
#ifdef DEBUG_RR
fprintf(stderr, "Adding new entry for SSRC %x in RTPTransmissionStatsDB\n", SSRC);
#endif
}
stats->noteIncomingRR(lastFromAddress,
lossStats, lastPacketNumReceived, jitter,
lastSRTime, diffSR_RRTime);
}
void RTPTransmissionStatsDB::removeRecord(u_int32_t SSRC) {
RTPTransmissionStats* stats = lookup(SSRC);
if (stats != NULL) {
long SSRC_long = (long)SSRC;
fTable->Remove((char const*)SSRC_long);
--fNumReceivers;
delete stats;
}
}
RTPTransmissionStatsDB::Iterator
::Iterator(RTPTransmissionStatsDB& receptionStatsDB)
: fIter(HashTable::Iterator::create(*(receptionStatsDB.fTable))) {
}
RTPTransmissionStatsDB::Iterator::~Iterator() {
delete fIter;
}
RTPTransmissionStats*
RTPTransmissionStatsDB::Iterator::next() {
char const* key; // dummy
return (RTPTransmissionStats*)(fIter->next(key));
}
RTPTransmissionStats* RTPTransmissionStatsDB::lookup(u_int32_t SSRC) const {
long SSRC_long = (long)SSRC;
return (RTPTransmissionStats*)(fTable->Lookup((char const*)SSRC_long));
}
void RTPTransmissionStatsDB::add(u_int32_t SSRC, RTPTransmissionStats* stats) {
long SSRC_long = (long)SSRC;
fTable->Add((char const*)SSRC_long, stats);
++fNumReceivers;
}
////////// RTPTransmissionStats //////////
RTPTransmissionStats::RTPTransmissionStats(RTPSink& rtpSink, u_int32_t SSRC)
: fOurRTPSink(rtpSink), fSSRC(SSRC), fLastPacketNumReceived(0),
fPacketLossRatio(0), fTotNumPacketsLost(0), fJitter(0),
fLastSRTime(0), fDiffSR_RRTime(0), fAtLeastTwoRRsHaveBeenReceived(False), fFirstPacket(True),
fTotalOctetCount_hi(0), fTotalOctetCount_lo(0),
fTotalPacketCount_hi(0), fTotalPacketCount_lo(0) {
gettimeofday(&fTimeCreated, NULL);
fLastOctetCount = rtpSink.octetCount();
fLastPacketCount = rtpSink.packetCount();
}
RTPTransmissionStats::~RTPTransmissionStats() {}
void RTPTransmissionStats
::noteIncomingRR(struct sockaddr_in const& lastFromAddress,
unsigned lossStats, unsigned lastPacketNumReceived,
unsigned jitter, unsigned lastSRTime,
unsigned diffSR_RRTime) {
if (fFirstPacket) {
fFirstPacket = False;
fFirstPacketNumReported = lastPacketNumReceived;
} else {
fAtLeastTwoRRsHaveBeenReceived = True;
fOldLastPacketNumReceived = fLastPacketNumReceived;
fOldTotNumPacketsLost = fTotNumPacketsLost;
}
gettimeofday(&fTimeReceived, NULL);
fLastFromAddress = lastFromAddress;
fPacketLossRatio = lossStats>>24;
fTotNumPacketsLost = lossStats&0xFFFFFF;
fLastPacketNumReceived = lastPacketNumReceived;
fJitter = jitter;
fLastSRTime = lastSRTime;
fDiffSR_RRTime = diffSR_RRTime;
#ifdef DEBUG_RR
fprintf(stderr, "RTCP RR data (received at %lu.%06ld): lossStats 0x%08x, lastPacketNumReceived 0x%08x, jitter 0x%08x, lastSRTime 0x%08x, diffSR_RRTime 0x%08x\n",
fTimeReceived.tv_sec, fTimeReceived.tv_usec, lossStats, lastPacketNumReceived, jitter, lastSRTime, diffSR_RRTime);
unsigned rtd = roundTripDelay();
fprintf(stderr, "=> round-trip delay: 0x%04x (== %f seconds)\n", rtd, rtd/65536.0);
#endif
// Update our counts of the total number of octets and packets sent towards
// this receiver:
u_int32_t newOctetCount = fOurRTPSink.octetCount();
u_int32_t octetCountDiff = newOctetCount - fLastOctetCount;
fLastOctetCount = newOctetCount;
u_int32_t prevTotalOctetCount_lo = fTotalOctetCount_lo;
fTotalOctetCount_lo += octetCountDiff;
if (fTotalOctetCount_lo < prevTotalOctetCount_lo) { // wrap around
++fTotalOctetCount_hi;
}
u_int32_t newPacketCount = fOurRTPSink.packetCount();
u_int32_t packetCountDiff = newPacketCount - fLastPacketCount;
fLastPacketCount = newPacketCount;
u_int32_t prevTotalPacketCount_lo = fTotalPacketCount_lo;
fTotalPacketCount_lo += packetCountDiff;
if (fTotalPacketCount_lo < prevTotalPacketCount_lo) { // wrap around
++fTotalPacketCount_hi;
}
}
unsigned RTPTransmissionStats::roundTripDelay() const {
// Compute the round-trip delay that was indicated by the most recently-received
// RTCP RR packet. Use the method noted in the RTP/RTCP specification (RFC 3350).
if (fLastSRTime == 0) {
// Either no RTCP RR packet has been received yet, or else the
// reporting receiver has not yet received any RTCP SR packets from us:
return 0;
}
// First, convert the time that we received the last RTCP RR packet to NTP format,
// in units of 1/65536 (2^-16) seconds:
unsigned lastReceivedTimeNTP_high
= fTimeReceived.tv_sec + 0x83AA7E80; // 1970 epoch -> 1900 epoch
double fractionalPart = (fTimeReceived.tv_usec*0x0400)/15625.0; // 2^16/10^6
unsigned lastReceivedTimeNTP
= (unsigned)((lastReceivedTimeNTP_high<<16) + fractionalPart + 0.5);
int rawResult = lastReceivedTimeNTP - fLastSRTime - fDiffSR_RRTime;
if (rawResult < 0) {
// This can happen if there's clock drift between the sender and receiver,
// and if the round-trip time was very small.
rawResult = 0;
}
return (unsigned)rawResult;
}
void RTPTransmissionStats::getTotalOctetCount(u_int32_t& hi, u_int32_t& lo) {
hi = fTotalOctetCount_hi;
lo = fTotalOctetCount_lo;
}
void RTPTransmissionStats::getTotalPacketCount(u_int32_t& hi, u_int32_t& lo) {
hi = fTotalPacketCount_hi;
lo = fTotalPacketCount_lo;
}
unsigned RTPTransmissionStats::packetsReceivedSinceLastRR() const {
if (!fAtLeastTwoRRsHaveBeenReceived) return 0;
return fLastPacketNumReceived-fOldLastPacketNumReceived;
}
int RTPTransmissionStats::packetsLostBetweenRR() const {
if (!fAtLeastTwoRRsHaveBeenReceived) return 0;
return fTotNumPacketsLost - fOldTotNumPacketsLost;
}