1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15**********/
16// "liveMedia"
17// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
18// A 'ServerMediaSubsession' object that represents an existing
19// 'RTPSink', rather than one that creates new 'RTPSink's on demand.
20// Implementation
21
22#include "PassiveServerMediaSubsession.hh"
23#include <GroupsockHelper.hh>
24
25////////// PassiveServerMediaSubsession //////////
26
27PassiveServerMediaSubsession*
28PassiveServerMediaSubsession::createNew(RTPSink& rtpSink,
29 RTCPInstance* rtcpInstance) {
30 return new PassiveServerMediaSubsession(rtpSink, rtcpInstance);
31}
32
33PassiveServerMediaSubsession
34::PassiveServerMediaSubsession(RTPSink& rtpSink, RTCPInstance* rtcpInstance)
35 : ServerMediaSubsession(rtpSink.envir()),
36 fSDPLines(NULL), fRTPSink(rtpSink), fRTCPInstance(rtcpInstance) {
37 fClientRTCPSourceRecords = HashTable::create(ONE_WORD_HASH_KEYS);
38}
39
40class RTCPSourceRecord {
41public:
42 RTCPSourceRecord(netAddressBits addr, Port const& port)
43 : addr(addr), port(port) {
44 }
45
46 netAddressBits addr;
47 Port port;
48};
49
50PassiveServerMediaSubsession::~PassiveServerMediaSubsession() {
51 delete[] fSDPLines;
52
53 // Clean out the RTCPSourceRecord table:
54 while (1) {
55 RTCPSourceRecord* source = (RTCPSourceRecord*)(fClientRTCPSourceRecords->RemoveNext());
56 if (source == NULL) break;
57 delete source;
58 }
59
60 delete fClientRTCPSourceRecords;
61}
62
63Boolean PassiveServerMediaSubsession::rtcpIsMuxed() {
64 if (fRTCPInstance == NULL) return False;
65
66 // Check whether RTP and RTCP use the same "groupsock" object:
67 return &(fRTPSink.groupsockBeingUsed()) == fRTCPInstance->RTCPgs();
68}
69
70char const*
71PassiveServerMediaSubsession::sdpLines() {
72 if (fSDPLines == NULL ) {
73 // Construct a set of SDP lines that describe this subsession:
74 // Use the components from "rtpSink":
75 Groupsock const& gs = fRTPSink.groupsockBeingUsed();
76 AddressString groupAddressStr(gs.groupAddress());
77 unsigned short portNum = ntohs(gs.port().num());
78 unsigned char ttl = gs.ttl();
79 unsigned char rtpPayloadType = fRTPSink.rtpPayloadType();
80 char const* mediaType = fRTPSink.sdpMediaType();
81 unsigned estBitrate
82 = fRTCPInstance == NULL ? 50 : fRTCPInstance->totSessionBW();
83 char* rtpmapLine = fRTPSink.rtpmapLine();
84 char const* rtcpmuxLine = rtcpIsMuxed() ? "a=rtcp-mux\r\n" : "";
85 char const* rangeLine = rangeSDPLine();
86 char const* auxSDPLine = fRTPSink.auxSDPLine();
87 if (auxSDPLine == NULL) auxSDPLine = "";
88
89 char const* const sdpFmt =
90 "m=%s %d RTP/AVP %d\r\n"
91 "c=IN IP4 %s/%d\r\n"
92 "b=AS:%u\r\n"
93 "%s"
94 "%s"
95 "%s"
96 "%s"
97 "a=control:%s\r\n";
98 unsigned sdpFmtSize = strlen(sdpFmt)
99 + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */
100 + strlen(groupAddressStr.val()) + 3 /* max char len */
101 + 20 /* max int len */
102 + strlen(rtpmapLine)
103 + strlen(rtcpmuxLine)
104 + strlen(rangeLine)
105 + strlen(auxSDPLine)
106 + strlen(trackId());
107 char* sdpLines = new char[sdpFmtSize];
108 sprintf(sdpLines, sdpFmt,
109 mediaType, // m= <media>
110 portNum, // m= <port>
111 rtpPayloadType, // m= <fmt list>
112 groupAddressStr.val(), // c= <connection address>
113 ttl, // c= TTL
114 estBitrate, // b=AS:<bandwidth>
115 rtpmapLine, // a=rtpmap:... (if present)
116 rtcpmuxLine, // a=rtcp-mux:... (if present)
117 rangeLine, // a=range:... (if present)
118 auxSDPLine, // optional extra SDP line
119 trackId()); // a=control:<track-id>
120 delete[] (char*)rangeLine; delete[] rtpmapLine;
121
122 fSDPLines = strDup(sdpLines);
123 delete[] sdpLines;
124 }
125
126 return fSDPLines;
127}
128
129void PassiveServerMediaSubsession
130::getStreamParameters(unsigned clientSessionId,
131 netAddressBits clientAddress,
132 Port const& /*clientRTPPort*/,
133 Port const& clientRTCPPort,
134 int /*tcpSocketNum*/,
135 unsigned char /*rtpChannelId*/,
136 unsigned char /*rtcpChannelId*/,
137 netAddressBits& destinationAddress,
138 u_int8_t& destinationTTL,
139 Boolean& isMulticast,
140 Port& serverRTPPort,
141 Port& serverRTCPPort,
142 void*& streamToken) {
143 isMulticast = True;
144 Groupsock& gs = fRTPSink.groupsockBeingUsed();
145 if (destinationTTL == 255) destinationTTL = gs.ttl();
146 if (destinationAddress == 0) { // normal case
147 destinationAddress = gs.groupAddress().s_addr;
148 } else { // use the client-specified destination address instead:
149 struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;
150 gs.changeDestinationParameters(destinationAddr, 0, destinationTTL);
151 if (fRTCPInstance != NULL) {
152 Groupsock* rtcpGS = fRTCPInstance->RTCPgs();
153 rtcpGS->changeDestinationParameters(destinationAddr, 0, destinationTTL);
154 }
155 }
156 serverRTPPort = gs.port();
157 if (fRTCPInstance != NULL) {
158 Groupsock* rtcpGS = fRTCPInstance->RTCPgs();
159 serverRTCPPort = rtcpGS->port();
160 }
161 streamToken = NULL; // not used
162
163 // Make a record of this client's source - for RTCP RR handling:
164 RTCPSourceRecord* source = new RTCPSourceRecord(clientAddress, clientRTCPPort);
165 fClientRTCPSourceRecords->Add((char const*)clientSessionId, source);
166}
167
168void PassiveServerMediaSubsession::startStream(unsigned clientSessionId,
169 void* /*streamToken*/,
170 TaskFunc* rtcpRRHandler,
171 void* rtcpRRHandlerClientData,
172 unsigned short& rtpSeqNum,
173 unsigned& rtpTimestamp,
174 ServerRequestAlternativeByteHandler* /*serverRequestAlternativeByteHandler*/,
175 void* /*serverRequestAlternativeByteHandlerClientData*/) {
176 rtpSeqNum = fRTPSink.currentSeqNo();
177 rtpTimestamp = fRTPSink.presetNextTimestamp();
178
179 // Try to use a big send buffer for RTP - at least 0.1 second of
180 // specified bandwidth and at least 50 KB
181 unsigned streamBitrate = fRTCPInstance == NULL ? 50 : fRTCPInstance->totSessionBW(); // in kbps
182 unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
183 if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024;
184 increaseSendBufferTo(envir(), fRTPSink.groupsockBeingUsed().socketNum(), rtpBufSize);
185
186 if (fRTCPInstance != NULL) {
187 // Hack: Send a RTCP "SR" packet now, so that receivers will (likely) be able to
188 // get RTCP-synchronized presentation times immediately:
189 fRTCPInstance->sendReport();
190
191 // Set up the handler for incoming RTCP "RR" packets from this client:
192 RTCPSourceRecord* source = (RTCPSourceRecord*)(fClientRTCPSourceRecords->Lookup((char const*)clientSessionId));
193 if (source != NULL) {
194 fRTCPInstance->setSpecificRRHandler(source->addr, source->port,
195 rtcpRRHandler, rtcpRRHandlerClientData);
196 }
197 }
198}
199
200float PassiveServerMediaSubsession::getCurrentNPT(void* streamToken) {
201 // Return the elapsed time between our "RTPSink"s creation time, and the current time:
202 struct timeval const& creationTime = fRTPSink.creationTime(); // alias
203
204 struct timeval timeNow;
205 gettimeofday(&timeNow, NULL);
206
207 return (float)(timeNow.tv_sec - creationTime.tv_sec + (timeNow.tv_usec - creationTime.tv_usec)/1000000.0);
208}
209
210void PassiveServerMediaSubsession
211::getRTPSinkandRTCP(void* streamToken,
212 RTPSink const*& rtpSink, RTCPInstance const*& rtcp) {
213 rtpSink = &fRTPSink;
214 rtcp = fRTCPInstance;
215}
216
217void PassiveServerMediaSubsession::deleteStream(unsigned clientSessionId, void*& /*streamToken*/) {
218 // Lookup and remove the 'RTCPSourceRecord' for this client. Also turn off RTCP "RR" handling:
219 RTCPSourceRecord* source = (RTCPSourceRecord*)(fClientRTCPSourceRecords->Lookup((char const*)clientSessionId));
220 if (source != NULL) {
221 if (fRTCPInstance != NULL) {
222 fRTCPInstance->unsetSpecificRRHandler(source->addr, source->port);
223 }
224
225 fClientRTCPSourceRecords->Remove((char const*)clientSessionId);
226 delete source;
227 }
228}
229