Hi, >> I am currently trying to implement uncompressed video streaming according to >> RFC4175.
> Because this is likely to be something of general use (e.g., someone else was > asking about this just yesterday), I suggest posting the code that you have > so far (i.e., for your subclass of "MultiFramedRTPSink" (for sending) and > "MultiFramedRTPSource" (for receiving)) to the mailing list, and I'll take a > look at it, and see if I can clean it up to include in the LIVE555 source > code. Please find my current source code attached to this mail. The .patch file contains everything that ought to go into the library anyhow according to the license. The "UncompressedVideoRTPSink" is currently subclassed from "VideoRTPSink". The use of "1448" as fixed value for the frame size is just the current state of experimentation. In my "UncompressedVideoSink" class (that is too specific to post) I parse the SDP description string using attrVal_int("width"); attrVal_int("height"); attrVal_int("depth"); attrVal_str("sampling"); if (strcmp(samplingStr, "mono") == 0) ... and set up my player's receive buffer accordingly. Thank you for considering to add this feature. Please especially keep me updated on what I did wrong in my implementation attempt. And please, although it may not be especially mentioned in the RFC, please also consider the option for "mono" color sampling, as it is required in my case as well as for the earlier posted request on the Flir A320 camera. Regards, Marco Porsch
live555-uncompressed-video-rtp-source.patch
Description: live555-uncompressed-video-rtp-source.patch
//--------------------------------------------------------------------------------------------------------------------- /// @file UncompressedVideoRTPSink.cpp /// /// @brief implementation of UncompressedVideoRTPSink (inherits from VideoRTPSink) /// sends uncompressed video according to RFC4175 /// /// @author Marco Porsch (m.por...@intenta.de) /// @author Intenta GmbH /// /// @version 1.00 /// @date 2014-07-11 /// /// Copyright (c) Intenta GmbH 2014 //--------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------- // Includes //--------------------------------------------------------------------------------------------------------------------- #include "UncompressedVideoRTPSink.h" //--------------------------------------------------------------------------------------------------------------------- // Functions //--------------------------------------------------------------------------------------------------------------------- UncompressedVideoRTPSink* UncompressedVideoRTPSink::createNew( UsageEnvironment& env, Groupsock* rtpgs, unsigned char rtpPayloadFormat, const char* imgSampling, unsigned imgWidth, unsigned imgHeight, unsigned imgChannels, unsigned imgDepth) { return new UncompressedVideoRTPSink(env, rtpgs, rtpPayloadFormat, imgSampling, imgWidth, imgHeight, imgChannels, imgDepth); } UncompressedVideoRTPSink::UncompressedVideoRTPSink(UsageEnvironment& env, Groupsock* rtpgs, unsigned char rtpPayloadFormat, const char* imgSampling, unsigned imgWidth, unsigned imgHeight, unsigned imgChannels, unsigned imgDepth) : VideoRTPSink(env, rtpgs, rtpPayloadFormat, 90000, "raw"), // see http://tools.ietf.org/html/rfc4175 m_lineLength(imgWidth * imgChannels) { // see http://tools.ietf.org/html/rfc4175#section-7 char const* const fmt = "a=fmtp:%d sampling=%s; width=%d; height=%d; depth=%d\r\n"; sprintf(m_auxSdpLine, fmt, fRTPPayloadType, imgSampling, imgWidth, imgHeight, imgChannels * 8); } UncompressedVideoRTPSink::~UncompressedVideoRTPSink() { } unsigned getHeaderSize(unsigned fragmentationOffset, unsigned frameSize, unsigned lineLength) { const unsigned numBytesLineFragmentStart = (lineLength - (fragmentationOffset % lineLength)) % lineLength; const unsigned numBytesWholeLines = (frameSize - numBytesLineFragmentStart) / lineLength; const unsigned numBytesLineFragmentEnd = (frameSize - numBytesLineFragmentStart) % lineLength; const unsigned headerSize = 2 + 6 * ((numBytesLineFragmentStart > 0 ? 1 : 0) + numBytesWholeLines + (numBytesLineFragmentEnd > 0 ? 1 : 0)); return headerSize; } void UncompressedVideoRTPSink::doSpecialFrameHandling( unsigned fragmentationOffset, unsigned char* frameStart, unsigned numBytesInFrame, struct timeval framePresentationTime, unsigned numRemainingBytes) { const unsigned headerSize = getHeaderSize(fragmentationOffset, numBytesInFrame, m_lineLength); unsigned char* payloadHeader = new unsigned char[headerSize]; payloadHeader[0] = 0; // extended sequence number payloadHeader[1] = 0; if (numRemainingBytes == 0) { // This packet contains the last (or only) fragment of the frame. // Set the RTP 'M' ('marker') bit: setMarkerBit(); } unsigned perLineOffset = fragmentationOffset; unsigned numBytesLeftInFrame = numBytesInFrame; unsigned char* payloadLineHeader = payloadHeader + 2; while (numBytesLeftInFrame > 0) { const unsigned lineNo = perLineOffset / m_lineLength; const unsigned offset = perLineOffset % m_lineLength; const unsigned length = (m_lineLength - offset > numBytesLeftInFrame ? numBytesLeftInFrame : m_lineLength - offset); perLineOffset += length; numBytesLeftInFrame -= length; // write length payloadLineHeader[0] = (length >> 8) & 0xff; payloadLineHeader[1] = length & 0xff; const unsigned char field = 0; // we only support progressive video for now // write line no and field bit payloadLineHeader[2] = ((lineNo >> 8) & 0x7f) | ((field << 7) & 0x80); payloadLineHeader[3] = lineNo & 0xff; const unsigned char continuation = (numBytesLeftInFrame == 0 ? 0x00 : 0x80); /* write offset and continuation marker */ payloadLineHeader[4] = ((offset >> 8) & 0x7f) | continuation; payloadLineHeader[5] = offset & 0xff; payloadLineHeader += 6; } setSpecialHeaderBytes(payloadHeader, headerSize); //setFrameSpecificHeaderBytes(payloadHeader, m_lastHeaderSize); delete[] payloadHeader; // Important: Also call our base class's doSpecialFrameHandling(), // to set the packet's timestamp: MultiFramedRTPSink::doSpecialFrameHandling(fragmentationOffset, frameStart, numBytesInFrame, framePresentationTime, numRemainingBytes); if (numRemainingBytes == 0) // next frame is the first frame m_nextHeaderSize = getHeaderSize(0, 1448, m_lineLength); else if (numRemainingBytes < 1448) // next frame is the frame last frame m_nextHeaderSize = getHeaderSize(fragmentationOffset + numBytesInFrame, numRemainingBytes, m_lineLength); else // regular case m_nextHeaderSize = getHeaderSize(fragmentationOffset + numBytesInFrame, 1448, m_lineLength); } unsigned UncompressedVideoRTPSink::specialHeaderSize() const { return m_nextHeaderSize; } unsigned UncompressedVideoRTPSink::frameSpecificHeaderSize() const { return 0; } char const* UncompressedVideoRTPSink::auxSDPLine() { return m_auxSdpLine; }
//--------------------------------------------------------------------------------------------------------------------- /// @file UncompressedVideoRTPSink.h /// /// @brief definition of UncompressedVideoRTPSink (inherits from VideoRTPSink) /// sends uncompressed video according to RFC4175 /// /// @author Marco Porsch (m.por...@intenta.de) /// @author Intenta GmbH /// /// @version 1.00 /// @date 2014-07-11 /// /// Copyright (c) Intenta GmbH 2014 //--------------------------------------------------------------------------------------------------------------------- #ifndef UNCOMPRESSED_VIDEO_RTP_SINK_H #define UNCOMPRESSED_VIDEO_RTP_SINK_H //--------------------------------------------------------------------------------------------------------------------- // Includes //--------------------------------------------------------------------------------------------------------------------- #include <liveMedia.hh> //--------------------------------------------------------------------------------------------------------------------- // Classes //--------------------------------------------------------------------------------------------------------------------- class UncompressedVideoRTPSink : public VideoRTPSink { public: static UncompressedVideoRTPSink* createNew(UsageEnvironment& env, Groupsock* rtpgs, unsigned char rtpPayloadFormat, const char* imgSampling, unsigned imgWidth, unsigned imgHeight, unsigned imgChannels, unsigned imgDepth); protected: UncompressedVideoRTPSink(UsageEnvironment& env, Groupsock* rtpgs, unsigned char rtpPayloadFormat, const char* imgSampling, unsigned imgWidth, unsigned imgHeight, unsigned imgChannels, unsigned imgDepth); ~UncompressedVideoRTPSink(); private: // redefined virtual functions: virtual void doSpecialFrameHandling(unsigned fragmentationOffset, unsigned char* frameStart, unsigned numBytesInFrame, struct timeval framePresentationTime, unsigned numRemainingBytes); virtual unsigned specialHeaderSize() const; virtual unsigned frameSpecificHeaderSize() const; virtual char const* auxSDPLine(); private: unsigned m_nextHeaderSize; const unsigned m_lineLength; char m_auxSdpLine[150]; }; #endif // UNCOMPRESSED_VIDEO_RTP_SINK_H
_______________________________________________ live-devel mailing list live-devel@lists.live555.com http://lists.live555.com/mailman/listinfo/live-devel