2016-05-05 14:32:40 +00:00
/* ---------------------------------------------------------------------------
* * This software is in the public domain , furnished " as is " , without technical
* * support , and with no warranty , express or implied , as to its usefulness for
* * any purpose .
* *
* * HTTPServer . h
* *
* * V4L2 RTSP streamer
* *
* * HTTP server that serves HLS & MPEG - DASH playlist and segments
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include "RTSPServer.hh"
# include "RTSPCommon.hh"
2020-04-10 13:20:05 +00:00
# include <GroupsockHelper.hh> // for "ignoreSigPipeOnSocket()"
# define TCP_STREAM_SINK_MIN_READ_SIZE 1000
# define TCP_STREAM_SINK_BUFFER_SIZE 10000
2020-05-02 14:36:00 +00:00
class TCPSink : public MediaSink {
2020-04-10 13:20:05 +00:00
public :
2020-05-02 14:36:00 +00:00
TCPSink ( UsageEnvironment & env , int socketNum ) : MediaSink ( env ) , fUnwrittenBytesStart ( 0 ) , fUnwrittenBytesEnd ( 0 ) , fInputSourceIsOpen ( False ) , fOutputSocketIsWritable ( True ) , fOutputSocketNum ( socketNum ) {
2020-04-10 13:20:05 +00:00
ignoreSigPipeOnSocket ( socketNum ) ;
}
protected :
2020-05-02 14:36:00 +00:00
virtual ~ TCPSink ( ) {
2020-04-10 13:20:05 +00:00
envir ( ) . taskScheduler ( ) . disableBackgroundHandling ( fOutputSocketNum ) ;
}
protected :
virtual Boolean continuePlaying ( ) {
fInputSourceIsOpen = fSource ! = NULL ;
processBuffer ( ) ;
return True ;
}
private :
void processBuffer ( ) {
// First, try writing data to our output socket, if we can:
if ( fOutputSocketIsWritable & & numUnwrittenBytes ( ) > 0 ) {
int numBytesWritten = send ( fOutputSocketNum , ( const char * ) & fBuffer [ fUnwrittenBytesStart ] , numUnwrittenBytes ( ) , 0 ) ;
if ( numBytesWritten < ( int ) numUnwrittenBytes ( ) ) {
// The output socket is no longer writable. Set a handler to be called when it becomes writable again.
fOutputSocketIsWritable = False ;
if ( envir ( ) . getErrno ( ) ! = EPIPE ) { // on this error, the socket might still be writable, but no longer usable
envir ( ) . taskScheduler ( ) . setBackgroundHandling ( fOutputSocketNum , SOCKET_WRITABLE , socketWritableHandler , this ) ;
}
}
if ( numBytesWritten > 0 ) {
// We wrote at least some of our data. Update our buffer pointers:
fUnwrittenBytesStart + = numBytesWritten ;
if ( fUnwrittenBytesStart > fUnwrittenBytesEnd ) fUnwrittenBytesStart = fUnwrittenBytesEnd ; // sanity check
if ( fUnwrittenBytesStart = = fUnwrittenBytesEnd & & ( ! fInputSourceIsOpen | | ! fSource - > isCurrentlyAwaitingData ( ) ) ) {
fUnwrittenBytesStart = fUnwrittenBytesEnd = 0 ; // reset the buffer to empty
}
}
}
// Then, read from our input source, if we can (& we're not already reading from it):
if ( fInputSourceIsOpen & & freeBufferSpace ( ) > = TCP_STREAM_SINK_MIN_READ_SIZE & & ! fSource - > isCurrentlyAwaitingData ( ) ) {
fSource - > getNextFrame ( & fBuffer [ fUnwrittenBytesEnd ] , freeBufferSpace ( ) , afterGettingFrame , this , ourOnSourceClosure , this ) ;
} else if ( ! fInputSourceIsOpen & & numUnwrittenBytes ( ) = = 0 ) {
// We're now done:
onSourceClosure ( ) ;
}
}
2020-05-02 14:36:00 +00:00
static void socketWritableHandler ( void * clientData , int mask ) { ( ( TCPSink * ) clientData ) - > socketWritableHandler ( ) ; }
2020-04-10 13:20:05 +00:00
void socketWritableHandler ( ) {
envir ( ) . taskScheduler ( ) . disableBackgroundHandling ( fOutputSocketNum ) ; // disable this handler until the next time it's needed
fOutputSocketIsWritable = True ;
processBuffer ( ) ;
}
static void afterGettingFrame ( void * clientData , unsigned frameSize , unsigned numTruncatedBytes ,
struct timeval presentationTime , unsigned durationInMicroseconds ) {
2020-05-02 14:36:00 +00:00
( ( TCPSink * ) clientData ) - > afterGettingFrame ( frameSize , numTruncatedBytes ) ;
2020-04-10 13:20:05 +00:00
}
void afterGettingFrame ( unsigned frameSize , unsigned numTruncatedBytes ) {
if ( numTruncatedBytes > 0 ) {
envir ( ) < < " TCPStreamSink::afterGettingFrame(): The input frame data was too large for our buffer. "
< < numTruncatedBytes
< < " bytes of trailing data was dropped! Correct this by increasing the definition of \" TCP_STREAM_SINK_BUFFER_SIZE \" in \" include/TCPStreamSink.hh \" . \n " ;
}
fUnwrittenBytesEnd + = frameSize ;
processBuffer ( ) ;
}
2020-05-02 14:36:00 +00:00
static void ourOnSourceClosure ( void * clientData ) { ( ( TCPSink * ) clientData ) - > ourOnSourceClosure ( ) ; }
2020-04-10 13:20:05 +00:00
void ourOnSourceClosure ( ) {
// The input source has closed:
fInputSourceIsOpen = False ;
processBuffer ( ) ;
}
unsigned numUnwrittenBytes ( ) const { return fUnwrittenBytesEnd - fUnwrittenBytesStart ; }
unsigned freeBufferSpace ( ) const { return TCP_STREAM_SINK_BUFFER_SIZE - fUnwrittenBytesEnd ; }
private :
unsigned char fBuffer [ TCP_STREAM_SINK_BUFFER_SIZE ] ;
unsigned fUnwrittenBytesStart , fUnwrittenBytesEnd ;
Boolean fInputSourceIsOpen , fOutputSocketIsWritable ;
int fOutputSocketNum ;
} ;
2016-05-05 14:32:40 +00:00
// ---------------------------------------------------------
// Extend RTSP server to add support for HLS and MPEG-DASH
// ---------------------------------------------------------
2020-11-27 18:36:43 +00:00
# if LIVEMEDIA_LIBRARY_VERSION_INT < 1606435200
# define SOCKETCLIENT sockaddr_in
# else
# define SOCKETCLIENT sockaddr_storage
# endif
2016-05-05 14:32:40 +00:00
class HTTPServer : public RTSPServer
{
class HTTPClientConnection : public RTSPServer : : RTSPClientConnection
{
public :
2020-11-27 18:36:43 +00:00
HTTPClientConnection ( RTSPServer & ourServer , int clientSocket , struct SOCKETCLIENT clientAddr )
2020-10-11 12:38:46 +00:00
: RTSPServer : : RTSPClientConnection ( ourServer , clientSocket , clientAddr ) , m_TCPSink ( NULL ) , m_StreamToken ( NULL ) , m_Subsession ( NULL ) , m_Source ( NULL ) {
2016-05-05 14:32:40 +00:00
}
2016-06-04 17:37:07 +00:00
virtual ~ HTTPClientConnection ( ) ;
2016-05-05 14:32:40 +00:00
private :
void sendHeader ( const char * contentType , unsigned int contentLength ) ;
2017-09-03 19:25:29 +00:00
void streamSource ( FramedSource * source ) ;
void streamSource ( const std : : string & content ) ;
2016-05-05 14:32:40 +00:00
ServerMediaSubsession * getSubsesion ( const char * urlSuffix ) ;
2018-12-08 20:34:23 +00:00
bool sendFile ( char const * urlSuffix ) ;
2016-05-21 17:02:54 +00:00
bool sendM3u8PlayList ( char const * urlSuffix ) ;
bool sendMpdPlayList ( char const * urlSuffix ) ;
virtual void handleHTTPCmd_StreamingGET ( char const * urlSuffix , char const * fullRequestStr ) ;
2017-09-03 15:44:33 +00:00
virtual void handleCmd_notFound ( ) ;
2016-05-05 14:32:40 +00:00
static void afterStreaming ( void * clientData ) ;
private :
2020-04-10 13:20:05 +00:00
static u_int32_t m_ClientSessionId ;
2020-05-02 14:36:00 +00:00
TCPSink * m_TCPSink ;
2020-04-10 13:20:05 +00:00
void * m_StreamToken ;
ServerMediaSubsession * m_Subsession ;
2020-10-11 12:38:46 +00:00
FramedSource * m_Source ;
2016-05-05 14:32:40 +00:00
} ;
public :
2019-03-30 22:06:24 +00:00
static HTTPServer * createNew ( UsageEnvironment & env , Port rtspPort , UserAuthenticationDatabase * authDatabase , unsigned reclamationTestSeconds , unsigned int hlsSegment , const std : : string webroot )
2016-05-05 14:32:40 +00:00
{
HTTPServer * httpServer = NULL ;
int ourSocket = setUpOurSocket ( env , rtspPort ) ;
if ( ourSocket ! = - 1 )
{
2019-03-30 22:06:24 +00:00
httpServer = new HTTPServer ( env , ourSocket , rtspPort , authDatabase , reclamationTestSeconds , hlsSegment , webroot ) ;
2016-05-05 14:32:40 +00:00
}
return httpServer ;
}
2019-03-30 22:06:24 +00:00
HTTPServer ( UsageEnvironment & env , int ourSocket , Port rtspPort , UserAuthenticationDatabase * authDatabase , unsigned reclamationTestSeconds , unsigned int hlsSegment , const std : : string & webroot )
: RTSPServer ( env , ourSocket , rtspPort , authDatabase , reclamationTestSeconds ) , m_hlsSegment ( hlsSegment ) , m_webroot ( webroot )
2016-05-05 14:32:40 +00:00
{
2019-03-30 23:02:07 +00:00
if ( ( ! m_webroot . empty ( ) ) & & ( * m_webroot . rend ( ) ! = ' / ' ) ) {
m_webroot + = " / " ;
}
2016-05-05 14:32:40 +00:00
}
2020-11-27 18:36:43 +00:00
RTSPServer : : RTSPClientConnection * createNewClientConnection ( int clientSocket , struct SOCKETCLIENT clientAddr )
2016-05-05 14:32:40 +00:00
{
return new HTTPClientConnection ( * this , clientSocket , clientAddr ) ;
}
2019-03-30 22:06:24 +00:00
private :
2016-05-05 14:32:40 +00:00
const unsigned int m_hlsSegment ;
2019-03-30 22:06:24 +00:00
std : : string m_webroot ;
2016-05-05 14:32:40 +00:00
} ;