liveMedia/MediaSession.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2013 Live Networks, Inc.  All rights reserved.
00018 // A data structure that represents a session that consists of
00019 // potentially multiple (audio and/or video) sub-sessions
00020 // Implementation
00021 
00022 #include "liveMedia.hh"
00023 #include "Locale.hh"
00024 #include "GroupsockHelper.hh"
00025 #include <ctype.h>
00026 
00028 
00029 MediaSession* MediaSession::createNew(UsageEnvironment& env,
00030                                       char const* sdpDescription) {
00031   MediaSession* newSession = new MediaSession(env);
00032   if (newSession != NULL) {
00033     if (!newSession->initializeWithSDP(sdpDescription)) {
00034       delete newSession;
00035       return NULL;
00036     }
00037   }
00038 
00039   return newSession;
00040 }
00041 
00042 Boolean MediaSession::lookupByName(UsageEnvironment& env,
00043                                    char const* instanceName,
00044                                    MediaSession*& resultSession) {
00045   resultSession = NULL; // unless we succeed
00046 
00047   Medium* medium;
00048   if (!Medium::lookupByName(env, instanceName, medium)) return False;
00049 
00050   if (!medium->isMediaSession()) {
00051     env.setResultMsg(instanceName, " is not a 'MediaSession' object");
00052     return False;
00053   }
00054 
00055   resultSession = (MediaSession*)medium;
00056   return True;
00057 }
00058 
00059 MediaSession::MediaSession(UsageEnvironment& env)
00060   : Medium(env),
00061     fSubsessionsHead(NULL), fSubsessionsTail(NULL),
00062     fConnectionEndpointName(NULL),
00063     fMaxPlayStartTime(0.0f), fMaxPlayEndTime(0.0f), fAbsStartTime(NULL), fAbsEndTime(NULL),
00064     fScale(1.0f), fMediaSessionType(NULL), fSessionName(NULL), fSessionDescription(NULL),
00065     fControlPath(NULL) {
00066   fSourceFilterAddr.s_addr = 0;
00067 
00068   // Get our host name, and use this for the RTCP CNAME:
00069   const unsigned maxCNAMElen = 100;
00070   char CNAME[maxCNAMElen+1];
00071 #ifndef CRIS
00072   gethostname((char*)CNAME, maxCNAMElen);
00073 #else
00074   // "gethostname()" isn't defined for this platform
00075   sprintf(CNAME, "unknown host %d", (unsigned)(our_random()*0x7FFFFFFF));
00076 #endif
00077   CNAME[maxCNAMElen] = '\0'; // just in case
00078   fCNAME = strDup(CNAME);
00079 }
00080 
00081 MediaSession::~MediaSession() {
00082   delete fSubsessionsHead;
00083   delete[] fCNAME;
00084   delete[] fConnectionEndpointName;
00085   delete[] fAbsStartTime; delete[] fAbsEndTime;
00086   delete[] fMediaSessionType;
00087   delete[] fSessionName;
00088   delete[] fSessionDescription;
00089   delete[] fControlPath;
00090 }
00091 
00092 Boolean MediaSession::isMediaSession() const {
00093   return True;
00094 }
00095 
00096 MediaSubsession* MediaSession::createNewMediaSubsession() {
00097   // default implementation:
00098   return new MediaSubsession(*this);
00099 }
00100 
00101 Boolean MediaSession::initializeWithSDP(char const* sdpDescription) {
00102   if (sdpDescription == NULL) return False;
00103 
00104   // Begin by processing all SDP lines until we see the first "m="
00105   char const* sdpLine = sdpDescription;
00106   char const* nextSDPLine;
00107   while (1) {
00108     if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
00109     //##### We should really check for:
00110     // - "a=control:" attributes (to set the URL for aggregate control)
00111     // - the correct SDP version (v=0)
00112     if (sdpLine[0] == 'm') break;
00113     sdpLine = nextSDPLine;
00114     if (sdpLine == NULL) break; // there are no m= lines at all
00115 
00116     // Check for various special SDP lines that we understand:
00117     if (parseSDPLine_s(sdpLine)) continue;
00118     if (parseSDPLine_i(sdpLine)) continue;
00119     if (parseSDPLine_c(sdpLine)) continue;
00120     if (parseSDPAttribute_control(sdpLine)) continue;
00121     if (parseSDPAttribute_range(sdpLine)) continue;
00122     if (parseSDPAttribute_type(sdpLine)) continue;
00123     if (parseSDPAttribute_source_filter(sdpLine)) continue;
00124   }
00125 
00126   while (sdpLine != NULL) {
00127     // We have a "m=" line, representing a new sub-session:
00128     MediaSubsession* subsession = createNewMediaSubsession();
00129     if (subsession == NULL) {
00130       envir().setResultMsg("Unable to create new MediaSubsession");
00131       return False;
00132     }
00133 
00134     // Parse the line as "m=<medium_name> <client_portNum> RTP/AVP <fmt>"
00135     // or "m=<medium_name> <client_portNum>/<num_ports> RTP/AVP <fmt>"
00136     // (Should we be checking for >1 payload format number here?)#####
00137     char* mediumName = strDupSize(sdpLine); // ensures we have enough space
00138     char const* protocolName = NULL;
00139     unsigned payloadFormat;
00140     if ((sscanf(sdpLine, "m=%s %hu RTP/AVP %u",
00141                 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
00142          sscanf(sdpLine, "m=%s %hu/%*u RTP/AVP %u",
00143                 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3)
00144         && payloadFormat <= 127) {
00145       protocolName = "RTP";
00146     } else if ((sscanf(sdpLine, "m=%s %hu UDP %u",
00147                        mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
00148                 sscanf(sdpLine, "m=%s %hu udp %u",
00149                        mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
00150                 sscanf(sdpLine, "m=%s %hu RAW/RAW/UDP %u",
00151                        mediumName, &subsession->fClientPortNum, &payloadFormat) == 3)
00152                && payloadFormat <= 127) {
00153       // This is a RAW UDP source
00154       protocolName = "UDP";
00155     } else {
00156       // This "m=" line is bad; output an error message saying so:
00157       char* sdpLineStr;
00158       if (nextSDPLine == NULL) {
00159         sdpLineStr = (char*)sdpLine;
00160       } else {
00161         sdpLineStr = strDup(sdpLine);
00162         sdpLineStr[nextSDPLine-sdpLine] = '\0';
00163       }
00164       envir() << "Bad SDP \"m=\" line: " <<  sdpLineStr << "\n";
00165       if (sdpLineStr != (char*)sdpLine) delete[] sdpLineStr;
00166 
00167       delete[] mediumName;
00168       delete subsession;
00169 
00170       // Skip the following SDP lines, up until the next "m=":
00171       while (1) {
00172         sdpLine = nextSDPLine;
00173         if (sdpLine == NULL) break; // we've reached the end
00174         if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
00175 
00176         if (sdpLine[0] == 'm') break; // we've reached the next subsession
00177       }
00178       continue;
00179     }
00180 
00181     // Insert this subsession at the end of the list:
00182     if (fSubsessionsTail == NULL) {
00183       fSubsessionsHead = fSubsessionsTail = subsession;
00184     } else {
00185       fSubsessionsTail->setNext(subsession);
00186       fSubsessionsTail = subsession;
00187     }
00188 
00189     subsession->serverPortNum = subsession->fClientPortNum; // by default
00190 
00191     char const* mStart = sdpLine;
00192     subsession->fSavedSDPLines = strDup(mStart);
00193 
00194     subsession->fMediumName = strDup(mediumName);
00195     delete[] mediumName;
00196     subsession->fProtocolName = strDup(protocolName);
00197     subsession->fRTPPayloadFormat = payloadFormat;
00198 
00199     // Process the following SDP lines, up until the next "m=":
00200     while (1) {
00201       sdpLine = nextSDPLine;
00202       if (sdpLine == NULL) break; // we've reached the end
00203       if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
00204 
00205       if (sdpLine[0] == 'm') break; // we've reached the next subsession
00206 
00207       // Check for various special SDP lines that we understand:
00208       if (subsession->parseSDPLine_c(sdpLine)) continue;
00209       if (subsession->parseSDPLine_b(sdpLine)) continue;
00210       if (subsession->parseSDPAttribute_rtpmap(sdpLine)) continue;
00211       if (subsession->parseSDPAttribute_control(sdpLine)) continue;
00212       if (subsession->parseSDPAttribute_range(sdpLine)) continue;
00213       if (subsession->parseSDPAttribute_fmtp(sdpLine)) continue;
00214       if (subsession->parseSDPAttribute_source_filter(sdpLine)) continue;
00215       if (subsession->parseSDPAttribute_x_dimensions(sdpLine)) continue;
00216       if (subsession->parseSDPAttribute_framerate(sdpLine)) continue;
00217 
00218       // (Later, check for malformed lines, and other valid SDP lines#####)
00219     }
00220     if (sdpLine != NULL) subsession->fSavedSDPLines[sdpLine-mStart] = '\0';
00221 
00222     // If we don't yet know the codec name, try looking it up from the
00223     // list of static payload types:
00224     if (subsession->fCodecName == NULL) {
00225       subsession->fCodecName
00226         = lookupPayloadFormat(subsession->fRTPPayloadFormat,
00227                               subsession->fRTPTimestampFrequency,
00228                               subsession->fNumChannels);
00229       if (subsession->fCodecName == NULL) {
00230         char typeStr[20];
00231         sprintf(typeStr, "%d", subsession->fRTPPayloadFormat);
00232         envir().setResultMsg("Unknown codec name for RTP payload type ",
00233                              typeStr);
00234         return False;
00235       }
00236     }
00237 
00238     // If we don't yet know this subsession's RTP timestamp frequency
00239     // (because it uses a dynamic payload type and the corresponding
00240     // SDP "rtpmap" attribute erroneously didn't specify it),
00241     // then guess it now:
00242     if (subsession->fRTPTimestampFrequency == 0) {
00243       subsession->fRTPTimestampFrequency
00244         = guessRTPTimestampFrequency(subsession->fMediumName,
00245                                      subsession->fCodecName);
00246     }
00247   }
00248 
00249   return True;
00250 }
00251 
00252 Boolean MediaSession::parseSDPLine(char const* inputLine,
00253                                    char const*& nextLine){
00254   // Begin by finding the start of the next line (if any):
00255   nextLine = NULL;
00256   for (char const* ptr = inputLine; *ptr != '\0'; ++ptr) {
00257     if (*ptr == '\r' || *ptr == '\n') {
00258       // We found the end of the line
00259       ++ptr;
00260       while (*ptr == '\r' || *ptr == '\n') ++ptr;
00261       nextLine = ptr;
00262       if (nextLine[0] == '\0') nextLine = NULL; // special case for end
00263       break;
00264     }
00265   }
00266 
00267   // Then, check that this line is a SDP line of the form <char>=<etc>
00268   // (However, we also accept blank lines in the input.)
00269   if (inputLine[0] == '\r' || inputLine[0] == '\n') return True;
00270   if (strlen(inputLine) < 2 || inputLine[1] != '='
00271       || inputLine[0] < 'a' || inputLine[0] > 'z') {
00272     envir().setResultMsg("Invalid SDP line: ", inputLine);
00273     return False;
00274   }
00275 
00276   return True;
00277 }
00278 
00279 static char* parseCLine(char const* sdpLine) {
00280   char* resultStr = NULL;
00281   char* buffer = strDupSize(sdpLine); // ensures we have enough space
00282   if (sscanf(sdpLine, "c=IN IP4 %[^/\r\n]", buffer) == 1) {
00283     // Later, handle the optional /<ttl> and /<numAddresses> #####
00284     resultStr = strDup(buffer);
00285   }
00286   delete[] buffer;
00287 
00288   return resultStr;
00289 }
00290 
00291 Boolean MediaSession::parseSDPLine_s(char const* sdpLine) {
00292   // Check for "s=<session name>" line
00293   char* buffer = strDupSize(sdpLine);
00294   Boolean parseSuccess = False;
00295 
00296   if (sscanf(sdpLine, "s=%[^\r\n]", buffer) == 1) {
00297     delete[] fSessionName; fSessionName = strDup(buffer);
00298     parseSuccess = True;
00299   }
00300   delete[] buffer;
00301 
00302   return parseSuccess;
00303 }
00304 
00305 Boolean MediaSession::parseSDPLine_i(char const* sdpLine) {
00306   // Check for "i=<session description>" line
00307   char* buffer = strDupSize(sdpLine);
00308   Boolean parseSuccess = False;
00309 
00310   if (sscanf(sdpLine, "i=%[^\r\n]", buffer) == 1) {
00311     delete[] fSessionDescription; fSessionDescription = strDup(buffer);
00312     parseSuccess = True;
00313   }
00314   delete[] buffer;
00315 
00316   return parseSuccess;
00317 }
00318 
00319 Boolean MediaSession::parseSDPLine_c(char const* sdpLine) {
00320   // Check for "c=IN IP4 <connection-endpoint>"
00321   // or "c=IN IP4 <connection-endpoint>/<ttl+numAddresses>"
00322   // (Later, do something with <ttl+numAddresses> also #####)
00323   char* connectionEndpointName = parseCLine(sdpLine);
00324   if (connectionEndpointName != NULL) {
00325     delete[] fConnectionEndpointName;
00326     fConnectionEndpointName = connectionEndpointName;
00327     return True;
00328   }
00329 
00330   return False;
00331 }
00332 
00333 Boolean MediaSession::parseSDPAttribute_type(char const* sdpLine) {
00334   // Check for a "a=type:broadcast|meeting|moderated|test|H.332|recvonly" line:
00335   Boolean parseSuccess = False;
00336 
00337   char* buffer = strDupSize(sdpLine);
00338   if (sscanf(sdpLine, "a=type: %[^ ]", buffer) == 1) {
00339     delete[] fMediaSessionType;
00340     fMediaSessionType = strDup(buffer);
00341     parseSuccess = True;
00342   }
00343   delete[] buffer;
00344 
00345   return parseSuccess;
00346 }
00347 
00348 Boolean MediaSession::parseSDPAttribute_control(char const* sdpLine) {
00349   // Check for a "a=control:<control-path>" line:
00350   Boolean parseSuccess = False;
00351 
00352   char* controlPath = strDupSize(sdpLine); // ensures we have enough space
00353   if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) {
00354     parseSuccess = True;
00355     delete[] fControlPath; fControlPath = strDup(controlPath);
00356   }
00357   delete[] controlPath;
00358 
00359   return parseSuccess;
00360 }
00361 
00362 static Boolean parseRangeAttribute(char const* sdpLine, double& startTime, double& endTime) {
00363   return sscanf(sdpLine, "a=range: npt = %lg - %lg", &startTime, &endTime) == 2;
00364 }
00365 
00366 static Boolean parseRangeAttribute(char const* sdpLine, char*& absStartTime, char*& absEndTime) {
00367   size_t len = strlen(sdpLine) + 1;
00368   char* as = new char[len];
00369   char* ae = new char[len];
00370   int sscanfResult = sscanf(sdpLine, "a=range: clock = %[^-\r\n]-%[^\r\n]", as, ae);
00371   if (sscanfResult == 2) {
00372     absStartTime = as;
00373     absEndTime = ae;
00374   } else if (sscanfResult == 1) {
00375     absStartTime = as;
00376     delete[] ae;
00377   } else {
00378     delete[] as; delete[] ae;
00379     return False;
00380   }
00381 
00382   return True;
00383 }
00384 
00385 Boolean MediaSession::parseSDPAttribute_range(char const* sdpLine) {
00386   // Check for a "a=range:npt=<startTime>-<endTime>" line:
00387   // (Later handle other kinds of "a=range" attributes also???#####)
00388   Boolean parseSuccess = False;
00389 
00390   double playStartTime;
00391   double playEndTime;
00392   if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) {
00393     parseSuccess = True;
00394     if (playStartTime > fMaxPlayStartTime) {
00395       fMaxPlayStartTime = playStartTime;
00396     }
00397     if (playEndTime > fMaxPlayEndTime) {
00398       fMaxPlayEndTime = playEndTime;
00399     }
00400   } else if (parseRangeAttribute(sdpLine, _absStartTime(), _absEndTime())) {
00401     parseSuccess = True;
00402   }
00403 
00404   return parseSuccess;
00405 }
00406 
00407 static Boolean parseSourceFilterAttribute(char const* sdpLine,
00408                                           struct in_addr& sourceAddr) {
00409   // Check for a "a=source-filter:incl IN IP4 <something> <source>" line.
00410   // Note: At present, we don't check that <something> really matches
00411   // one of our multicast addresses.  We also don't support more than
00412   // one <source> #####
00413   Boolean result = False; // until we succeed
00414   char* sourceName = strDupSize(sdpLine); // ensures we have enough space
00415   do {
00416     if (sscanf(sdpLine, "a=source-filter: incl IN IP4 %*s %s",
00417                sourceName) != 1) break;
00418 
00419     // Now, convert this name to an address, if we can:
00420     NetAddressList addresses(sourceName);
00421     if (addresses.numAddresses() == 0) break;
00422 
00423     netAddressBits sourceAddrBits
00424       = *(netAddressBits*)(addresses.firstAddress()->data());
00425     if (sourceAddrBits == 0) break;
00426 
00427     sourceAddr.s_addr = sourceAddrBits;
00428     result = True;
00429   } while (0);
00430 
00431   delete[] sourceName;
00432   return result;
00433 }
00434 
00435 Boolean MediaSession
00436 ::parseSDPAttribute_source_filter(char const* sdpLine) {
00437   return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr);
00438 }
00439 
00440 char* MediaSession::lookupPayloadFormat(unsigned char rtpPayloadType,
00441                                         unsigned& freq, unsigned& nCh) {
00442   // Look up the codec name and timestamp frequency for known (static)
00443   // RTP payload formats.
00444   char const* temp = NULL;
00445   switch (rtpPayloadType) {
00446   case 0: {temp = "PCMU"; freq = 8000; nCh = 1; break;}
00447   case 2: {temp = "G726-32"; freq = 8000; nCh = 1; break;}
00448   case 3: {temp = "GSM"; freq = 8000; nCh = 1; break;}
00449   case 4: {temp = "G723"; freq = 8000; nCh = 1; break;}
00450   case 5: {temp = "DVI4"; freq = 8000; nCh = 1; break;}
00451   case 6: {temp = "DVI4"; freq = 16000; nCh = 1; break;}
00452   case 7: {temp = "LPC"; freq = 8000; nCh = 1; break;}
00453   case 8: {temp = "PCMA"; freq = 8000; nCh = 1; break;}
00454   case 9: {temp = "G722"; freq = 8000; nCh = 1; break;}
00455   case 10: {temp = "L16"; freq = 44100; nCh = 2; break;}
00456   case 11: {temp = "L16"; freq = 44100; nCh = 1; break;}
00457   case 12: {temp = "QCELP"; freq = 8000; nCh = 1; break;}
00458   case 14: {temp = "MPA"; freq = 90000; nCh = 1; break;}
00459     // 'number of channels' is actually encoded in the media stream
00460   case 15: {temp = "G728"; freq = 8000; nCh = 1; break;}
00461   case 16: {temp = "DVI4"; freq = 11025; nCh = 1; break;}
00462   case 17: {temp = "DVI4"; freq = 22050; nCh = 1; break;}
00463   case 18: {temp = "G729"; freq = 8000; nCh = 1; break;}
00464   case 25: {temp = "CELB"; freq = 90000; nCh = 1; break;}
00465   case 26: {temp = "JPEG"; freq = 90000; nCh = 1; break;}
00466   case 28: {temp = "NV"; freq = 90000; nCh = 1; break;}
00467   case 31: {temp = "H261"; freq = 90000; nCh = 1; break;}
00468   case 32: {temp = "MPV"; freq = 90000; nCh = 1; break;}
00469   case 33: {temp = "MP2T"; freq = 90000; nCh = 1; break;}
00470   case 34: {temp = "H263"; freq = 90000; nCh = 1; break;}
00471   };
00472 
00473   return strDup(temp);
00474 }
00475 
00476 unsigned MediaSession::guessRTPTimestampFrequency(char const* mediumName,
00477                                                   char const* codecName) {
00478   // By default, we assume that audio sessions use a frequency of 8000,
00479   // video sessions use a frequency of 90000,
00480   // and text sessions use a frequency of 1000.
00481   // Begin by checking for known exceptions to this rule
00482   // (where the frequency is known unambiguously (e.g., not like "DVI4"))
00483   if (strcmp(codecName, "L16") == 0) return 44100;
00484   if (strcmp(codecName, "MPA") == 0
00485       || strcmp(codecName, "MPA-ROBUST") == 0
00486       || strcmp(codecName, "X-MP3-DRAFT-00")) return 90000;
00487 
00488   // Now, guess default values:
00489   if (strcmp(mediumName, "video") == 0) return 90000;
00490   else if (strcmp(mediumName, "text") == 0) return 1000;
00491   return 8000; // for "audio", and any other medium
00492 }
00493 
00494 char* MediaSession::absStartTime() const {
00495   if (fAbsStartTime != NULL) return fAbsStartTime;
00496 
00497   // If a subsession has an 'absolute' start time, then use that:
00498   MediaSubsessionIterator iter(*this);
00499   MediaSubsession* subsession;
00500   while ((subsession = iter.next()) != NULL) {
00501     if (subsession->_absStartTime() != NULL) return subsession->_absStartTime();
00502   }
00503   return NULL;
00504 }
00505 
00506 char* MediaSession::absEndTime() const {
00507   if (fAbsEndTime != NULL) return fAbsEndTime;
00508 
00509   // If a subsession has an 'absolute' end time, then use that:
00510   MediaSubsessionIterator iter(*this);
00511   MediaSubsession* subsession;
00512   while ((subsession = iter.next()) != NULL) {
00513     if (subsession->_absEndTime() != NULL) return subsession->_absEndTime();
00514   }
00515   return NULL;
00516 }
00517 
00518 Boolean MediaSession
00519 ::initiateByMediaType(char const* mimeType,
00520                       MediaSubsession*& resultSubsession,
00521                       int useSpecialRTPoffset) {
00522   // Look through this session's subsessions for media that match "mimeType"
00523   resultSubsession = NULL;
00524   MediaSubsessionIterator iter(*this);
00525   MediaSubsession* subsession;
00526   while ((subsession = iter.next()) != NULL) {
00527     Boolean wasAlreadyInitiated = subsession->readSource() != NULL;
00528     if (!wasAlreadyInitiated) {
00529       // Try to create a source for this subsession:
00530       if (!subsession->initiate(useSpecialRTPoffset)) return False;
00531     }
00532 
00533     // Make sure the source's MIME type is one that we handle:
00534     if (strcmp(subsession->readSource()->MIMEtype(), mimeType) != 0) {
00535       if (!wasAlreadyInitiated) subsession->deInitiate();
00536       continue;
00537     }
00538 
00539     resultSubsession = subsession;
00540     break; // use this
00541   }
00542 
00543   if (resultSubsession == NULL) {
00544     envir().setResultMsg("Session has no usable media subsession");
00545     return False;
00546   }
00547 
00548   return True;
00549 }
00550 
00551 
00553 
00554 MediaSubsessionIterator::MediaSubsessionIterator(MediaSession const& session)
00555   : fOurSession(session) {
00556   reset();
00557 }
00558 
00559 MediaSubsessionIterator::~MediaSubsessionIterator() {
00560 }
00561 
00562 MediaSubsession* MediaSubsessionIterator::next() {
00563   MediaSubsession* result = fNextPtr;
00564 
00565   if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext;
00566 
00567   return result;
00568 }
00569 
00570 void MediaSubsessionIterator::reset() {
00571   fNextPtr = fOurSession.fSubsessionsHead;
00572 }
00573 
00575 
00576 MediaSubsession::MediaSubsession(MediaSession& parent)
00577   : serverPortNum(0), sink(NULL), miscPtr(NULL),
00578     fParent(parent), fNext(NULL),
00579     fConnectionEndpointName(NULL),
00580     fClientPortNum(0), fRTPPayloadFormat(0xFF),
00581     fSavedSDPLines(NULL), fMediumName(NULL), fCodecName(NULL), fProtocolName(NULL),
00582     fRTPTimestampFrequency(0), fControlPath(NULL),
00583     fSourceFilterAddr(parent.sourceFilterAddr()), fBandwidth(0),
00584     fAuxiliarydatasizelength(0), fConstantduration(0), fConstantsize(0),
00585     fCRC(0), fCtsdeltalength(0), fDe_interleavebuffersize(0), fDtsdeltalength(0),
00586     fIndexdeltalength(0), fIndexlength(0), fInterleaving(0), fMaxdisplacement(0),
00587     fObjecttype(0), fOctetalign(0), fProfile_level_id(0), fRobustsorting(0),
00588     fSizelength(0), fStreamstateindication(0), fStreamtype(0),
00589     fCpresent(False), fRandomaccessindication(False),
00590     fConfig(NULL), fMode(NULL), fSpropParameterSets(NULL), fEmphasis(NULL), fChannelOrder(NULL),
00591     fPlayStartTime(0.0), fPlayEndTime(0.0), fAbsStartTime(NULL), fAbsEndTime(NULL),
00592     fVideoWidth(0), fVideoHeight(0), fVideoFPS(0), fNumChannels(1), fScale(1.0f), fNPT_PTS_Offset(0.0f),
00593     fRTPSocket(NULL), fRTCPSocket(NULL),
00594     fRTPSource(NULL), fRTCPInstance(NULL), fReadSource(NULL),
00595     fReceiveRawMP3ADUs(False), fReceiveRawJPEGFrames(False),
00596     fSessionId(NULL) {
00597   rtpInfo.seqNum = 0; rtpInfo.timestamp = 0; rtpInfo.infoIsNew = False;
00598 }
00599 
00600 MediaSubsession::~MediaSubsession() {
00601   deInitiate();
00602 
00603   delete[] fConnectionEndpointName; delete[] fSavedSDPLines;
00604   delete[] fMediumName; delete[] fCodecName; delete[] fProtocolName;
00605   delete[] fControlPath;
00606   delete[] fConfig; delete[] fMode; delete[] fSpropParameterSets; delete[] fEmphasis; delete[] fChannelOrder;
00607   delete[] fAbsStartTime; delete[] fAbsEndTime;
00608   delete[] fSessionId;
00609 
00610   delete fNext;
00611 }
00612 
00613 void MediaSubsession::addFilter(FramedFilter* filter){
00614   if (filter == NULL || filter->inputSource() != fReadSource) return; // sanity check
00615   fReadSource = filter;
00616 }
00617 
00618 double MediaSubsession::playStartTime() const {
00619   if (fPlayStartTime > 0) return fPlayStartTime;
00620 
00621   return fParent.playStartTime();
00622 }
00623 
00624 double MediaSubsession::playEndTime() const {
00625   if (fPlayEndTime > 0) return fPlayEndTime;
00626 
00627   return fParent.playEndTime();
00628 }
00629 
00630 char* MediaSubsession::absStartTime() const {
00631   if (fAbsStartTime != NULL) return fAbsStartTime;
00632 
00633   return fParent.absStartTime();
00634 }
00635 
00636 char* MediaSubsession::absEndTime() const {
00637   if (fAbsEndTime != NULL) return fAbsEndTime;
00638 
00639   return fParent.absEndTime();
00640 }
00641 
00642 static Boolean const honorSDPPortChoice
00643 #ifdef IGNORE_UNICAST_SDP_PORTS
00644 = False;
00645 #else
00646 = True;
00647 #endif
00648 
00649 Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
00650   if (fReadSource != NULL) return True; // has already been initiated
00651 
00652   do {
00653     if (fCodecName == NULL) {
00654       env().setResultMsg("Codec is unspecified");
00655       break;
00656     }
00657 
00658     // Create RTP and RTCP 'Groupsocks' on which to receive incoming data.
00659     // (Groupsocks will work even for unicast addresses)
00660     struct in_addr tempAddr;
00661     tempAddr.s_addr = connectionEndpointAddress();
00662         // This could get changed later, as a result of a RTSP "SETUP"
00663 
00664     if (fClientPortNum != 0  && (honorSDPPortChoice || IsMulticastAddress(tempAddr.s_addr))) {
00665       // This is a multicast stream, and the sockets' port numbers were specified for us.  Use these:
00666       Boolean const protocolIsRTP = strcmp(fProtocolName, "RTP") == 0;
00667       if (protocolIsRTP) {
00668         fClientPortNum = fClientPortNum&~1; // use an even-numbered port for RTP, and the next (odd-numbered) port for RTCP
00669       }
00670       if (isSSM()) {
00671         fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, fClientPortNum);
00672       } else {
00673         fRTPSocket = new Groupsock(env(), tempAddr, fClientPortNum, 255);
00674       }
00675       if (fRTPSocket == NULL) {
00676         env().setResultMsg("Failed to create RTP socket");
00677         break;
00678       }
00679       
00680       if (protocolIsRTP) {
00681         // Set our RTCP port to be the RTP port +1
00682         portNumBits const rtcpPortNum = fClientPortNum|1;
00683         if (isSSM()) {
00684           fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
00685         } else {
00686           fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
00687         }
00688       }
00689     } else {
00690       // Port numbers were not specified in advance, so we use ephemeral port numbers.
00691       // Create sockets until we get a port-number pair (even: RTP; even+1: RTCP).
00692       // We need to make sure that we don't keep trying to use the same bad port numbers over and over again.
00693       // so we store bad sockets in a table, and delete them all when we're done.
00694       HashTable* socketHashTable = HashTable::create(ONE_WORD_HASH_KEYS);
00695       if (socketHashTable == NULL) break;
00696       Boolean success = False;
00697       NoReuse dummy(env()); // ensures that our new ephemeral port number won't be one that's already in use
00698 
00699       while (1) {
00700         // Create a new socket:
00701         if (isSSM()) {
00702           fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, 0);
00703         } else {
00704           fRTPSocket = new Groupsock(env(), tempAddr, 0, 255);
00705         }
00706         if (fRTPSocket == NULL) {
00707           env().setResultMsg("MediaSession::initiate(): unable to create RTP and RTCP sockets");
00708           break;
00709         }
00710 
00711         // Get the client port number, and check whether it's even (for RTP):
00712         Port clientPort(0);
00713         if (!getSourcePort(env(), fRTPSocket->socketNum(), clientPort)) {
00714           break;
00715         }
00716         fClientPortNum = ntohs(clientPort.num()); 
00717         if ((fClientPortNum&1) != 0) { // it's odd
00718           // Record this socket in our table, and keep trying:
00719           unsigned key = (unsigned)fClientPortNum;
00720           Groupsock* existing = (Groupsock*)socketHashTable->Add((char const*)key, fRTPSocket);
00721           delete existing; // in case it wasn't NULL
00722           continue;
00723         }
00724 
00725         // Make sure we can use the next (i.e., odd) port number, for RTCP:
00726         portNumBits rtcpPortNum = fClientPortNum|1;
00727         if (isSSM()) {
00728           fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
00729         } else {
00730           fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
00731         }
00732         if (fRTCPSocket != NULL && fRTCPSocket->socketNum() >= 0) {
00733           // Success! Use these two sockets.
00734           success = True;
00735           break;
00736         } else {
00737           // We couldn't create the RTCP socket (perhaps that port number's already in use elsewhere?).
00738           delete fRTCPSocket;
00739 
00740           // Record the first socket in our table, and keep trying:
00741           unsigned key = (unsigned)fClientPortNum;
00742           Groupsock* existing = (Groupsock*)socketHashTable->Add((char const*)key, fRTPSocket);
00743           delete existing; // in case it wasn't NULL
00744           continue;
00745         }
00746       }
00747 
00748       // Clean up the socket hash table (and contents):
00749       Groupsock* oldGS;
00750       while ((oldGS = (Groupsock*)socketHashTable->RemoveNext()) != NULL) {
00751         delete oldGS;
00752       }
00753       delete socketHashTable;
00754 
00755       if (!success) break; // a fatal error occurred trying to create the RTP and RTCP sockets; we can't continue
00756     }
00757 
00758     // Try to use a big receive buffer for RTP - at least 0.1 second of
00759     // specified bandwidth and at least 50 KB
00760     unsigned rtpBufSize = fBandwidth * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
00761     if (rtpBufSize < 50 * 1024)
00762       rtpBufSize = 50 * 1024;
00763     increaseReceiveBufferTo(env(), fRTPSocket->socketNum(), rtpBufSize);
00764 
00765     if (isSSM() && fRTCPSocket != NULL) {
00766       // Special case for RTCP SSM: Send RTCP packets back to the source via unicast:
00767       fRTCPSocket->changeDestinationParameters(fSourceFilterAddr,0,~0);
00768     }
00769 
00770     // Create "fRTPSource" and "fReadSource":
00771     if (!createSourceObjects(useSpecialRTPoffset)) break;
00772 
00773     if (fReadSource == NULL) {
00774       env().setResultMsg("Failed to create read source");
00775       break;
00776     }
00777 
00778     // Finally, create our RTCP instance. (It starts running automatically)
00779     if (fRTPSource != NULL && fRTCPSocket != NULL) {
00780       // If bandwidth is specified, use it and add 5% for RTCP overhead.
00781       // Otherwise make a guess at 500 kbps.
00782       unsigned totSessionBandwidth
00783         = fBandwidth ? fBandwidth + fBandwidth / 20 : 500;
00784       fRTCPInstance = RTCPInstance::createNew(env(), fRTCPSocket,
00785                                               totSessionBandwidth,
00786                                               (unsigned char const*)
00787                                               fParent.CNAME(),
00788                                               NULL /* we're a client */,
00789                                               fRTPSource);
00790       if (fRTCPInstance == NULL) {
00791         env().setResultMsg("Failed to create RTCP instance");
00792         break;
00793       }
00794     }
00795 
00796     return True;
00797   } while (0);
00798 
00799   delete fRTPSocket; fRTPSocket = NULL;
00800   delete fRTCPSocket; fRTCPSocket = NULL;
00801   Medium::close(fRTCPInstance); fRTCPInstance = NULL;
00802   Medium::close(fReadSource); fReadSource = fRTPSource = NULL;
00803   fClientPortNum = 0;
00804   return False;
00805 }
00806 
00807 void MediaSubsession::deInitiate() {
00808   Medium::close(fRTCPInstance);
00809   fRTCPInstance = NULL;
00810 
00811   Medium::close(fReadSource); // this is assumed to also close fRTPSource
00812   fReadSource = NULL; fRTPSource = NULL;
00813 
00814   delete fRTCPSocket; delete fRTPSocket;
00815   fRTCPSocket = fRTPSocket = NULL;
00816 }
00817 
00818 Boolean MediaSubsession::setClientPortNum(unsigned short portNum) {
00819   if (fReadSource != NULL) {
00820     env().setResultMsg("A read source has already been created");
00821     return False;
00822   }
00823 
00824   fClientPortNum = portNum;
00825   return True;
00826 }
00827 
00828 netAddressBits MediaSubsession::connectionEndpointAddress() const {
00829   do {
00830     // Get the endpoint name from with us, or our parent session:
00831     char const* endpointString = connectionEndpointName();
00832     if (endpointString == NULL) {
00833       endpointString = parentSession().connectionEndpointName();
00834     }
00835     if (endpointString == NULL) break;
00836 
00837     // Now, convert this name to an address, if we can:
00838     NetAddressList addresses(endpointString);
00839     if (addresses.numAddresses() == 0) break;
00840 
00841     return *(netAddressBits*)(addresses.firstAddress()->data());
00842   } while (0);
00843 
00844   // No address known:
00845   return 0;
00846 }
00847 
00848 void MediaSubsession::setDestinations(netAddressBits defaultDestAddress) {
00849   // Get the destination address from the connection endpoint name
00850   // (This will be 0 if it's not known, in which case we use the default)
00851   netAddressBits destAddress = connectionEndpointAddress();
00852   if (destAddress == 0) destAddress = defaultDestAddress;
00853   struct in_addr destAddr; destAddr.s_addr = destAddress;
00854 
00855   // The destination TTL remains unchanged:
00856   int destTTL = ~0; // means: don't change
00857 
00858   if (fRTPSocket != NULL) {
00859     Port destPort(serverPortNum);
00860     fRTPSocket->changeDestinationParameters(destAddr, destPort, destTTL);
00861   }
00862   if (fRTCPSocket != NULL && !isSSM()) {
00863     // Note: For SSM sessions, the dest address for RTCP was already set.
00864     Port destPort(serverPortNum+1);
00865     fRTCPSocket->changeDestinationParameters(destAddr, destPort, destTTL);
00866   }
00867 }
00868 
00869 void MediaSubsession::setSessionId(char const* sessionId) {
00870   delete[] fSessionId;
00871   fSessionId = strDup(sessionId);
00872 }
00873 
00874 double MediaSubsession::getNormalPlayTime(struct timeval const& presentationTime) {
00875   if (rtpSource() == NULL || rtpSource()->timestampFrequency() == 0) return 0.0; // no RTP source, or bad freq!
00876 
00877   // First, check whether our "RTPSource" object has already been synchronized using RTCP.
00878   // If it hasn't, then - as a special case - we need to use the RTP timestamp to compute the NPT.
00879   if (!rtpSource()->hasBeenSynchronizedUsingRTCP()) {
00880     if (!rtpInfo.infoIsNew) return 0.0; // the "rtpInfo" structure has not been filled in
00881     u_int32_t timestampOffset = rtpSource()->curPacketRTPTimestamp() - rtpInfo.timestamp;
00882     double nptOffset = (timestampOffset/(double)(rtpSource()->timestampFrequency()))*scale();
00883     double npt = playStartTime() + nptOffset;
00884 
00885     return npt;
00886   } else {
00887     // Common case: We have been synchronized using RTCP.  This means that the "presentationTime" parameter
00888     // will be accurate, and so we should use this to compute the NPT.
00889     double ptsDouble = (double)(presentationTime.tv_sec + presentationTime.tv_usec/1000000.0);
00890 
00891     if (rtpInfo.infoIsNew) {
00892       // This is the first time we've been called with a synchronized presentation time since the "rtpInfo"
00893       // structure was last filled in.  Use this "presentationTime" to compute "fNPT_PTS_Offset":
00894       if (seqNumLT(rtpSource()->curPacketRTPSeqNum(), rtpInfo.seqNum)) return -0.1; // sanity check; ignore old packets
00895       u_int32_t timestampOffset = rtpSource()->curPacketRTPTimestamp() - rtpInfo.timestamp;
00896       double nptOffset = (timestampOffset/(double)(rtpSource()->timestampFrequency()))*scale();
00897       double npt = playStartTime() + nptOffset;
00898       fNPT_PTS_Offset = npt - ptsDouble*scale();
00899       rtpInfo.infoIsNew = False; // for next time
00900 
00901       return npt;
00902     } else {
00903       // Use the precomputed "fNPT_PTS_Offset" to compute the NPT from the PTS:
00904       if (fNPT_PTS_Offset == 0.0) return 0.0; // error: The "rtpInfo" structure was apparently never filled in
00905       return (double)(ptsDouble*scale() + fNPT_PTS_Offset);
00906     }
00907   }
00908 }
00909 
00910 Boolean MediaSubsession::parseSDPLine_c(char const* sdpLine) {
00911   // Check for "c=IN IP4 <connection-endpoint>"
00912   // or "c=IN IP4 <connection-endpoint>/<ttl+numAddresses>"
00913   // (Later, do something with <ttl+numAddresses> also #####)
00914   char* connectionEndpointName = parseCLine(sdpLine);
00915   if (connectionEndpointName != NULL) {
00916     delete[] fConnectionEndpointName;
00917     fConnectionEndpointName = connectionEndpointName;
00918     return True;
00919   }
00920 
00921   return False;
00922 }
00923 
00924 Boolean MediaSubsession::parseSDPLine_b(char const* sdpLine) {
00925   // Check for "b=<bwtype>:<bandwidth>" line
00926   // RTP applications are expected to use bwtype="AS"
00927   return sscanf(sdpLine, "b=AS:%u", &fBandwidth) == 1;
00928 }
00929 
00930 Boolean MediaSubsession::parseSDPAttribute_rtpmap(char const* sdpLine) {
00931   // Check for a "a=rtpmap:<fmt> <codec>/<freq>" line:
00932   // (Also check without the "/<freq>"; RealNetworks omits this)
00933   // Also check for a trailing "/<numChannels>".
00934   Boolean parseSuccess = False;
00935 
00936   unsigned rtpmapPayloadFormat;
00937   char* codecName = strDupSize(sdpLine); // ensures we have enough space
00938   unsigned rtpTimestampFrequency = 0;
00939   unsigned numChannels = 1;
00940   if (sscanf(sdpLine, "a=rtpmap: %u %[^/]/%u/%u",
00941              &rtpmapPayloadFormat, codecName, &rtpTimestampFrequency,
00942              &numChannels) == 4
00943       || sscanf(sdpLine, "a=rtpmap: %u %[^/]/%u",
00944              &rtpmapPayloadFormat, codecName, &rtpTimestampFrequency) == 3
00945       || sscanf(sdpLine, "a=rtpmap: %u %s",
00946                 &rtpmapPayloadFormat, codecName) == 2) {
00947     parseSuccess = True;
00948     if (rtpmapPayloadFormat == fRTPPayloadFormat) {
00949       // This "rtpmap" matches our payload format, so set our
00950       // codec name and timestamp frequency:
00951       // (First, make sure the codec name is upper case)
00952       {
00953         Locale l("POSIX");
00954         for (char* p = codecName; *p != '\0'; ++p) *p = toupper(*p);
00955       }
00956       delete[] fCodecName; fCodecName = strDup(codecName);
00957       fRTPTimestampFrequency = rtpTimestampFrequency;
00958       fNumChannels = numChannels;
00959     }
00960   }
00961   delete[] codecName;
00962 
00963   return parseSuccess;
00964 }
00965 
00966 Boolean MediaSubsession::parseSDPAttribute_control(char const* sdpLine) {
00967   // Check for a "a=control:<control-path>" line:
00968   Boolean parseSuccess = False;
00969 
00970   char* controlPath = strDupSize(sdpLine); // ensures we have enough space
00971   if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) {
00972     parseSuccess = True;
00973     delete[] fControlPath; fControlPath = strDup(controlPath);
00974   }
00975   delete[] controlPath;
00976 
00977   return parseSuccess;
00978 }
00979 
00980 Boolean MediaSubsession::parseSDPAttribute_range(char const* sdpLine) {
00981   // Check for a "a=range:npt=<startTime>-<endTime>" line:
00982   // (Later handle other kinds of "a=range" attributes also???#####)
00983   Boolean parseSuccess = False;
00984 
00985   double playStartTime;
00986   double playEndTime;
00987   if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) {
00988     parseSuccess = True;
00989     if (playStartTime > fPlayStartTime) {
00990       fPlayStartTime = playStartTime;
00991       if (playStartTime > fParent.playStartTime()) {
00992         fParent.playStartTime() = playStartTime;
00993       }
00994     }
00995     if (playEndTime > fPlayEndTime) {
00996       fPlayEndTime = playEndTime;
00997       if (playEndTime > fParent.playEndTime()) {
00998         fParent.playEndTime() = playEndTime;
00999       }
01000     }
01001   } else if (parseRangeAttribute(sdpLine, _absStartTime(), _absEndTime())) {
01002     parseSuccess = True;
01003   }
01004 
01005   return parseSuccess;
01006 }
01007 
01008 Boolean MediaSubsession::parseSDPAttribute_fmtp(char const* sdpLine) {
01009   // Check for a "a=fmtp:" line:
01010   // TEMP: We check only for a handful of expected parameter names #####
01011   // Later: (i) check that payload format number matches; #####
01012   //        (ii) look for other parameters also (generalize?) #####
01013   do {
01014     if (strncmp(sdpLine, "a=fmtp:", 7) != 0) break; sdpLine += 7;
01015     while (isdigit(*sdpLine)) ++sdpLine;
01016 
01017     // The remaining "sdpLine" should be a sequence of
01018     //     <name>=<value>;
01019     // parameter assignments.  Look at each of these.
01020     // First, convert the line to lower-case, to ease comparison:
01021     char* const lineCopy = strDup(sdpLine); char* line = lineCopy;
01022     {
01023       Locale l("POSIX");
01024       for (char* c = line; *c != '\0'; ++c) *c = tolower(*c);
01025     }
01026     while (*line != '\0' && *line != '\r' && *line != '\n') {
01027       unsigned u;
01028       char* valueStr = strDupSize(line);
01029       if (sscanf(line, " auxiliarydatasizelength = %u", &u) == 1) {
01030         fAuxiliarydatasizelength = u;
01031       } else if (sscanf(line, " constantduration = %u", &u) == 1) {
01032         fConstantduration = u;
01033       } else if (sscanf(line, " constantsize; = %u", &u) == 1) {
01034         fConstantsize = u;
01035       } else if (sscanf(line, " crc = %u", &u) == 1) {
01036         fCRC = u;
01037       } else if (sscanf(line, " ctsdeltalength = %u", &u) == 1) {
01038         fCtsdeltalength = u;
01039       } else if (sscanf(line, " de-interleavebuffersize = %u", &u) == 1) {
01040         fDe_interleavebuffersize = u;
01041       } else if (sscanf(line, " dtsdeltalength = %u", &u) == 1) {
01042         fDtsdeltalength = u;
01043       } else if (sscanf(line, " indexdeltalength = %u", &u) == 1) {
01044         fIndexdeltalength = u;
01045       } else if (sscanf(line, " indexlength = %u", &u) == 1) {
01046         fIndexlength = u;
01047       } else if (sscanf(line, " interleaving = %u", &u) == 1) {
01048         fInterleaving = u;
01049       } else if (sscanf(line, " maxdisplacement = %u", &u) == 1) {
01050         fMaxdisplacement = u;
01051       } else if (sscanf(line, " objecttype = %u", &u) == 1) {
01052         fObjecttype = u;
01053       } else if (sscanf(line, " octet-align = %u", &u) == 1) {
01054         fOctetalign = u;
01055       } else if (sscanf(line, " profile-level-id = %x", &u) == 1) {
01056         // Note that the "profile-level-id" parameter is assumed to be hexadecimal
01057         fProfile_level_id = u;
01058       } else if (sscanf(line, " robust-sorting = %u", &u) == 1) {
01059         fRobustsorting = u;
01060       } else if (sscanf(line, " sizelength = %u", &u) == 1) {
01061         fSizelength = u;
01062       } else if (sscanf(line, " streamstateindication = %u", &u) == 1) {
01063         fStreamstateindication = u;
01064       } else if (sscanf(line, " streamtype = %u", &u) == 1) {
01065         fStreamtype = u;
01066       } else if (sscanf(line, " cpresent = %u", &u) == 1) {
01067         fCpresent = u != 0;
01068       } else if (sscanf(line, " randomaccessindication = %u", &u) == 1) {
01069         fRandomaccessindication = u != 0;
01070       } else if (sscanf(sdpLine, " config = %[^; \t\r\n]", valueStr) == 1 ||
01071                  sscanf(sdpLine, " configuration = %[^; \t\r\n]", valueStr) == 1) {
01072         // Note: We used "sdpLine" here, because the value may be case-sensitive (if it's Base-64).
01073         delete[] fConfig; fConfig = strDup(valueStr);
01074       } else if (sscanf(line, " mode = %[^; \t\r\n]", valueStr) == 1) {
01075         delete[] fMode; fMode = strDup(valueStr);
01076       } else if (sscanf(sdpLine, " sprop-parameter-sets = %[^; \t\r\n]", valueStr) == 1) {
01077         // Note: We used "sdpLine" here, because the value is case-sensitive.
01078         delete[] fSpropParameterSets; fSpropParameterSets = strDup(valueStr);
01079       } else if (sscanf(line, " emphasis = %[^; \t\r\n]", valueStr) == 1) {
01080         delete[] fEmphasis; fEmphasis = strDup(valueStr);
01081       } else if (sscanf(sdpLine, " channel-order = %[^; \t\r\n]", valueStr) == 1) {
01082         // Note: We used "sdpLine" here, because the value is case-sensitive.
01083         delete[] fChannelOrder; fChannelOrder = strDup(valueStr);
01084       } else {
01085         // Some of the above parameters are Boolean.  Check whether the parameter
01086         // names appear alone, without a "= 1" at the end:
01087         if (sscanf(line, " %[^; \t\r\n]", valueStr) == 1) {
01088           if (strcmp(valueStr, "octet-align") == 0) {
01089             fOctetalign = 1;
01090           } else if (strcmp(valueStr, "cpresent") == 0) {
01091             fCpresent = True;
01092           } else if (strcmp(valueStr, "crc") == 0) {
01093             fCRC = 1;
01094           } else if (strcmp(valueStr, "robust-sorting") == 0) {
01095             fRobustsorting = 1;
01096           } else if (strcmp(valueStr, "randomaccessindication") == 0) {
01097             fRandomaccessindication = True;
01098           }
01099         }
01100       }
01101       delete[] valueStr;
01102 
01103       // Move to the next parameter assignment string:
01104       while (*line != '\0' && *line != '\r' && *line != '\n'
01105              && *line != ';') ++line;
01106       while (*line == ';') ++line;
01107 
01108       // Do the same with sdpLine; needed for finding case sensitive values:
01109       while (*sdpLine != '\0' && *sdpLine != '\r' && *sdpLine != '\n'
01110              && *sdpLine != ';') ++sdpLine;
01111       while (*sdpLine == ';') ++sdpLine;
01112     }
01113     delete[] lineCopy;
01114     return True;
01115   } while (0);
01116 
01117   return False;
01118 }
01119 
01120 Boolean MediaSubsession
01121 ::parseSDPAttribute_source_filter(char const* sdpLine) {
01122   return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr);
01123 }
01124 
01125 Boolean MediaSubsession::parseSDPAttribute_x_dimensions(char const* sdpLine) {
01126   // Check for a "a=x-dimensions:<width>,<height>" line:
01127   Boolean parseSuccess = False;
01128 
01129   int width, height;
01130   if (sscanf(sdpLine, "a=x-dimensions:%d,%d", &width, &height) == 2) {
01131     parseSuccess = True;
01132     fVideoWidth = (unsigned short)width;
01133     fVideoHeight = (unsigned short)height;
01134   }
01135 
01136   return parseSuccess;
01137 }
01138 
01139 Boolean MediaSubsession::parseSDPAttribute_framerate(char const* sdpLine) {
01140   // Check for a "a=framerate: <fps>" or "a=x-framerate: <fps>" line:
01141   Boolean parseSuccess = False;
01142 
01143   float frate;
01144   int rate;
01145   if (sscanf(sdpLine, "a=framerate: %f", &frate) == 1 || sscanf(sdpLine, "a=framerate:%f", &frate) == 1) {
01146     parseSuccess = True;
01147     fVideoFPS = (unsigned)frate;
01148   } else if (sscanf(sdpLine, "a=x-framerate: %d", &rate) == 1) {
01149     parseSuccess = True;
01150     fVideoFPS = (unsigned)rate;
01151   }
01152 
01153   return parseSuccess;
01154 }
01155 
01156 Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
01157   do {
01158     // First, check "fProtocolName"
01159     if (strcmp(fProtocolName, "UDP") == 0) {
01160       // A UDP-packetized stream (*not* a RTP stream)
01161       fReadSource = BasicUDPSource::createNew(env(), fRTPSocket);
01162       fRTPSource = NULL; // Note!
01163       
01164       if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream
01165         fReadSource = MPEG2TransportStreamFramer::createNew(env(), fReadSource);
01166         // this sets "durationInMicroseconds" correctly, based on the PCR values
01167       }
01168     } else {
01169       // Check "fCodecName" against the set of codecs that we support,
01170       // and create our RTP source accordingly
01171       // (Later make this code more efficient, as this set grows #####)
01172       // (Also, add more fmts that can be implemented by SimpleRTPSource#####)
01173       Boolean createSimpleRTPSource = False; // by default; can be changed below
01174       Boolean doNormalMBitRule = False; // default behavior if "createSimpleRTPSource" is True
01175       if (strcmp(fCodecName, "QCELP") == 0) { // QCELP audio
01176         fReadSource =
01177           QCELPAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
01178                                          fRTPPayloadFormat,
01179                                          fRTPTimestampFrequency);
01180         // Note that fReadSource will differ from fRTPSource in this case
01181       } else if (strcmp(fCodecName, "AMR") == 0) { // AMR audio (narrowband)
01182         fReadSource =
01183           AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
01184                                        fRTPPayloadFormat, False /*isWideband*/,
01185                                        fNumChannels, fOctetalign != 0, fInterleaving,
01186                                        fRobustsorting != 0, fCRC != 0);
01187         // Note that fReadSource will differ from fRTPSource in this case
01188       } else if (strcmp(fCodecName, "AMR-WB") == 0) { // AMR audio (wideband)
01189         fReadSource =
01190           AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
01191                                        fRTPPayloadFormat, True /*isWideband*/,
01192                                        fNumChannels, fOctetalign != 0, fInterleaving,
01193                                        fRobustsorting != 0, fCRC != 0);
01194         // Note that fReadSource will differ from fRTPSource in this case
01195       } else if (strcmp(fCodecName, "MPA") == 0) { // MPEG-1 or 2 audio
01196         fReadSource = fRTPSource
01197           = MPEG1or2AudioRTPSource::createNew(env(), fRTPSocket,
01198                                               fRTPPayloadFormat,
01199                                               fRTPTimestampFrequency);
01200       } else if (strcmp(fCodecName, "MPA-ROBUST") == 0) { // robust MP3 audio
01201         fReadSource = fRTPSource
01202           = MP3ADURTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01203                                        fRTPTimestampFrequency);
01204         if (fRTPSource == NULL) break;
01205         
01206         if (!fReceiveRawMP3ADUs) {
01207           // Add a filter that deinterleaves the ADUs after depacketizing them:
01208           MP3ADUdeinterleaver* deinterleaver
01209             = MP3ADUdeinterleaver::createNew(env(), fRTPSource);
01210           if (deinterleaver == NULL) break;
01211         
01212           // Add another filter that converts these ADUs to MP3 frames:
01213           fReadSource = MP3FromADUSource::createNew(env(), deinterleaver);
01214         }
01215       } else if (strcmp(fCodecName, "X-MP3-DRAFT-00") == 0) {
01216         // a non-standard variant of "MPA-ROBUST" used by RealNetworks
01217         // (one 'ADU'ized MP3 frame per packet; no headers)
01218         fRTPSource
01219           = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01220                                        fRTPTimestampFrequency,
01221                                        "audio/MPA-ROBUST" /*hack*/);
01222         if (fRTPSource == NULL) break;
01223         
01224         // Add a filter that converts these ADUs to MP3 frames:
01225         fReadSource = MP3FromADUSource::createNew(env(), fRTPSource,
01226                                                   False /*no ADU header*/);
01227       } else if (strcmp(fCodecName, "MP4A-LATM") == 0) { // MPEG-4 LATM audio
01228         fReadSource = fRTPSource
01229           = MPEG4LATMAudioRTPSource::createNew(env(), fRTPSocket,
01230                                                fRTPPayloadFormat,
01231                                                fRTPTimestampFrequency);
01232       } else if (strcmp(fCodecName, "VORBIS") == 0) { // Vorbis audio
01233         fReadSource = fRTPSource
01234           = VorbisAudioRTPSource::createNew(env(), fRTPSocket,
01235                                             fRTPPayloadFormat,
01236                                             fRTPTimestampFrequency);
01237       } else if (strcmp(fCodecName, "VP8") == 0) { // VP8 video
01238         fReadSource = fRTPSource
01239           = VP8VideoRTPSource::createNew(env(), fRTPSocket,
01240                                          fRTPPayloadFormat,
01241                                          fRTPTimestampFrequency);
01242       } else if (strcmp(fCodecName, "AC3") == 0 || strcmp(fCodecName, "EAC3") == 0) { // AC3 audio
01243         fReadSource = fRTPSource
01244           = AC3AudioRTPSource::createNew(env(), fRTPSocket,
01245                                          fRTPPayloadFormat,
01246                                          fRTPTimestampFrequency);
01247       } else if (strcmp(fCodecName, "MP4V-ES") == 0) { // MPEG-4 Elementary Stream video
01248         fReadSource = fRTPSource
01249           = MPEG4ESVideoRTPSource::createNew(env(), fRTPSocket,
01250                                              fRTPPayloadFormat,
01251                                              fRTPTimestampFrequency);
01252       } else if (strcmp(fCodecName, "MPEG4-GENERIC") == 0) {
01253         fReadSource = fRTPSource
01254           = MPEG4GenericRTPSource::createNew(env(), fRTPSocket,
01255                                              fRTPPayloadFormat,
01256                                              fRTPTimestampFrequency,
01257                                              fMediumName, fMode,
01258                                              fSizelength, fIndexlength,
01259                                              fIndexdeltalength);
01260       } else if (strcmp(fCodecName, "MPV") == 0) { // MPEG-1 or 2 video
01261         fReadSource = fRTPSource
01262           = MPEG1or2VideoRTPSource::createNew(env(), fRTPSocket,
01263                                               fRTPPayloadFormat,
01264                                               fRTPTimestampFrequency);
01265       } else if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream
01266         fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01267                                                 fRTPTimestampFrequency, "video/MP2T",
01268                                                 0, False);
01269         fReadSource = MPEG2TransportStreamFramer::createNew(env(), fRTPSource);
01270         // this sets "durationInMicroseconds" correctly, based on the PCR values
01271       } else if (strcmp(fCodecName, "H261") == 0) { // H.261
01272         fReadSource = fRTPSource
01273           = H261VideoRTPSource::createNew(env(), fRTPSocket,
01274                                           fRTPPayloadFormat,
01275                                           fRTPTimestampFrequency);
01276       } else if (strcmp(fCodecName, "H263-1998") == 0 ||
01277                  strcmp(fCodecName, "H263-2000") == 0) { // H.263+
01278         fReadSource = fRTPSource
01279           = H263plusVideoRTPSource::createNew(env(), fRTPSocket,
01280                                               fRTPPayloadFormat,
01281                                               fRTPTimestampFrequency);
01282       } else if (strcmp(fCodecName, "H264") == 0) {
01283         fReadSource = fRTPSource
01284           = H264VideoRTPSource::createNew(env(), fRTPSocket,
01285                                           fRTPPayloadFormat,
01286                                           fRTPTimestampFrequency);
01287       } else if (strcmp(fCodecName, "DV") == 0) {
01288         fReadSource = fRTPSource
01289           = DVVideoRTPSource::createNew(env(), fRTPSocket,
01290                                         fRTPPayloadFormat,
01291                                         fRTPTimestampFrequency);
01292       } else if (strcmp(fCodecName, "JPEG") == 0) { // motion JPEG
01293         if (fReceiveRawJPEGFrames) {
01294           // Special case (used when proxying JPEG/RTP streams): Receive each JPEG/RTP packet, including the special RTP headers:
01295           fReadSource = fRTPSource
01296             = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01297                                          fRTPTimestampFrequency, "video/JPEG",
01298                                          0/*special offset*/, False/*doNormalMBitRule => ignore the 'M' bit*/);
01299         } else {
01300           // Normal case: Receive each JPEG frame as a complete, displayable JPEG image:
01301           fReadSource = fRTPSource
01302             = JPEGVideoRTPSource::createNew(env(), fRTPSocket,
01303                                             fRTPPayloadFormat,
01304                                             fRTPTimestampFrequency,
01305                                             videoWidth(),
01306                                             videoHeight());
01307         }
01308       } else if (strcmp(fCodecName, "X-QT") == 0
01309                  || strcmp(fCodecName, "X-QUICKTIME") == 0) {
01310         // Generic QuickTime streams, as defined in
01311         // <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
01312         char* mimeType
01313           = new char[strlen(mediumName()) + strlen(codecName()) + 2] ;
01314         sprintf(mimeType, "%s/%s", mediumName(), codecName());
01315         fReadSource = fRTPSource
01316           = QuickTimeGenericRTPSource::createNew(env(), fRTPSocket,
01317                                                  fRTPPayloadFormat,
01318                                                  fRTPTimestampFrequency,
01319                                                  mimeType);
01320         delete[] mimeType;
01321       } else if (  strcmp(fCodecName, "PCMU") == 0 // PCM u-law audio
01322                    || strcmp(fCodecName, "GSM") == 0 // GSM audio
01323                    || strcmp(fCodecName, "DVI4") == 0 // DVI4 (IMA ADPCM) audio
01324                    || strcmp(fCodecName, "PCMA") == 0 // PCM a-law audio
01325                    || strcmp(fCodecName, "MP1S") == 0 // MPEG-1 System Stream
01326                    || strcmp(fCodecName, "MP2P") == 0 // MPEG-2 Program Stream
01327                    || strcmp(fCodecName, "L8") == 0 // 8-bit linear audio
01328                    || strcmp(fCodecName, "L16") == 0 // 16-bit linear audio
01329                    || strcmp(fCodecName, "L20") == 0 // 20-bit linear audio (RFC 3190)
01330                    || strcmp(fCodecName, "L24") == 0 // 24-bit linear audio (RFC 3190)
01331                    || strcmp(fCodecName, "G726-16") == 0 // G.726, 16 kbps
01332                    || strcmp(fCodecName, "G726-24") == 0 // G.726, 24 kbps
01333                    || strcmp(fCodecName, "G726-32") == 0 // G.726, 32 kbps
01334                    || strcmp(fCodecName, "G726-40") == 0 // G.726, 40 kbps
01335                    || strcmp(fCodecName, "SPEEX") == 0 // SPEEX audio
01336                    || strcmp(fCodecName, "ILBC") == 0 // iLBC audio
01337                    || strcmp(fCodecName, "OPUS") == 0 // Opus audio
01338                    || strcmp(fCodecName, "T140") == 0 // T.140 text (RFC 4103)
01339                    || strcmp(fCodecName, "DAT12") == 0 // 12-bit nonlinear audio (RFC 3190)
01340                    || strcmp(fCodecName, "VND.ONVIF.METADATA") == 0 // 'ONVIF' 'metadata' (a XML document)
01341                    ) {
01342         createSimpleRTPSource = True;
01343         useSpecialRTPoffset = 0;
01344         if (strcmp(fCodecName, "VND.ONVIF.METADATA") == 0) {
01345           // This RTP payload format uses the RTP "M" bit to indicate the end of the content (a XML document):
01346           doNormalMBitRule = True;
01347         }
01348       } else if (useSpecialRTPoffset >= 0) {
01349         // We don't know this RTP payload format, but try to receive
01350         // it using a 'SimpleRTPSource' with the specified header offset:
01351         createSimpleRTPSource = True;
01352       } else {
01353         env().setResultMsg("RTP payload format unknown or not supported");
01354         break;
01355       }
01356       
01357       if (createSimpleRTPSource) {
01358         char* mimeType
01359           = new char[strlen(mediumName()) + strlen(codecName()) + 2] ;
01360         sprintf(mimeType, "%s/%s", mediumName(), codecName());
01361         fReadSource = fRTPSource
01362           = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01363                                        fRTPTimestampFrequency, mimeType,
01364                                        (unsigned)useSpecialRTPoffset,
01365                                        doNormalMBitRule);
01366         delete[] mimeType;
01367       }
01368     }
01369 
01370     return True;
01371   } while (0);
01372 
01373   return False; // an error occurred
01374 }

Generated on Mon Apr 29 13:28:01 2013 for live by  doxygen 1.5.2