| /********** |
| 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. |
| // RTP sink for Raw video |
| // Implementation |
| |
| #include "RawVideoRTPSink.hh" |
| |
| RawVideoRTPSink* RawVideoRTPSink |
| ::createNew(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat, |
| unsigned height, unsigned width, unsigned depth, |
| char const* sampling, char const* colorimetry) { |
| return new RawVideoRTPSink(env, RTPgs, |
| rtpPayloadFormat, |
| height, width, depth, |
| sampling, colorimetry); |
| } |
| |
| RawVideoRTPSink |
| ::RawVideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat, |
| unsigned height, unsigned width, unsigned depth, |
| char const* sampling, char const* colorimetry) |
| : VideoRTPSink(env, RTPgs, rtpPayloadFormat, 90000, "RAW"), |
| fFmtpSDPLine(NULL), fSampling(NULL), fWidth(width), fHeight(height), |
| fDepth(depth), fColorimetry(NULL), fLineindex(0) { |
| |
| // Then use this 'config' string to construct our "a=fmtp:" SDP line: |
| unsigned fmtpSDPLineMaxSize = 200;// 200 => more than enough space |
| fFmtpSDPLine = new char[fmtpSDPLineMaxSize]; |
| sprintf(fFmtpSDPLine, "a=fmtp:%d sampling=%s;width=%u;height=%u;depth=%u;colorimetry=%s\r\n", |
| rtpPayloadType(), sampling, width, height, depth, colorimetry); |
| |
| // Set parameters |
| fSampling = strDup(sampling); |
| fColorimetry = strDup(colorimetry); |
| setFrameParameters(); |
| } |
| |
| RawVideoRTPSink::~RawVideoRTPSink() { |
| delete[] fFmtpSDPLine; |
| delete[] fSampling; |
| delete[] fColorimetry;} |
| |
| char const* RawVideoRTPSink::auxSDPLine() { |
| return fFmtpSDPLine; |
| } |
| |
| void RawVideoRTPSink |
| ::doSpecialFrameHandling(unsigned fragmentationOffset, |
| unsigned char* frameStart, |
| unsigned numBytesInFrame, |
| struct timeval framePresentationTime, |
| unsigned numRemainingBytes) { |
| |
| unsigned * lengths = NULL; |
| unsigned * offsets= NULL; |
| unsigned nbLines = getNbLineInPacket(fragmentationOffset, lengths, offsets); |
| unsigned specialHeaderSize = 2 + (6 * nbLines); |
| u_int8_t* specialHeader = new u_int8_t[specialHeaderSize]; |
| |
| // Extended Sequence Number (not used) |
| specialHeader[0] = 0; |
| specialHeader[1] = 0; |
| |
| for (unsigned i = 0; i < nbLines; i++) { |
| // detection of new line incrementation |
| if ((offsets[i] == 0) && fragmentationOffset != 0) { |
| fLineindex = fLineindex + fFrameParameters.scanLineIterationStep; |
| } |
| |
| // Set length |
| specialHeader[2 + (i * 6) + 0] = lengths[i] >> 8; |
| specialHeader[2 + (i * 6) + 1] = (u_int8_t)lengths[i]; |
| |
| // Field Identification (false for us) |
| bool fieldIdent = false; |
| |
| // Set line index |
| specialHeader[2 + (i * 6) + 2] = ((fLineindex >> 8) & 0x7F) | (fieldIdent << 7); |
| specialHeader[2 + (i * 6) + 3] = (u_int8_t)fLineindex; |
| |
| // Set Continuation bit |
| bool continuationBit = (i < nbLines - 1) ? true : false; |
| |
| // Set offset |
| specialHeader[2 + (i * 6) + 4] = ((offsets[i] >> 8) & 0x7F) | (continuationBit << 7); |
| specialHeader[2 + (i * 6) + 5] = (u_int8_t)offsets[i]; |
| } |
| |
| setSpecialHeaderBytes(specialHeader, specialHeaderSize); |
| |
| if (numRemainingBytes == 0) { |
| // This packet contains the last (or only) fragment of the frame. |
| // Set the RTP 'M' ('marker') bit: |
| setMarkerBit(); |
| // Reset line index |
| fLineindex = 0; |
| } |
| |
| // Also set the RTP timestamp: |
| setTimestamp(framePresentationTime); |
| |
| delete[] specialHeader; |
| delete[] lengths; |
| delete[] offsets; |
| } |
| |
| Boolean RawVideoRTPSink::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/, |
| unsigned /*numBytesInFrame*/) const { |
| // Only one frame per packet: |
| return False; |
| } |
| |
| unsigned RawVideoRTPSink::specialHeaderSize() const { |
| unsigned * lengths = NULL; |
| unsigned * offsets= NULL; |
| unsigned nbLines = getNbLineInPacket(curFragmentationOffset(), lengths, offsets); |
| delete[] lengths; |
| delete[] offsets; |
| return 2 + (6 * nbLines); |
| } |
| |
| unsigned RawVideoRTPSink::getNbLineInPacket(unsigned fragOffset, unsigned * &lengths, unsigned * &offsets) const |
| { |
| unsigned rtpHeaderSize = 12; |
| unsigned specialHeaderSize = 2; // Extended Sequence Nb |
| unsigned packetMaxSize = ourMaxPacketSize(); |
| unsigned nbLines = 0; |
| unsigned remainingSizeInPacket; |
| |
| if (fragOffset >= fFrameParameters.frameSize) { |
| envir() << "RawVideoRTPSink::getNbLineInPacket(): bad fragOffset " << fragOffset << "\n"; |
| return 0; |
| } |
| unsigned lengthArray[100] = {0}; |
| unsigned offsetArray[100] = {0}; |
| unsigned curDataTotalLength = 0; |
| unsigned lineOffset = (fragOffset % fFrameParameters.scanLineSize); |
| |
| unsigned remainingLineSize = fFrameParameters.scanLineSize - (fragOffset % fFrameParameters.scanLineSize); |
| while(1) { |
| if (packetMaxSize - specialHeaderSize - rtpHeaderSize - 6 <= curDataTotalLength) { |
| break; // packet sanity check |
| } |
| |
| // add one line |
| nbLines ++; |
| specialHeaderSize += 6; |
| |
| remainingSizeInPacket = packetMaxSize - specialHeaderSize - rtpHeaderSize - curDataTotalLength; |
| remainingSizeInPacket -= remainingSizeInPacket % fFrameParameters.pGroupSize; // use only multiple of pgroup |
| lengthArray[nbLines-1] = remainingLineSize < remainingSizeInPacket ? remainingLineSize : remainingSizeInPacket; |
| offsetArray[nbLines-1] = lineOffset * fFrameParameters.scanLineIterationStep / fFrameParameters.pGroupSize; |
| if (remainingLineSize >= remainingSizeInPacket) { |
| break; //packet full |
| } |
| |
| remainingLineSize = fFrameParameters.scanLineSize; |
| curDataTotalLength += lengthArray[nbLines-1]; |
| lineOffset = 0; |
| |
| if (fragOffset + curDataTotalLength >= fFrameParameters.frameSize) { |
| break; // end of the frame. |
| } |
| } |
| |
| lengths = new unsigned[nbLines]; |
| offsets = new unsigned[nbLines]; |
| for (unsigned i = 0; i < nbLines; i++) { |
| lengths[i] = lengthArray[i]; |
| offsets[i] = offsetArray[i]; |
| } |
| return nbLines; |
| } |
| |
| unsigned RawVideoRTPSink::computeOverflowForNewFrame(unsigned newFrameSize) const { |
| unsigned initialOverflow = MultiFramedRTPSink::computeOverflowForNewFrame(newFrameSize); |
| |
| // Adjust (increase) this overflow to be a multiple of the pgroup value |
| unsigned numFrameBytesUsed = newFrameSize - initialOverflow; |
| initialOverflow += numFrameBytesUsed % fFrameParameters.pGroupSize; |
| |
| return initialOverflow; |
| } |
| |
| void RawVideoRTPSink::setFrameParameters() { |
| fFrameParameters.scanLineIterationStep = 1; |
| if ((strncmp("RGB", fSampling, strlen(fSampling)) == 0) || (strncmp("BGR", fSampling, strlen(fSampling)) == 0)) { |
| switch (fDepth) { |
| case 8: |
| fFrameParameters.pGroupSize = 3; |
| fFrameParameters.nbOfPixelInPGroup = 1; |
| break; |
| case 10: |
| fFrameParameters.pGroupSize = 15; |
| fFrameParameters.nbOfPixelInPGroup = 4; |
| break; |
| case 12: |
| fFrameParameters.pGroupSize = 9; |
| fFrameParameters.nbOfPixelInPGroup = 2; |
| break; |
| case 16: |
| fFrameParameters.pGroupSize = 6; |
| fFrameParameters.nbOfPixelInPGroup = 1; |
| break; |
| default: |
| break; |
| } |
| } |
| else if ((strncmp("RGBA", fSampling, strlen(fSampling)) == 0) || (strncmp("BGRA", fSampling, strlen(fSampling)) == 0)) { |
| switch (fDepth) { |
| case 8: |
| fFrameParameters.pGroupSize = 4; |
| break; |
| case 10: |
| fFrameParameters.pGroupSize = 5; |
| break; |
| case 12: |
| fFrameParameters.pGroupSize = 6; |
| break; |
| case 16: |
| fFrameParameters.pGroupSize = 8; |
| break; |
| default: |
| break; |
| } |
| fFrameParameters.nbOfPixelInPGroup = 1; |
| } else if (strncmp("YCbCr-4:4:4", fSampling, strlen(fSampling)) == 0) { |
| switch (fDepth) { |
| case 8: |
| fFrameParameters.pGroupSize = 3; |
| fFrameParameters.nbOfPixelInPGroup = 1; |
| break; |
| case 10: |
| fFrameParameters.pGroupSize = 15; |
| fFrameParameters.nbOfPixelInPGroup = 4; |
| break; |
| case 12: |
| fFrameParameters.pGroupSize = 9; |
| fFrameParameters.nbOfPixelInPGroup = 2; |
| break; |
| case 16: |
| fFrameParameters.pGroupSize = 6; |
| fFrameParameters.nbOfPixelInPGroup = 1; |
| break; |
| default: |
| break; |
| } |
| } else if (strncmp("YCbCr-4:2:2", fSampling, strlen(fSampling)) == 0) { |
| switch (fDepth) { |
| case 8: |
| fFrameParameters.pGroupSize = 4; |
| break; |
| case 10: |
| fFrameParameters.pGroupSize = 5; |
| break; |
| case 12: |
| fFrameParameters.pGroupSize = 6; |
| break; |
| case 16: |
| fFrameParameters.pGroupSize = 8; |
| break; |
| default: |
| break; |
| } |
| fFrameParameters.nbOfPixelInPGroup = 2; |
| } else if (strncmp("YCbCr-4:1:1", fSampling, strlen(fSampling)) == 0) { |
| switch (fDepth) { |
| case 8: |
| fFrameParameters.pGroupSize = 6; |
| break; |
| case 10: |
| fFrameParameters.pGroupSize = 15; |
| break; |
| case 12: |
| fFrameParameters.pGroupSize = 9; |
| break; |
| case 16: |
| fFrameParameters.pGroupSize = 12; |
| break; |
| default: |
| break; |
| } |
| fFrameParameters.nbOfPixelInPGroup = 4; |
| } else if (strncmp("YCbCr-4:2:0", fSampling, strlen(fSampling)) == 0) { |
| switch (fDepth) { |
| case 8: |
| fFrameParameters.pGroupSize = 6; |
| break; |
| case 10: |
| fFrameParameters.pGroupSize = 15; |
| break; |
| case 12: |
| fFrameParameters.pGroupSize = 9; |
| break; |
| case 16: |
| fFrameParameters.pGroupSize = 12; |
| break; |
| default: |
| break; |
| } |
| fFrameParameters.nbOfPixelInPGroup = 4; |
| fFrameParameters.scanLineIterationStep = 2; |
| } |
| fFrameParameters.frameSize = fHeight * fWidth * fFrameParameters.pGroupSize / fFrameParameters.nbOfPixelInPGroup; |
| fFrameParameters.scanLineSize = fWidth * fFrameParameters.pGroupSize / fFrameParameters.nbOfPixelInPGroup * fFrameParameters.scanLineIterationStep; |
| } |