00001
00002
00003
00004 #include "splicedescriptors.h"
00005 #include "atscdescriptors.h"
00006 #include "mythlogging.h"
00007 #include "mpegtables.h"
00008 #include "mythmiscutil.h"
00009
00010 const unsigned char DEFAULT_PAT_HEADER[8] =
00011 {
00012 0x00,
00013 0xb0,
00014 0x00,
00015 0x00,
00016
00017 0x00,
00018 0xc1,
00019 0x00,
00020 0x00,
00021 };
00022
00023 const unsigned char DEFAULT_PMT_HEADER[12] =
00024 {
00025 0x02,
00026 0xb0,
00027 0x00,
00028 0x00,
00029
00030 0x00,
00031 0xc1,
00032 0x00,
00033 0x00,
00034 0xff, 0xff,
00035 0x00, 0x00,
00036 };
00037
00038 static const uint len_for_alloc[] =
00039 {
00040 TSPacket::kPayloadSize
00041 - 1
00042 - 3 ,
00043 4000,
00044 };
00045
00046 uint StreamID::Normalize(uint stream_id, const desc_list_t &desc,
00047 const QString &sistandard)
00048 {
00049 if ((sistandard != "dvb") && (OpenCableVideo == stream_id))
00050 return MPEG2Video;
00051
00052 if (MPEGDescriptor::Find(desc, DescriptorID::ac3))
00053 return AC3Audio;
00054
00055 QString reg;
00056 const unsigned char *d = MPEGDescriptor::Find(
00057 desc, DescriptorID::registration);
00058 if (d)
00059 {
00060 RegistrationDescriptor rd(d);
00061 if (rd.IsValid())
00062 reg = rd.FormatIdentifierString();
00063 }
00064
00065 if (reg == "DTS1")
00066 return DTSAudio;
00067
00068 #if 0
00069
00070 if (MPEGDescriptor::Find(desc, DescriptorID::teletext) ||
00071 MPEGDescriptor::Find(desc, DescriptorID::subtitling))
00072 return stream_id;
00073 #endif
00074
00075 return stream_id;
00076 }
00077
00078 bool PSIPTable::HasCRC(void) const
00079 {
00080 bool has_crc = false;
00081
00082 switch (TableID())
00083 {
00084
00085 case TableID::PAT:
00086 case TableID::CAT:
00087 case TableID::PMT:
00088 has_crc = true;
00089 break;
00090
00091
00092
00093 case TableID::NIT:
00094 case TableID::SDT:
00095 case TableID::PF_EIT:
00096 has_crc = true;
00097 break;
00098 case TableID::TDT:
00099 has_crc = false;
00100 break;
00101
00102
00103 case TableID::NITo:
00104 case TableID::SDTo:
00105 case TableID::BAT:
00106 case TableID::PF_EITo:
00107 has_crc = true;
00108 break;
00109 case TableID::RST:
00110 case TableID::ST:
00111 has_crc = false;
00112 break;
00113 case TableID::TOT:
00114 has_crc = true;
00115 break;
00116
00117
00118
00119
00120
00121 case TableID::DIT:
00122 has_crc = false;
00123 break;
00124 case TableID::SIT:
00125 has_crc = true;
00126 break;
00127
00128
00129 case TableID::NITscte:
00130 case TableID::NTT:
00131 case TableID::SVCTscte:
00132 case TableID::STTscte:
00133 case TableID::SITscte:
00134 has_crc = true;
00135 break;
00136 case TableID::ADET:
00137 has_crc = false;
00138 break;
00139
00140
00141 case TableID::MGT:
00142 case TableID::TVCT:
00143 case TableID::CVCT:
00144 case TableID::RRT:
00145 case TableID::EIT:
00146 case TableID::ETT:
00147 case TableID::STT:
00148 case TableID::DET:
00149 case TableID::DST:
00150
00151
00152 case TableID::NRT:
00153 case TableID::LTST:
00154 case TableID::DCCT:
00155 case TableID::DCCSCT:
00156
00157 case TableID::AEIT:
00158 case TableID::AETT:
00159 case TableID::SVCT:
00160 has_crc = true;
00161 break;
00162
00163 default:
00164 {
00165
00166 if (TableID::SC_EITbeg <= TableID() &&
00167 TableID() <= TableID::SC_EITendo)
00168 {
00169 has_crc = true;
00170 }
00171
00172
00173 if (TableID::DN_EITbego <= TableID() &&
00174 TableID() <= TableID::DN_EITendo)
00175 {
00176 has_crc = true;
00177 }
00178 }
00179 break;
00180 }
00181
00182 return has_crc;
00183 }
00184
00185 bool PSIPTable::HasSectionNumber(void) const
00186 {
00187 bool has_sn = false;
00188 switch (TableID())
00189 {
00190
00191 case TableID::PAT:
00192 case TableID::CAT:
00193 case TableID::PMT:
00194
00195 case TableID::MGT:
00196 case TableID::TVCT:
00197 case TableID::CVCT:
00198 case TableID::RRT:
00199 case TableID::EIT:
00200 case TableID::ETT:
00201 case TableID::STT:
00202 case TableID::DET:
00203 case TableID::DST:
00204 has_sn = true;
00205 break;
00206 }
00207
00208 return has_sn;
00209 }
00210
00211 bool PSIPTable::VerifyPSIP(bool verify_crc) const
00212 {
00213 if (verify_crc && (CalcCRC() != CRC()))
00214 {
00215 LOG(VB_SIPARSER, LOG_ERR,
00216 QString("PSIPTable: Failed CRC check 0x%1 != 0x%2 "
00217 "for StreamID = 0x%3")
00218 .arg(CRC(),0,16).arg(CalcCRC(),0,16).arg(StreamID(),0,16));
00219 return false;
00220 }
00221
00222 unsigned char *bufend = _fullbuffer + _allocSize;
00223
00224 if ((_pesdata + 2) >= bufend)
00225 return false;
00226
00227 if (psipdata() >= bufend)
00228 return false;
00229
00230 if (TableID::PAT == TableID())
00231 {
00232 uint pcnt = (SectionLength() - PSIP_OFFSET - 2) >> 2;
00233 bool ok = (psipdata() + (pcnt << 2) + 3 < bufend);
00234 if (!ok)
00235 {
00236 LOG(VB_SIPARSER, LOG_ERR,
00237 "PSIPTable: PAT: program list extends past end of buffer");
00238 return false;
00239 }
00240
00241 if ((Length() == 0xfff) && (TableIDExtension() == 0xffff) &&
00242 (Section() == 0xff) && (LastSection() == 0xff))
00243 {
00244 LOG(VB_SIPARSER, LOG_ERR, "PSIPTable: PAT: All values at maximums");
00245 return false;
00246 }
00247
00248 return true;
00249 }
00250
00251 if (TableID::PMT == TableID())
00252 {
00253 if (psipdata() + 3 >= bufend)
00254 {
00255 LOG(VB_SIPARSER, LOG_ERR,
00256 "PSIPTable: PMT: can't query program info length");
00257 return false;
00258 }
00259
00260 if (psipdata() + Length() - 9 > bufend)
00261 {
00262 LOG(VB_SIPARSER, LOG_ERR,
00263 "PSIPTable: PMT: reported length too large");
00264 return false;
00265 }
00266
00267 uint proginfolen = ((psipdata()[2]<<8) | psipdata()[3]) & 0x0fff;
00268 const unsigned char *proginfo = psipdata() + 4;
00269 const unsigned char *cpos = proginfo + proginfolen;
00270 if (cpos > bufend)
00271 {
00272 LOG(VB_SIPARSER, LOG_ERR,
00273 "PSIPTable: PMT: program info extends past end of buffer");
00274 return false;
00275 }
00276
00277 vector<unsigned char*> _ptrs;
00278 const unsigned char *pos = cpos;
00279 uint i = 0;
00280 for (; pos < psipdata() + Length() - 9; i++)
00281 {
00282 const unsigned char *ptr = pos;
00283 if (pos + 4 > bufend)
00284 {
00285 LOG(VB_SIPARSER, LOG_ERR,
00286 QString("PSIPTable: PMT: stream info %1 extends "
00287 "past end of buffer").arg(i));
00288 return false;
00289 }
00290 pos += 5 + (((ptr[3] << 8) | ptr[4]) & 0x0fff);
00291 }
00292 if (pos > bufend)
00293 {
00294 LOG(VB_SIPARSER, LOG_ERR,
00295 QString("PSIPTable: PMT: last stream info %1 extends "
00296 "past end of buffer").arg(i));
00297 return false;
00298 }
00299
00300 return true;
00301 }
00302
00303 return true;
00304 }
00305
00306 ProgramAssociationTable* ProgramAssociationTable::CreateBlank(bool smallPacket)
00307 {
00308 (void) smallPacket;
00309 TSPacket *tspacket = TSPacket::CreatePayloadOnlyPacket();
00310 memcpy(tspacket->data() + sizeof(TSHeader) + 1,
00311 DEFAULT_PAT_HEADER, sizeof(DEFAULT_PAT_HEADER));
00312 PSIPTable psip = PSIPTable::View(*tspacket);
00313 psip.SetLength(TSPacket::kPayloadSize
00314 - 1
00315 - 3 );
00316 ProgramAssociationTable *pat = new ProgramAssociationTable(psip);
00317 pat->SetTotalLength(sizeof(DEFAULT_PAT_HEADER));
00318 delete tspacket;
00319 return pat;
00320 }
00321
00322 ProgramAssociationTable* ProgramAssociationTable::Create(
00323 uint tsid, uint version,
00324 const vector<uint>& pnum, const vector<uint>& pid)
00325 {
00326 const uint count = min(pnum.size(), pid.size());
00327 ProgramAssociationTable* pat = CreateBlank();
00328 pat->SetVersionNumber(version);
00329 pat->SetTranportStreamID(tsid);
00330 pat->SetTotalLength(PSIP_OFFSET + (count * 4));
00331
00332
00333 if ((count * 4) >= (184 - (PSIP_OFFSET+1)))
00334 {
00335 LOG(VB_GENERAL, LOG_ERR,
00336 "PAT::Create: Error, old PAT size exceeds maximum PAT size.");
00337 delete pat;
00338 return 0;
00339 }
00340
00341 uint offset = PSIP_OFFSET;
00342 for (uint i = 0; i < count; i++)
00343 {
00344
00345 pat->pesdata()[offset++] = pnum[i]>>8;
00346 pat->pesdata()[offset++] = pnum[i] & 0xff;
00347
00348 pat->pesdata()[offset++] = ((pid[i]>>8) & 0x1f) | 0xe0;
00349 pat->pesdata()[offset++] = pid[i] & 0xff;
00350 }
00351
00352 pat->Finalize();
00353
00354 return pat;
00355 }
00356
00357 ProgramMapTable* ProgramMapTable::CreateBlank(bool smallPacket)
00358 {
00359 ProgramMapTable *pmt = NULL;
00360 TSPacket *tspacket = TSPacket::CreatePayloadOnlyPacket();
00361 memcpy(tspacket->data() + sizeof(TSHeader) + 1,
00362 DEFAULT_PMT_HEADER, sizeof(DEFAULT_PMT_HEADER));
00363
00364 if (smallPacket)
00365 {
00366 PSIPTable psip = PSIPTable::View(*tspacket);
00367 psip.SetLength(len_for_alloc[0]);
00368 pmt = new ProgramMapTable(psip);
00369 }
00370 else
00371 {
00372 PSIPTable psip(*tspacket);
00373 psip.SetLength(len_for_alloc[1]);
00374 pmt = new ProgramMapTable(psip);
00375 }
00376
00377 pmt->SetTotalLength(sizeof(DEFAULT_PMT_HEADER));
00378 delete tspacket;
00379 return pmt;
00380 }
00381
00382 ProgramMapTable* ProgramMapTable::Create(
00383 uint programNumber, uint basepid, uint pcrpid, uint version,
00384 vector<uint> pids, vector<uint> types)
00385 {
00386 const uint count = min(pids.size(), types.size());
00387 ProgramMapTable* pmt = CreateBlank(false);
00388 pmt->tsheader()->SetPID(basepid);
00389
00390 pmt->RemoveAllStreams();
00391 pmt->SetProgramNumber(programNumber);
00392 pmt->SetPCRPID(pcrpid);
00393 pmt->SetVersionNumber(version);
00394
00395 for (uint i=0; i<count; i++)
00396 pmt->AppendStream(pids[i], types[i]);
00397 pmt->Finalize();
00398
00399 return pmt;
00400 }
00401
00402 ProgramMapTable* ProgramMapTable::Create(
00403 uint programNumber, uint basepid, uint pcrpid, uint version,
00404 const desc_list_t &global_desc,
00405 const vector<uint> &pids,
00406 const vector<uint> &types,
00407 const vector<desc_list_t> &prog_desc)
00408 {
00409 const uint count = min(pids.size(), types.size());
00410 ProgramMapTable* pmt = CreateBlank(false);
00411 pmt->tsheader()->SetPID(basepid);
00412
00413 pmt->RemoveAllStreams();
00414 pmt->SetProgramNumber(programNumber);
00415 pmt->SetPCRPID(pcrpid);
00416 pmt->SetVersionNumber(version);
00417
00418 vector<unsigned char> gdesc;
00419 for (uint i=0; i<global_desc.size(); i++)
00420 {
00421 uint len = global_desc[i][1] + 2;
00422 gdesc.insert(gdesc.end(), global_desc[i], global_desc[i] + len);
00423 }
00424 pmt->SetProgramInfo(&gdesc[0], gdesc.size());
00425
00426 for (uint i = 0; i < count; i++)
00427 {
00428 vector<unsigned char> pdesc;
00429 for (uint j = 0; j < prog_desc[i].size(); j++)
00430 {
00431 uint len = prog_desc[i][j][1] + 2;
00432 pdesc.insert(pdesc.end(),
00433 prog_desc[i][j], prog_desc[i][j] + len);
00434 }
00435
00436 pmt->AppendStream(pids[i], types[i], &pdesc[0], pdesc.size());
00437 }
00438 pmt->Finalize();
00439
00440 LOG(VB_SIPARSER, LOG_INFO, "Created PMT \n" + pmt->toString());
00441
00442 return pmt;
00443 }
00444
00445 void ProgramMapTable::Parse() const
00446 {
00447 _ptrs.clear();
00448 const unsigned char *cpos = psipdata() + pmt_header + ProgramInfoLength();
00449 unsigned char *pos = const_cast<unsigned char*>(cpos);
00450 for (uint i = 0; pos < psipdata() + Length() - 9; i++)
00451 {
00452 _ptrs.push_back(pos);
00453 pos += 5 + StreamInfoLength(i);
00454 #if 0
00455 LOG(VB_SIPARSER, LOG_DEBUG, QString("Parsing PMT(0x%1) i(%2) len(%3)")
00456 .arg((uint64_t)this, 0, 16) .arg(i) .arg(StreamInfoLength(i)));
00457 #endif
00458 }
00459 _ptrs.push_back(pos);
00460 #if 0
00461 LOG(VB_SIPARSER, LOG_DEBUG, QString("Parsed PMT(0x%1)\n%2")
00462 .arg((uint64_t)this, 0, 16) .arg(toString()));
00463 #endif
00464 }
00465
00466 void ProgramMapTable::AppendStream(
00467 uint pid, uint type,
00468 unsigned char* streamInfo, uint infoLength)
00469 {
00470 if (!StreamCount())
00471 _ptrs.push_back(psipdata() + pmt_header + ProgramInfoLength());
00472 memset(_ptrs[StreamCount()], 0xff, 5);
00473 SetStreamPID(StreamCount(), pid);
00474 SetStreamType(StreamCount(), type);
00475 SetStreamProgramInfo(StreamCount(), streamInfo, infoLength);
00476 _ptrs.push_back(_ptrs[StreamCount()]+5+StreamInfoLength(StreamCount()));
00477 SetTotalLength(_ptrs[StreamCount()] - pesdata());
00478 }
00479
00489 bool ProgramMapTable::IsVideo(uint i, QString sistandard) const
00490 {
00491 if (StreamID::IsVideo(StreamType(i)))
00492 return true;
00493
00494 desc_list_t list = MPEGDescriptor::
00495 Parse(StreamInfo(i), StreamInfoLength(i));
00496 uint stream_id = StreamID::Normalize(StreamType(i), list, sistandard);
00497
00498 return StreamID::IsVideo(stream_id);
00499 }
00500
00510 bool ProgramMapTable::IsAudio(uint i, QString sistandard) const
00511 {
00512 if (StreamID::IsAudio(StreamType(i)))
00513 return true;
00514
00515 desc_list_t list = MPEGDescriptor::
00516 Parse(StreamInfo(i), StreamInfoLength(i));
00517 uint stream_id = StreamID::Normalize(StreamType(i), list, sistandard);
00518
00519 return StreamID::IsAudio(stream_id);
00520 }
00521
00525 bool ProgramMapTable::IsEncrypted(QString sistandard) const
00526 {
00527 bool encrypted = IsProgramEncrypted();
00528
00529 for (uint i = 0; !encrypted && i < StreamCount(); i++) {
00530
00531 if (IsAudio(i,sistandard) || IsVideo(i,sistandard))
00532 encrypted |= IsStreamEncrypted(i);
00533 }
00534
00535 return encrypted;
00536 }
00537
00541 bool ProgramMapTable::IsProgramEncrypted(void) const
00542 {
00543 desc_list_t descs = MPEGDescriptor::ParseOnlyInclude(
00544 ProgramInfo(), ProgramInfoLength(), DescriptorID::conditional_access);
00545
00546 uint encrypted = 0;
00547 QMap<uint,uint> encryption_system;
00548 for (uint i = 0; i < descs.size(); i++)
00549 {
00550 ConditionalAccessDescriptor cad(descs[i]);
00551 encryption_system[cad.PID()] = cad.SystemID();
00552 encrypted |= cad.SystemID();
00553
00554 #if 0
00555 LOG(VB_GENERAL, LOG_INFO, "DTVsm: " + cad.toString());
00556 #endif
00557 }
00558
00559 return encrypted != 0;
00560 }
00561
00567 bool ProgramMapTable::IsStreamEncrypted(uint i) const
00568 {
00569 desc_list_t descs = MPEGDescriptor::ParseOnlyInclude(
00570 StreamInfo(i), StreamInfoLength(i), DescriptorID::conditional_access);
00571
00572 uint encrypted = 0;
00573 QMap<uint,uint> encryption_system;
00574 for (uint j = 0; j < descs.size(); j++)
00575 {
00576 ConditionalAccessDescriptor cad(descs[j]);
00577 encryption_system[cad.PID()] = cad.SystemID();
00578 encrypted |= cad.SystemID();
00579
00580 #if 0
00581 LOG(VB_GENERAL, LOG_INFO, "DTVsm: " + cad.toString());
00582 #endif
00583 }
00584
00585 return encrypted != 0;
00586 }
00587
00588 bool ProgramMapTable::IsStillPicture(QString sistandard) const
00589 {
00590 static const unsigned char STILL_PICTURE_FLAG = 0x01;
00591
00592 for (uint i = 0; i < StreamCount(); i++)
00593 {
00594 if (IsVideo(i, sistandard))
00595 {
00596 return StreamInfoLength(i) > 2 &&
00597 (StreamInfo(i)[2] & STILL_PICTURE_FLAG);
00598 }
00599 }
00600 return false;
00601 }
00602
00603
00612 uint ProgramMapTable::FindPIDs(uint type,
00613 vector<uint> &pids,
00614 const QString &sistandard) const
00615 {
00616 if ((StreamID::AnyMask & type) != StreamID::AnyMask)
00617 {
00618 for (uint i=0; i < StreamCount(); i++)
00619 if (type == StreamType(i))
00620 pids.push_back(StreamPID(i));
00621 }
00622 else if (StreamID::AnyVideo == type)
00623 {
00624 for (uint i=0; i < StreamCount(); i++)
00625 if (IsVideo(i, sistandard))
00626 pids.push_back(StreamPID(i));
00627 }
00628 else if (StreamID::AnyAudio == type)
00629 {
00630 for (uint i=0; i < StreamCount(); i++)
00631 if (IsAudio(i, sistandard))
00632 pids.push_back(StreamPID(i));
00633 }
00634
00635 return pids.size();
00636 }
00637
00648 uint ProgramMapTable::FindPIDs(uint type,
00649 vector<uint> &pids,
00650 vector<uint> &types,
00651 const QString &sistandard,
00652 bool normalize) const
00653 {
00654 uint pids_start = pids.size();
00655
00656 if ((StreamID::AnyMask & type) != StreamID::AnyMask)
00657 {
00658 for (uint i=0; i < StreamCount(); i++)
00659 if (type == StreamType(i))
00660 {
00661 pids.push_back(StreamPID(i));
00662 types.push_back(StreamType(i));
00663 }
00664 }
00665 else if (StreamID::AnyVideo == type)
00666 {
00667 for (uint i=0; i < StreamCount(); i++)
00668 if (IsVideo(i, sistandard))
00669 {
00670 pids.push_back(StreamPID(i));
00671 types.push_back(StreamType(i));
00672 }
00673 }
00674 else if (StreamID::AnyAudio == type)
00675 {
00676 for (uint i=0; i < StreamCount(); i++)
00677 if (IsAudio(i, sistandard))
00678 {
00679 pids.push_back(StreamPID(i));
00680 types.push_back(StreamType(i));
00681 }
00682 }
00683
00684 if (!normalize)
00685 return pids.size();
00686
00687 for (uint i = pids_start; i < pids.size(); i++)
00688 {
00689 int index = FindPID(pids[i]);
00690 if (index >= 0)
00691 {
00692 desc_list_t desc = MPEGDescriptor::Parse(
00693 StreamInfo(i), StreamInfoLength(i));
00694 types[i] = StreamID::Normalize(types[i], desc, sistandard);
00695 }
00696 }
00697
00698 return pids.size();
00699 }
00700
00701 uint ProgramMapTable::FindUnusedPID(uint desired_pid)
00702 {
00703 uint pid = desired_pid;
00704 while (FindPID(pid) >= 0)
00705 pid += 0x10;
00706
00707 if (desired_pid <= 0x1fff)
00708 return pid;
00709
00710 pid = desired_pid;
00711 while (FindPID(desired_pid) >= 0)
00712 desired_pid += 1;
00713
00714 if (desired_pid <= 0x1fff)
00715 return pid;
00716
00717 pid = 0x20;
00718 while (FindPID(desired_pid) >= 0)
00719 desired_pid += 1;
00720
00721 return desired_pid & 0x1fff;
00722 }
00723
00724 QString PSIPTable::toString(void) const
00725 {
00726 QString str;
00727 str.append(QString(" PSIP tableID(0x%1) length(%2) extension(0x%3)\n")
00728 .arg(TableID(), 0, 16).arg(Length())
00729 .arg(TableIDExtension(), 0, 16));
00730 str.append(QString(" version(%1) current(%2) "
00731 "section(%3) last_section(%4)\n")
00732 .arg(Version()).arg(IsCurrent())
00733 .arg(Section()).arg(LastSection()));
00734 if ((TableID() >= TableID::MGT) && (TableID() <= TableID::SRM))
00735 {
00736 str.append(QString(" atsc_protocol_version(%1)\n")
00737 .arg(ATSCProtocolVersion()));
00738 }
00739 return str;
00740 }
00741
00742 QString PSIPTable::toStringXML(uint indent_level) const
00743 {
00744 QString indent = xml_indent(indent_level);
00745 return indent + "<PSIPSection " + XMLValues(indent_level + 1) + " />";
00746 }
00747
00748 QString PSIPTable::XMLValues(uint indent_level) const
00749 {
00750 QString indent = xml_indent(indent_level);
00751
00752 QString str = QString(
00753 "table_id=\"0x%1\" length=\"%2\"")
00754 .arg(TableID(), 2, 16, QChar('0'))
00755 .arg(Length());
00756
00757 if (HasSectionNumber())
00758 {
00759 str += QString(" section=\"%4\" last_section=\"%5\"")
00760 .arg(Section()).arg(LastSection());
00761 }
00762
00763 if ((TableID() >= TableID::MGT) && (TableID() <= TableID::SRM))
00764 {
00765 str += QString("\n%1version=\"%2\" current=\"%3\" "
00766 "protocol_version=\"%4\" extension=\"0x%5\"")
00767 .arg(indent)
00768 .arg(Version()).arg(xml_bool_to_string(IsCurrent()))
00769 .arg(ATSCProtocolVersion())
00770 .arg(TableIDExtension(), 0, 16);
00771 }
00772
00773 return str;
00774 }
00775
00776 QString ProgramAssociationTable::toString(void) const
00777 {
00778 QString str;
00779 str.append(QString("Program Association Section\n"));
00780 str.append(PSIPTable::toString());
00781 str.append(QString(" tsid(%1) ").arg(TransportStreamID()));
00782 str.append(QString("programCount(%1)\n").arg(ProgramCount()));
00783
00784 uint cnt0 = 0, cnt1fff = 0;
00785 for (uint i = 0; i < ProgramCount(); i++)
00786 {
00787 if (0x1fff == ProgramPID(i))
00788 {
00789 cnt1fff++;
00790 continue;
00791 }
00792
00793 if (0x0 == ProgramPID(i))
00794 {
00795 cnt0++;
00796 continue;
00797 }
00798
00799 str += QString(" program number %1 has PID 0x%2\n")
00800 .arg(ProgramNumber(i),5)
00801 .arg(ProgramPID(i),4,16,QChar('0'));
00802 }
00803
00804 if (cnt0 || cnt1fff)
00805 {
00806 str.append(QString(" also contains %1 dummy programs\n")
00807 .arg(cnt0 + cnt1fff));
00808 }
00809
00810 return str;
00811 }
00812
00813 QString ProgramAssociationTable::toStringXML(uint indent_level) const
00814 {
00815 QString indent_0 = xml_indent(indent_level);
00816 QString indent_1 = xml_indent(indent_level + 1);
00817
00818 QString str = QString(
00819 "%1<ProgramAssociationSection tsid=\"0x%2\" program_count=\"%3\""
00820 "\n%4%5>\n")
00821 .arg(indent_0)
00822 .arg(TransportStreamID(),4,16,QChar('0'))
00823 .arg(ProgramCount())
00824 .arg(indent_1)
00825 .arg(PSIPTable::XMLValues(indent_level + 1));
00826
00827 for (uint i = 0; i < ProgramCount(); i++)
00828 {
00829 bool dummy = (0x1fff == ProgramPID(i)) || (0x0 == ProgramPID(i));
00830 str += QString("%1<Program number=\"%2\" pid=\"0x%3\" %4/>\n")
00831 .arg(indent_1)
00832 .arg(ProgramNumber(i))
00833 .arg(ProgramPID(i),4,16,QChar('0'))
00834 .arg(dummy ? "comment=\"Dummy Program\" " : "");
00835 }
00836
00837 return str + indent_0 + "</ProgramAssociationSection>";
00838 }
00839
00840 QString ProgramMapTable::toString(void) const
00841 {
00842 QString str =
00843 QString("Program Map Section"
00844 "\n%1"
00845 " pnum(%2) pid(0x%3)\n")
00846 .arg(PSIPTable::toString())
00847 .arg(ProgramNumber())
00848 .arg(tsheader()->PID(),0,16);
00849
00850 vector<const unsigned char*> desc =
00851 MPEGDescriptor::Parse(ProgramInfo(), ProgramInfoLength());
00852 for (uint i = 0; i < desc.size(); i++)
00853 {
00854 str.append(QString(" %1\n")
00855 .arg(MPEGDescriptor(desc[i], 300).toString()));
00856 }
00857
00858 for (uint i = 0; i < StreamCount(); i++)
00859 {
00860 str.append(QString(" Stream #%1 pid(0x%2) type(0x%3 %4)\n")
00861 .arg(i).arg(StreamPID(i), 0, 16)
00862 .arg(StreamType(i), 2, 16, QChar('0'))
00863 .arg(StreamTypeString(i)));
00864 vector<const unsigned char*> desc =
00865 MPEGDescriptor::Parse(StreamInfo(i), StreamInfoLength(i));
00866 for (uint i = 0; i < desc.size(); i++)
00867 {
00868 str.append(QString(" %1\n")
00869 .arg(MPEGDescriptor(desc[i], 300).toString()));
00870 }
00871 }
00872 return str;
00873 }
00874
00875 QString ProgramMapTable::toStringXML(uint indent_level) const
00876 {
00877 QString indent_0 = xml_indent(indent_level);
00878 QString indent_1 = xml_indent(indent_level + 1);
00879
00880 QString str = QString(
00881 "%1<ProgramMapSection pcr_pid=\"0x%2\" program_number=\"%3\"\n"
00882 "%4program_info_length=\"%5\" stream_count=\"%7\"%8>\n")
00883 .arg(indent_0)
00884 .arg(PCRPID(),0,16)
00885 .arg(ProgramNumber())
00886 .arg(indent_1)
00887 .arg(ProgramInfoLength())
00888 .arg(PSIPTable::XMLValues(indent_level + 1));
00889
00890 vector<const unsigned char*> gdesc =
00891 MPEGDescriptor::Parse(ProgramInfo(), ProgramInfoLength());
00892 for (uint i = 0; i < gdesc.size(); i++)
00893 {
00894 str += MPEGDescriptor(gdesc[i], 300)
00895 .toStringXML(indent_level + 1) + "\n";
00896 }
00897
00898 for (uint i = 0; i < StreamCount(); i++)
00899 {
00900 str += QString("%1<Stream pid=\"0x%2\" type=\"0x%3\" "
00901 "type_desc=\"%4\" stream_info_length=\"%5\"")
00902 .arg(indent_1)
00903 .arg(StreamPID(i),2,16,QChar('0'))
00904 .arg(StreamType(i),2,16,QChar('0'))
00905 .arg(StreamTypeString(i))
00906 .arg(StreamInfoLength(i));
00907 vector<const unsigned char*> ldesc =
00908 MPEGDescriptor::Parse(StreamInfo(i), StreamInfoLength(i));
00909 str += (ldesc.empty()) ? " />\n" : ">\n";
00910 for (uint i = 0; i < ldesc.size(); i++)
00911 {
00912 str += MPEGDescriptor(ldesc[i], 300)
00913 .toStringXML(indent_level + 2) + "\n";
00914 }
00915 if (!ldesc.empty())
00916 str += indent_1 + "</Stream>\n";
00917 }
00918
00919 return str + indent_0 + "</ProgramMapSection>";
00920 }
00921
00922 const char *StreamID::toString(uint streamID)
00923 {
00924
00925 switch (streamID)
00926 {
00927 case StreamID::MPEG2Video:
00928 return "video-mpeg2";
00929 case StreamID::MPEG1Video:
00930 return "video-mpeg1";
00931 case StreamID::MPEG4Video:
00932 return "video-mpeg4";
00933 case StreamID::H264Video:
00934 return "video-h264";
00935 case StreamID::OpenCableVideo:
00936 return "video-opencable";
00937
00938
00939 case StreamID::AC3Audio:
00940 return "audio-ac3";
00941 case StreamID::MPEG2Audio:
00942 return "audio-mp2-layer[1,2,3]";
00943 case StreamID::MPEG1Audio:
00944 return "audio-mp1-layer[1,2,3]";
00945 case StreamID::MPEG2AudioAmd1:
00946 return "audio-aac-latm";
00947 case StreamID::MPEG2AACAudio:
00948 return "audio-aac";
00949 case StreamID::DTSAudio:
00950 return "audio-dts";
00951
00952
00953 case StreamID::PrivSec:
00954 return "private-sec";
00955 case StreamID::PrivData:
00956 return "private-data";
00957
00958
00959 case StreamID::DSMCC_A:
00960 return "dsmcc-a encap";
00961 case StreamID::DSMCC_B:
00962 return "dsmcc-b std data";
00963 case StreamID::DSMCC_C:
00964 return "dsmcc-c NPD data";
00965 case StreamID::DSMCC_D:
00966 return "dsmcc-d data";
00967
00968
00969 case StreamID::Splice:
00970 return "splice";
00971
00972
00973
00974 case TableID::CENSOR:
00975 return "censor";
00976 case TableID::ECN:
00977 return "extended channel name";
00978 case TableID::SRVLOC:
00979 return "service location";
00980 case TableID::TSS:
00981 return "time-shifted service";
00982 case TableID::CMPNAME:
00983 return "component name";
00984 }
00985 return "unknown";
00986 }
00987
00988 QString StreamID::GetDescription(uint stream_id)
00989 {
00990
00991 switch (stream_id)
00992 {
00993
00994 case StreamID::MPEG1Video:
00995 return "11172-2 MPEG-1 Video";
00996 case StreamID::MPEG2Video:
00997 return "13818-2 MPEG-2 Video";
00998 case StreamID::MPEG4Video:
00999 return "14492-2 MPEG-4 Video";
01000 case StreamID::H264Video:
01001 return "H.264 Video";
01002 case StreamID::OpenCableVideo:
01003 return "OpenCable Video";
01004 case StreamID::VC1Video:
01005 return "VC-1 Video";
01006
01007
01008 case StreamID::MPEG1Audio:
01009 return "11172-2 MPEG-1 Audio";
01010 case StreamID::MPEG2Audio:
01011 return "13818-3 MPEG-2 Audio";
01012 case StreamID::MPEG2AACAudio:
01013 return "13818-7 AAC MPEG-2 Audio";
01014 case StreamID::MPEG2AudioAmd1:
01015 return "13818-3 AAC LATM MPEG-2 Audio";
01016 case StreamID::AC3Audio:
01017 return "AC3 Audio";
01018 case StreamID::DTSAudio:
01019 return "DTS Audio";
01020
01021
01022 case StreamID::DSMCC:
01023 return "13818-1 DSM-CC";
01024 case StreamID::DSMCC_A:
01025 return "13818-6 DSM-CC Type A";
01026 case StreamID::DSMCC_B:
01027 return "13818-6 DSM-CC Type B";
01028 case StreamID::DSMCC_C:
01029 return "13818-6 DSM-CC Type C";
01030 case StreamID::DSMCC_D:
01031 return "13818-6 DSM-CC Type D";
01032 case StreamID::DSMCC_DL:
01033 return "13818-6 Download";
01034 case StreamID::MetaDataPES:
01035 return "13818-6 Metadata in PES";
01036 case StreamID::MetaDataSec:
01037 return "13818-6 Metadata in Sections";
01038 case StreamID::MetaDataDC:
01039 return "13818-6 Metadata in Data Carousel";
01040 case StreamID::MetaDataOC:
01041 return "13818-6 Metadata in Obj Carousel";
01042 case StreamID::MetaDataDL:
01043 return "13818-6 Metadata in Download";
01044
01045
01046 case StreamID::PrivSec:
01047 return "13818-1 Private Sections";
01048 case StreamID::PrivData:
01049 return "13818-3 Private Data";
01050 case StreamID::MHEG:
01051 return "13522 MHEG";
01052 case StreamID::H222_1:
01053 return "ITU H.222.1";
01054 case StreamID::MPEG2Aux:
01055 return "13818-1 Aux & ITU H.222.0";
01056 case StreamID::FlexMuxPES:
01057 return "14496-1 SL/FlexMux in PES";
01058 case StreamID::FlexMuxSec:
01059 return "14496-1 SL/FlexMux in Sections";
01060 case StreamID::MPEG2IPMP:
01061 return "13818-10 IPMP";
01062 case StreamID::MPEG2IPMP2:
01063 return "13818-10 IPMP2";
01064
01065 case AnyMask: return QString();
01066 case AnyVideo: return "video";
01067 case AnyAudio: return "audio";
01068 }
01069
01070 return QString();
01071 }
01072
01073 QString ProgramMapTable::GetLanguage(uint i) const
01074 {
01075 const desc_list_t list = MPEGDescriptor::Parse(
01076 StreamInfo(i), StreamInfoLength(i));
01077 const unsigned char *lang_desc = MPEGDescriptor::Find(
01078 list, DescriptorID::iso_639_language);
01079
01080 if (!lang_desc)
01081 return QString::null;
01082
01083 ISO639LanguageDescriptor iso_lang(lang_desc);
01084 return iso_lang.CanonicalLanguageString();
01085 }
01086
01087 QString ProgramMapTable::StreamDescription(uint i, QString sistandard) const
01088 {
01089 desc_list_t list;
01090
01091 list = MPEGDescriptor::Parse(StreamInfo(i), StreamInfoLength(i));
01092 uint type = StreamID::Normalize(StreamType(i), list, sistandard);
01093 QString desc = StreamID::toString(type);
01094 QString lang = GetLanguage(i);
01095
01096 if (!lang.isEmpty())
01097 desc += QString(" (%1)").arg(lang);
01098
01099 return desc;
01100 }
01101
01102 QString ConditionalAccessTable::toString(void) const
01103 {
01104 QString str =
01105 QString("Condiditional Access Section %1")
01106 .arg(PSIPTable::toString());
01107
01108 vector<const unsigned char*> gdesc =
01109 MPEGDescriptor::Parse(Descriptors(), DescriptorsLength());
01110 for (uint i = 0; i < gdesc.size(); i++)
01111 str += " " + MPEGDescriptor(gdesc[i], 300).toString() + "\n";
01112
01113 str += "\n";
01114
01115 return str;
01116 }
01117
01118 QString ConditionalAccessTable::toStringXML(uint indent_level) const
01119 {
01120 QString indent_0 = xml_indent(indent_level);
01121
01122 QString str =
01123 QString("%1<ConditionalAccessSection %3")
01124 .arg(indent_0)
01125 .arg(PSIPTable::XMLValues(indent_level + 1));
01126
01127 vector<const unsigned char*> gdesc =
01128 MPEGDescriptor::Parse(Descriptors(), DescriptorsLength());
01129 str += (gdesc.empty()) ? " />\n" : ">\n";
01130 for (uint i = 0; i < gdesc.size(); i++)
01131 {
01132 str += MPEGDescriptor(gdesc[i], 300)
01133 .toStringXML(indent_level + 1) + "\n";
01134 }
01135 if (!gdesc.empty())
01136 str += indent_0 + "</ConditionalAccessSection>\n";
01137
01138 return str;
01139 }
01140
01141 QString SpliceTimeView::toString(int64_t first, int64_t last) const
01142 {
01143 if (!IsTimeSpecified())
01144 return QString("splice_time(N/A)");
01145
01146 int64_t abs_pts_time = PTSTime();
01147 if ((first > 0) && (last > 0))
01148 {
01149 int64_t elapsed = abs_pts_time - first;
01150 elapsed = (elapsed < 0) ? elapsed + 0x1000000000LL : elapsed;
01151 QTime abs = QTime(0,0,0,0).addMSecs(elapsed/90);
01152
01153 elapsed = abs_pts_time - last;
01154 elapsed = (elapsed < 0) ? elapsed + 0x1000000000LL : elapsed;
01155 QTime rel = QTime(0,0,0,0).addMSecs(elapsed/90);
01156
01157 return QString("splice_time(pts: %1 abs: %2, rel: +%3)")
01158 .arg(abs_pts_time)
01159 .arg(abs.toString("hh:mm:ss.zzz"))
01160 .arg(rel.toString("hh:mm:ss.zzz"));
01161 }
01162
01163 return QString("splice_time(pts: %1)").arg(abs_pts_time);
01164 }
01165
01166 QString SpliceTimeView::toStringXML(
01167 uint indent_level, int64_t first, int64_t last) const
01168 {
01169 QString indent = xml_indent(indent_level);
01170
01171 if (!IsTimeSpecified())
01172 return indent + "<SpliceTime />";
01173
01174 int64_t abs_pts_time = PTSTime();
01175
01176 QString abs_str;
01177 if (first > 0)
01178 {
01179 int64_t elapsed = abs_pts_time - first;
01180 elapsed = (elapsed < 0) ? elapsed + 0x1000000000LL : elapsed;
01181 QTime abs = QTime(0,0,0,0).addMSecs(elapsed/90);
01182 abs_str = QString("absolute=\"%1\" ")
01183 .arg(abs.toString("hh:mm:ss.zzz"));
01184 }
01185
01186 QString rel_str;
01187 if (last > 0)
01188 {
01189 int64_t elapsed = abs_pts_time - last;
01190 elapsed = (elapsed < 0) ? elapsed + 0x1000000000LL : elapsed;
01191 QTime rel = QTime(0,0,0,0).addMSecs(elapsed/90);
01192 rel_str = QString("relative=\"+%1\" ")
01193 .arg(rel.toString("hh:mm:ss.zzz"));
01194 }
01195
01196 return QString("%1<SpliceTime pts=\"%2\" %3%4/>")
01197 .arg(indent).arg(abs_pts_time).arg(abs_str).arg(rel_str);
01198 }
01199
01201 SpliceInformationTable *SpliceInformationTable::GetDecrypted(
01202 const QString &codeWord) const
01203 {
01204
01205 return NULL;
01206 }
01207
01208 bool SpliceInformationTable::Parse(void)
01209 {
01210 _epilog = NULL;
01211 _ptrs0.clear();
01212 _ptrs1.clear();
01213
01214 if (TableID::SITscte != TableID())
01215 return false;
01216
01217 if (SpliceProtocolVersion() != 0)
01218 return false;
01219
01220 if (IsEncryptedPacket())
01221 return true;
01222
01223 uint type = SpliceCommandType();
01224 if (kSCTNull == type || kSCTBandwidthReservation == type)
01225 {
01226 _epilog = pesdata() + 14;
01227 }
01228 else if (kSCTTimeSignal == type)
01229 {
01230 _epilog = pesdata() + 14 + TimeSignal().size();
01231 }
01232 else if (kSCTSpliceSchedule == type)
01233 {
01234 uint splice_count = pesdata()[14];
01235 const unsigned char *cur = pesdata() + 15;
01236 for (uint i = 0; i < splice_count; i++)
01237 {
01238 _ptrs0.push_back(cur);
01239 bool event_cancel = cur[4] & 0x80;
01240 if (event_cancel)
01241 {
01242 _ptrs1.push_back(NULL);
01243 cur += 5;
01244 continue;
01245 }
01246 bool program_slice = cur[5] & 0x40;
01247 uint component_count = cur[6];
01248 _ptrs1.push_back(cur + (program_slice ? 10 : 7 * component_count));
01249 }
01250 if (splice_count)
01251 {
01252 bool duration = _ptrs0.back()[5] & 0x2;
01253 _epilog = _ptrs1.back() + ((duration) ? 9 : 4);
01254 }
01255 else
01256 {
01257 _epilog = cur;
01258 }
01259 }
01260 else if (kSCTSpliceInsert == type)
01261 {
01262 _ptrs1.push_back(pesdata() + 14);
01263 bool splice_cancel = pesdata()[18] & 0x80;
01264 if (splice_cancel)
01265 {
01266 _epilog = pesdata() + 19;
01267 }
01268 else
01269 {
01270 bool program_splice = pesdata()[19] & 0x40;
01271 bool duration = pesdata()[19] & 0x20;
01272 bool splice_immediate = pesdata()[19] & 0x10;
01273 const unsigned char *cur = pesdata() + 20;
01274 if (program_splice && !splice_immediate)
01275 {
01276 cur += SpliceTimeView(cur).size();
01277 }
01278 else if (!program_splice)
01279 {
01280 uint component_count = pesdata()[20];
01281 cur = pesdata() + 21;
01282 for (uint i = 0; i < component_count; i++)
01283 {
01284 _ptrs0.push_back(cur);
01285 cur += (splice_immediate) ?
01286 1 : 1 + SpliceTimeView(cur).size();
01287 }
01288 }
01289 _ptrs1.push_back(cur);
01290 _ptrs1.push_back(cur + (duration ? 5 : 0));
01291 }
01292 }
01293 else
01294 {
01295 _epilog = NULL;
01296 }
01297
01298 return _epilog != NULL;
01299 }
01300
01301 QString SpliceInformationTable::EncryptionAlgorithmString(void) const
01302 {
01303 uint alg = EncryptionAlgorithm();
01304 switch (alg)
01305 {
01306 case kNoEncryption: return "None";
01307 case kECB: return "DES-ECB";
01308 case kCBC: return "DES-CBC";
01309 case k3DES: return "3DES";
01310 default:
01311 return QString((alg<32) ? "Reserved(%1)" : "Private(%1)").arg(alg);
01312 }
01313 }
01314
01315 QString SpliceInformationTable::SpliceCommandTypeString(void) const
01316 {
01317 uint type = SpliceCommandType();
01318 switch (type)
01319 {
01320 case kSCTNull:
01321 return "Null";
01322 case kSCTSpliceSchedule:
01323 return "SpliceSchedule";
01324 case kSCTSpliceInsert:
01325 return "SpliceInsert";
01326 case kSCTTimeSignal:
01327 return "TimeSignal";
01328 case kSCTBandwidthReservation:
01329 return "BandwidthReservation";
01330 case kSCTPrivateCommand:
01331 return "Private";
01332 default:
01333 return QString("Reserved(%1)").arg(type);
01334 };
01335 }
01336
01337 QString SpliceInformationTable::toString(int64_t first, int64_t last) const
01338 {
01339 QString str =
01340 QString("SpliceInformationSection enc_alg(%1) pts_adj(%2)")
01341 .arg(IsEncryptedPacket()?EncryptionAlgorithmString():"None")
01342 .arg(PTSAdjustment());
01343 str += IsEncryptedPacket() ? QString(" cw_index(%1)") : QString("");
01344 str += QString(" command_len(%1) command_type(%2)")
01345 .arg(SpliceCommandLength())
01346 .arg(SpliceCommandTypeString());
01347
01348 if (IsEncryptedPacket())
01349 return str;
01350
01351 switch (SpliceCommandType())
01352 {
01353 case kSCTSpliceSchedule:
01354 break;
01355 case kSCTSpliceInsert:
01356 {
01357 str += "\n " + SpliceInsert().toString(first, last);
01358 break;
01359 }
01360 case kSCTTimeSignal:
01361 break;
01362 }
01363
01364 return str;
01365 }
01366
01367 QString SpliceInsertView::toString(int64_t first, int64_t last) const
01368 {
01369 QString str =
01370 QString("eventid(0x%1) cancel(%2) "
01371 "out_of_network(%3) program_splice(%4) "
01372 "duration(%5) immediate(%6)\n ")
01373 .arg(SpliceEventID(),0,16)
01374 .arg(IsSpliceEventCancel()?"yes":"no")
01375 .arg(IsOutOfNetwork()?"yes":"no")
01376 .arg(IsProgramSplice()?"yes":"no")
01377 .arg(IsDuration()?"yes":"no")
01378 .arg(IsSpliceImmediate()?"yes":"no");
01379
01380 if (IsProgramSplice() && !IsSpliceImmediate())
01381 str += SpliceTime().toString(first, last);
01382
01383 str += QString(" unique_program_id(%1)")
01384 .arg(UniqueProgramID());
01385
01386 str += QString(" avail(%1/%2)")
01387 .arg(AvailNum()).arg(AvailsExpected());
01388
01389 return str;
01390 }
01391
01392 QString SpliceInformationTable::toStringXML(
01393 uint indent_level, int64_t first, int64_t last) const
01394 {
01395 QString indent = xml_indent(indent_level);
01396
01397 QString cap_time = "";
01398 if (first >= 0)
01399 {
01400 cap_time = QString("pts=\"%1\" ").arg(first);
01401 if (last >= 0)
01402 {
01403 QTime abs = QTime(0,0,0,0).addMSecs((last - first)/90);
01404 cap_time += QString("capture_time=\"%1\" ")
01405 .arg(abs.toString("hh:mm:ss.zzz"));
01406 }
01407 }
01408
01409 QString str = QString(
01410 "%1<SpliceInformationSection %2 encryption_algorithm=\"%3\" "
01411 "pts_adjustment=\"%4\" code_word_index=\"%5\" command_type=\"%6\">\n")
01412 .arg(indent)
01413 .arg(cap_time)
01414 .arg(EncryptionAlgorithmString())
01415 .arg(PTSAdjustment())
01416 .arg(CodeWordIndex())
01417 .arg(SpliceCommandTypeString());
01418
01419 if (IsEncryptedPacket())
01420 return str + indent + "</SpliceInformationSection>";
01421
01422 switch (SpliceCommandType())
01423 {
01424 case kSCTSpliceSchedule:
01425 break;
01426 case kSCTSpliceInsert:
01427 {
01428 str += SpliceInsert().toStringXML(indent_level + 1, first, last);
01429 str += "\n";
01430 break;
01431 }
01432 case kSCTTimeSignal:
01433 break;
01434 }
01435
01436 str += indent + "</SpliceInformationSection>";
01437 return str;
01438 }
01439
01440 QString SpliceInsertView::toStringXML(
01441 uint indent_level, int64_t first, int64_t last) const
01442 {
01443 QString indent_0 = xml_indent(indent_level);
01444 QString indent_1 = xml_indent(indent_level + 1);
01445 QString str = QString(
01446 "%1<SpliceInsert eventid=\"0x%2\" cancel=\"%3\"\n")
01447 .arg(indent_0)
01448 .arg(SpliceEventID(),0,16)
01449 .arg(xml_bool_to_string(IsSpliceEventCancel()));
01450
01451 str += QString(
01452 "%1out_of_network=\"%2\" program_splice=\"%3\" duration=\"%4\"\n")
01453 .arg(indent_1)
01454 .arg(xml_bool_to_string(IsOutOfNetwork()))
01455 .arg(xml_bool_to_string(IsProgramSplice()))
01456 .arg(xml_bool_to_string(IsDuration()));
01457
01458 str += QString(
01459 "%1immediate=\"%2\" unique_program_id=\"%3\"\n"
01460 "%4avail_num=\"%5\" avails_expected=\"%6\">\n")
01461 .arg(indent_1)
01462 .arg(xml_bool_to_string(IsSpliceImmediate()))
01463 .arg(UniqueProgramID())
01464 .arg(indent_1)
01465 .arg(AvailNum())
01466 .arg(AvailsExpected());
01467
01468 if (IsProgramSplice() && !IsSpliceImmediate())
01469 {
01470 str += SpliceTime().toStringXML(indent_level + 1, first, last) + "\n";
01471 }
01472
01473 str += indent_0 + "</SpliceInsert>";
01474 return str;
01475 }