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), fReceiveRawMP3ADUs(False),
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 void MediaSubsession::addFilter(FramedFilter* filter){
00565   if (filter == NULL || filter->inputSource() != fReadSource) return; // sanity check
00566   fReadSource = filter;
00567 }
00568 
00569 double MediaSubsession::playStartTime() const {
00570   if (fPlayStartTime > 0) return fPlayStartTime;
00571 
00572   return fParent.playStartTime();
00573 }
00574 
00575 double MediaSubsession::playEndTime() const {
00576   if (fPlayEndTime > 0) return fPlayEndTime;
00577 
00578   return fParent.playEndTime();
00579 }
00580 
00581 Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
00582   if (fReadSource != NULL) return True; // has already been initiated
00583 
00584   do {
00585     if (fCodecName == NULL) {
00586       env().setResultMsg("Codec is unspecified");
00587       break;
00588     }
00589 
00590     // Create RTP and RTCP 'Groupsocks' on which to receive incoming data.
00591     // (Groupsocks will work even for unicast addresses)
00592     struct in_addr tempAddr;
00593     tempAddr.s_addr = connectionEndpointAddress();
00594         // This could get changed later, as a result of a RTSP "SETUP"
00595 
00596     if (fClientPortNum != 0) {
00597       // The sockets' port numbers were specified for us.  Use these:
00598       Boolean const protocolIsRTP = strcmp(fProtocolName, "RTP") == 0;
00599       if (protocolIsRTP) {
00600         fClientPortNum = fClientPortNum&~1; // use an even-numbered port for RTP, and the next (odd-numbered) port for RTCP
00601       }
00602       if (isSSM()) {
00603         fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, fClientPortNum);
00604       } else {
00605         fRTPSocket = new Groupsock(env(), tempAddr, fClientPortNum, 255);
00606       }
00607       if (fRTPSocket == NULL) {
00608         env().setResultMsg("Failed to create RTP socket");
00609         break;
00610       }
00611       
00612       if (protocolIsRTP) {
00613         // Set our RTCP port to be the RTP port +1
00614         portNumBits const rtcpPortNum = fClientPortNum|1;
00615         if (isSSM()) {
00616           fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
00617         } else {
00618           fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
00619         }
00620       }
00621     } else {
00622       // Port numbers were not specified in advance, so we use ephemeral port numbers.
00623       // Create sockets until we get a port-number pair (even: RTP; even+1: RTCP).
00624       // We need to make sure that we don't keep trying to use the same bad port numbers over and over again.
00625       // so we store bad sockets in a table, and delete them all when we're done.
00626       HashTable* socketHashTable = HashTable::create(ONE_WORD_HASH_KEYS);
00627       if (socketHashTable == NULL) break;
00628       Boolean success = False;
00629       NoReuse dummy(env()); // ensures that our new ephemeral port number won't be one that's already in use
00630 
00631       while (1) {
00632         // Create a new socket:
00633         if (isSSM()) {
00634           fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, 0);
00635         } else {
00636           fRTPSocket = new Groupsock(env(), tempAddr, 0, 255);
00637         }
00638         if (fRTPSocket == NULL) {
00639           env().setResultMsg("MediaSession::initiate(): unable to create RTP and RTCP sockets");
00640           break;
00641         }
00642 
00643         // Get the client port number, and check whether it's even (for RTP):
00644         Port clientPort(0);
00645         if (!getSourcePort(env(), fRTPSocket->socketNum(), clientPort)) {
00646           break;
00647         }
00648         fClientPortNum = ntohs(clientPort.num()); 
00649         if ((fClientPortNum&1) != 0) { // it's odd
00650           // Record this socket in our table, and keep trying:
00651           unsigned key = (unsigned)fClientPortNum;
00652           Groupsock* existing = (Groupsock*)socketHashTable->Add((char const*)key, fRTPSocket);
00653           delete existing; // in case it wasn't NULL
00654           continue;
00655         }
00656 
00657         // Make sure we can use the next (i.e., odd) port number, for RTCP:
00658         portNumBits rtcpPortNum = fClientPortNum|1;
00659         if (isSSM()) {
00660           fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
00661         } else {
00662           fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
00663         }
00664         if (fRTCPSocket != NULL && fRTCPSocket->socketNum() >= 0) {
00665           // Success! Use these two sockets.
00666           success = True;
00667           break;
00668         } else {
00669           // We couldn't create the RTCP socket (perhaps that port number's already in use elsewhere?).
00670           delete fRTCPSocket;
00671 
00672           // Record the first socket in our table, and keep trying:
00673           unsigned key = (unsigned)fClientPortNum;
00674           Groupsock* existing = (Groupsock*)socketHashTable->Add((char const*)key, fRTPSocket);
00675           delete existing; // in case it wasn't NULL
00676           continue;
00677         }
00678       }
00679 
00680       // Clean up the socket hash table (and contents):
00681       Groupsock* oldGS;
00682       while ((oldGS = (Groupsock*)socketHashTable->RemoveNext()) != NULL) {
00683         delete oldGS;
00684       }
00685       delete socketHashTable;
00686 
00687       if (!success) break; // a fatal error occurred trying to create the RTP and RTCP sockets; we can't continue
00688     }
00689 
00690     // Try to use a big receive buffer for RTP - at least 0.1 second of
00691     // specified bandwidth and at least 50 KB
00692     unsigned rtpBufSize = fBandwidth * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
00693     if (rtpBufSize < 50 * 1024)
00694       rtpBufSize = 50 * 1024;
00695     increaseReceiveBufferTo(env(), fRTPSocket->socketNum(), rtpBufSize);
00696 
00697     if (isSSM() && fRTCPSocket != NULL) {
00698       // Special case for RTCP SSM: Send RTCP packets back to the source via unicast:
00699       fRTCPSocket->changeDestinationParameters(fSourceFilterAddr,0,~0);
00700     }
00701 
00702     // Create "fRTPSource" and "fReadSource":
00703     if (!createSourceObjects(useSpecialRTPoffset)) break;
00704 
00705     if (fReadSource == NULL) {
00706       env().setResultMsg("Failed to create read source");
00707       break;
00708     }
00709 
00710     // Finally, create our RTCP instance. (It starts running automatically)
00711     if (fRTPSource != NULL && fRTCPSocket != NULL) {
00712       // If bandwidth is specified, use it and add 5% for RTCP overhead.
00713       // Otherwise make a guess at 500 kbps.
00714       unsigned totSessionBandwidth
00715         = fBandwidth ? fBandwidth + fBandwidth / 20 : 500;
00716       fRTCPInstance = RTCPInstance::createNew(env(), fRTCPSocket,
00717                                               totSessionBandwidth,
00718                                               (unsigned char const*)
00719                                               fParent.CNAME(),
00720                                               NULL /* we're a client */,
00721                                               fRTPSource);
00722       if (fRTCPInstance == NULL) {
00723         env().setResultMsg("Failed to create RTCP instance");
00724         break;
00725       }
00726     }
00727 
00728     return True;
00729   } while (0);
00730 
00731   delete fRTPSocket; fRTPSocket = NULL;
00732   delete fRTCPSocket; fRTCPSocket = NULL;
00733   Medium::close(fRTCPInstance); fRTCPInstance = NULL;
00734   Medium::close(fReadSource); fReadSource = fRTPSource = NULL;
00735   fClientPortNum = 0;
00736   return False;
00737 }
00738 
00739 void MediaSubsession::deInitiate() {
00740   Medium::close(fRTCPInstance);
00741   fRTCPInstance = NULL;
00742 
00743   Medium::close(fReadSource); // this is assumed to also close fRTPSource
00744   fReadSource = NULL; fRTPSource = NULL;
00745 
00746   delete fRTCPSocket; delete fRTPSocket;
00747   fRTCPSocket = fRTPSocket = NULL;
00748 }
00749 
00750 Boolean MediaSubsession::setClientPortNum(unsigned short portNum) {
00751   if (fReadSource != NULL) {
00752     env().setResultMsg("A read source has already been created");
00753     return False;
00754   }
00755 
00756   fClientPortNum = portNum;
00757   return True;
00758 }
00759 
00760 netAddressBits MediaSubsession::connectionEndpointAddress() const {
00761   do {
00762     // Get the endpoint name from with us, or our parent session:
00763     char const* endpointString = connectionEndpointName();
00764     if (endpointString == NULL) {
00765       endpointString = parentSession().connectionEndpointName();
00766     }
00767     if (endpointString == NULL) break;
00768 
00769     // Now, convert this name to an address, if we can:
00770     NetAddressList addresses(endpointString);
00771     if (addresses.numAddresses() == 0) break;
00772 
00773     return *(netAddressBits*)(addresses.firstAddress()->data());
00774   } while (0);
00775 
00776   // No address known:
00777   return 0;
00778 }
00779 
00780 void MediaSubsession::setDestinations(netAddressBits defaultDestAddress) {
00781   // Get the destination address from the connection endpoint name
00782   // (This will be 0 if it's not known, in which case we use the default)
00783   netAddressBits destAddress = connectionEndpointAddress();
00784   if (destAddress == 0) destAddress = defaultDestAddress;
00785   struct in_addr destAddr; destAddr.s_addr = destAddress;
00786 
00787   // The destination TTL remains unchanged:
00788   int destTTL = ~0; // means: don't change
00789 
00790   if (fRTPSocket != NULL) {
00791     Port destPort(serverPortNum);
00792     fRTPSocket->changeDestinationParameters(destAddr, destPort, destTTL);
00793   }
00794   if (fRTCPSocket != NULL && !isSSM()) {
00795     // Note: For SSM sessions, the dest address for RTCP was already set.
00796     Port destPort(serverPortNum+1);
00797     fRTCPSocket->changeDestinationParameters(destAddr, destPort, destTTL);
00798   }
00799 }
00800 
00801 void MediaSubsession::setSessionId(char const* sessionId) {
00802   delete[] fSessionId;
00803   fSessionId = strDup(sessionId);
00804 }
00805 
00806 double MediaSubsession::getNormalPlayTime(struct timeval const& presentationTime) {
00807   if (rtpSource() == NULL || rtpSource()->timestampFrequency() == 0) return 0.0; // no RTP source, or bad freq!
00808 
00809   // First, check whether our "RTPSource" object has already been synchronized using RTCP.
00810   // If it hasn't, then - as a special case - we need to use the RTP timestamp to compute the NPT.
00811   if (!rtpSource()->hasBeenSynchronizedUsingRTCP()) {
00812     if (!rtpInfo.infoIsNew) return 0.0; // the "rtpInfo" structure has not been filled in
00813     u_int32_t timestampOffset = rtpSource()->curPacketRTPTimestamp() - rtpInfo.timestamp;
00814     double nptOffset = (timestampOffset/(double)(rtpSource()->timestampFrequency()))*scale();
00815     double npt = playStartTime() + nptOffset;
00816 
00817     return npt;
00818   } else {
00819     // Common case: We have been synchronized using RTCP.  This means that the "presentationTime" parameter
00820     // will be accurate, and so we should use this to compute the NPT.
00821     double ptsDouble = (double)(presentationTime.tv_sec + presentationTime.tv_usec/1000000.0);
00822 
00823     if (rtpInfo.infoIsNew) {
00824       // This is the first time we've been called with a synchronized presentation time since the "rtpInfo"
00825       // structure was last filled in.  Use this "presentationTime" to compute "fNPT_PTS_Offset":
00826       if (seqNumLT(rtpSource()->curPacketRTPSeqNum(), rtpInfo.seqNum)) return -0.1; // sanity check; ignore old packets
00827       u_int32_t timestampOffset = rtpSource()->curPacketRTPTimestamp() - rtpInfo.timestamp;
00828       double nptOffset = (timestampOffset/(double)(rtpSource()->timestampFrequency()))*scale();
00829       double npt = playStartTime() + nptOffset;
00830       fNPT_PTS_Offset = npt - ptsDouble*scale();
00831       rtpInfo.infoIsNew = False; // for next time
00832 
00833       return npt;
00834     } else {
00835       // Use the precomputed "fNPT_PTS_Offset" to compute the NPT from the PTS:
00836       if (fNPT_PTS_Offset == 0.0) return 0.0; // error: The "rtpInfo" structure was apparently never filled in
00837       return (double)(ptsDouble*scale() + fNPT_PTS_Offset);
00838     }
00839   }
00840 }
00841 
00842 Boolean MediaSubsession::parseSDPLine_c(char const* sdpLine) {
00843   // Check for "c=IN IP4 <connection-endpoint>"
00844   // or "c=IN IP4 <connection-endpoint>/<ttl+numAddresses>"
00845   // (Later, do something with <ttl+numAddresses> also #####)
00846   char* connectionEndpointName = parseCLine(sdpLine);
00847   if (connectionEndpointName != NULL) {
00848     delete[] fConnectionEndpointName;
00849     fConnectionEndpointName = connectionEndpointName;
00850     return True;
00851   }
00852 
00853   return False;
00854 }
00855 
00856 Boolean MediaSubsession::parseSDPLine_b(char const* sdpLine) {
00857   // Check for "b=<bwtype>:<bandwidth>" line
00858   // RTP applications are expected to use bwtype="AS"
00859   return sscanf(sdpLine, "b=AS:%u", &fBandwidth) == 1;
00860 }
00861 
00862 Boolean MediaSubsession::parseSDPAttribute_rtpmap(char const* sdpLine) {
00863   // Check for a "a=rtpmap:<fmt> <codec>/<freq>" line:
00864   // (Also check without the "/<freq>"; RealNetworks omits this)
00865   // Also check for a trailing "/<numChannels>".
00866   Boolean parseSuccess = False;
00867 
00868   unsigned rtpmapPayloadFormat;
00869   char* codecName = strDupSize(sdpLine); // ensures we have enough space
00870   unsigned rtpTimestampFrequency = 0;
00871   unsigned numChannels = 1;
00872   if (sscanf(sdpLine, "a=rtpmap: %u %[^/]/%u/%u",
00873              &rtpmapPayloadFormat, codecName, &rtpTimestampFrequency,
00874              &numChannels) == 4
00875       || sscanf(sdpLine, "a=rtpmap: %u %[^/]/%u",
00876              &rtpmapPayloadFormat, codecName, &rtpTimestampFrequency) == 3
00877       || sscanf(sdpLine, "a=rtpmap: %u %s",
00878                 &rtpmapPayloadFormat, codecName) == 2) {
00879     parseSuccess = True;
00880     if (rtpmapPayloadFormat == fRTPPayloadFormat) {
00881       // This "rtpmap" matches our payload format, so set our
00882       // codec name and timestamp frequency:
00883       // (First, make sure the codec name is upper case)
00884       {
00885         Locale l("POSIX");
00886         for (char* p = codecName; *p != '\0'; ++p) *p = toupper(*p);
00887       }
00888       delete[] fCodecName; fCodecName = strDup(codecName);
00889       fRTPTimestampFrequency = rtpTimestampFrequency;
00890       fNumChannels = numChannels;
00891     }
00892   }
00893   delete[] codecName;
00894 
00895   return parseSuccess;
00896 }
00897 
00898 Boolean MediaSubsession::parseSDPAttribute_control(char const* sdpLine) {
00899   // Check for a "a=control:<control-path>" line:
00900   Boolean parseSuccess = False;
00901 
00902   char* controlPath = strDupSize(sdpLine); // ensures we have enough space
00903   if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) {
00904     parseSuccess = True;
00905     delete[] fControlPath; fControlPath = strDup(controlPath);
00906   }
00907   delete[] controlPath;
00908 
00909   return parseSuccess;
00910 }
00911 
00912 Boolean MediaSubsession::parseSDPAttribute_range(char const* sdpLine) {
00913   // Check for a "a=range:npt=<startTime>-<endTime>" line:
00914   // (Later handle other kinds of "a=range" attributes also???#####)
00915   Boolean parseSuccess = False;
00916 
00917   double playStartTime;
00918   double playEndTime;
00919   if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) {
00920     parseSuccess = True;
00921     if (playStartTime > fPlayStartTime) {
00922       fPlayStartTime = playStartTime;
00923       if (playStartTime > fParent.playStartTime()) {
00924         fParent.playStartTime() = playStartTime;
00925       }
00926     }
00927     if (playEndTime > fPlayEndTime) {
00928       fPlayEndTime = playEndTime;
00929       if (playEndTime > fParent.playEndTime()) {
00930         fParent.playEndTime() = playEndTime;
00931       }
00932     }
00933   }
00934 
00935   return parseSuccess;
00936 }
00937 
00938 Boolean MediaSubsession::parseSDPAttribute_fmtp(char const* sdpLine) {
00939   // Check for a "a=fmtp:" line:
00940   // TEMP: We check only for a handful of expected parameter names #####
00941   // Later: (i) check that payload format number matches; #####
00942   //        (ii) look for other parameters also (generalize?) #####
00943   do {
00944     if (strncmp(sdpLine, "a=fmtp:", 7) != 0) break; sdpLine += 7;
00945     while (isdigit(*sdpLine)) ++sdpLine;
00946 
00947     // The remaining "sdpLine" should be a sequence of
00948     //     <name>=<value>;
00949     // parameter assignments.  Look at each of these.
00950     // First, convert the line to lower-case, to ease comparison:
00951     char* const lineCopy = strDup(sdpLine); char* line = lineCopy;
00952     {
00953       Locale l("POSIX");
00954       for (char* c = line; *c != '\0'; ++c) *c = tolower(*c);
00955     }
00956     while (*line != '\0' && *line != '\r' && *line != '\n') {
00957       unsigned u;
00958       char* valueStr = strDupSize(line);
00959       if (sscanf(line, " auxiliarydatasizelength = %u", &u) == 1) {
00960         fAuxiliarydatasizelength = u;
00961       } else if (sscanf(line, " constantduration = %u", &u) == 1) {
00962         fConstantduration = u;
00963       } else if (sscanf(line, " constantsize; = %u", &u) == 1) {
00964         fConstantsize = u;
00965       } else if (sscanf(line, " crc = %u", &u) == 1) {
00966         fCRC = u;
00967       } else if (sscanf(line, " ctsdeltalength = %u", &u) == 1) {
00968         fCtsdeltalength = u;
00969       } else if (sscanf(line, " de-interleavebuffersize = %u", &u) == 1) {
00970         fDe_interleavebuffersize = u;
00971       } else if (sscanf(line, " dtsdeltalength = %u", &u) == 1) {
00972         fDtsdeltalength = u;
00973       } else if (sscanf(line, " indexdeltalength = %u", &u) == 1) {
00974         fIndexdeltalength = u;
00975       } else if (sscanf(line, " indexlength = %u", &u) == 1) {
00976         fIndexlength = u;
00977       } else if (sscanf(line, " interleaving = %u", &u) == 1) {
00978         fInterleaving = u;
00979       } else if (sscanf(line, " maxdisplacement = %u", &u) == 1) {
00980         fMaxdisplacement = u;
00981       } else if (sscanf(line, " objecttype = %u", &u) == 1) {
00982         fObjecttype = u;
00983       } else if (sscanf(line, " octet-align = %u", &u) == 1) {
00984         fOctetalign = u;
00985       } else if (sscanf(line, " profile-level-id = %x", &u) == 1) {
00986         // Note that the "profile-level-id" parameter is assumed to be hexadecimal
00987         fProfile_level_id = u;
00988       } else if (sscanf(line, " robust-sorting = %u", &u) == 1) {
00989         fRobustsorting = u;
00990       } else if (sscanf(line, " sizelength = %u", &u) == 1) {
00991         fSizelength = u;
00992       } else if (sscanf(line, " streamstateindication = %u", &u) == 1) {
00993         fStreamstateindication = u;
00994       } else if (sscanf(line, " streamtype = %u", &u) == 1) {
00995         fStreamtype = u;
00996       } else if (sscanf(line, " cpresent = %u", &u) == 1) {
00997         fCpresent = u != 0;
00998       } else if (sscanf(line, " randomaccessindication = %u", &u) == 1) {
00999         fRandomaccessindication = u != 0;
01000       } else if (sscanf(sdpLine, " config = %[^; \t\r\n]", valueStr) == 1 ||
01001                  sscanf(sdpLine, " configuration = %[^; \t\r\n]", valueStr) == 1) {
01002         // Note: We used "sdpLine" here, because the value may be case-sensitive (if it's Base-64).
01003         delete[] fConfig; fConfig = strDup(valueStr);
01004       } else if (sscanf(line, " mode = %[^; \t\r\n]", valueStr) == 1) {
01005         delete[] fMode; fMode = strDup(valueStr);
01006       } else if (sscanf(sdpLine, " sprop-parameter-sets = %[^; \t\r\n]", valueStr) == 1) {
01007         // Note: We used "sdpLine" here, because the value is case-sensitive.
01008         delete[] fSpropParameterSets; fSpropParameterSets = strDup(valueStr);
01009       } else if (sscanf(line, " emphasis = %[^; \t\r\n]", valueStr) == 1) {
01010         delete[] fEmphasis; fEmphasis = strDup(valueStr);
01011       } else if (sscanf(sdpLine, " channel-order = %[^; \t\r\n]", valueStr) == 1) {
01012         // Note: We used "sdpLine" here, because the value is case-sensitive.
01013         delete[] fChannelOrder; fChannelOrder = strDup(valueStr);
01014       } else {
01015         // Some of the above parameters are Boolean.  Check whether the parameter
01016         // names appear alone, without a "= 1" at the end:
01017         if (sscanf(line, " %[^; \t\r\n]", valueStr) == 1) {
01018           if (strcmp(valueStr, "octet-align") == 0) {
01019             fOctetalign = 1;
01020           } else if (strcmp(valueStr, "cpresent") == 0) {
01021             fCpresent = True;
01022           } else if (strcmp(valueStr, "crc") == 0) {
01023             fCRC = 1;
01024           } else if (strcmp(valueStr, "robust-sorting") == 0) {
01025             fRobustsorting = 1;
01026           } else if (strcmp(valueStr, "randomaccessindication") == 0) {
01027             fRandomaccessindication = True;
01028           }
01029         }
01030       }
01031       delete[] valueStr;
01032 
01033       // Move to the next parameter assignment string:
01034       while (*line != '\0' && *line != '\r' && *line != '\n'
01035              && *line != ';') ++line;
01036       while (*line == ';') ++line;
01037 
01038       // Do the same with sdpLine; needed for finding case sensitive values:
01039       while (*sdpLine != '\0' && *sdpLine != '\r' && *sdpLine != '\n'
01040              && *sdpLine != ';') ++sdpLine;
01041       while (*sdpLine == ';') ++sdpLine;
01042     }
01043     delete[] lineCopy;
01044     return True;
01045   } while (0);
01046 
01047   return False;
01048 }
01049 
01050 Boolean MediaSubsession
01051 ::parseSDPAttribute_source_filter(char const* sdpLine) {
01052   return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr);
01053 }
01054 
01055 Boolean MediaSubsession::parseSDPAttribute_x_dimensions(char const* sdpLine) {
01056   // Check for a "a=x-dimensions:<width>,<height>" line:
01057   Boolean parseSuccess = False;
01058 
01059   int width, height;
01060   if (sscanf(sdpLine, "a=x-dimensions:%d,%d", &width, &height) == 2) {
01061     parseSuccess = True;
01062     fVideoWidth = (unsigned short)width;
01063     fVideoHeight = (unsigned short)height;
01064   }
01065 
01066   return parseSuccess;
01067 }
01068 
01069 Boolean MediaSubsession::parseSDPAttribute_framerate(char const* sdpLine) {
01070   // Check for a "a=framerate: <fps>" or "a=x-framerate: <fps>" line:
01071   Boolean parseSuccess = False;
01072 
01073   float frate;
01074   int rate;
01075   if (sscanf(sdpLine, "a=framerate: %f", &frate) == 1 || sscanf(sdpLine, "a=framerate:%f", &frate) == 1) {
01076     parseSuccess = True;
01077     fVideoFPS = (unsigned)frate;
01078   } else if (sscanf(sdpLine, "a=x-framerate: %d", &rate) == 1) {
01079     parseSuccess = True;
01080     fVideoFPS = (unsigned)rate;
01081   }
01082 
01083   return parseSuccess;
01084 }
01085 
01086 Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
01087   do {
01088     // First, check "fProtocolName"
01089     if (strcmp(fProtocolName, "UDP") == 0) {
01090       // A UDP-packetized stream (*not* a RTP stream)
01091       fReadSource = BasicUDPSource::createNew(env(), fRTPSocket);
01092       fRTPSource = NULL; // Note!
01093       
01094       if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream
01095         fReadSource = MPEG2TransportStreamFramer::createNew(env(), fReadSource);
01096         // this sets "durationInMicroseconds" correctly, based on the PCR values
01097       }
01098     } else {
01099       // Check "fCodecName" against the set of codecs that we support,
01100       // and create our RTP source accordingly
01101       // (Later make this code more efficient, as this set grows #####)
01102       // (Also, add more fmts that can be implemented by SimpleRTPSource#####)
01103       Boolean createSimpleRTPSource = False; // by default; can be changed below
01104       Boolean doNormalMBitRule = False; // default behavior if "createSimpleRTPSource" is True
01105       if (strcmp(fCodecName, "QCELP") == 0) { // QCELP audio
01106         fReadSource =
01107           QCELPAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
01108                                          fRTPPayloadFormat,
01109                                          fRTPTimestampFrequency);
01110         // Note that fReadSource will differ from fRTPSource in this case
01111       } else if (strcmp(fCodecName, "AMR") == 0) { // AMR audio (narrowband)
01112         fReadSource =
01113           AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
01114                                        fRTPPayloadFormat, 0 /*isWideband*/,
01115                                        fNumChannels, fOctetalign, fInterleaving,
01116                                        fRobustsorting, fCRC);
01117         // Note that fReadSource will differ from fRTPSource in this case
01118       } else if (strcmp(fCodecName, "AMR-WB") == 0) { // AMR audio (wideband)
01119         fReadSource =
01120           AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
01121                                        fRTPPayloadFormat, 1 /*isWideband*/,
01122                                        fNumChannels, fOctetalign, fInterleaving,
01123                                        fRobustsorting, fCRC);
01124         // Note that fReadSource will differ from fRTPSource in this case
01125       } else if (strcmp(fCodecName, "MPA") == 0) { // MPEG-1 or 2 audio
01126         fReadSource = fRTPSource
01127           = MPEG1or2AudioRTPSource::createNew(env(), fRTPSocket,
01128                                               fRTPPayloadFormat,
01129                                               fRTPTimestampFrequency);
01130       } else if (strcmp(fCodecName, "MPA-ROBUST") == 0) { // robust MP3 audio
01131         fReadSource = fRTPSource
01132           = MP3ADURTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01133                                        fRTPTimestampFrequency);
01134         if (fRTPSource == NULL) break;
01135         
01136         if (!fReceiveRawMP3ADUs) {
01137           // Add a filter that deinterleaves the ADUs after depacketizing them:
01138           MP3ADUdeinterleaver* deinterleaver
01139             = MP3ADUdeinterleaver::createNew(env(), fRTPSource);
01140           if (deinterleaver == NULL) break;
01141         
01142           // Add another filter that converts these ADUs to MP3 frames:
01143           fReadSource = MP3FromADUSource::createNew(env(), deinterleaver);
01144         }
01145       } else if (strcmp(fCodecName, "X-MP3-DRAFT-00") == 0) {
01146         // a non-standard variant of "MPA-ROBUST" used by RealNetworks
01147         // (one 'ADU'ized MP3 frame per packet; no headers)
01148         fRTPSource
01149           = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01150                                        fRTPTimestampFrequency,
01151                                        "audio/MPA-ROBUST" /*hack*/);
01152         if (fRTPSource == NULL) break;
01153         
01154         // Add a filter that converts these ADUs to MP3 frames:
01155         fReadSource = MP3FromADUSource::createNew(env(), fRTPSource,
01156                                                   False /*no ADU header*/);
01157       } else if (strcmp(fCodecName, "MP4A-LATM") == 0) { // MPEG-4 LATM audio
01158         fReadSource = fRTPSource
01159           = MPEG4LATMAudioRTPSource::createNew(env(), fRTPSocket,
01160                                                fRTPPayloadFormat,
01161                                                fRTPTimestampFrequency);
01162       } else if (strcmp(fCodecName, "VORBIS") == 0) { // Vorbis audio
01163         fReadSource = fRTPSource
01164           = VorbisAudioRTPSource::createNew(env(), fRTPSocket,
01165                                             fRTPPayloadFormat,
01166                                             fRTPTimestampFrequency);
01167       } else if (strcmp(fCodecName, "VP8") == 0) { // VP8 video
01168         fReadSource = fRTPSource
01169           = VP8VideoRTPSource::createNew(env(), fRTPSocket,
01170                                          fRTPPayloadFormat,
01171                                          fRTPTimestampFrequency);
01172       } else if (strcmp(fCodecName, "AC3") == 0 || strcmp(fCodecName, "EAC3") == 0) { // AC3 audio
01173         fReadSource = fRTPSource
01174           = AC3AudioRTPSource::createNew(env(), fRTPSocket,
01175                                          fRTPPayloadFormat,
01176                                          fRTPTimestampFrequency);
01177       } else if (strcmp(fCodecName, "MP4V-ES") == 0) { // MPEG-4 Elementary Stream video
01178         fReadSource = fRTPSource
01179           = MPEG4ESVideoRTPSource::createNew(env(), fRTPSocket,
01180                                              fRTPPayloadFormat,
01181                                              fRTPTimestampFrequency);
01182       } else if (strcmp(fCodecName, "MPEG4-GENERIC") == 0) {
01183         fReadSource = fRTPSource
01184           = MPEG4GenericRTPSource::createNew(env(), fRTPSocket,
01185                                              fRTPPayloadFormat,
01186                                              fRTPTimestampFrequency,
01187                                              fMediumName, fMode,
01188                                              fSizelength, fIndexlength,
01189                                              fIndexdeltalength);
01190       } else if (strcmp(fCodecName, "MPV") == 0) { // MPEG-1 or 2 video
01191         fReadSource = fRTPSource
01192           = MPEG1or2VideoRTPSource::createNew(env(), fRTPSocket,
01193                                               fRTPPayloadFormat,
01194                                               fRTPTimestampFrequency);
01195       } else if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream
01196         fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01197                                                 fRTPTimestampFrequency, "video/MP2T",
01198                                                 0, False);
01199         fReadSource = MPEG2TransportStreamFramer::createNew(env(), fRTPSource);
01200         // this sets "durationInMicroseconds" correctly, based on the PCR values
01201       } else if (strcmp(fCodecName, "H261") == 0) { // H.261
01202         fReadSource = fRTPSource
01203           = H261VideoRTPSource::createNew(env(), fRTPSocket,
01204                                           fRTPPayloadFormat,
01205                                           fRTPTimestampFrequency);
01206       } else if (strcmp(fCodecName, "H263-1998") == 0 ||
01207                  strcmp(fCodecName, "H263-2000") == 0) { // H.263+
01208         fReadSource = fRTPSource
01209           = H263plusVideoRTPSource::createNew(env(), fRTPSocket,
01210                                               fRTPPayloadFormat,
01211                                               fRTPTimestampFrequency);
01212       } else if (strcmp(fCodecName, "H264") == 0) {
01213         fReadSource = fRTPSource
01214           = H264VideoRTPSource::createNew(env(), fRTPSocket,
01215                                           fRTPPayloadFormat,
01216                                           fRTPTimestampFrequency);
01217       } else if (strcmp(fCodecName, "DV") == 0) {
01218         fReadSource = fRTPSource
01219           = DVVideoRTPSource::createNew(env(), fRTPSocket,
01220                                         fRTPPayloadFormat,
01221                                         fRTPTimestampFrequency);
01222       } else if (strcmp(fCodecName, "JPEG") == 0) { // motion JPEG
01223         fReadSource = fRTPSource
01224           = JPEGVideoRTPSource::createNew(env(), fRTPSocket,
01225                                           fRTPPayloadFormat,
01226                                           fRTPTimestampFrequency,
01227                                           videoWidth(),
01228                                           videoHeight());
01229       } else if (strcmp(fCodecName, "X-QT") == 0
01230                  || strcmp(fCodecName, "X-QUICKTIME") == 0) {
01231         // Generic QuickTime streams, as defined in
01232         // <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
01233         char* mimeType
01234           = new char[strlen(mediumName()) + strlen(codecName()) + 2] ;
01235         sprintf(mimeType, "%s/%s", mediumName(), codecName());
01236         fReadSource = fRTPSource
01237           = QuickTimeGenericRTPSource::createNew(env(), fRTPSocket,
01238                                                  fRTPPayloadFormat,
01239                                                  fRTPTimestampFrequency,
01240                                                  mimeType);
01241         delete[] mimeType;
01242       } else if (  strcmp(fCodecName, "PCMU") == 0 // PCM u-law audio
01243                    || strcmp(fCodecName, "GSM") == 0 // GSM audio
01244                    || strcmp(fCodecName, "DVI4") == 0 // DVI4 (IMA ADPCM) audio
01245                    || strcmp(fCodecName, "PCMA") == 0 // PCM a-law audio
01246                    || strcmp(fCodecName, "MP1S") == 0 // MPEG-1 System Stream
01247                    || strcmp(fCodecName, "MP2P") == 0 // MPEG-2 Program Stream
01248                    || strcmp(fCodecName, "L8") == 0 // 8-bit linear audio
01249                    || strcmp(fCodecName, "L16") == 0 // 16-bit linear audio
01250                    || strcmp(fCodecName, "L20") == 0 // 20-bit linear audio (RFC 3190)
01251                    || strcmp(fCodecName, "L24") == 0 // 24-bit linear audio (RFC 3190)
01252                    || strcmp(fCodecName, "G726-16") == 0 // G.726, 16 kbps
01253                    || strcmp(fCodecName, "G726-24") == 0 // G.726, 24 kbps
01254                    || strcmp(fCodecName, "G726-32") == 0 // G.726, 32 kbps
01255                    || strcmp(fCodecName, "G726-40") == 0 // G.726, 40 kbps
01256                    || strcmp(fCodecName, "SPEEX") == 0 // SPEEX audio
01257                    || strcmp(fCodecName, "T140") == 0 // T.140 text (RFC 4103)
01258                    || strcmp(fCodecName, "DAT12") == 0 // 12-bit nonlinear audio (RFC 3190)
01259                    ) {
01260         createSimpleRTPSource = True;
01261         useSpecialRTPoffset = 0;
01262       } else if (useSpecialRTPoffset >= 0) {
01263         // We don't know this RTP payload format, but try to receive
01264         // it using a 'SimpleRTPSource' with the specified header offset:
01265         createSimpleRTPSource = True;
01266       } else {
01267         env().setResultMsg("RTP payload format unknown or not supported");
01268         break;
01269       }
01270       
01271       if (createSimpleRTPSource) {
01272         char* mimeType
01273           = new char[strlen(mediumName()) + strlen(codecName()) + 2] ;
01274         sprintf(mimeType, "%s/%s", mediumName(), codecName());
01275         fReadSource = fRTPSource
01276           = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat,
01277                                        fRTPTimestampFrequency, mimeType,
01278                                        (unsigned)useSpecialRTPoffset,
01279                                        doNormalMBitRule);
01280         delete[] mimeType;
01281       }
01282     }
01283 
01284     return True;
01285   } while (0);
01286 
01287   return False; // an error occurred
01288 }

Generated on Thu May 17 07:11:46 2012 for live by  doxygen 1.5.2