|
MythTV
0.26-pre
|
00001 // -*- Mode: c++ -*- 00002 // Copyright (c) 2003-2004, Daniel Thor Kristjansson 00003 00004 #include "splicedescriptors.h" 00005 #include "atscdescriptors.h" 00006 #include "mythlogging.h" 00007 #include "mpegtables.h" 00008 #include "mythmiscutil.h" // for xml_indent 00009 00010 const unsigned char DEFAULT_PAT_HEADER[8] = 00011 { 00012 0x00, // TableID::PAT 00013 0xb0, // Syntax indicator 00014 0x00, // Length (set seperately) 00015 0x00, // Transport stream ID top bits 00016 00017 0x00, // Transport stream ID bottom bits 00018 0xc1, // current | reserved 00019 0x00, // Current Section 00020 0x00, // Last Section 00021 }; 00022 00023 const unsigned char DEFAULT_PMT_HEADER[12] = 00024 { 00025 0x02, // TableID::PMT 00026 0xb0, // Syntax indicator 00027 0x00, // Length (set seperately) 00028 0x00, // MPEG Program number top bits (set seperately) 00029 00030 0x00, // MPEG Program number bottom bits (set seperately) 00031 0xc1, // Version + Current/Next 00032 0x00, // Current Section 00033 0x00, // Last Section 00034 0xff, 0xff, // PCR pid 00035 0x00, 0x00, // Program Info Length 00036 }; 00037 00038 static const uint len_for_alloc[] = 00039 { 00040 TSPacket::kPayloadSize 00041 - 1 /* for start of field pointer */ 00042 - 3 /* for data before data last byte of pes length */, 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 // not needed while there is no specific stream id for these 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 // MPEG 00085 case TableID::PAT: 00086 case TableID::CAT: 00087 case TableID::PMT: 00088 has_crc = true; 00089 break; 00090 // case TableID::TSDT 00091 00092 // DVB manditory 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 // DVB optional 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 // case TableID::RNT: 00117 // case TableID::CT: 00118 // case TableID::RCT: 00119 // case TableID::CIT: 00120 // case TableID::MPEFEC: 00121 case TableID::DIT: 00122 has_crc = false; 00123 break; 00124 case TableID::SIT: 00125 has_crc = true; 00126 break; 00127 00128 // SCTE 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 // ATSC 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 //case TableID::PIT: 00152 case TableID::NRT: 00153 case TableID::LTST: 00154 case TableID::DCCT: 00155 case TableID::DCCSCT: 00156 //case TableID::SITatsc: 00157 case TableID::AEIT: 00158 case TableID::AETT: 00159 case TableID::SVCT: 00160 has_crc = true; 00161 break; 00162 00163 default: 00164 { 00165 // DVB Longterm EIT data 00166 if (TableID::SC_EITbeg <= TableID() && 00167 TableID() <= TableID::SC_EITendo) 00168 { 00169 has_crc = true; 00170 } 00171 00172 // Dishnet Longterm EIT data 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 // MPEG 00191 case TableID::PAT: 00192 case TableID::CAT: 00193 case TableID::PMT: 00194 // ATSC 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; // can't query length 00226 00227 if (psipdata() >= bufend) 00228 return false; // data outside buffer 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; // currently always a small packet.. 00309 TSPacket *tspacket = TSPacket::CreatePayloadOnlyPacket(); 00310 memcpy(tspacket->data() + sizeof(TSHeader) + 1/* start of field pointer */, 00311 DEFAULT_PAT_HEADER, sizeof(DEFAULT_PAT_HEADER)); 00312 PSIPTable psip = PSIPTable::View(*tspacket); 00313 psip.SetLength(TSPacket::kPayloadSize 00314 - 1 /* for start of field pointer */ 00315 - 3 /* for data before data last byte of pes length */); 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 // create PAT data 00333 if ((count * 4) >= (184 - (PSIP_OFFSET+1))) 00334 { // old PAT must be in single TS for this create function 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 // pnum 00345 pat->pesdata()[offset++] = pnum[i]>>8; 00346 pat->pesdata()[offset++] = pnum[i] & 0xff; 00347 // pid 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/* start of field pointer */, 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 /* Only check audio/video streams */ 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 // valid for some ATSC/DVB stuff too 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 // audio 00939 case StreamID::AC3Audio: 00940 return "audio-ac3"; // EIT, PMT 00941 case StreamID::MPEG2Audio: 00942 return "audio-mp2-layer[1,2,3]"; // EIT, PMT 00943 case StreamID::MPEG1Audio: 00944 return "audio-mp1-layer[1,2,3]"; // EIT, PMT 00945 case StreamID::MPEG2AudioAmd1: 00946 return "audio-aac-latm"; // EIT, PMT 00947 case StreamID::MPEG2AACAudio: 00948 return "audio-aac"; // EIT, PMT 00949 case StreamID::DTSAudio: 00950 return "audio-dts"; // EIT, PMT 00951 00952 // other 00953 case StreamID::PrivSec: 00954 return "private-sec"; 00955 case StreamID::PrivData: 00956 return "private-data"; 00957 00958 // DSMCC Object Carousel 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 // Can be in any MPEG stream ATSC, DVB, or ARIB ; but defined in SCTE 35 00969 case StreamID::Splice: 00970 return "splice"; // PMT 00971 00972 //case TableID::STUFFING: XXX: Duplicate? 00973 // return "stuffing"; // optionally in any 00974 case TableID::CENSOR: 00975 return "censor"; // EIT, optionally in PMT 00976 case TableID::ECN: 00977 return "extended channel name"; 00978 case TableID::SRVLOC: 00979 return "service location"; // required in VCT 00980 case TableID::TSS: // other channels with same stuff 00981 return "time-shifted service"; 00982 case TableID::CMPNAME: 00983 return "component name"; //??? PMT 00984 } 00985 return "unknown"; 00986 } 00987 00988 QString StreamID::GetDescription(uint stream_id) 00989 { 00990 // valid for some ATSC/DVB stuff too 00991 switch (stream_id) 00992 { 00993 // video 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 // audio 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 // DSMCC Object Carousel 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 // other 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; /* rel_pts_time */ 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; /* rel_pts_time */ 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 // TODO 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; // it's "parsed" but you can't read encrypted portion 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 }
1.7.6.1