/********** This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. (See .) This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********/ // "liveMedia" // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. // Raw Video RTP Sources (RFC 4175) // Implementation #include "RawVideoRTPSource.hh" ////////// RawVideoBufferedPacket and RawVideoBufferedPacketFactory ////////// class RawVideoBufferedPacket: public BufferedPacket { public: RawVideoBufferedPacket(RawVideoRTPSource* ourSource); virtual ~RawVideoBufferedPacket(); private: // redefined virtual functions virtual void getNextEnclosedFrameParameters(unsigned char*& framePtr, unsigned dataSize, unsigned& frameSize, unsigned& frameDurationInMicroseconds); private: RawVideoRTPSource* fOurSource; }; class RawVideoBufferedPacketFactory: public BufferedPacketFactory { private: // redefined virtual functions virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource); }; ////////// LineHeader ////////// struct LineHeader { u_int16_t length; u_int16_t fieldIdAndLineNumber; u_int16_t offsetWithinLine; }; ///////// RawVideoRTPSource implementation (RFC 4175) //////// RawVideoRTPSource* RawVideoRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, unsigned rtpTimestampFrequency) { return new RawVideoRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency); } RawVideoRTPSource ::RawVideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, unsigned rtpTimestampFrequency) : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, new RawVideoBufferedPacketFactory), fNumLines(0), fNextLine(0), fLineHeaders(NULL) { } RawVideoRTPSource::~RawVideoRTPSource() { delete[] fLineHeaders; } u_int16_t RawVideoRTPSource::currentLineNumber() const { if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon! return fLineHeaders[fNextLine-1].fieldIdAndLineNumber&0x7FFF; } u_int8_t RawVideoRTPSource::currentLineFieldId() const { if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon! return (fLineHeaders[fNextLine-1].fieldIdAndLineNumber&0x8000)>>15; } u_int16_t RawVideoRTPSource::currentOffsetWithinLine() const { if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon! return fLineHeaders[fNextLine-1].offsetWithinLine; } Boolean RawVideoRTPSource ::processSpecialHeader(BufferedPacket* packet, unsigned& resultSpecialHeaderSize) { unsigned char* headerStart = packet->data(); unsigned packetSize = packet->dataSize(); // The first 2 bytes of the header are the "Extended Sequence Number". // In the current implementation, we ignore this. if (packetSize < 2) return False; headerStart += 2; unsigned char* lineHeaderStart = headerStart; packetSize -= 2; // The rest of the header should consist of N*6 bytes (with N >= 1) for each line included. // Count how many of these there are: unsigned numLines = 0; while (1) { if (packetSize < 6) return False; // there's not enough room for another line header ++numLines; Boolean continuationBit = (headerStart[4]&0x80)>>7; headerStart += 6; packetSize -= 6; // Check the "C" (continuation) bit of this header to see whether any more line headers follow: if (continuationBit == 0) break; // no more line headers follow } // We now know how many lines are contained in this payload. Allocate and fill in "fLineHeaders": fNumLines = numLines; // ASSERT: >= 1 fNextLine = 0; delete[] fLineHeaders; fLineHeaders = new LineHeader[fNumLines]; unsigned totalLength = 0; for (unsigned i = 0; i < fNumLines; ++i) { fLineHeaders[i].length = (lineHeaderStart[0]<<8) + lineHeaderStart[1]; totalLength += fLineHeaders[i].length; fLineHeaders[i].fieldIdAndLineNumber = (lineHeaderStart[2]<<8) + lineHeaderStart[3]; fLineHeaders[i].offsetWithinLine = ((lineHeaderStart[4]&0x7F)<<8) + lineHeaderStart[5]; lineHeaderStart += 6; } // Make sure that we have enough bytes for all of the line lengths promised: if (totalLength > packetSize) { fNumLines = 0; delete[] fLineHeaders; fLineHeaders = NULL; return False; } // Everything looks good: fCurrentPacketBeginsFrame = (fLineHeaders[0].fieldIdAndLineNumber&0x7FFF) == 0 && fLineHeaders[0].offsetWithinLine == 0; fCurrentPacketCompletesFrame = packet->rtpMarkerBit(); resultSpecialHeaderSize = headerStart - packet->data(); return True; } char const* RawVideoRTPSource::MIMEtype() const { return "video/RAW"; } ////////// RawVideoBufferedPacket and RawVideoBufferedPacketFactory implementation ////////// RawVideoBufferedPacket ::RawVideoBufferedPacket(RawVideoRTPSource* ourSource) : fOurSource(ourSource) { } RawVideoBufferedPacket::~RawVideoBufferedPacket() { } void RawVideoBufferedPacket::getNextEnclosedFrameParameters(unsigned char*& /*framePtr*/, unsigned dataSize, unsigned& frameSize, unsigned& frameDurationInMicroseconds) { frameDurationInMicroseconds = 0; // because all lines within the same packet are from the same frame if (fOurSource->fNextLine >= fOurSource->fNumLines) { fOurSource->envir() << "RawVideoBufferedPacket::nextEnclosedFrameParameters(" << dataSize << "): data error (" << fOurSource->fNextLine << " >= " << fOurSource->fNumLines << ")!\n"; frameSize = dataSize; return; } frameSize = fOurSource->fLineHeaders[fOurSource->fNextLine++].length; } BufferedPacket* RawVideoBufferedPacketFactory ::createNewPacket(MultiFramedRTPSource* ourSource) { return new RawVideoBufferedPacket((RawVideoRTPSource*)ourSource); }