extract memory buffer from HLS mediasubsession

pull/120/head
mpromonet 6 years ago
parent fbdc2bc710
commit 7cd878c6db

@ -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;
};

@ -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 <map>
#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<unsigned int,std::string> m_outputBuffers;
unsigned int m_refTime;
unsigned int m_sliceDuration;
};

@ -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 <map>
#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<unsigned int,std::string> 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;
};

@ -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 <map>
#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;
};

@ -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<unsigned int,std::string>::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<unsigned int,std::string>::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;
}

@ -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 <map>
#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<unsigned int,std::string>::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<unsigned int,std::string>::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;
}

@ -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;
}

@ -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<ServerMediaSubsession*> 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);

Loading…
Cancel
Save