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 creates new, unicast, "RTPSink"s
19// on demand.
20// Implementation
21
22#include "OnDemandServerMediaSubsession.hh"
23#include <GroupsockHelper.hh>
24
25OnDemandServerMediaSubsession
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
45OnDemandServerMediaSubsession::~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
58char const*
59OnDemandServerMediaSubsession::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
85void 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
206void 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
229void 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
239void 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
256void 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
267void 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
285void 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
297float 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
313FramedSource* OnDemandServerMediaSubsession::getStreamSource(void* streamToken) {
314 if (streamToken == NULL) return NULL;
315
316 StreamState* streamState = (StreamState*)streamToken;
317 return streamState->mediaSource();
318}
319
320void 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
334void 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
361char const* OnDemandServerMediaSubsession
362::getAuxSDPLine(RTPSink* rtpSink, FramedSource* /*inputSource*/) {
363 // Default implementation:
364 return rtpSink == NULL ? NULL : rtpSink->auxSDPLine();
365}
366
367void OnDemandServerMediaSubsession::seekStreamSource(FramedSource* /*inputSource*/,
368 double& /*seekNPT*/, double /*streamDuration*/, u_int64_t& numBytes) {
369 // Default implementation: Do nothing
370 numBytes = 0;
371}
372
373void 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
380void OnDemandServerMediaSubsession
381::setStreamSourceScale(FramedSource* /*inputSource*/, float /*scale*/) {
382 // Default implementation: Do nothing
383}
384
385void OnDemandServerMediaSubsession
386::setStreamSourceDuration(FramedSource* /*inputSource*/, double /*streamDuration*/, u_int64_t& numBytes) {
387 // Default implementation: Do nothing
388 numBytes = 0;
389}
390
391void OnDemandServerMediaSubsession::closeStreamSource(FramedSource *inputSource) {
392 Medium::close(inputSource);
393}
394
395Groupsock* 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
401RTCPInstance* 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
408void OnDemandServerMediaSubsession
409::setRTCPAppPacketHandler(RTCPAppHandlerFunc* handler, void* clientData) {
410 fAppHandlerTask = handler;
411 fAppHandlerClientData = clientData;
412}
413
414void 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
423void 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
475static 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
489StreamState::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
501StreamState::~StreamState() {
502 reclaim();
503}
504
505void 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
563void StreamState::pause() {
564 if (fRTPSink != NULL) fRTPSink->stopPlaying();
565 if (fUDPSink != NULL) fUDPSink->stopPlaying();
566 fAreCurrentlyPlaying = False;
567}
568
569void 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
602void 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
609void 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