continue to work on HLS using memory buffers

This commit is contained in:
mpromonet 2016-04-17 16:39:07 +00:00
parent 16ac9205d4
commit 8a593ab42f
3 changed files with 246 additions and 246 deletions

View File

@ -10,11 +10,13 @@
#ifndef SERVER_MEDIA_SUBSESSION #ifndef SERVER_MEDIA_SUBSESSION
#define SERVER_MEDIA_SUBSESSION #define SERVER_MEDIA_SUBSESSION
#include <sys/stat.h>
#include <string> #include <string>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <sys/stat.h> #include <map>
// live555 // live555
#include <liveMedia.hh> #include <liveMedia.hh>
@ -87,92 +89,136 @@ class UnicastServerMediaSubsession : public OnDemandServerMediaSubsession , publ
// ----------------------------------------- // -----------------------------------------
// ServerMediaSubsession for HLS // ServerMediaSubsession for HLS
// ----------------------------------------- // -----------------------------------------
class HLSServerMediaSubsession : public UnicastServerMediaSubsession
class HLSSink : public MediaSink
{ {
public: class HLSSink : public MediaSink
static HLSSink* createNew(UsageEnvironment& env, unsigned int bufferSize) {
{ public:
return new HLSSink(env, bufferSize); static HLSSink* createNew(UsageEnvironment& env, unsigned int bufferSize, unsigned int sliceDuration)
}
protected:
HLSSink(UsageEnvironment& env, unsigned bufferSize) : MediaSink(env), m_bufferSize(bufferSize), m_slice(0), m_firstslice(0)
{
m_buffer = new unsigned char[m_bufferSize];
}
virtual ~HLSSink()
{
delete[] m_buffer;
}
virtual Boolean continuePlaying()
{
Boolean ret = False;
if (fSource != NULL)
{ {
fSource->getNextFrame(m_buffer, m_bufferSize, return new HLSSink(env, bufferSize, sliceDuration);
afterGettingFrame, this,
onSourceClosure, this);
ret = True;
} }
return ret;
} protected:
HLSSink(UsageEnvironment& env, unsigned bufferSize, unsigned int sliceDuration) : MediaSink(env), m_bufferSize(bufferSize), m_refTime(0), m_sliceDuration(sliceDuration)
static void afterGettingFrame(void* clientData, unsigned frameSize,
unsigned numTruncatedBytes,
struct timeval presentationTime,
unsigned /*durationInMicroseconds*/)
{
HLSSink* sink = (HLSSink*)clientData;
sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
}
void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime)
{
if (numTruncatedBytes > 0)
{ {
envir() << "FileSink::afterGettingFrame(): The input frame data was too large for our buffer size \n"; m_buffer = new unsigned char[m_bufferSize];
} }
if (m_os.is_open())
virtual ~HLSSink()
{ {
if (m_slice != (presentationTime.tv_sec/10)) delete[] m_buffer;
}
virtual Boolean continuePlaying()
{
Boolean ret = False;
if (fSource != NULL)
{ {
m_os.close(); fSource->getNextFrame(m_buffer, m_bufferSize,
afterGettingFrame, this,
onSourceClosure, this);
ret = True;
} }
return ret;
} }
if (!m_os.is_open())
static void afterGettingFrame(void* clientData, unsigned frameSize,
unsigned numTruncatedBytes,
struct timeval presentationTime,
unsigned durationInMicroseconds)
{ {
m_slice = presentationTime.tv_sec/10; HLSSink* sink = (HLSSink*)clientData;
if (m_firstslice == 0) sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
}
void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime)
{
if (numTruncatedBytes > 0)
{ {
m_firstslice = m_slice; envir() << "FileSink::afterGettingFrame(): The input frame data was too large for our buffer size \n";
// realloc a bigger buffer
m_bufferSize += numTruncatedBytes;
delete[] m_buffer;
m_buffer = new unsigned char[m_bufferSize];
} }
std::ostringstream os; else
os << m_slice << ".ts"; {
m_os.open(os.str().c_str()); // append buffer to slice buffer
if (m_refTime == 0)
{
m_refTime = presentationTime.tv_sec;
}
unsigned int slice = (presentationTime.tv_sec-m_refTime)/m_sliceDuration;
std::string& outputBuffer = m_outputBuffers[slice];
outputBuffer.append((const char*)m_buffer, frameSize);
// remove old buffers
while (m_outputBuffers.size()>5)
{
m_outputBuffers.erase(m_outputBuffers.begin());
}
}
continuePlaying();
} }
if (m_os.is_open())
public:
unsigned int getHLSBufferSize(unsigned int slice)
{ {
m_os.write((char*)m_buffer, frameSize); unsigned int size = 0;
std::map<unsigned int,std::string>::iterator it = m_outputBuffers.find(slice);
if (it != m_outputBuffers.end())
{
size = it->second.size();
}
return size;
} }
continuePlaying(); const char* getHLSBuffer(unsigned int slice)
} {
const char* content = NULL;
private: std::map<unsigned int,std::string>::iterator it = m_outputBuffers.find(slice);
unsigned char * m_buffer; if (it != m_outputBuffers.end())
unsigned int m_bufferSize; {
std::ofstream m_os; content = it->second.c_str();
public: }
unsigned int m_slice; return content;
unsigned int m_firstslice; }
};
unsigned int firstTime()
class HLSServerMediaSubsession : public OnDemandServerMediaSubsession , public BaseServerMediaSubsession {
{ unsigned int firstTime = 0;
if (m_outputBuffers.size() != 0)
{
firstTime = m_outputBuffers.begin()->first;
}
return firstTime*m_sliceDuration;
}
unsigned int duration()
{
unsigned int duration = 0;
if (m_outputBuffers.size() != 0)
{
duration = m_outputBuffers.rbegin()->first - m_outputBuffers.begin()->first;
}
return (duration)*m_sliceDuration;
}
unsigned int getSliceDuration()
{
return m_sliceDuration;
}
private:
unsigned char * m_buffer;
unsigned int m_bufferSize;
std::map<unsigned int,std::string> m_outputBuffers;
unsigned int m_refTime;
unsigned int m_sliceDuration;
};
public: public:
static HLSServerMediaSubsession* createNew(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format) static HLSServerMediaSubsession* createNew(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format)
{ {
@ -181,57 +227,48 @@ class HLSServerMediaSubsession : public OnDemandServerMediaSubsession , public B
protected: protected:
HLSServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format) HLSServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format)
: OnDemandServerMediaSubsession(env, False), BaseServerMediaSubsession(replicator), m_format(format) : UnicastServerMediaSubsession(env, replicator, format)
{ {
// Create a source // Create a source
FramedSource* source = replicator->createStreamReplica(); FramedSource* source = replicator->createStreamReplica();
FramedSource* videoSource = createSource(env, source, format); FramedSource* videoSource = createSource(env, source, format);
// Start Playing the Sink // Start Playing the HLS Sink
m_videoSink = HLSSink::createNew(env, 65535); m_hlsSink = HLSSink::createNew(env, OutPacketBuffer::maxSize, 10);
m_videoSink->startPlaying(*videoSource, NULL, NULL); m_hlsSink->startPlaying(*videoSource, NULL, NULL);
}
virtual ~HLSServerMediaSubsession()
{
Medium::close(m_hlsSink);
} }
virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) virtual float getCurrentNPT(void* streamToken)
{ {
FramedSource* source = m_replicator->createStreamReplica(); return (m_hlsSink->firstTime());
return createSource(envir(), source, m_format);
}
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource)
{
return createSink(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, m_format);
} }
virtual float duration() const
virtual char const* getAuxSDPLine(RTPSink* rtpSink,FramedSource* inputSource); {
virtual float duration() const { return (m_hlsSink->duration());
std::cout << "duration " << (m_videoSink->m_slice - m_videoSink->m_firstslice)*10 << std::endl;
return (m_videoSink->m_slice - m_videoSink->m_firstslice)*10;
} }
virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes) virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes)
{ {
m_slice = seekNPT / 10; m_slice = seekNPT / m_hlsSink->getSliceDuration();
seekNPT = m_slice*10; seekNPT = m_slice * m_hlsSink->getSliceDuration();
std::ostringstream os; numBytes = m_hlsSink->getHLSBufferSize(m_slice);
os << m_slice+m_videoSink->m_firstslice << ".ts"; std::cout << "seek seekNPT:" << seekNPT << " slice:" << m_slice << " numBytes:" << numBytes << std::endl;
struct stat sb;
int statResult = stat(os.str().c_str(), &sb);
if (statResult == 0)
{
numBytes = sb.st_size;
}
} }
virtual FramedSource* getStreamSource(void* streamToken) virtual FramedSource* getStreamSource(void* streamToken)
{ {
std::ostringstream os; unsigned int size = m_hlsSink->getHLSBufferSize(m_slice);
os << m_slice+m_videoSink->m_firstslice << ".ts"; u_int8_t* content = new u_int8_t[size];
return ByteStreamFileSource::createNew(envir(), os.str().c_str()); memcpy(content, m_hlsSink->getHLSBuffer(m_slice), size);
return ByteStreamMemoryBufferSource::createNew(envir(), content, size);
} }
protected: protected:
const std::string m_format;
unsigned int m_slice; unsigned int m_slice;
HLSSink * m_videoSink; HLSSink * m_hlsSink;
}; };
#endif #endif

View File

@ -145,10 +145,3 @@ char const* UnicastServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink,FramedS
return this->getAuxLine(dynamic_cast<V4L2DeviceSource*>(m_replicator->inputSource()), rtpSink->rtpPayloadType()); return this->getAuxLine(dynamic_cast<V4L2DeviceSource*>(m_replicator->inputSource()), rtpSink->rtpPayloadType());
} }
// -----------------------------------------
// ServerMediaSubsession for Unicast
// -----------------------------------------
char const* HLSServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink,FramedSource* inputSource)
{
return this->getAuxLine(dynamic_cast<V4L2DeviceSource*>(m_replicator->inputSource()), rtpSink->rtpPayloadType());
}

View File

@ -13,20 +13,17 @@
** -------------------------------------------------------------------------*/ ** -------------------------------------------------------------------------*/
#include <iostream> #include <sstream>
#include "RTSPServer.hh" #include "RTSPServer.hh"
#include "RTSPCommon.hh" #include "RTSPCommon.hh"
#include <sys/stat.h>
#include <time.h> #include <time.h>
#ifndef _BYTE_STREAM_MEMORY_BUFFER_SOURCE_HH
#include "ByteStreamMemoryBufferSource.hh" #include "ByteStreamMemoryBufferSource.hh"
#endif
#ifndef _TCP_STREAM_SINK_HH
#include "TCPStreamSink.hh" #include "TCPStreamSink.hh"
#endif
// -----------------------------------------
// RTSP server supporting live HLS
// -----------------------------------------
class HLSServer : public RTSPServer class HLSServer : public RTSPServer
{ {
@ -34,102 +31,100 @@ class HLSServer : public RTSPServer
{ {
public: public:
HLSClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr) HLSClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
: RTSPServer::RTSPClientConnection(ourServer, clientSocket, clientAddr), fClientSessionId(0), fStreamSource(NULL), fPlaylistSource(NULL), fTCPSink(NULL) { : RTSPServer::RTSPClientConnection(ourServer, clientSocket, clientAddr), fClientSessionId(0), fTCPSink(NULL) {
} }
~HLSClientConnection() { ~HLSClientConnection() {
if (fTCPSink != NULL) fTCPSink->stopPlaying(); if (fTCPSink != NULL)
Medium::close(fPlaylistSource); {
Medium::close(fStreamSource); fTCPSink->stopPlaying();
Medium::close(fTCPSink); Medium::close(fTCPSink);
}
} }
private: private:
void sendPlayList(char const* urlSuffix) void sendHeader(const char* contentType, unsigned int contentLength)
{ {
// First, make sure that the named file exists, and is streamable: // Construct our response:
ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix); snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
if (session == NULL) {
std::cout << "============no session==============" << std::endl;
handleHTTPCmd_notFound();
return;
}
// To be able to construct a playlist for the requested file, we need to know its duration:
float duration = session->duration();
if (duration <= 0.0) {
std::cout << "============no duration==============" << std::endl;
handleHTTPCmd_notSupported();
return;
}
// Now, construct the playlist. It will consist of a prefix, one or more media file specifications, and a suffix:
unsigned const maxIntLen = 10; // >= the maximum possible strlen() of an integer in the playlist
char const* const playlistPrefixFmt =
"#EXTM3U\r\n"
"#EXT-X-ALLOW-CACHE:YES\r\n"
"#EXT-X-MEDIA-SEQUENCE:%d\r\n"
"#EXT-X-TARGETDURATION:%d\r\n";
unsigned const playlistPrefixFmt_maxLen = strlen(playlistPrefixFmt) + maxIntLen;
char const* const playlistMediaFileSpecFmt =
"#EXTINF:%d,\r\n"
"%s?segment=%d,%d\r\n";
unsigned const playlistMediaFileSpecFmt_maxLen = strlen(playlistMediaFileSpecFmt) + maxIntLen + strlen(urlSuffix) + 2*maxIntLen;
// Figure out the 'target duration' that will produce a playlist that will fit in our response buffer. (But make it at least 10s.)
unsigned const playlistMaxSize = 10000;
unsigned const mediaFileSpecsMaxSize = playlistMaxSize - (playlistPrefixFmt_maxLen /*+ playlistSuffixFmt_maxLen*/);
unsigned const maxNumMediaFileSpecs = mediaFileSpecsMaxSize/playlistMediaFileSpecFmt_maxLen;
unsigned targetDuration = (unsigned)(duration/maxNumMediaFileSpecs + 1);
if (targetDuration < 10) targetDuration = 10;
unsigned int startTime = 0;
char* playlist = new char[playlistMaxSize];
char* s = playlist;
sprintf(s, playlistPrefixFmt, startTime,targetDuration);
s += strlen(s);
unsigned durSoFar = startTime;
while (1) {
unsigned dur = targetDuration < duration ? targetDuration : (unsigned)duration;
duration -= dur;
sprintf(s, playlistMediaFileSpecFmt, dur, urlSuffix, durSoFar, dur);
s += strlen(s);
if (duration < 1.0) break;
durSoFar += dur;
}
unsigned playlistLen = s - playlist;
// Construct our response:
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"HTTP/1.1 200 OK\r\n" "HTTP/1.1 200 OK\r\n"
"%s" "%s"
"Server: LIVE555 Streaming Media v%s\r\n" "Server: LIVE555 Streaming Media v%s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"Content-Type: application/vnd.apple.mpegurl\r\n"
"\r\n", "\r\n",
dateHeader(), dateHeader(),
LIVEMEDIA_LIBRARY_VERSION_STRING, LIVEMEDIA_LIBRARY_VERSION_STRING,
playlistLen); contentType,
contentLength);
// Send the response header now, because we're about to add more data (the playlist): // Send the response header
send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0); send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
fResponseBuffer[0] = '\0'; // We've already sent the response. This tells the calling code not to send it again. fResponseBuffer[0] = '\0'; // We've already sent the response. This tells the calling code not to send it again.
}
void streamSource(FramedSource* source)
{
if (fTCPSink != NULL)
{
fTCPSink->stopPlaying();
Medium::close(fTCPSink);
fTCPSink = NULL;
}
if (source != NULL)
{
fTCPSink = TCPStreamSink::createNew(envir(), fClientOutputSocket);
fTCPSink->startPlaying(*source, afterStreaming, this);
}
}
void sendPlayList(char const* urlSuffix)
{
// First, make sure that the named file exists, and is streamable:
ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix);
if (session == NULL) {
handleHTTPCmd_notFound();
return;
}
// Then, send the playlist. Because it's large, we don't do so using "send()", because that might not send it all at once. // To be able to construct a playlist for the requested file, we need to know its duration:
// Instead, we stream the playlist over the TCP socket: float duration = session->duration();
if (fPlaylistSource != NULL) { // sanity check if (duration <= 0.0) {
if (fTCPSink != NULL) fTCPSink->stopPlaying(); handleHTTPCmd_notSupported();
Medium::close(fPlaylistSource); return;
} }
fPlaylistSource = ByteStreamMemoryBufferSource::createNew(envir(), (u_int8_t*)playlist, playlistLen);
if (fTCPSink == NULL) fTCPSink = TCPStreamSink::createNew(envir(), fClientOutputSocket); ServerMediaSubsessionIterator iter(*session);
fTCPSink->startPlaying(*fPlaylistSource, afterStreaming, this); ServerMediaSubsession* subsession = iter.next();
if (subsession == NULL) {
handleHTTPCmd_notSupported();
return;
}
unsigned int startTime = subsession->getCurrentNPT(NULL);
unsigned sliceDuration = 10;
std::ostringstream os;
os << "#EXTM3U\r\n"
<< "#EXT-X-ALLOW-CACHE:YES\r\n"
<< "#EXT-X-MEDIA-SEQUENCE:" << startTime << "\r\n"
<< "#EXT-X-TARGETDURATION:" << sliceDuration << "\r\n";
for (unsigned int slice=0; slice*sliceDuration<duration; slice++)
{
os << "#EXTINF:" << sliceDuration << ",\r\n";
os << urlSuffix << "?segment=" << (startTime+slice*sliceDuration) << "\r\n";
}
const std::string& playList(os.str());
// send response header
this->sendHeader("application/vnd.apple.mpegurl", playList.size());
// stream body
u_int8_t* playListBuffer = new u_int8_t[playList.size()];
memcpy(playListBuffer, playList.c_str(), playList.size());
this->streamSource(ByteStreamMemoryBufferSource::createNew(envir(), playListBuffer, playList.size()));
} }
void handleHTTPCmd_StreamingGET(char const* urlSuffix, char const* /*fullRequestStr*/) { void handleHTTPCmd_StreamingGET(char const* urlSuffix, char const* /*fullRequestStr*/) {
@ -138,8 +133,8 @@ class HLSServer : public RTSPServer
do { do {
char const* questionMarkPos = strrchr(urlSuffix, '?'); char const* questionMarkPos = strrchr(urlSuffix, '?');
if (questionMarkPos == NULL) break; if (questionMarkPos == NULL) break;
unsigned offsetInSeconds, durationInSeconds; unsigned offsetInSeconds;
if (sscanf(questionMarkPos, "?segment=%u,%u", &offsetInSeconds, &durationInSeconds) != 2) break; if (sscanf(questionMarkPos, "?segment=%u", &offsetInSeconds) != 1) break;
char* streamName = strDup(urlSuffix); char* streamName = strDup(urlSuffix);
streamName[questionMarkPos-urlSuffix] = '\0'; streamName[questionMarkPos-urlSuffix] = '\0';
@ -173,42 +168,21 @@ class HLSServer : public RTSPServer
// Seek the stream source to the desired place, with the desired duration, and (as a side effect) get the number of bytes: // Seek the stream source to the desired place, with the desired duration, and (as a side effect) get the number of bytes:
double dOffsetInSeconds = (double)offsetInSeconds; double dOffsetInSeconds = (double)offsetInSeconds;
u_int64_t numBytes; u_int64_t numBytes = 0;
subsession->seekStream(fClientSessionId, streamToken, dOffsetInSeconds, (double)durationInSeconds, numBytes); subsession->seekStream(fClientSessionId, streamToken, dOffsetInSeconds, 0.0, numBytes);
unsigned numTSBytesToStream = (unsigned)numBytes;
std::cout << "numTSBytesToStream:" << numTSBytesToStream << std::endl;
if (numTSBytesToStream == 0) { if (numBytes == 0) {
// For some reason, we do not know the size of the requested range. We can't handle this request: // For some reason, we do not know the size of the requested range. We can't handle this request:
handleHTTPCmd_notSupported(); handleHTTPCmd_notSupported();
break; break;
} }
// Construct our response: // send response header
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, this->sendHeader("video/mp2t", numBytes);
"HTTP/1.1 200 OK\r\n"
"%s"
"Server: LIVE555 Streaming Media v%s\r\n"
"Content-Length: %d\r\n"
"Content-Type: text/plain; charset=ISO-8859-1\r\n"
"\r\n",
dateHeader(),
LIVEMEDIA_LIBRARY_VERSION_STRING,
numTSBytesToStream);
// Send the response now, because we're about to add more data (from the source):
send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
fResponseBuffer[0] = '\0'; // We've already sent the response. This tells the calling code not to send it again.
// Ask the media source to deliver - to the TCP sink - the desired data: // stream body
if (fStreamSource != NULL) { // sanity check this->streamSource(subsession->getStreamSource(streamToken));
if (fTCPSink != NULL) fTCPSink->stopPlaying();
Medium::close(fStreamSource);
}
fStreamSource = subsession->getStreamSource(streamToken);
if (fStreamSource != NULL) {
if (fTCPSink == NULL) fTCPSink = TCPStreamSink::createNew(envir(), fClientOutputSocket);
fTCPSink->startPlaying(*fStreamSource, afterStreaming, this);
}
} while(0); } while(0);
delete[] streamName; delete[] streamName;
@ -218,8 +192,8 @@ class HLSServer : public RTSPServer
this->sendPlayList(urlSuffix); this->sendPlayList(urlSuffix);
} }
static void afterStreaming(void* clientData) { static void afterStreaming(void* clientData)
std::cout << "afterStreaming" << std::endl; {
HLSServer::HLSClientConnection* clientConnection = (HLSServer::HLSClientConnection*)clientData; HLSServer::HLSClientConnection* clientConnection = (HLSServer::HLSClientConnection*)clientData;
// Arrange to delete the 'client connection' object: // Arrange to delete the 'client connection' object:
if (clientConnection->fRecursionCount > 0) { if (clientConnection->fRecursionCount > 0) {
@ -227,33 +201,27 @@ class HLSServer : public RTSPServer
clientConnection->fIsActive = False; // will cause the object to get deleted at the end of handling the request clientConnection->fIsActive = False; // will cause the object to get deleted at the end of handling the request
} else { } else {
// We're no longer handling a request; delete the object now: // We're no longer handling a request; delete the object now:
// delete clientConnection; delete clientConnection;
} }
} }
private: private:
u_int32_t fClientSessionId; u_int32_t fClientSessionId;
FramedSource* fStreamSource;
ByteStreamMemoryBufferSource* fPlaylistSource;
TCPStreamSink* fTCPSink; TCPStreamSink* fTCPSink;
}; };
public: public:
static HLSServer* createNew(UsageEnvironment& env, Port rtspPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) { static HLSServer* createNew(UsageEnvironment& env, Port rtspPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) {
int ourSocket = setUpOurSocket(env, rtspPort); int ourSocket = setUpOurSocket(env, rtspPort);
if (ourSocket == -1) return NULL; if (ourSocket == -1) return NULL;
return new HLSServer(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds);
return new HLSServer(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds);
} }
HLSServer(UsageEnvironment& env, int ourSocket, Port rtspPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) HLSServer(UsageEnvironment& env, int ourSocket, Port rtspPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds)
: RTSPServer(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds) { : RTSPServer(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds) {
} }
virtual ~HLSServer() {
}
RTSPServer::RTSPClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) { RTSPServer::RTSPClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) {
return new HLSClientConnection(*this, clientSocket, clientAddr); return new HLSClientConnection(*this, clientSocket, clientAddr);
} }
}; };
@ -561,12 +529,14 @@ int main(int argc, char** argv)
rtcpPortNum+=2; rtcpPortNum+=2;
} }
// Create Unicast Session // Create Unicast Session
addSession(rtspServer, baseUrl+url, UnicastServerMediaSubsession::createNew(*env,replicator,rtpFormat));
if (muxTS) if (muxTS)
{ {
addSession(rtspServer, "hls", HLSServerMediaSubsession::createNew(*env,replicator,rtpFormat)); addSession(rtspServer, baseUrl+url, HLSServerMediaSubsession::createNew(*env,replicator,rtpFormat));
}
else
{
addSession(rtspServer, baseUrl+url, UnicastServerMediaSubsession::createNew(*env,replicator,rtpFormat));
} }
} }
if (out) if (out)