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// A class for generating MPEG-2 Transport Stream from one or more input
19// Elementary Stream data sources
20// Implementation
21
22#include "MPEG2TransportStreamMultiplexor.hh"
23
24#define TRANSPORT_PACKET_SIZE 188
25
26#define PAT_PERIOD_IF_UNTIMED 100 // # of packets between Program Association Tables (if not timed)
27#define PMT_PERIOD_IF_UNTIMED 500 // # of packets between Program Map Tables (if not timed)
28
29void MPEG2TransportStreamMultiplexor
30::setTimedSegmentation(unsigned segmentationDuration,
31 onEndOfSegmentFunc* onEndOfSegmentFunc,
32 void* onEndOfSegmentClientData) {
33 fSegmentationDuration = segmentationDuration;
34 fOnEndOfSegmentFunc = onEndOfSegmentFunc;
35 fOnEndOfSegmentClientData = onEndOfSegmentClientData;
36}
37
38MPEG2TransportStreamMultiplexor
39::MPEG2TransportStreamMultiplexor(UsageEnvironment& env)
40 : FramedSource(env),
41 fHaveVideoStreams(True/*by default*/),
42 fOutgoingPacketCounter(0), fProgramMapVersion(0xFF),
43 fPreviousInputProgramMapVersion(0xFF), fCurrentInputProgramMapVersion(0),
44 fPCR_PID(0), fCurrentPID(0),
45 fInputBuffer(NULL), fInputBufferSize(0), fInputBufferBytesUsed(0),
46 fIsFirstAdaptationField(True), fSegmentationDuration(0), fSegmentationIndication(1),
47 fCurrentSegmentDuration(0.0), fPreviousPTS(0.0),
48 fOnEndOfSegmentFunc(NULL), fOnEndOfSegmentClientData(NULL) {
49 for (unsigned i = 0; i < PID_TABLE_SIZE; ++i) {
50 fPIDState[i].counter = 0;
51 fPIDState[i].streamType = 0;
52 }
53}
54
55MPEG2TransportStreamMultiplexor::~MPEG2TransportStreamMultiplexor() {
56}
57
58Boolean MPEG2TransportStreamMultiplexor::isMPEG2TransportStreamMultiplexor() const {
59 return True;
60}
61
62void MPEG2TransportStreamMultiplexor::doGetNextFrame() {
63 if (fInputBufferBytesUsed >= fInputBufferSize) {
64 // No more bytes are available from the current buffer.
65 // Arrange to read a new one.
66 awaitNewBuffer(fInputBuffer);
67 return;
68 }
69
70 do {
71 // Periodically return a Program Association Table packet instead:
72 if ((segmentationIsTimed() && fSegmentationIndication == 1)
73 || (!segmentationIsTimed() && fOutgoingPacketCounter % PAT_PERIOD_IF_UNTIMED == 0)) {
74 ++fOutgoingPacketCounter;
75 deliverPATPacket();
76 fSegmentationIndication = 2; // for next time
77 break;
78 }
79 ++fOutgoingPacketCounter;
80
81 // Periodically (or when we see a new PID) return a Program Map Table instead:
82 Boolean programMapHasChanged = fCurrentInputProgramMapVersion != fPreviousInputProgramMapVersion;
83 if (programMapHasChanged
84 || (segmentationIsTimed() && fSegmentationIndication == 2)
85 || (!segmentationIsTimed() && fOutgoingPacketCounter % PMT_PERIOD_IF_UNTIMED == 0)) {
86 if (programMapHasChanged) { // reset values for next time:
87 fPreviousInputProgramMapVersion = fCurrentInputProgramMapVersion;
88 }
89 deliverPMTPacket(programMapHasChanged);
90 fSegmentationIndication = 0; // for next time
91 break;
92 }
93
94 // Normal case: Deliver (or continue delivering) the recently-read data:
95 deliverDataToClient(fCurrentPID, fInputBuffer, fInputBufferSize,
96 fInputBufferBytesUsed);
97 } while (0);
98
99 // NEED TO SET fPresentationTime, durationInMicroseconds #####
100 // Complete the delivery to the client:
101 if ((fOutgoingPacketCounter%10) == 0) {
102 // To avoid excessive recursion (and stack overflow) caused by excessively large input frames,
103 // occasionally return to the event loop to do this:
104 nextTask() = envir().taskScheduler().scheduleDelayedTask(0, (TaskFunc*)FramedSource::afterGetting, this);
105 } else {
106 afterGetting(this);
107 }
108}
109
110void MPEG2TransportStreamMultiplexor
111::handleNewBuffer(unsigned char* buffer, unsigned bufferSize,
112 int mpegVersion, MPEG1or2Demux::SCR scr, int16_t PID) {
113 if (bufferSize < 4) return;
114 fInputBuffer = buffer;
115 fInputBufferSize = bufferSize;
116 fInputBufferBytesUsed = 0;
117
118 u_int8_t stream_id = fInputBuffer[3];
119 // Use "stream_id" directly as our PID.
120 // Also, figure out the Program Map 'stream type' from this.
121 if (stream_id == 0xBE) { // padding_stream; ignore
122 fInputBufferSize = 0;
123 } else if (stream_id == 0xBC) { // program_stream_map
124 setProgramStreamMap(fInputBufferSize);
125 fInputBufferSize = 0; // then, ignore the buffer
126 } else {
127 if (PID == -1)
128 fCurrentPID = stream_id;
129 else
130 fCurrentPID = PID;
131
132 // Set the stream's type:
133 u_int8_t& streamType = fPIDState[fCurrentPID].streamType; // alias
134
135 if (streamType == 0) {
136 // Instead, set the stream's type to default values, based on whether
137 // the stream is audio or video, and whether it's MPEG-1 or MPEG-2:
138 if ((stream_id&0xF0) == 0xE0) { // video
139 streamType = mpegVersion == 1 ? 1 : mpegVersion == 2 ? 2 : mpegVersion == 4 ? 0x10 :
140 mpegVersion == 5/*H.264*/ ? 0x1B : 0x24/*assume H.265*/;
141 } else if ((stream_id&0xE0) == 0xC0) { // audio
142 streamType = mpegVersion == 1 ? 3 : mpegVersion == 2 ? 4 : mpegVersion == 3 ? 6 : 0xF;
143 } else if (stream_id == 0xBD) { // private_stream1 (usually AC-3 or Opus)
144 streamType = 0x06; // for DVB or Opus; for ATSC, use 0x81
145 } else { // something else
146 streamType = 0x81; // private
147 }
148 }
149
150 if (fPCR_PID == 0) { // set it to this stream, if it's appropriate:
151 if ((!fHaveVideoStreams && (streamType == 3 || streamType == 4 || streamType == 6 || streamType == 0xF))/* audio stream */ ||
152 (streamType == 1 || streamType == 2 || streamType == 0x10 || streamType == 0x1B || streamType == 0x24)/* video stream */) {
153 fPCR_PID = fCurrentPID; // use this stream's SCR for PCR
154 }
155 }
156 if (fCurrentPID == fPCR_PID) {
157 // Record the input's current SCR timestamp, for use as our PCR:
158 fPCR = scr;
159 }
160 }
161
162 // Now that we have new input data, retry the last delivery to the client:
163 doGetNextFrame();
164}
165
166void MPEG2TransportStreamMultiplexor
167::deliverDataToClient(u_int16_t pid, unsigned char* buffer, unsigned bufferSize,
168 unsigned& startPositionInBuffer) {
169 // Construct a new Transport packet, and deliver it to the client:
170 if (fMaxSize < TRANSPORT_PACKET_SIZE) {
171 fFrameSize = 0; // the client hasn't given us enough space; deliver nothing
172 fNumTruncatedBytes = TRANSPORT_PACKET_SIZE;
173 } else {
174 fFrameSize = TRANSPORT_PACKET_SIZE;
175 Boolean willAddPCR = pid == fPCR_PID && startPositionInBuffer == 0
176 && !(fPCR.highBit == 0 && fPCR.remainingBits == 0 && fPCR.extension == 0);
177 unsigned const numBytesAvailable = bufferSize - startPositionInBuffer;
178 unsigned numHeaderBytes = 4; // by default
179 unsigned numPCRBytes = 0; // by default
180 unsigned numPaddingBytes = 0; // by default
181 unsigned numDataBytes;
182 u_int8_t adaptation_field_control;
183 if (willAddPCR) {
184 adaptation_field_control = 0x30;
185 numHeaderBytes += 2; // for the "adaptation_field_length" and flags
186 numPCRBytes = 6;
187 if (numBytesAvailable >= TRANSPORT_PACKET_SIZE - numHeaderBytes - numPCRBytes) {
188 numDataBytes = TRANSPORT_PACKET_SIZE - numHeaderBytes - numPCRBytes;
189 } else {
190 numDataBytes = numBytesAvailable;
191 numPaddingBytes
192 = TRANSPORT_PACKET_SIZE - numHeaderBytes - numPCRBytes - numDataBytes;
193 }
194 } else if (numBytesAvailable >= TRANSPORT_PACKET_SIZE - numHeaderBytes) {
195 // This is the common case
196 adaptation_field_control = 0x10;
197 numDataBytes = TRANSPORT_PACKET_SIZE - numHeaderBytes;
198 } else {
199 adaptation_field_control = 0x30;
200 ++numHeaderBytes; // for the "adaptation_field_length"
201 // ASSERT: numBytesAvailable <= TRANSPORT_PACKET_SIZE - numHeaderBytes
202 numDataBytes = numBytesAvailable;
203 if (numDataBytes < TRANSPORT_PACKET_SIZE - numHeaderBytes) {
204 ++numHeaderBytes; // for the adaptation field flags
205 numPaddingBytes = TRANSPORT_PACKET_SIZE - numHeaderBytes - numDataBytes;
206 }
207 }
208 // ASSERT: numHeaderBytes+numPCRBytes+numPaddingBytes+numDataBytes
209 // == TRANSPORT_PACKET_SIZE
210
211 // Fill in the header of the Transport Stream packet:
212 unsigned char* header = fTo;
213 *header++ = 0x47; // sync_byte
214 *header++ = ((startPositionInBuffer == 0) ? 0x40 : 0x00)|(pid>>8);
215 // transport_error_indicator, payload_unit_start_indicator, transport_priority,
216 // first 5 bits of PID
217 *header++ = pid;
218 // last 8 bits of PID
219 unsigned& continuity_counter = fPIDState[pid].counter; // alias
220 *header++ = adaptation_field_control|(continuity_counter&0x0F);
221 // transport_scrambling_control, adaptation_field_control, continuity_counter
222 ++continuity_counter;
223 if (adaptation_field_control == 0x30) {
224 // Add an adaptation field:
225 u_int8_t adaptation_field_length
226 = (numHeaderBytes == 5) ? 0 : 1 + numPCRBytes + numPaddingBytes;
227 *header++ = adaptation_field_length;
228 if (numHeaderBytes > 5) {
229 u_int8_t flags = willAddPCR ? 0x10 : 0x00;
230 if (fIsFirstAdaptationField) {
231 flags |= 0x80; // discontinuity_indicator
232 fIsFirstAdaptationField = False;
233 }
234 *header++ = flags;
235 if (willAddPCR) {
236 u_int32_t pcrHigh32Bits = (fPCR.highBit<<31) | (fPCR.remainingBits>>1);
237 u_int8_t pcrLowBit = fPCR.remainingBits&1;
238 u_int8_t extHighBit = (fPCR.extension&0x100)>>8;
239 *header++ = pcrHigh32Bits>>24;
240 *header++ = pcrHigh32Bits>>16;
241 *header++ = pcrHigh32Bits>>8;
242 *header++ = pcrHigh32Bits;
243 *header++ = (pcrLowBit<<7)|0x7E|extHighBit;
244 *header++ = (u_int8_t)fPCR.extension; // low 8 bits of extension
245
246 if (fSegmentationDuration > 0) {
247 // Use the PCR to compute the duration of the segment so far, to check whether
248 // segmentation needs to occur now:
249 double pts = fPCR.highBit ? 0x80000000/45000.0 : 0.0;
250 pts += fPCR.remainingBits/90000.0;
251 pts += fPCR.extension/27000000.0;
252
253 double lastSubSegmentDuration = fPreviousPTS == 0.0 ? 0.0 : pts - fPreviousPTS;
254 fCurrentSegmentDuration += lastSubSegmentDuration;
255
256 // Check whether we need to segment the stream now:
257 if (fCurrentSegmentDuration > (double)fSegmentationDuration
258 || fCurrentSegmentDuration + lastSubSegmentDuration > (double)fSegmentationDuration) {
259 // It's time to segment the stream.
260 if (fOnEndOfSegmentFunc != NULL) {
261 (*fOnEndOfSegmentFunc)(fOnEndOfSegmentClientData, fCurrentSegmentDuration);
262 }
263
264 fCurrentSegmentDuration = 0.0; // for next time
265 fSegmentationIndication = 1; // output a PAT next
266 }
267
268 fPreviousPTS = pts; // for next time
269 }
270 }
271 }
272 }
273
274 // Add any padding bytes:
275 for (unsigned i = 0; i < numPaddingBytes; ++i) *header++ = 0xFF;
276
277 // Finally, add the data bytes:
278 memmove(header, &buffer[startPositionInBuffer], numDataBytes);
279 startPositionInBuffer += numDataBytes;
280 }
281}
282
283#define PAT_PID 0
284#ifndef OUR_PROGRAM_NUMBER
285#define OUR_PROGRAM_NUMBER 1
286#endif
287#define OUR_PROGRAM_MAP_PID 0x1000
288
289void MPEG2TransportStreamMultiplexor::deliverPATPacket() {
290 // First, create a new buffer for the PAT packet:
291 unsigned const patSize = TRANSPORT_PACKET_SIZE - 4; // allow for the 4-byte header
292 unsigned char* patBuffer = new unsigned char[patSize];
293
294 // and fill it in:
295 unsigned char* pat = patBuffer;
296 *pat++ = 0; // pointer_field
297 *pat++ = 0; // table_id
298 *pat++ = 0xB0; // section_syntax_indicator; 0; reserved, section_length (high)
299 *pat++ = 13; // section_length (low)
300 *pat++ = 0; *pat++ = 1; // transport_stream_id
301 *pat++ = 0xC1; // reserved; version_number; current_next_indicator
302 *pat++ = 0; // section_number
303 *pat++ = 0; // last_section_number
304 *pat++ = OUR_PROGRAM_NUMBER>>8; *pat++ = OUR_PROGRAM_NUMBER; // program_number
305 *pat++ = 0xE0|(OUR_PROGRAM_MAP_PID>>8); // reserved; program_map_PID (high)
306 *pat++ = OUR_PROGRAM_MAP_PID&0xFF; // program_map_PID (low)
307
308 // Compute the CRC from the bytes we currently have (not including "pointer_field"):
309 u_int32_t crc = calculateCRC(patBuffer+1, pat - (patBuffer+1));
310 *pat++ = crc>>24; *pat++ = crc>>16; *pat++ = crc>>8; *pat++ = crc;
311
312 // Fill in the rest of the packet with padding bytes:
313 while (pat < &patBuffer[patSize]) *pat++ = 0xFF;
314
315 // Deliver the packet:
316 unsigned startPosition = 0;
317 deliverDataToClient(PAT_PID, patBuffer, patSize, startPosition);
318
319 // Finally, remove the new buffer:
320 delete[] patBuffer;
321}
322
323void MPEG2TransportStreamMultiplexor::deliverPMTPacket(Boolean hasChanged) {
324 if (hasChanged) ++fProgramMapVersion;
325
326 // First, create a new buffer for the PMT packet:
327 unsigned const pmtSize = TRANSPORT_PACKET_SIZE - 4; // allow for the 4-byte header
328 unsigned char* pmtBuffer = new unsigned char[pmtSize];
329
330 // and fill it in:
331 unsigned char* pmt = pmtBuffer;
332 *pmt++ = 0; // pointer_field
333 *pmt++ = 2; // table_id
334 *pmt++ = 0xB0; // section_syntax_indicator; 0; reserved, section_length (high)
335 unsigned char* section_lengthPtr = pmt; // save for later
336 *pmt++ = 0; // section_length (low) (fill in later)
337 *pmt++ = OUR_PROGRAM_NUMBER>>8; *pmt++ = OUR_PROGRAM_NUMBER; // program_number
338 *pmt++ = 0xC1|((fProgramMapVersion&0x1F)<<1); // reserved; version_number; current_next_indicator
339 *pmt++ = 0; // section_number
340 *pmt++ = 0; // last_section_number
341 *pmt++ = 0xE0|(fPCR_PID>>8); // reserved; PCR_PID (high)
342 *pmt++ = fPCR_PID; // PCR_PID (low)
343 *pmt++ = 0xF0; // reserved; program_info_length (high)
344 *pmt++ = 0; // program_info_length (low)
345 for (int pid = 0; pid < PID_TABLE_SIZE; ++pid) {
346 if (fPIDState[pid].streamType != 0) {
347 // This PID gets recorded in the table
348 *pmt++ = fPIDState[pid].streamType;
349 *pmt++ = 0xE0|(pid>>8); // reserved; elementary_pid (high)
350 *pmt++ = pid; // elementary_pid (low)
351 *pmt++ = 0xF0; // reserved; ES_info_length (high)
352 *pmt++ = 0; // ES_info_length (low)
353 }
354 }
355 unsigned section_length = pmt - (section_lengthPtr+1) + 4 /*for CRC*/;
356 *section_lengthPtr = section_length;
357
358 // Compute the CRC from the bytes we currently have (not including "pointer_field"):
359 u_int32_t crc = calculateCRC(pmtBuffer+1, pmt - (pmtBuffer+1));
360 *pmt++ = crc>>24; *pmt++ = crc>>16; *pmt++ = crc>>8; *pmt++ = crc;
361
362 // Fill in the rest of the packet with padding bytes:
363 while (pmt < &pmtBuffer[pmtSize]) *pmt++ = 0xFF;
364
365 // Deliver the packet:
366 unsigned startPosition = 0;
367 deliverDataToClient(OUR_PROGRAM_MAP_PID, pmtBuffer, pmtSize, startPosition);
368
369 // Finally, remove the new buffer:
370 delete[] pmtBuffer;
371}
372
373void MPEG2TransportStreamMultiplexor::setProgramStreamMap(unsigned frameSize) {
374 if (frameSize <= 16) return; // program_stream_map is too small to be useful
375 if (frameSize > 0xFF) return; // program_stream_map is too large
376
377 u_int16_t program_stream_map_length = (fInputBuffer[4]<<8) | fInputBuffer[5];
378 if ((u_int16_t)frameSize > 6+program_stream_map_length) {
379 frameSize = 6+program_stream_map_length;
380 }
381
382 u_int8_t versionByte = fInputBuffer[6];
383 if ((versionByte&0x80) == 0) return; // "current_next_indicator" is not set
384 fCurrentInputProgramMapVersion = versionByte&0x1F;
385
386 u_int16_t program_stream_info_length = (fInputBuffer[8]<<8) | fInputBuffer[9];
387 unsigned offset = 10 + program_stream_info_length; // skip over 'descriptors'
388
389 u_int16_t elementary_stream_map_length
390 = (fInputBuffer[offset]<<8) | fInputBuffer[offset+1];
391 offset += 2;
392 frameSize -= 4; // sizeof CRC_32
393 if (frameSize > offset + elementary_stream_map_length) {
394 frameSize = offset + elementary_stream_map_length;
395 }
396
397 while (offset + 4 <= frameSize) {
398 u_int8_t stream_type = fInputBuffer[offset];
399 u_int8_t elementary_stream_id = fInputBuffer[offset+1];
400
401 fPIDState[elementary_stream_id].streamType = stream_type;
402
403 u_int16_t elementary_stream_info_length
404 = (fInputBuffer[offset+2]<<8) | fInputBuffer[offset+3];
405 offset += 4 + elementary_stream_info_length;
406 }
407}
408
409static u_int32_t const CRC32[256] = {
410 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
411 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
412 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
413 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
414 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
415 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
416 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
417 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
418 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
419 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
420 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
421 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
422 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
423 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
424 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
425 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
426 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
427 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
428 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
429 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
430 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
431 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
432 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
433 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
434 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
435 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
436 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
437 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
438 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
439 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
440 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
441 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
442 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
443 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
444 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
445 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
446 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
447 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
448 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
449 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
450 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
451 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
452 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
453 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
454 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
455 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
456 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
457 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
458 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
459 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
460 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
461 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
462 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
463 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
464 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
465 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
466 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
467 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
468 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
469 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
470 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
471 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
472 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
473 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
474};
475
476u_int32_t calculateCRC(u_int8_t const* data, unsigned dataLength, u_int32_t initialValue) {
477 u_int32_t crc = initialValue;
478
479 while (dataLength-- > 0) {
480 crc = (crc<<8) ^ CRC32[(crc>>24) ^ (u_int32_t)(*data++)];
481 }
482
483 return crc;
484}
485