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

Generated on Fri Sep 3 02:35:41 2010 for live by  doxygen 1.5.2