liveMedia/DarwinInjector.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-2014 Live Networks, Inc.  All rights reserved.
00018 // An object that redirects one or more RTP/RTCP streams - forming a single
00019 // multimedia session - into a 'Darwin Streaming Server' (for subsequent
00020 // reflection to potentially arbitrarily many remote RTSP clients).
00021 // Implementation
00022 
00023 #include "DarwinInjector.hh"
00024 #include <GroupsockHelper.hh>
00025 
00027 
00028 class SubstreamDescriptor {
00029 public:
00030   SubstreamDescriptor(RTPSink* rtpSink, RTCPInstance* rtcpInstance, unsigned trackId);
00031   ~SubstreamDescriptor();
00032 
00033   SubstreamDescriptor*& next() { return fNext; }
00034   RTPSink* rtpSink() const { return fRTPSink; }
00035   RTCPInstance* rtcpInstance() const { return fRTCPInstance; }
00036   char const* sdpLines() const { return fSDPLines; }
00037 
00038 private:
00039   SubstreamDescriptor* fNext;
00040   RTPSink* fRTPSink;
00041   RTCPInstance* fRTCPInstance;
00042   char* fSDPLines;
00043 };
00044 
00045 
00047 
00048 DarwinInjector* DarwinInjector::createNew(UsageEnvironment& env,
00049                                           char const* applicationName,
00050                                           int verbosityLevel) {
00051   return new DarwinInjector(env, applicationName, verbosityLevel);
00052 }
00053 
00054 Boolean DarwinInjector::lookupByName(UsageEnvironment& env, char const* name,
00055                                      DarwinInjector*& result) {
00056   result = NULL; // unless we succeed
00057 
00058   Medium* medium;
00059   if (!Medium::lookupByName(env, name, medium)) return False;
00060 
00061   if (!medium->isDarwinInjector()) {
00062     env.setResultMsg(name, " is not a 'Darwin injector'");
00063     return False;
00064   }
00065 
00066   result = (DarwinInjector*)medium;
00067   return True;
00068 }
00069 
00070 DarwinInjector::DarwinInjector(UsageEnvironment& env,
00071                                char const* applicationName, int verbosityLevel)
00072   : Medium(env),
00073     fApplicationName(strDup(applicationName)), fVerbosityLevel(verbosityLevel),
00074     fRTSPClient(NULL), fSubstreamSDPSizes(0),
00075     fHeadSubstream(NULL), fTailSubstream(NULL), fSession(NULL), fLastTrackId(0), fResultString(NULL) {
00076 }
00077 
00078 DarwinInjector::~DarwinInjector() {
00079   if (fSession != NULL) { // close down and delete the session
00080     fRTSPClient->sendTeardownCommand(*fSession, NULL);
00081     Medium::close(fSession);
00082   }
00083 
00084   delete fHeadSubstream;
00085   delete[] (char*)fApplicationName;
00086   Medium::close(fRTSPClient);
00087 }
00088 
00089 void DarwinInjector::addStream(RTPSink* rtpSink, RTCPInstance* rtcpInstance) {
00090   if (rtpSink == NULL) return; // "rtpSink" should be non-NULL
00091 
00092   SubstreamDescriptor* newDescriptor = new SubstreamDescriptor(rtpSink, rtcpInstance, ++fLastTrackId);
00093   if (fHeadSubstream == NULL) {
00094     fHeadSubstream = fTailSubstream = newDescriptor;
00095   } else {
00096     fTailSubstream->next() = newDescriptor;
00097     fTailSubstream = newDescriptor;
00098   }
00099 
00100   fSubstreamSDPSizes += strlen(newDescriptor->sdpLines());
00101 }
00102 
00103 // Define a special subclass of "RTSPClient" that has a pointer field to a "DarwinInjector".  We'll use this to implement RTSP ops:
00104 class RTSPClientForDarwinInjector: public RTSPClient {
00105 public:
00106   RTSPClientForDarwinInjector(UsageEnvironment& env, char const* rtspURL, int verbosityLevel, char const* applicationName,
00107                               DarwinInjector* ourDarwinInjector)
00108     : RTSPClient(env, rtspURL, verbosityLevel, applicationName, 0, -1),
00109       fOurDarwinInjector(ourDarwinInjector) {}
00110   virtual ~RTSPClientForDarwinInjector() {}
00111   DarwinInjector* fOurDarwinInjector;
00112 };
00113 
00114 Boolean DarwinInjector
00115 ::setDestination(char const* remoteRTSPServerNameOrAddress,
00116                  char const* remoteFileName,
00117                  char const* sessionName,
00118                  char const* sessionInfo,
00119                  portNumBits remoteRTSPServerPortNumber,
00120                  char const* remoteUserName,
00121                  char const* remotePassword,
00122                  char const* sessionAuthor,
00123                  char const* sessionCopyright,
00124                  int timeout) {
00125   char* sdp = NULL;
00126   char* url = NULL;
00127   Boolean success = False; // until we learn otherwise
00128 
00129   do {
00130     // Construct a RTSP URL for the remote stream:
00131     char const* const urlFmt = "rtsp://%s:%u/%s";
00132     unsigned urlLen
00133       = strlen(urlFmt) + strlen(remoteRTSPServerNameOrAddress) + 5 /* max short len */ + strlen(remoteFileName);
00134     url = new char[urlLen];
00135     sprintf(url, urlFmt, remoteRTSPServerNameOrAddress, remoteRTSPServerPortNumber, remoteFileName);
00136 
00137     // Begin by creating our RTSP client object:
00138     fRTSPClient = new RTSPClientForDarwinInjector(envir(), url, fVerbosityLevel, fApplicationName, this);
00139     if (fRTSPClient == NULL) break;
00140 
00141     // Get the remote RTSP server's IP address:
00142     struct in_addr addr;
00143     {
00144       NetAddressList addresses(remoteRTSPServerNameOrAddress);
00145       if (addresses.numAddresses() == 0) break;
00146       NetAddress const* address = addresses.firstAddress();
00147       addr.s_addr = *(unsigned*)(address->data());
00148     }
00149     AddressString remoteRTSPServerAddressStr(addr);
00150 
00151     // Construct a SDP description for the session that we'll be streaming:
00152     char const* const sdpFmt =
00153       "v=0\r\n"
00154       "o=- %u %u IN IP4 127.0.0.1\r\n"
00155       "s=%s\r\n"
00156       "i=%s\r\n"
00157       "c=IN IP4 %s\r\n"
00158       "t=0 0\r\n"
00159       "a=x-qt-text-nam:%s\r\n"
00160       "a=x-qt-text-inf:%s\r\n"
00161       "a=x-qt-text-cmt:source application:%s\r\n"
00162       "a=x-qt-text-aut:%s\r\n"
00163       "a=x-qt-text-cpy:%s\r\n";
00164       // plus, %s for each substream SDP
00165     unsigned sdpLen = strlen(sdpFmt)
00166       + 20 /* max int len */ + 20 /* max int len */
00167       + strlen(sessionName)
00168       + strlen(sessionInfo)
00169       + strlen(remoteRTSPServerAddressStr.val())
00170       + strlen(sessionName)
00171       + strlen(sessionInfo)
00172       + strlen(fApplicationName)
00173       + strlen(sessionAuthor)
00174       + strlen(sessionCopyright)
00175       + fSubstreamSDPSizes;
00176     unsigned const sdpSessionId = our_random32();
00177     unsigned const sdpVersion = sdpSessionId;
00178     sdp = new char[sdpLen];
00179     sprintf(sdp, sdpFmt,
00180             sdpSessionId, sdpVersion, // o= line
00181             sessionName, // s= line
00182             sessionInfo, // i= line
00183             remoteRTSPServerAddressStr.val(), // c= line
00184             sessionName, // a=x-qt-text-nam: line
00185             sessionInfo, // a=x-qt-text-inf: line
00186             fApplicationName, // a=x-qt-text-cmt: line
00187             sessionAuthor, // a=x-qt-text-aut: line
00188             sessionCopyright // a=x-qt-text-cpy: line
00189             );
00190     char* p = &sdp[strlen(sdp)];
00191     SubstreamDescriptor* ss;
00192     for (ss = fHeadSubstream; ss != NULL; ss = ss->next()) {
00193       sprintf(p, "%s", ss->sdpLines());
00194       p += strlen(p);
00195     }
00196 
00197     // Do a RTSP "ANNOUNCE" with this SDP description:
00198     Authenticator auth;
00199     Authenticator* authToUse = NULL;
00200     if (remoteUserName[0] != '\0' || remotePassword[0] != '\0') {
00201       auth.setUsernameAndPassword(remoteUserName, remotePassword);
00202       authToUse = &auth;
00203     }
00204     fWatchVariable = 0;
00205     (void)fRTSPClient->sendAnnounceCommand(sdp, genericResponseHandler, authToUse);
00206 
00207     // Now block (but handling events) until we get a response:
00208     envir().taskScheduler().doEventLoop(&fWatchVariable);
00209 
00210     delete[] fResultString;
00211     if (fResultCode != 0) break; // an error occurred with the RTSP "ANNOUNCE" command
00212 
00213     // Next, tell the remote server to start receiving the stream from us.
00214     // (To do this, we first create a "MediaSession" object from the SDP description.)
00215     fSession = MediaSession::createNew(envir(), sdp);
00216     if (fSession == NULL) break;
00217 
00218     ss = fHeadSubstream;
00219     MediaSubsessionIterator iter(*fSession);
00220     MediaSubsession* subsession;
00221     ss = fHeadSubstream;
00222     unsigned streamChannelId = 0;
00223     while ((subsession = iter.next()) != NULL) {
00224       if (!subsession->initiate()) break;
00225 
00226       fWatchVariable = 0;
00227       (void)fRTSPClient->sendSetupCommand(*subsession, genericResponseHandler,
00228                                           True /*streamOutgoing*/,
00229                                           True /*streamUsingTCP*/);
00230       // Now block (but handling events) until we get a response:
00231       envir().taskScheduler().doEventLoop(&fWatchVariable);
00232 
00233       delete[] fResultString;
00234       if (fResultCode != 0) break; // an error occurred with the RTSP "SETUP" command
00235 
00236       // Tell this subsession's RTPSink and RTCPInstance to use
00237       // the RTSP TCP connection:
00238       ss->rtpSink()->setStreamSocket(fRTSPClient->socketNum(), streamChannelId++);
00239       if (ss->rtcpInstance() != NULL) {
00240         ss->rtcpInstance()->setStreamSocket(fRTSPClient->socketNum(),
00241                                             streamChannelId++);
00242       }
00243       ss = ss->next();
00244     }
00245     if (subsession != NULL) break; // an error occurred above
00246 
00247     // Tell the RTSP server to start:
00248     fWatchVariable = 0;
00249     (void)fRTSPClient->sendPlayCommand(*fSession, genericResponseHandler);
00250 
00251     // Now block (but handling events) until we get a response:
00252     envir().taskScheduler().doEventLoop(&fWatchVariable);
00253 
00254     delete[] fResultString;
00255     if (fResultCode != 0) break; // an error occurred with the RTSP "PLAY" command
00256 
00257     // Finally, make sure that the output TCP buffer is a reasonable size:
00258     increaseSendBufferTo(envir(), fRTSPClient->socketNum(), 100*1024);
00259 
00260     success = True;
00261   } while (0);
00262 
00263   delete[] sdp;
00264   delete[] url;
00265   return success;
00266 }
00267 
00268 Boolean DarwinInjector::isDarwinInjector() const {
00269   return True;
00270 }
00271 
00272 void DarwinInjector::genericResponseHandler(RTSPClient* rtspClient, int responseCode, char* responseString) {
00273   DarwinInjector* di = ((RTSPClientForDarwinInjector*)rtspClient)-> fOurDarwinInjector;
00274   di->genericResponseHandler1(responseCode, responseString);
00275 }
00276 
00277 void DarwinInjector::genericResponseHandler1(int responseCode, char* responseString) {
00278   // Set result values:
00279   fResultCode = responseCode;
00280   fResultString = responseString;
00281 
00282   // Signal a break from the event loop (thereby returning from the blocking command):                                              
00283   fWatchVariable = ~0;
00284 }
00285 
00287 
00288 SubstreamDescriptor::SubstreamDescriptor(RTPSink* rtpSink,
00289                                          RTCPInstance* rtcpInstance, unsigned trackId)
00290   : fNext(NULL), fRTPSink(rtpSink), fRTCPInstance(rtcpInstance) {
00291   // Create the SDP description for this substream
00292   char const* mediaType = fRTPSink->sdpMediaType();
00293   unsigned char rtpPayloadType = fRTPSink->rtpPayloadType();
00294   char const* rtpPayloadFormatName = fRTPSink->rtpPayloadFormatName();
00295   unsigned rtpTimestampFrequency = fRTPSink->rtpTimestampFrequency();
00296   unsigned numChannels = fRTPSink->numChannels();
00297   char* rtpmapLine;
00298   if (rtpPayloadType >= 96) {
00299     char* encodingParamsPart;
00300     if (numChannels != 1) {
00301       encodingParamsPart = new char[1 + 20 /* max int len */];
00302       sprintf(encodingParamsPart, "/%d", numChannels);
00303     } else {
00304       encodingParamsPart = strDup("");
00305     }
00306     char const* const rtpmapFmt = "a=rtpmap:%d %s/%d%s\r\n";
00307     unsigned rtpmapFmtSize = strlen(rtpmapFmt)
00308       + 3 /* max char len */ + strlen(rtpPayloadFormatName)
00309       + 20 /* max int len */ + strlen(encodingParamsPart);
00310     rtpmapLine = new char[rtpmapFmtSize];
00311     sprintf(rtpmapLine, rtpmapFmt,
00312             rtpPayloadType, rtpPayloadFormatName,
00313             rtpTimestampFrequency, encodingParamsPart);
00314     delete[] encodingParamsPart;
00315   } else {
00316     // Static payload type => no "a=rtpmap:" line
00317     rtpmapLine = strDup("");
00318   }
00319   unsigned rtpmapLineSize = strlen(rtpmapLine);
00320   char const* auxSDPLine = fRTPSink->auxSDPLine();
00321   if (auxSDPLine == NULL) auxSDPLine = "";
00322   unsigned auxSDPLineSize = strlen(auxSDPLine);
00323 
00324   char const* const sdpFmt =
00325     "m=%s 0 RTP/AVP %u\r\n"
00326     "%s" // "a=rtpmap:" line (if present)
00327     "%s" // auxilliary (e.g., "a=fmtp:") line (if present)
00328     "a=control:trackID=%u\r\n";
00329   unsigned sdpFmtSize = strlen(sdpFmt)
00330     + strlen(mediaType) + 3 /* max char len */
00331     + rtpmapLineSize
00332     + auxSDPLineSize
00333     + 20 /* max int len */;
00334   char* sdpLines = new char[sdpFmtSize];
00335   sprintf(sdpLines, sdpFmt,
00336           mediaType, // m= <media>
00337           rtpPayloadType, // m= <fmt list>
00338           rtpmapLine, // a=rtpmap:... (if present)
00339           auxSDPLine, // optional extra SDP line
00340           trackId); // a=control:<track-id>
00341   fSDPLines = strDup(sdpLines);
00342   delete[] sdpLines;
00343   delete[] rtpmapLine;
00344 }
00345 
00346 SubstreamDescriptor::~SubstreamDescriptor() {
00347   delete fSDPLines;
00348   delete fNext;
00349 }

Generated on Tue Mar 25 14:35:34 2014 for live by  doxygen 1.5.2