| 1 | /********** | 
|---|
| 2 | This library is free software; you can redistribute it and/or modify it under | 
|---|
| 3 | the terms of the GNU Lesser General Public License as published by the | 
|---|
| 4 | Free Software Foundation; either version 3 of the License, or (at your | 
|---|
| 5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) | 
|---|
| 6 |  | 
|---|
| 7 | This library is distributed in the hope that it will be useful, but WITHOUT | 
|---|
| 8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 
|---|
| 9 | FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for | 
|---|
| 10 | more details. | 
|---|
| 11 |  | 
|---|
| 12 | You should have received a copy of the GNU Lesser General Public License | 
|---|
| 13 | along with this library; if not, write to the Free Software Foundation, Inc., | 
|---|
| 14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA | 
|---|
| 15 | **********/ | 
|---|
| 16 | // "liveMedia" | 
|---|
| 17 | // Copyright (c) 1996-2020 Live Networks, Inc.  All rights reserved. | 
|---|
| 18 | // Raw Video RTP Sources (RFC 4175) | 
|---|
| 19 | // Implementation | 
|---|
| 20 |  | 
|---|
| 21 | #include "RawVideoRTPSource.hh" | 
|---|
| 22 |  | 
|---|
| 23 | ////////// RawVideoBufferedPacket and RawVideoBufferedPacketFactory ////////// | 
|---|
| 24 |  | 
|---|
| 25 | class RawVideoBufferedPacket: public BufferedPacket { | 
|---|
| 26 | public: | 
|---|
| 27 | RawVideoBufferedPacket(RawVideoRTPSource* ourSource); | 
|---|
| 28 | virtual ~RawVideoBufferedPacket(); | 
|---|
| 29 |  | 
|---|
| 30 | private: // redefined virtual functions | 
|---|
| 31 | virtual void getNextEnclosedFrameParameters(unsigned char*& framePtr, | 
|---|
| 32 | unsigned dataSize, | 
|---|
| 33 | unsigned& frameSize, | 
|---|
| 34 | unsigned& frameDurationInMicroseconds); | 
|---|
| 35 | private: | 
|---|
| 36 | RawVideoRTPSource* fOurSource; | 
|---|
| 37 | }; | 
|---|
| 38 |  | 
|---|
| 39 | class RawVideoBufferedPacketFactory: public BufferedPacketFactory { | 
|---|
| 40 | private: // redefined virtual functions | 
|---|
| 41 | virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource); | 
|---|
| 42 | }; | 
|---|
| 43 |  | 
|---|
| 44 |  | 
|---|
| 45 | ////////// LineHeader ////////// | 
|---|
| 46 |  | 
|---|
| 47 | struct  { | 
|---|
| 48 | u_int16_t ; | 
|---|
| 49 | u_int16_t fieldIdAndLineNumber; | 
|---|
| 50 | u_int16_t ; | 
|---|
| 51 | }; | 
|---|
| 52 |  | 
|---|
| 53 |  | 
|---|
| 54 | ///////// RawVideoRTPSource implementation (RFC 4175) //////// | 
|---|
| 55 |  | 
|---|
| 56 | RawVideoRTPSource* | 
|---|
| 57 | RawVideoRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs, | 
|---|
| 58 | unsigned char rtpPayloadFormat, | 
|---|
| 59 | unsigned rtpTimestampFrequency) { | 
|---|
| 60 | return new RawVideoRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency); | 
|---|
| 61 | } | 
|---|
| 62 |  | 
|---|
| 63 | RawVideoRTPSource | 
|---|
| 64 | ::RawVideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs, | 
|---|
| 65 | unsigned char rtpPayloadFormat, | 
|---|
| 66 | unsigned rtpTimestampFrequency) | 
|---|
| 67 | : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, | 
|---|
| 68 | new RawVideoBufferedPacketFactory), | 
|---|
| 69 | fNumLines(0), fNextLine(0), fLineHeaders(NULL) { | 
|---|
| 70 | } | 
|---|
| 71 |  | 
|---|
| 72 | RawVideoRTPSource::~RawVideoRTPSource() { | 
|---|
| 73 | delete[] fLineHeaders; | 
|---|
| 74 | } | 
|---|
| 75 |  | 
|---|
| 76 | u_int16_t RawVideoRTPSource::currentLineNumber() const { | 
|---|
| 77 | if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon! | 
|---|
| 78 | return fLineHeaders[fNextLine-1].fieldIdAndLineNumber&0x7FFF; | 
|---|
| 79 | } | 
|---|
| 80 |  | 
|---|
| 81 | u_int8_t RawVideoRTPSource::currentLineFieldId() const { | 
|---|
| 82 | if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon! | 
|---|
| 83 | return (fLineHeaders[fNextLine-1].fieldIdAndLineNumber&0x8000)>>15; | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | u_int16_t RawVideoRTPSource::currentOffsetWithinLine() const { | 
|---|
| 87 | if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon! | 
|---|
| 88 | return fLineHeaders[fNextLine-1].offsetWithinLine; | 
|---|
| 89 | } | 
|---|
| 90 |  | 
|---|
| 91 | Boolean RawVideoRTPSource | 
|---|
| 92 | ::(BufferedPacket* packet, | 
|---|
| 93 | unsigned& ) { | 
|---|
| 94 |  | 
|---|
| 95 | unsigned char*  = packet->data(); | 
|---|
| 96 | unsigned packetSize = packet->dataSize(); | 
|---|
| 97 |  | 
|---|
| 98 | // The first 2 bytes of the header are the "Extended Sequence Number". | 
|---|
| 99 | // In the current implementation, we ignore this. | 
|---|
| 100 | if (packetSize < 2) return False; | 
|---|
| 101 | headerStart += 2; | 
|---|
| 102 | unsigned char*  = headerStart; | 
|---|
| 103 | packetSize -= 2; | 
|---|
| 104 |  | 
|---|
| 105 | // The rest of the header should consist of N*6 bytes (with N >= 1) for each line included. | 
|---|
| 106 | // Count how many of these there are: | 
|---|
| 107 | unsigned numLines = 0; | 
|---|
| 108 | while (1) { | 
|---|
| 109 | if (packetSize < 6) return False; // there's not enough room for another line header | 
|---|
| 110 | ++numLines; | 
|---|
| 111 | Boolean continuationBit = (headerStart[4]&0x80)>>7; | 
|---|
| 112 | headerStart += 6; | 
|---|
| 113 | packetSize -= 6; | 
|---|
| 114 |  | 
|---|
| 115 | // Check the "C" (continuation) bit of this header to see whether any more line headers follow: | 
|---|
| 116 | if (continuationBit == 0) break; // no more line headers follow | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | // We now know how many lines are contained in this payload.  Allocate and fill in "fLineHeaders": | 
|---|
| 120 | fNumLines = numLines; // ASSERT: >= 1 | 
|---|
| 121 | fNextLine = 0; | 
|---|
| 122 | delete[] fLineHeaders; fLineHeaders = new LineHeader[fNumLines]; | 
|---|
| 123 | unsigned totalLength = 0; | 
|---|
| 124 | for (unsigned i = 0; i < fNumLines; ++i) { | 
|---|
| 125 | fLineHeaders[i].length = (lineHeaderStart[0]<<8) + lineHeaderStart[1]; | 
|---|
| 126 | totalLength += fLineHeaders[i].length; | 
|---|
| 127 | fLineHeaders[i].fieldIdAndLineNumber = (lineHeaderStart[2]<<8) + lineHeaderStart[3]; | 
|---|
| 128 | fLineHeaders[i].offsetWithinLine = ((lineHeaderStart[4]&0x7F)<<8) + lineHeaderStart[5]; | 
|---|
| 129 | lineHeaderStart += 6; | 
|---|
| 130 | } | 
|---|
| 131 |  | 
|---|
| 132 | // Make sure that we have enough bytes for all of the line lengths promised: | 
|---|
| 133 | if (totalLength > packetSize) { | 
|---|
| 134 | fNumLines = 0; | 
|---|
| 135 | delete[] fLineHeaders; fLineHeaders = NULL; | 
|---|
| 136 | return False; | 
|---|
| 137 | } | 
|---|
| 138 |  | 
|---|
| 139 | // Everything looks good: | 
|---|
| 140 | fCurrentPacketBeginsFrame | 
|---|
| 141 | = (fLineHeaders[0].fieldIdAndLineNumber&0x7FFF) == 0 && fLineHeaders[0].offsetWithinLine == 0; | 
|---|
| 142 | fCurrentPacketCompletesFrame = packet->rtpMarkerBit(); | 
|---|
| 143 | resultSpecialHeaderSize = headerStart - packet->data(); | 
|---|
| 144 | return True; | 
|---|
| 145 | } | 
|---|
| 146 |  | 
|---|
| 147 | char const* RawVideoRTPSource::MIMEtype() const { | 
|---|
| 148 | return "video/RAW"; | 
|---|
| 149 | } | 
|---|
| 150 |  | 
|---|
| 151 |  | 
|---|
| 152 | ////////// RawVideoBufferedPacket and RawVideoBufferedPacketFactory implementation ////////// | 
|---|
| 153 |  | 
|---|
| 154 | RawVideoBufferedPacket | 
|---|
| 155 | ::RawVideoBufferedPacket(RawVideoRTPSource* ourSource) | 
|---|
| 156 | : fOurSource(ourSource) { | 
|---|
| 157 | } | 
|---|
| 158 |  | 
|---|
| 159 | RawVideoBufferedPacket::~RawVideoBufferedPacket() { | 
|---|
| 160 | } | 
|---|
| 161 |  | 
|---|
| 162 | void RawVideoBufferedPacket::getNextEnclosedFrameParameters(unsigned char*& /*framePtr*/, | 
|---|
| 163 | unsigned dataSize, | 
|---|
| 164 | unsigned& frameSize, | 
|---|
| 165 | unsigned& frameDurationInMicroseconds) { | 
|---|
| 166 | frameDurationInMicroseconds = 0; // because all lines within the same packet are from the same frame | 
|---|
| 167 |  | 
|---|
| 168 | if (fOurSource->fNextLine >= fOurSource->fNumLines) { | 
|---|
| 169 | fOurSource->envir() << "RawVideoBufferedPacket::nextEnclosedFrameParameters(" | 
|---|
| 170 | << dataSize << "): data error (" | 
|---|
| 171 | << fOurSource->fNextLine << " >= "<< fOurSource->fNumLines << ")!\n"; | 
|---|
| 172 | frameSize = dataSize; | 
|---|
| 173 | return; | 
|---|
| 174 | } | 
|---|
| 175 |  | 
|---|
| 176 | frameSize = fOurSource->fLineHeaders[fOurSource->fNextLine++].length; | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | BufferedPacket* RawVideoBufferedPacketFactory | 
|---|
| 180 | ::createNewPacket(MultiFramedRTPSource* ourSource) { | 
|---|
| 181 | return new RawVideoBufferedPacket((RawVideoRTPSource*)ourSource); | 
|---|
| 182 | } | 
|---|
| 183 |  | 
|---|