1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 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 Sources containing generic QuickTime stream data, as defined in
19// <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
20// Implementation
21
22#include "QuickTimeGenericRTPSource.hh"
23
24///// QTGenericBufferedPacket and QTGenericBufferedPacketFactory /////
25
26// A subclass of BufferedPacket, used to separate out
27// individual frames (when PCK == 2)
28
29class QTGenericBufferedPacket: public BufferedPacket {
30public:
31 QTGenericBufferedPacket(QuickTimeGenericRTPSource& ourSource);
32 virtual ~QTGenericBufferedPacket();
33
34private: // redefined virtual functions
35 virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr,
36 unsigned dataSize);
37private:
38 QuickTimeGenericRTPSource& fOurSource;
39};
40
41class QTGenericBufferedPacketFactory: public BufferedPacketFactory {
42private: // redefined virtual functions
43 virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
44};
45
46
47////////// QuickTimeGenericRTPSource //////////
48
49QuickTimeGenericRTPSource*
50QuickTimeGenericRTPSource::createNew(UsageEnvironment& env,
51 Groupsock* RTPgs,
52 unsigned char rtpPayloadFormat,
53 unsigned rtpTimestampFrequency,
54 char const* mimeTypeString) {
55 return new QuickTimeGenericRTPSource(env, RTPgs, rtpPayloadFormat,
56 rtpTimestampFrequency,
57 mimeTypeString);
58}
59
60QuickTimeGenericRTPSource
61::QuickTimeGenericRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
62 unsigned char rtpPayloadFormat,
63 unsigned rtpTimestampFrequency,
64 char const* mimeTypeString)
65 : MultiFramedRTPSource(env, RTPgs,
66 rtpPayloadFormat, rtpTimestampFrequency,
67 new QTGenericBufferedPacketFactory),
68 fMIMEtypeString(strDup(mimeTypeString)) {
69 qtState.PCK = 0;
70 qtState.timescale = 0;
71 qtState.sdAtom = NULL;
72 qtState.sdAtomSize = qtState.width = qtState.height = 0;
73}
74
75QuickTimeGenericRTPSource::~QuickTimeGenericRTPSource() {
76 delete[] qtState.sdAtom;
77 delete[] (char*)fMIMEtypeString;
78}
79
80Boolean QuickTimeGenericRTPSource
81::processSpecialHeader(BufferedPacket* packet,
82 unsigned& resultSpecialHeaderSize) {
83 unsigned char* headerStart = packet->data();
84 unsigned packetSize = packet->dataSize();
85
86 // The "QuickTime Header" must be at least 4 bytes in size:
87 // Extract the known fields from the first 4 bytes:
88 unsigned expectedHeaderSize = 4;
89 if (packetSize < expectedHeaderSize) return False;
90
91 unsigned char VER = (headerStart[0]&0xF0)>>4;
92 if (VER > 1) return False; // unknown header version
93 qtState.PCK = (headerStart[0]&0x0C)>>2;
94#ifdef DEBUG
95 Boolean S = (headerStart[0]&0x02) != 0;
96#endif
97 Boolean Q = (headerStart[0]&0x01) != 0;
98
99 Boolean L = (headerStart[1]&0x80) != 0;
100
101#ifdef DEBUG
102 Boolean D = (headerStart[2]&0x80) != 0;
103 unsigned short payloadId = ((headerStart[2]&0x7F)<<8)|headerStart[3];
104#endif
105 headerStart += 4;
106
107#ifdef DEBUG
108 fprintf(stderr, "PCK: %d, S: %d, Q: %d, L: %d, D: %d, payloadId: %d\n", qtState.PCK, S, Q, L, D, payloadId);
109#endif
110
111 if (Q) { // A "QuickTime Payload Description" follows
112 expectedHeaderSize += 4;
113 if (packetSize < expectedHeaderSize) return False;
114
115#ifdef DEBUG
116 Boolean K = (headerStart[0]&0x80) != 0;
117 Boolean F = (headerStart[0]&0x40) != 0;
118 Boolean A = (headerStart[0]&0x20) != 0;
119 Boolean Z = (headerStart[0]&0x10) != 0;
120#endif
121 unsigned payloadDescriptionLength = (headerStart[2]<<8)|headerStart[3];
122 headerStart += 4;
123
124#ifdef DEBUG
125 fprintf(stderr, "\tK: %d, F: %d, A: %d, Z: %d, payloadDescriptionLength: %d\n", K, F, A, Z, payloadDescriptionLength);
126#endif
127 // Make sure "payloadDescriptionLength" is valid
128 if (payloadDescriptionLength < 12) return False;
129 expectedHeaderSize += (payloadDescriptionLength - 4);
130 unsigned nonPaddedSize = expectedHeaderSize;
131 expectedHeaderSize += 3;
132 expectedHeaderSize -= expectedHeaderSize%4; // adds padding
133 if (packetSize < expectedHeaderSize) return False;
134 unsigned char padding = expectedHeaderSize - nonPaddedSize;
135
136#ifdef DEBUG
137 unsigned mediaType = (headerStart[0]<<24)|(headerStart[1]<<16)
138 |(headerStart[2]<<8)|headerStart[3];
139#endif
140 qtState.timescale = (headerStart[4]<<24)|(headerStart[5]<<16)
141 |(headerStart[6]<<8)|headerStart[7];
142 headerStart += 8;
143
144 payloadDescriptionLength -= 12;
145#ifdef DEBUG
146 fprintf(stderr, "\tmediaType: '%c%c%c%c', timescale: %d, %d bytes of TLVs left\n", mediaType>>24, (mediaType&0xFF0000)>>16, (mediaType&0xFF00)>>8, mediaType&0xFF, qtState.timescale, payloadDescriptionLength);
147#endif
148
149 while (payloadDescriptionLength > 3) {
150 unsigned short tlvLength = (headerStart[0]<<8)|headerStart[1];
151 unsigned short tlvType = (headerStart[2]<<8)|headerStart[3];
152 payloadDescriptionLength -= 4;
153 if (tlvLength > payloadDescriptionLength) return False; // bad TLV
154 headerStart += 4;
155#ifdef DEBUG
156 fprintf(stderr, "\t\tTLV '%c%c', length %d, leaving %d remaining bytes\n", tlvType>>8, tlvType&0xFF, tlvLength, payloadDescriptionLength - tlvLength);
157 for (int i = 0; i < tlvLength; ++i) fprintf(stderr, "%02x:", headerStart[i]); fprintf(stderr, "\n");
158#endif
159
160 // Check for 'TLV's that we can use for our 'qtState'
161 switch (tlvType) {
162 case ('s'<<8|'d'): { // session description atom
163 // Sanity check: the first 4 bytes of this must equal "tlvLength":
164 unsigned atomLength = (headerStart[0]<<24)|(headerStart[1]<<16)
165 |(headerStart[2]<<8)|(headerStart[3]);
166 if (atomLength != (unsigned)tlvLength) break;
167
168 delete[] qtState.sdAtom; qtState.sdAtom = new char[tlvLength];
169 memmove(qtState.sdAtom, headerStart, tlvLength);
170 qtState.sdAtomSize = tlvLength;
171 break;
172 }
173 case ('t'<<8|'w'): { // track width
174 qtState.width = (headerStart[0]<<8)|headerStart[1];
175 break;
176 }
177 case ('t'<<8|'h'): { // track height
178 qtState.height = (headerStart[0]<<8)|headerStart[1];
179 break;
180 }
181 }
182
183 payloadDescriptionLength -= tlvLength;
184 headerStart += tlvLength;
185 }
186 if (payloadDescriptionLength > 0) return False; // malformed TLV data
187 headerStart += padding;
188 }
189
190 if (L) { // Sample-Specific info follows
191 expectedHeaderSize += 4;
192 if (packetSize < expectedHeaderSize) return False;
193
194 unsigned ssInfoLength = (headerStart[2]<<8)|headerStart[3];
195 headerStart += 4;
196
197#ifdef DEBUG
198 fprintf(stderr, "\tssInfoLength: %d\n", ssInfoLength);
199#endif
200 // Make sure "ssInfoLength" is valid
201 if (ssInfoLength < 4) return False;
202 expectedHeaderSize += (ssInfoLength - 4);
203 unsigned nonPaddedSize = expectedHeaderSize;
204 expectedHeaderSize += 3;
205 expectedHeaderSize -= expectedHeaderSize%4; // adds padding
206 if (packetSize < expectedHeaderSize) return False;
207 unsigned char padding = expectedHeaderSize - nonPaddedSize;
208
209 ssInfoLength -= 4;
210 while (ssInfoLength > 3) {
211 unsigned short tlvLength = (headerStart[0]<<8)|headerStart[1];
212#ifdef DEBUG
213 unsigned short tlvType = (headerStart[2]<<8)|headerStart[3];
214#endif
215 ssInfoLength -= 4;
216 if (tlvLength > ssInfoLength) return False; // bad TLV
217#ifdef DEBUG
218 fprintf(stderr, "\t\tTLV '%c%c', length %d, leaving %d remaining bytes\n", tlvType>>8, tlvType&0xFF, tlvLength, ssInfoLength - tlvLength);
219 for (int i = 0; i < tlvLength; ++i) fprintf(stderr, "%02x:", headerStart[4+i]); fprintf(stderr, "\n");
220#endif
221 ssInfoLength -= tlvLength;
222 headerStart += 4 + tlvLength;
223 }
224 if (ssInfoLength > 0) return False; // malformed TLV data
225 headerStart += padding;
226 }
227
228 fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame;
229 // whether the *previous* packet ended a frame
230 fCurrentPacketCompletesFrame = packet->rtpMarkerBit();
231
232 resultSpecialHeaderSize = expectedHeaderSize;
233#ifdef DEBUG
234 fprintf(stderr, "Result special header size: %d\n", resultSpecialHeaderSize);
235#endif
236 return True;
237}
238
239char const* QuickTimeGenericRTPSource::MIMEtype() const {
240 if (fMIMEtypeString == NULL) return MultiFramedRTPSource::MIMEtype();
241
242 return fMIMEtypeString;
243}
244
245
246////////// QTGenericBufferedPacket and QTGenericBufferedPacketFactory impl
247
248QTGenericBufferedPacket
249::QTGenericBufferedPacket(QuickTimeGenericRTPSource& ourSource)
250 : fOurSource(ourSource) {
251}
252
253QTGenericBufferedPacket::~QTGenericBufferedPacket() {
254}
255
256unsigned QTGenericBufferedPacket::
257 nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) {
258 // We use the entire packet for a frame, unless "PCK" == 2
259 if (fOurSource.qtState.PCK != 2) return dataSize;
260
261 if (dataSize < 8) return 0; // sanity check
262
263 unsigned short sampleLength = (framePtr[2]<<8)|framePtr[3];
264 // later, extract and use the "timestamp" field #####
265 framePtr += 8;
266 dataSize -= 8;
267
268 return sampleLength < dataSize ? sampleLength : dataSize;
269}
270
271BufferedPacket* QTGenericBufferedPacketFactory
272::createNewPacket(MultiFramedRTPSource* ourSource) {
273 return new QTGenericBufferedPacket((QuickTimeGenericRTPSource&)(*ourSource));
274}
275