diff --git a/inc/AddH26xMarkerFilter.h b/inc/AddH26xMarkerFilter.h new file mode 100644 index 0000000..0a5809a --- /dev/null +++ b/inc/AddH26xMarkerFilter.h @@ -0,0 +1,71 @@ +/* --------------------------------------------------------------------------- +** 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. +** +** AddH26xMarkerFilter.h +** +** -------------------------------------------------------------------------*/ + +#pragma once + +class AddH26xMarkerFilter : public FramedFilter { + public: + AddH26xMarkerFilter (UsageEnvironment& env, FramedSource* inputSource): FramedFilter(env, inputSource) { + m_bufferSize = OutPacketBuffer::maxSize; + m_buffer = new unsigned char[m_bufferSize]; + } + virtual ~AddH26xMarkerFilter () { + delete [] m_buffer; + } + + private: + + static void afterGettingFrame(void* clientData, unsigned frameSize, + unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds) { + AddH26xMarkerFilter* sink = (AddH26xMarkerFilter*)clientData; + sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime); + } + + void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) + { + fPresentationTime = presentationTime; + fDurationInMicroseconds = 0; + if (numTruncatedBytes > 0) + { + envir() << "AddH26xMarkerFilter::afterGettingFrame(): The input frame data was too large for our buffer size truncated:" << numTruncatedBytes << " bufferSize:" << m_bufferSize << "\n"; + m_bufferSize += numTruncatedBytes; + delete[] m_buffer; + m_buffer = new unsigned char[m_bufferSize]; + fFrameSize = 0; + } else { + char marker[] = {0,0,0,1}; + fFrameSize = frameSize + sizeof(marker); + if (fFrameSize > fMaxSize) { + fNumTruncatedBytes = fFrameSize - fMaxSize; + envir() << "AddH26xMarkerFilter::afterGettingFrame(): buffer too small truncated:" << fNumTruncatedBytes << " bufferSize:" << fFrameSize << "\n"; + } else { + fNumTruncatedBytes = 0; + memcpy(fTo, marker, sizeof(marker)); + memcpy(fTo+sizeof(marker), m_buffer, frameSize); + } + } + afterGetting(this); + } + + virtual void doGetNextFrame() { + if (fInputSource != NULL) + { + fInputSource->getNextFrame(m_buffer, m_bufferSize, + afterGettingFrame, this, + handleClosure, this); + } + } + + unsigned char* m_buffer; + unsigned int m_bufferSize; +}; + + diff --git a/inc/MemoryBufferSink.h b/inc/MemoryBufferSink.h new file mode 100644 index 0000000..97e1db1 --- /dev/null +++ b/inc/MemoryBufferSink.h @@ -0,0 +1,56 @@ +/* --------------------------------------------------------------------------- +** 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. +** +** MemoryBufferSink.h +** +** Implement a live555 Sink that store time slices in memory +** +** -------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include "MediaSink.hh" + +class MemoryBufferSink : public MediaSink +{ + public: + static MemoryBufferSink* createNew(UsageEnvironment& env, unsigned int bufferSize, unsigned int sliceDuration) + { + return new MemoryBufferSink(env, bufferSize, sliceDuration); + } + + protected: + MemoryBufferSink(UsageEnvironment& env, unsigned bufferSize, unsigned int sliceDuration); + virtual ~MemoryBufferSink(); + + virtual Boolean continuePlaying(); + + static void afterGettingFrame(void* clientData, unsigned frameSize, + unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds) { + MemoryBufferSink* sink = (MemoryBufferSink*)clientData; + sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime); + } + + void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime); + + public: + unsigned int getBufferSize(unsigned int slice); + std::string getBuffer(unsigned int slice); + unsigned int firstTime(); + unsigned int duration(); + unsigned int getSliceDuration() { return m_sliceDuration; } + + private: + unsigned char * m_buffer; + unsigned int m_bufferSize; + std::map m_outputBuffers; + unsigned int m_refTime; + unsigned int m_sliceDuration; +}; + diff --git a/inc/SegmentServerMediaSubsession.h b/inc/SegmentServerMediaSubsession.h deleted file mode 100644 index 9ba8aba..0000000 --- a/inc/SegmentServerMediaSubsession.h +++ /dev/null @@ -1,79 +0,0 @@ -/* --------------------------------------------------------------------------- -** 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. -** -** ServerMediaSubsession.h -** -** -------------------------------------------------------------------------*/ - -#pragma once - -#include -#include "UnicastServerMediaSubsession.h" - -// ----------------------------------------- -// ServerMediaSubsession for HLS -// ----------------------------------------- -class HLSServerMediaSubsession : public UnicastServerMediaSubsession -{ - class HLSSink : public MediaSink - { - public: - static HLSSink* createNew(UsageEnvironment& env, unsigned int bufferSize, unsigned int sliceDuration) - { - return new HLSSink(env, bufferSize, sliceDuration); - } - - protected: - HLSSink(UsageEnvironment& env, unsigned bufferSize, unsigned int sliceDuration); - virtual ~HLSSink(); - - virtual Boolean continuePlaying(); - - 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); - - public: - unsigned int getHLSBufferSize(unsigned int slice); - std::string getHLSBuffer(unsigned int slice); - unsigned int firstTime(); - unsigned int duration(); - unsigned int getSliceDuration() { return m_sliceDuration; } - - private: - unsigned char * m_buffer; - unsigned int m_bufferSize; - std::map m_outputBuffers; - unsigned int m_refTime; - unsigned int m_sliceDuration; - }; - - public: - static HLSServerMediaSubsession* createNew(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format, unsigned int sliceDuration) - { - return new HLSServerMediaSubsession(env, replicator, format, sliceDuration); - } - - protected: - HLSServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format, unsigned int sliceDuration); - virtual ~HLSServerMediaSubsession(); - - virtual float getCurrentNPT(void* streamToken); - virtual float duration() const ; - virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes); - virtual FramedSource* getStreamSource(void* streamToken); - - protected: - unsigned int m_slice; - HLSSink * m_hlsSink; -}; - - diff --git a/inc/TSServerMediaSubsession.h b/inc/TSServerMediaSubsession.h new file mode 100644 index 0000000..bcc6e5c --- /dev/null +++ b/inc/TSServerMediaSubsession.h @@ -0,0 +1,41 @@ +/* --------------------------------------------------------------------------- +** 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. +** +** TSServerMediaSubsession.h +** +** -------------------------------------------------------------------------*/ + +#pragma once + +#include +#include "UnicastServerMediaSubsession.h" +#include "MemoryBufferSink.h" + +// ----------------------------------------- +// ServerMediaSubsession for HLS +// ----------------------------------------- +class TSServerMediaSubsession : public UnicastServerMediaSubsession +{ + public: + static TSServerMediaSubsession* createNew(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format, unsigned int sliceDuration) + { + return new TSServerMediaSubsession(env, replicator, format, sliceDuration); + } + + protected: + TSServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format, unsigned int sliceDuration); + virtual ~TSServerMediaSubsession(); + + virtual float getCurrentNPT(void* streamToken); + virtual float duration() const ; + virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes); + virtual FramedSource* getStreamSource(void* streamToken); + + protected: + unsigned int m_slice; + MemoryBufferSink* m_hlsSink; +}; + + diff --git a/src/MemoryBufferSink.cpp b/src/MemoryBufferSink.cpp new file mode 100644 index 0000000..79d31b6 --- /dev/null +++ b/src/MemoryBufferSink.cpp @@ -0,0 +1,112 @@ +/* --------------------------------------------------------------------------- +** 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. +** +** MemoryBufferSink.cpp +** +** -------------------------------------------------------------------------*/ + +#include "MemoryBufferSink.h" + +// ----------------------------------------- +// MemoryBufferSink +// ----------------------------------------- +MemoryBufferSink::MemoryBufferSink(UsageEnvironment& env, unsigned bufferSize, unsigned int sliceDuration) : MediaSink(env), m_bufferSize(bufferSize), m_refTime(0), m_sliceDuration(sliceDuration) +{ + m_buffer = new unsigned char[m_bufferSize]; +} + +MemoryBufferSink::~MemoryBufferSink() +{ + delete[] m_buffer; +} + + +Boolean MemoryBufferSink::continuePlaying() +{ + Boolean ret = False; + if (fSource != NULL) + { + fSource->getNextFrame(m_buffer, m_bufferSize, + afterGettingFrame, this, + onSourceClosure, this); + ret = True; + } + return ret; +} + + +void MemoryBufferSink::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"; + // realloc a bigger buffer + m_bufferSize += numTruncatedBytes; + delete[] m_buffer; + m_buffer = new unsigned char[m_bufferSize]; + } + else + { + // 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(); +} + +unsigned int MemoryBufferSink::getBufferSize(unsigned int slice) +{ + unsigned int size = 0; + std::map::iterator it = m_outputBuffers.find(slice); + if (it != m_outputBuffers.end()) + { + size = it->second.size(); + } + return size; +} + +std::string MemoryBufferSink::getBuffer(unsigned int slice) +{ + std::string content; + std::map::iterator it = m_outputBuffers.find(slice); + if (it != m_outputBuffers.end()) + { + content = it->second; + + } + return content; +} + +unsigned int MemoryBufferSink::firstTime() +{ + unsigned int firstTime = 0; + if (m_outputBuffers.size() != 0) + { + firstTime = m_outputBuffers.begin()->first; + } + return firstTime*m_sliceDuration; +} + +unsigned int MemoryBufferSink::duration() +{ + unsigned int duration = 0; + if (m_outputBuffers.size() != 0) + { + duration = m_outputBuffers.rbegin()->first - m_outputBuffers.begin()->first; + } + return (duration)*m_sliceDuration; +} diff --git a/src/SegmentServerMediaSubsession.cpp b/src/SegmentServerMediaSubsession.cpp deleted file mode 100644 index d5676c7..0000000 --- a/src/SegmentServerMediaSubsession.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* --------------------------------------------------------------------------- -** 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. -** -** SegmentServerMediaSubsession.cpp -** -** -------------------------------------------------------------------------*/ - -#include - -#include "SegmentServerMediaSubsession.h" - -// ----------------------------------------- -// ServerMediaSubsession for HLS -// ----------------------------------------- -HLSServerMediaSubsession::HLSSink::HLSSink(UsageEnvironment& env, unsigned bufferSize, unsigned int sliceDuration) : MediaSink(env), m_bufferSize(bufferSize), m_refTime(0), m_sliceDuration(sliceDuration) -{ - m_buffer = new unsigned char[m_bufferSize]; -} - -HLSServerMediaSubsession::HLSSink::~HLSSink() -{ - delete[] m_buffer; -} - - -Boolean HLSServerMediaSubsession::HLSSink::continuePlaying() -{ - Boolean ret = False; - if (fSource != NULL) - { - fSource->getNextFrame(m_buffer, m_bufferSize, - afterGettingFrame, this, - onSourceClosure, this); - ret = True; - } - return ret; -} - - -void HLSServerMediaSubsession::HLSSink::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"; - // realloc a bigger buffer - m_bufferSize += numTruncatedBytes; - delete[] m_buffer; - m_buffer = new unsigned char[m_bufferSize]; - } - else - { - // 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(); -} - -unsigned int HLSServerMediaSubsession::HLSSink::getHLSBufferSize(unsigned int slice) -{ - unsigned int size = 0; - std::map::iterator it = m_outputBuffers.find(slice); - if (it != m_outputBuffers.end()) - { - size = it->second.size(); - } - return size; -} - -std::string HLSServerMediaSubsession::HLSSink::getHLSBuffer(unsigned int slice) -{ - std::string content; - std::map::iterator it = m_outputBuffers.find(slice); - if (it != m_outputBuffers.end()) - { - content = it->second; - - } - return content; -} - -unsigned int HLSServerMediaSubsession::HLSSink::firstTime() -{ - unsigned int firstTime = 0; - if (m_outputBuffers.size() != 0) - { - firstTime = m_outputBuffers.begin()->first; - } - return firstTime*m_sliceDuration; -} - -unsigned int HLSServerMediaSubsession::HLSSink::duration() -{ - unsigned int duration = 0; - if (m_outputBuffers.size() != 0) - { - duration = m_outputBuffers.rbegin()->first - m_outputBuffers.begin()->first; - } - return (duration)*m_sliceDuration; -} - -char marker[] = {0,0,0,1}; -class AddMarker : public FramedFilter { - public: - AddMarker (UsageEnvironment& env, FramedSource* inputSource): FramedFilter(env, inputSource) { - m_bufferSize = OutPacketBuffer::maxSize; - m_buffer = new unsigned char[m_bufferSize]; - } - virtual ~AddMarker () { - delete [] m_buffer; - } - - private: - - static void afterGettingFrame(void* clientData, unsigned frameSize, - unsigned numTruncatedBytes, - struct timeval presentationTime, - unsigned durationInMicroseconds) { - AddMarker* sink = (AddMarker*)clientData; - sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime); - } - - void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) - { - fPresentationTime = presentationTime; - fDurationInMicroseconds = 0; - if (numTruncatedBytes > 0) - { - envir() << "AddMarker::afterGettingFrame(): The input frame data was too large for our buffer size truncated:" << numTruncatedBytes << " bufferSize:" << m_bufferSize << "\n"; - m_bufferSize += numTruncatedBytes; - delete[] m_buffer; - m_buffer = new unsigned char[m_bufferSize]; - fFrameSize = 0; - } else { - fFrameSize = frameSize + sizeof(marker); - if (fFrameSize > fMaxSize) { - fNumTruncatedBytes = fFrameSize - fMaxSize; - envir() << "AddMarker::afterGettingFrame(): buffer too small truncated:" << fNumTruncatedBytes << " bufferSize:" << fFrameSize << "\n"; - } else { - fNumTruncatedBytes = 0; - memcpy(fTo, marker, sizeof(marker)); - memcpy(fTo+sizeof(marker), m_buffer, frameSize); - } - } - afterGetting(this); - } - - virtual void doGetNextFrame() { - if (fInputSource != NULL) - { - fInputSource->getNextFrame(m_buffer, m_bufferSize, - afterGettingFrame, this, - handleClosure, this); - } - } - - unsigned char* m_buffer; - unsigned int m_bufferSize; -}; - - -HLSServerMediaSubsession::HLSServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format, unsigned int sliceDuration) - : UnicastServerMediaSubsession(env, replicator, "video/MP2T"), m_slice(0) -{ - // Create a source - FramedSource* source = replicator->createStreamReplica(); - - - if (format == "video/H264") { - // add marker - FramedSource* filter = new AddMarker(env, source); - // mux to TS - MPEG2TransportStreamFromESSource* muxer = MPEG2TransportStreamFromESSource::createNew(env); - muxer->addNewVideoSource(filter, 5); - source = muxer; - } else if (format == "video/H265") { - // add marker - FramedSource* filter = new AddMarker(env, source); - // mux to TS - MPEG2TransportStreamFromESSource* muxer = MPEG2TransportStreamFromESSource::createNew(env); - muxer->addNewVideoSource(filter, 6); - source = muxer; - } - - FramedSource* videoSource = createSource(env, source, m_format); - - // Start Playing the HLS Sink - m_hlsSink = HLSSink::createNew(env, OutPacketBuffer::maxSize, sliceDuration); - m_hlsSink->startPlaying(*videoSource, NULL, NULL); -} - -HLSServerMediaSubsession::~HLSServerMediaSubsession() -{ - Medium::close(m_hlsSink); -} - -float HLSServerMediaSubsession::getCurrentNPT(void* streamToken) -{ - return (m_hlsSink->firstTime()); -} - -float HLSServerMediaSubsession::duration() const -{ - return (m_hlsSink->duration()); -} - -void HLSServerMediaSubsession::seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes) -{ - m_slice = seekNPT / m_hlsSink->getSliceDuration(); - seekNPT = m_slice * m_hlsSink->getSliceDuration(); - numBytes = m_hlsSink->getHLSBufferSize(m_slice); - std::cout << "seek seekNPT:" << seekNPT << " slice:" << m_slice << " numBytes:" << numBytes << std::endl; -} - -FramedSource* HLSServerMediaSubsession::getStreamSource(void* streamToken) -{ - FramedSource* source = NULL; - - std::string buffer = m_hlsSink->getHLSBuffer(m_slice); - unsigned int size = buffer.size(); - if ( size != 0 ) { - u_int8_t* content = new u_int8_t[size]; - memcpy(content, buffer.c_str(), size); - source = ByteStreamMemoryBufferSource::createNew(envir(), content, size); - } - return source; -} diff --git a/src/TSServerMediaSubsession.cpp b/src/TSServerMediaSubsession.cpp new file mode 100644 index 0000000..216281c --- /dev/null +++ b/src/TSServerMediaSubsession.cpp @@ -0,0 +1,78 @@ +/* --------------------------------------------------------------------------- +** 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. +** +** TSServerMediaSubsession.cpp +** +** -------------------------------------------------------------------------*/ + +#include "TSServerMediaSubsession.h" +#include "AddH26xMarkerFilter.h" + +TSServerMediaSubsession::TSServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator, const std::string& format, unsigned int sliceDuration) + : UnicastServerMediaSubsession(env, replicator, "video/MP2T"), m_slice(0) +{ + // Create a source + FramedSource* source = replicator->createStreamReplica(); + + + if (format == "video/H264") { + // add marker + FramedSource* filter = new AddH26xMarkerFilter(env, source); + // mux to TS + MPEG2TransportStreamFromESSource* muxer = MPEG2TransportStreamFromESSource::createNew(env); + muxer->addNewVideoSource(filter, 5); + source = muxer; + } else if (format == "video/H265") { + // add marker + FramedSource* filter = new AddH26xMarkerFilter(env, source); + // mux to TS + MPEG2TransportStreamFromESSource* muxer = MPEG2TransportStreamFromESSource::createNew(env); + muxer->addNewVideoSource(filter, 6); + source = muxer; + } + + FramedSource* videoSource = createSource(env, source, m_format); + + // Start Playing the HLS Sink + m_hlsSink = MemoryBufferSink::createNew(env, OutPacketBuffer::maxSize, sliceDuration); + m_hlsSink->startPlaying(*videoSource, NULL, NULL); +} + +TSServerMediaSubsession::~TSServerMediaSubsession() +{ + Medium::close(m_hlsSink); +} + +float TSServerMediaSubsession::getCurrentNPT(void* streamToken) +{ + return (m_hlsSink->firstTime()); +} + +float TSServerMediaSubsession::duration() const +{ + return (m_hlsSink->duration()); +} + +void TSServerMediaSubsession::seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes) +{ + m_slice = seekNPT / m_hlsSink->getSliceDuration(); + seekNPT = m_slice * m_hlsSink->getSliceDuration(); + numBytes = m_hlsSink->getBufferSize(m_slice); + std::cout << "seek seekNPT:" << seekNPT << " slice:" << m_slice << " numBytes:" << numBytes << std::endl; +} + +FramedSource* TSServerMediaSubsession::getStreamSource(void* streamToken) +{ + FramedSource* source = NULL; + + std::string buffer = m_hlsSink->getBuffer(m_slice); + unsigned int size = buffer.size(); + if ( size != 0 ) { + u_int8_t* content = new u_int8_t[size]; + memcpy(content, buffer.c_str(), size); + source = ByteStreamMemoryBufferSource::createNew(envir(), content, size); + } + return source; +} diff --git a/src/main.cpp b/src/main.cpp index 06b7738..040ac78 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,7 +40,7 @@ #include "ServerMediaSubsession.h" #include "UnicastServerMediaSubsession.h" #include "MulticastServerMediaSubsession.h" -#include "SegmentServerMediaSubsession.h" +#include "TSServerMediaSubsession.h" #include "HTTPServer.h" #ifdef HAVE_ALSA @@ -705,7 +705,7 @@ int main(int argc, char** argv) std::list subSession; if (videoReplicator) { - subSession.push_back(HLSServerMediaSubsession::createNew(*env, videoReplicator, rtpFormat, hlsSegment)); + subSession.push_back(TSServerMediaSubsession::createNew(*env, videoReplicator, rtpFormat, hlsSegment)); } nbSource += addSession(rtspServer, baseUrl+tsurl, subSession);