| /********** |
| 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 <http://www.gnu.org/copyleft/lesser.html>.) |
| |
| 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); |
| } |