File: QuickTimeGenericRTPSource.cpp

package info (click to toggle)
liblivemedia 2005.04.01-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 2,620 kB
  • ctags: 4,358
  • sloc: cpp: 33,542; ansic: 926; sh: 73; makefile: 62
file content (274 lines) | stat: -rw-r--r-- 9,772 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/**********
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 2.1 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.,
59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2004 Live Networks, Inc.  All rights reserved.
// RTP Sources containing generic QuickTime stream data, as defined in
//     <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
// Implementation

#include "QuickTimeGenericRTPSource.hh"

///// QTGenericBufferedPacket and QTGenericBufferedPacketFactory /////

// A subclass of BufferedPacket, used to separate out
// individual frames (when PCK == 2)

class QTGenericBufferedPacket: public BufferedPacket {
public:
  QTGenericBufferedPacket(QuickTimeGenericRTPSource& ourSource);
  virtual ~QTGenericBufferedPacket();

private: // redefined virtual functions
  virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr,
					 unsigned dataSize);
private:
  QuickTimeGenericRTPSource& fOurSource;
};

class QTGenericBufferedPacketFactory: public BufferedPacketFactory {
private: // redefined virtual functions
  virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
};


////////// QuickTimeGenericRTPSource //////////

QuickTimeGenericRTPSource*
QuickTimeGenericRTPSource::createNew(UsageEnvironment& env,
				     Groupsock* RTPgs,
				     unsigned char rtpPayloadFormat,
				     unsigned rtpTimestampFrequency,
				     char const* mimeTypeString) {
  return new QuickTimeGenericRTPSource(env, RTPgs, rtpPayloadFormat,
				       rtpTimestampFrequency,
				       mimeTypeString);
}

QuickTimeGenericRTPSource
::QuickTimeGenericRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
			    unsigned char rtpPayloadFormat,
			    unsigned rtpTimestampFrequency,
			    char const* mimeTypeString)
  : MultiFramedRTPSource(env, RTPgs,
			 rtpPayloadFormat, rtpTimestampFrequency,
			 new QTGenericBufferedPacketFactory),
    fMIMEtypeString(strDup(mimeTypeString)) {
  qtState.PCK = 0;
  qtState.timescale = 0;
  qtState.sdAtom = NULL;
  qtState.sdAtomSize = qtState.width = qtState.height = 0;
}

QuickTimeGenericRTPSource::~QuickTimeGenericRTPSource() {
  delete[] qtState.sdAtom;
  delete[] (char*)fMIMEtypeString;
}

Boolean QuickTimeGenericRTPSource
::processSpecialHeader(BufferedPacket* packet,
                       unsigned& resultSpecialHeaderSize) {
  unsigned char* headerStart = packet->data();
  unsigned packetSize = packet->dataSize();

  // The "QuickTime Header" must be at least 4 bytes in size:
  // Extract the known fields from the first 4 bytes:
  unsigned expectedHeaderSize = 4;
  if (packetSize < expectedHeaderSize) return False;

  unsigned char VER = (headerStart[0]&0xF0)>>4;
  if (VER > 1) return False; // unknown header version
  qtState.PCK = (headerStart[0]&0x0C)>>2;
#ifdef DEBUG
  Boolean S = (headerStart[0]&0x02) != 0;
#endif
  Boolean Q = (headerStart[0]&0x01) != 0;

  Boolean L = (headerStart[1]&0x80) != 0;

#ifdef DEBUG
  Boolean D = (headerStart[2]&0x80) != 0;
  unsigned short payloadId = ((headerStart[2]&0x7F)<<8)|headerStart[3];
#endif
  headerStart += 4;

#ifdef DEBUG
  fprintf(stderr, "PCK: %d, S: %d, Q: %d, L: %d, D: %d, payloadId: %d\n", qtState.PCK, S, Q, L, D, payloadId);
#endif

  if (Q) { // A "QuickTime Payload Description" follows
    expectedHeaderSize += 4;
    if (packetSize < expectedHeaderSize) return False;

#ifdef DEBUG
    Boolean K = (headerStart[0]&0x80) != 0;
    Boolean F = (headerStart[0]&0x40) != 0;
    Boolean A = (headerStart[0]&0x20) != 0;
    Boolean Z = (headerStart[0]&0x10) != 0;
#endif
    unsigned payloadDescriptionLength = (headerStart[2]<<8)|headerStart[3];
    headerStart += 4;
    
#ifdef DEBUG
    fprintf(stderr, "\tK: %d, F: %d, A: %d, Z: %d, payloadDescriptionLength: %d\n", K, F, A, Z, payloadDescriptionLength);
#endif
    // Make sure "payloadDescriptionLength" is valid
    if (payloadDescriptionLength < 12) return False;
    expectedHeaderSize += (payloadDescriptionLength - 4);
    unsigned nonPaddedSize = expectedHeaderSize;
    expectedHeaderSize += 3;
    expectedHeaderSize -= expectedHeaderSize%4; // adds padding
    if (packetSize < expectedHeaderSize) return False;
    unsigned char padding = expectedHeaderSize - nonPaddedSize; 

#ifdef DEBUG
    unsigned mediaType = (headerStart[0]<<24)|(headerStart[1]<<16)
      |(headerStart[2]<<8)|headerStart[3];
#endif
    qtState.timescale = (headerStart[4]<<24)|(headerStart[5]<<16)
      |(headerStart[6]<<8)|headerStart[7];
    headerStart += 8;

    payloadDescriptionLength -= 12;
#ifdef DEBUG
    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);
#endif

    while (payloadDescriptionLength > 3) {
      unsigned short tlvLength = (headerStart[0]<<8)|headerStart[1];
      unsigned short tlvType = (headerStart[2]<<8)|headerStart[3];
      payloadDescriptionLength -= 4;
      if (tlvLength > payloadDescriptionLength) return False; // bad TLV
      headerStart += 4;
#ifdef DEBUG
      fprintf(stderr, "\t\tTLV '%c%c', length %d, leaving %d remaining bytes\n", tlvType>>8, tlvType&0xFF, tlvLength, payloadDescriptionLength - tlvLength);
      for (int i = 0; i < tlvLength; ++i) fprintf(stderr, "%02x:", headerStart[i]); fprintf(stderr, "\n");
#endif

      // Check for 'TLV's that we can use for our 'qtState'
      switch (tlvType) {
      case ('s'<<8|'d'): { // session description atom
	// Sanity check: the first 4 bytes of this must equal "tlvLength":
	unsigned atomLength  = (headerStart[0]<<24)|(headerStart[1]<<16)
	  |(headerStart[2]<<8)|(headerStart[3]);
	if (atomLength != (unsigned)tlvLength) break;

	delete[] qtState.sdAtom; qtState.sdAtom = new char[tlvLength];
	memmove(qtState.sdAtom, headerStart, tlvLength);
	qtState.sdAtomSize = tlvLength;
	break;
      }
      case ('t'<<8|'w'): { // track width
	qtState.width = (headerStart[0]<<8)|headerStart[1];
	break;
      }
      case ('t'<<8|'h'): { // track height
	qtState.height = (headerStart[0]<<8)|headerStart[1];
	break;
      }
      }

      payloadDescriptionLength -= tlvLength;
      headerStart += tlvLength;
    }
    if (payloadDescriptionLength > 0) return False; // malformed TLV data
    headerStart += padding;
  }

  if (L) { // Sample-Specific info follows
    expectedHeaderSize += 4;
    if (packetSize < expectedHeaderSize) return False;

    unsigned ssInfoLength = (headerStart[2]<<8)|headerStart[3];
    headerStart += 4;
    
#ifdef DEBUG
    fprintf(stderr, "\tssInfoLength: %d\n", ssInfoLength);
#endif
    // Make sure "ssInfoLength" is valid
    if (ssInfoLength < 4) return False;
    expectedHeaderSize += (ssInfoLength - 4);
    unsigned nonPaddedSize = expectedHeaderSize;
    expectedHeaderSize += 3;
    expectedHeaderSize -= expectedHeaderSize%4; // adds padding
    if (packetSize < expectedHeaderSize) return False;
    unsigned char padding = expectedHeaderSize - nonPaddedSize; 

    ssInfoLength -= 4;
    while (ssInfoLength > 3) {
      unsigned short tlvLength = (headerStart[0]<<8)|headerStart[1];
#ifdef DEBUG
      unsigned short tlvType = (headerStart[2]<<8)|headerStart[3];
#endif
      ssInfoLength -= 4;
      if (tlvLength > ssInfoLength) return False; // bad TLV
#ifdef DEBUG
      fprintf(stderr, "\t\tTLV '%c%c', length %d, leaving %d remaining bytes\n", tlvType>>8, tlvType&0xFF, tlvLength, ssInfoLength - tlvLength);
      for (int i = 0; i < tlvLength; ++i) fprintf(stderr, "%02x:", headerStart[4+i]); fprintf(stderr, "\n");
#endif
      ssInfoLength -= tlvLength;
      headerStart += 4 + tlvLength;
    }
    if (ssInfoLength > 0) return False; // malformed TLV data
    headerStart += padding;
  }

  fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame;
          // whether the *previous* packet ended a frame
  fCurrentPacketCompletesFrame = packet->rtpMarkerBit();

  resultSpecialHeaderSize = expectedHeaderSize;
#ifdef DEBUG
  fprintf(stderr, "Result special header size: %d\n", resultSpecialHeaderSize);
#endif
  return True;
}

char const* QuickTimeGenericRTPSource::MIMEtype() const {
  if (fMIMEtypeString == NULL) return MultiFramedRTPSource::MIMEtype();

  return fMIMEtypeString;
}


////////// QTGenericBufferedPacket and QTGenericBufferedPacketFactory impl

QTGenericBufferedPacket
::QTGenericBufferedPacket(QuickTimeGenericRTPSource& ourSource)
  : fOurSource(ourSource) {
}

QTGenericBufferedPacket::~QTGenericBufferedPacket() {
}

unsigned QTGenericBufferedPacket::
  nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) {
  // We use the entire packet for a frame, unless "PCK" == 2
  if (fOurSource.qtState.PCK != 2) return dataSize;

  if (dataSize < 8) return 0; // sanity check

  unsigned short sampleLength = (framePtr[2]<<8)|framePtr[3];
  // later, extract and use the "timestamp" field #####
  framePtr += 8;
  dataSize -= 8;
  
  return sampleLength < dataSize ? sampleLength : dataSize;
}

BufferedPacket* QTGenericBufferedPacketFactory
::createNewPacket(MultiFramedRTPSource* ourSource) {
  return new QTGenericBufferedPacket((QuickTimeGenericRTPSource&)(*ourSource));
}