| 1 | /********** |
| 2 | This library is free software; you can redistribute it and/or modify it under |
| 3 | the terms of the GNU Lesser General Public License as published by the |
| 4 | Free Software Foundation; either version 3 of the License, or (at your |
| 5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| 6 | |
| 7 | This library is distributed in the hope that it will be useful, but WITHOUT |
| 8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| 10 | more details. |
| 11 | |
| 12 | You should have received a copy of the GNU Lesser General Public License |
| 13 | along with this library; if not, write to the Free Software Foundation, Inc., |
| 14 | 51 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 creates new, unicast, "RTPSink"s |
| 19 | // on demand. |
| 20 | // Implementation |
| 21 | |
| 22 | #include "OnDemandServerMediaSubsession.hh" |
| 23 | #include <GroupsockHelper.hh> |
| 24 | |
| 25 | OnDemandServerMediaSubsession |
| 26 | ::OnDemandServerMediaSubsession(UsageEnvironment& env, |
| 27 | Boolean reuseFirstSource, |
| 28 | portNumBits initialPortNum, |
| 29 | Boolean multiplexRTCPWithRTP) |
| 30 | : ServerMediaSubsession(env), |
| 31 | fSDPLines(NULL), fReuseFirstSource(reuseFirstSource), |
| 32 | fMultiplexRTCPWithRTP(multiplexRTCPWithRTP), fLastStreamToken(NULL), |
| 33 | fAppHandlerTask(NULL), fAppHandlerClientData(NULL) { |
| 34 | fDestinationsHashTable = HashTable::create(ONE_WORD_HASH_KEYS); |
| 35 | if (fMultiplexRTCPWithRTP) { |
| 36 | fInitialPortNum = initialPortNum; |
| 37 | } else { |
| 38 | // Make sure RTP ports are even-numbered: |
| 39 | fInitialPortNum = (initialPortNum+1)&~1; |
| 40 | } |
| 41 | gethostname(fCNAME, sizeof fCNAME); |
| 42 | fCNAME[sizeof fCNAME-1] = '\0'; // just in case |
| 43 | } |
| 44 | |
| 45 | OnDemandServerMediaSubsession::~OnDemandServerMediaSubsession() { |
| 46 | delete[] fSDPLines; |
| 47 | |
| 48 | // Clean out the destinations hash table: |
| 49 | while (1) { |
| 50 | Destinations* destinations |
| 51 | = (Destinations*)(fDestinationsHashTable->RemoveNext()); |
| 52 | if (destinations == NULL) break; |
| 53 | delete destinations; |
| 54 | } |
| 55 | delete fDestinationsHashTable; |
| 56 | } |
| 57 | |
| 58 | char const* |
| 59 | OnDemandServerMediaSubsession::sdpLines() { |
| 60 | if (fSDPLines == NULL) { |
| 61 | // We need to construct a set of SDP lines that describe this |
| 62 | // subsession (as a unicast stream). To do so, we first create |
| 63 | // dummy (unused) source and "RTPSink" objects, |
| 64 | // whose parameters we use for the SDP lines: |
| 65 | unsigned estBitrate; |
| 66 | FramedSource* inputSource = createNewStreamSource(0, estBitrate); |
| 67 | if (inputSource == NULL) return NULL; // file not found |
| 68 | |
| 69 | struct in_addr dummyAddr; |
| 70 | dummyAddr.s_addr = 0; |
| 71 | Groupsock* dummyGroupsock = createGroupsock(dummyAddr, 0); |
| 72 | unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic |
| 73 | RTPSink* dummyRTPSink = createNewRTPSink(dummyGroupsock, rtpPayloadType, inputSource); |
| 74 | if (dummyRTPSink != NULL && dummyRTPSink->estimatedBitrate() > 0) estBitrate = dummyRTPSink->estimatedBitrate(); |
| 75 | |
| 76 | setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate); |
| 77 | Medium::close(dummyRTPSink); |
| 78 | delete dummyGroupsock; |
| 79 | closeStreamSource(inputSource); |
| 80 | } |
| 81 | |
| 82 | return fSDPLines; |
| 83 | } |
| 84 | |
| 85 | void OnDemandServerMediaSubsession |
| 86 | ::getStreamParameters(unsigned clientSessionId, |
| 87 | netAddressBits clientAddress, |
| 88 | Port const& clientRTPPort, |
| 89 | Port const& clientRTCPPort, |
| 90 | int tcpSocketNum, |
| 91 | unsigned char rtpChannelId, |
| 92 | unsigned char rtcpChannelId, |
| 93 | netAddressBits& destinationAddress, |
| 94 | u_int8_t& /*destinationTTL*/, |
| 95 | Boolean& isMulticast, |
| 96 | Port& serverRTPPort, |
| 97 | Port& serverRTCPPort, |
| 98 | void*& streamToken) { |
| 99 | if (destinationAddress == 0) destinationAddress = clientAddress; |
| 100 | struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress; |
| 101 | isMulticast = False; |
| 102 | |
| 103 | if (fLastStreamToken != NULL && fReuseFirstSource) { |
| 104 | // Special case: Rather than creating a new 'StreamState', |
| 105 | // we reuse the one that we've already created: |
| 106 | serverRTPPort = ((StreamState*)fLastStreamToken)->serverRTPPort(); |
| 107 | serverRTCPPort = ((StreamState*)fLastStreamToken)->serverRTCPPort(); |
| 108 | ++((StreamState*)fLastStreamToken)->referenceCount(); |
| 109 | streamToken = fLastStreamToken; |
| 110 | } else { |
| 111 | // Normal case: Create a new media source: |
| 112 | unsigned streamBitrate; |
| 113 | FramedSource* mediaSource |
| 114 | = createNewStreamSource(clientSessionId, streamBitrate); |
| 115 | |
| 116 | // Create 'groupsock' and 'sink' objects for the destination, |
| 117 | // using previously unused server port numbers: |
| 118 | RTPSink* rtpSink = NULL; |
| 119 | BasicUDPSink* udpSink = NULL; |
| 120 | Groupsock* rtpGroupsock = NULL; |
| 121 | Groupsock* rtcpGroupsock = NULL; |
| 122 | |
| 123 | if (clientRTPPort.num() != 0 || tcpSocketNum >= 0) { // Normal case: Create destinations |
| 124 | portNumBits serverPortNum; |
| 125 | if (clientRTCPPort.num() == 0) { |
| 126 | // We're streaming raw UDP (not RTP). Create a single groupsock: |
| 127 | NoReuse dummy(envir()); // ensures that we skip over ports that are already in use |
| 128 | for (serverPortNum = fInitialPortNum; ; ++serverPortNum) { |
| 129 | struct in_addr dummyAddr; dummyAddr.s_addr = 0; |
| 130 | |
| 131 | serverRTPPort = serverPortNum; |
| 132 | rtpGroupsock = createGroupsock(dummyAddr, serverRTPPort); |
| 133 | if (rtpGroupsock->socketNum() >= 0) break; // success |
| 134 | } |
| 135 | |
| 136 | udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock); |
| 137 | } else { |
| 138 | // Normal case: We're streaming RTP (over UDP or TCP). Create a pair of |
| 139 | // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even). |
| 140 | // (If we're multiplexing RTCP and RTP over the same port number, it can be odd or even.) |
| 141 | NoReuse dummy(envir()); // ensures that we skip over ports that are already in use |
| 142 | for (portNumBits serverPortNum = fInitialPortNum; ; ++serverPortNum) { |
| 143 | struct in_addr dummyAddr; dummyAddr.s_addr = 0; |
| 144 | |
| 145 | serverRTPPort = serverPortNum; |
| 146 | rtpGroupsock = createGroupsock(dummyAddr, serverRTPPort); |
| 147 | if (rtpGroupsock->socketNum() < 0) { |
| 148 | delete rtpGroupsock; |
| 149 | continue; // try again |
| 150 | } |
| 151 | |
| 152 | if (fMultiplexRTCPWithRTP) { |
| 153 | // Use the RTP 'groupsock' object for RTCP as well: |
| 154 | serverRTCPPort = serverRTPPort; |
| 155 | rtcpGroupsock = rtpGroupsock; |
| 156 | } else { |
| 157 | // Create a separate 'groupsock' object (with the next (odd) port number) for RTCP: |
| 158 | serverRTCPPort = ++serverPortNum; |
| 159 | rtcpGroupsock = createGroupsock(dummyAddr, serverRTCPPort); |
| 160 | if (rtcpGroupsock->socketNum() < 0) { |
| 161 | delete rtpGroupsock; |
| 162 | delete rtcpGroupsock; |
| 163 | continue; // try again |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | break; // success |
| 168 | } |
| 169 | |
| 170 | unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic |
| 171 | rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource); |
| 172 | if (rtpSink != NULL && rtpSink->estimatedBitrate() > 0) streamBitrate = rtpSink->estimatedBitrate(); |
| 173 | } |
| 174 | |
| 175 | // Turn off the destinations for each groupsock. They'll get set later |
| 176 | // (unless TCP is used instead): |
| 177 | if (rtpGroupsock != NULL) rtpGroupsock->removeAllDestinations(); |
| 178 | if (rtcpGroupsock != NULL) rtcpGroupsock->removeAllDestinations(); |
| 179 | |
| 180 | if (rtpGroupsock != NULL) { |
| 181 | // Try to use a big send buffer for RTP - at least 0.1 second of |
| 182 | // specified bandwidth and at least 50 KB |
| 183 | unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes |
| 184 | if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024; |
| 185 | increaseSendBufferTo(envir(), rtpGroupsock->socketNum(), rtpBufSize); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | // Set up the state of the stream. The stream will get started later: |
| 190 | streamToken = fLastStreamToken |
| 191 | = new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink, |
| 192 | streamBitrate, mediaSource, |
| 193 | rtpGroupsock, rtcpGroupsock); |
| 194 | } |
| 195 | |
| 196 | // Record these destinations as being for this client session id: |
| 197 | Destinations* destinations; |
| 198 | if (tcpSocketNum < 0) { // UDP |
| 199 | destinations = new Destinations(destinationAddr, clientRTPPort, clientRTCPPort); |
| 200 | } else { // TCP |
| 201 | destinations = new Destinations(tcpSocketNum, rtpChannelId, rtcpChannelId); |
| 202 | } |
| 203 | fDestinationsHashTable->Add((char const*)clientSessionId, destinations); |
| 204 | } |
| 205 | |
| 206 | void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId, |
| 207 | void* streamToken, |
| 208 | TaskFunc* rtcpRRHandler, |
| 209 | void* rtcpRRHandlerClientData, |
| 210 | unsigned short& rtpSeqNum, |
| 211 | unsigned& rtpTimestamp, |
| 212 | ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler, |
| 213 | void* serverRequestAlternativeByteHandlerClientData) { |
| 214 | StreamState* streamState = (StreamState*)streamToken; |
| 215 | Destinations* destinations |
| 216 | = (Destinations*)(fDestinationsHashTable->Lookup((char const*)clientSessionId)); |
| 217 | if (streamState != NULL) { |
| 218 | streamState->startPlaying(destinations, clientSessionId, |
| 219 | rtcpRRHandler, rtcpRRHandlerClientData, |
| 220 | serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData); |
| 221 | RTPSink* rtpSink = streamState->rtpSink(); // alias |
| 222 | if (rtpSink != NULL) { |
| 223 | rtpSeqNum = rtpSink->currentSeqNo(); |
| 224 | rtpTimestamp = rtpSink->presetNextTimestamp(); |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | void OnDemandServerMediaSubsession::pauseStream(unsigned /*clientSessionId*/, |
| 230 | void* streamToken) { |
| 231 | // Pausing isn't allowed if multiple clients are receiving data from |
| 232 | // the same source: |
| 233 | if (fReuseFirstSource) return; |
| 234 | |
| 235 | StreamState* streamState = (StreamState*)streamToken; |
| 236 | if (streamState != NULL) streamState->pause(); |
| 237 | } |
| 238 | |
| 239 | void OnDemandServerMediaSubsession::seekStream(unsigned /*clientSessionId*/, |
| 240 | void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes) { |
| 241 | numBytes = 0; // by default: unknown |
| 242 | |
| 243 | // Seeking isn't allowed if multiple clients are receiving data from the same source: |
| 244 | if (fReuseFirstSource) return; |
| 245 | |
| 246 | StreamState* streamState = (StreamState*)streamToken; |
| 247 | if (streamState != NULL && streamState->mediaSource() != NULL) { |
| 248 | seekStreamSource(streamState->mediaSource(), seekNPT, streamDuration, numBytes); |
| 249 | |
| 250 | streamState->startNPT() = (float)seekNPT; |
| 251 | RTPSink* rtpSink = streamState->rtpSink(); // alias |
| 252 | if (rtpSink != NULL) rtpSink->resetPresentationTimes(); |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | void OnDemandServerMediaSubsession::seekStream(unsigned /*clientSessionId*/, |
| 257 | void* streamToken, char*& absStart, char*& absEnd) { |
| 258 | // Seeking isn't allowed if multiple clients are receiving data from the same source: |
| 259 | if (fReuseFirstSource) return; |
| 260 | |
| 261 | StreamState* streamState = (StreamState*)streamToken; |
| 262 | if (streamState != NULL && streamState->mediaSource() != NULL) { |
| 263 | seekStreamSource(streamState->mediaSource(), absStart, absEnd); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | void OnDemandServerMediaSubsession::nullSeekStream(unsigned /*clientSessionId*/, void* streamToken, |
| 268 | double streamEndTime, u_int64_t& numBytes) { |
| 269 | numBytes = 0; // by default: unknown |
| 270 | |
| 271 | StreamState* streamState = (StreamState*)streamToken; |
| 272 | if (streamState != NULL && streamState->mediaSource() != NULL) { |
| 273 | // Because we're not seeking here, get the current NPT, and remember it as the new 'start' NPT: |
| 274 | streamState->startNPT() = getCurrentNPT(streamToken); |
| 275 | |
| 276 | double duration = streamEndTime - streamState->startNPT(); |
| 277 | if (duration < 0.0) duration = 0.0; |
| 278 | setStreamSourceDuration(streamState->mediaSource(), duration, numBytes); |
| 279 | |
| 280 | RTPSink* rtpSink = streamState->rtpSink(); // alias |
| 281 | if (rtpSink != NULL) rtpSink->resetPresentationTimes(); |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | void OnDemandServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/, |
| 286 | void* streamToken, float scale) { |
| 287 | // Changing the scale factor isn't allowed if multiple clients are receiving data |
| 288 | // from the same source: |
| 289 | if (fReuseFirstSource) return; |
| 290 | |
| 291 | StreamState* streamState = (StreamState*)streamToken; |
| 292 | if (streamState != NULL && streamState->mediaSource() != NULL) { |
| 293 | setStreamSourceScale(streamState->mediaSource(), scale); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | float OnDemandServerMediaSubsession::getCurrentNPT(void* streamToken) { |
| 298 | do { |
| 299 | if (streamToken == NULL) break; |
| 300 | |
| 301 | StreamState* streamState = (StreamState*)streamToken; |
| 302 | RTPSink* rtpSink = streamState->rtpSink(); |
| 303 | if (rtpSink == NULL) break; |
| 304 | |
| 305 | return streamState->startNPT() |
| 306 | + (rtpSink->mostRecentPresentationTime().tv_sec - rtpSink->initialPresentationTime().tv_sec) |
| 307 | + (rtpSink->mostRecentPresentationTime().tv_usec - rtpSink->initialPresentationTime().tv_usec)/1000000.0f; |
| 308 | } while (0); |
| 309 | |
| 310 | return 0.0; |
| 311 | } |
| 312 | |
| 313 | FramedSource* OnDemandServerMediaSubsession::getStreamSource(void* streamToken) { |
| 314 | if (streamToken == NULL) return NULL; |
| 315 | |
| 316 | StreamState* streamState = (StreamState*)streamToken; |
| 317 | return streamState->mediaSource(); |
| 318 | } |
| 319 | |
| 320 | void OnDemandServerMediaSubsession |
| 321 | ::getRTPSinkandRTCP(void* streamToken, |
| 322 | RTPSink const*& rtpSink, RTCPInstance const*& rtcp) { |
| 323 | if (streamToken == NULL) { |
| 324 | rtpSink = NULL; |
| 325 | rtcp = NULL; |
| 326 | return; |
| 327 | } |
| 328 | |
| 329 | StreamState* streamState = (StreamState*)streamToken; |
| 330 | rtpSink = streamState->rtpSink(); |
| 331 | rtcp = streamState->rtcpInstance(); |
| 332 | } |
| 333 | |
| 334 | void OnDemandServerMediaSubsession::deleteStream(unsigned clientSessionId, |
| 335 | void*& streamToken) { |
| 336 | StreamState* streamState = (StreamState*)streamToken; |
| 337 | |
| 338 | // Look up (and remove) the destinations for this client session: |
| 339 | Destinations* destinations |
| 340 | = (Destinations*)(fDestinationsHashTable->Lookup((char const*)clientSessionId)); |
| 341 | if (destinations != NULL) { |
| 342 | fDestinationsHashTable->Remove((char const*)clientSessionId); |
| 343 | |
| 344 | // Stop streaming to these destinations: |
| 345 | if (streamState != NULL) streamState->endPlaying(destinations, clientSessionId); |
| 346 | } |
| 347 | |
| 348 | // Delete the "StreamState" structure if it's no longer being used: |
| 349 | if (streamState != NULL) { |
| 350 | if (streamState->referenceCount() > 0) --streamState->referenceCount(); |
| 351 | if (streamState->referenceCount() == 0) { |
| 352 | delete streamState; |
| 353 | streamToken = NULL; |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | // Finally, delete the destinations themselves: |
| 358 | delete destinations; |
| 359 | } |
| 360 | |
| 361 | char const* OnDemandServerMediaSubsession |
| 362 | ::getAuxSDPLine(RTPSink* rtpSink, FramedSource* /*inputSource*/) { |
| 363 | // Default implementation: |
| 364 | return rtpSink == NULL ? NULL : rtpSink->auxSDPLine(); |
| 365 | } |
| 366 | |
| 367 | void OnDemandServerMediaSubsession::seekStreamSource(FramedSource* /*inputSource*/, |
| 368 | double& /*seekNPT*/, double /*streamDuration*/, u_int64_t& numBytes) { |
| 369 | // Default implementation: Do nothing |
| 370 | numBytes = 0; |
| 371 | } |
| 372 | |
| 373 | void OnDemandServerMediaSubsession::seekStreamSource(FramedSource* /*inputSource*/, |
| 374 | char*& absStart, char*& absEnd) { |
| 375 | // Default implementation: do nothing (but delete[] and assign "absStart" and "absEnd" to NULL, to show that we don't handle this) |
| 376 | delete[] absStart; absStart = NULL; |
| 377 | delete[] absEnd; absEnd = NULL; |
| 378 | } |
| 379 | |
| 380 | void OnDemandServerMediaSubsession |
| 381 | ::setStreamSourceScale(FramedSource* /*inputSource*/, float /*scale*/) { |
| 382 | // Default implementation: Do nothing |
| 383 | } |
| 384 | |
| 385 | void OnDemandServerMediaSubsession |
| 386 | ::setStreamSourceDuration(FramedSource* /*inputSource*/, double /*streamDuration*/, u_int64_t& numBytes) { |
| 387 | // Default implementation: Do nothing |
| 388 | numBytes = 0; |
| 389 | } |
| 390 | |
| 391 | void OnDemandServerMediaSubsession::closeStreamSource(FramedSource *inputSource) { |
| 392 | Medium::close(inputSource); |
| 393 | } |
| 394 | |
| 395 | Groupsock* OnDemandServerMediaSubsession |
| 396 | ::createGroupsock(struct in_addr const& addr, Port port) { |
| 397 | // Default implementation; may be redefined by subclasses: |
| 398 | return new Groupsock(envir(), addr, port, 255); |
| 399 | } |
| 400 | |
| 401 | RTCPInstance* OnDemandServerMediaSubsession |
| 402 | ::createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */ |
| 403 | unsigned char const* cname, RTPSink* sink) { |
| 404 | // Default implementation; may be redefined by subclasses: |
| 405 | return RTCPInstance::createNew(envir(), RTCPgs, totSessionBW, cname, sink, NULL/*we're a server*/); |
| 406 | } |
| 407 | |
| 408 | void OnDemandServerMediaSubsession |
| 409 | ::setRTCPAppPacketHandler(RTCPAppHandlerFunc* handler, void* clientData) { |
| 410 | fAppHandlerTask = handler; |
| 411 | fAppHandlerClientData = clientData; |
| 412 | } |
| 413 | |
| 414 | void OnDemandServerMediaSubsession |
| 415 | ::sendRTCPAppPacket(u_int8_t subtype, char const* name, |
| 416 | u_int8_t* appDependentData, unsigned appDependentDataSize) { |
| 417 | StreamState* streamState = (StreamState*)fLastStreamToken; |
| 418 | if (streamState != NULL) { |
| 419 | streamState->sendRTCPAppPacket(subtype, name, appDependentData, appDependentDataSize); |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | void OnDemandServerMediaSubsession |
| 424 | ::setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource, unsigned estBitrate) { |
| 425 | if (rtpSink == NULL) return; |
| 426 | |
| 427 | char const* mediaType = rtpSink->sdpMediaType(); |
| 428 | unsigned char rtpPayloadType = rtpSink->rtpPayloadType(); |
| 429 | AddressString ipAddressStr(fServerAddressForSDP); |
| 430 | char* rtpmapLine = rtpSink->rtpmapLine(); |
| 431 | char const* rtcpmuxLine = fMultiplexRTCPWithRTP ? "a=rtcp-mux\r\n" : "" ; |
| 432 | char const* rangeLine = rangeSDPLine(); |
| 433 | char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource); |
| 434 | if (auxSDPLine == NULL) auxSDPLine = "" ; |
| 435 | |
| 436 | char const* const sdpFmt = |
| 437 | "m=%s %u RTP/AVP %d\r\n" |
| 438 | "c=IN IP4 %s\r\n" |
| 439 | "b=AS:%u\r\n" |
| 440 | "%s" |
| 441 | "%s" |
| 442 | "%s" |
| 443 | "%s" |
| 444 | "a=control:%s\r\n" ; |
| 445 | unsigned sdpFmtSize = strlen(sdpFmt) |
| 446 | + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ |
| 447 | + strlen(ipAddressStr.val()) |
| 448 | + 20 /* max int len */ |
| 449 | + strlen(rtpmapLine) |
| 450 | + strlen(rtcpmuxLine) |
| 451 | + strlen(rangeLine) |
| 452 | + strlen(auxSDPLine) |
| 453 | + strlen(trackId()); |
| 454 | char* sdpLines = new char[sdpFmtSize]; |
| 455 | sprintf(sdpLines, sdpFmt, |
| 456 | mediaType, // m= <media> |
| 457 | fPortNumForSDP, // m= <port> |
| 458 | rtpPayloadType, // m= <fmt list> |
| 459 | ipAddressStr.val(), // c= address |
| 460 | estBitrate, // b=AS:<bandwidth> |
| 461 | rtpmapLine, // a=rtpmap:... (if present) |
| 462 | rtcpmuxLine, // a=rtcp-mux:... (if present) |
| 463 | rangeLine, // a=range:... (if present) |
| 464 | auxSDPLine, // optional extra SDP line |
| 465 | trackId()); // a=control:<track-id> |
| 466 | delete[] (char*)rangeLine; delete[] rtpmapLine; |
| 467 | |
| 468 | delete[] fSDPLines; fSDPLines = strDup(sdpLines); |
| 469 | delete[] sdpLines; |
| 470 | } |
| 471 | |
| 472 | |
| 473 | ////////// StreamState implementation ////////// |
| 474 | |
| 475 | static void afterPlayingStreamState(void* clientData) { |
| 476 | StreamState* streamState = (StreamState*)clientData; |
| 477 | if (streamState->streamDuration() == 0.0) { |
| 478 | // When the input stream ends, tear it down. This will cause a RTCP "BYE" |
| 479 | // to be sent to each client, teling it that the stream has ended. |
| 480 | // (Because the stream didn't have a known duration, there was no other |
| 481 | // way for clients to know when the stream ended.) |
| 482 | streamState->reclaim(); |
| 483 | } |
| 484 | // Otherwise, keep the stream alive, in case a client wants to |
| 485 | // subsequently re-play the stream starting from somewhere other than the end. |
| 486 | // (This can be done only on streams that have a known duration.) |
| 487 | } |
| 488 | |
| 489 | StreamState::StreamState(OnDemandServerMediaSubsession& master, |
| 490 | Port const& serverRTPPort, Port const& serverRTCPPort, |
| 491 | RTPSink* rtpSink, BasicUDPSink* udpSink, |
| 492 | unsigned totalBW, FramedSource* mediaSource, |
| 493 | Groupsock* rtpGS, Groupsock* rtcpGS) |
| 494 | : fMaster(master), fAreCurrentlyPlaying(False), fReferenceCount(1), |
| 495 | fServerRTPPort(serverRTPPort), fServerRTCPPort(serverRTCPPort), |
| 496 | fRTPSink(rtpSink), fUDPSink(udpSink), fStreamDuration(master.duration()), |
| 497 | fTotalBW(totalBW), fRTCPInstance(NULL) /* created later */, |
| 498 | fMediaSource(mediaSource), fStartNPT(0.0), fRTPgs(rtpGS), fRTCPgs(rtcpGS) { |
| 499 | } |
| 500 | |
| 501 | StreamState::~StreamState() { |
| 502 | reclaim(); |
| 503 | } |
| 504 | |
| 505 | void StreamState |
| 506 | ::startPlaying(Destinations* dests, unsigned clientSessionId, |
| 507 | TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData, |
| 508 | ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler, |
| 509 | void* serverRequestAlternativeByteHandlerClientData) { |
| 510 | if (dests == NULL) return; |
| 511 | |
| 512 | if (fRTCPInstance == NULL && fRTPSink != NULL) { |
| 513 | // Create (and start) a 'RTCP instance' for this RTP sink: |
| 514 | fRTCPInstance = fMaster.createRTCP(fRTCPgs, fTotalBW, (unsigned char*)fMaster.fCNAME, fRTPSink); |
| 515 | // Note: This starts RTCP running automatically |
| 516 | fRTCPInstance->setAppHandler(fMaster.fAppHandlerTask, fMaster.fAppHandlerClientData); |
| 517 | } |
| 518 | |
| 519 | if (dests->isTCP) { |
| 520 | // Change RTP and RTCP to use the TCP socket instead of UDP: |
| 521 | if (fRTPSink != NULL) { |
| 522 | fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId); |
| 523 | RTPInterface |
| 524 | ::setServerRequestAlternativeByteHandler(fRTPSink->envir(), dests->tcpSocketNum, |
| 525 | serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData); |
| 526 | // So that we continue to handle RTSP commands from the client |
| 527 | } |
| 528 | if (fRTCPInstance != NULL) { |
| 529 | fRTCPInstance->addStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId); |
| 530 | fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId, |
| 531 | rtcpRRHandler, rtcpRRHandlerClientData); |
| 532 | } |
| 533 | } else { |
| 534 | // Tell the RTP and RTCP 'groupsocks' about this destination |
| 535 | // (in case they don't already have it): |
| 536 | if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort, clientSessionId); |
| 537 | if (fRTCPgs != NULL && !(fRTCPgs == fRTPgs && dests->rtcpPort.num() == dests->rtpPort.num())) { |
| 538 | fRTCPgs->addDestination(dests->addr, dests->rtcpPort, clientSessionId); |
| 539 | } |
| 540 | if (fRTCPInstance != NULL) { |
| 541 | fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort, |
| 542 | rtcpRRHandler, rtcpRRHandlerClientData); |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | if (fRTCPInstance != NULL) { |
| 547 | // Hack: Send an initial RTCP "SR" packet, before the initial RTP packet, so that receivers will (likely) be able to |
| 548 | // get RTCP-synchronized presentation times immediately: |
| 549 | fRTCPInstance->sendReport(); |
| 550 | } |
| 551 | |
| 552 | if (!fAreCurrentlyPlaying && fMediaSource != NULL) { |
| 553 | if (fRTPSink != NULL) { |
| 554 | fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this); |
| 555 | fAreCurrentlyPlaying = True; |
| 556 | } else if (fUDPSink != NULL) { |
| 557 | fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this); |
| 558 | fAreCurrentlyPlaying = True; |
| 559 | } |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | void StreamState::pause() { |
| 564 | if (fRTPSink != NULL) fRTPSink->stopPlaying(); |
| 565 | if (fUDPSink != NULL) fUDPSink->stopPlaying(); |
| 566 | fAreCurrentlyPlaying = False; |
| 567 | } |
| 568 | |
| 569 | void StreamState::endPlaying(Destinations* dests, unsigned clientSessionId) { |
| 570 | #if 0 |
| 571 | // The following code is temporarily disabled, because it erroneously sends RTCP "BYE"s to all clients if multiple |
| 572 | // clients are streaming from the same data source (i.e., if "reuseFirstSource" is True), and we don't want that to happen |
| 573 | // if we're being called as a result of a single one of these clients having sent a "TEARDOWN" (rather than the whole stream |
| 574 | // having been closed, for all clients). |
| 575 | // This will be fixed for real later. |
| 576 | if (fRTCPInstance != NULL) { |
| 577 | // Hack: Explicitly send a RTCP "BYE" packet now, because the code below will prevent that from happening later, |
| 578 | // when "fRTCPInstance" gets deleted: |
| 579 | fRTCPInstance->sendBYE(); |
| 580 | } |
| 581 | #endif |
| 582 | |
| 583 | if (dests->isTCP) { |
| 584 | if (fRTPSink != NULL) { |
| 585 | RTPInterface::clearServerRequestAlternativeByteHandler(fRTPSink->envir(), dests->tcpSocketNum); |
| 586 | fRTPSink->removeStreamSocket(dests->tcpSocketNum, dests->rtpChannelId); |
| 587 | } |
| 588 | if (fRTCPInstance != NULL) { |
| 589 | fRTCPInstance->removeStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId); |
| 590 | fRTCPInstance->unsetSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId); |
| 591 | } |
| 592 | } else { |
| 593 | // Tell the RTP and RTCP 'groupsocks' to stop using these destinations: |
| 594 | if (fRTPgs != NULL) fRTPgs->removeDestination(clientSessionId); |
| 595 | if (fRTCPgs != NULL && fRTCPgs != fRTPgs) fRTCPgs->removeDestination(clientSessionId); |
| 596 | if (fRTCPInstance != NULL) { |
| 597 | fRTCPInstance->unsetSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort); |
| 598 | } |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | void StreamState::sendRTCPAppPacket(u_int8_t subtype, char const* name, |
| 603 | u_int8_t* appDependentData, unsigned appDependentDataSize) { |
| 604 | if (fRTCPInstance != NULL) { |
| 605 | fRTCPInstance->sendAppPacket(subtype, name, appDependentData, appDependentDataSize); |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | void StreamState::reclaim() { |
| 610 | // Delete allocated media objects |
| 611 | Medium::close(fRTCPInstance) /* will send a RTCP BYE */; fRTCPInstance = NULL; |
| 612 | Medium::close(fRTPSink); fRTPSink = NULL; |
| 613 | Medium::close(fUDPSink); fUDPSink = NULL; |
| 614 | |
| 615 | fMaster.closeStreamSource(fMediaSource); fMediaSource = NULL; |
| 616 | if (fMaster.fLastStreamToken == this) fMaster.fLastStreamToken = NULL; |
| 617 | |
| 618 | delete fRTPgs; |
| 619 | if (fRTCPgs != fRTPgs) delete fRTCPgs; |
| 620 | fRTPgs = NULL; fRTCPgs = NULL; |
| 621 | } |
| 622 | |