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 data structure that represents a session that consists of
19// potentially multiple (audio and/or video) sub-sessions
20// Implementation
21
22#include "liveMedia.hh"
23#include "Locale.hh"
24#include "GroupsockHelper.hh"
25#include <ctype.h>
26
27////////// MediaSession //////////
28
29MediaSession* MediaSession::createNew(UsageEnvironment& env,
30 char const* sdpDescription) {
31 MediaSession* newSession = new MediaSession(env);
32 if (newSession != NULL) {
33 if (!newSession->initializeWithSDP(sdpDescription)) {
34 delete newSession;
35 return NULL;
36 }
37 }
38
39 return newSession;
40}
41
42Boolean MediaSession::lookupByName(UsageEnvironment& env,
43 char const* instanceName,
44 MediaSession*& resultSession) {
45 resultSession = NULL; // unless we succeed
46
47 Medium* medium;
48 if (!Medium::lookupByName(env, instanceName, medium)) return False;
49
50 if (!medium->isMediaSession()) {
51 env.setResultMsg(instanceName, " is not a 'MediaSession' object");
52 return False;
53 }
54
55 resultSession = (MediaSession*)medium;
56 return True;
57}
58
59MediaSession::MediaSession(UsageEnvironment& env)
60 : Medium(env),
61 fSubsessionsHead(NULL), fSubsessionsTail(NULL),
62 fConnectionEndpointName(NULL),
63 fMaxPlayStartTime(0.0f), fMaxPlayEndTime(0.0f), fAbsStartTime(NULL), fAbsEndTime(NULL),
64 fScale(1.0f), fSpeed(1.0f),
65 fMediaSessionType(NULL), fSessionName(NULL), fSessionDescription(NULL), fControlPath(NULL) {
66 fSourceFilterAddr.s_addr = 0;
67
68 // Get our host name, and use this for the RTCP CNAME:
69 const unsigned maxCNAMElen = 100;
70 char CNAME[maxCNAMElen+1];
71#ifndef CRIS
72 gethostname((char*)CNAME, maxCNAMElen);
73#else
74 // "gethostname()" isn't defined for this platform
75 sprintf(CNAME, "unknown host %d", (unsigned)(our_random()*0x7FFFFFFF));
76#endif
77 CNAME[maxCNAMElen] = '\0'; // just in case
78 fCNAME = strDup(CNAME);
79}
80
81MediaSession::~MediaSession() {
82 delete fSubsessionsHead;
83 delete[] fCNAME;
84 delete[] fConnectionEndpointName;
85 delete[] fAbsStartTime; delete[] fAbsEndTime;
86 delete[] fMediaSessionType;
87 delete[] fSessionName;
88 delete[] fSessionDescription;
89 delete[] fControlPath;
90}
91
92Boolean MediaSession::isMediaSession() const {
93 return True;
94}
95
96MediaSubsession* MediaSession::createNewMediaSubsession() {
97 // default implementation:
98 return new MediaSubsession(*this);
99}
100
101Boolean MediaSession::initializeWithSDP(char const* sdpDescription) {
102 if (sdpDescription == NULL) return False;
103
104 // Begin by processing all SDP lines until we see the first "m="
105 char const* sdpLine = sdpDescription;
106 char const* nextSDPLine;
107 while (1) {
108 if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
109 //##### We should really check for the correct SDP version (v=0)
110 if (sdpLine[0] == 'm') break;
111 sdpLine = nextSDPLine;
112 if (sdpLine == NULL) break; // there are no m= lines at all
113
114 // Check for various special SDP lines that we understand:
115 if (parseSDPLine_s(sdpLine)) continue;
116 if (parseSDPLine_i(sdpLine)) continue;
117 if (parseSDPLine_c(sdpLine)) continue;
118 if (parseSDPAttribute_control(sdpLine)) continue;
119 if (parseSDPAttribute_range(sdpLine)) continue;
120 if (parseSDPAttribute_type(sdpLine)) continue;
121 if (parseSDPAttribute_source_filter(sdpLine)) continue;
122 }
123
124 while (sdpLine != NULL) {
125 // We have a "m=" line, representing a new sub-session:
126 MediaSubsession* subsession = createNewMediaSubsession();
127 if (subsession == NULL) {
128 envir().setResultMsg("Unable to create new MediaSubsession");
129 return False;
130 }
131
132 // Parse the line as "m=<medium_name> <client_portNum> RTP/AVP <fmt>"
133 // or "m=<medium_name> <client_portNum>/<num_ports> RTP/AVP <fmt>"
134 // (Should we be checking for >1 payload format number here?)#####
135 char* mediumName = strDupSize(sdpLine); // ensures we have enough space
136 char const* protocolName = NULL;
137 unsigned payloadFormat;
138 if ((sscanf(sdpLine, "m=%s %hu RTP/AVP %u",
139 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
140 sscanf(sdpLine, "m=%s %hu/%*u RTP/AVP %u",
141 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3)
142 && payloadFormat <= 127) {
143 protocolName = "RTP";
144 } else if ((sscanf(sdpLine, "m=%s %hu UDP %u",
145 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
146 sscanf(sdpLine, "m=%s %hu udp %u",
147 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
148 sscanf(sdpLine, "m=%s %hu RAW/RAW/UDP %u",
149 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3)
150 && payloadFormat <= 127) {
151 // This is a RAW UDP source
152 protocolName = "UDP";
153 } else {
154 // This "m=" line is bad; output an error message saying so:
155 char* sdpLineStr;
156 if (nextSDPLine == NULL) {
157 sdpLineStr = (char*)sdpLine;
158 } else {
159 sdpLineStr = strDup(sdpLine);
160 sdpLineStr[nextSDPLine-sdpLine] = '\0';
161 }
162 envir() << "Bad SDP \"m=\" line: " << sdpLineStr << "\n";
163 if (sdpLineStr != (char*)sdpLine) delete[] sdpLineStr;
164
165 delete[] mediumName;
166 delete subsession;
167
168 // Skip the following SDP lines, up until the next "m=":
169 while (1) {
170 sdpLine = nextSDPLine;
171 if (sdpLine == NULL) break; // we've reached the end
172 if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
173
174 if (sdpLine[0] == 'm') break; // we've reached the next subsession
175 }
176 continue;
177 }
178
179 // Insert this subsession at the end of the list:
180 if (fSubsessionsTail == NULL) {
181 fSubsessionsHead = fSubsessionsTail = subsession;
182 } else {
183 fSubsessionsTail->setNext(subsession);
184 fSubsessionsTail = subsession;
185 }
186
187 subsession->serverPortNum = subsession->fClientPortNum; // by default
188
189 char const* mStart = sdpLine;
190 subsession->fSavedSDPLines = strDup(mStart);
191
192 subsession->fMediumName = strDup(mediumName);
193 delete[] mediumName;
194 subsession->fProtocolName = strDup(protocolName);
195 subsession->fRTPPayloadFormat = payloadFormat;
196
197 // Process the following SDP lines, up until the next "m=":
198 while (1) {
199 sdpLine = nextSDPLine;
200 if (sdpLine == NULL) break; // we've reached the end
201 if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
202
203 if (sdpLine[0] == 'm') break; // we've reached the next subsession
204
205 // Check for various special SDP lines that we understand:
206 if (subsession->parseSDPLine_c(sdpLine)) continue;
207 if (subsession->parseSDPLine_b(sdpLine)) continue;
208 if (subsession->parseSDPAttribute_rtpmap(sdpLine)) continue;
209 if (subsession->parseSDPAttribute_rtcpmux(sdpLine)) continue;
210 if (subsession->parseSDPAttribute_control(sdpLine)) continue;
211 if (subsession->parseSDPAttribute_range(sdpLine)) continue;
212 if (subsession->parseSDPAttribute_fmtp(sdpLine)) continue;
213 if (subsession->parseSDPAttribute_source_filter(sdpLine)) continue;
214 if (subsession->parseSDPAttribute_x_dimensions(sdpLine)) continue;
215 if (subsession->parseSDPAttribute_framerate(sdpLine)) continue;
216
217 // (Later, check for malformed lines, and other valid SDP lines#####)
218 }
219 if (sdpLine != NULL) subsession->fSavedSDPLines[sdpLine-mStart] = '\0';
220
221 // If we don't yet know the codec name, try looking it up from the
222 // list of static payload types:
223 if (subsession->fCodecName == NULL) {
224 subsession->fCodecName
225 = lookupPayloadFormat(subsession->fRTPPayloadFormat,
226 subsession->fRTPTimestampFrequency,
227 subsession->fNumChannels);
228 if (subsession->fCodecName == NULL) {
229 char typeStr[20];
230 sprintf(typeStr, "%d", subsession->fRTPPayloadFormat);
231 envir().setResultMsg("Unknown codec name for RTP payload type ",
232 typeStr);
233 return False;
234 }
235 }
236
237 // If we don't yet know this subsession's RTP timestamp frequency
238 // (because it uses a dynamic payload type and the corresponding
239 // SDP "rtpmap" attribute erroneously didn't specify it),
240 // then guess it now:
241 if (subsession->fRTPTimestampFrequency == 0) {
242 subsession->fRTPTimestampFrequency
243 = guessRTPTimestampFrequency(subsession->fMediumName,
244 subsession->fCodecName);
245 }
246 }
247
248 return True;
249}
250
251Boolean MediaSession::parseSDPLine(char const* inputLine,
252 char const*& nextLine){
253 // Begin by finding the start of the next line (if any):
254 nextLine = NULL;
255 for (char const* ptr = inputLine; *ptr != '\0'; ++ptr) {
256 if (*ptr == '\r' || *ptr == '\n') {
257 // We found the end of the line
258 ++ptr;
259 while (*ptr == '\r' || *ptr == '\n') ++ptr;
260 nextLine = ptr;
261 if (nextLine[0] == '\0') nextLine = NULL; // special case for end
262 break;
263 }
264 }
265
266 // Then, check that this line is a SDP line of the form <char>=<etc>
267 // (However, we also accept blank lines in the input.)
268 if (inputLine[0] == '\r' || inputLine[0] == '\n') return True;
269 if (strlen(inputLine) < 2 || inputLine[1] != '='
270 || inputLine[0] < 'a' || inputLine[0] > 'z') {
271 envir().setResultMsg("Invalid SDP line: ", inputLine);
272 return False;
273 }
274
275 return True;
276}
277
278static char* parseCLine(char const* sdpLine) {
279 char* resultStr = NULL;
280 char* buffer = strDupSize(sdpLine); // ensures we have enough space
281 if (sscanf(sdpLine, "c=IN IP4 %[^/\r\n]", buffer) == 1) {
282 // Later, handle the optional /<ttl> and /<numAddresses> #####
283 resultStr = strDup(buffer);
284 }
285 delete[] buffer;
286
287 return resultStr;
288}
289
290Boolean MediaSession::parseSDPLine_s(char const* sdpLine) {
291 // Check for "s=<session name>" line
292 char* buffer = strDupSize(sdpLine);
293 Boolean parseSuccess = False;
294
295 if (sscanf(sdpLine, "s=%[^\r\n]", buffer) == 1) {
296 delete[] fSessionName; fSessionName = strDup(buffer);
297 parseSuccess = True;
298 }
299 delete[] buffer;
300
301 return parseSuccess;
302}
303
304Boolean MediaSession::parseSDPLine_i(char const* sdpLine) {
305 // Check for "i=<session description>" line
306 char* buffer = strDupSize(sdpLine);
307 Boolean parseSuccess = False;
308
309 if (sscanf(sdpLine, "i=%[^\r\n]", buffer) == 1) {
310 delete[] fSessionDescription; fSessionDescription = strDup(buffer);
311 parseSuccess = True;
312 }
313 delete[] buffer;
314
315 return parseSuccess;
316}
317
318Boolean MediaSession::parseSDPLine_c(char const* sdpLine) {
319 // Check for "c=IN IP4 <connection-endpoint>"
320 // or "c=IN IP4 <connection-endpoint>/<ttl+numAddresses>"
321 // (Later, do something with <ttl+numAddresses> also #####)
322 char* connectionEndpointName = parseCLine(sdpLine);
323 if (connectionEndpointName != NULL) {
324 delete[] fConnectionEndpointName;
325 fConnectionEndpointName = connectionEndpointName;
326 return True;
327 }
328
329 return False;
330}
331
332Boolean MediaSession::parseSDPAttribute_type(char const* sdpLine) {
333 // Check for a "a=type:broadcast|meeting|moderated|test|H.332|recvonly" line:
334 Boolean parseSuccess = False;
335
336 char* buffer = strDupSize(sdpLine);
337 if (sscanf(sdpLine, "a=type: %[^ ]", buffer) == 1) {
338 delete[] fMediaSessionType;
339 fMediaSessionType = strDup(buffer);
340 parseSuccess = True;
341 }
342 delete[] buffer;
343
344 return parseSuccess;
345}
346
347Boolean MediaSession::parseSDPAttribute_control(char const* sdpLine) {
348 // Check for a "a=control:<control-path>" line:
349 Boolean parseSuccess = False;
350
351 char* controlPath = strDupSize(sdpLine); // ensures we have enough space
352 if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) {
353 parseSuccess = True;
354 delete[] fControlPath; fControlPath = strDup(controlPath);
355 }
356 delete[] controlPath;
357
358 return parseSuccess;
359}
360
361static Boolean parseRangeAttribute(char const* sdpLine, double& startTime, double& endTime) {
362 return sscanf(sdpLine, "a=range: npt = %lg - %lg", &startTime, &endTime) == 2;
363}
364
365static Boolean parseRangeAttribute(char const* sdpLine, char*& absStartTime, char*& absEndTime) {
366 size_t len = strlen(sdpLine) + 1;
367 char* as = new char[len];
368 char* ae = new char[len];
369 int sscanfResult = sscanf(sdpLine, "a=range: clock = %[^-\r\n]-%[^\r\n]", as, ae);
370 if (sscanfResult == 2) {
371 absStartTime = as;
372 absEndTime = ae;
373 } else if (sscanfResult == 1) {
374 absStartTime = as;
375 delete[] ae;
376 } else {
377 delete[] as; delete[] ae;
378 return False;
379 }
380
381 return True;
382}
383
384Boolean MediaSession::parseSDPAttribute_range(char const* sdpLine) {
385 // Check for a "a=range:npt=<startTime>-<endTime>" line:
386 // (Later handle other kinds of "a=range" attributes also???#####)
387 Boolean parseSuccess = False;
388
389 double playStartTime;
390 double playEndTime;
391 if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) {
392 parseSuccess = True;
393 if (playStartTime > fMaxPlayStartTime) {
394 fMaxPlayStartTime = playStartTime;
395 }
396 if (playEndTime > fMaxPlayEndTime) {
397 fMaxPlayEndTime = playEndTime;
398 }
399 } else if (parseRangeAttribute(sdpLine, _absStartTime(), _absEndTime())) {
400 parseSuccess = True;
401 }
402
403 return parseSuccess;
404}
405
406static Boolean parseSourceFilterAttribute(char const* sdpLine,
407 struct in_addr& sourceAddr) {
408 // Check for a "a=source-filter:incl IN IP4 <something> <source>" line.
409 // Note: At present, we don't check that <something> really matches
410 // one of our multicast addresses. We also don't support more than
411 // one <source> #####
412 Boolean result = False; // until we succeed
413 char* sourceName = strDupSize(sdpLine); // ensures we have enough space
414 do {
415 if (sscanf(sdpLine, "a=source-filter: incl IN IP4 %*s %s",
416 sourceName) != 1) break;
417
418 // Now, convert this name to an address, if we can:
419 NetAddressList addresses(sourceName);
420 if (addresses.numAddresses() == 0) break;
421
422 netAddressBits sourceAddrBits
423 = *(netAddressBits*)(addresses.firstAddress()->data());
424 if (sourceAddrBits == 0) break;
425
426 sourceAddr.s_addr = sourceAddrBits;
427 result = True;
428 } while (0);
429
430 delete[] sourceName;
431 return result;
432}
433
434Boolean MediaSession
435::parseSDPAttribute_source_filter(char const* sdpLine) {
436 return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr);
437}
438
439char* MediaSession::lookupPayloadFormat(unsigned char rtpPayloadType,
440 unsigned& freq, unsigned& nCh) {
441 // Look up the codec name and timestamp frequency for known (static)
442 // RTP payload formats.
443 char const* temp = NULL;
444 switch (rtpPayloadType) {
445 case 0: {temp = "PCMU"; freq = 8000; nCh = 1; break;}
446 case 2: {temp = "G726-32"; freq = 8000; nCh = 1; break;}
447 case 3: {temp = "GSM"; freq = 8000; nCh = 1; break;}
448 case 4: {temp = "G723"; freq = 8000; nCh = 1; break;}
449 case 5: {temp = "DVI4"; freq = 8000; nCh = 1; break;}
450 case 6: {temp = "DVI4"; freq = 16000; nCh = 1; break;}
451 case 7: {temp = "LPC"; freq = 8000; nCh = 1; break;}
452 case 8: {temp = "PCMA"; freq = 8000; nCh = 1; break;}
453 case 9: {temp = "G722"; freq = 8000; nCh = 1; break;}
454 case 10: {temp = "L16"; freq = 44100; nCh = 2; break;}
455 case 11: {temp = "L16"; freq = 44100; nCh = 1; break;}
456 case 12: {temp = "QCELP"; freq = 8000; nCh = 1; break;}
457 case 14: {temp = "MPA"; freq = 90000; nCh = 1; break;}
458 // 'number of channels' is actually encoded in the media stream
459 case 15: {temp = "G728"; freq = 8000; nCh = 1; break;}
460 case 16: {temp = "DVI4"; freq = 11025; nCh = 1; break;}
461 case 17: {temp = "DVI4"; freq = 22050; nCh = 1; break;}
462 case 18: {temp = "G729"; freq = 8000; nCh = 1; break;}
463 case 25: {temp = "CELB"; freq = 90000; nCh = 1; break;}
464 case 26: {temp = "JPEG"; freq = 90000; nCh = 1; break;}
465 case 28: {temp = "NV"; freq = 90000; nCh = 1; break;}
466 case 31: {temp = "H261"; freq = 90000; nCh = 1; break;}
467 case 32: {temp = "MPV"; freq = 90000; nCh = 1; break;}
468 case 33: {temp = "MP2T"; freq = 90000; nCh = 1; break;}
469 case 34: {temp = "H263"; freq = 90000; nCh = 1; break;}
470 };
471
472 return strDup(temp);
473}
474
475unsigned MediaSession::guessRTPTimestampFrequency(char const* mediumName,
476 char const* codecName) {
477 // By default, we assume that audio sessions use a frequency of 8000,
478 // video sessions use a frequency of 90000,
479 // and text sessions use a frequency of 1000.
480 // Begin by checking for known exceptions to this rule
481 // (where the frequency is known unambiguously (e.g., not like "DVI4"))
482 if (strcmp(codecName, "L16") == 0) return 44100;
483 if (strcmp(codecName, "MPA") == 0
484 || strcmp(codecName, "MPA-ROBUST") == 0
485 || strcmp(codecName, "X-MP3-DRAFT-00") == 0) return 90000;
486
487 // Now, guess default values:
488 if (strcmp(mediumName, "video") == 0) return 90000;
489 else if (strcmp(mediumName, "text") == 0) return 1000;
490 return 8000; // for "audio", and any other medium
491}
492
493char* MediaSession::absStartTime() const {
494 if (fAbsStartTime != NULL) return fAbsStartTime;
495
496 // If a subsession has an 'absolute' start time, then use that:
497 MediaSubsessionIterator iter(*this);
498 MediaSubsession* subsession;
499 while ((subsession = iter.next()) != NULL) {
500 if (subsession->_absStartTime() != NULL) return subsession->_absStartTime();
501 }
502 return NULL;
503}
504
505char* MediaSession::absEndTime() const {
506 if (fAbsEndTime != NULL) return fAbsEndTime;
507
508 // If a subsession has an 'absolute' end time, then use that:
509 MediaSubsessionIterator iter(*this);
510 MediaSubsession* subsession;
511 while ((subsession = iter.next()) != NULL) {
512 if (subsession->_absEndTime() != NULL) return subsession->_absEndTime();
513 }
514 return NULL;
515}
516
517Boolean MediaSession
518::initiateByMediaType(char const* mimeType,
519 MediaSubsession*& resultSubsession,
520 int useSpecialRTPoffset) {
521 // Look through this session's subsessions for media that match "mimeType"
522 resultSubsession = NULL;
523 MediaSubsessionIterator iter(*this);
524 MediaSubsession* subsession;
525 while ((subsession = iter.next()) != NULL) {
526 Boolean wasAlreadyInitiated = subsession->readSource() != NULL;
527 if (!wasAlreadyInitiated) {
528 // Try to create a source for this subsession:
529 if (!subsession->initiate(useSpecialRTPoffset)) return False;
530 }
531
532 // Make sure the source's MIME type is one that we handle:
533 if (strcmp(subsession->readSource()->MIMEtype(), mimeType) != 0) {
534 if (!wasAlreadyInitiated) subsession->deInitiate();
535 continue;
536 }
537
538 resultSubsession = subsession;
539 break; // use this
540 }
541
542 if (resultSubsession == NULL) {
543 envir().setResultMsg("Session has no usable media subsession");
544 return False;
545 }
546
547 return True;
548}
549
550
551////////// MediaSubsessionIterator //////////
552
553MediaSubsessionIterator::MediaSubsessionIterator(MediaSession const& session)
554 : fOurSession(session) {
555 reset();
556}
557
558MediaSubsessionIterator::~MediaSubsessionIterator() {
559}
560
561MediaSubsession* MediaSubsessionIterator::next() {
562 MediaSubsession* result = fNextPtr;
563
564 if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext;
565
566 return result;
567}
568
569void MediaSubsessionIterator::reset() {
570 fNextPtr = fOurSession.fSubsessionsHead;
571}
572
573
574////////// SDPAttribute definition //////////
575
576class SDPAttribute {
577public:
578 SDPAttribute(char const* strValue, Boolean valueIsHexadecimal);
579 virtual ~SDPAttribute();
580
581 char const* strValue() const { return fStrValue; }
582 char const* strValueToLower() const { return fStrValueToLower; }
583 int intValue() const { return fIntValue; }
584 Boolean valueIsHexadecimal() const { return fValueIsHexadecimal; }
585
586private:
587 char* fStrValue;
588 char* fStrValueToLower;
589 int fIntValue;
590 Boolean fValueIsHexadecimal;
591};
592
593
594////////// MediaSubsession //////////
595
596MediaSubsession::MediaSubsession(MediaSession& parent)
597 : serverPortNum(0), sink(NULL), miscPtr(NULL),
598 fParent(parent), fNext(NULL),
599 fConnectionEndpointName(NULL),
600 fClientPortNum(0), fRTPPayloadFormat(0xFF),
601 fSavedSDPLines(NULL), fMediumName(NULL), fCodecName(NULL), fProtocolName(NULL),
602 fRTPTimestampFrequency(0), fMultiplexRTCPWithRTP(False), fControlPath(NULL),
603 fSourceFilterAddr(parent.sourceFilterAddr()), fBandwidth(0),
604 fPlayStartTime(0.0), fPlayEndTime(0.0), fAbsStartTime(NULL), fAbsEndTime(NULL),
605 fVideoWidth(0), fVideoHeight(0), fVideoFPS(0), fNumChannels(1), fScale(1.0f), fNPT_PTS_Offset(0.0f),
606 fAttributeTable(HashTable::create(STRING_HASH_KEYS)),
607 fRTPSocket(NULL), fRTCPSocket(NULL),
608 fRTPSource(NULL), fRTCPInstance(NULL), fReadSource(NULL),
609 fReceiveRawMP3ADUs(False), fReceiveRawJPEGFrames(False),
610 fSessionId(NULL) {
611 rtpInfo.seqNum = 0; rtpInfo.timestamp = 0; rtpInfo.infoIsNew = False;
612
613 // A few attributes have unusual default values. Set these now:
614 setAttribute("profile-level-id", "0", True/*value is hexadecimal*/); // used with "video/H264"
615 // This won't work for MPEG-4 (unless the value is <10), because for MPEG-4, the value
616 // is assumed to be a decimal string, not a hexadecimal string. NEED TO FIX #####
617 setAttribute("profile-id", "1"); // used with "video/H265"
618 setAttribute("level-id", "93"); // used with "video/H265"
619 setAttribute("interop-constraints", "B00000000000"); // used with "video/H265"
620 setAttribute("sampling", "RGB"); // used with "video/JPEG2000"
621}
622
623MediaSubsession::~MediaSubsession() {
624 deInitiate();
625
626 delete[] fConnectionEndpointName; delete[] fSavedSDPLines;
627 delete[] fMediumName; delete[] fCodecName; delete[] fProtocolName;
628 delete[] fControlPath;
629 delete[] fAbsStartTime; delete[] fAbsEndTime;
630 delete[] fSessionId;
631
632 // Empty and delete our 'attributes table':
633 SDPAttribute* attr;
634 while ((attr = (SDPAttribute*)fAttributeTable->RemoveNext()) != NULL) {
635 delete attr;
636 }
637 delete fAttributeTable;
638
639 delete fNext;
640}
641
642void MediaSubsession::addFilter(FramedFilter* filter){
643 fReadSource = filter;
644}
645
646double MediaSubsession::playStartTime() const {
647 if (fPlayStartTime > 0) return fPlayStartTime;
648
649 return fParent.playStartTime();
650}
651
652double MediaSubsession::playEndTime() const {
653 if (fPlayEndTime > 0) return fPlayEndTime;
654
655 return fParent.playEndTime();
656}
657
658char* MediaSubsession::absStartTime() const {
659 if (fAbsStartTime != NULL) return fAbsStartTime;
660
661 return fParent.absStartTime();
662}
663
664char* MediaSubsession::absEndTime() const {
665 if (fAbsEndTime != NULL) return fAbsEndTime;
666
667 return fParent.absEndTime();
668}
669
670static Boolean const honorSDPPortChoice
671#ifdef IGNORE_UNICAST_SDP_PORTS
672= False;
673#else
674= True;
675#endif
676
677Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
678 if (fReadSource != NULL) return True; // has already been initiated
679
680 do {
681 if (fCodecName == NULL) {
682 env().setResultMsg("Codec is unspecified");
683 break;
684 }
685
686 // Create RTP and RTCP 'Groupsocks' on which to receive incoming data.
687 // (Groupsocks will work even for unicast addresses)
688 struct in_addr tempAddr;
689 tempAddr.s_addr = connectionEndpointAddress();
690 // This could get changed later, as a result of a RTSP "SETUP"
691
692 if (fClientPortNum != 0 && (honorSDPPortChoice || IsMulticastAddress(tempAddr.s_addr))) {
693 // The sockets' port numbers were specified for us. Use these:
694 Boolean const protocolIsRTP = strcmp(fProtocolName, "RTP") == 0;
695 if (protocolIsRTP && !fMultiplexRTCPWithRTP) {
696 fClientPortNum = fClientPortNum&~1;
697 // use an even-numbered port for RTP, and the next (odd-numbered) port for RTCP
698 }
699 if (isSSM()) {
700 fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, fClientPortNum);
701 } else {
702 fRTPSocket = new Groupsock(env(), tempAddr, fClientPortNum, 255);
703 }
704 if (fRTPSocket == NULL) {
705 env().setResultMsg("Failed to create RTP socket");
706 break;
707 }
708
709 if (protocolIsRTP) {
710 if (fMultiplexRTCPWithRTP) {
711 // Use the RTP 'groupsock' object for RTCP as well:
712 fRTCPSocket = fRTPSocket;
713 } else {
714 // Set our RTCP port to be the RTP port + 1:
715 portNumBits const rtcpPortNum = fClientPortNum|1;
716 if (isSSM()) {
717 fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
718 } else {
719 fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
720 }
721 }
722 }
723 } else {
724 // Port numbers were not specified in advance, so we use ephemeral port numbers.
725 // Create sockets until we get a port-number pair (even: RTP; even+1: RTCP).
726 // (However, if we're multiplexing RTCP with RTP, then we create only one socket,
727 // and the port number can be even or odd.)
728 // We need to make sure that we don't keep trying to use the same bad port numbers over
729 // and over again, so we store bad sockets in a table, and delete them all when we're done.
730 HashTable* socketHashTable = HashTable::create(ONE_WORD_HASH_KEYS);
731 if (socketHashTable == NULL) break;
732 Boolean success = False;
733 NoReuse dummy(env());
734 // ensures that our new ephemeral port number won't be one that's already in use
735
736 while (1) {
737 // Create a new socket:
738 if (isSSM()) {
739 fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, 0);
740 } else {
741 fRTPSocket = new Groupsock(env(), tempAddr, 0, 255);
742 }
743 if (fRTPSocket == NULL) {
744 env().setResultMsg("MediaSession::initiate(): unable to create RTP and RTCP sockets");
745 break;
746 }
747
748 // Get the client port number:
749 Port clientPort(0);
750 if (!getSourcePort(env(), fRTPSocket->socketNum(), clientPort)) {
751 break;
752 }
753 fClientPortNum = ntohs(clientPort.num());
754
755 if (fMultiplexRTCPWithRTP) {
756 // Use this RTP 'groupsock' object for RTCP as well:
757 fRTCPSocket = fRTPSocket;
758 success = True;
759 break;
760 }
761
762 // To be usable for RTP, the client port number must be even:
763 if ((fClientPortNum&1) != 0) { // it's odd
764 // Record this socket in our table, and keep trying:
765 unsigned key = (unsigned)fClientPortNum;
766 Groupsock* existing = (Groupsock*)socketHashTable->Add((char const*)key, fRTPSocket);
767 delete existing; // in case it wasn't NULL
768 continue;
769 }
770
771 // Make sure we can use the next (i.e., odd) port number, for RTCP:
772 portNumBits rtcpPortNum = fClientPortNum|1;
773 if (isSSM()) {
774 fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
775 } else {
776 fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
777 }
778 if (fRTCPSocket != NULL && fRTCPSocket->socketNum() >= 0) {
779 // Success! Use these two sockets.
780 success = True;
781 break;
782 } else {
783 // We couldn't create the RTCP socket (perhaps that port number's already in use elsewhere?).
784 delete fRTCPSocket; fRTCPSocket = NULL;
785
786 // Record the first socket in our table, and keep trying:
787 unsigned key = (unsigned)fClientPortNum;
788 Groupsock* existing = (Groupsock*)socketHashTable->Add((char const*)key, fRTPSocket);
789 delete existing; // in case it wasn't NULL
790 continue;
791 }
792 }
793
794 // Clean up the socket hash table (and contents):
795 Groupsock* oldGS;
796 while ((oldGS = (Groupsock*)socketHashTable->RemoveNext()) != NULL) {
797 delete oldGS;
798 }
799 delete socketHashTable;
800
801 if (!success) break; // a fatal error occurred trying to create the RTP and RTCP sockets; we can't continue
802 }
803
804 // Try to use a big receive buffer for RTP - at least 0.1 second of
805 // specified bandwidth and at least 50 KB
806 unsigned rtpBufSize = fBandwidth * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
807 if (rtpBufSize < 50 * 1024)
808 rtpBufSize = 50 * 1024;
809 increaseReceiveBufferTo(env(), fRTPSocket->socketNum(), rtpBufSize);
810
811 if (isSSM() && fRTCPSocket != NULL) {
812 // Special case for RTCP SSM: Send RTCP packets back to the source via unicast:
813 fRTCPSocket->changeDestinationParameters(fSourceFilterAddr,0,~0);
814 }
815
816 // Create "fRTPSource" and "fReadSource":
817 if (!createSourceObjects(useSpecialRTPoffset)) break;
818
819 if (fReadSource == NULL) {
820 env().setResultMsg("Failed to create read source");
821 break;
822 }
823
824 // Finally, create our RTCP instance. (It starts running automatically)
825 if (fRTPSource != NULL && fRTCPSocket != NULL) {
826 // If bandwidth is specified, use it and add 5% for RTCP overhead.
827 // Otherwise make a guess at 500 kbps.
828 unsigned totSessionBandwidth
829 = fBandwidth ? fBandwidth + fBandwidth / 20 : 500;
830 fRTCPInstance = RTCPInstance::createNew(env(), fRTCPSocket,
831 totSessionBandwidth,
832 (unsigned char const*)
833 fParent.CNAME(),
834 NULL /* we're a client */,
835 fRTPSource);
836 if (fRTCPInstance == NULL) {
837 env().setResultMsg("Failed to create RTCP instance");
838 break;
839 }
840 }
841
842 return True;
843 } while (0);
844
845 deInitiate();
846 fClientPortNum = 0;
847 return False;
848}
849
850void MediaSubsession::deInitiate() {
851 Medium::close(fRTCPInstance); fRTCPInstance = NULL;
852
853 Medium::close(fReadSource); // this is assumed to also close fRTPSource
854 fReadSource = NULL; fRTPSource = NULL;
855
856 delete fRTPSocket;
857 if (fRTCPSocket != fRTPSocket) delete fRTCPSocket;
858 fRTPSocket = NULL; fRTCPSocket = NULL;
859}
860
861Boolean MediaSubsession::setClientPortNum(unsigned short portNum) {
862 if (fReadSource != NULL) {
863 env().setResultMsg("A read source has already been created");
864 return False;
865 }
866
867 fClientPortNum = portNum;
868 return True;
869}
870
871char const* MediaSubsession::attrVal_str(char const* attrName) const {
872 SDPAttribute* attr = (SDPAttribute*)(fAttributeTable->Lookup(attrName));
873 if (attr == NULL) return "";
874
875 return attr->strValue();
876}
877
878char const* MediaSubsession::attrVal_strToLower(char const* attrName) const {
879 SDPAttribute* attr = (SDPAttribute*)(fAttributeTable->Lookup(attrName));
880 if (attr == NULL) return "";
881
882 return attr->strValueToLower();
883}
884
885unsigned MediaSubsession::attrVal_int(char const* attrName) const {
886 SDPAttribute* attr = (SDPAttribute*)(fAttributeTable->Lookup(attrName));
887 if (attr == NULL) return 0;
888
889 return attr->intValue();
890}
891
892char const* MediaSubsession::fmtp_config() const {
893 char const* result = attrVal_str("config");
894 if (result[0] == '\0') result = attrVal_str("configuration");
895
896 return result;
897}
898
899netAddressBits MediaSubsession::connectionEndpointAddress() const {
900 do {
901 // Get the endpoint name from with us, or our parent session:
902 char const* endpointString = connectionEndpointName();
903 if (endpointString == NULL) {
904 endpointString = parentSession().connectionEndpointName();
905 }
906 if (endpointString == NULL) break;
907
908 // Now, convert this name to an address, if we can:
909 NetAddressList addresses(endpointString);
910 if (addresses.numAddresses() == 0) break;
911
912 return *(netAddressBits*)(addresses.firstAddress()->data());
913 } while (0);
914
915 // No address known:
916 return 0;
917}
918
919void MediaSubsession::setDestinations(netAddressBits defaultDestAddress) {
920 // Get the destination address from the connection endpoint name
921 // (This will be 0 if it's not known, in which case we use the default)
922 netAddressBits destAddress = connectionEndpointAddress();
923 if (destAddress == 0) destAddress = defaultDestAddress;
924 struct in_addr destAddr; destAddr.s_addr = destAddress;
925
926 // The destination TTL remains unchanged:
927 int destTTL = ~0; // means: don't change
928
929 if (fRTPSocket != NULL) {
930 Port destPort(serverPortNum);
931 fRTPSocket->changeDestinationParameters(destAddr, destPort, destTTL);
932 }
933 if (fRTCPSocket != NULL && !isSSM() && !fMultiplexRTCPWithRTP) {
934 // Note: For SSM sessions, the dest address for RTCP was already set.
935 Port destPort(serverPortNum+1);
936 fRTCPSocket->changeDestinationParameters(destAddr, destPort, destTTL);
937 }
938}
939
940void MediaSubsession::setSessionId(char const* sessionId) {
941 delete[] fSessionId;
942 fSessionId = strDup(sessionId);
943}
944
945double MediaSubsession::getNormalPlayTime(struct timeval const& presentationTime) {
946 if (rtpSource() == NULL || rtpSource()->timestampFrequency() == 0) return 0.0; // no RTP source, or bad freq!
947
948 // First, check whether our "RTPSource" object has already been synchronized using RTCP.
949 // If it hasn't, then - as a special case - we need to use the RTP timestamp to compute the NPT.
950 if (!rtpSource()->hasBeenSynchronizedUsingRTCP()) {
951 if (!rtpInfo.infoIsNew) return 0.0; // the "rtpInfo" structure has not been filled in
952 u_int32_t timestampOffset = rtpSource()->curPacketRTPTimestamp() - rtpInfo.timestamp;
953 double nptOffset = (timestampOffset/(double)(rtpSource()->timestampFrequency()))*scale();
954 double npt = playStartTime() + nptOffset;
955
956 return npt;
957 } else {
958 // Common case: We have been synchronized using RTCP. This means that the "presentationTime" parameter
959 // will be accurate, and so we should use this to compute the NPT.
960 double ptsDouble = (double)(presentationTime.tv_sec + presentationTime.tv_usec/1000000.0);
961
962 if (rtpInfo.infoIsNew) {
963 // This is the first time we've been called with a synchronized presentation time since the "rtpInfo"
964 // structure was last filled in. Use this "presentationTime" to compute "fNPT_PTS_Offset":
965 if (seqNumLT(rtpSource()->curPacketRTPSeqNum(), rtpInfo.seqNum)) return -0.1; // sanity check; ignore old packets
966 u_int32_t timestampOffset = rtpSource()->curPacketRTPTimestamp() - rtpInfo.timestamp;
967 double nptOffset = (timestampOffset/(double)(rtpSource()->timestampFrequency()))*scale();
968 double npt = playStartTime() + nptOffset;
969 fNPT_PTS_Offset = npt - ptsDouble*scale();
970 rtpInfo.infoIsNew = False; // for next time
971
972 return npt;
973 } else {
974 // Use the precomputed "fNPT_PTS_Offset" to compute the NPT from the PTS:
975 if (fNPT_PTS_Offset == 0.0) return 0.0; // error: The "rtpInfo" structure was apparently never filled in
976 return (double)(ptsDouble*scale() + fNPT_PTS_Offset);
977 }
978 }
979}
980
981void MediaSubsession
982::setAttribute(char const* name, char const* value, Boolean valueIsHexadecimal) {
983 // Replace any existing attribute record with this name (except that the 'valueIsHexadecimal'
984 // property will be inherited from it, if it exists).
985 SDPAttribute* oldAttr = (SDPAttribute*)fAttributeTable->Lookup(name);
986 if (oldAttr != NULL) {
987 valueIsHexadecimal = oldAttr->valueIsHexadecimal();
988 fAttributeTable->Remove(name);
989 delete oldAttr;
990 }
991
992 SDPAttribute* newAttr = new SDPAttribute(value, valueIsHexadecimal);
993 (void)fAttributeTable->Add(name, newAttr);
994}
995
996Boolean MediaSubsession::parseSDPLine_c(char const* sdpLine) {
997 // Check for "c=IN IP4 <connection-endpoint>"
998 // or "c=IN IP4 <connection-endpoint>/<ttl+numAddresses>"
999 // (Later, do something with <ttl+numAddresses> also #####)
1000 char* connectionEndpointName = parseCLine(sdpLine);
1001 if (connectionEndpointName != NULL) {
1002 delete[] fConnectionEndpointName;
1003 fConnectionEndpointName = connectionEndpointName;
1004 return True;
1005 }
1006
1007 return False;
1008}
1009
1010Boolean MediaSubsession::parseSDPLine_b(char const* sdpLine) {
1011 // Check for "b=<bwtype>:<bandwidth>" line
1012 // RTP applications are expected to use bwtype="AS"
1013 return sscanf(sdpLine, "b=AS:%u", &fBandwidth) == 1;
1014}
1015
1016Boolean MediaSubsession::parseSDPAttribute_rtpmap(char const* sdpLine) {
1017 // Check for a "a=rtpmap:<fmt> <codec>/<freq>" line:
1018 // (Also check without the "/<freq>"; RealNetworks omits this)
1019 // Also check for a trailing "/<numChannels>".
1020 Boolean parseSuccess = False;
1021
1022 unsigned rtpmapPayloadFormat;
1023 char* codecName = strDupSize(sdpLine); // ensures we have enough space
1024 unsigned rtpTimestampFrequency = 0;
1025 unsigned numChannels = 1;
1026 if (sscanf(sdpLine, "a=rtpmap: %u %[^/]/%u/%u",
1027 &rtpmapPayloadFormat, codecName, &rtpTimestampFrequency,
1028 &numChannels) == 4
1029 || sscanf(sdpLine, "a=rtpmap: %u %[^/]/%u",
1030 &rtpmapPayloadFormat, codecName, &rtpTimestampFrequency) == 3
1031 || sscanf(sdpLine, "a=rtpmap: %u %s",
1032 &rtpmapPayloadFormat, codecName) == 2) {
1033 parseSuccess = True;
1034 if (rtpmapPayloadFormat == fRTPPayloadFormat) {
1035 // This "rtpmap" matches our payload format, so set our
1036 // codec name and timestamp frequency:
1037 // (First, make sure the codec name is upper case)
1038 {
1039 Locale l("POSIX");
1040 for (char* p = codecName; *p != '\0'; ++p) *p = toupper(*p);
1041 }
1042 delete[] fCodecName; fCodecName = strDup(codecName);
1043 fRTPTimestampFrequency = rtpTimestampFrequency;
1044 fNumChannels = numChannels;
1045 }
1046 }
1047 delete[] codecName;
1048
1049 return parseSuccess;
1050}
1051
1052Boolean MediaSubsession::parseSDPAttribute_rtcpmux(char const* sdpLine) {
1053 if (strncmp(sdpLine, "a=rtcp-mux", 10) == 0) {
1054 fMultiplexRTCPWithRTP = True;
1055 return True;
1056 }
1057
1058 return False;
1059}
1060
1061Boolean MediaSubsession::parseSDPAttribute_control(char const* sdpLine) {
1062 // Check for a "a=control:<control-path>" line:
1063 Boolean parseSuccess = False;
1064
1065 char* controlPath = strDupSize(sdpLine); // ensures we have enough space
1066 if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) {
1067 parseSuccess = True;
1068 delete[] fControlPath; fControlPath = strDup(controlPath);
1069 }
1070 delete[] controlPath;
1071
1072 return parseSuccess;
1073}
1074
1075Boolean MediaSubsession::parseSDPAttribute_range(char const* sdpLine) {
1076 // Check for a "a=range:npt=<startTime>-<endTime>" line:
1077 // (Later handle other kinds of "a=range" attributes also???#####)
1078 Boolean parseSuccess = False;
1079
1080 double playStartTime;
1081 double playEndTime;
1082 if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) {
1083 parseSuccess = True;
1084 if (playStartTime > fPlayStartTime) {
1085 fPlayStartTime = playStartTime;
1086 if (playStartTime > fParent.playStartTime()) {
1087 fParent.playStartTime() = playStartTime;
1088 }
1089 }
1090 if (playEndTime > fPlayEndTime) {
1091 fPlayEndTime = playEndTime;
1092 if (playEndTime > fParent.playEndTime()) {
1093 fParent.playEndTime() = playEndTime;
1094 }
1095 }
1096 } else if (parseRangeAttribute(sdpLine, _absStartTime(), _absEndTime())) {
1097 parseSuccess = True;
1098 }
1099
1100 return parseSuccess;
1101}
1102
1103Boolean MediaSubsession::parseSDPAttribute_fmtp(char const* sdpLine) {
1104 // Check for a "a=fmtp:" line:
1105 // Later: Check that payload format number matches; #####
1106 do {
1107 if (strncmp(sdpLine, "a=fmtp:", 7) != 0) break; sdpLine += 7;
1108 while (isdigit(*sdpLine)) ++sdpLine;
1109
1110 // The remaining "sdpLine" should be a sequence of
1111 // <name>=<value>;
1112 // or
1113 // <name>;
1114 // parameter assignments. Look at each of these.
1115 unsigned const sdpLineLen = strlen(sdpLine);
1116 char* nameStr = new char[sdpLineLen+1];
1117 char* valueStr = new char[sdpLineLen+1];
1118
1119 while (*sdpLine != '\0' && *sdpLine != '\r' && *sdpLine != '\n') {
1120 int sscanfResult = sscanf(sdpLine, " %[^=; \t\r\n] = %[^; \t\r\n]", nameStr, valueStr);
1121 if (sscanfResult >= 1) {
1122 // <name> or <name>=<value>
1123 // Convert <name> to lower-case, to ease comparison:
1124 Locale l("POSIX");
1125 for (char* c = nameStr; *c != '\0'; ++c) *c = tolower(*c);
1126
1127 if (sscanfResult == 1) {
1128 // <name>
1129 setAttribute(nameStr);
1130 } else {
1131 // <name>=<value>
1132 setAttribute(nameStr, valueStr);
1133 }
1134 }
1135
1136 // Move to the next parameter assignment string:
1137 while (*sdpLine != '\0' && *sdpLine != '\r' && *sdpLine != '\n' && *sdpLine != ';') ++sdpLine;
1138 while (*sdpLine == ';') ++sdpLine;
1139 }
1140 delete[] nameStr; delete[] valueStr;
1141 return True;
1142 } while (0);
1143
1144 return False;
1145}
1146
1147Boolean MediaSubsession
1148::parseSDPAttribute_source_filter(char const* sdpLine) {
1149 return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr);
1150}
1151
1152Boolean MediaSubsession::parseSDPAttribute_x_dimensions(char const* sdpLine) {
1153 // Check for a "a=x-dimensions:<width>,<height>" line:
1154 Boolean parseSuccess = False;
1155
1156 int width, height;
1157 if (sscanf(sdpLine, "a=x-dimensions:%d,%d", &width, &height) == 2) {
1158 parseSuccess = True;
1159 fVideoWidth = (unsigned short)width;
1160 fVideoHeight = (unsigned short)height;
1161 }
1162
1163 return parseSuccess;
1164}
1165
1166Boolean MediaSubsession::parseSDPAttribute_framerate(char const* sdpLine) {
1167 // Check for a "a=framerate: <fps>" or "a=x-framerate: <fps>" line:
1168 Boolean parseSuccess = False;
1169
1170 float frate;
1171 int rate;
1172 if (sscanf(sdpLine, "a=framerate: %f", &frate) == 1 || sscanf(sdpLine, "a=framerate:%f", &frate) == 1) {
1173 parseSuccess = True;
1174 fVideoFPS = (unsigned)frate;
1175 } else if (sscanf(sdpLine, "a=x-framerate: %d", &rate) == 1) {
1176 parseSuccess = True;
1177 fVideoFPS = (unsigned)rate;
1178 }
1179
1180 return parseSuccess;
1181}
1182
1183Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
1184 do {
1185 // First, check "fProtocolName"
1186 if (strcmp(fProtocolName, "UDP") == 0) {
1187 // A UDP-packetized stream (*not* a RTP stream)
1188 fReadSource = BasicUDPSource::createNew(env(), fRTPSocket);
1189 fRTPSource = NULL; // Note!
1190
1191 if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream
1192 fReadSource = MPEG2TransportStreamFramer::createNew(env(), fReadSource);
1193 // this sets "durationInMicroseconds" correctly, based on the PCR values
1194 }
1195 } else {
1196 // Check "fCodecName" against the set of codecs that we support,
1197 // and create our RTP source accordingly
1198 // (Later make this code more efficient, as this set grows #####)
1199 // (Also, add more fmts that can be implemented by SimpleRTPSource#####)
1200 Boolean createSimpleRTPSource = False; // by default; can be changed below
1201 Boolean doNormalMBitRule = False; // default behavior if "createSimpleRTPSource" is True
1202 if (strcmp(fCodecName, "QCELP") == 0) { // QCELP audio
1203 fReadSource =
1204 QCELPAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
1205 fRTPPayloadFormat,
1206 fRTPTimestampFrequency);
1207 // Note that fReadSource will differ from fRTPSource in this case
1208 } else if (strcmp(fCodecName, "AMR") == 0) { // AMR audio (narrowband)
1209 fReadSource =
1210 AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
1211 fRTPPayloadFormat, False /*isWideband*/,
1212 fNumChannels, attrVal_bool("octet-align"),
1213 attrVal_unsigned("interleaving"),
1214 attrVal_bool("robust-sorting"),
1215 attrVal_bool("crc"));
1216 // Note that fReadSource will differ from fRTPSource in this case
1217 } else if (strcmp(fCodecName, "AMR-WB") == 0) { // AMR audio (wideband)
1218 fReadSource =
1219 AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
1220 fRTPPayloadFormat, True /*isWideband*/,
1221 fNumChannels, attrVal_bool("octet-align"),
1222 attrVal_unsigned("interleaving"),
1223 attrVal_bool("robust-sorting"),
1224 attrVal_bool("crc"));
1225 // Note that fReadSource will differ from fRTPSource in this case
1226 } else if (strcmp(fCodecName, "MPA") == 0) { // MPEG-1 or 2 audio
1227 fReadSource = fRTPSource
1228 = MPEG1or2AudioRTPSource::createNew(env(), fRTPSocket,
1229 fRTPPayloadFormat,
1230 fRTPTimestampFrequency);
1231 } else if (strcmp(fCodecName, "MPA-ROBUST") == 0) { // robust MP3 audio
1232 fReadSource = fRTPSource
1233 = MP3ADURTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
1234 fRTPTimestampFrequency);
1235 if (fRTPSource == NULL) break;
1236
1237 if (!fReceiveRawMP3ADUs) {
1238 // Add a filter that deinterleaves the ADUs after depacketizing them:
1239 MP3ADUdeinterleaver* deinterleaver
1240 = MP3ADUdeinterleaver::createNew(env(), fRTPSource);
1241 if (deinterleaver == NULL) break;
1242
1243 // Add another filter that converts these ADUs to MP3 frames:
1244 fReadSource = MP3FromADUSource::createNew(env(), deinterleaver);
1245 }
1246 } else if (strcmp(fCodecName, "X-MP3-DRAFT-00") == 0) {
1247 // a non-standard variant of "MPA-ROBUST" used by RealNetworks
1248 // (one 'ADU'ized MP3 frame per packet; no headers)
1249 fRTPSource
1250 = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
1251 fRTPTimestampFrequency,
1252 "audio/MPA-ROBUST" /*hack*/);
1253 if (fRTPSource == NULL) break;
1254
1255 // Add a filter that converts these ADUs to MP3 frames:
1256 fReadSource = MP3FromADUSource::createNew(env(), fRTPSource,
1257 False /*no ADU header*/);
1258 } else if (strcmp(fCodecName, "MP4A-LATM") == 0) { // MPEG-4 LATM audio
1259 fReadSource = fRTPSource
1260 = MPEG4LATMAudioRTPSource::createNew(env(), fRTPSocket,
1261 fRTPPayloadFormat,
1262 fRTPTimestampFrequency);
1263 } else if (strcmp(fCodecName, "VORBIS") == 0) { // Vorbis audio
1264 fReadSource = fRTPSource
1265 = VorbisAudioRTPSource::createNew(env(), fRTPSocket,
1266 fRTPPayloadFormat,
1267 fRTPTimestampFrequency);
1268 } else if (strcmp(fCodecName, "THEORA") == 0) { // Theora video
1269 fReadSource = fRTPSource
1270 = TheoraVideoRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat);
1271 } else if (strcmp(fCodecName, "RAW") == 0) { // Uncompressed raw video (RFC 4175)
1272 fReadSource = fRTPSource
1273 = RawVideoRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat, fRTPTimestampFrequency);
1274 } else if (strcmp(fCodecName, "VP8") == 0) { // VP8 video
1275 fReadSource = fRTPSource
1276 = VP8VideoRTPSource::createNew(env(), fRTPSocket,
1277 fRTPPayloadFormat,
1278 fRTPTimestampFrequency);
1279 } else if (strcmp(fCodecName, "VP9") == 0) { // VP9 video
1280 fReadSource = fRTPSource
1281 = VP9VideoRTPSource::createNew(env(), fRTPSocket,
1282 fRTPPayloadFormat,
1283 fRTPTimestampFrequency);
1284 } else if (strcmp(fCodecName, "AC3") == 0 || strcmp(fCodecName, "EAC3") == 0) { // AC3 audio
1285 fReadSource = fRTPSource
1286 = AC3AudioRTPSource::createNew(env(), fRTPSocket,
1287 fRTPPayloadFormat,
1288 fRTPTimestampFrequency);
1289 } else if (strcmp(fCodecName, "MP4V-ES") == 0) { // MPEG-4 Elementary Stream video
1290 fReadSource = fRTPSource
1291 = MPEG4ESVideoRTPSource::createNew(env(), fRTPSocket,
1292 fRTPPayloadFormat,
1293 fRTPTimestampFrequency);
1294 } else if (strcmp(fCodecName, "MPEG4-GENERIC") == 0) {
1295 fReadSource = fRTPSource
1296 = MPEG4GenericRTPSource::createNew(env(), fRTPSocket,
1297 fRTPPayloadFormat,
1298 fRTPTimestampFrequency,
1299 fMediumName, attrVal_strToLower("mode"),
1300 attrVal_unsigned("sizelength"),
1301 attrVal_unsigned("indexlength"),
1302 attrVal_unsigned("indexdeltalength"));
1303 } else if (strcmp(fCodecName, "MPV") == 0) { // MPEG-1 or 2 video
1304 fReadSource = fRTPSource
1305 = MPEG1or2VideoRTPSource::createNew(env(), fRTPSocket,
1306 fRTPPayloadFormat,
1307 fRTPTimestampFrequency);
1308 } else if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream
1309 fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
1310 fRTPTimestampFrequency, "video/MP2T",
1311 0, False);
1312 fReadSource = MPEG2TransportStreamFramer::createNew(env(), fRTPSource);
1313 // this sets "durationInMicroseconds" correctly, based on the PCR values
1314 } else if (strcmp(fCodecName, "H261") == 0) { // H.261
1315 fReadSource = fRTPSource
1316 = H261VideoRTPSource::createNew(env(), fRTPSocket,
1317 fRTPPayloadFormat,
1318 fRTPTimestampFrequency);
1319 } else if (strcmp(fCodecName, "H263-1998") == 0 ||
1320 strcmp(fCodecName, "H263-2000") == 0) { // H.263+
1321 fReadSource = fRTPSource
1322 = H263plusVideoRTPSource::createNew(env(), fRTPSocket,
1323 fRTPPayloadFormat,
1324 fRTPTimestampFrequency);
1325 } else if (strcmp(fCodecName, "H264") == 0) {
1326 fReadSource = fRTPSource
1327 = H264VideoRTPSource::createNew(env(), fRTPSocket,
1328 fRTPPayloadFormat,
1329 fRTPTimestampFrequency);
1330 } else if (strcmp(fCodecName, "H265") == 0) {
1331 Boolean expectDONFields = attrVal_unsigned("sprop-depack-buf-nalus") > 0;
1332 fReadSource = fRTPSource
1333 = H265VideoRTPSource::createNew(env(), fRTPSocket,
1334 fRTPPayloadFormat,
1335 expectDONFields,
1336 fRTPTimestampFrequency);
1337 } else if (strcmp(fCodecName, "DV") == 0) {
1338 fReadSource = fRTPSource
1339 = DVVideoRTPSource::createNew(env(), fRTPSocket,
1340 fRTPPayloadFormat,
1341 fRTPTimestampFrequency);
1342 } else if (strcmp(fCodecName, "JPEG") == 0) { // motion JPEG
1343 if (fReceiveRawJPEGFrames) {
1344 // Special case (used when proxying JPEG/RTP streams): Receive each JPEG/RTP packet, including the special RTP headers:
1345 fReadSource = fRTPSource
1346 = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
1347 fRTPTimestampFrequency, "video/JPEG",
1348 0/*special offset*/, False/*doNormalMBitRule => ignore the 'M' bit*/);
1349 } else {
1350 // Normal case: Receive each JPEG frame as a complete, displayable JPEG image:
1351 fReadSource = fRTPSource
1352 = JPEGVideoRTPSource::createNew(env(), fRTPSocket,
1353 fRTPPayloadFormat,
1354 fRTPTimestampFrequency,
1355 videoWidth(),
1356 videoHeight());
1357 }
1358 } else if (strcmp(fCodecName, "JPEG2000") == 0) { // JPEG 2000 video
1359 fReadSource = fRTPSource
1360 = JPEG2000VideoRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
1361 fRTPTimestampFrequency,
1362 attrVal_str("sampling"));
1363 } else if (strcmp(fCodecName, "X-QT") == 0
1364 || strcmp(fCodecName, "X-QUICKTIME") == 0) {
1365 // Generic QuickTime streams, as defined in
1366 // <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
1367 char* mimeType
1368 = new char[strlen(mediumName()) + strlen(codecName()) + 2] ;
1369 sprintf(mimeType, "%s/%s", mediumName(), codecName());
1370 fReadSource = fRTPSource
1371 = QuickTimeGenericRTPSource::createNew(env(), fRTPSocket,
1372 fRTPPayloadFormat,
1373 fRTPTimestampFrequency,
1374 mimeType);
1375 delete[] mimeType;
1376 } else if ( strcmp(fCodecName, "PCMU") == 0 // PCM u-law audio
1377 || strcmp(fCodecName, "GSM") == 0 // GSM audio
1378 || strcmp(fCodecName, "DVI4") == 0 // DVI4 (IMA ADPCM) audio
1379 || strcmp(fCodecName, "PCMA") == 0 // PCM a-law audio
1380 || strcmp(fCodecName, "MP1S") == 0 // MPEG-1 System Stream
1381 || strcmp(fCodecName, "MP2P") == 0 // MPEG-2 Program Stream
1382 || strcmp(fCodecName, "L8") == 0 // 8-bit linear audio
1383 || strcmp(fCodecName, "L16") == 0 // 16-bit linear audio
1384 || strcmp(fCodecName, "L20") == 0 // 20-bit linear audio (RFC 3190)
1385 || strcmp(fCodecName, "L24") == 0 // 24-bit linear audio (RFC 3190)
1386 || strcmp(fCodecName, "G722") == 0 // G.722 audio (RFC 3551)
1387 || strcmp(fCodecName, "G726-16") == 0 // G.726, 16 kbps
1388 || strcmp(fCodecName, "G726-24") == 0 // G.726, 24 kbps
1389 || strcmp(fCodecName, "G726-32") == 0 // G.726, 32 kbps
1390 || strcmp(fCodecName, "G726-40") == 0 // G.726, 40 kbps
1391 || strcmp(fCodecName, "SPEEX") == 0 // SPEEX audio
1392 || strcmp(fCodecName, "ILBC") == 0 // iLBC audio
1393 || strcmp(fCodecName, "OPUS") == 0 // Opus audio
1394 || strcmp(fCodecName, "T140") == 0 // T.140 text (RFC 4103)
1395 || strcmp(fCodecName, "DAT12") == 0 // 12-bit nonlinear audio (RFC 3190)
1396 || strcmp(fCodecName, "VND.ONVIF.METADATA") == 0 // 'ONVIF' 'metadata' (a XML document)
1397 ) {
1398 createSimpleRTPSource = True;
1399 useSpecialRTPoffset = 0;
1400 if (strcmp(fCodecName, "VND.ONVIF.METADATA") == 0) {
1401 // This RTP payload format uses the RTP "M" bit to indicate the end of the content (a XML document):
1402 doNormalMBitRule = True;
1403 }
1404 } else if (useSpecialRTPoffset >= 0) {
1405 // We don't know this RTP payload format, but try to receive
1406 // it using a 'SimpleRTPSource' with the specified header offset:
1407 createSimpleRTPSource = True;
1408 } else {
1409 env().setResultMsg("RTP payload format unknown or not supported");
1410 break;
1411 }
1412
1413 if (createSimpleRTPSource) {
1414 char* mimeType
1415 = new char[strlen(mediumName()) + strlen(codecName()) + 2] ;
1416 sprintf(mimeType, "%s/%s", mediumName(), codecName());
1417 fReadSource = fRTPSource
1418 = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
1419 fRTPTimestampFrequency, mimeType,
1420 (unsigned)useSpecialRTPoffset,
1421 doNormalMBitRule);
1422 delete[] mimeType;
1423 }
1424 }
1425
1426 return True;
1427 } while (0);
1428
1429 return False; // an error occurred
1430}
1431
1432
1433////////// SDPAttribute implementation //////////
1434
1435SDPAttribute::SDPAttribute(char const* strValue, Boolean valueIsHexadecimal)
1436 : fStrValue(strDup(strValue)), fStrValueToLower(NULL), fValueIsHexadecimal(valueIsHexadecimal) {
1437 if (fStrValue == NULL) {
1438 // No value was given for this attribute, so consider it to be a Boolean, with value True:
1439 fIntValue = 1;
1440 } else {
1441 // Create a 'tolower' version of "fStrValue", in case it's needed:
1442 Locale l("POSIX");
1443 size_t strSize;
1444
1445 fStrValueToLower = strDupSize(fStrValue, strSize);
1446 for (unsigned i = 0; i < strSize-1; ++i) fStrValueToLower[i] = tolower(fStrValue[i]);
1447 fStrValueToLower[strSize-1] = '\0';
1448
1449 // Try to parse "fStrValueToLower" as an integer. If we can't, assume an integer value of 0:
1450 if (sscanf(fStrValueToLower, valueIsHexadecimal ? "%x" : "%d", &fIntValue) != 1) {
1451 fIntValue = 0;
1452 }
1453 }
1454}
1455
1456SDPAttribute::~SDPAttribute() {
1457 delete[] fStrValue;
1458 delete[] fStrValueToLower;
1459}
1460