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-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 // (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   Medium::close(fSubsessionsHead);
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     float ssduration = subsession->duration();
00172     if (subsession == fSubsessionsHead) { // this is the first subsession
00173       minSubsessionDuration = maxSubsessionDuration = ssduration;
00174     } else if (ssduration < minSubsessionDuration) {
00175         minSubsessionDuration = ssduration;
00176     } else if (ssduration > maxSubsessionDuration) {
00177         maxSubsessionDuration = ssduration;
00178     }
00179   }
00180 
00181   if (maxSubsessionDuration != minSubsessionDuration) {
00182     return -maxSubsessionDuration; // because subsession durations differ
00183   } else {
00184     return maxSubsessionDuration; // all subsession durations are the same
00185   }
00186 }
00187 
00188 Boolean ServerMediaSession::isServerMediaSession() const {
00189   return True;
00190 }
00191 
00192 char* ServerMediaSession::generateSDPDescription() {
00193   AddressString ipAddressStr(ourIPAddress(envir()));
00194   unsigned ipAddressStrSize = strlen(ipAddressStr.val());
00195 
00196   // For a SSM sessions, we need a "a=source-filter: incl ..." line also:
00197   char* sourceFilterLine;
00198   if (fIsSSM) {
00199     char const* const sourceFilterFmt =
00200       "a=source-filter: incl IN IP4 * %s\r\n"
00201       "a=rtcp-unicast: reflection\r\n";
00202     unsigned const sourceFilterFmtSize = strlen(sourceFilterFmt) + ipAddressStrSize + 1;
00203 
00204     sourceFilterLine = new char[sourceFilterFmtSize];
00205     sprintf(sourceFilterLine, sourceFilterFmt, ipAddressStr.val());
00206   } else {
00207     sourceFilterLine = strDup("");
00208   }
00209 
00210   char* rangeLine = NULL; // for now
00211   char* sdp = NULL; // for now
00212 
00213   do {
00214     // Count the lengths of each subsession's media-level SDP lines.
00215     // (We do this first, because the call to "subsession->sdpLines()"
00216     // causes correct subsession 'duration()'s to be calculated later.)
00217     unsigned sdpLength = 0;
00218     ServerMediaSubsession* subsession;
00219     for (subsession = fSubsessionsHead; subsession != NULL;
00220          subsession = subsession->fNext) {
00221       char const* sdpLines = subsession->sdpLines();
00222       if (sdpLines == NULL) break; // the media's not available
00223       sdpLength += strlen(sdpLines);
00224     }
00225     if (subsession != NULL) break; // an error occurred
00226 
00227     // Unless subsessions have differing durations, we also have a "a=range:" line:
00228     float dur = duration();
00229     if (dur == 0.0) {
00230       rangeLine = strDup("a=range:npt=0-\r\n");
00231     } else if (dur > 0.0) {
00232       char buf[100];
00233       sprintf(buf, "a=range:npt=0-%.3f\r\n", dur);
00234       rangeLine = strDup(buf);
00235     } else { // subsessions have differing durations, so "a=range:" lines go there
00236       rangeLine = strDup("");
00237     }
00238 
00239     char const* const sdpPrefixFmt =
00240       "v=0\r\n"
00241       "o=- %ld%06ld %d IN IP4 %s\r\n"
00242       "s=%s\r\n"
00243       "i=%s\r\n"
00244       "t=0 0\r\n"
00245       "a=tool:%s%s\r\n"
00246       "a=type:broadcast\r\n"
00247       "a=control:*\r\n"
00248       "%s"
00249       "%s"
00250       "a=x-qt-text-nam:%s\r\n"
00251       "a=x-qt-text-inf:%s\r\n"
00252       "%s";
00253     sdpLength += strlen(sdpPrefixFmt)
00254       + 20 + 6 + 20 + ipAddressStrSize
00255       + strlen(fDescriptionSDPString)
00256       + strlen(fInfoSDPString)
00257       + strlen(libNameStr) + strlen(libVersionStr)
00258       + strlen(sourceFilterLine)
00259       + strlen(rangeLine)
00260       + strlen(fDescriptionSDPString)
00261       + strlen(fInfoSDPString)
00262       + strlen(fMiscSDPLines);
00263     sdp = new char[sdpLength];
00264     if (sdp == NULL) break;
00265 
00266     // Generate the SDP prefix (session-level lines):
00267     sprintf(sdp, sdpPrefixFmt,
00268             fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>
00269             1, // o= <version> // (needs to change if params are modified)
00270             ipAddressStr.val(), // o= <address>
00271             fDescriptionSDPString, // s= <description>
00272             fInfoSDPString, // i= <info>
00273             libNameStr, libVersionStr, // a=tool:
00274             sourceFilterLine, // a=source-filter: incl (if a SSM session)
00275             rangeLine, // a=range: line
00276             fDescriptionSDPString, // a=x-qt-text-nam: line
00277             fInfoSDPString, // a=x-qt-text-inf: line
00278             fMiscSDPLines); // miscellaneous session SDP lines (if any)
00279 
00280     // Then, add the (media-level) lines for each subsession:
00281     char* mediaSDP = sdp;
00282     for (subsession = fSubsessionsHead; subsession != NULL;
00283          subsession = subsession->fNext) {
00284       mediaSDP += strlen(mediaSDP);
00285       sprintf(mediaSDP, "%s", subsession->sdpLines());
00286     }
00287   } while (0);
00288 
00289   delete[] rangeLine; delete[] sourceFilterLine;
00290   return sdp;
00291 }
00292 
00293 
00295 
00296 ServerMediaSubsessionIterator
00297 ::ServerMediaSubsessionIterator(ServerMediaSession& session)
00298   : fOurSession(session) {
00299   reset();
00300 }
00301 
00302 ServerMediaSubsessionIterator::~ServerMediaSubsessionIterator() {
00303 }
00304 
00305 ServerMediaSubsession* ServerMediaSubsessionIterator::next() {
00306   ServerMediaSubsession* result = fNextPtr;
00307 
00308   if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext;
00309 
00310   return result;
00311 }
00312 
00313 void ServerMediaSubsessionIterator::reset() {
00314   fNextPtr = fOurSession.fSubsessionsHead;
00315 }
00316 
00317 
00319 
00320 ServerMediaSubsession::ServerMediaSubsession(UsageEnvironment& env)
00321   : Medium(env),
00322     fParentSession(NULL), fServerAddressForSDP(0), fPortNumForSDP(0),
00323     fNext(NULL), fTrackNumber(0), fTrackId(NULL) {
00324 }
00325 
00326 ServerMediaSubsession::~ServerMediaSubsession() {
00327   delete[] (char*)fTrackId;
00328   Medium::close(fNext);
00329 }
00330 
00331 char const* ServerMediaSubsession::trackId() {
00332   if (fTrackNumber == 0) return NULL; // not yet in a ServerMediaSession
00333 
00334   if (fTrackId == NULL) {
00335     char buf[100];
00336     sprintf(buf, "track%d", fTrackNumber);
00337     fTrackId = strDup(buf);
00338   }
00339   return fTrackId;
00340 }
00341 
00342 void ServerMediaSubsession::pauseStream(unsigned /*clientSessionId*/,
00343                                         void* /*streamToken*/) {
00344   // default implementation: do nothing
00345 }
00346 void ServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,
00347                                        void* /*streamToken*/, double& /*seekNPT*/, double /*streamDuration*/, u_int64_t& numBytes) {
00348   // default implementation: do nothing
00349   numBytes = 0;
00350 }
00351 FramedSource* ServerMediaSubsession::getStreamSource(void* /*streamToken*/) {
00352   // default implementation: return NULL
00353   return NULL;
00354 }
00355 void ServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,
00356                                            void* /*streamToken*/, float /*scale*/) {
00357   // default implementation: do nothing
00358 }
00359 void ServerMediaSubsession::deleteStream(unsigned /*clientSessionId*/,
00360                                          void*& /*streamToken*/) {
00361   // default implementation: do nothing
00362 }
00363 
00364 void ServerMediaSubsession::testScaleFactor(float& scale) {
00365   // default implementation: Support scale = 1 only
00366   scale = 1;
00367 }
00368 
00369 float ServerMediaSubsession::duration() const {
00370   // default implementation: assume an unbounded session:
00371   return 0.0;
00372 }
00373 
00374 void ServerMediaSubsession::setServerAddressAndPortForSDP(netAddressBits addressBits,
00375                                                           portNumBits portBits) {
00376   fServerAddressForSDP = addressBits;
00377   fPortNumForSDP = portBits;
00378 }
00379 
00380 char const*
00381 ServerMediaSubsession::rangeSDPLine() const {
00382   if (fParentSession == NULL) return NULL;
00383 
00384   // If all of our parent's subsessions have the same duration
00385   // (as indicated by "fParentSession->duration() >= 0"), there's no "a=range:" line:
00386   if (fParentSession->duration() >= 0.0) return strDup("");
00387 
00388   // Use our own duration for a "a=range:" line:
00389   float ourDuration = duration();
00390   if (ourDuration == 0.0) {
00391     return strDup("a=range:npt=0-\r\n");
00392   } else {
00393     char buf[100];
00394     sprintf(buf, "a=range:npt=0-%.3f\r\n", ourDuration);
00395     return strDup(buf);
00396   }
00397 }

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