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

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