00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "RTSPServerSupportingHTTPStreaming.hh"
00022 #include "RTSPCommon.hh"
00023 #include <sys/stat.h>
00024 #include <time.h>
00025
00026 RTSPServerSupportingHTTPStreaming*
00027 RTSPServerSupportingHTTPStreaming::createNew(UsageEnvironment& env, Port rtspPort,
00028 UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) {
00029 int ourSocket = setUpOurSocket(env, rtspPort);
00030 if (ourSocket == -1) return NULL;
00031
00032 return new RTSPServerSupportingHTTPStreaming(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds);
00033 }
00034
00035 RTSPServerSupportingHTTPStreaming
00036 ::RTSPServerSupportingHTTPStreaming(UsageEnvironment& env, int ourSocket, Port rtspPort,
00037 UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds)
00038 : RTSPServer(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds) {
00039 }
00040
00041 RTSPServerSupportingHTTPStreaming::~RTSPServerSupportingHTTPStreaming() {
00042 }
00043
00044 RTSPServer::RTSPClientSession*
00045 RTSPServerSupportingHTTPStreaming::createNewClientSession(unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr) {
00046 return new RTSPClientSessionSupportingHTTPStreaming(*this, sessionId, clientSocket, clientAddr);
00047 }
00048
00049 RTSPServerSupportingHTTPStreaming::RTSPClientSessionSupportingHTTPStreaming
00050 ::RTSPClientSessionSupportingHTTPStreaming(RTSPServer& ourServer, unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr)
00051 : RTSPClientSession(ourServer, sessionId, clientSocket, clientAddr),
00052 fPlaylistSource(NULL), fTCPSink(NULL) {
00053 }
00054
00055 RTSPServerSupportingHTTPStreaming::RTSPClientSessionSupportingHTTPStreaming::~RTSPClientSessionSupportingHTTPStreaming() {
00056 Medium::close(fPlaylistSource);
00057 Medium::close(fTCPSink);
00058 }
00059
00060 static char const* lastModifiedHeader(char const* fileName) {
00061 static char buf[200];
00062
00063 struct stat sb;
00064 int statResult = stat(fileName, &sb);
00065 if (statResult != 0) {
00066
00067 buf[0] = '\0';
00068 } else {
00069 strftime(buf, sizeof buf, "Last-Modified: %a, %b %d %Y %H:%M:%S GMT\r\n", gmtime((const time_t*)&sb.st_mtime));
00070 }
00071
00072 return buf;
00073 }
00074
00075 void RTSPServerSupportingHTTPStreaming::RTSPClientSessionSupportingHTTPStreaming
00076 ::handleHTTPCmd_StreamingGET(char const* urlSuffix, char const* ) {
00077
00078
00079 do {
00080 char const* questionMarkPos = strrchr(urlSuffix, '?');
00081 if (questionMarkPos == NULL) break;
00082 unsigned offsetInSeconds, durationInSeconds;
00083 if (sscanf(questionMarkPos, "?segment=%u,%u", &offsetInSeconds, &durationInSeconds) != 2) break;
00084
00085 char* streamName = strDup(urlSuffix);
00086 streamName[questionMarkPos-urlSuffix] = '\0';
00087
00088 do {
00089 ServerMediaSession* session = fOurServer.lookupServerMediaSession(streamName);
00090 if (session == NULL) {
00091 handleHTTPCmd_notFound();
00092 break;
00093 }
00094
00095
00096
00097 ServerMediaSubsessionIterator iter(*session);
00098 ServerMediaSubsession* subsession = iter.next();
00099 if (subsession == NULL) {
00100
00101 handleHTTPCmd_notFound();
00102 break;
00103 }
00104
00105
00106
00107 Port clientRTPPort(0), clientRTCPPort(0), serverRTPPort(0), serverRTCPPort(0);
00108 netAddressBits destinationAddress = 0;
00109 u_int8_t destinationTTL = 0;
00110 Boolean isMulticast = False;
00111 void* streamToken;
00112 subsession->getStreamParameters(fOurSessionId, 0, clientRTPPort,clientRTCPPort, 0,0,0, destinationAddress,destinationTTL, isMulticast, serverRTPPort,serverRTCPPort, streamToken);
00113
00114
00115 reclaimStreamStates();
00116 fNumStreamStates = 1;
00117 fStreamStates = new struct streamState[fNumStreamStates];
00118 fStreamStates[0].subsession = subsession;
00119 fStreamStates[0].streamToken = streamToken;
00120
00121
00122 double dOffsetInSeconds = (double)offsetInSeconds;
00123 u_int64_t numBytes;
00124 subsession->seekStream(fOurSessionId, streamToken, dOffsetInSeconds, (double)durationInSeconds, numBytes);
00125 unsigned numTSBytesToStream = (unsigned)numBytes;
00126
00127 if (numTSBytesToStream == 0) {
00128
00129 handleHTTPCmd_notSupported();
00130 break;
00131 }
00132
00133
00134 snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00135 "HTTP/1.1 200 OK\r\n"
00136 "%s"
00137 "Server: LIVE555 Streaming Media v%s\r\n"
00138 "%s"
00139 "Content-Length: %d\r\n"
00140 "Content-Type: text/plain; charset=ISO-8859-1\r\n"
00141 "\r\n",
00142 dateHeader(),
00143 LIVEMEDIA_LIBRARY_VERSION_STRING,
00144 lastModifiedHeader(streamName),
00145 numTSBytesToStream);
00146
00147 send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
00148 fResponseBuffer[0] = '\0';
00149
00150
00151 FramedSource* mediaSource = subsession->getStreamSource(streamToken);
00152 if (mediaSource != NULL) {
00153 if (fTCPSink == NULL) fTCPSink = TCPStreamSink::createNew(envir(), fClientOutputSocket);
00154 fTCPSink->startPlaying(*mediaSource, afterStreaming, this);
00155 }
00156 } while(0);
00157
00158 delete[] streamName;
00159 return;
00160 } while (0);
00161
00162
00163
00164
00165
00166 ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix);
00167 if (session == NULL) {
00168 handleHTTPCmd_notFound();
00169 return;
00170 }
00171
00172
00173 float duration = session->duration();
00174 if (duration <= 0.0) {
00175
00176 handleHTTPCmd_notSupported();
00177 return;
00178 }
00179
00180
00181 unsigned const maxIntLen = 10;
00182 char const* const playlistPrefixFmt =
00183 "#EXTM3U\r\n"
00184 "#EXT-X-ALLOW-CACHE:YES\r\n"
00185 "#EXT-X-MEDIA-SEQUENCE:0\r\n"
00186 "#EXT-X-TARGETDURATION:%d\r\n";
00187 unsigned const playlistPrefixFmt_maxLen = strlen(playlistPrefixFmt) + maxIntLen;
00188
00189 char const* const playlistMediaFileSpecFmt =
00190 "#EXTINF:%d,\r\n"
00191 "%s?segment=%d,%d\r\n";
00192 unsigned const playlistMediaFileSpecFmt_maxLen = strlen(playlistMediaFileSpecFmt) + maxIntLen + strlen(urlSuffix) + 2*maxIntLen;
00193
00194 char const* const playlistSuffixFmt =
00195 "#EXT-X-ENDLIST\r\n";
00196 unsigned const playlistSuffixFmt_maxLen = strlen(playlistSuffixFmt);
00197
00198
00199 unsigned const playlistMaxSize = 10000;
00200 unsigned const mediaFileSpecsMaxSize = playlistMaxSize - (playlistPrefixFmt_maxLen + playlistSuffixFmt_maxLen);
00201 unsigned const maxNumMediaFileSpecs = mediaFileSpecsMaxSize/playlistMediaFileSpecFmt_maxLen;
00202
00203 unsigned targetDuration = (unsigned)(duration/maxNumMediaFileSpecs + 1);
00204 if (targetDuration < 10) targetDuration = 10;
00205
00206 char* playlist = new char[playlistMaxSize];
00207 char* s = playlist;
00208 sprintf(s, playlistPrefixFmt, targetDuration);
00209 s += strlen(s);
00210
00211 unsigned durSoFar = 0;
00212 while (1) {
00213 unsigned dur = targetDuration < duration ? targetDuration : (unsigned)duration;
00214 duration -= dur;
00215 sprintf(s, playlistMediaFileSpecFmt, dur, urlSuffix, durSoFar, dur);
00216 s += strlen(s);
00217 if (duration < 1.0) break;
00218
00219 durSoFar += dur;
00220 }
00221
00222 sprintf(s, playlistSuffixFmt);
00223 s += strlen(s);
00224 unsigned playlistLen = s - playlist;
00225
00226
00227 snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00228 "HTTP/1.1 200 OK\r\n"
00229 "%s"
00230 "Server: LIVE555 Streaming Media v%s\r\n"
00231 "%s"
00232 "Content-Length: %d\r\n"
00233 "Content-Type: application/vnd.apple.mpegurl\r\n"
00234 "\r\n",
00235 dateHeader(),
00236 LIVEMEDIA_LIBRARY_VERSION_STRING,
00237 lastModifiedHeader(urlSuffix),
00238 playlistLen);
00239
00240
00241 send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
00242 fResponseBuffer[0] = '\0';
00243
00244
00245
00246 if (fPlaylistSource != NULL) {
00247 if (fTCPSink != NULL) fTCPSink->stopPlaying();
00248 Medium::close(fPlaylistSource);
00249 }
00250 fPlaylistSource = ByteStreamMemoryBufferSource::createNew(envir(), (u_int8_t*)playlist, playlistLen);
00251 if (fTCPSink == NULL) fTCPSink = TCPStreamSink::createNew(envir(), fClientOutputSocket);
00252 fTCPSink->startPlaying(*fPlaylistSource, afterStreaming, this);
00253 }
00254
00255 void RTSPServerSupportingHTTPStreaming::RTSPClientSessionSupportingHTTPStreaming::afterStreaming(void* clientData) {
00256 RTSPServerSupportingHTTPStreaming::RTSPClientSessionSupportingHTTPStreaming* clientSession
00257 = (RTSPServerSupportingHTTPStreaming::RTSPClientSessionSupportingHTTPStreaming*)clientData;
00258 delete clientSession;
00259 }