00001
00007
00008 #include <pthread.h>
00009 #include <netdb.h>
00010 #include <fcntl.h>
00011 #include <unistd.h>
00012 #include <sys/select.h>
00013 #include <sys/types.h>
00014 #include <sys/socket.h>
00015 #include <netinet/in.h>
00016
00017
00018 #include <iostream>
00019 using namespace std;
00020
00021
00022 #include <qhttp.h>
00023 #include <qdeepcopy.h>
00024
00025
00026 #include "dbox2recorder.h"
00027 #include "dbox2channel.h"
00028 #include "RingBuffer.h"
00029 #include "mythcontext.h"
00030 #include "tspacket.h"
00031
00032 #define DEBUG_DBOX2_TS
00033
00034
00035
00036 #define DBOX2_TIMEOUT 15
00037 #define PAT_TID 0x00
00038 #define PMT_TID 0x02
00039 #define STREAM_TYPE_VIDEO_MPEG1 0x01
00040 #define STREAM_TYPE_VIDEO_MPEG2 0x02
00041 #define STREAM_TYPE_AUDIO_MPEG1 0x03
00042 #define STREAM_TYPE_AUDIO_MPEG2 0x04
00043 #define STREAM_TYPE_PRIVATE_SECTION 0x05
00044 #define STREAM_TYPE_PRIVATE_DATA 0x06
00045 #define STREAM_TYPE_AUDIO_AAC 0x0f
00046 #define STREAM_TYPE_VIDEO_MPEG4 0x10
00047 #define STREAM_TYPE_VIDEO_H264 0x1b
00048
00049 #define STREAM_TYPE_AUDIO_AC3 0x81
00050 #define STREAM_TYPE_AUDIO_DTS 0x8a
00051
00052 #define LOC QString("DBox2Rec(%1): ").arg(m_cardid)
00053 #define LOC_WARN QString("DBox2Rec(%1) Warning: ").arg(m_cardid)
00054 #define LOC_ERR QString("DBox2Rec(%1) Error: ").arg(m_cardid)
00055
00056 static int socketConnect(int socket, sockaddr* addr, int len)
00057 {
00058 return connect(socket, addr, len);
00059 }
00060
00061 static unsigned int mpegts_crc32(const unsigned char *data, int len);
00062
00063 void DBox2Relay::SetRecorder(DBox2Recorder *rec)
00064 {
00065 QMutexLocker locker(&m_lock);
00066 m_rec = rec;
00067 }
00068
00069 void DBox2Relay::httpRequestFinished(int id, bool error)
00070 {
00071 QMutexLocker locker(&m_lock);
00072 if (m_rec)
00073 m_rec->httpRequestFinished(id, error);
00074 }
00075
00076 typedef struct ts_packet_
00077 {
00078 uint8_t pid[2];
00079 uint8_t flags;
00080 uint8_t count;
00081 uint8_t data[184];
00082 uint8_t adapt_length;
00083 uint8_t adapt_flags;
00084 uint8_t pcr[6];
00085 uint8_t opcr[6];
00086 uint8_t splice_count;
00087 uint8_t priv_dat_len;
00088 uint8_t *priv_dat;
00089 uint8_t adapt_ext_len;
00090 uint8_t adapt_eflags;
00091 uint8_t ltw[2];
00092 uint8_t piece_rate[3];
00093 uint8_t dts[5];
00094 int rest;
00095 uint8_t stuffing;
00096 } ts_packet;
00097
00101 DBox2Recorder::DBox2Recorder(TVRec *rec, DBox2Channel *channel)
00102 : DTVRecorder(rec), m_cardid(rec->GetCaptureCardNum()),
00103
00104 m_patPacket(new uint8_t[TSPacket::SIZE]), pat_cc(0), pkts_until_pat(0),
00105 m_pidPAT(0x0), m_pmtPID(-1), m_ac3PID(-1),
00106 m_sectionID(-1),
00107
00108 m_channel(channel),
00109
00110 port(-1), httpPort(-1), ip(""), isOpen(false), http(new QHttp()),
00111 m_relay(new DBox2Relay(this)),
00112 m_lastPIDRequestID(-1), m_lastInfoRequestID(-1), lastpacket(0),
00113
00114 bufferSize(1024 * 1024),
00115
00116 m_videoWidth(-1), m_videoHeight(-1), m_videoFormat(""),
00117
00118 _request_abort(false)
00119 {
00120 VERBOSE(VB_RECORD, LOC + "Instantiating recorder.");
00121
00122
00123 transportStream.socket = -1;
00124 transportStream.buffer = new uint8_t[bufferSize];
00125 transportStream.bufferIndex = 0;
00126
00127
00128 QObject::connect(http, SIGNAL(requestFinished (int,bool)),
00129 m_relay, SLOT( httpRequestFinished(int,bool)));
00130
00131 m_channel->SetRecorder(this);
00132 m_channel->RecorderAlive(true);
00133 }
00134
00135 void DBox2Recorder::TeardownAll(void)
00136 {
00137 VERBOSE(VB_RECORD, LOC + "Shutting down recorder.");
00138
00139 m_channel->RecorderAlive(false);
00140
00141
00142 if (http)
00143 {
00144 http->abort();
00145 http->closeConnection();
00146 http->disconnect();
00147 }
00148
00149 Close();
00150
00151 if (transportStream.buffer)
00152 {
00153 delete [] transportStream.buffer;
00154 transportStream.buffer = NULL;
00155 }
00156
00157 if (http)
00158 {
00159 http->deleteLater();
00160 http = NULL;
00161 }
00162
00163 if (m_relay)
00164 {
00165 m_relay->SetRecorder(NULL);
00166 m_relay->deleteLater();
00167 m_relay = NULL;
00168 }
00169
00170 m_channel->SetRecorder(NULL);
00171 }
00172
00173 void DBox2Recorder::Close(void)
00174 {
00175 if (!isOpen)
00176 return;
00177
00178 VERBOSE(VB_RECORD, LOC + "Closing all connections...");
00179
00180
00181 if (transportStream.socket > 0)
00182 {
00183 close(transportStream.socket);
00184 transportStream.socket = -1;
00185 }
00186 isOpen = false;
00187 }
00188
00189 bool DBox2Recorder::Open(void)
00190 {
00191 if (isOpen)
00192 Close();
00193
00194 m_channel->RecorderAlive(true);
00195 return RequestStream();
00196 }
00197
00198 bool DBox2Recorder::RequestStream()
00199 {
00200 VERBOSE(VB_RECORD, LOC +
00201 QString("Initializing Host: %1, Streaming-Port: %2, Http-Port: %3")
00202 .arg(ip).arg(port).arg(httpPort));
00203
00204
00205
00206
00207
00208 VERBOSE(VB_RECORD, LOC + QString("Retrieving PIDs from %1:%2...")
00209 .arg(ip).arg(httpPort));
00210
00211 QHttpRequestHeader header( "GET", "/control/zapto?getallpids" );
00212 header.setValue("Host", ip);
00213 http->setHost(ip, httpPort);
00214 m_lastPIDRequestID = http->request(header);
00215 return true;
00216 }
00217
00218 bool DBox2Recorder::RequestInfo()
00219 {
00220 VERBOSE(VB_RECORD, LOC + "Requesting stream info on " +
00221 QString("Host: %1, Streaming-Port: %2, Http-Port: %3")
00222 .arg(ip).arg(port).arg(httpPort));
00223
00224
00225
00226
00227
00228 VERBOSE(VB_RECORD, LOC + QString("Retrieving Info from %1:%2...")
00229 .arg(ip).arg(httpPort));
00230
00231 QHttpRequestHeader header( "GET", "/control/info?streaminfo" );
00232 header.setValue("Host", ip);
00233 http->setHost(ip, httpPort);
00234 m_lastInfoRequestID = http->request(header);
00235 return true;
00236 }
00237
00238 int DBox2Recorder::OpenStream(void)
00239 {
00240 QString message = "";
00241 for (uint i = 0; i < m_pids.size(); i++)
00242 message += QString("0x%1 ").arg(m_pids[i], 0, 16);
00243
00244 VERBOSE(VB_RECORD, LOC + "Opening pids " + message);
00245
00246 struct hostent *hp = gethostbyname(ip);
00247 if (!hp)
00248 {
00249 VERBOSE(VB_IMPORTANT, LOC_ERR +
00250 "OpenStream() Unable to look up hostname (1).");
00251
00252 return -1;
00253 }
00254
00255 struct sockaddr_in adr;
00256 memset ((char *)&adr, 0, sizeof(struct sockaddr_in));
00257
00258 adr.sin_family = AF_INET;
00259 adr.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr;
00260 port = 31339;
00261
00262 adr.sin_port = htons(port);
00263
00264 if (!adr.sin_addr.s_addr)
00265 {
00266 VERBOSE(VB_IMPORTANT, LOC_ERR +
00267 "OpenStream() Unable to look up hostname (2).");
00268
00269 return -1;
00270 }
00271
00272
00273 int newSocket = socket(AF_INET, SOCK_STREAM, 0);
00274
00275 if (-1 == socketConnect(newSocket, (sockaddr*) &adr,
00276 sizeof(struct sockaddr_in)))
00277 {
00278 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to connect socket" + ENO);
00279 close(newSocket);
00280 return -1;
00281 }
00282
00283
00284 QString request = "GET /";
00285
00286
00287 for (uint i = 0; i < m_pids.size(); i++)
00288 {
00289 request += QString("%1").arg(m_pids[i], 0, 16);
00290 if (i + 1 < m_pids.size())
00291 request += ",";
00292 }
00293
00294 request += " HTTP/1.0";
00295
00296 VERBOSE(VB_RECORD, LOC + "Sending Request '"<<request<<"'");
00297
00298 request += "\r\n\r\n";
00299
00300 write(newSocket, request.ascii(), strlen(request.ascii()));
00301
00302 return newSocket;
00303 }
00304
00305 void DBox2Recorder::StartRecording(void)
00306 {
00307 struct timeval tv;
00308
00309 VERBOSE(VB_RECORD, LOC + "StartRecording");
00310
00311 if (!Open())
00312 {
00313 VERBOSE(VB_IMPORTANT, LOC_ERR + "Could not open recorder. Aborting.");
00314 _error = true;
00315 return;
00316 }
00317
00318 _request_recording = true;
00319 _recording = true;
00320 _request_abort = false;
00321
00322 lastpacket = time(NULL);
00323 long lastShown = time(NULL);
00324
00325 while (_request_recording)
00326 {
00327 if (_request_abort)
00328 break;
00329
00330 bool was_paused = request_pause || paused;
00331 if (PauseAndWait())
00332 continue;
00333 if (was_paused)
00334 lastpacket = time(NULL);
00335
00336 if ((m_lastPIDRequestID >= 0) || (m_lastInfoRequestID >= 0))
00337 {
00338 if ((time(NULL) - lastShown) >= 1)
00339 {
00340 VERBOSE(VB_IMPORTANT, LOC +
00341 "Waiting for request to finish...");
00342
00343 lastShown = time(NULL);
00344 lastpacket = time(NULL);
00345 }
00346 usleep(1000);
00347 }
00348 else if ((time(NULL) - lastpacket) > DBOX2_TIMEOUT)
00349 {
00350 VERBOSE(VB_IMPORTANT, LOC +
00351 QString("No Input in %1 seconds.").arg(DBOX2_TIMEOUT));
00352
00353 _error = true;
00354 return;
00355 }
00356
00357 tv.tv_sec = DBOX2_TIMEOUT;
00358 tv.tv_usec = 0;
00359
00360
00361 if (isOpen)
00362 {
00363 processStream(&transportStream);
00364 }
00365 else
00366 {
00367
00368 usleep(1000);
00369 }
00370 }
00371
00372 FinishRecording();
00373 _recording = false;
00374 }
00375
00376 void DBox2Recorder::SetOptionsFromProfile(RecordingProfile *profile,
00377 const QString &videodev,
00378 const QString &audiodev,
00379 const QString &vbidev)
00380 {
00381 (void)videodev;
00382 (void)audiodev;
00383 (void)vbidev;
00384 (void)profile;
00385 }
00386
00387 void DBox2Recorder::SetOption(const QString &name, const QString &value)
00388 {
00389 if (name == "ip")
00390 ip = QDeepCopy<QString>(value);
00391 if (name == "host")
00392 ip = QDeepCopy<QString>(value);
00393 }
00394
00395 void DBox2Recorder::SetOption(const QString &name, int value)
00396 {
00397 if (name == "port")
00398 port = value;
00399 if (name == "httpport")
00400 httpPort = value;
00401 }
00402
00403 void DBox2Recorder::httpRequestFinished(int id, bool error)
00404 {
00405 if (error)
00406 {
00407 VERBOSE(VB_IMPORTANT, LOC_ERR + "HTTP Request failed!");
00408 return;
00409 }
00410
00411 QString buffer = http->readAll();
00412
00413 if (id == m_lastPIDRequestID)
00414 {
00415 VERBOSE(VB_RECORD, LOC + "Retrieving PIDs succeeded. Parsing...");
00416
00417 m_pids.clear();
00418 m_pmtPID = -1;
00419 m_ac3PID = -1;
00420 for (int i = 0; i < 10; i ++)
00421 {
00422 QString pidString = buffer.section("\n", i, i);
00423 if (!pidString.isEmpty())
00424 {
00425 int pid = pidString.section(" ", 0, 0).toInt();
00426 if (pid == 0)
00427 {
00428 VERBOSE(VB_GENERAL, LOC_WARN + "Got 0 PID!!!.");
00429 continue;
00430 }
00431
00432 m_pids.push_back(pid);
00433
00434 QString pidType = pidString.section(" ", 1).upper();
00435 if (pidType == "PMT")
00436 {
00437 m_pmtPID = m_pids.back();
00438
00439 #ifdef DEBUG_DBOX2_PMT
00440 VERBOSE(VB_RECORD, LOC + "Found PMT with " +
00441 QString("PID 0x%1.").arg(m_pmtPID,0,16));
00442 #endif // DEBUG_DBOX2_PMT
00443 }
00444 else if ((pidType.contains("DOLBY DIGITAL") > 0) ||
00445 (pidType.contains("AC3") > 0))
00446 {
00447 m_ac3PID = m_pids.back();
00448
00449 #ifdef DEBUG_DBOX2_PMT
00450 VERBOSE(VB_RECORD, LOC + "Found AC3 stream with " +
00451 QString("PID 0x%1.").arg(m_ac3PID,0,16));
00452 #endif // DEBUG_DBOX2_PMT
00453 }
00454
00455 #ifdef DEBUG_DBOX2_PID
00456 VERBOSE(VB_RECORD, LOC +
00457 QString("Found PID 0x%1.").arg(m_pids[i]));
00458 #endif // DEBUG_DBOX2_PMT
00459 }
00460 }
00461
00462 if (m_pids.size() == 0)
00463 {
00464 VERBOSE(VB_IMPORTANT, LOC_ERR +
00465 "No usable PIDS found. Cannot continue.");
00466
00467
00468 m_channel->SwitchToLastChannel();
00469 return;
00470 }
00471
00472
00473 transportStream.bufferIndex = 0;
00474 transportStream.socket = OpenStream();
00475 if (transportStream.socket == -1)
00476 return;
00477
00478
00479 pkts_until_pat = 0;
00480 m_sectionID = 1;
00481 CreatePAT(m_patPacket);
00482
00483 isOpen = true;
00484 m_lastPIDRequestID = -1;
00485 return;
00486 }
00487
00488 if (id == m_lastInfoRequestID)
00489 {
00490 VERBOSE(VB_RECORD, LOC + "Retrieving info succeeded. Parsing..." +
00491 QString("\n\t\t\tGot: %1.").arg(buffer));
00492
00493 m_videoWidth = buffer.section("\n", 0, 0).toInt();
00494 m_videoHeight = buffer.section("\n", 1, 1).toInt();
00495 m_videoFormat = buffer.section("\n", 3, 3);
00496
00497 VERBOSE(VB_RECORD, LOC + QString("Video is %1x%2 (Format %3).")
00498 .arg(m_videoWidth).arg(m_videoHeight).arg(m_videoFormat));
00499
00500 m_lastInfoRequestID = -1;
00501 RequestStream();
00502 }
00503 }
00504
00505 int DBox2Recorder::processStream(stream_meta *stream)
00506 {
00507
00508 int bytesRead = read(stream->socket,
00509 stream->buffer + stream->bufferIndex,
00510 bufferSize - stream->bufferIndex);
00511
00512 if (bytesRead <= 0)
00513 {
00514 VERBOSE(VB_IMPORTANT, LOC + "Error reading data.");
00515 return 0;
00516 }
00517
00518 stream->bufferIndex += bytesRead;
00519
00520 int readIndex = 0;
00521 while (readIndex + (int)TSPacket::SIZE < (int)stream->bufferIndex)
00522 {
00523
00524 int tsPos = findTSHeader(stream->buffer + readIndex,
00525 stream->bufferIndex);
00526 if (tsPos == -1)
00527 {
00528 VERBOSE(VB_IMPORTANT, LOC + "No TS header.");
00529 break;
00530 }
00531
00532 if (tsPos > 0)
00533 {
00534 VERBOSE(VB_IMPORTANT, LOC +
00535 QString("TS header at %1, not in sync.").arg(tsPos));
00536 }
00537
00538
00539 if ((stream->bufferIndex - readIndex - tsPos) < (int)TSPacket::SIZE)
00540 break;
00541
00542 updatePMTSectionID(stream->buffer + readIndex + tsPos, m_pmtPID);
00543
00544
00545 if (pkts_until_pat == 0)
00546 {
00547 BufferedWrite(*(reinterpret_cast<const TSPacket*>(m_patPacket)));
00548 pkts_until_pat = 2000;
00549 }
00550 else
00551 {
00552 pkts_until_pat--;
00553 }
00554
00555 const void *data = stream->buffer + readIndex + tsPos;
00556 const TSPacket *tspacket = reinterpret_cast<const TSPacket*>(data);
00557
00558 _buffer_packets = !FindMPEG2Keyframes(tspacket);
00559
00560 BufferedWrite(*tspacket);
00561
00562 lastpacket = time(NULL);
00563 readIndex += tsPos + TSPacket::SIZE;
00564 }
00565
00566 if (readIndex > 0)
00567 {
00568 memcpy(stream->buffer, stream->buffer + readIndex,
00569 bufferSize - readIndex);
00570 stream->bufferIndex = stream->bufferIndex - readIndex;
00571 }
00572
00573 if (stream->bufferIndex < 0)
00574 {
00575 VERBOSE(VB_IMPORTANT, LOC_WARN +
00576 QString("Buffer index is %1. Resetting.")
00577 .arg(stream->bufferIndex));
00578
00579 stream->bufferIndex = 0;
00580 }
00581
00582 return 0;
00583 }
00584
00585 void DBox2Recorder::CreatePAT(unsigned char *ts_packet)
00586 {
00587 VERBOSE(VB_IMPORTANT, LOC +
00588 QString("Creating PAT for PMT pid 0x%1.").arg(m_pmtPID,0,16));
00589
00590 memset(ts_packet, 0xFF, 188);
00591
00592 ts_packet[0] = 0x47;
00593 ts_packet[1] = 0x40 | ((m_pidPAT >> 8) & 0x1F);
00594 ts_packet[2] = m_pidPAT & 0xFF;
00595 ts_packet[3] = 0x10 | pat_cc;
00596 ts_packet[4] = 0x00;
00597
00598 ++pat_cc &= 0x0F;
00599 unsigned char *pat = ts_packet + 5;
00600 int p = 0;
00601
00602 pat[p++] = PAT_TID;
00603 pat[p++] = 0xB0;
00604 p++;
00605 pat[p++] = 0;
00606 pat[p++] = 1;
00607 pat[p++] = 0xC3;
00608 pat[p++] = 0;
00609 pat[p++] = 0;
00610 pat[p++] = (m_sectionID >> 8) & 0xFF;
00611 pat[p++] = m_sectionID & 0xFF;
00612 pat[p++] = (m_pmtPID >> 8) & 0x1F;
00613 pat[p++] = m_pmtPID & 0xFF;
00614
00615 pat[2] = p + 4 - 3;
00616
00617 unsigned int crc = mpegts_crc32(pat, p);
00618 pat[p++] = (crc >> 24) & 0xFF;
00619 pat[p++] = (crc >> 16) & 0xFF;
00620 pat[p++] = (crc >> 8) & 0xFF;
00621 pat[p++] = crc & 0xFF;
00622 }
00623
00624 void DBox2Recorder::ChannelChanged(void)
00625 {
00626 VERBOSE(VB_IMPORTANT, LOC + "Channel changed. Requesting streams...");
00627 Open();
00628 }
00629
00630 void DBox2Recorder::ChannelChanging(void)
00631 {
00632 VERBOSE(VB_IMPORTANT, LOC + "Channel changing. Closing current stream...");
00633 Close();
00634 }
00635
00636 int DBox2Recorder::findTSHeader(unsigned char *buffer, int len)
00637 {
00638 int pos = 0;
00639 while (pos < len)
00640 {
00641 if (buffer[pos] == 0x47)
00642 return pos;
00643 pos++;
00644 }
00645 return -1;
00646 }
00647
00648 int DBox2Recorder::getPMTSectionID(unsigned char *buffer, int pmtPID)
00649 {
00650 int tempPID = ((buffer[1] & 0x1F) << 8) | (buffer[2] & 0xFF);
00651 if (tempPID != pmtPID)
00652 return -1;
00653
00654 #ifdef DEBUG_DBOX2_PMT
00655 VERBOSE(VB_IMPORTANT, LOC + "Found PMT packet with " +
00656 QString("PID 0x%2.").arg(tempPID));
00657 #endif // DEBUG_DBOX2_PMT
00658
00659 return (buffer[8] & 0xFF << 8) | (buffer[9] & 0xFF);
00660 }
00661
00662 void DBox2Recorder::updatePMTSectionID(unsigned char *buffer, int pmtPID)
00663 {
00664 int sectionID = getPMTSectionID(buffer, pmtPID);
00665 if (sectionID == -1)
00666 return;
00667
00668 #ifdef DEBUG_DBOX2_PMT
00669 VERBOSE(VB_IMPORTANT, LOC +
00670 QString("Found PMT packet with PID 0x%1 ").arg(pmtPID,0,16) +
00671 QString("and section id %3. Updating...").arg(sectionID));
00672 #endif // DEBUG_DBOX2_PMT
00673
00674 unsigned char *pmt = buffer + 5;
00675
00676 pmt[3] = 0;
00677 pmt[4] = 1;
00678 if (m_ac3PID != -1)
00679 {
00680 #ifdef DEBUG_DBOX2_PMT
00681 VERBOSE(VB_IMPORTANT, LOC + "Looking for AC3 PID in PMT packet...");
00682 #endif // DEBUG_DBOX2_PMT
00683
00684
00685 unsigned char p = 10;
00686
00687 p += (((pmt[p] & 0x0F) << 8) | (pmt[p+1] & 0xFF)) + 2;
00688 for (uint i = 0; i < m_pids.size(); i++)
00689 {
00690
00691 p++;
00692
00693 int pid = (pmt[p] & 0x0F) << 8;
00694 pid = pid | (pmt[p+1] & 0xFF);
00695
00696 #ifdef DEBUG_DBOX2_PMT
00697 VERBOSE(VB_IMPORTANT, LOC + "Looking for AC3 PID in PMT packet " +
00698 QString("(Pid #%1 == %2)").arg(i).arg(pid));
00699 #endif // DEBUG_DBOX2_PMT
00700
00701 if (pid == m_ac3PID)
00702 {
00703 #ifdef DEBUG_DBOX2_PMT
00704 VERBOSE(VB_IMPORTANT, LOC + "Found AC3 PID in PMT packet. "
00705 "Updating Stream Type...");
00706 #endif // DEBUG_DBOX2_PMT
00707
00708 pmt[p-1] = STREAM_TYPE_AUDIO_AC3;
00709 break;
00710 }
00711
00712 p += 2;
00713
00714 p += ((pmt[p] & 0x0F) << 8) | (pmt[p+1] & 0xFF) + 2;
00715 }
00716 }
00717 int len = pmt[2];
00718 int crcOffset = len + 3 - 4;
00719
00720
00721 unsigned long crc = mpegts_crc32(pmt, crcOffset);
00722 pmt[crcOffset] = (crc >> 24) & 0xFF;
00723 pmt[crcOffset+1] = (crc >> 16) & 0xFF;
00724 pmt[crcOffset+2] = (crc >> 8) & 0xFF;
00725 pmt[crcOffset+3] = crc & 0xFF;
00726 }
00727
00728 static const uint32_t crc_table[256] =
00729 {
00730 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
00731 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
00732 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
00733 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
00734 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
00735 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
00736 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
00737 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
00738 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
00739 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
00740 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
00741 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
00742 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
00743 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
00744 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
00745 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
00746 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
00747 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
00748 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
00749 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
00750 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
00751 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
00752 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
00753 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
00754 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
00755 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
00756 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
00757 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
00758 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
00759 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
00760 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
00761 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
00762 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
00763 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
00764 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
00765 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
00766 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
00767 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
00768 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
00769 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
00770 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
00771 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
00772 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
00773 };
00774
00775 static unsigned int mpegts_crc32(const unsigned char *data, int len)
00776 {
00777 register int i;
00778 unsigned int crc = 0xffffffff;
00779
00780 for (i = 0; i < len; i++)
00781 crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *data++) & 0xff];
00782
00783 return crc;
00784 }