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

Generated on Thu Feb 2 23:51:30 2012 for live by  doxygen 1.5.2