| /********** |
| 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 MPEG-4 Elementary Stream video (RFC 3016) |
| // Implementation |
| |
| #include "MPEG4ESVideoRTPSink.hh" |
| #include "MPEG4VideoStreamFramer.hh" |
| #include "MPEG4LATMAudioRTPSource.hh" // for "parseGeneralConfigStr()" |
| |
| MPEG4ESVideoRTPSink |
| ::MPEG4ESVideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, u_int32_t rtpTimestampFrequency, |
| u_int8_t profileAndLevelIndication, char const* configStr) |
| : VideoRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, "MP4V-ES"), |
| fVOPIsPresent(False), fProfileAndLevelIndication(profileAndLevelIndication), fFmtpSDPLine(NULL) { |
| fConfigBytes = parseGeneralConfigStr(configStr, fNumConfigBytes); |
| } |
| |
| MPEG4ESVideoRTPSink::~MPEG4ESVideoRTPSink() { |
| delete[] fFmtpSDPLine; |
| delete[] fConfigBytes; |
| } |
| |
| MPEG4ESVideoRTPSink* |
| MPEG4ESVideoRTPSink::createNew(UsageEnvironment& env, |
| Groupsock* RTPgs, unsigned char rtpPayloadFormat, |
| u_int32_t rtpTimestampFrequency) { |
| return new MPEG4ESVideoRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency); |
| } |
| |
| MPEG4ESVideoRTPSink* |
| MPEG4ESVideoRTPSink::createNew(UsageEnvironment& env, |
| Groupsock* RTPgs, unsigned char rtpPayloadFormat, u_int32_t rtpTimestampFrequency, |
| u_int8_t profileAndLevelIndication, char const* configStr) { |
| return new MPEG4ESVideoRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, profileAndLevelIndication, configStr); |
| } |
| |
| Boolean MPEG4ESVideoRTPSink::sourceIsCompatibleWithUs(MediaSource& source) { |
| // Our source must be an appropriate framer: |
| return source.isMPEG4VideoStreamFramer(); |
| } |
| |
| #define VOP_START_CODE 0x000001B6 |
| |
| void MPEG4ESVideoRTPSink |
| ::doSpecialFrameHandling(unsigned fragmentationOffset, |
| unsigned char* frameStart, |
| unsigned numBytesInFrame, |
| struct timeval framePresentationTime, |
| unsigned numRemainingBytes) { |
| if (fragmentationOffset == 0) { |
| // Begin by inspecting the 4-byte code at the start of the frame: |
| if (numBytesInFrame < 4) return; // shouldn't happen |
| u_int32_t startCode |
| = (frameStart[0]<<24) | (frameStart[1]<<16) | (frameStart[2]<<8) | frameStart[3]; |
| |
| fVOPIsPresent = startCode == VOP_START_CODE; |
| } |
| |
| // Set the RTP 'M' (marker) bit iff this frame ends a VOP |
| // (and there are no fragments remaining). |
| // This relies on the source being a "MPEG4VideoStreamFramer". |
| MPEG4VideoStreamFramer* framerSource = (MPEG4VideoStreamFramer*)fSource; |
| if (framerSource != NULL && framerSource->pictureEndMarker() |
| && numRemainingBytes == 0) { |
| setMarkerBit(); |
| framerSource->pictureEndMarker() = False; |
| } |
| |
| // Also set the RTP timestamp. (We do this for each frame |
| // in the packet, to ensure that the timestamp of the VOP (if present) |
| // gets used.) |
| setTimestamp(framePresentationTime); |
| } |
| |
| Boolean MPEG4ESVideoRTPSink::allowFragmentationAfterStart() const { |
| return True; |
| } |
| |
| Boolean MPEG4ESVideoRTPSink |
| ::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/, |
| unsigned /*numBytesInFrame*/) const { |
| // Once we've packed a VOP into the packet, then no other |
| // frame can be packed into it: |
| return !fVOPIsPresent; |
| } |
| |
| char const* MPEG4ESVideoRTPSink::auxSDPLine() { |
| // Generate a new "a=fmtp:" line each time, using our own 'configuration' information (if we have it), |
| // otherwise parameters from our framer source (in case they've changed since the last time that |
| // we were called): |
| unsigned configLength = fNumConfigBytes; |
| unsigned char* config = fConfigBytes; |
| if (fProfileAndLevelIndication == 0 || config == NULL) { |
| // We need to get this information from our framer source: |
| MPEG4VideoStreamFramer* framerSource = (MPEG4VideoStreamFramer*)fSource; |
| if (framerSource == NULL) return NULL; // we don't yet have a source |
| |
| fProfileAndLevelIndication = framerSource->profile_and_level_indication(); |
| if (fProfileAndLevelIndication == 0) return NULL; // our source isn't ready |
| |
| config = framerSource->getConfigBytes(configLength); |
| if (config == NULL) return NULL; // our source isn't ready |
| } |
| |
| char const* fmtpFmt = |
| "a=fmtp:%d " |
| "profile-level-id=%d;" |
| "config="; |
| unsigned fmtpFmtSize = strlen(fmtpFmt) |
| + 3 /* max char len */ |
| + 3 /* max char len */ |
| + 2*configLength /* 2*, because each byte prints as 2 chars */ |
| + 2 /* trailing \r\n */; |
| char* fmtp = new char[fmtpFmtSize]; |
| sprintf(fmtp, fmtpFmt, rtpPayloadType(), fProfileAndLevelIndication); |
| char* endPtr = &fmtp[strlen(fmtp)]; |
| for (unsigned i = 0; i < configLength; ++i) { |
| sprintf(endPtr, "%02X", config[i]); |
| endPtr += 2; |
| } |
| sprintf(endPtr, "\r\n"); |
| |
| delete[] fFmtpSDPLine; |
| fFmtpSDPLine = strDup(fmtp); |
| delete[] fmtp; |
| return fFmtpSDPLine; |
| } |