/********** 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 .) 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. // A 'ServerMediaSubsession' object that represents an existing // 'RTPSink', rather than one that creates new 'RTPSink's on demand. // Implementation #include "PassiveServerMediaSubsession.hh" #include ////////// PassiveServerMediaSubsession ////////// PassiveServerMediaSubsession* PassiveServerMediaSubsession::createNew(RTPSink& rtpSink, RTCPInstance* rtcpInstance) { return new PassiveServerMediaSubsession(rtpSink, rtcpInstance); } PassiveServerMediaSubsession ::PassiveServerMediaSubsession(RTPSink& rtpSink, RTCPInstance* rtcpInstance) : ServerMediaSubsession(rtpSink.envir()), fSDPLines(NULL), fRTPSink(rtpSink), fRTCPInstance(rtcpInstance) { fClientRTCPSourceRecords = HashTable::create(ONE_WORD_HASH_KEYS); } class RTCPSourceRecord { public: RTCPSourceRecord(netAddressBits addr, Port const& port) : addr(addr), port(port) { } netAddressBits addr; Port port; }; PassiveServerMediaSubsession::~PassiveServerMediaSubsession() { delete[] fSDPLines; // Clean out the RTCPSourceRecord table: while (1) { RTCPSourceRecord* source = (RTCPSourceRecord*)(fClientRTCPSourceRecords->RemoveNext()); if (source == NULL) break; delete source; } delete fClientRTCPSourceRecords; } Boolean PassiveServerMediaSubsession::rtcpIsMuxed() { if (fRTCPInstance == NULL) return False; // Check whether RTP and RTCP use the same "groupsock" object: return &(fRTPSink.groupsockBeingUsed()) == fRTCPInstance->RTCPgs(); } char const* PassiveServerMediaSubsession::sdpLines() { if (fSDPLines == NULL ) { // Construct a set of SDP lines that describe this subsession: // Use the components from "rtpSink": Groupsock const& gs = fRTPSink.groupsockBeingUsed(); AddressString groupAddressStr(gs.groupAddress()); unsigned short portNum = ntohs(gs.port().num()); unsigned char ttl = gs.ttl(); unsigned char rtpPayloadType = fRTPSink.rtpPayloadType(); char const* mediaType = fRTPSink.sdpMediaType(); unsigned estBitrate = fRTCPInstance == NULL ? 50 : fRTCPInstance->totSessionBW(); char* rtpmapLine = fRTPSink.rtpmapLine(); char const* rtcpmuxLine = rtcpIsMuxed() ? "a=rtcp-mux\r\n" : ""; char const* rangeLine = rangeSDPLine(); char const* auxSDPLine = fRTPSink.auxSDPLine(); if (auxSDPLine == NULL) auxSDPLine = ""; char const* const sdpFmt = "m=%s %d RTP/AVP %d\r\n" "c=IN IP4 %s/%d\r\n" "b=AS:%u\r\n" "%s" "%s" "%s" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(groupAddressStr.val()) + 3 /* max char len */ + 20 /* max int len */ + strlen(rtpmapLine) + strlen(rtcpmuxLine) + strlen(rangeLine) + strlen(auxSDPLine) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= portNum, // m= rtpPayloadType, // m= groupAddressStr.val(), // c= ttl, // c= TTL estBitrate, // b=AS: rtpmapLine, // a=rtpmap:... (if present) rtcpmuxLine, // a=rtcp-mux:... (if present) rangeLine, // a=range:... (if present) auxSDPLine, // optional extra SDP line trackId()); // a=control: delete[] (char*)rangeLine; delete[] rtpmapLine; fSDPLines = strDup(sdpLines); delete[] sdpLines; } return fSDPLines; } void PassiveServerMediaSubsession ::getStreamParameters(unsigned clientSessionId, netAddressBits clientAddress, Port const& /*clientRTPPort*/, Port const& clientRTCPPort, int /*tcpSocketNum*/, unsigned char /*rtpChannelId*/, unsigned char /*rtcpChannelId*/, netAddressBits& destinationAddress, u_int8_t& destinationTTL, Boolean& isMulticast, Port& serverRTPPort, Port& serverRTCPPort, void*& streamToken) { isMulticast = True; Groupsock& gs = fRTPSink.groupsockBeingUsed(); if (destinationTTL == 255) destinationTTL = gs.ttl(); if (destinationAddress == 0) { // normal case destinationAddress = gs.groupAddress().s_addr; } else { // use the client-specified destination address instead: struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress; gs.changeDestinationParameters(destinationAddr, 0, destinationTTL); if (fRTCPInstance != NULL) { Groupsock* rtcpGS = fRTCPInstance->RTCPgs(); rtcpGS->changeDestinationParameters(destinationAddr, 0, destinationTTL); } } serverRTPPort = gs.port(); if (fRTCPInstance != NULL) { Groupsock* rtcpGS = fRTCPInstance->RTCPgs(); serverRTCPPort = rtcpGS->port(); } streamToken = NULL; // not used // Make a record of this client's source - for RTCP RR handling: RTCPSourceRecord* source = new RTCPSourceRecord(clientAddress, clientRTCPPort); fClientRTCPSourceRecords->Add((char const*)clientSessionId, source); } void PassiveServerMediaSubsession::startStream(unsigned clientSessionId, void* /*streamToken*/, TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData, unsigned short& rtpSeqNum, unsigned& rtpTimestamp, ServerRequestAlternativeByteHandler* /*serverRequestAlternativeByteHandler*/, void* /*serverRequestAlternativeByteHandlerClientData*/) { rtpSeqNum = fRTPSink.currentSeqNo(); rtpTimestamp = fRTPSink.presetNextTimestamp(); // Try to use a big send buffer for RTP - at least 0.1 second of // specified bandwidth and at least 50 KB unsigned streamBitrate = fRTCPInstance == NULL ? 50 : fRTCPInstance->totSessionBW(); // in kbps unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024; increaseSendBufferTo(envir(), fRTPSink.groupsockBeingUsed().socketNum(), rtpBufSize); if (fRTCPInstance != NULL) { // Hack: Send a RTCP "SR" packet now, so that receivers will (likely) be able to // get RTCP-synchronized presentation times immediately: fRTCPInstance->sendReport(); // Set up the handler for incoming RTCP "RR" packets from this client: RTCPSourceRecord* source = (RTCPSourceRecord*)(fClientRTCPSourceRecords->Lookup((char const*)clientSessionId)); if (source != NULL) { fRTCPInstance->setSpecificRRHandler(source->addr, source->port, rtcpRRHandler, rtcpRRHandlerClientData); } } } float PassiveServerMediaSubsession::getCurrentNPT(void* streamToken) { // Return the elapsed time between our "RTPSink"s creation time, and the current time: struct timeval const& creationTime = fRTPSink.creationTime(); // alias struct timeval timeNow; gettimeofday(&timeNow, NULL); return (float)(timeNow.tv_sec - creationTime.tv_sec + (timeNow.tv_usec - creationTime.tv_usec)/1000000.0); } void PassiveServerMediaSubsession ::getRTPSinkandRTCP(void* streamToken, RTPSink const*& rtpSink, RTCPInstance const*& rtcp) { rtpSink = &fRTPSink; rtcp = fRTCPInstance; } void PassiveServerMediaSubsession::deleteStream(unsigned clientSessionId, void*& /*streamToken*/) { // Lookup and remove the 'RTCPSourceRecord' for this client. Also turn off RTCP "RR" handling: RTCPSourceRecord* source = (RTCPSourceRecord*)(fClientRTCPSourceRecords->Lookup((char const*)clientSessionId)); if (source != NULL) { if (fRTCPInstance != NULL) { fRTCPInstance->unsetSpecificRRHandler(source->addr, source->port); } fClientRTCPSourceRecords->Remove((char const*)clientSessionId); delete source; } }