diff --git a/inc/MJPEGVideoSource.h b/inc/MJPEGVideoSource.h new file mode 100644 index 0000000..0494b4b --- /dev/null +++ b/inc/MJPEGVideoSource.h @@ -0,0 +1,120 @@ +/* --------------------------------------------------------------------------- +** 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. +** +** MJPEGVideoSource.h +** +** V4L2 RTSP streamer +** +** +** -------------------------------------------------------------------------*/ + +#include "JPEGVideoSource.hh" + +class MJPEGVideoSource : public JPEGVideoSource +{ + public: + static MJPEGVideoSource* createNew (UsageEnvironment& env, FramedSource* source) + { + return new MJPEGVideoSource(env,source); + } + virtual void doGetNextFrame() + { + if (m_inputSource) + m_inputSource->getNextFrame(fTo, fMaxSize, afterGettingFrameSub, this, FramedSource::handleClosure, this); + } + virtual void doStopGettingFrames() + { + FramedSource::doStopGettingFrames(); + if (m_inputSource) + m_inputSource->stopGettingFrames(); + } + static void afterGettingFrameSub(void* clientData, unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned durationInMicroseconds) + { + MJPEGVideoSource* source = (MJPEGVideoSource*)clientData; + source->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds); + } + void afterGettingFrame(unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned durationInMicroseconds) + { + int headerSize = 0; + bool headerOk = false; + fFrameSize = 0; + + for (unsigned int i = 0; i < frameSize ; ++i) + { + // SOF + if ( (i+11) < frameSize && fTo[i] == 0xFF && fTo[i+1] == 0xC0 ) + { + m_height = (fTo[i+5]<<5)|(fTo[i+6]>>3); + m_width = (fTo[i+7]<<5)|(fTo[i+8]>>3); + m_type = (fTo[i+11] - 0x21); + } + // DQT + if ( (i+5+64) < frameSize && (fTo[i] == 0xFF) && (fTo[i+1] == 0xDB)) + { + int quantSize = fTo[i+3]; + int quantIdx = fTo[i+4]; + if (quantIdx < 3) + { + if ( quantIdx+1 > m_qTableCount ) + m_qTableCount = quantIdx+1; + memcpy(m_qTable + quantIdx*64, fTo + i + 5, 64); + } + } + // End of header + if ( (i+1) < frameSize && fTo[i] == 0x3F && fTo[i+1] == 0x00 ) + { + headerOk = true; + headerSize = i+2; + break; + } + } + + if (headerOk) + { + fFrameSize = frameSize - headerSize; + memmove( fTo, fTo + headerSize, fFrameSize ); + } + + fNumTruncatedBytes = numTruncatedBytes; + fPresentationTime = presentationTime; + fDurationInMicroseconds = durationInMicroseconds; + afterGetting(this); + } + virtual u_int8_t type() { return m_type; }; + virtual u_int8_t qFactor() { return 128; }; + virtual u_int8_t width() { return m_width; }; + virtual u_int8_t height() { return m_height; }; + u_int8_t const* quantizationTables( u_int8_t& precision, u_int16_t& length ) + { + length = 0; + precision = 0; + if (m_qTableCount > 0) + { + length = 64*m_qTableCount; + } + return m_qTable; + } + + protected: + MJPEGVideoSource(UsageEnvironment& env, FramedSource* source) : JPEGVideoSource(env), + m_inputSource(source), + m_width(0), m_height(0), m_qTableCount(0), + m_type(0) + { + memset(&m_qTable,0,sizeof(m_qTable)); + } + virtual ~MJPEGVideoSource() + { + Medium::close(m_inputSource); + } + + protected: + FramedSource* m_inputSource; + u_int8_t m_width; + u_int8_t m_height; + u_int8_t m_qTable[64*3]; + int m_qTableCount; + u_int8_t m_type; +}; diff --git a/src/ServerMediaSubsession.cpp b/src/ServerMediaSubsession.cpp index 09d6753..2bff538 100644 --- a/src/ServerMediaSubsession.cpp +++ b/src/ServerMediaSubsession.cpp @@ -16,6 +16,7 @@ // project #include "ServerMediaSubsession.h" +#include "MJPEGVideoSource.h" #include "V4l2DeviceSource.h" // --------------------------------- @@ -32,6 +33,10 @@ FramedSource* BaseServerMediaSubsession::createSource(UsageEnvironment& env, Fra { source = H264VideoStreamDiscreteFramer::createNew(env, videoES); } + else if (format == "video/JPEG") + { + source = MJPEGVideoSource::createNew(env, videoES); + } else { source = videoES; @@ -54,6 +59,10 @@ RTPSink* BaseServerMediaSubsession::createSink(UsageEnvironment& env, Groupsock { videoSink = VP8VideoRTPSink::createNew (env, rtpGroupsock,rtpPayloadTypeIfDynamic); } + else if (format == "video/JPEG") + { + videoSink = JPEGVideoRTPSink::createNew (env, rtpGroupsock); + } return videoSink; } diff --git a/src/main.cpp b/src/main.cpp index 9b94e58..0331397 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -126,6 +126,7 @@ std::string getRtpFormat(int format, bool muxTS) switch(format) { case V4L2_PIX_FMT_H264 : rtpFormat = "video/H264"; break; + case V4L2_PIX_FMT_JPEG : rtpFormat = "video/JPEG"; break; #ifdef V4L2_PIX_FMT_VP8 case V4L2_PIX_FMT_VP8 : rtpFormat = "video/VP8" ; break; #endif