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 | |