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-2008 Live Networks, Inc.  All rights reserved.
00018 // A data structure that represents a session that consists of
00019 // potentially multiple (audio and/or video) sub-sessions
00020 // (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   fInfoSDPString = strDup(info == NULL ? libNameStr : info);
00068   fDescriptionSDPString
00069     = strDup(description == NULL ? libNameStr : description);
00070   fMiscSDPLines = strDup(miscSDPLines == NULL ? "" : miscSDPLines);
00071 
00072   gettimeofday(&fCreationTime, NULL);
00073 }
00074 
00075 ServerMediaSession::~ServerMediaSession() {
00076   Medium::close(fSubsessionsHead);
00077   delete[] fStreamName;
00078   delete[] fInfoSDPString;
00079   delete[] fDescriptionSDPString;
00080   delete[] fMiscSDPLines;
00081 }
00082 
00083 Boolean
00084 ServerMediaSession::addSubsession(ServerMediaSubsession* subsession) {
00085   if (subsession->fParentSession != NULL) return False; // it's already used
00086 
00087   if (fSubsessionsTail == NULL) {
00088     fSubsessionsHead = subsession;
00089   } else {
00090     fSubsessionsTail->fNext = subsession;
00091   }
00092   fSubsessionsTail = subsession;
00093 
00094   subsession->fParentSession = this;
00095   subsession->fTrackNumber = ++fSubsessionCounter;
00096   return True;
00097 }
00098 
00099 void ServerMediaSession::testScaleFactor(float& scale) {
00100   // First, try setting all subsessions to the desired scale.
00101   // If the subsessions' actual scales differ from each other, choose the
00102   // value that's closest to 1, and then try re-setting all subsessions to that
00103   // value.  If the subsessions' actual scales still differ, re-set them all to 1.
00104   float minSSScale = 1.0;
00105   float maxSSScale = 1.0;
00106   float bestSSScale = 1.0;
00107   float bestDistanceTo1 = 0.0;
00108   ServerMediaSubsession* subsession;
00109   for (subsession = fSubsessionsHead; subsession != NULL;
00110        subsession = subsession->fNext) {
00111     float ssscale = scale;
00112     subsession->testScaleFactor(ssscale);
00113     if (subsession == fSubsessionsHead) { // this is the first subsession
00114       minSSScale = maxSSScale = bestSSScale = ssscale;
00115       bestDistanceTo1 = (float)fabs(ssscale - 1.0f);
00116     } else {
00117       if (ssscale < minSSScale) {
00118         minSSScale = ssscale;
00119       } else if (ssscale > maxSSScale) {
00120         maxSSScale = ssscale;
00121       }
00122 
00123       float distanceTo1 = (float)fabs(ssscale - 1.0f);
00124       if (distanceTo1 < bestDistanceTo1) {
00125         bestSSScale = ssscale;
00126         bestDistanceTo1 = distanceTo1;
00127       }
00128     }
00129   }
00130   if (minSSScale == maxSSScale) {
00131     // All subsessions are at the same scale: minSSScale == bestSSScale == maxSSScale
00132     scale = minSSScale;
00133     return;
00134   }
00135 
00136   // The scales for each subsession differ.  Try to set each one to the value
00137   // that's closest to 1:
00138   for (subsession = fSubsessionsHead; subsession != NULL;
00139        subsession = subsession->fNext) {
00140     float ssscale = bestSSScale;
00141     subsession->testScaleFactor(ssscale);
00142     if (ssscale != bestSSScale) break; // no luck
00143   }
00144   if (subsession == NULL) {
00145     // All subsessions are at the same scale: bestSSScale
00146     scale = bestSSScale;
00147     return;
00148   }
00149 
00150   // Still no luck.  Set each subsession's scale to 1:
00151   for (subsession = fSubsessionsHead; subsession != NULL;
00152        subsession = subsession->fNext) {
00153     float ssscale = 1;
00154     subsession->testScaleFactor(ssscale);
00155   }
00156   scale = 1;
00157 }
00158 
00159 float ServerMediaSession::duration() const {
00160   float minSubsessionDuration = 0.0;
00161   float maxSubsessionDuration = 0.0;
00162   for (ServerMediaSubsession* subsession = fSubsessionsHead; subsession != NULL;
00163        subsession = subsession->fNext) {
00164     float ssduration = subsession->duration();
00165     if (subsession == fSubsessionsHead) { // this is the first subsession
00166       minSubsessionDuration = maxSubsessionDuration = ssduration;
00167     } else if (ssduration < minSubsessionDuration) {
00168         minSubsessionDuration = ssduration;
00169     } else if (ssduration > maxSubsessionDuration) {
00170         maxSubsessionDuration = ssduration;
00171     }
00172   }
00173 
00174   if (maxSubsessionDuration != minSubsessionDuration) {
00175     return -maxSubsessionDuration; // because subsession durations differ
00176   } else {
00177     return maxSubsessionDuration; // all subsession durations are the same
00178   }
00179 }
00180 
00181 Boolean ServerMediaSession::isServerMediaSession() const {
00182   return True;
00183 }
00184 
00185 char* ServerMediaSession::generateSDPDescription() {
00186   struct in_addr ipAddress;
00187   ipAddress.s_addr = ourIPAddress(envir());
00188   char* const ipAddressStr = strDup(our_inet_ntoa(ipAddress));
00189   unsigned ipAddressStrSize = strlen(ipAddressStr);
00190 
00191   // For a SSM sessions, we need a "a=source-filter: incl ..." line also:
00192   char* sourceFilterLine;
00193   if (fIsSSM) {
00194     char const* const sourceFilterFmt =
00195       "a=source-filter: incl IN IP4 * %s\r\n"
00196       "a=rtcp-unicast: reflection\r\n";
00197     unsigned const sourceFilterFmtSize = strlen(sourceFilterFmt) + ipAddressStrSize + 1;
00198 
00199     sourceFilterLine = new char[sourceFilterFmtSize];
00200     sprintf(sourceFilterLine, sourceFilterFmt, ipAddressStr);
00201   } else {
00202     sourceFilterLine = strDup("");
00203   }
00204 
00205   char* rangeLine = NULL; // for now
00206   char* sdp = NULL; // for now
00207 
00208   do {
00209     // Count the lengths of each subsession's media-level SDP lines.
00210     // (We do this first, because the call to "subsession->sdpLines()"
00211     // causes correct subsession 'duration()'s to be calculated later.)
00212     unsigned sdpLength = 0;
00213     ServerMediaSubsession* subsession;
00214     for (subsession = fSubsessionsHead; subsession != NULL;
00215          subsession = subsession->fNext) {
00216       char const* sdpLines = subsession->sdpLines();
00217       if (sdpLines == NULL) break; // the media's not available
00218       sdpLength += strlen(sdpLines);
00219     }
00220     if (subsession != NULL) break; // an error occurred
00221 
00222     // Unless subsessions have differing durations, we also have a "a=range:" line:
00223     float dur = duration();
00224     if (dur == 0.0) {
00225       rangeLine = strDup("a=range:npt=0-\r\n");
00226     } else if (dur > 0.0) {
00227       char buf[100];
00228       sprintf(buf, "a=range:npt=0-%.3f\r\n", dur);
00229       rangeLine = strDup(buf);
00230     } else { // subsessions have differing durations, so "a=range:" lines go there
00231       rangeLine = strDup("");
00232     }
00233 
00234     char const* const sdpPrefixFmt =
00235       "v=0\r\n"
00236       "o=- %ld%06ld %d IN IP4 %s\r\n"
00237       "s=%s\r\n"
00238       "i=%s\r\n"
00239       "t=0 0\r\n"
00240       "a=tool:%s%s\r\n"
00241       "a=type:broadcast\r\n"
00242       "a=control:*\r\n"
00243       "%s"
00244       "%s"
00245       "a=x-qt-text-nam:%s\r\n"
00246       "a=x-qt-text-inf:%s\r\n"
00247       "%s";
00248     sdpLength += strlen(sdpPrefixFmt)
00249       + 20 + 6 + 20 + ipAddressStrSize
00250       + strlen(fDescriptionSDPString)
00251       + strlen(fInfoSDPString)
00252       + strlen(libNameStr) + strlen(libVersionStr)
00253       + strlen(sourceFilterLine)
00254       + strlen(rangeLine)
00255       + strlen(fDescriptionSDPString)
00256       + strlen(fInfoSDPString)
00257       + strlen(fMiscSDPLines);
00258     sdp = new char[sdpLength];
00259     if (sdp == NULL) break;
00260 
00261     // Generate the SDP prefix (session-level lines):
00262     sprintf(sdp, sdpPrefixFmt,
00263             fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>
00264             1, // o= <version> // (needs to change if params are modified)
00265             ipAddressStr, // o= <address>
00266             fDescriptionSDPString, // s= <description>
00267             fInfoSDPString, // i= <info>
00268             libNameStr, libVersionStr, // a=tool:
00269             sourceFilterLine, // a=source-filter: incl (if a SSM session)
00270             rangeLine, // a=range: line
00271             fDescriptionSDPString, // a=x-qt-text-nam: line
00272             fInfoSDPString, // a=x-qt-text-inf: line
00273             fMiscSDPLines); // miscellaneous session SDP lines (if any)
00274 
00275     // Then, add the (media-level) lines for each subsession:
00276     char* mediaSDP = sdp;
00277     for (subsession = fSubsessionsHead; subsession != NULL;
00278          subsession = subsession->fNext) {
00279       mediaSDP += strlen(mediaSDP);
00280       sprintf(mediaSDP, "%s", subsession->sdpLines());
00281     }
00282   } while (0);
00283 
00284   delete[] rangeLine; delete[] sourceFilterLine; delete[] ipAddressStr;
00285   return sdp;
00286 }
00287 
00288 
00290 
00291 ServerMediaSubsessionIterator
00292 ::ServerMediaSubsessionIterator(ServerMediaSession& session)
00293   : fOurSession(session) {
00294   reset();
00295 }
00296 
00297 ServerMediaSubsessionIterator::~ServerMediaSubsessionIterator() {
00298 }
00299 
00300 ServerMediaSubsession* ServerMediaSubsessionIterator::next() {
00301   ServerMediaSubsession* result = fNextPtr;
00302 
00303   if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext;
00304 
00305   return result;
00306 }
00307 
00308 void ServerMediaSubsessionIterator::reset() {
00309   fNextPtr = fOurSession.fSubsessionsHead;
00310 }
00311 
00312 
00314 
00315 ServerMediaSubsession::ServerMediaSubsession(UsageEnvironment& env)
00316   : Medium(env),
00317     fParentSession(NULL), fServerAddressForSDP(0), fPortNumForSDP(0),
00318     fNext(NULL), fTrackNumber(0), fTrackId(NULL) {
00319 }
00320 
00321 ServerMediaSubsession::~ServerMediaSubsession() {
00322   delete[] (char*)fTrackId;
00323   Medium::close(fNext);
00324 }
00325 
00326 char const* ServerMediaSubsession::trackId() {
00327   if (fTrackNumber == 0) return NULL; // not yet in a ServerMediaSession
00328 
00329   if (fTrackId == NULL) {
00330     char buf[100];
00331     sprintf(buf, "track%d", fTrackNumber);
00332     fTrackId = strDup(buf);
00333   }
00334   return fTrackId;
00335 }
00336 
00337 void ServerMediaSubsession::pauseStream(unsigned /*clientSessionId*/,
00338                                         void* /*streamToken*/) {
00339   // default implementation: do nothing
00340 }
00341 void ServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,
00342                                        void* /*streamToken*/, float /*seekNPT*/) {
00343   // default implementation: do nothing
00344 }
00345 void ServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,
00346                                            void* /*streamToken*/, float /*scale*/) {
00347   // default implementation: do nothing
00348 }
00349 void ServerMediaSubsession::deleteStream(unsigned /*clientSessionId*/,
00350                                          void*& /*streamToken*/) {
00351   // default implementation: do nothing
00352 }
00353 
00354 void ServerMediaSubsession::testScaleFactor(float& scale) {
00355   // default implementation: Support scale = 1 only
00356   scale = 1;
00357 }
00358 
00359 float ServerMediaSubsession::duration() const {
00360   // default implementation: assume an unbounded session:
00361   return 0.0;
00362 }
00363 
00364 void ServerMediaSubsession::setServerAddressAndPortForSDP(netAddressBits addressBits,
00365                                                           portNumBits portBits) {
00366   fServerAddressForSDP = addressBits;
00367   fPortNumForSDP = portBits;
00368 }
00369 
00370 char const*
00371 ServerMediaSubsession::rangeSDPLine() const {
00372   if (fParentSession == NULL) return NULL;
00373 
00374   // If all of our parent's subsessions have the same duration
00375   // (as indicated by "fParentSession->duration() >= 0"), there's no "a=range:" line:
00376   if (fParentSession->duration() >= 0.0) return strDup("");
00377 
00378   // Use our own duration for a "a=range:" line:
00379   float ourDuration = duration();
00380   if (ourDuration == 0.0) {
00381     return strDup("a=range:npt=0-\r\n");
00382   } else {
00383     char buf[100];
00384     sprintf(buf, "a=range:npt=0-%.3f\r\n", ourDuration);
00385     return strDup(buf);
00386   }
00387 }

Generated on Tue Oct 7 15:38:10 2008 for live by  doxygen 1.5.2