liveMedia/MPEG2TransportStreamMultiplexor.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2012 Live Networks, Inc.  All rights reserved.
00018 // A class for generating MPEG-2 Transport Stream from one or more input
00019 // Elementary Stream data sources
00020 // Implementation
00021 
00022 #include "MPEG2TransportStreamMultiplexor.hh"
00023 
00024 #define TRANSPORT_PACKET_SIZE 188
00025 
00026 #define PAT_FREQUENCY 100 // # of packets between Program Association Tables
00027 #define PMT_FREQUENCY 500 // # of packets between Program Map Tables
00028 
00029 #define PID_TABLE_SIZE 256
00030 
00031 MPEG2TransportStreamMultiplexor
00032 ::MPEG2TransportStreamMultiplexor(UsageEnvironment& env)
00033   : FramedSource(env),
00034     fHaveVideoStreams(True/*by default*/),
00035     fOutgoingPacketCounter(0), fProgramMapVersion(0),
00036     fPreviousInputProgramMapVersion(0xFF), fCurrentInputProgramMapVersion(0xFF),
00037     fPCR_PID(0), fCurrentPID(0),
00038     fInputBuffer(NULL), fInputBufferSize(0), fInputBufferBytesUsed(0),
00039     fIsFirstAdaptationField(True) {
00040   for (unsigned i = 0; i < PID_TABLE_SIZE; ++i) {
00041     fPIDState[i].counter = 0;
00042     fPIDState[i].streamType = 0;
00043   }
00044 }
00045 
00046 MPEG2TransportStreamMultiplexor::~MPEG2TransportStreamMultiplexor() {
00047 }
00048 
00049 void MPEG2TransportStreamMultiplexor::doGetNextFrame() {
00050   if (fInputBufferBytesUsed >= fInputBufferSize) {
00051     // No more bytes are available from the current buffer.
00052     // Arrange to read a new one.
00053     awaitNewBuffer(fInputBuffer);
00054     return;
00055   }
00056 
00057   do {
00058     // Periodically return a Program Association Table packet instead:
00059     if (fOutgoingPacketCounter++ % PAT_FREQUENCY == 0) {
00060       deliverPATPacket();
00061       break;
00062     }
00063 
00064     // Periodically (or when we see a new PID) return a Program Map Table instead:
00065     Boolean programMapHasChanged = fPIDState[fCurrentPID].counter == 0
00066       || fCurrentInputProgramMapVersion != fPreviousInputProgramMapVersion;
00067     if (fOutgoingPacketCounter % PMT_FREQUENCY == 0 || programMapHasChanged) {
00068       if (programMapHasChanged) { // reset values for next time:
00069         fPIDState[fCurrentPID].counter = 1;
00070         fPreviousInputProgramMapVersion = fCurrentInputProgramMapVersion;
00071       }
00072       deliverPMTPacket(programMapHasChanged);
00073       break;
00074     }
00075 
00076     // Normal case: Deliver (or continue delivering) the recently-read data:
00077     deliverDataToClient(fCurrentPID, fInputBuffer, fInputBufferSize,
00078                         fInputBufferBytesUsed);
00079   } while (0);
00080 
00081   // NEED TO SET fPresentationTime, durationInMicroseconds #####
00082   // Complete the delivery to the client:
00083   afterGetting(this);
00084 }
00085 
00086 void MPEG2TransportStreamMultiplexor
00087 ::handleNewBuffer(unsigned char* buffer, unsigned bufferSize,
00088                   int mpegVersion, MPEG1or2Demux::SCR scr) {
00089   if (bufferSize < 4) return;
00090   fInputBuffer = buffer;
00091   fInputBufferSize = bufferSize;
00092   fInputBufferBytesUsed = 0;
00093 
00094   u_int8_t stream_id = fInputBuffer[3];
00095   // Use "stream_id" directly as our PID.
00096   // Also, figure out the Program Map 'stream type' from this.
00097   if (stream_id == 0xBE) { // padding_stream; ignore
00098     fInputBufferSize = 0;
00099   } else if (stream_id == 0xBC) { // program_stream_map
00100     setProgramStreamMap(fInputBufferSize);
00101     fInputBufferSize = 0; // then, ignore the buffer
00102   } else {
00103     fCurrentPID = stream_id;
00104 
00105     // Set the stream's type:
00106     u_int8_t& streamType = fPIDState[fCurrentPID].streamType; // alias
00107 
00108     if (streamType == 0) {
00109       // Instead, set the stream's type to default values, based on whether
00110       // the stream is audio or video, and whether it's MPEG-1 or MPEG-2:
00111       if ((stream_id&0xF0) == 0xE0) { // video
00112         streamType = mpegVersion == 1 ? 1 : mpegVersion == 2 ? 2 : mpegVersion == 4 ? 0x10 : 0x1B;
00113       } else if ((stream_id&0xE0) == 0xC0) { // audio
00114         streamType = mpegVersion == 1 ? 3 : mpegVersion == 2 ? 4 : 0xF;
00115       } else if (stream_id == 0xBD) { // private_stream1 (usually AC-3)
00116         streamType = 0x06; // for DVB; for ATSC, use 0x81
00117       } else { // something else, e.g., AC-3 uses private_stream1 (0xBD)
00118         streamType = 0x81; // private
00119       }
00120     }
00121 
00122     if (fPCR_PID == 0) { // set it to this stream, if it's appropriate:
00123       if ((!fHaveVideoStreams && (streamType == 3 || streamType == 4 || streamType == 0xF))/* audio stream */ ||
00124           (streamType == 1 || streamType == 2 || streamType == 0x10 || streamType == 0x1B)/* video stream */) {
00125         fPCR_PID = fCurrentPID; // use this stream's SCR for PCR
00126       }
00127     }
00128     if (fCurrentPID == fPCR_PID) {
00129       // Record the input's current SCR timestamp, for use as our PCR:
00130       fPCR = scr;
00131     }
00132   }
00133 
00134   // Now that we have new input data, retry the last delivery to the client:
00135   doGetNextFrame();
00136 }
00137 
00138 void MPEG2TransportStreamMultiplexor
00139 ::deliverDataToClient(u_int8_t pid, unsigned char* buffer, unsigned bufferSize,
00140                       unsigned& startPositionInBuffer) {
00141   // Construct a new Transport packet, and deliver it to the client:
00142   if (fMaxSize < TRANSPORT_PACKET_SIZE) {
00143     fFrameSize = 0; // the client hasn't given us enough space; deliver nothing
00144     fNumTruncatedBytes = TRANSPORT_PACKET_SIZE;
00145   } else {
00146     fFrameSize = TRANSPORT_PACKET_SIZE;
00147     Boolean willAddPCR = pid == fPCR_PID && startPositionInBuffer == 0
00148       && !(fPCR.highBit == 0 && fPCR.remainingBits == 0 && fPCR.extension == 0);
00149     unsigned const numBytesAvailable = bufferSize - startPositionInBuffer;
00150     unsigned numHeaderBytes = 4; // by default
00151     unsigned numPCRBytes = 0; // by default
00152     unsigned numPaddingBytes = 0; // by default
00153     unsigned numDataBytes;
00154     u_int8_t adaptation_field_control;
00155     if (willAddPCR) {
00156       adaptation_field_control = 0x30;
00157       numHeaderBytes += 2; // for the "adaptation_field_length" and flags
00158       numPCRBytes = 6;
00159       if (numBytesAvailable >= TRANSPORT_PACKET_SIZE - numHeaderBytes - numPCRBytes) {
00160         numDataBytes = TRANSPORT_PACKET_SIZE - numHeaderBytes - numPCRBytes;
00161       } else {
00162         numDataBytes = numBytesAvailable;
00163         numPaddingBytes
00164           = TRANSPORT_PACKET_SIZE - numHeaderBytes - numPCRBytes - numDataBytes;
00165       }
00166     } else if (numBytesAvailable >= TRANSPORT_PACKET_SIZE - numHeaderBytes) {
00167       // This is the common case
00168       adaptation_field_control = 0x10;
00169       numDataBytes = TRANSPORT_PACKET_SIZE - numHeaderBytes;
00170     } else {
00171       adaptation_field_control = 0x30;
00172       ++numHeaderBytes; // for the "adaptation_field_length"
00173       // ASSERT: numBytesAvailable <= TRANSPORT_PACKET_SIZE - numHeaderBytes
00174       numDataBytes = numBytesAvailable;
00175       if (numDataBytes < TRANSPORT_PACKET_SIZE - numHeaderBytes) {
00176         ++numHeaderBytes; // for the adaptation field flags
00177         numPaddingBytes = TRANSPORT_PACKET_SIZE - numHeaderBytes - numDataBytes;
00178       }
00179     }
00180     // ASSERT: numHeaderBytes+numPCRBytes+numPaddingBytes+numDataBytes
00181     //         == TRANSPORT_PACKET_SIZE
00182 
00183     // Fill in the header of the Transport Stream packet:
00184     unsigned char* header = fTo;
00185     *header++ = 0x47; // sync_byte
00186     *header++ = (startPositionInBuffer == 0) ? 0x40 : 0x00;
00187       // transport_error_indicator, payload_unit_start_indicator, transport_priority,
00188       // first 5 bits of PID
00189     *header++ = pid;
00190       // last 8 bits of PID
00191     unsigned& continuity_counter = fPIDState[pid].counter; // alias
00192     *header++ = adaptation_field_control|(continuity_counter&0x0F);
00193       // transport_scrambling_control, adaptation_field_control, continuity_counter
00194     ++continuity_counter;
00195     if (adaptation_field_control == 0x30) {
00196       // Add an adaptation field:
00197       u_int8_t adaptation_field_length
00198         = (numHeaderBytes == 5) ? 0 : 1 + numPCRBytes + numPaddingBytes;
00199       *header++ = adaptation_field_length;
00200       if (numHeaderBytes > 5) {
00201         u_int8_t flags = willAddPCR ? 0x10 : 0x00;
00202         if (fIsFirstAdaptationField) {
00203           flags |= 0x80; // discontinuity_indicator
00204           fIsFirstAdaptationField = False;
00205         }
00206         *header++ = flags;
00207         if (willAddPCR) {
00208           u_int32_t pcrHigh32Bits = (fPCR.highBit<<31) | (fPCR.remainingBits>>1);
00209           u_int8_t pcrLowBit = fPCR.remainingBits&1;
00210           u_int8_t extHighBit = (fPCR.extension&0x100)>>8;
00211           *header++ = pcrHigh32Bits>>24;
00212           *header++ = pcrHigh32Bits>>16;
00213           *header++ = pcrHigh32Bits>>8;
00214           *header++ = pcrHigh32Bits;
00215           *header++ = (pcrLowBit<<7)|0x7E|extHighBit;
00216           *header++ = (u_int8_t)fPCR.extension; // low 8 bits of extension
00217         }
00218       }
00219     }
00220 
00221     // Add any padding bytes:
00222     for (unsigned i = 0; i < numPaddingBytes; ++i) *header++ = 0xFF;
00223 
00224     // Finally, add the data bytes:
00225     memmove(header, &buffer[startPositionInBuffer], numDataBytes);
00226     startPositionInBuffer += numDataBytes;
00227   }
00228 }
00229 
00230 static u_int32_t calculateCRC(u_int8_t* data, unsigned dataLength); // forward
00231 
00232 #define PAT_PID 0
00233 #define OUR_PROGRAM_NUMBER 1
00234 #define OUR_PROGRAM_MAP_PID 0x30
00235 
00236 void MPEG2TransportStreamMultiplexor::deliverPATPacket() {
00237   // First, create a new buffer for the PAT packet:
00238   unsigned const patSize = TRANSPORT_PACKET_SIZE - 4; // allow for the 4-byte header
00239   unsigned char* patBuffer = new unsigned char[patSize];
00240 
00241   // and fill it in:
00242   unsigned char* pat = patBuffer;
00243   *pat++ = 0; // pointer_field
00244   *pat++ = 0; // table_id
00245   *pat++ = 0xB0; // section_syntax_indicator; 0; reserved, section_length (high)
00246   *pat++ = 13; // section_length (low)
00247   *pat++ = 0; *pat++ = 1; // transport_stream_id
00248   *pat++ = 0xC3; // reserved; version_number; current_next_indicator
00249   *pat++ = 0; // section_number
00250   *pat++ = 0; // last_section_number
00251   *pat++ = OUR_PROGRAM_NUMBER>>8; *pat++ = OUR_PROGRAM_NUMBER; // program_number
00252   *pat++ = 0xE0|(OUR_PROGRAM_MAP_PID>>8); // reserved; program_map_PID (high)
00253   *pat++ = OUR_PROGRAM_MAP_PID; // program_map_PID (low)
00254 
00255   // Compute the CRC from the bytes we currently have (not including "pointer_field"):
00256   u_int32_t crc = calculateCRC(patBuffer+1, pat - (patBuffer+1));
00257   *pat++ = crc>>24; *pat++ = crc>>16; *pat++ = crc>>8; *pat++ = crc;
00258 
00259   // Fill in the rest of the packet with padding bytes:
00260   while (pat < &patBuffer[patSize]) *pat++ = 0xFF;
00261 
00262   // Deliver the packet:
00263   unsigned startPosition = 0;
00264   deliverDataToClient(PAT_PID, patBuffer, patSize, startPosition);
00265 
00266   // Finally, remove the new buffer:
00267   delete[] patBuffer;
00268 }
00269 
00270 void MPEG2TransportStreamMultiplexor::deliverPMTPacket(Boolean hasChanged) {
00271   if (hasChanged) ++fProgramMapVersion;
00272 
00273   // First, create a new buffer for the PMT packet:
00274   unsigned const pmtSize = TRANSPORT_PACKET_SIZE - 4; // allow for the 4-byte header
00275   unsigned char* pmtBuffer = new unsigned char[pmtSize];
00276 
00277   // and fill it in:
00278   unsigned char* pmt = pmtBuffer;
00279   *pmt++ = 0; // pointer_field
00280   *pmt++ = 2; // table_id
00281   *pmt++ = 0xB0; // section_syntax_indicator; 0; reserved, section_length (high)
00282   unsigned char* section_lengthPtr = pmt; // save for later
00283   *pmt++ = 0; // section_length (low) (fill in later)
00284   *pmt++ = OUR_PROGRAM_NUMBER>>8; *pmt++ = OUR_PROGRAM_NUMBER; // program_number
00285   *pmt++ = 0xC1|((fProgramMapVersion&0x1F)<<1); // reserved; version_number; current_next_indicator
00286   *pmt++ = 0; // section_number
00287   *pmt++ = 0; // last_section_number
00288   *pmt++ = 0xE0; // reserved; PCR_PID (high)
00289   *pmt++ = fPCR_PID; // PCR_PID (low)
00290   *pmt++ = 0xF0; // reserved; program_info_length (high)
00291   *pmt++ = 0; // program_info_length (low)
00292   for (int pid = 0; pid < PID_TABLE_SIZE; ++pid) {
00293     if (fPIDState[pid].streamType != 0) {
00294       // This PID gets recorded in the table
00295       *pmt++ = fPIDState[pid].streamType;
00296       *pmt++ = 0xE0; // reserved; elementary_pid (high)
00297       *pmt++ = pid; // elementary_pid (low)
00298       *pmt++ = 0xF0; // reserved; ES_info_length (high)
00299       *pmt++ = 0; // ES_info_length (low)
00300     }
00301   }
00302   unsigned section_length = pmt - (section_lengthPtr+1) + 4 /*for CRC*/;
00303   *section_lengthPtr = section_length;
00304 
00305   // Compute the CRC from the bytes we currently have (not including "pointer_field"):
00306   u_int32_t crc = calculateCRC(pmtBuffer+1, pmt - (pmtBuffer+1));
00307   *pmt++ = crc>>24; *pmt++ = crc>>16; *pmt++ = crc>>8; *pmt++ = crc;
00308 
00309   // Fill in the rest of the packet with padding bytes:
00310   while (pmt < &pmtBuffer[pmtSize]) *pmt++ = 0xFF;
00311 
00312   // Deliver the packet:
00313   unsigned startPosition = 0;
00314   deliverDataToClient(OUR_PROGRAM_MAP_PID, pmtBuffer, pmtSize, startPosition);
00315 
00316   // Finally, remove the new buffer:
00317   delete[] pmtBuffer;
00318 }
00319 
00320 void MPEG2TransportStreamMultiplexor::setProgramStreamMap(unsigned frameSize) {
00321   if (frameSize <= 16) return; // program_stream_map is too small to be useful
00322   if (frameSize > 0xFF) return; // program_stream_map is too large
00323 
00324   u_int16_t program_stream_map_length = (fInputBuffer[4]<<8) | fInputBuffer[5];
00325   if ((u_int16_t)frameSize > 6+program_stream_map_length) {
00326     frameSize = 6+program_stream_map_length;
00327   }
00328 
00329   u_int8_t versionByte = fInputBuffer[6];
00330   if ((versionByte&0x80) == 0) return; // "current_next_indicator" is not set
00331   fCurrentInputProgramMapVersion = versionByte&0x1F;
00332 
00333   u_int16_t program_stream_info_length = (fInputBuffer[8]<<8) | fInputBuffer[9];
00334   unsigned offset = 10 + program_stream_info_length; // skip over 'descriptors'
00335 
00336   u_int16_t elementary_stream_map_length
00337     = (fInputBuffer[offset]<<8) | fInputBuffer[offset+1];
00338   offset += 2;
00339   frameSize -= 4; // sizeof CRC_32
00340   if (frameSize > offset + elementary_stream_map_length) {
00341     frameSize = offset + elementary_stream_map_length;
00342   }
00343 
00344   while (offset + 4 <= frameSize) {
00345     u_int8_t stream_type = fInputBuffer[offset];
00346     u_int8_t elementary_stream_id = fInputBuffer[offset+1];
00347 
00348     fPIDState[elementary_stream_id].streamType = stream_type;
00349 
00350     u_int16_t elementary_stream_info_length
00351       = (fInputBuffer[offset+2]<<8) | fInputBuffer[offset+3];
00352     offset += 4 + elementary_stream_info_length;
00353   }
00354 }
00355 
00356 static u_int32_t const CRC32[256] = {
00357   0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
00358   0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
00359   0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
00360   0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
00361   0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
00362   0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
00363   0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
00364   0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
00365   0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
00366   0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
00367   0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
00368   0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
00369   0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
00370   0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
00371   0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
00372   0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
00373   0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
00374   0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
00375   0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
00376   0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
00377   0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
00378   0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
00379   0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
00380   0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
00381   0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
00382   0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
00383   0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
00384   0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
00385   0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
00386   0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
00387   0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
00388   0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
00389   0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
00390   0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
00391   0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
00392   0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
00393   0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
00394   0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
00395   0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
00396   0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
00397   0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
00398   0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
00399   0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
00400   0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
00401   0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
00402   0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
00403   0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
00404   0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
00405   0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
00406   0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
00407   0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
00408   0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
00409   0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
00410   0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
00411   0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
00412   0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
00413   0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
00414   0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
00415   0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
00416   0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
00417   0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
00418   0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
00419   0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
00420   0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
00421 };
00422 
00423 static u_int32_t calculateCRC(u_int8_t* data, unsigned dataLength) {
00424   u_int32_t crc = 0xFFFFFFFF;
00425 
00426   while (dataLength-- > 0) {
00427     crc = (crc<<8) ^ CRC32[(crc>>24) ^ (u_int32_t)(*data++)];
00428   }
00429 
00430   return crc;
00431 }

Generated on Thu May 17 07:11:47 2012 for live by  doxygen 1.5.2