mediaServer/DynamicRTSPServer.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 // Copyright (c) 1996-2014, Live Networks, Inc.  All rights reserved
00017 // A subclass of "RTSPServer" that creates "ServerMediaSession"s on demand,
00018 // based on whether or not the specified stream name exists as a file
00019 // Implementation
00020 
00021 #include "DynamicRTSPServer.hh"
00022 #include <liveMedia.hh>
00023 #include <string.h>
00024 
00025 DynamicRTSPServer*
00026 DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,
00027                              UserAuthenticationDatabase* authDatabase,
00028                              unsigned reclamationTestSeconds) {
00029   int ourSocket = setUpOurSocket(env, ourPort);
00030   if (ourSocket == -1) return NULL;
00031 
00032   return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
00033 }
00034 
00035 DynamicRTSPServer::DynamicRTSPServer(UsageEnvironment& env, int ourSocket,
00036                                      Port ourPort,
00037                                      UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds)
00038   : RTSPServerSupportingHTTPStreaming(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds) {
00039 }
00040 
00041 DynamicRTSPServer::~DynamicRTSPServer() {
00042 }
00043 
00044 static ServerMediaSession* createNewSMS(UsageEnvironment& env,
00045                                         char const* fileName, FILE* fid); // forward
00046 
00047 ServerMediaSession*
00048 DynamicRTSPServer::lookupServerMediaSession(char const* streamName) {
00049   // First, check whether the specified "streamName" exists as a local file:
00050   FILE* fid = fopen(streamName, "rb");
00051   Boolean fileExists = fid != NULL;
00052 
00053   // Next, check whether we already have a "ServerMediaSession" for this file:
00054   ServerMediaSession* sms = RTSPServer::lookupServerMediaSession(streamName);
00055   Boolean smsExists = sms != NULL;
00056 
00057   // Handle the four possibilities for "fileExists" and "smsExists":
00058   if (!fileExists) {
00059     if (smsExists) {
00060       // "sms" was created for a file that no longer exists. Remove it:
00061       removeServerMediaSession(sms);
00062     }
00063     return NULL;
00064   } else {
00065     if (!smsExists) {
00066       // Create a new "ServerMediaSession" object for streaming from the named file.
00067       sms = createNewSMS(envir(), streamName, fid);
00068       addServerMediaSession(sms);
00069     }
00070     fclose(fid);
00071     return sms;
00072   }
00073 }
00074 
00075 // Special code for handling Matroska files:
00076 struct MatroskaDemuxCreationState {
00077   MatroskaFileServerDemux* demux;
00078   char watchVariable;
00079 };
00080 static void onMatroskaDemuxCreation(MatroskaFileServerDemux* newDemux, void* clientData) {
00081   MatroskaDemuxCreationState* creationState = (MatroskaDemuxCreationState*)clientData;
00082   creationState->demux = newDemux;
00083   creationState->watchVariable = 1;
00084 }
00085 // END Special code for handling Matroska files:
00086 
00087 // Special code for handling Ogg files:
00088 struct OggDemuxCreationState {
00089   OggFileServerDemux* demux;
00090   char watchVariable;
00091 };
00092 static void onOggDemuxCreation(OggFileServerDemux* newDemux, void* clientData) {
00093   OggDemuxCreationState* creationState = (OggDemuxCreationState*)clientData;
00094   creationState->demux = newDemux;
00095   creationState->watchVariable = 1;
00096 }
00097 // END Special code for handling Ogg files:
00098 
00099 #define NEW_SMS(description) do {\
00100 char const* descStr = description\
00101     ", streamed by the LIVE555 Media Server";\
00102 sms = ServerMediaSession::createNew(env, fileName, fileName, descStr);\
00103 } while(0)
00104 
00105 static ServerMediaSession* createNewSMS(UsageEnvironment& env,
00106                                         char const* fileName, FILE* /*fid*/) {
00107   // Use the file name extension to determine the type of "ServerMediaSession":
00108   char const* extension = strrchr(fileName, '.');
00109   if (extension == NULL) return NULL;
00110 
00111   ServerMediaSession* sms = NULL;
00112   Boolean const reuseSource = False;
00113   if (strcmp(extension, ".aac") == 0) {
00114     // Assumed to be an AAC Audio (ADTS format) file:
00115     NEW_SMS("AAC Audio");
00116     sms->addSubsession(ADTSAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
00117   } else if (strcmp(extension, ".amr") == 0) {
00118     // Assumed to be an AMR Audio file:
00119     NEW_SMS("AMR Audio");
00120     sms->addSubsession(AMRAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
00121   } else if (strcmp(extension, ".ac3") == 0) {
00122     // Assumed to be an AC-3 Audio file:
00123     NEW_SMS("AC-3 Audio");
00124     sms->addSubsession(AC3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
00125   } else if (strcmp(extension, ".m4e") == 0) {
00126     // Assumed to be a MPEG-4 Video Elementary Stream file:
00127     NEW_SMS("MPEG-4 Video");
00128     sms->addSubsession(MPEG4VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
00129   } else if (strcmp(extension, ".264") == 0) {
00130     // Assumed to be a H.264 Video Elementary Stream file:
00131     NEW_SMS("H.264 Video");
00132     OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 frames
00133     sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
00134   } else if (strcmp(extension, ".265") == 0) {
00135     // Assumed to be a H.265 Video Elementary Stream file:
00136     NEW_SMS("H.265 Video");
00137     OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.265 frames
00138     sms->addSubsession(H265VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
00139   } else if (strcmp(extension, ".mp3") == 0) {
00140     // Assumed to be a MPEG-1 or 2 Audio file:
00141     NEW_SMS("MPEG-1 or 2 Audio");
00142     // To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
00143 //#define STREAM_USING_ADUS 1
00144     // To also reorder ADUs before streaming, uncomment the following:
00145 //#define INTERLEAVE_ADUS 1
00146     // (For more information about ADUs and interleaving,
00147     //  see <http://www.live555.com/rtp-mp3/>)
00148     Boolean useADUs = False;
00149     Interleaving* interleaving = NULL;
00150 #ifdef STREAM_USING_ADUS
00151     useADUs = True;
00152 #ifdef INTERLEAVE_ADUS
00153     unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...
00154     unsigned const interleaveCycleSize
00155       = (sizeof interleaveCycle)/(sizeof (unsigned char));
00156     interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);
00157 #endif
00158 #endif
00159     sms->addSubsession(MP3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, useADUs, interleaving));
00160   } else if (strcmp(extension, ".mpg") == 0) {
00161     // Assumed to be a MPEG-1 or 2 Program Stream (audio+video) file:
00162     NEW_SMS("MPEG-1 or 2 Program Stream");
00163     MPEG1or2FileServerDemux* demux
00164       = MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);
00165     sms->addSubsession(demux->newVideoServerMediaSubsession());
00166     sms->addSubsession(demux->newAudioServerMediaSubsession());
00167   } else if (strcmp(extension, ".vob") == 0) {
00168     // Assumed to be a VOB (MPEG-2 Program Stream, with AC-3 audio) file:
00169     NEW_SMS("VOB (MPEG-2 video with AC-3 audio)");
00170     MPEG1or2FileServerDemux* demux
00171       = MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);
00172     sms->addSubsession(demux->newVideoServerMediaSubsession());
00173     sms->addSubsession(demux->newAC3AudioServerMediaSubsession());
00174   } else if (strcmp(extension, ".ts") == 0) {
00175     // Assumed to be a MPEG Transport Stream file:
00176     // Use an index file name that's the same as the TS file name, except with ".tsx":
00177     unsigned indexFileNameLen = strlen(fileName) + 2; // allow for trailing "x\0"
00178     char* indexFileName = new char[indexFileNameLen];
00179     sprintf(indexFileName, "%sx", fileName);
00180     NEW_SMS("MPEG Transport Stream");
00181     sms->addSubsession(MPEG2TransportFileServerMediaSubsession::createNew(env, fileName, indexFileName, reuseSource));
00182     delete[] indexFileName;
00183   } else if (strcmp(extension, ".wav") == 0) {
00184     // Assumed to be a WAV Audio file:
00185     NEW_SMS("WAV Audio Stream");
00186     // To convert 16-bit PCM data to 8-bit u-law, prior to streaming,
00187     // change the following to True:
00188     Boolean convertToULaw = False;
00189     sms->addSubsession(WAVAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, convertToULaw));
00190   } else if (strcmp(extension, ".dv") == 0) {
00191     // Assumed to be a DV Video file
00192     // First, make sure that the RTPSinks' buffers will be large enough to handle the huge size of DV frames (as big as 288000).
00193     OutPacketBuffer::maxSize = 300000;
00194 
00195     NEW_SMS("DV Video");
00196     sms->addSubsession(DVVideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
00197   } else if (strcmp(extension, ".mkv") == 0 || strcmp(extension, ".webm") == 0) {
00198     // Assumed to be a Matroska file (note that WebM ('.webm') files are also Matroska files)
00199     NEW_SMS("Matroska video+audio+(optional)subtitles");
00200 
00201     // Create a Matroska file server demultiplexor for the specified file.
00202     // (We enter the event loop to wait for this to complete.)
00203     MatroskaDemuxCreationState creationState;
00204     creationState.watchVariable = 0;
00205     MatroskaFileServerDemux::createNew(env, fileName, onMatroskaDemuxCreation, &creationState);
00206     env.taskScheduler().doEventLoop(&creationState.watchVariable);
00207 
00208     ServerMediaSubsession* smss;
00209     while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {
00210       sms->addSubsession(smss);
00211     }
00212   } else if (strcmp(extension, ".ogg") == 0 || strcmp(extension, ".ogv") == 0 || strcmp(extension, ".opus") == 0) {
00213     // Assumed to be an Ogg file
00214     NEW_SMS("Ogg video and/or audio");
00215 
00216     // Create a Ogg file server demultiplexor for the specified file.
00217     // (We enter the event loop to wait for this to complete.)
00218     OggDemuxCreationState creationState;
00219     creationState.watchVariable = 0;
00220     OggFileServerDemux::createNew(env, fileName, onOggDemuxCreation, &creationState);
00221     env.taskScheduler().doEventLoop(&creationState.watchVariable);
00222 
00223     ServerMediaSubsession* smss;
00224     while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {
00225       sms->addSubsession(smss);
00226     }
00227   }
00228 
00229   return sms;
00230 }

Generated on Wed Apr 23 16:12:01 2014 for live by  doxygen 1.5.2