/********** 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 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 "fRTCPInterface" 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; }