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-2008 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 #ifdef SUPPORT_REAL_RTSP
00025 #include "../RealRTSP/include/RealRTSP.hh"
00026 #endif
00027 #include "GroupsockHelper.hh"
00028 #include <ctype.h>
00029 
00031 
00032 MediaSession* MediaSession::createNew(UsageEnvironment& env,
00033                                       char const* sdpDescription) {
00034   MediaSession* newSession = new MediaSession(env);
00035   if (newSession != NULL) {
00036     if (!newSession->initializeWithSDP(sdpDescription)) {
00037       delete newSession;
00038       return NULL;
00039     }
00040   }
00041 
00042   return newSession;
00043 }
00044 
00045 Boolean MediaSession::lookupByName(UsageEnvironment& env,
00046                                    char const* instanceName,
00047                                    MediaSession*& resultSession) {
00048   resultSession = NULL; // unless we succeed
00049 
00050   Medium* medium;
00051   if (!Medium::lookupByName(env, instanceName, medium)) return False;
00052 
00053   if (!medium->isMediaSession()) {
00054     env.setResultMsg(instanceName, " is not a 'MediaSession' object");
00055     return False;
00056   }
00057 
00058   resultSession = (MediaSession*)medium;
00059   return True;
00060 }
00061 
00062 MediaSession::MediaSession(UsageEnvironment& env)
00063   : Medium(env),
00064     fSubsessionsHead(NULL), fSubsessionsTail(NULL),
00065     fConnectionEndpointName(NULL), fMaxPlayStartTime(0.0f), fMaxPlayEndTime(0.0f),
00066     fScale(1.0f), fMediaSessionType(NULL), fSessionName(NULL), fSessionDescription(NULL),
00067     fControlPath(NULL) {
00068 #ifdef SUPPORT_REAL_RTSP
00069   RealInitSDPAttributes(this);
00070 #endif
00071   fSourceFilterAddr.s_addr = 0;
00072 
00073   // Get our host name, and use this for the RTCP CNAME:
00074   const unsigned maxCNAMElen = 100;
00075   char CNAME[maxCNAMElen+1];
00076 #ifndef CRIS
00077   gethostname((char*)CNAME, maxCNAMElen);
00078 #else
00079   // "gethostname()" isn't defined for this platform
00080   sprintf(CNAME, "unknown host %d", (unsigned)(our_random()*0x7FFFFFFF));
00081 #endif
00082   CNAME[maxCNAMElen] = '\0'; // just in case
00083   fCNAME = strDup(CNAME);
00084 }
00085 
00086 MediaSession::~MediaSession() {
00087   delete fSubsessionsHead;
00088   delete[] fCNAME;
00089   delete[] fConnectionEndpointName;
00090   delete[] fMediaSessionType;
00091   delete[] fSessionName;
00092   delete[] fSessionDescription;
00093   delete[] fControlPath;
00094 #ifdef SUPPORT_REAL_RTSP
00095   RealReclaimSDPAttributes(this);
00096 #endif
00097 }
00098 
00099 Boolean MediaSession::isMediaSession() const {
00100   return True;
00101 }
00102 
00103 Boolean MediaSession::initializeWithSDP(char const* sdpDescription) {
00104   if (sdpDescription == NULL) return False;
00105 
00106   // Begin by processing all SDP lines until we see the first "m="
00107   char const* sdpLine = sdpDescription;
00108   char const* nextSDPLine;
00109   while (1) {
00110     if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
00111     //##### We should really check for:
00112     // - "a=control:" attributes (to set the URL for aggregate control)
00113     // - the correct SDP version (v=0)
00114     if (sdpLine[0] == 'm') break;
00115     sdpLine = nextSDPLine;
00116     if (sdpLine == NULL) break; // there are no m= lines at all
00117 
00118     // Check for various special SDP lines that we understand:
00119     if (parseSDPLine_s(sdpLine)) continue;
00120     if (parseSDPLine_i(sdpLine)) continue;
00121     if (parseSDPLine_c(sdpLine)) continue;
00122     if (parseSDPAttribute_control(sdpLine)) continue;
00123     if (parseSDPAttribute_range(sdpLine)) continue;
00124     if (parseSDPAttribute_type(sdpLine)) continue;
00125     if (parseSDPAttribute_source_filter(sdpLine)) continue;
00126 #ifdef SUPPORT_REAL_RTSP
00127     if (RealParseSDPAttributes(this, sdpLine)) continue;
00128 #endif
00129   }
00130 
00131   while (sdpLine != NULL) {
00132     // We have a "m=" line, representing a new sub-session:
00133     MediaSubsession* subsession = new MediaSubsession(*this);
00134     if (subsession == NULL) {
00135       envir().setResultMsg("Unable to create new MediaSubsession");
00136       return False;
00137     }
00138 
00139     // Parse the line as "m=<medium_name> <client_portNum> RTP/AVP <fmt>"
00140     // or "m=<medium_name> <client_portNum>/<num_ports> RTP/AVP <fmt>"
00141     // (Should we be checking for >1 payload format number here?)#####
00142     char* mediumName = strDupSize(sdpLine); // ensures we have enough space
00143     char* protocolName = NULL;
00144     unsigned payloadFormat;
00145     if ((sscanf(sdpLine, "m=%s %hu RTP/AVP %u",
00146                 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
00147          sscanf(sdpLine, "m=%s %hu/%*u RTP/AVP %u",
00148                 mediumName, &subsession->fClientPortNum, &payloadFormat) == 3)
00149         && payloadFormat <= 127) {
00150       protocolName = "RTP";
00151     } else if ((sscanf(sdpLine, "m=%s %hu UDP %u",
00152                        mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
00153                 sscanf(sdpLine, "m=%s %hu udp %u",
00154                        mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 ||
00155                 sscanf(sdpLine, "m=%s %hu RAW/RAW/UDP %u",
00156                        mediumName, &subsession->fClientPortNum, &payloadFormat) == 3)
00157                && payloadFormat <= 127) {
00158       // This is a RAW UDP source
00159       protocolName = "UDP";
00160     } else {
00161       // This "m=" line is bad; output an error message saying so:
00162       char* sdpLineStr;
00163       if (nextSDPLine == NULL) {
00164         sdpLineStr = (char*)sdpLine;
00165       } else {
00166         sdpLineStr = strDup(sdpLine);
00167         sdpLineStr[nextSDPLine-sdpLine] = '\0';
00168       }
00169       envir() << "Bad SDP \"m=\" line: " <<  sdpLineStr << "\n";
00170       if (sdpLineStr != (char*)sdpLine) delete[] sdpLineStr;
00171 
00172       delete[] mediumName;
00173       delete subsession;
00174 
00175       // Skip the following SDP lines, up until the next "m=":
00176       while (1) {
00177         sdpLine = nextSDPLine;
00178         if (sdpLine == NULL) break; // we've reached the end
00179         if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
00180 
00181         if (sdpLine[0] == 'm') break; // we've reached the next subsession
00182       }
00183       continue;
00184     }
00185 
00186     // Insert this subsession at the end of the list:
00187     if (fSubsessionsTail == NULL) {
00188       fSubsessionsHead = fSubsessionsTail = subsession;
00189     } else {
00190       fSubsessionsTail->setNext(subsession);
00191       fSubsessionsTail = subsession;
00192     }
00193 
00194     subsession->serverPortNum = subsession->fClientPortNum; // by default
00195 
00196     char const* mStart = sdpLine;
00197     subsession->fSavedSDPLines = strDup(mStart);
00198 
00199     subsession->fMediumName = strDup(mediumName);
00200     delete[] mediumName;
00201     subsession->fProtocolName = strDup(protocolName);
00202     subsession->fRTPPayloadFormat = payloadFormat;
00203 
00204     // Process the following SDP lines, up until the next "m=":
00205     while (1) {
00206       sdpLine = nextSDPLine;
00207       if (sdpLine == NULL) break; // we've reached the end
00208       if (!parseSDPLine(sdpLine, nextSDPLine)) return False;
00209 
00210       if (sdpLine[0] == 'm') break; // we've reached the next subsession
00211 
00212       // Check for various special SDP lines that we understand:
00213       if (subsession->parseSDPLine_c(sdpLine)) continue;
00214       if (subsession->parseSDPAttribute_rtpmap(sdpLine)) continue;
00215       if (subsession->parseSDPAttribute_control(sdpLine)) continue;
00216       if (subsession->parseSDPAttribute_range(sdpLine)) continue;
00217       if (subsession->parseSDPAttribute_fmtp(sdpLine)) continue;
00218       if (subsession->parseSDPAttribute_source_filter(sdpLine)) continue;
00219       if (subsession->parseSDPAttribute_x_dimensions(sdpLine)) continue;
00220       if (subsession->parseSDPAttribute_framerate(sdpLine)) continue;
00221 #ifdef SUPPORT_REAL_RTSP
00222       if (RealParseSDPAttributes(subsession, sdpLine)) continue;
00223 #endif
00224 
00225       // (Later, check for malformed lines, and other valid SDP lines#####)
00226     }
00227     if (sdpLine != NULL) subsession->fSavedSDPLines[sdpLine-mStart] = '\0';
00228 
00229     // If we don't yet know the codec name, try looking it up from the
00230     // list of static payload types:
00231     if (subsession->fCodecName == NULL) {
00232       subsession->fCodecName
00233         = lookupPayloadFormat(subsession->fRTPPayloadFormat,
00234                               subsession->fRTPTimestampFrequency,
00235                               subsession->fNumChannels);
00236       if (subsession->fCodecName == NULL) {
00237         char typeStr[20];
00238         sprintf(typeStr, "%d", subsession->fRTPPayloadFormat);
00239         envir().setResultMsg("Unknown codec name for RTP payload type ",
00240                              typeStr);
00241         return False;
00242       }
00243     }
00244 
00245     // If we don't yet know this subsession's RTP timestamp frequency
00246     // (because it uses a dynamic payload type and the corresponding
00247     // SDP "rtpmap" attribute erroneously didn't specify it),
00248     // then guess it now:
00249     if (subsession->fRTPTimestampFrequency == 0) {
00250       subsession->fRTPTimestampFrequency
00251         = guessRTPTimestampFrequency(subsession->fMediumName,
00252                                      subsession->fCodecName);
00253     }
00254   }
00255 
00256   return True;
00257 }
00258 
00259 Boolean MediaSession::parseSDPLine(char const* inputLine,
00260                                    char const*& nextLine){
00261   // Begin by finding the start of the next line (if any):
00262   nextLine = NULL;
00263   for (char const* ptr = inputLine; *ptr != '\0'; ++ptr) {
00264     if (*ptr == '\r' || *ptr == '\n') {
00265       // We found the end of the line
00266       ++ptr;
00267       while (*ptr == '\r' || *ptr == '\n') ++ptr;
00268       nextLine = ptr;
00269       if (nextLine[0] == '\0') nextLine = NULL; // special case for end
00270       break;
00271     }
00272   }
00273 
00274   // Then, check that this line is a SDP line of the form <char>=<etc>
00275   // (However, we also accept blank lines in the input.)
00276   if (inputLine[0] == '\r' || inputLine[0] == '\n') return True;
00277   if (strlen(inputLine) < 2 || inputLine[1] != '='
00278       || inputLine[0] < 'a' || inputLine[0] > 'z') {
00279     envir().setResultMsg("Invalid SDP line: ", inputLine);
00280     return False;
00281   }
00282 
00283   return True;
00284 }
00285 
00286 static char* parseCLine(char const* sdpLine) {
00287   char* resultStr = NULL;
00288   char* buffer = strDupSize(sdpLine); // ensures we have enough space
00289   if (sscanf(sdpLine, "c=IN IP4 %[^/ ]", buffer) == 1) {
00290     // Later, handle the optional /<ttl> and /<numAddresses> #####
00291     resultStr = strDup(buffer);
00292   }
00293   delete[] buffer;
00294 
00295   return resultStr;
00296 }
00297 
00298 Boolean MediaSession::parseSDPLine_s(char const* sdpLine) {
00299   // Check for "s=<session name>" line
00300   char* buffer = strDupSize(sdpLine);
00301   Boolean parseSuccess = False;
00302 
00303   if (sscanf(sdpLine, "s=%[^\r\n]", buffer) == 1) {
00304     delete[] fSessionName; fSessionName = strDup(buffer);
00305     parseSuccess = True;
00306   }
00307   delete[] buffer;
00308 
00309   return parseSuccess;
00310 }
00311 
00312 Boolean MediaSession::parseSDPLine_i(char const* sdpLine) {
00313   // Check for "i=<session description>" line
00314   char* buffer = strDupSize(sdpLine);
00315   Boolean parseSuccess = False;
00316 
00317   if (sscanf(sdpLine, "i=%[^\r\n]", buffer) == 1) {
00318     delete[] fSessionDescription; fSessionDescription = strDup(buffer);
00319     parseSuccess = True;
00320   }
00321   delete[] buffer;
00322 
00323   return parseSuccess;
00324 }
00325 
00326 Boolean MediaSession::parseSDPLine_c(char const* sdpLine) {
00327   // Check for "c=IN IP4 <connection-endpoint>"
00328   // or "c=IN IP4 <connection-endpoint>/<ttl+numAddresses>"
00329   // (Later, do something with <ttl+numAddresses> also #####)
00330   char* connectionEndpointName = parseCLine(sdpLine);
00331   if (connectionEndpointName != NULL) {
00332     delete[] fConnectionEndpointName;
00333     fConnectionEndpointName = connectionEndpointName;
00334     return True;
00335   }
00336 
00337   return False;
00338 }
00339 
00340 Boolean MediaSession::parseSDPAttribute_type(char const* sdpLine) {
00341   // Check for a "a=type:broadcast|meeting|moderated|test|H.332|recvonly" line:
00342   Boolean parseSuccess = False;
00343 
00344   char* buffer = strDupSize(sdpLine);
00345   if (sscanf(sdpLine, "a=type: %[^ ]", buffer) == 1) {
00346     delete[] fMediaSessionType;
00347     fMediaSessionType = strDup(buffer);
00348     parseSuccess = True;
00349   }
00350   delete[] buffer;
00351 
00352   return parseSuccess;
00353 }
00354 
00355 static Boolean parseRangeAttribute(char const* sdpLine, float& startTime, float& endTime) {
00356   return sscanf(sdpLine, "a=range: npt = %g - %g", &startTime, &endTime) == 2;
00357 }
00358 
00359 Boolean MediaSession::parseSDPAttribute_control(char const* sdpLine) {
00360   // Check for a "a=control:<control-path>" line:
00361   Boolean parseSuccess = False;
00362 
00363   char* controlPath = strDupSize(sdpLine); // ensures we have enough space
00364   if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) {
00365     parseSuccess = True;
00366     delete[] fControlPath; fControlPath = strDup(controlPath);
00367   }
00368   delete[] controlPath;
00369 
00370   return parseSuccess;
00371 }
00372 
00373 Boolean MediaSession::parseSDPAttribute_range(char const* sdpLine) {
00374   // Check for a "a=range:npt=<startTime>-<endTime>" line:
00375   // (Later handle other kinds of "a=range" attributes also???#####)
00376   Boolean parseSuccess = False;
00377 
00378   float playStartTime;
00379   float playEndTime;
00380   if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) {
00381     parseSuccess = True;
00382     if (playStartTime > fMaxPlayStartTime) {
00383       fMaxPlayStartTime = playStartTime;
00384     }
00385     if (playEndTime > fMaxPlayEndTime) {
00386       fMaxPlayEndTime = playEndTime;
00387     }
00388   }
00389 
00390   return parseSuccess;
00391 }
00392 
00393 static Boolean parseSourceFilterAttribute(char const* sdpLine,
00394                                           struct in_addr& sourceAddr) {
00395   // Check for a "a=source-filter:incl IN IP4 <something> <source>" line.
00396   // Note: At present, we don't check that <something> really matches
00397   // one of our multicast addresses.  We also don't support more than
00398   // one <source> #####
00399   Boolean result = False; // until we succeed
00400   char* sourceName = strDupSize(sdpLine); // ensures we have enough space
00401   do {
00402     if (sscanf(sdpLine, "a=source-filter: incl IN IP4 %*s %s",
00403                sourceName) != 1) break;
00404 
00405     // Now, convert this name to an address, if we can:
00406     NetAddressList addresses(sourceName);
00407     if (addresses.numAddresses() == 0) break;
00408 
00409     netAddressBits sourceAddrBits
00410       = *(netAddressBits*)(addresses.firstAddress()->data());
00411     if (sourceAddrBits == 0) break;
00412 
00413     sourceAddr.s_addr = sourceAddrBits;
00414     result = True;
00415   } while (0);
00416 
00417   delete[] sourceName;
00418   return result;
00419 }
00420 
00421 Boolean MediaSession
00422 ::parseSDPAttribute_source_filter(char const* sdpLine) {
00423   return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr);
00424 }
00425 
00426 char* MediaSession::lookupPayloadFormat(unsigned char rtpPayloadType,
00427                                         unsigned& freq, unsigned& nCh) {
00428   // Look up the codec name and timestamp frequency for known (static)
00429   // RTP payload formats.
00430   char* temp = NULL;
00431   switch (rtpPayloadType) {
00432   case 0: {temp = "PCMU"; freq = 8000; nCh = 1; break;}
00433   case 2: {temp = "G726-32"; freq = 8000; nCh = 1; break;}
00434   case 3: {temp = "GSM"; freq = 8000; nCh = 1; break;}
00435   case 4: {temp = "G723"; freq = 8000; nCh = 1; break;}
00436   case 5: {temp = "DVI4"; freq = 8000; nCh = 1; break;}
00437   case 6: {temp = "DVI4"; freq = 16000; nCh = 1; break;}
00438   case 7: {temp = "LPC"; freq = 8000; nCh = 1; break;}
00439   case 8: {temp = "PCMA"; freq = 8000; nCh = 1; break;}
00440   case 9: {temp = "G722"; freq = 8000; nCh = 1; break;}
00441   case 10: {temp = "L16"; freq = 44100; nCh = 2; break;}
00442   case 11: {temp = "L16"; freq = 44100; nCh = 1; break;}
00443   case 12: {temp = "QCELP"; freq = 8000; nCh = 1; break;}
00444   case 14: {temp = "MPA"; freq = 90000; nCh = 1; break;}
00445     // 'number of channels' is actually encoded in the media stream
00446   case 15: {temp = "G728"; freq = 8000; nCh = 1; break;}
00447   case 16: {temp = "DVI4"; freq = 11025; nCh = 1; break;}
00448   case 17: {temp = "DVI4"; freq = 22050; nCh = 1; break;}
00449   case 18: {temp = "G729"; freq = 8000; nCh = 1; break;}
00450   case 25: {temp = "CELB"; freq = 90000; nCh = 1; break;}
00451   case 26: {temp = "JPEG"; freq = 90000; nCh = 1; break;}
00452   case 28: {temp = "NV"; freq = 90000; nCh = 1; break;}
00453   case 31: {temp = "H261"; freq = 90000; nCh = 1; break;}
00454   case 32: {temp = "MPV"; freq = 90000; nCh = 1; break;}
00455   case 33: {temp = "MP2T"; freq = 90000; nCh = 1; break;}
00456   case 34: {temp = "H263"; freq = 90000; nCh = 1; break;}
00457   };
00458 
00459   return strDup(temp);
00460 }
00461 
00462 unsigned MediaSession::guessRTPTimestampFrequency(char const* mediumName,
00463                                                   char const* codecName) {
00464   // By default, we assume that audio sessions use a frequency of 8000,
00465   // and that video sessions use a frequency of 90000.
00466   // Begin by checking for known exceptions to this rule
00467   // (where the frequency is known unambiguously (e.g., not like "DVI4"))
00468   if (strcmp(codecName, "L16") == 0) return 44100;
00469   if (strcmp(codecName, "MPA") == 0
00470       || strcmp(codecName, "MPA-ROBUST") == 0
00471       || strcmp(codecName, "X-MP3-DRAFT-00")) return 90000;
00472 
00473   // Now, guess default values:
00474   if (strcmp(mediumName, "video") == 0) return 90000;
00475   return 8000; // for "audio", and any other medium
00476 }
00477 
00478 Boolean MediaSession
00479 ::initiateByMediaType(char const* mimeType,
00480                       MediaSubsession*& resultSubsession,
00481                       int useSpecialRTPoffset) {
00482   // Look through this session's subsessions for media that match "mimeType"
00483   resultSubsession = NULL;
00484   MediaSubsessionIterator iter(*this);
00485   MediaSubsession* subsession;
00486   while ((subsession = iter.next()) != NULL) {
00487     Boolean wasAlreadyInitiated = subsession->readSource() != NULL;
00488     if (!wasAlreadyInitiated) {
00489       // Try to create a source for this subsession:
00490       if (!subsession->initiate(useSpecialRTPoffset)) return False;
00491     }
00492 
00493     // Make sure the source's MIME type is one that we handle:
00494     if (strcmp(subsession->readSource()->MIMEtype(), mimeType) != 0) {
00495       if (!wasAlreadyInitiated) subsession->deInitiate();
00496       continue;
00497     }
00498 
00499     resultSubsession = subsession;
00500     break; // use this
00501   }
00502 
00503   if (resultSubsession == NULL) {
00504     envir().setResultMsg("Session has no usable media subsession");
00505     return False;
00506   }
00507 
00508   return True;
00509 }
00510 
00511 
00513 
00514 MediaSubsessionIterator::MediaSubsessionIterator(MediaSession& session)
00515   : fOurSession(session) {
00516   reset();
00517 }
00518 
00519 MediaSubsessionIterator::~MediaSubsessionIterator() {
00520 }
00521 
00522 MediaSubsession* MediaSubsessionIterator::next() {
00523   MediaSubsession* result = fNextPtr;
00524 
00525   if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext;
00526 
00527   return result;
00528 }
00529 
00530 void MediaSubsessionIterator::reset() {
00531   fNextPtr = fOurSession.fSubsessionsHead;
00532 }
00533 
00535 
00536 MediaSubsession::MediaSubsession(MediaSession& parent)
00537   : sessionId(NULL), serverPortNum(0), sink(NULL), miscPtr(NULL),
00538     fParent(parent), fNext(NULL),
00539     fConnectionEndpointName(NULL),
00540     fClientPortNum(0), fRTPPayloadFormat(0xFF),
00541     fSavedSDPLines(NULL), fMediumName(NULL), fCodecName(NULL), fProtocolName(NULL),
00542     fRTPTimestampFrequency(0), fControlPath(NULL),
00543     fSourceFilterAddr(parent.sourceFilterAddr()),
00544     fAuxiliarydatasizelength(0), fConstantduration(0), fConstantsize(0),
00545     fCRC(0), fCtsdeltalength(0), fDe_interleavebuffersize(0), fDtsdeltalength(0),
00546     fIndexdeltalength(0), fIndexlength(0), fInterleaving(0), fMaxdisplacement(0),
00547     fObjecttype(0), fOctetalign(0), fProfile_level_id(0), fRobustsorting(0),
00548     fSizelength(0), fStreamstateindication(0), fStreamtype(0),
00549     fCpresent(False), fRandomaccessindication(False),
00550     fConfig(NULL), fMode(NULL), fSpropParameterSets(NULL),
00551     fPlayStartTime(0.0), fPlayEndTime(0.0),
00552     fVideoWidth(0), fVideoHeight(0), fVideoFPS(0), fNumChannels(1), fScale(1.0f), fNPT_PTS_Offset(0.0f),
00553     fRTPSocket(NULL), fRTCPSocket(NULL),
00554     fRTPSource(NULL), fRTCPInstance(NULL), fReadSource(NULL) {
00555   rtpInfo.seqNum = 0; rtpInfo.timestamp = 0; rtpInfo.infoIsNew = False;
00556 #ifdef SUPPORT_REAL_RTSP
00557   RealInitSDPAttributes(this);
00558 #endif
00559 }
00560 
00561 MediaSubsession::~MediaSubsession() {
00562   deInitiate();
00563 
00564   delete[] fConnectionEndpointName; delete[] fSavedSDPLines;
00565   delete[] fMediumName; delete[] fCodecName; delete[] fProtocolName;
00566   delete[] fControlPath; delete[] fConfig; delete[] fMode; delete[] fSpropParameterSets;
00567 
00568   delete fNext;
00569 #ifdef SUPPORT_REAL_RTSP
00570   RealReclaimSDPAttributes(this);
00571 #endif
00572 }
00573 
00574 float MediaSubsession::playStartTime() const {
00575   if (fPlayStartTime > 0) return fPlayStartTime;
00576 
00577   return fParent.playStartTime();
00578 }
00579 
00580 float MediaSubsession::playEndTime() const {
00581   if (fPlayEndTime > 0) return fPlayEndTime;
00582 
00583   return fParent.playEndTime();
00584 }
00585 
00586 Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
00587   if (fReadSource != NULL) return True; // has already been initiated
00588 
00589   do {
00590     if (fCodecName == NULL) {
00591       env().setResultMsg("Codec is unspecified");
00592       break;
00593     }
00594 
00595     // Create RTP and RTCP 'Groupsocks' on which to receive incoming data.
00596     // (Groupsocks will work even for unicast addresses)
00597     Groupsock* oldGroupsock = NULL;
00598     Boolean success = False;
00599     struct in_addr tempAddr;
00600     tempAddr.s_addr = connectionEndpointAddress();
00601         // This could get changed later, as a result of a RTSP "SETUP"
00602     while (1) {
00603       unsigned short rtpPortNum = fClientPortNum&~1;
00604       if (isSSM()) {
00605         fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr,
00606                                    rtpPortNum);
00607       } else {
00608         fRTPSocket = new Groupsock(env(), tempAddr, rtpPortNum, 255);
00609       }
00610       if (fRTPSocket == NULL) {
00611         env().setResultMsg("Failed to create RTP socket");
00612         break;
00613       }
00614 
00615       // Get the client port number, to make sure that it's even (for RTP):
00616       Port clientPort(0);
00617       if (!getSourcePort(env(), fRTPSocket->socketNum(), clientPort)) {
00618         break;
00619       }
00620       fClientPortNum = ntohs(clientPort.num());
00621 
00622       // If the port number's not even, try again:
00623       if ((fClientPortNum&1) == 0) {
00624         success = True;
00625         break;
00626       }
00627       // Try again:
00628       delete oldGroupsock;
00629       oldGroupsock = fRTPSocket;
00630       fClientPortNum = 0;
00631     }
00632     delete oldGroupsock;
00633     if (!success) break;
00634 
00635     // Set our RTCP port to be the RTP port +1
00636     unsigned short const rtcpPortNum = fClientPortNum|1;
00637     if (isSSM()) {
00638       fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr,
00639                                   rtcpPortNum);
00640       // Also, send RTCP packets back to the source via unicast:
00641       if (fRTCPSocket != NULL) {
00642         fRTCPSocket->changeDestinationParameters(fSourceFilterAddr,0,~0);
00643       }
00644     } else {
00645       fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
00646     }
00647     if (fRTCPSocket == NULL) {
00648       char tmpBuf[100];
00649       sprintf(tmpBuf, "Failed to create RTCP socket (port %d)",
00650               rtcpPortNum);
00651       env().setResultMsg(tmpBuf);
00652       break;
00653     }
00654 
00655     // Check "fProtocolName"
00656     if (strcmp(fProtocolName, "UDP") == 0) {
00657       // A UDP-packetized stream (*not* a RTP stream)
00658       fReadSource = BasicUDPSource::createNew(env(), fRTPSocket);
00659       fRTPSource = NULL; // Note!
00660 
00661       if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream
00662         fReadSource = MPEG2TransportStreamFramer::createNew(env(), fReadSource);
00663             // this sets "durationInMicroseconds" correctly, based on the PCR values
00664       }
00665     } else {
00666       // Check "fCodecName" against the set of codecs that we support,
00667       // and create our RTP source accordingly
00668       // (Later make this code more efficient, as this set grows #####)
00669       // (Also, add more fmts that can be implemented by SimpleRTPSource#####)
00670       Boolean createSimpleRTPSource = False;
00671       Boolean doNormalMBitRule = False; // used if "createSimpleRTPSource"
00672       if (strcmp(fCodecName, "QCELP") == 0) { // QCELP audio
00673         fReadSource =
00674           QCELPAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
00675                                          fRTPPayloadFormat,
00676                                          fRTPTimestampFrequency);
00677         // Note that fReadSource will differ from fRTPSource in this case
00678       } else if (strcmp(fCodecName, "AMR") == 0) { // AMR audio (narrowband)
00679         fReadSource =
00680           AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
00681                                        fRTPPayloadFormat, 0 /*isWideband*/,
00682                                        fNumChannels, fOctetalign, fInterleaving,
00683                                        fRobustsorting, fCRC);
00684         // Note that fReadSource will differ from fRTPSource in this case
00685       } else if (strcmp(fCodecName, "AMR-WB") == 0) { // AMR audio (wideband)
00686         fReadSource =
00687           AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
00688                                        fRTPPayloadFormat, 1 /*isWideband*/,
00689                                        fNumChannels, fOctetalign, fInterleaving,
00690                                        fRobustsorting, fCRC);
00691         // Note that fReadSource will differ from fRTPSource in this case
00692       } else if (strcmp(fCodecName, "MPA") == 0) { // MPEG-1 or 2 audio
00693         fReadSource = fRTPSource
00694           = MPEG1or2AudioRTPSource::createNew(env(), fRTPSocket,
00695                                               fRTPPayloadFormat,
00696                                               fRTPTimestampFrequency);
00697       } else if (strcmp(fCodecName, "MPA-ROBUST") == 0) { // robust MP3 audio
00698         fRTPSource
00699           = MP3ADURTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
00700                                        fRTPTimestampFrequency);
00701         if (fRTPSource == NULL) break;
00702 
00703         // Add a filter that deinterleaves the ADUs after depacketizing them:
00704         MP3ADUdeinterleaver* deinterleaver
00705           = MP3ADUdeinterleaver::createNew(env(), fRTPSource);
00706         if (deinterleaver == NULL) break;
00707 
00708         // Add another filter that converts these ADUs to MP3 frames:
00709         fReadSource = MP3FromADUSource::createNew(env(), deinterleaver);
00710       } else if (strcmp(fCodecName, "X-MP3-DRAFT-00") == 0) {
00711         // a non-standard variant of "MPA-ROBUST" used by RealNetworks
00712         // (one 'ADU'ized MP3 frame per packet; no headers)
00713         fRTPSource
00714           = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
00715                                        fRTPTimestampFrequency,
00716                                        "audio/MPA-ROBUST" /*hack*/);
00717         if (fRTPSource == NULL) break;
00718 
00719         // Add a filter that converts these ADUs to MP3 frames:
00720         fReadSource = MP3FromADUSource::createNew(env(), fRTPSource,
00721                                                   False /*no ADU header*/);
00722       } else if (strcmp(fCodecName, "MP4A-LATM") == 0) { // MPEG-4 LATM audio
00723         fReadSource = fRTPSource
00724           = MPEG4LATMAudioRTPSource::createNew(env(), fRTPSocket,
00725                                                fRTPPayloadFormat,
00726                                                fRTPTimestampFrequency);
00727       } else if (strcmp(fCodecName, "AC3") == 0) { // AC3 audio
00728         fReadSource = fRTPSource
00729           = AC3AudioRTPSource::createNew(env(), fRTPSocket,
00730                                          fRTPPayloadFormat,
00731                                          fRTPTimestampFrequency);
00732       } else if (strcmp(fCodecName, "MP4V-ES") == 0) { // MPEG-4 Elem Str vid
00733         fReadSource = fRTPSource
00734           = MPEG4ESVideoRTPSource::createNew(env(), fRTPSocket,
00735                                              fRTPPayloadFormat,
00736                                              fRTPTimestampFrequency);
00737       } else if (strcmp(fCodecName, "MPEG4-GENERIC") == 0) {
00738         fReadSource = fRTPSource
00739           = MPEG4GenericRTPSource::createNew(env(), fRTPSocket,
00740                                              fRTPPayloadFormat,
00741                                              fRTPTimestampFrequency,
00742                                              fMediumName, fMode,
00743                                              fSizelength, fIndexlength,
00744                                              fIndexdeltalength);
00745       } else if (strcmp(fCodecName, "MPV") == 0) { // MPEG-1 or 2 video
00746         fReadSource = fRTPSource
00747           = MPEG1or2VideoRTPSource::createNew(env(), fRTPSocket,
00748                                               fRTPPayloadFormat,
00749                                               fRTPTimestampFrequency);
00750       } else if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream
00751         fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
00752                                                 fRTPTimestampFrequency, "video/MP2T",
00753                                                 0, False);
00754         fReadSource = MPEG2TransportStreamFramer::createNew(env(), fRTPSource);
00755             // this sets "durationInMicroseconds" correctly, based on the PCR values
00756       } else if (strcmp(fCodecName, "H261") == 0) { // H.261
00757         fReadSource = fRTPSource
00758           = H261VideoRTPSource::createNew(env(), fRTPSocket,
00759                                           fRTPPayloadFormat,
00760                                           fRTPTimestampFrequency);
00761       } else if (strcmp(fCodecName, "H263-1998") == 0 ||
00762                  strcmp(fCodecName, "H263-2000") == 0) { // H.263+
00763         fReadSource = fRTPSource
00764           = H263plusVideoRTPSource::createNew(env(), fRTPSocket,
00765                                               fRTPPayloadFormat,
00766                                               fRTPTimestampFrequency);
00767       } else if (strcmp(fCodecName, "H264") == 0) {
00768         fReadSource = fRTPSource
00769           = H264VideoRTPSource::createNew(env(), fRTPSocket,
00770                                           fRTPPayloadFormat,
00771                                           fRTPTimestampFrequency);
00772       } else if (strcmp(fCodecName, "JPEG") == 0) { // motion JPEG
00773         fReadSource = fRTPSource
00774           = JPEGVideoRTPSource::createNew(env(), fRTPSocket,
00775                                           fRTPPayloadFormat,
00776                                           fRTPTimestampFrequency,
00777                                           videoWidth(),
00778                                           videoHeight());
00779       } else if (strcmp(fCodecName, "X-QT") == 0
00780                  || strcmp(fCodecName, "X-QUICKTIME") == 0) {
00781         // Generic QuickTime streams, as defined in
00782         // <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
00783         char* mimeType
00784           = new char[strlen(mediumName()) + strlen(codecName()) + 2] ;
00785         sprintf(mimeType, "%s/%s", mediumName(), codecName());
00786         fReadSource = fRTPSource
00787           = QuickTimeGenericRTPSource::createNew(env(), fRTPSocket,
00788                                                  fRTPPayloadFormat,
00789                                                  fRTPTimestampFrequency,
00790                                                  mimeType);
00791         delete[] mimeType;
00792 #ifdef SUPPORT_REAL_RTSP
00793       } else if (strcmp(fCodecName, "X-PN-REALAUDIO") == 0 ||
00794                  strcmp(fCodecName, "X-PN-MULTIRATE-REALAUDIO-LIVE") == 0 ||
00795                  strcmp(fCodecName, "X-PN-REALVIDEO") == 0 ||
00796                  strcmp(fCodecName, "X-PN-MULTIRATE-REALVIDEO-LIVE") == 0) {
00797         // A RealNetworks 'RDT' stream (*not* a RTP stream)
00798         fReadSource = RealRDTSource::createNew(