liveMedia/ServerMediaSession.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2013 Live Networks, Inc.  All rights reserved.
00018 // A data structure that represents a session that consists of
00019 // potentially multiple (audio and/or video) sub-sessions
00020 // (This data structure is used for media *streamers* - i.e., servers.
00021 //  For media receivers, use "MediaSession" instead.)
00022 // Implementation
00023 
00024 #include "ServerMediaSession.hh"
00025 #include <GroupsockHelper.hh>
00026 #include <math.h>
00027 
00029 
00030 ServerMediaSession* ServerMediaSession
00031 ::createNew(UsageEnvironment& env,
00032             char const* streamName, char const* info,
00033             char const* description, Boolean isSSM, char const* miscSDPLines) {
00034   return new ServerMediaSession(env, streamName, info, description,
00035                                 isSSM, miscSDPLines);
00036 }
00037 
00038 Boolean ServerMediaSession
00039 ::lookupByName(UsageEnvironment& env, char const* mediumName,
00040                ServerMediaSession*& resultSession) {
00041   resultSession = NULL; // unless we succeed
00042 
00043   Medium* medium;
00044   if (!Medium::lookupByName(env, mediumName, medium)) return False;
00045 
00046   if (!medium->isServerMediaSession()) {
00047     env.setResultMsg(mediumName, " is not a 'ServerMediaSession' object");
00048     return False;
00049   }
00050 
00051   resultSession = (ServerMediaSession*)medium;
00052   return True;
00053 }
00054 
00055 static char const* const libNameStr = "LIVE555 Streaming Media v";
00056 char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
00057 
00058 ServerMediaSession::ServerMediaSession(UsageEnvironment& env,
00059                                        char const* streamName,
00060                                        char const* info,
00061                                        char const* description,
00062                                        Boolean isSSM, char const* miscSDPLines)
00063   : Medium(env), fIsSSM(isSSM), fSubsessionsHead(NULL),
00064     fSubsessionsTail(NULL), fSubsessionCounter(0),
00065     fReferenceCount(0), fDeleteWhenUnreferenced(False) {
00066   fStreamName = strDup(streamName == NULL ? "" : streamName);
00067 
00068   char* libNamePlusVersionStr = NULL; // by default
00069   if (info == NULL || description == NULL) {
00070     libNamePlusVersionStr = new char[strlen(libNameStr) + strlen(libVersionStr) + 1];
00071     sprintf(libNamePlusVersionStr, "%s%s", libNameStr, libVersionStr);
00072   }
00073   fInfoSDPString = strDup(info == NULL ? libNamePlusVersionStr : info);
00074   fDescriptionSDPString = strDup(description == NULL ? libNamePlusVersionStr : description);
00075   delete[] libNamePlusVersionStr;
00076 
00077   fMiscSDPLines = strDup(miscSDPLines == NULL ? "" : miscSDPLines);
00078 
00079   gettimeofday(&fCreationTime, NULL);
00080 }
00081 
00082 ServerMediaSession::~ServerMediaSession() {
00083   deleteAllSubsessions();
00084   delete[] fStreamName;
00085   delete[] fInfoSDPString;
00086   delete[] fDescriptionSDPString;
00087   delete[] fMiscSDPLines;
00088 }
00089 
00090 Boolean
00091 ServerMediaSession::addSubsession(ServerMediaSubsession* subsession) {
00092   if (subsession->fParentSession != NULL) return False; // it's already used
00093 
00094   if (fSubsessionsTail == NULL) {
00095     fSubsessionsHead = subsession;
00096   } else {
00097     fSubsessionsTail->fNext = subsession;
00098   }
00099   fSubsessionsTail = subsession;
00100 
00101   subsession->fParentSession = this;
00102   subsession->fTrackNumber = ++fSubsessionCounter;
00103   return True;
00104 }
00105 
00106 void ServerMediaSession::testScaleFactor(float& scale) {
00107   // First, try setting all subsessions to the desired scale.
00108   // If the subsessions' actual scales differ from each other, choose the
00109   // value that's closest to 1, and then try re-setting all subsessions to that
00110   // value.  If the subsessions' actual scales still differ, re-set them all to 1.
00111   float minSSScale = 1.0;
00112   float maxSSScale = 1.0;
00113   float bestSSScale = 1.0;
00114   float bestDistanceTo1 = 0.0;
00115   ServerMediaSubsession* subsession;
00116   for (subsession = fSubsessionsHead; subsession != NULL;
00117        subsession = subsession->fNext) {
00118     float ssscale = scale;
00119     subsession->testScaleFactor(ssscale);
00120     if (subsession == fSubsessionsHead) { // this is the first subsession
00121       minSSScale = maxSSScale = bestSSScale = ssscale;
00122       bestDistanceTo1 = (float)fabs(ssscale - 1.0f);
00123     } else {
00124       if (ssscale < minSSScale) {
00125         minSSScale = ssscale;
00126       } else if (ssscale > maxSSScale) {
00127         maxSSScale = ssscale;
00128       }
00129 
00130       float distanceTo1 = (float)fabs(ssscale - 1.0f);
00131       if (distanceTo1 < bestDistanceTo1) {
00132         bestSSScale = ssscale;
00133         bestDistanceTo1 = distanceTo1;
00134       }
00135     }
00136   }
00137   if (minSSScale == maxSSScale) {
00138     // All subsessions are at the same scale: minSSScale == bestSSScale == maxSSScale
00139     scale = minSSScale;
00140     return;
00141   }
00142 
00143   // The scales for each subsession differ.  Try to set each one to the value
00144   // that's closest to 1:
00145   for (subsession = fSubsessionsHead; subsession != NULL;
00146        subsession = subsession->fNext) {
00147     float ssscale = bestSSScale;
00148     subsession->testScaleFactor(ssscale);
00149     if (ssscale != bestSSScale) break; // no luck
00150   }
00151   if (subsession == NULL) {
00152     // All subsessions are at the same scale: bestSSScale
00153     scale = bestSSScale;
00154     return;
00155   }
00156 
00157   // Still no luck.  Set each subsession's scale to 1:
00158   for (subsession = fSubsessionsHead; subsession != NULL;
00159        subsession = subsession->fNext) {
00160     float ssscale = 1;
00161     subsession->testScaleFactor(ssscale);
00162   }
00163   scale = 1;
00164 }
00165 
00166 float ServerMediaSession::duration() const {
00167   float minSubsessionDuration = 0.0;
00168   float maxSubsessionDuration = 0.0;
00169   for (ServerMediaSubsession* subsession = fSubsessionsHead; subsession != NULL;
00170        subsession = subsession->fNext) {
00171     // Hack: If any subsession supports seeking by 'absolute' time, then return a negative value, to indicate that only subsessions
00172     // will have a "a=range:" attribute:
00173     char* absStartTime = NULL; char* absEndTime = NULL;
00174     subsession->getAbsoluteTimeRange(absStartTime, absEndTime);
00175     if (absStartTime != NULL) return -1.0f;
00176 
00177     float ssduration = subsession->duration();
00178     if (subsession == fSubsessionsHead) { // this is the first subsession
00179       minSubsessionDuration = maxSubsessionDuration = ssduration;
00180     } else if (ssduration < minSubsessionDuration) {
00181         minSubsessionDuration = ssduration;
00182     } else if (ssduration > maxSubsessionDuration) {
00183         maxSubsessionDuration = ssduration;
00184     }
00185   }
00186 
00187   if (maxSubsessionDuration != minSubsessionDuration) {
00188     return -maxSubsessionDuration; // because subsession durations differ
00189   } else {
00190     return maxSubsessionDuration; // all subsession durations are the same
00191   }
00192 }
00193 
00194 void ServerMediaSession::deleteAllSubsessions() {
00195   Medium::close(fSubsessionsHead);
00196   fSubsessionsHead = fSubsessionsTail = NULL;
00197   fSubsessionCounter = 0;
00198 }
00199 
00200 Boolean ServerMediaSession::isServerMediaSession() const {
00201   return True;
00202 }
00203 
00204 char* ServerMediaSession::generateSDPDescription() {
00205   AddressString ipAddressStr(ourIPAddress(envir()));
00206   unsigned ipAddressStrSize = strlen(ipAddressStr.val());
00207 
00208   // For a SSM sessions, we need a "a=source-filter: incl ..." line also:
00209   char* sourceFilterLine;
00210   if (fIsSSM) {
00211     char const* const sourceFilterFmt =
00212       "a=source-filter: incl IN IP4 * %s\r\n"
00213       "a=rtcp-unicast: reflection\r\n";
00214     unsigned const sourceFilterFmtSize = strlen(sourceFilterFmt) + ipAddressStrSize + 1;
00215 
00216     sourceFilterLine = new char[sourceFilterFmtSize];
00217     sprintf(sourceFilterLine, sourceFilterFmt, ipAddressStr.val());
00218   } else {
00219     sourceFilterLine = strDup("");
00220   }
00221 
00222   char* rangeLine = NULL; // for now
00223   char* sdp = NULL; // for now
00224 
00225   do {
00226     // Count the lengths of each subsession's media-level SDP lines.
00227     // (We do this first, because the call to "subsession->sdpLines()"
00228     // causes correct subsession 'duration()'s to be calculated later.)
00229     unsigned sdpLength = 0;
00230     ServerMediaSubsession* subsession;
00231     for (subsession = fSubsessionsHead; subsession != NULL;
00232          subsession = subsession->fNext) {
00233       char const* sdpLines = subsession->sdpLines();
00234       if (sdpLines == NULL) continue; // the media's not available
00235       sdpLength += strlen(sdpLines);
00236     }
00237     if (sdpLength == 0) break; // the session has no usable subsessions
00238 
00239     // Unless subsessions have differing durations, we also have a "a=range:" line:
00240     float dur = duration();
00241     if (dur == 0.0) {
00242       rangeLine = strDup("a=range:npt=0-\r\n");
00243     } else if (dur > 0.0) {
00244       char buf[100];
00245       sprintf(buf, "a=range:npt=0-%.3f\r\n", dur);
00246       rangeLine = strDup(buf);
00247     } else { // subsessions have differing durations, so "a=range:" lines go there
00248       rangeLine = strDup("");
00249     }
00250 
00251     char const* const sdpPrefixFmt =
00252       "v=0\r\n"
00253       "o=- %ld%06ld %d IN IP4 %s\r\n"
00254       "s=%s\r\n"
00255       "i=%s\r\n"
00256       "t=0 0\r\n"
00257       "a=tool:%s%s\r\n"
00258       "a=type:broadcast\r\n"
00259       "a=control:*\r\n"
00260       "%s"
00261       "%s"
00262       "a=x-qt-text-nam:%s\r\n"
00263       "a=x-qt-text-inf:%s\r\n"
00264       "%s";
00265     sdpLength += strlen(sdpPrefixFmt)
00266       + 20 + 6 + 20 + ipAddressStrSize
00267       + strlen(fDescriptionSDPString)
00268       + strlen(fInfoSDPString)
00269       + strlen(libNameStr) + strlen(libVersionStr)
00270       + strlen(sourceFilterLine)
00271       + strlen(rangeLine)
00272       + strlen(fDescriptionSDPString)
00273       + strlen(fInfoSDPString)
00274       + strlen(fMiscSDPLines);
00275     sdp = new char[sdpLength];
00276     if (sdp == NULL) break;
00277 
00278     // Generate the SDP prefix (session-level lines):
00279     sprintf(sdp, sdpPrefixFmt,
00280             fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>
00281             1, // o= <version> // (needs to change if params are modified)
00282             ipAddressStr.val(), // o= <address>
00283             fDescriptionSDPString, // s= <description>
00284             fInfoSDPString, // i= <info>
00285             libNameStr, libVersionStr, // a=tool:
00286             sourceFilterLine, // a=source-filter: incl (if a SSM session)
00287             rangeLine, // a=range: line
00288             fDescriptionSDPString, // a=x-qt-text-nam: line
00289             fInfoSDPString, // a=x-qt-text-inf: line
00290             fMiscSDPLines); // miscellaneous session SDP lines (if any)
00291 
00292     // Then, add the (media-level) lines for each subsession:
00293     char* mediaSDP = sdp;
00294     for (subsession = fSubsessionsHead; subsession != NULL;
00295          subsession = subsession->fNext) {
00296       mediaSDP += strlen(mediaSDP);
00297       char const* sdpLines = subsession->sdpLines();
00298       if (sdpLines != NULL) sprintf(mediaSDP, "%s", sdpLines);
00299     }
00300   } while (0);
00301 
00302   delete[] rangeLine; delete[] sourceFilterLine;
00303   return sdp;
00304 }
00305 
00306 
00308 
00309 ServerMediaSubsessionIterator
00310 ::ServerMediaSubsessionIterator(ServerMediaSession& session)
00311   : fOurSession(session) {
00312   reset();
00313 }
00314 
00315 ServerMediaSubsessionIterator::~ServerMediaSubsessionIterator() {
00316 }
00317 
00318 ServerMediaSubsession* ServerMediaSubsessionIterator::next() {
00319   ServerMediaSubsession* result = fNextPtr;
00320 
00321   if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext;
00322 
00323   return result;
00324 }
00325 
00326 void ServerMediaSubsessionIterator::reset() {
00327   fNextPtr = fOurSession.fSubsessionsHead;
00328 }
00329 
00330 
00332 
00333 ServerMediaSubsession::ServerMediaSubsession(UsageEnvironment& env)
00334   : Medium(env),
00335     fParentSession(NULL), fServerAddressForSDP(0), fPortNumForSDP(0),
00336     fNext(NULL), fTrackNumber(0), fTrackId(NULL) {
00337 }
00338 
00339 ServerMediaSubsession::~ServerMediaSubsession() {
00340   delete[] (char*)fTrackId;
00341   Medium::close(fNext);
00342 }
00343 
00344 char const* ServerMediaSubsession::trackId() {
00345   if (fTrackNumber == 0) return NULL; // not yet in a ServerMediaSession
00346 
00347   if (fTrackId == NULL) {
00348     char buf[100];
00349     sprintf(buf, "track%d", fTrackNumber);
00350     fTrackId = strDup(buf);
00351   }
00352   return fTrackId;
00353 }
00354 
00355 void ServerMediaSubsession::pauseStream(unsigned /*clientSessionId*/,
00356                                         void* /*streamToken*/) {
00357   // default implementation: do nothing
00358 }
00359 void ServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,
00360                                        void* /*streamToken*/, double& /*seekNPT*/, double /*streamDuration*/, u_int64_t& numBytes) {
00361   // default implementation: do nothing
00362   numBytes = 0;
00363 }
00364 void ServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,
00365                                        void* /*streamToken*/, char*& absStart, char*& absEnd) {
00366   // default implementation: do nothing (but delete[] and assign "absStart" and "absEnd" to NULL, to show that we don't handle this)
00367   delete[] absStart; absStart = NULL;
00368   delete[] absEnd; absEnd = NULL;
00369 }
00370 void ServerMediaSubsession::nullSeekStream(unsigned /*clientSessionId*/, void* /*streamToken*/) {
00371   // default implementation: do nothing
00372 }
00373 void ServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,
00374                                            void* /*streamToken*/, float /*scale*/) {
00375   // default implementation: do nothing
00376 }
00377 float ServerMediaSubsession::getCurrentNPT(void* /*streamToken*/) {
00378   // default implementation: return 0.0
00379   return 0.0;
00380 }
00381 FramedSource* ServerMediaSubsession::getStreamSource(void* /*streamToken*/) {
00382   // default implementation: return NULL
00383   return NULL;
00384 }
00385 void ServerMediaSubsession::deleteStream(unsigned /*clientSessionId*/,
00386                                          void*& /*streamToken*/) {
00387   // default implementation: do nothing
00388 }
00389 
00390 void ServerMediaSubsession::testScaleFactor(float& scale) {
00391   // default implementation: Support scale = 1 only
00392   scale = 1;
00393 }
00394 
00395 float ServerMediaSubsession::duration() const {
00396   // default implementation: assume an unbounded session:
00397   return 0.0;
00398 }
00399 
00400 void ServerMediaSubsession::getAbsoluteTimeRange(char*& absStartTime, char*& absEndTime) const {
00401   // default implementation: We don't support seeking by 'absolute' time, so indicate this by setting both parameters to NULL:
00402   absStartTime = absEndTime = NULL;
00403 }
00404 
00405 void ServerMediaSubsession::setServerAddressAndPortForSDP(netAddressBits addressBits,
00406                                                           portNumBits portBits) {
00407   fServerAddressForSDP = addressBits;
00408   fPortNumForSDP = portBits;
00409 }
00410 
00411 char const*
00412 ServerMediaSubsession::rangeSDPLine() const {
00413   // First, check for the special case where we support seeking by 'absolute' time:
00414   char* absStart = NULL; char* absEnd = NULL;
00415   getAbsoluteTimeRange(absStart, absEnd);
00416   if (absStart != NULL) {
00417     char buf[100];
00418 
00419     if (absEnd != NULL) {
00420       sprintf(buf, "a=range:clock=%s-%s\r\n", absStart, absEnd);
00421     } else {
00422       sprintf(buf, "a=range:clock=%s-\r\n", absStart);
00423     }
00424     return strDup(buf);
00425   }
00426 
00427   if (fParentSession == NULL) return NULL;
00428 
00429   // If all of our parent's subsessions have the same duration
00430   // (as indicated by "fParentSession->duration() >= 0"), there's no "a=range:" line:
00431   if (fParentSession->duration() >= 0.0) return strDup("");
00432 
00433   // Use our own duration for a "a=range:" line:
00434   float ourDuration = duration();
00435   if (ourDuration == 0.0) {
00436     return strDup("a=range:npt=0-\r\n");
00437   } else {
00438     char buf[100];
00439     sprintf(buf, "a=range:npt=0-%.3f\r\n", ourDuration);
00440     return strDup(buf);
00441   }
00442 }

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