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 | // MPEG4-GENERIC ("audio", "video", or "application") RTP stream sources |
19 | // Implementation |
20 | |
21 | #include "MPEG4GenericRTPSource.hh" |
22 | #include "BitVector.hh" |
23 | #include "MPEG4LATMAudioRTPSource.hh" // for parseGeneralConfigStr() |
24 | |
25 | ////////// MPEG4GenericBufferedPacket and MPEG4GenericBufferedPacketFactory |
26 | |
27 | class MPEG4GenericBufferedPacket: public BufferedPacket { |
28 | public: |
29 | MPEG4GenericBufferedPacket(MPEG4GenericRTPSource* ourSource); |
30 | virtual ~MPEG4GenericBufferedPacket(); |
31 | |
32 | private: // redefined virtual functions |
33 | virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr, |
34 | unsigned dataSize); |
35 | private: |
36 | MPEG4GenericRTPSource* fOurSource; |
37 | }; |
38 | |
39 | class MPEG4GenericBufferedPacketFactory: public BufferedPacketFactory { |
40 | private: // redefined virtual functions |
41 | virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource); |
42 | }; |
43 | |
44 | |
45 | ////////// AUHeader ////////// |
46 | struct { |
47 | unsigned ; |
48 | unsigned ; // indexDelta for the 2nd & subsequent headers |
49 | }; |
50 | |
51 | |
52 | ///////// MPEG4GenericRTPSource implementation //////// |
53 | |
54 | //##### NOTE: INCOMPLETE!!! Support more modes, and interleaving ##### |
55 | |
56 | MPEG4GenericRTPSource* |
57 | MPEG4GenericRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs, |
58 | unsigned char rtpPayloadFormat, |
59 | unsigned rtpTimestampFrequency, |
60 | char const* mediumName, |
61 | char const* mode, |
62 | unsigned sizeLength, unsigned indexLength, |
63 | unsigned indexDeltaLength |
64 | ) { |
65 | return new MPEG4GenericRTPSource(env, RTPgs, rtpPayloadFormat, |
66 | rtpTimestampFrequency, mediumName, |
67 | mode, sizeLength, indexLength, |
68 | indexDeltaLength |
69 | ); |
70 | } |
71 | |
72 | MPEG4GenericRTPSource |
73 | ::MPEG4GenericRTPSource(UsageEnvironment& env, Groupsock* RTPgs, |
74 | unsigned char rtpPayloadFormat, |
75 | unsigned rtpTimestampFrequency, |
76 | char const* mediumName, |
77 | char const* mode, |
78 | unsigned sizeLength, unsigned indexLength, |
79 | unsigned indexDeltaLength |
80 | ) |
81 | : MultiFramedRTPSource(env, RTPgs, |
82 | rtpPayloadFormat, rtpTimestampFrequency, |
83 | new MPEG4GenericBufferedPacketFactory), |
84 | fSizeLength(sizeLength), fIndexLength(indexLength), |
85 | fIndexDeltaLength(indexDeltaLength), |
86 | fNumAUHeaders(0), fNextAUHeader(0), fAUHeaders(NULL) { |
87 | unsigned mimeTypeLength = |
88 | strlen(mediumName) + 14 /* strlen("/MPEG4-GENERIC") */ + 1; |
89 | fMIMEType = new char[mimeTypeLength]; |
90 | if (fMIMEType != NULL) { |
91 | sprintf(fMIMEType, "%s/MPEG4-GENERIC" , mediumName); |
92 | } |
93 | |
94 | fMode = strDup(mode); |
95 | // Check for a "mode" that we don't yet support: //##### |
96 | if (mode == NULL || |
97 | (strcmp(mode, "aac-hbr" ) != 0 && strcmp(mode, "generic" ) != 0)) { |
98 | envir() << "MPEG4GenericRTPSource Warning: Unknown or unsupported \"mode\": " |
99 | << mode << "\n" ; |
100 | } |
101 | } |
102 | |
103 | MPEG4GenericRTPSource::~MPEG4GenericRTPSource() { |
104 | delete[] fAUHeaders; |
105 | delete[] fMode; |
106 | delete[] fMIMEType; |
107 | } |
108 | |
109 | Boolean MPEG4GenericRTPSource |
110 | ::(BufferedPacket* packet, |
111 | unsigned& ) { |
112 | unsigned char* = packet->data(); |
113 | unsigned packetSize = packet->dataSize(); |
114 | |
115 | fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame; |
116 | // whether the *previous* packet ended a frame |
117 | |
118 | // The RTP "M" (marker) bit indicates the last fragment of a frame: |
119 | fCurrentPacketCompletesFrame = packet->rtpMarkerBit(); |
120 | |
121 | // default values: |
122 | resultSpecialHeaderSize = 0; |
123 | fNumAUHeaders = 0; |
124 | fNextAUHeader = 0; |
125 | delete[] fAUHeaders; fAUHeaders = NULL; |
126 | |
127 | if (fSizeLength > 0) { |
128 | // The packet begins with a "AU Header Section". Parse it, to |
129 | // determine the "AU-header"s for each frame present in this packet: |
130 | resultSpecialHeaderSize += 2; |
131 | if (packetSize < resultSpecialHeaderSize) return False; |
132 | |
133 | unsigned = (headerStart[0]<<8)|headerStart[1]; |
134 | unsigned = (AU_headers_length+7)/8; |
135 | if (packetSize |
136 | < resultSpecialHeaderSize + AU_headers_length_bytes) return False; |
137 | resultSpecialHeaderSize += AU_headers_length_bytes; |
138 | |
139 | // Figure out how many AU-headers are present in the packet: |
140 | int bitsAvail = AU_headers_length - (fSizeLength + fIndexLength); |
141 | if (bitsAvail >= 0 && (fSizeLength + fIndexDeltaLength) > 0) { |
142 | fNumAUHeaders = 1 + bitsAvail/(fSizeLength + fIndexDeltaLength); |
143 | } |
144 | if (fNumAUHeaders > 0) { |
145 | fAUHeaders = new AUHeader[fNumAUHeaders]; |
146 | // Fill in each header: |
147 | BitVector bv(&headerStart[2], 0, AU_headers_length); |
148 | fAUHeaders[0].size = bv.getBits(fSizeLength); |
149 | fAUHeaders[0].index = bv.getBits(fIndexLength); |
150 | |
151 | for (unsigned i = 1; i < fNumAUHeaders; ++i) { |
152 | fAUHeaders[i].size = bv.getBits(fSizeLength); |
153 | fAUHeaders[i].index = bv.getBits(fIndexDeltaLength); |
154 | } |
155 | } |
156 | |
157 | } |
158 | |
159 | return True; |
160 | } |
161 | |
162 | char const* MPEG4GenericRTPSource::MIMEtype() const { |
163 | return fMIMEType; |
164 | } |
165 | |
166 | |
167 | ////////// MPEG4GenericBufferedPacket |
168 | ////////// and MPEG4GenericBufferedPacketFactory implementation |
169 | |
170 | MPEG4GenericBufferedPacket |
171 | ::MPEG4GenericBufferedPacket(MPEG4GenericRTPSource* ourSource) |
172 | : fOurSource(ourSource) { |
173 | } |
174 | |
175 | MPEG4GenericBufferedPacket::~MPEG4GenericBufferedPacket() { |
176 | } |
177 | |
178 | unsigned MPEG4GenericBufferedPacket |
179 | ::nextEnclosedFrameSize(unsigned char*& /*framePtr*/, unsigned dataSize) { |
180 | // WE CURRENTLY DON'T IMPLEMENT INTERLEAVING. FIX THIS! ##### |
181 | AUHeader* = fOurSource->fAUHeaders; |
182 | if (auHeader == NULL) return dataSize; |
183 | unsigned = fOurSource->fNumAUHeaders; |
184 | |
185 | if (fOurSource->fNextAUHeader >= numAUHeaders) { |
186 | fOurSource->envir() << "MPEG4GenericBufferedPacket::nextEnclosedFrameSize(" |
187 | << dataSize << "): data error (" |
188 | << auHeader << "," << fOurSource->fNextAUHeader |
189 | << "," << numAUHeaders << ")!\n" ; |
190 | return dataSize; |
191 | } |
192 | |
193 | auHeader = &auHeader[fOurSource->fNextAUHeader++]; |
194 | return auHeader->size <= dataSize ? auHeader->size : dataSize; |
195 | } |
196 | |
197 | BufferedPacket* MPEG4GenericBufferedPacketFactory |
198 | ::createNewPacket(MultiFramedRTPSource* ourSource) { |
199 | return new MPEG4GenericBufferedPacket((MPEG4GenericRTPSource*)ourSource); |
200 | } |
201 | |
202 | |
203 | ////////// samplingFrequencyFromAudioSpecificConfig() implementation ////////// |
204 | |
205 | static unsigned const samplingFrequencyFromIndex[16] = { |
206 | 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, |
207 | 16000, 12000, 11025, 8000, 7350, 0, 0, 0 |
208 | }; |
209 | |
210 | unsigned samplingFrequencyFromAudioSpecificConfig(char const* configStr) { |
211 | unsigned char* config = NULL; |
212 | unsigned result = 0; // if returned, indicates an error |
213 | |
214 | do { |
215 | // Begin by parsing the config string: |
216 | unsigned configSize; |
217 | config = parseGeneralConfigStr(configStr, configSize); |
218 | if (config == NULL) break; |
219 | |
220 | if (configSize < 2) break; |
221 | unsigned char samplingFrequencyIndex = ((config[0]&0x07)<<1) | (config[1]>>7); |
222 | if (samplingFrequencyIndex < 15) { |
223 | result = samplingFrequencyFromIndex[samplingFrequencyIndex]; |
224 | break; |
225 | } |
226 | |
227 | // Index == 15 means that the actual frequency is next (24 bits): |
228 | if (configSize < 5) break; |
229 | result = ((config[1]&0x7F)<<17) | (config[2]<<9) | (config[3]<<1) | (config[4]>>7); |
230 | } while (0); |
231 | |
232 | delete[] config; |
233 | return result; |
234 | } |
235 | |