| 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 | // RTP sink for MPEG-4 Elementary Stream video (RFC 3016) |
| 19 | // Implementation |
| 20 | |
| 21 | #include "MPEG4ESVideoRTPSink.hh" |
| 22 | #include "MPEG4VideoStreamFramer.hh" |
| 23 | #include "MPEG4LATMAudioRTPSource.hh" // for "parseGeneralConfigStr()" |
| 24 | |
| 25 | MPEG4ESVideoRTPSink |
| 26 | ::MPEG4ESVideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat, u_int32_t rtpTimestampFrequency, |
| 27 | u_int8_t profileAndLevelIndication, char const* configStr) |
| 28 | : VideoRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, "MP4V-ES" ), |
| 29 | fVOPIsPresent(False), fProfileAndLevelIndication(profileAndLevelIndication), fFmtpSDPLine(NULL) { |
| 30 | fConfigBytes = parseGeneralConfigStr(configStr, fNumConfigBytes); |
| 31 | } |
| 32 | |
| 33 | MPEG4ESVideoRTPSink::~MPEG4ESVideoRTPSink() { |
| 34 | delete[] fFmtpSDPLine; |
| 35 | delete[] fConfigBytes; |
| 36 | } |
| 37 | |
| 38 | MPEG4ESVideoRTPSink* |
| 39 | MPEG4ESVideoRTPSink::createNew(UsageEnvironment& env, |
| 40 | Groupsock* RTPgs, unsigned char rtpPayloadFormat, |
| 41 | u_int32_t rtpTimestampFrequency) { |
| 42 | return new MPEG4ESVideoRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency); |
| 43 | } |
| 44 | |
| 45 | MPEG4ESVideoRTPSink* |
| 46 | MPEG4ESVideoRTPSink::createNew(UsageEnvironment& env, |
| 47 | Groupsock* RTPgs, unsigned char rtpPayloadFormat, u_int32_t rtpTimestampFrequency, |
| 48 | u_int8_t profileAndLevelIndication, char const* configStr) { |
| 49 | return new MPEG4ESVideoRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, profileAndLevelIndication, configStr); |
| 50 | } |
| 51 | |
| 52 | Boolean MPEG4ESVideoRTPSink::sourceIsCompatibleWithUs(MediaSource& source) { |
| 53 | // Our source must be an appropriate framer: |
| 54 | return source.isMPEG4VideoStreamFramer(); |
| 55 | } |
| 56 | |
| 57 | #define VOP_START_CODE 0x000001B6 |
| 58 | |
| 59 | void MPEG4ESVideoRTPSink |
| 60 | ::doSpecialFrameHandling(unsigned fragmentationOffset, |
| 61 | unsigned char* frameStart, |
| 62 | unsigned numBytesInFrame, |
| 63 | struct timeval framePresentationTime, |
| 64 | unsigned numRemainingBytes) { |
| 65 | if (fragmentationOffset == 0) { |
| 66 | // Begin by inspecting the 4-byte code at the start of the frame: |
| 67 | if (numBytesInFrame < 4) return; // shouldn't happen |
| 68 | u_int32_t startCode |
| 69 | = (frameStart[0]<<24) | (frameStart[1]<<16) | (frameStart[2]<<8) | frameStart[3]; |
| 70 | |
| 71 | fVOPIsPresent = startCode == VOP_START_CODE; |
| 72 | } |
| 73 | |
| 74 | // Set the RTP 'M' (marker) bit iff this frame ends a VOP |
| 75 | // (and there are no fragments remaining). |
| 76 | // This relies on the source being a "MPEG4VideoStreamFramer". |
| 77 | MPEG4VideoStreamFramer* framerSource = (MPEG4VideoStreamFramer*)fSource; |
| 78 | if (framerSource != NULL && framerSource->pictureEndMarker() |
| 79 | && numRemainingBytes == 0) { |
| 80 | setMarkerBit(); |
| 81 | framerSource->pictureEndMarker() = False; |
| 82 | } |
| 83 | |
| 84 | // Also set the RTP timestamp. (We do this for each frame |
| 85 | // in the packet, to ensure that the timestamp of the VOP (if present) |
| 86 | // gets used.) |
| 87 | setTimestamp(framePresentationTime); |
| 88 | } |
| 89 | |
| 90 | Boolean MPEG4ESVideoRTPSink::allowFragmentationAfterStart() const { |
| 91 | return True; |
| 92 | } |
| 93 | |
| 94 | Boolean MPEG4ESVideoRTPSink |
| 95 | ::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/, |
| 96 | unsigned /*numBytesInFrame*/) const { |
| 97 | // Once we've packed a VOP into the packet, then no other |
| 98 | // frame can be packed into it: |
| 99 | return !fVOPIsPresent; |
| 100 | } |
| 101 | |
| 102 | char const* MPEG4ESVideoRTPSink::auxSDPLine() { |
| 103 | // Generate a new "a=fmtp:" line each time, using our own 'configuration' information (if we have it), |
| 104 | // otherwise parameters from our framer source (in case they've changed since the last time that |
| 105 | // we were called): |
| 106 | unsigned configLength = fNumConfigBytes; |
| 107 | unsigned char* config = fConfigBytes; |
| 108 | if (fProfileAndLevelIndication == 0 || config == NULL) { |
| 109 | // We need to get this information from our framer source: |
| 110 | MPEG4VideoStreamFramer* framerSource = (MPEG4VideoStreamFramer*)fSource; |
| 111 | if (framerSource == NULL) return NULL; // we don't yet have a source |
| 112 | |
| 113 | fProfileAndLevelIndication = framerSource->profile_and_level_indication(); |
| 114 | if (fProfileAndLevelIndication == 0) return NULL; // our source isn't ready |
| 115 | |
| 116 | config = framerSource->getConfigBytes(configLength); |
| 117 | if (config == NULL) return NULL; // our source isn't ready |
| 118 | } |
| 119 | |
| 120 | char const* fmtpFmt = |
| 121 | "a=fmtp:%d " |
| 122 | "profile-level-id=%d;" |
| 123 | "config=" ; |
| 124 | unsigned fmtpFmtSize = strlen(fmtpFmt) |
| 125 | + 3 /* max char len */ |
| 126 | + 3 /* max char len */ |
| 127 | + 2*configLength /* 2*, because each byte prints as 2 chars */ |
| 128 | + 2 /* trailing \r\n */; |
| 129 | char* fmtp = new char[fmtpFmtSize]; |
| 130 | sprintf(fmtp, fmtpFmt, rtpPayloadType(), fProfileAndLevelIndication); |
| 131 | char* endPtr = &fmtp[strlen(fmtp)]; |
| 132 | for (unsigned i = 0; i < configLength; ++i) { |
| 133 | sprintf(endPtr, "%02X" , config[i]); |
| 134 | endPtr += 2; |
| 135 | } |
| 136 | sprintf(endPtr, "\r\n" ); |
| 137 | |
| 138 | delete[] fFmtpSDPLine; |
| 139 | fFmtpSDPLine = strDup(fmtp); |
| 140 | delete[] fmtp; |
| 141 | return fFmtpSDPLine; |
| 142 | } |
| 143 | |