MythTV  0.26-pre
mpegstreamdata.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 // Copyright (c) 2003-2004, Daniel Thor Kristjansson
00003 
00004 #include <algorithm> // for find & max
00005 using namespace std;
00006 
00007 // POSIX headers
00008 #include <sys/time.h> // for gettimeofday
00009 
00010 // Qt headers
00011 #include <QString>
00012 
00013 // MythTV headers
00014 #include "mpegstreamdata.h"
00015 #include "mpegtables.h"
00016 #include "ringbuffer.h"
00017 #include "mpegtables.h"
00018 
00019 #include "atscstreamdata.h"
00020 #include "atsctables.h"
00021 
00022 //#define DEBUG_MPEG_RADIO // uncomment to strip video streams from TS stream
00023 
00024 void init_sections(sections_t &sect, uint last_section)
00025 {
00026     static const unsigned char init_bits[8] =
00027         { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, };
00028 
00029     sect.clear();
00030 
00031     uint endz = last_section >> 3;
00032     if (endz)
00033         sect.resize(endz, 0x00);
00034     sect.resize(32, 0xff);
00035     sect[endz] = init_bits[last_section & 0x7];
00036 
00037 #if 0
00038     {
00039         QString msg = QString("init_sections ls(%1): ").arg(last_section);
00040         for (uint i = 0 ; i < 32; i++)
00041             msg += QString("%1 ").arg((int)sect[i], 0, 16);
00042         LOG(VB_GENERAL, LOG_DEBUG, msg);
00043     }
00044 #endif
00045 }
00046 
00047 const unsigned char MPEGStreamData::bit_sel[8] =
00048 {
00049     0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
00050 };
00051 
00065 MPEGStreamData::MPEGStreamData(int desiredProgram, bool cacheTables)
00066     : _sistandard("mpeg"),
00067       _have_CRC_bug(false),
00068       _local_utc_offset(0), _si_time_offset_cnt(0),
00069       _si_time_offset_indx(0),
00070       _eit_helper(NULL), _eit_rate(0.0f),
00071       _listening_disabled(false),
00072       _encryption_lock(QMutex::Recursive), _listener_lock(QMutex::Recursive),
00073       _cache_tables(cacheTables), _cache_lock(QMutex::Recursive),
00074       // Single program stuff
00075       _desired_program(desiredProgram),
00076       _recording_type("all"),
00077       _strip_pmt_descriptors(false),
00078       _normalize_stream_type(true),
00079       _pid_video_single_program(0xffffffff),
00080       _pid_pmt_single_program(0xffffffff),
00081       _pmt_single_program_num_video(1),
00082       _pmt_single_program_num_audio(0),
00083       _pat_single_program(NULL), _pmt_single_program(NULL),
00084       _invalid_pat_seen(false), _invalid_pat_warning(false)
00085 {
00086     _local_utc_offset = calc_utc_offset();
00087 
00088     memset(_si_time_offsets, 0, sizeof(_si_time_offsets));
00089 
00090     AddListeningPID(MPEG_PAT_PID);
00091     AddListeningPID(MPEG_CAT_PID);
00092 }
00093 
00094 MPEGStreamData::~MPEGStreamData()
00095 {
00096     Reset(-1);
00097     SetPATSingleProgram(NULL);
00098     SetPMTSingleProgram(NULL);
00099 
00100     // Delete any cached tables that haven't been returned
00101     psip_refcnt_map_t::iterator it = _cached_slated_for_deletion.begin();
00102     for (; it != _cached_slated_for_deletion.end(); ++it)
00103         delete it.key();
00104 
00105     QMutexLocker locker(&_listener_lock);
00106     _mpeg_listeners.clear();
00107     _mpeg_sp_listeners.clear();
00108 }
00109 
00110 void MPEGStreamData::SetDesiredProgram(int p)
00111 {
00112     bool reset = true;
00113     uint pid = 0;
00114     const ProgramAssociationTable* pat = NULL;
00115     pat_vec_t pats = GetCachedPATs();
00116 
00117     for (uint i = (p) ? 0 : pats.size(); i < pats.size() && !pid; i++)
00118     {
00119         pat = pats[i];
00120         pid = pats[i]->FindPID(p);
00121     }
00122 
00123     if (pid)
00124     {
00125         reset = false;
00126         _desired_program = p;
00127         ProcessPAT(pat);
00128         pmt_vec_t pmts = GetCachedPMTs();
00129         for (uint i = 0; i < pmts.size(); i++)
00130         {
00131             if (pmts[i]->ProgramNumber() == (uint)p)
00132                 ProcessPMT(pmts[i]);
00133         }
00134         ReturnCachedPMTTables(pmts);
00135     }
00136 
00137     ReturnCachedPATTables(pats);
00138 
00139     if (reset)
00140         Reset(p);
00141 }
00142 
00143 void MPEGStreamData::SetRecordingType(const QString &recording_type)
00144 {
00145     _recording_type = recording_type;
00146     _recording_type.detach();
00147     uint neededVideo = (_recording_type == "tv")    ? 1 : 0;
00148     uint neededAudio = (_recording_type == "audio") ? 1 : 0;
00149     SetVideoStreamsRequired(neededVideo);
00150     SetAudioStreamsRequired(neededAudio);
00151 }
00152 
00153 QString MPEGStreamData::GetRecordingType(void) const
00154 {
00155     QString tmp = _recording_type;
00156     tmp.detach();
00157     return tmp;
00158 }
00159 
00160 void MPEGStreamData::SetEITHelper(EITHelper *eit_helper)
00161 {
00162     QMutexLocker locker(&_listener_lock);
00163     _eit_helper = eit_helper;
00164 }
00165 
00166 void MPEGStreamData::SetEITRate(float rate)
00167 {
00168     QMutexLocker locker(&_listener_lock);
00169     _eit_rate = rate;
00170 }
00171 
00172 void MPEGStreamData::Reset(int desiredProgram)
00173 {
00174     _desired_program       = desiredProgram;
00175     _recording_type        = "all";
00176     _strip_pmt_descriptors = false;
00177     _normalize_stream_type = true;
00178 
00179     _invalid_pat_seen = false;
00180 
00181     SetPATSingleProgram(NULL);
00182     SetPMTSingleProgram(NULL);
00183 
00184     pid_psip_map_t old = _partial_psip_packet_cache;
00185     pid_psip_map_t::iterator it = old.begin();
00186     for (; it != old.end(); ++it)
00187         DeletePartialPSIP(it.key());
00188     _partial_psip_packet_cache.clear();
00189 
00190     _pids_listening.clear();
00191     _pids_notlistening.clear();
00192     _pids_writing.clear();
00193     _pids_audio.clear();
00194 
00195     _pid_video_single_program = _pid_pmt_single_program = 0xffffffff;
00196 
00197     _pat_version.clear();
00198     _pat_section_seen.clear();
00199 
00200     _pmt_version.clear();
00201     _pmt_section_seen.clear();
00202 
00203     {
00204         QMutexLocker locker(&_cache_lock);
00205 
00206         pat_cache_t::iterator it1 = _cached_pats.begin();
00207         for (; it1 != _cached_pats.end(); ++it1)
00208             DeleteCachedTable(*it1);
00209         _cached_pats.clear();
00210 
00211         pmt_cache_t::iterator it2 = _cached_pmts.begin();
00212         for (; it2 != _cached_pmts.end(); ++it2)
00213             DeleteCachedTable(*it2);
00214         _cached_pmts.clear();
00215 
00216         cat_cache_t::iterator it3 = _cached_cats.begin();
00217         for (; it3 != _cached_cats.end(); ++it3)
00218             DeleteCachedTable(*it3);
00219         _cached_cats.clear();
00220     }
00221 
00222     ResetDecryptionMonitoringState();
00223 
00224     AddListeningPID(MPEG_PAT_PID);
00225     AddListeningPID(MPEG_CAT_PID);
00226 }
00227 
00228 void MPEGStreamData::DeletePartialPSIP(uint pid)
00229 {
00230     pid_psip_map_t::iterator it = _partial_psip_packet_cache.find(pid);
00231     if (it != _partial_psip_packet_cache.end())
00232     {
00233         PSIPTable *pkt = *it;
00234         _partial_psip_packet_cache.erase(it);
00235         delete pkt;
00236     }
00237 }
00238 
00260 PSIPTable* MPEGStreamData::AssemblePSIP(const TSPacket* tspacket,
00261                                         bool &moreTablePackets)
00262 {
00263     bool broken = true;
00264     moreTablePackets = true;
00265 
00266     PSIPTable* partial = GetPartialPSIP(tspacket->PID());
00267     if (partial && partial->AddTSPacket(tspacket, broken) && !broken)
00268     {
00269         // check if it's safe to read pespacket's Length()
00270         if ((partial->PSIOffset() + 1 + 3) > partial->TSSizeInBuffer())
00271         {
00272             LOG(VB_RECORD, LOG_ERR,
00273                 QString("Discarding broken PSIP packet. Packet's length at "
00274                         "position %1 isn't in the buffer of %2 bytes.")
00275                     .arg(partial->PSIOffset() + 1 + 3)
00276                     .arg(partial->TSSizeInBuffer()));
00277             DeletePartialPSIP(tspacket->PID());
00278             return NULL;
00279         }
00280 
00281         // Discard broken packets
00282         bool buggy = _have_CRC_bug &&
00283         ((TableID::PMT == partial->StreamID()) ||
00284          (TableID::PAT == partial->StreamID()));
00285         if (!buggy && !partial->IsGood())
00286         {
00287             LOG(VB_SIPARSER, LOG_ERR, "Discarding broken PSIP packet");
00288             DeletePartialPSIP(tspacket->PID());
00289             return NULL;
00290         }
00291 
00292         PSIPTable* psip = new PSIPTable(*partial);
00293 
00294         // Advance to the next packet
00295         // pesdata starts only at PSIOffset()+1
00296         uint packetStart = partial->PSIOffset() + 1 + psip->SectionLength();
00297         if (packetStart < partial->TSSizeInBuffer())
00298         {
00299             if (partial->pesdata()[psip->SectionLength()] != 0xff)
00300             {
00301 #if 0 /* This doesn't work, you can't start PSIP packet like this
00302          because the PayloadStart() flag won't be set in this TSPacket
00303          -- dtk  May 4th, 2007
00304        */
00305 
00306                 // If the next section starts in the new tspacket
00307                 // create a new partial packet to prevent overflow
00308                 if ((partial->TSSizeInBuffer() > TSPacket::kSize) &&
00309                     (packetStart >
00310                      partial->TSSizeInBuffer() - TSPacket::PAYLOAD_SIZE))
00311                 {
00312                     // Saving will handle deleting the old one
00313                     SavePartialPSIP(tspacket->PID(),
00314                                    new PSIPTable(*tspacket));
00315                 }
00316                 else
00317 #endif
00318                 {
00319                     partial->SetPSIOffset(partial->PSIOffset() +
00320                                           psip->SectionLength());
00321                 }
00322                 return psip;
00323             }
00324         }
00325         // discard incomplete packets
00326         if (packetStart > partial->TSSizeInBuffer())
00327         {
00328             LOG(VB_RECORD, LOG_ERR,
00329                 QString("Discarding broken PSIP packet. ") +
00330                 QString("Packet with %1 bytes doesn't fit "
00331                         "into a buffer of %2 bytes.")
00332                     .arg(packetStart).arg(partial->TSSizeInBuffer()));
00333             delete psip;
00334             psip = NULL;
00335         }
00336 
00337         moreTablePackets = false;
00338         DeletePartialPSIP(tspacket->PID());
00339         return psip;
00340     }
00341     else if (partial)
00342     {
00343         if (broken)
00344             DeletePartialPSIP(tspacket->PID());
00345 
00346         moreTablePackets = false;
00347         return 0; // partial packet is not yet complete.
00348     }
00349 
00350     if (!tspacket->PayloadStart())
00351     {
00352         // We didn't see this PSIP packet's start, so this must be the
00353         // tail end of something we missed. Ignore it.
00354         moreTablePackets = false;
00355         return 0;
00356     }
00357 
00358     const int offset = tspacket->AFCOffset() + tspacket->StartOfFieldPointer();
00359     if (offset>181)
00360     {
00361         LOG(VB_GENERAL, LOG_ERR, "Error: offset>181, pes length & "
00362                 "current cannot be queried");
00363         return 0;
00364     }
00365 
00366     // table_id (8 bits) and section_length(12), syntax(1), priv(1), res(2)
00367     // pointer_field (+8 bits), since payload start is true if we are here.
00368     const int extra_offset = 4;
00369 
00370     const unsigned char* pesdata = tspacket->data() + offset;
00371     const int pes_length = (pesdata[2] & 0x0f) << 8 | pesdata[3];
00372     if ((pes_length + offset + extra_offset) > 188)
00373     {
00374         SavePartialPSIP(tspacket->PID(), new PSIPTable(*tspacket));
00375         moreTablePackets = false;
00376         return 0;
00377     }
00378 
00379     PSIPTable *psip = new PSIPTable(*tspacket); // must be complete packet
00380 
00381     // There might be another section after this one in the
00382     // current packet. We need room before the end of the
00383     // packet, and it must not be packet stuffing.
00384     if ((offset + psip->SectionLength() < TSPacket::kSize) &&
00385         (pesdata[psip->SectionLength() + 1] != 0xff))
00386     {
00387         // This isn't stuffing, so we need to put this
00388         // on as a partial packet.
00389         PSIPTable *pesp = new PSIPTable(*tspacket);
00390         pesp->SetPSIOffset(offset + psip->SectionLength());
00391         SavePartialPSIP(tspacket->PID(), pesp);
00392         return psip;
00393     }
00394 
00395     moreTablePackets = false;
00396     return psip;
00397 }
00398 
00399 bool MPEGStreamData::CreatePATSingleProgram(
00400     const ProgramAssociationTable& pat)
00401 {
00402     LOG(VB_RECORD, LOG_INFO, "CreatePATSingleProgram()");
00403     LOG(VB_RECORD, LOG_INFO, "PAT in input stream");
00404     LOG(VB_RECORD, LOG_INFO, pat.toString());
00405     if (_desired_program < 0)
00406     {
00407         LOG(VB_RECORD, LOG_ERR, "Desired program not set yet");
00408         return false;
00409     }
00410     _pid_pmt_single_program = pat.FindPID(_desired_program);
00411     LOG(VB_RECORD, LOG_INFO, QString("desired_program(%1) pid(0x%2)").
00412             arg(_desired_program).arg(_pid_pmt_single_program, 0, 16));
00413 
00414     if (!_pid_pmt_single_program)
00415     {
00416         _pid_pmt_single_program = pat.FindAnyPID();
00417         if (!_pid_pmt_single_program)
00418         {
00419             LOG(VB_GENERAL, LOG_ERR, "No program found in PAT. "
00420                                      "This recording will not play in MythTV.");
00421         }
00422         LOG(VB_GENERAL, LOG_ERR,
00423             QString("Desired program #%1 not found in PAT."
00424                     "\n\t\t\tCannot create single program PAT.")
00425                 .arg(_desired_program));
00426         SetPATSingleProgram(NULL);
00427         return false;
00428     }
00429 
00430     AddListeningPID(_pid_pmt_single_program);
00431 
00432     vector<uint> pnums, pids;
00433 
00434     pnums.push_back(1);
00435     pids.push_back(_pid_pmt_single_program);
00436 
00437     uint tsid = pat.TableIDExtension();
00438     uint ver = pat.Version();
00439     ProgramAssociationTable* pat2 =
00440         ProgramAssociationTable::Create(tsid, ver, pnums, pids);
00441 
00442     if (!pat2)
00443     {
00444         LOG(VB_GENERAL, LOG_ERR,
00445             "MPEGStreamData::CreatePATSingleProgram: "
00446             "Failed to create Program Association Table.");
00447         return false;
00448     }
00449 
00450     pat2->tsheader()->SetContinuityCounter(pat.tsheader()->ContinuityCounter());
00451 
00452     LOG(VB_RECORD, LOG_INFO, QString("pmt_pid(0x%1)")
00453             .arg(_pid_pmt_single_program, 0, 16));
00454     LOG(VB_RECORD, LOG_INFO, "PAT for output stream");
00455     LOG(VB_RECORD, LOG_INFO, pat2->toString());
00456 
00457     SetPATSingleProgram(pat2);
00458 
00459     return true;
00460 
00461 }
00462 
00463 static desc_list_t extract_atsc_desc(const tvct_vec_t &tvct,
00464                               const cvct_vec_t &cvct,
00465                               uint pnum)
00466 {
00467     desc_list_t desc;
00468 
00469     vector<const VirtualChannelTable*> vct;
00470 
00471     for (uint i = 0; i < tvct.size(); i++)
00472         vct.push_back(tvct[i]);
00473 
00474     for (uint i = 0; i < cvct.size(); i++)
00475         vct.push_back(cvct[i]);
00476 
00477     for (uint i = 0; i < tvct.size(); i++)
00478     {
00479         for (uint j = 0; j < vct[i]->ChannelCount(); j++)
00480         {
00481             if (vct[i]->ProgramNumber(j) == pnum)
00482             {
00483                 desc_list_t ldesc = MPEGDescriptor::ParseOnlyInclude(
00484                     vct[i]->Descriptors(j), vct[i]->DescriptorsLength(j),
00485                     DescriptorID::caption_service);
00486 
00487                 if (!ldesc.empty())
00488                     desc.insert(desc.end(), ldesc.begin(), ldesc.end());
00489             }
00490         }
00491 
00492         if (0 != vct[i]->GlobalDescriptorsLength())
00493         {
00494             desc_list_t vdesc = MPEGDescriptor::ParseOnlyInclude(
00495                 vct[i]->GlobalDescriptors(),
00496                 vct[i]->GlobalDescriptorsLength(),
00497                 DescriptorID::caption_service);
00498 
00499             if (!vdesc.empty())
00500                 desc.insert(desc.end(), vdesc.begin(), vdesc.end());
00501         }
00502     }
00503 
00504     return desc;
00505 }
00506 
00507 bool MPEGStreamData::CreatePMTSingleProgram(const ProgramMapTable &pmt)
00508 {
00509     LOG(VB_RECORD, LOG_INFO, "CreatePMTSingleProgram()");
00510     LOG(VB_RECORD, LOG_INFO, "PMT in input stream");
00511     LOG(VB_RECORD, LOG_INFO, pmt.toString());
00512 
00513     if (!PATSingleProgram())
00514     {
00515         LOG(VB_RECORD, LOG_ERR, "no PAT yet...");
00516         return false; // no way to properly rewrite pids without PAT
00517     }
00518     pmt.Parse();
00519 
00520     uint programNumber = 1; // MPEG Program Number
00521 
00522     ATSCStreamData *sd = NULL;
00523     tvct_vec_t tvct;
00524     cvct_vec_t cvct;
00525 
00526     desc_list_t gdesc;
00527 
00528     if (!_strip_pmt_descriptors)
00529     {
00530         gdesc = MPEGDescriptor::ParseAndExclude(
00531             pmt.ProgramInfo(), pmt.ProgramInfoLength(),
00532             DescriptorID::conditional_access);
00533 
00534         // If there is no caption descriptor in PMT, copy any caption
00535         // descriptor found in VCT to global descriptors...
00536         sd = dynamic_cast<ATSCStreamData*>(this);
00537         if (sd && !MPEGDescriptor::Find(gdesc, DescriptorID::caption_service))
00538         {
00539             tvct = sd->GetCachedTVCTs();
00540             cvct = sd->GetCachedCVCTs();
00541 
00542             desc_list_t vdesc = extract_atsc_desc(
00543                 tvct, cvct, pmt.ProgramNumber());
00544 
00545             if (!vdesc.empty())
00546                 gdesc.insert(gdesc.end(), vdesc.begin(), vdesc.end());
00547         }
00548     }
00549 
00550     vector<uint> pids;
00551     vector<uint> types;
00552     vector<desc_list_t> pdesc;
00553 
00554     uint video_cnt = 0;
00555     uint audio_cnt = 0;
00556 
00557     vector<uint> videoPIDs, audioPIDs, dataPIDs;
00558 
00559     for (uint i = 0; i < pmt.StreamCount(); i++)
00560     {
00561         uint pid = pmt.StreamPID(i);
00562 
00563         desc_list_t desc = MPEGDescriptor::ParseAndExclude(
00564             pmt.StreamInfo(i), pmt.StreamInfoLength(i),
00565             DescriptorID::conditional_access);
00566 
00567         uint type = StreamID::Normalize(
00568             pmt.StreamType(i), desc, _sistandard);
00569 
00570         // Fixup for ITV HD
00571         if (pid == 3401 && type == StreamID::PrivData &&
00572             pmt.ProgramNumber() == 10510)
00573         {
00574             type = StreamID::H264Video;
00575         }
00576 
00577         bool is_video = StreamID::IsVideo(type);
00578         bool is_audio = StreamID::IsAudio(type);
00579 
00580         if (is_audio)
00581         {
00582             audio_cnt++;
00583             audioPIDs.push_back(pid);
00584         }
00585 
00586 #ifdef DEBUG_MPEG_RADIO
00587         if (is_video)
00588             continue;
00589 #endif // DEBUG_MPEG_RADIO
00590 
00591         if (is_video)
00592         {
00593             video_cnt++;
00594             videoPIDs.push_back(pid);
00595         }
00596 
00597         if (_strip_pmt_descriptors)
00598             desc.clear();
00599 
00600         // Filter out streams not used for basic television
00601         if (_recording_type == "tv" && !is_audio && !is_video &&
00602             !MPEGDescriptor::Find(desc, DescriptorID::teletext) &&
00603             !MPEGDescriptor::Find(desc, DescriptorID::subtitling))
00604         {
00605             continue;
00606         }
00607 
00608         if (!is_audio && !is_video)
00609             dataPIDs.push_back(pid);
00610 
00611         pdesc.push_back(desc);
00612         pids.push_back(pid);
00613         types.push_back(type);
00614     }
00615 
00616     if (video_cnt < _pmt_single_program_num_video)
00617     {
00618         LOG(VB_RECORD, LOG_ERR,
00619             QString("Only %1 video streams seen in PMT, but %2 are required.")
00620                 .arg(video_cnt).arg(_pmt_single_program_num_video));
00621         return false;
00622     }
00623 
00624     if (audioPIDs.size() < _pmt_single_program_num_audio)
00625     {
00626         LOG(VB_RECORD, LOG_ERR,
00627             QString("Only %1 audio streams seen in PMT, but %2 are required.")
00628                 .arg(audioPIDs.size()).arg(_pmt_single_program_num_audio));
00629         return false;
00630     }
00631 
00632     desc_list_t cdesc = MPEGDescriptor::ParseOnlyInclude(
00633         pmt.ProgramInfo(), pmt.ProgramInfoLength(),
00634         DescriptorID::conditional_access);
00635     for (uint i = 0; i < cdesc.size(); i++)
00636     {
00637         ConditionalAccessDescriptor cad(cdesc[i]);
00638         if (cad.IsValid())
00639             AddListeningPID(cad.PID());
00640     }
00641 
00642     _pids_audio.clear();
00643     for (uint i = 0; i < audioPIDs.size(); i++)
00644         AddAudioPID(audioPIDs[i]);
00645 
00646     if (videoPIDs.size() >= 1)
00647         _pid_video_single_program = videoPIDs[0];
00648     for (uint i = 1; i < videoPIDs.size(); i++)
00649         AddWritingPID(videoPIDs[i]);
00650 
00651     for (uint i = 0; i < dataPIDs.size(); i++)
00652         AddWritingPID(dataPIDs[i]);
00653 
00654     // Timebase
00655     int pcrpidIndex = pmt.FindPID(pmt.PCRPID());
00656     if (pcrpidIndex < 0)
00657     {
00658         // the timecode reference stream is not in the PMT,
00659         // add stream to misc record streams
00660         AddWritingPID(pmt.PCRPID());
00661     }
00662 
00663     // Create the PMT
00664     ProgramMapTable *pmt2 = ProgramMapTable::Create(
00665         programNumber, _pid_pmt_single_program, pmt.PCRPID(),
00666         pmt.Version(), gdesc, pids, types, pdesc);
00667 
00668     // Return any TVCT & CVCT tables, once we've copied any descriptors.
00669     if (sd)
00670     {
00671         sd->ReturnCachedTVCTTables(tvct);
00672         sd->ReturnCachedCVCTTables(cvct);
00673     }
00674 
00675     // Set Continuity Header
00676     uint cc_cnt = pmt.tsheader()->ContinuityCounter();
00677     pmt2->tsheader()->SetContinuityCounter(cc_cnt);
00678     SetPMTSingleProgram(pmt2);
00679 
00680     LOG(VB_RECORD, LOG_INFO, "PMT for output stream");
00681     LOG(VB_RECORD, LOG_INFO, pmt2->toString());
00682 
00683     return true;
00684 }
00685 
00689 bool MPEGStreamData::IsRedundant(uint pid, const PSIPTable &psip) const
00690 {
00691     (void) pid;
00692     const int table_id = psip.TableID();
00693     const int version  = psip.Version();
00694 
00695     if (TableID::PAT == table_id)
00696     {
00697         if (VersionPAT(psip.TableIDExtension()) != version)
00698             return false;
00699         return PATSectionSeen(psip.TableIDExtension(), psip.Section());
00700     }
00701 
00702     if (TableID::CAT == table_id)
00703     {
00704         if (VersionCAT(psip.TableIDExtension()) != version)
00705            return false;
00706         return CATSectionSeen(psip.TableIDExtension(), psip.Section());
00707     }
00708 
00709     if (TableID::PMT == table_id)
00710     {
00711         if (VersionPMT(psip.TableIDExtension()) != version)
00712             return false;
00713         return PMTSectionSeen(psip.TableIDExtension(), psip.Section());
00714     }
00715 
00716     return false;
00717 }
00718 
00722 bool MPEGStreamData::HandleTables(uint pid, const PSIPTable &psip)
00723 {
00724     if (IsRedundant(pid, psip))
00725         return true;
00726 
00727     const int version = psip.Version();
00728     // If we get this far decode table
00729     switch (psip.TableID())
00730     {
00731         case TableID::PAT:
00732         {
00733             uint tsid = psip.TableIDExtension();
00734             SetVersionPAT(tsid, version, psip.LastSection());
00735             SetPATSectionSeen(tsid, psip.Section());
00736 
00737             ProgramAssociationTable pat(psip);
00738 
00739             if (_cache_tables)
00740                 CachePAT(&pat);
00741 
00742             ProcessPAT(&pat);
00743 
00744             return true;
00745         }
00746         case TableID::CAT:
00747         {
00748             uint tsid = psip.TableIDExtension();
00749             SetVersionCAT(tsid, version, psip.LastSection());
00750             SetCATSectionSeen(tsid, psip.Section());
00751 
00752             ConditionalAccessTable cat(psip);
00753 
00754             if (_cache_tables)
00755                 CacheCAT(&cat);
00756 
00757             ProcessCAT(&cat);
00758 
00759             return true;
00760         }
00761         case TableID::PMT:
00762         {
00763             uint prog_num = psip.TableIDExtension();
00764             SetVersionPMT(prog_num, version, psip.LastSection());
00765             SetPMTSectionSeen(prog_num, psip.Section());
00766 
00767             ProgramMapTable pmt(psip);
00768 
00769             if (_cache_tables)
00770                 CachePMT(&pmt);
00771 
00772             ProcessPMT(&pmt);
00773 
00774             return true;
00775         }
00776         case TableID::SITscte:
00777         {
00778             SpliceInformationTable sit(psip);
00779 
00780             _listener_lock.lock();
00781             for (uint i = 0; i < _mpeg_listeners.size(); i++)
00782                 _mpeg_listeners[i]->HandleSplice(&sit);
00783             _listener_lock.unlock();
00784 
00785             return true;
00786         }
00787     }
00788     return false;
00789 }
00790 
00791 void MPEGStreamData::ProcessPAT(const ProgramAssociationTable *pat)
00792 {
00793     bool foundProgram = pat->FindPID(_desired_program);
00794 
00795     _listener_lock.lock();
00796     for (uint i = 0; i < _mpeg_listeners.size(); i++)
00797         _mpeg_listeners[i]->HandlePAT(pat);
00798     _listener_lock.unlock();
00799 
00800     if (_desired_program < 0)
00801         return;
00802 
00803     bool send_single_program = false;
00804     if (!_invalid_pat_seen && !foundProgram)
00805     {
00806         _invalid_pat_seen = true;
00807         _invalid_pat_warning = false;
00808         _invalid_pat_timer.start();
00809         LOG(VB_RECORD, LOG_WARNING,
00810             "ProcessPAT: PAT is missing program, setting timeout");
00811     }
00812     else if (_invalid_pat_seen && !foundProgram &&
00813              (_invalid_pat_timer.elapsed() > 400) && !_invalid_pat_warning)
00814     {
00815         _invalid_pat_warning = true; // only emit one warning...
00816         // After 400ms emit error if we haven't found correct PAT.
00817         LOG(VB_GENERAL, LOG_ERR,
00818             "ProcessPAT: Program not found in PAT. "
00819             "Rescan your transports.");
00820 
00821         send_single_program = CreatePATSingleProgram(*pat);
00822     }
00823     else if (foundProgram)
00824     {
00825         if (_invalid_pat_seen)
00826             LOG(VB_RECORD, LOG_INFO,
00827                 "ProcessPAT: Good PAT seen after a bad PAT");
00828 
00829         _invalid_pat_seen = false;
00830 
00831         send_single_program = CreatePATSingleProgram(*pat);
00832     }
00833 
00834     if (send_single_program)
00835     {
00836         QMutexLocker locker(&_listener_lock);
00837         ProgramAssociationTable *pat_sp = PATSingleProgram();
00838         for (uint i = 0; i < _mpeg_sp_listeners.size(); i++)
00839             _mpeg_sp_listeners[i]->HandleSingleProgramPAT(pat_sp);
00840     }
00841 }
00842 
00843 void MPEGStreamData::ProcessCAT(const ConditionalAccessTable *cat)
00844 {
00845     _listener_lock.lock();
00846     for (uint i = 0; i < _mpeg_listeners.size(); i++)
00847         _mpeg_listeners[i]->HandleCAT(cat);
00848     _listener_lock.unlock();
00849 
00850     desc_list_t cdesc = MPEGDescriptor::ParseOnlyInclude(
00851         cat->Descriptors(), cat->DescriptorsLength(),
00852         DescriptorID::conditional_access);
00853     for (uint i = 0; i < cdesc.size(); i++)
00854     {
00855         ConditionalAccessDescriptor cad(cdesc[i]);
00856         if (cad.IsValid())
00857             AddListeningPID(cad.PID());
00858     }
00859 }
00860 
00861 void MPEGStreamData::ProcessPMT(const ProgramMapTable *pmt)
00862 {
00863     _listener_lock.lock();
00864     for (uint i = 0; i < _mpeg_listeners.size(); i++)
00865         _mpeg_listeners[i]->HandlePMT(pmt->ProgramNumber(), pmt);
00866     _listener_lock.unlock();
00867 
00868     bool desired = pmt->ProgramNumber() == (uint) _desired_program;
00869     if (desired && CreatePMTSingleProgram(*pmt))
00870     {
00871         QMutexLocker locker(&_listener_lock);
00872         ProgramMapTable *pmt_sp = PMTSingleProgram();
00873         for (uint i = 0; i < _mpeg_sp_listeners.size(); i++)
00874             _mpeg_sp_listeners[i]->HandleSingleProgramPMT(pmt_sp);
00875     }
00876 }
00877 
00878 double MPEGStreamData::TimeOffset(void) const
00879 {
00880     QMutexLocker locker(&_si_time_lock);
00881     if (!_si_time_offset_cnt)
00882         return 0.0;
00883 
00884     double avg_offset = 0.0;
00885     double mult = 1.0 / _si_time_offset_cnt;
00886     for (uint i = 0; i < _si_time_offset_cnt; i++)
00887         avg_offset += _si_time_offsets[i] * mult;
00888 
00889     return avg_offset;
00890 }
00891 
00892 void MPEGStreamData::UpdateTimeOffset(uint64_t _si_utc_time)
00893 {
00894     struct timeval tm;
00895     if (gettimeofday(&tm, NULL) != 0)
00896         return;
00897 
00898     double utc_time = tm.tv_sec + (tm.tv_usec * 0.000001);
00899     double si_time  = _si_utc_time;
00900 
00901     QMutexLocker locker(&_si_time_lock);
00902     _si_time_offsets[_si_time_offset_indx] = si_time - utc_time;
00903 
00904     if (_si_time_offset_indx + 1 > _si_time_offset_cnt)
00905         _si_time_offset_cnt = _si_time_offset_indx + 1;
00906 
00907     _si_time_offset_indx = (_si_time_offset_indx + 1) & 0xf;
00908 
00909 }
00910 
00911 #define DONE_WITH_PSIP_PACKET() { if (psip) delete psip; \
00912     if (morePSIPTables) goto HAS_ANOTHER_PSIP; else return; }
00913 
00917 void MPEGStreamData::HandleTSTables(const TSPacket* tspacket)
00918 {
00919     bool morePSIPTables;
00920   HAS_ANOTHER_PSIP:
00921     // Assemble PSIP
00922     PSIPTable *psip = AssemblePSIP(tspacket, morePSIPTables);
00923     if (!psip)
00924        return;
00925 
00926     // drop stuffing packets
00927     if ((TableID::ST       == psip->TableID()) ||
00928         (TableID::STUFFING == psip->TableID()))
00929     {
00930         LOG(VB_RECORD, LOG_DEBUG, "Dropping Stuffing table");
00931         DONE_WITH_PSIP_PACKET();
00932     }
00933 
00934     // Don't do validation on tables withotu CRC
00935     if (!psip->HasCRC())
00936     {
00937         HandleTables(tspacket->PID(), *psip);
00938         DONE_WITH_PSIP_PACKET();
00939     }
00940 
00941     // Validate PSIP
00942     // but don't validate PMT/PAT if our driver has the PMT/PAT CRC bug.
00943     bool buggy = _have_CRC_bug &&
00944         ((TableID::PMT == psip->TableID()) ||
00945          (TableID::PAT == psip->TableID()));
00946     if (!buggy && !psip->IsGood())
00947     {
00948         LOG(VB_RECORD, LOG_ERR,
00949             QString("PSIP packet failed CRC check. pid(0x%1) type(0x%2)")
00950                 .arg(tspacket->PID(),0,16).arg(psip->TableID(),0,16));
00951         DONE_WITH_PSIP_PACKET();
00952     }
00953 
00954     if (TableID::MGT <= psip->TableID() && psip->TableID() <= TableID::STT &&
00955         !psip->IsCurrent())
00956     { // we don't cache the next table, for now
00957         LOG(VB_RECORD, LOG_DEBUG, QString("Table not current 0x%1")
00958             .arg(psip->TableID(),2,16,QChar('0')));
00959         DONE_WITH_PSIP_PACKET();
00960     }
00961 
00962     if (tspacket->Scrambled())
00963     { // scrambled! ATSC, DVB require tables not to be scrambled
00964         LOG(VB_RECORD, LOG_ERR,
00965             "PSIP packet is scrambled, not ATSC/DVB compiant");
00966         DONE_WITH_PSIP_PACKET();
00967     }
00968 
00969     if (!psip->VerifyPSIP(!_have_CRC_bug))
00970     {
00971         LOG(VB_RECORD, LOG_ERR, QString("PSIP table 0x%1 is invalid")
00972             .arg(psip->TableID(),2,16,QChar('0')));
00973         DONE_WITH_PSIP_PACKET();
00974     }
00975 
00976     // Don't decode redundant packets,
00977     // but if it is a desired PAT or PMT emit a "heartbeat" signal.
00978     if (IsRedundant(tspacket->PID(), *psip))
00979     {
00980         if (TableID::PAT == psip->TableID())
00981         {
00982             QMutexLocker locker(&_listener_lock);
00983             ProgramAssociationTable *pat_sp = PATSingleProgram();
00984             for (uint i = 0; i < _mpeg_sp_listeners.size(); i++)
00985                 _mpeg_sp_listeners[i]->HandleSingleProgramPAT(pat_sp);
00986         }
00987         if (TableID::PMT == psip->TableID() &&
00988             tspacket->PID() == _pid_pmt_single_program)
00989         {
00990             QMutexLocker locker(&_listener_lock);
00991             ProgramMapTable *pmt_sp = PMTSingleProgram();
00992             for (uint i = 0; i < _mpeg_sp_listeners.size(); i++)
00993                 _mpeg_sp_listeners[i]->HandleSingleProgramPMT(pmt_sp);
00994         }
00995         DONE_WITH_PSIP_PACKET(); // already parsed this table, toss it.
00996     }
00997 
00998     HandleTables(tspacket->PID(), *psip);
00999 
01000     DONE_WITH_PSIP_PACKET();
01001 }
01002 #undef DONE_WITH_PSIP_PACKET
01003 
01004 int MPEGStreamData::ProcessData(const unsigned char *buffer, int len)
01005 {
01006     int pos = 0;
01007     bool resync = false;
01008 
01009     while (pos + int(TSPacket::kSize) <= len)
01010     { // while we have a whole packet left...
01011         if (buffer[pos] != SYNC_BYTE || resync)
01012         {
01013             int newpos = ResyncStream(buffer, pos+1, len);
01014             LOG(VB_RECORD, LOG_DEBUG, QString("Resyncing @ %1+1 w/len %2 -> %3")
01015                 .arg(pos).arg(len).arg(newpos));
01016             if (newpos == -1)
01017                 return len - pos;
01018             if (newpos == -2)
01019                 return TSPacket::kSize;
01020             pos = newpos;
01021         }
01022 
01023         const TSPacket *pkt = reinterpret_cast<const TSPacket*>(&buffer[pos]);
01024         pos += TSPacket::kSize; // Advance to next TS packet
01025         resync = false;
01026         if (!ProcessTSPacket(*pkt))
01027         {
01028             if (pos + int(TSPacket::kSize) > len)
01029                 continue;
01030             if (buffer[pos] != SYNC_BYTE)
01031             {
01032                 // if ProcessTSPacket fails, and we don't appear to be
01033                 // in sync on the next packet, then resync. Otherwise
01034                 // just process the next packet normally.
01035                 pos -= TSPacket::kSize;
01036                 resync = true;
01037             }
01038         }
01039     }
01040 
01041     return len - pos;
01042 }
01043 
01044 bool MPEGStreamData::ProcessTSPacket(const TSPacket& tspacket)
01045 {
01046     bool ok = !tspacket.TransportError();
01047 
01048     if (IsEncryptionTestPID(tspacket.PID()))
01049     {
01050         ProcessEncryptedPacket(tspacket);
01051     }
01052 
01053     if (!ok)
01054         return false;
01055 
01056     if (tspacket.Scrambled())
01057         return true;
01058 
01059     if (IsVideoPID(tspacket.PID()))
01060     {
01061         for (uint j = 0; j < _ts_av_listeners.size(); j++)
01062             _ts_av_listeners[j]->ProcessVideoTSPacket(tspacket);
01063 
01064         return true;
01065     }
01066 
01067     if (IsAudioPID(tspacket.PID()))
01068     {
01069         for (uint j = 0; j < _ts_av_listeners.size(); j++)
01070             _ts_av_listeners[j]->ProcessAudioTSPacket(tspacket);
01071 
01072         return true;
01073     }
01074 
01075     if (IsWritingPID(tspacket.PID()))
01076     {
01077         for (uint j = 0; j < _ts_writing_listeners.size(); j++)
01078             _ts_writing_listeners[j]->ProcessTSPacket(tspacket);
01079     }
01080 
01081     if (IsListeningPID(tspacket.PID()) && tspacket.HasPayload())
01082     {
01083         HandleTSTables(&tspacket);
01084     }
01085 
01086     return true;
01087 }
01088 
01089 int MPEGStreamData::ResyncStream(const unsigned char *buffer, int curr_pos,
01090                                  int len)
01091 {
01092     // Search for two sync bytes 188 bytes apart,
01093     int pos = curr_pos;
01094     int nextpos = pos + TSPacket::kSize;
01095     if (nextpos >= len)
01096         return -1; // not enough bytes; caller should try again
01097 
01098     while (buffer[pos] != SYNC_BYTE || buffer[nextpos] != SYNC_BYTE)
01099     {
01100         pos++;
01101         nextpos++;
01102         if (nextpos == len)
01103             return -2; // not found
01104     }
01105 
01106     return pos;
01107 }
01108 
01109 bool MPEGStreamData::IsListeningPID(uint pid) const
01110 {
01111     if (_listening_disabled || IsNotListeningPID(pid))
01112         return false;
01113     pid_map_t::const_iterator it = _pids_listening.find(pid);
01114     return it != _pids_listening.end();
01115 }
01116 
01117 bool MPEGStreamData::IsNotListeningPID(uint pid) const
01118 {
01119     pid_map_t::const_iterator it = _pids_notlistening.find(pid);
01120     return it != _pids_notlistening.end();
01121 }
01122 
01123 bool MPEGStreamData::IsWritingPID(uint pid) const
01124 {
01125     pid_map_t::const_iterator it = _pids_writing.find(pid);
01126     return it != _pids_writing.end();
01127 }
01128 
01129 bool MPEGStreamData::IsAudioPID(uint pid) const
01130 {
01131     pid_map_t::const_iterator it = _pids_audio.find(pid);
01132     return it != _pids_audio.end();
01133 }
01134 
01135 uint MPEGStreamData::GetPIDs(pid_map_t &pids) const
01136 {
01137     uint sz = pids.size();
01138 
01139     if (_pid_video_single_program < 0x1fff)
01140         pids[_pid_video_single_program] = kPIDPriorityHigh;
01141 
01142     pid_map_t::const_iterator it = _pids_listening.begin();
01143     for (; it != _pids_listening.end(); ++it)
01144         pids[it.key()] = max(pids[it.key()], *it);
01145 
01146     it = _pids_audio.begin();
01147     for (; it != _pids_audio.end(); ++it)
01148         pids[it.key()] = max(pids[it.key()], *it);
01149 
01150     it = _pids_writing.begin();
01151     for (; it != _pids_writing.end(); ++it)
01152         pids[it.key()] = max(pids[it.key()], *it);
01153 
01154     return pids.size() - sz;
01155 }
01156 
01157 PIDPriority MPEGStreamData::GetPIDPriority(uint pid) const
01158 {
01159     if (_pid_video_single_program == pid)
01160         return kPIDPriorityHigh;
01161 
01162     pid_map_t::const_iterator it;
01163     it = _pids_listening.find(pid);
01164     if (it != _pids_listening.end())
01165         return *it;
01166     it = _pids_notlistening.find(pid);
01167     if (it != _pids_notlistening.end())
01168         return *it;
01169     it = _pids_writing.find(pid);
01170     if (it != _pids_writing.end())
01171         return *it;
01172     it = _pids_audio.find(pid);
01173     if (it != _pids_audio.end())
01174         return *it;
01175 
01176     return kPIDPriorityNone;
01177 }
01178 
01179 void MPEGStreamData::SavePartialPSIP(uint pid, PSIPTable* packet)
01180 {
01181     pid_psip_map_t::iterator it = _partial_psip_packet_cache.find(pid);
01182     if (it == _partial_psip_packet_cache.end())
01183         _partial_psip_packet_cache[pid] = packet;
01184     else
01185     {
01186         PSIPTable *old = *it;
01187         _partial_psip_packet_cache.remove(pid);
01188         _partial_psip_packet_cache.insert(pid, packet);
01189         delete old;
01190     }
01191 }
01192 
01193 void MPEGStreamData::SetPATSectionSeen(uint tsid, uint section)
01194 {
01195     sections_map_t::iterator it = _pat_section_seen.find(tsid);
01196     if (it == _pat_section_seen.end())
01197     {
01198         _pat_section_seen[tsid].resize(32, 0);
01199         it = _pat_section_seen.find(tsid);
01200     }
01201     (*it)[section>>3] |= bit_sel[section & 0x7];
01202 }
01203 
01204 bool MPEGStreamData::PATSectionSeen(uint tsid, uint section) const
01205 {
01206     sections_map_t::const_iterator it = _pat_section_seen.find(tsid);
01207     if (it == _pat_section_seen.end())
01208         return false;
01209     return (bool) ((*it)[section>>3] & bit_sel[section & 0x7]);
01210 }
01211 
01212 bool MPEGStreamData::HasAllPATSections(uint tsid) const
01213 {
01214     sections_map_t::const_iterator it = _pat_section_seen.find(tsid);
01215     if (it == _pat_section_seen.end())
01216         return false;
01217     for (uint i = 0; i < 32; i++)
01218         if ((*it)[i] != 0xff)
01219             return false;
01220     return true;
01221 }
01222 
01223 void MPEGStreamData::SetCATSectionSeen(uint tsid, uint section)
01224 {
01225     sections_map_t::iterator it = _cat_section_seen.find(tsid);
01226     if (it == _cat_section_seen.end())
01227     {
01228         _cat_section_seen[tsid].resize(32, 0);
01229         it = _cat_section_seen.find(tsid);
01230     }
01231     (*it)[section>>3] |= bit_sel[section & 0x7];
01232 }
01233 
01234 bool MPEGStreamData::CATSectionSeen(uint tsid, uint section) const
01235 {
01236     sections_map_t::const_iterator it = _cat_section_seen.find(tsid);
01237     if (it == _cat_section_seen.end())
01238         return false;
01239     return (bool) ((*it)[section>>3] & bit_sel[section & 0x7]);
01240 }
01241 
01242 bool MPEGStreamData::HasAllCATSections(uint tsid) const
01243 {
01244     sections_map_t::const_iterator it = _cat_section_seen.find(tsid);
01245     if (it == _cat_section_seen.end())
01246         return false;
01247     for (uint i = 0; i < 32; i++)
01248         if ((*it)[i] != 0xff)
01249             return false;
01250     return true;
01251 }
01252 
01253 void MPEGStreamData::SetPMTSectionSeen(uint prog_num, uint section)
01254 {
01255     sections_map_t::iterator it = _pmt_section_seen.find(prog_num);
01256     if (it == _pmt_section_seen.end())
01257     {
01258         _pmt_section_seen[prog_num].resize(32, 0);
01259         it = _pmt_section_seen.find(prog_num);
01260     }
01261     (*it)[section>>3] |= bit_sel[section & 0x7];
01262 }
01263 
01264 bool MPEGStreamData::PMTSectionSeen(uint prog_num, uint section) const
01265 {
01266     sections_map_t::const_iterator it = _pmt_section_seen.find(prog_num);
01267     if (it == _pmt_section_seen.end())
01268         return false;
01269     return (bool) ((*it)[section>>3] & bit_sel[section & 0x7]);
01270 }
01271 
01272 bool MPEGStreamData::HasAllPMTSections(uint prog_num) const
01273 {
01274     sections_map_t::const_iterator it = _pmt_section_seen.find(prog_num);
01275     if (it == _pmt_section_seen.end())
01276         return false;
01277     for (uint i = 0; i < 32; i++)
01278         if ((*it)[i] != 0xff)
01279             return false;
01280     return true;
01281 }
01282 
01283 bool MPEGStreamData::HasProgram(uint progNum) const
01284 {
01285     pmt_ptr_t pmt = GetCachedPMT(progNum, 0);
01286     bool hasit = pmt;
01287     ReturnCachedTable(pmt);
01288 
01289     return hasit;
01290 }
01291 
01292 bool MPEGStreamData::HasCachedAllPAT(uint tsid) const
01293 {
01294     QMutexLocker locker(&_cache_lock);
01295 
01296     pat_cache_t::const_iterator it = _cached_pats.find(tsid << 8);
01297     if (it == _cached_pats.end())
01298         return false;
01299 
01300     uint last_section = (*it)->LastSection();
01301     if (!last_section)
01302         return true;
01303 
01304     for (uint i = 1; i <= last_section; i++)
01305         if (_cached_pats.find((tsid << 8) | i) == _cached_pats.end())
01306             return false;
01307 
01308     return true;
01309 }
01310 
01311 bool MPEGStreamData::HasCachedAnyPAT(uint tsid) const
01312 {
01313     QMutexLocker locker(&_cache_lock);
01314 
01315     for (uint i = 0; i <= 255; i++)
01316         if (_cached_pats.find((tsid << 8) | i) != _cached_pats.end())
01317             return true;
01318 
01319     return false;
01320 }
01321 
01322 bool MPEGStreamData::HasCachedAnyPAT(void) const
01323 {
01324     QMutexLocker locker(&_cache_lock);
01325     return _cached_pats.size();
01326 }
01327 
01328 bool MPEGStreamData::HasCachedAllCAT(uint tsid) const
01329 {
01330     QMutexLocker locker(&_cache_lock);
01331 
01332     cat_cache_t::const_iterator it = _cached_cats.find(tsid << 8);
01333     if (it == _cached_cats.end())
01334         return false;
01335 
01336     uint last_section = (*it)->LastSection();
01337     if (!last_section)
01338         return true;
01339 
01340     for (uint i = 1; i <= last_section; i++)
01341         if (_cached_cats.find((tsid << 8) | i) == _cached_cats.end())
01342             return false;
01343 
01344     return true;
01345 }
01346 
01347 bool MPEGStreamData::HasCachedAnyCAT(uint tsid) const
01348 {
01349     QMutexLocker locker(&_cache_lock);
01350 
01351     for (uint i = 0; i <= 255; i++)
01352         if (_cached_cats.find((tsid << 8) | i) != _cached_cats.end())
01353             return true;
01354 
01355     return false;
01356 }
01357 
01358 bool MPEGStreamData::HasCachedAnyCAT(void) const
01359 {
01360     QMutexLocker locker(&_cache_lock);
01361     return _cached_cats.size();
01362 }
01363 
01364 bool MPEGStreamData::HasCachedAllPMT(uint pnum) const
01365 {
01366     QMutexLocker locker(&_cache_lock);
01367 
01368     pmt_cache_t::const_iterator it = _cached_pmts.find(pnum << 8);
01369     if (it == _cached_pmts.end())
01370         return false;
01371 
01372     uint last_section = (*it)->LastSection();
01373     if (!last_section)
01374         return true;
01375 
01376     for (uint i = 1; i <= last_section; i++)
01377         if (_cached_pmts.find((pnum << 8) | i) == _cached_pmts.end())
01378             return false;
01379 
01380     return true;
01381 }
01382 
01383 bool MPEGStreamData::HasCachedAnyPMT(uint pnum) const
01384 {
01385     QMutexLocker locker(&_cache_lock);
01386 
01387     for (uint i = 0; i <= 255; i++)
01388         if (_cached_pmts.find((pnum << 8) | i) != _cached_pmts.end())
01389             return true;
01390 
01391     return false;
01392 }
01393 
01394 bool MPEGStreamData::HasCachedAllPMTs(void) const
01395 {
01396     QMutexLocker locker(&_cache_lock);
01397 
01398     pat_cache_t::const_iterator it = _cached_pats.begin();
01399     for (; it != _cached_pats.end(); ++it)
01400     {
01401         const ProgramAssociationTable *pat = *it;
01402         if (!HasCachedAllPAT(pat->TransportStreamID()))
01403             return false;
01404 
01405         for (uint i = 0; i < pat->ProgramCount(); i++)
01406         {
01407             uint prognum = pat->ProgramNumber(i);
01408             if (prognum && !HasCachedAllPMT(prognum))
01409                 return false;
01410         }
01411     }
01412 
01413     return true;
01414 }
01415 
01416 bool MPEGStreamData::HasCachedAnyPMTs(void) const
01417 {
01418     QMutexLocker locker(&_cache_lock);
01419     return _cached_pmts.size();
01420 }
01421 
01422 pat_ptr_t MPEGStreamData::GetCachedPAT(uint tsid, uint section_num) const
01423 {
01424     QMutexLocker locker(&_cache_lock);
01425     ProgramAssociationTable *pat = NULL;
01426 
01427     uint key = (tsid << 8) | section_num;
01428     pat_cache_t::const_iterator it = _cached_pats.find(key);
01429     if (it != _cached_pats.end())
01430         IncrementRefCnt(pat = *it);
01431 
01432     return pat;
01433 }
01434 
01435 pat_vec_t MPEGStreamData::GetCachedPATs(uint tsid) const
01436 {
01437     QMutexLocker locker(&_cache_lock);
01438     pat_vec_t pats;
01439 
01440     for (uint i=0; i < 256; i++)
01441     {
01442         ProgramAssociationTable *pat = GetCachedPAT(tsid, i);
01443         if (pat)
01444             pats.push_back(pat);
01445     }
01446 
01447     return pats;
01448 }
01449 
01450 pat_vec_t MPEGStreamData::GetCachedPATs(void) const
01451 {
01452     QMutexLocker locker(&_cache_lock);
01453     pat_vec_t pats;
01454 
01455     pat_cache_t::const_iterator it = _cached_pats.begin();
01456     for (; it != _cached_pats.end(); ++it)
01457     {
01458         ProgramAssociationTable* pat = *it;
01459         IncrementRefCnt(pat);
01460         pats.push_back(pat);
01461     }
01462 
01463     return pats;
01464 }
01465 
01466 cat_ptr_t MPEGStreamData::GetCachedCAT(uint tsid, uint section_num) const
01467 {
01468     QMutexLocker locker(&_cache_lock);
01469     ConditionalAccessTable *cat = NULL;
01470 
01471     uint key = (tsid << 8) | section_num;
01472     cat_cache_t::const_iterator it = _cached_cats.find(key);
01473     if (it != _cached_cats.end())
01474         IncrementRefCnt(cat = *it);
01475 
01476     return cat;
01477 }
01478 
01479 cat_vec_t MPEGStreamData::GetCachedCATs(uint tsid) const
01480 {
01481     QMutexLocker locker(&_cache_lock);
01482     cat_vec_t cats;
01483 
01484     for (uint i=0; i < 256; i++)
01485     {
01486         ConditionalAccessTable *cat = GetCachedCAT(tsid, i);
01487         if (cat)
01488             cats.push_back(cat);
01489     }
01490 
01491     return cats;
01492 }
01493 
01494 cat_vec_t MPEGStreamData::GetCachedCATs(void) const
01495 {
01496     QMutexLocker locker(&_cache_lock);
01497     cat_vec_t cats;
01498 
01499     cat_cache_t::const_iterator it = _cached_cats.begin();
01500     for (; it != _cached_cats.end(); ++it)
01501     {
01502         ConditionalAccessTable* cat = *it;
01503         IncrementRefCnt(cat);
01504         cats.push_back(cat);
01505     }
01506 
01507     return cats;
01508 }
01509 
01510 pmt_ptr_t MPEGStreamData::GetCachedPMT(
01511     uint program_num, uint section_num) const
01512 {
01513     QMutexLocker locker(&_cache_lock);
01514     ProgramMapTable *pmt = NULL;
01515 
01516     uint key = (program_num << 8) | section_num;
01517     pmt_cache_t::const_iterator it = _cached_pmts.find(key);
01518     if (it != _cached_pmts.end())
01519         IncrementRefCnt(pmt = *it);
01520 
01521     return pmt;
01522 }
01523 
01524 pmt_vec_t MPEGStreamData::GetCachedPMTs(void) const
01525 {
01526     QMutexLocker locker(&_cache_lock);
01527     vector<const ProgramMapTable*> pmts;
01528 
01529     pmt_cache_t::const_iterator it = _cached_pmts.begin();
01530     for (; it != _cached_pmts.end(); ++it)
01531     {
01532         ProgramMapTable* pmt = *it;
01533         IncrementRefCnt(pmt);
01534         pmts.push_back(pmt);
01535     }
01536 
01537     return pmts;
01538 }
01539 
01540 pmt_map_t MPEGStreamData::GetCachedPMTMap(void) const
01541 {
01542     QMutexLocker locker(&_cache_lock);
01543     pmt_map_t pmts;
01544 
01545     pmt_cache_t::const_iterator it = _cached_pmts.begin();
01546     for (; it != _cached_pmts.end(); ++it)
01547     {
01548         ProgramMapTable* pmt = *it;
01549         IncrementRefCnt(pmt);
01550         pmts[pmt->ProgramNumber()].push_back(pmt);
01551     }
01552 
01553     return pmts;
01554 }
01555 
01556 void MPEGStreamData::ReturnCachedTable(const PSIPTable *psip) const
01557 {
01558     QMutexLocker locker(&_cache_lock);
01559 
01560     int val = _cached_ref_cnt[psip] - 1;
01561     _cached_ref_cnt[psip] = val;
01562 
01563     // if ref <= 0 and table was slated for deletion, delete it.
01564     if (val <= 0)
01565     {
01566         psip_refcnt_map_t::iterator it;
01567         it = _cached_slated_for_deletion.find(psip);
01568         if (it != _cached_slated_for_deletion.end())
01569             DeleteCachedTable(const_cast<PSIPTable*>(psip));
01570     }
01571 }
01572 
01573 void MPEGStreamData::ReturnCachedPATTables(pat_vec_t &pats) const
01574 {
01575     for (pat_vec_t::iterator it = pats.begin(); it != pats.end(); ++it)
01576         ReturnCachedTable(*it);
01577     pats.clear();
01578 }
01579 
01580 void MPEGStreamData::ReturnCachedPATTables(pat_map_t &pats) const
01581 {
01582     for (pat_map_t::iterator it = pats.begin(); it != pats.end(); ++it)
01583         ReturnCachedPATTables(*it);
01584     pats.clear();
01585 }
01586 
01587 void MPEGStreamData::ReturnCachedCATTables(cat_vec_t &cats) const
01588 {
01589     for (cat_vec_t::iterator it = cats.begin(); it != cats.end(); ++it)
01590         ReturnCachedTable(*it);
01591     cats.clear();
01592 }
01593 
01594 void MPEGStreamData::ReturnCachedCATTables(cat_map_t &cats) const
01595 {
01596     for (cat_map_t::iterator it = cats.begin(); it != cats.end(); ++it)
01597         ReturnCachedCATTables(*it);
01598     cats.clear();
01599 }
01600 
01601 void MPEGStreamData::ReturnCachedPMTTables(pmt_vec_t &pmts) const
01602 {
01603     for (pmt_vec_t::iterator it = pmts.begin(); it != pmts.end(); ++it)
01604         ReturnCachedTable(*it);
01605     pmts.clear();
01606 }
01607 
01608 void MPEGStreamData::ReturnCachedPMTTables(pmt_map_t &pmts) const
01609 {
01610     for (pmt_map_t::iterator it = pmts.begin(); it != pmts.end(); ++it)
01611         ReturnCachedPMTTables(*it);
01612     pmts.clear();
01613 }
01614 
01615 void MPEGStreamData::IncrementRefCnt(const PSIPTable *psip) const
01616 {
01617     QMutexLocker locker(&_cache_lock);
01618     _cached_ref_cnt[psip] = _cached_ref_cnt[psip] + 1;
01619 }
01620 
01621 bool MPEGStreamData::DeleteCachedTable(PSIPTable *psip) const
01622 {
01623     if (!psip)
01624         return false;
01625 
01626     uint tid = psip->TableIDExtension();
01627 
01628     QMutexLocker locker(&_cache_lock);
01629     if (_cached_ref_cnt[psip] > 0)
01630     {
01631         _cached_slated_for_deletion[psip] = 1;
01632         return false;
01633     }
01634     else if (TableID::PAT == psip->TableID() &&
01635              (_cached_pats[(tid << 8) | psip->Section()] == psip))
01636     {
01637         _cached_pats[(tid << 8) | psip->Section()] = NULL;
01638         delete psip;
01639     }
01640     else if (TableID::CAT == psip->TableID() &&
01641              (_cached_cats[(tid << 8) | psip->Section()] == psip))
01642     {
01643         _cached_cats[(tid << 8) | psip->Section()] = NULL;
01644         delete psip;
01645     }
01646     else if ((TableID::PMT == psip->TableID()) &&
01647              (_cached_pmts[(tid << 8) | psip->Section()] == psip))
01648     {
01649         _cached_pmts[(tid << 8) | psip->Section()] = NULL;
01650         delete psip;
01651     }
01652     else
01653     {
01654         _cached_slated_for_deletion[psip] = 2;
01655         return false;
01656     }
01657     psip_refcnt_map_t::iterator it;
01658     it = _cached_slated_for_deletion.find(psip);
01659     if (it != _cached_slated_for_deletion.end())
01660         _cached_slated_for_deletion.erase(it);
01661 
01662     return true;
01663 }
01664 
01665 void MPEGStreamData::CachePAT(const ProgramAssociationTable *_pat)
01666 {
01667     ProgramAssociationTable *pat = new ProgramAssociationTable(*_pat);
01668     uint key = (_pat->TransportStreamID() << 8) | _pat->Section();
01669 
01670     QMutexLocker locker(&_cache_lock);
01671 
01672     pat_cache_t::iterator it = _cached_pats.find(key);
01673     if (it != _cached_pats.end())
01674         DeleteCachedTable(*it);
01675 
01676     _cached_pats[key] = pat;
01677 }
01678 
01679 void MPEGStreamData::CacheCAT(const ConditionalAccessTable *_cat)
01680 {
01681     ConditionalAccessTable *cat = new ConditionalAccessTable(*_cat);
01682     uint key = (_cat->TableIDExtension() << 8) | _cat->Section();
01683 
01684     QMutexLocker locker(&_cache_lock);
01685 
01686     cat_cache_t::iterator it = _cached_cats.find(key);
01687     if (it != _cached_cats.end())
01688         DeleteCachedTable(*it);
01689 
01690     _cached_cats[key] = cat;
01691 }
01692 
01693 void MPEGStreamData::CachePMT(const ProgramMapTable *_pmt)
01694 {
01695     ProgramMapTable *pmt = new ProgramMapTable(*_pmt);
01696     uint key = (_pmt->ProgramNumber() << 8) | _pmt->Section();
01697 
01698     QMutexLocker locker(&_cache_lock);
01699 
01700     pmt_cache_t::iterator it = _cached_pmts.find(key);
01701     if (it != _cached_pmts.end())
01702         DeleteCachedTable(*it);
01703 
01704     _cached_pmts[key] = pmt;
01705 }
01706 
01707 void MPEGStreamData::AddMPEGListener(MPEGStreamListener *val)
01708 {
01709     QMutexLocker locker(&_listener_lock);
01710 
01711     mpeg_listener_vec_t::iterator it = _mpeg_listeners.begin();
01712     for (; it != _mpeg_listeners.end(); ++it)
01713         if (((void*)val) == ((void*)*it))
01714             return;
01715 
01716     _mpeg_listeners.push_back(val);
01717 }
01718 
01719 void MPEGStreamData::RemoveMPEGListener(MPEGStreamListener *val)
01720 {
01721     QMutexLocker locker(&_listener_lock);
01722 
01723     mpeg_listener_vec_t::iterator it = _mpeg_listeners.begin();
01724     for (; it != _mpeg_listeners.end(); ++it)
01725     {
01726         if (((void*)val) == ((void*)*it))
01727         {
01728             _mpeg_listeners.erase(it);
01729             return;
01730         }
01731     }
01732 }
01733 
01734 void MPEGStreamData::AddWritingListener(TSPacketListener *val)
01735 {
01736     QMutexLocker locker(&_listener_lock);
01737 
01738     ts_listener_vec_t::iterator it = _ts_writing_listeners.begin();
01739     for (; it != _ts_writing_listeners.end(); ++it)
01740         if (((void*)val) == ((void*)*it))
01741             return;
01742 
01743     _ts_writing_listeners.push_back(val);
01744 }
01745 
01746 void MPEGStreamData::RemoveWritingListener(TSPacketListener *val)
01747 {
01748     QMutexLocker locker(&_listener_lock);
01749 
01750     ts_listener_vec_t::iterator it = _ts_writing_listeners.begin();
01751     for (; it != _ts_writing_listeners.end(); ++it)
01752     {
01753         if (((void*)val) == ((void*)*it))
01754         {
01755             _ts_writing_listeners.erase(it);
01756             return;
01757         }
01758     }
01759 }
01760 
01761 void MPEGStreamData::AddAVListener(TSPacketListenerAV *val)
01762 {
01763     QMutexLocker locker(&_listener_lock);
01764 
01765     ts_av_listener_vec_t::iterator it = _ts_av_listeners.begin();
01766     for (; it != _ts_av_listeners.end(); ++it)
01767         if (((void*)val) == ((void*)*it))
01768             return;
01769 
01770     _ts_av_listeners.push_back(val);
01771 }
01772 
01773 void MPEGStreamData::RemoveAVListener(TSPacketListenerAV *val)
01774 {
01775     QMutexLocker locker(&_listener_lock);
01776 
01777     ts_av_listener_vec_t::iterator it = _ts_av_listeners.begin();
01778     for (; it != _ts_av_listeners.end(); ++it)
01779     {
01780         if (((void*)val) == ((void*)*it))
01781         {
01782             _ts_av_listeners.erase(it);
01783             return;
01784         }
01785     }
01786 }
01787 
01788 void MPEGStreamData::AddMPEGSPListener(MPEGSingleProgramStreamListener *val)
01789 {
01790     QMutexLocker locker(&_listener_lock);
01791 
01792     mpeg_sp_listener_vec_t::iterator it = _mpeg_sp_listeners.begin();
01793     for (; it != _mpeg_sp_listeners.end(); ++it)
01794         if (((void*)val) == ((void*)*it))
01795             return;
01796 
01797     _mpeg_sp_listeners.push_back(val);
01798 }
01799 
01800 void MPEGStreamData::RemoveMPEGSPListener(MPEGSingleProgramStreamListener *val)
01801 {
01802     QMutexLocker locker(&_listener_lock);
01803 
01804     mpeg_sp_listener_vec_t::iterator it = _mpeg_sp_listeners.begin();
01805     for (; it != _mpeg_sp_listeners.end(); ++it)
01806     {
01807         if (((void*)val) == ((void*)*it))
01808         {
01809             _mpeg_sp_listeners.erase(it);
01810             return;
01811         }
01812     }
01813 }
01814 
01815 void MPEGStreamData::AddEncryptionTestPID(uint pnum, uint pid, bool isvideo)
01816 {
01817     QMutexLocker locker(&_encryption_lock);
01818 
01819 #if 0
01820     LOG(VB_GENERAL, LOG_DEBUG, QString("AddEncryptionTestPID(%1, 0x%2)")
01821             .arg(pnum) .arg(pid, 0, 16));
01822 #endif
01823 
01824     AddListeningPID(pid);
01825 
01826     _encryption_pid_to_info[pid] = CryptInfo((isvideo) ? 10000 : 500, 8);
01827 
01828     _encryption_pid_to_pnums[pid].push_back(pnum);
01829     _encryption_pnum_to_pids[pnum].push_back(pid);
01830     _encryption_pnum_to_status[pnum] = kEncUnknown;
01831 }
01832 
01833 void MPEGStreamData::RemoveEncryptionTestPIDs(uint pnum)
01834 {
01835     QMutexLocker locker(&_encryption_lock);
01836 
01837 #if 0
01838     LOG(VB_RECORD, LOG_DEBUG,
01839         QString("Tearing down up decryption monitoring for program %1")
01840             .arg(pnum));
01841 #endif
01842 
01843     QMap<uint, uint_vec_t>::iterator list;
01844     uint_vec_t::iterator it;
01845 
01846     uint_vec_t pids = _encryption_pnum_to_pids[pnum];
01847     for (uint i = 0; i < pids.size(); i++)
01848     {
01849         uint pid = pids[i];
01850 
01851 #if 0
01852         LOG(VB_GENERAL, LOG_DEBUG, QString("Removing 0x%1 PID Enc monitoring")
01853                 .arg(pid,0,16));
01854 #endif
01855 
01856         RemoveListeningPID(pid);
01857 
01858         list = _encryption_pid_to_pnums.find(pid);
01859         if (list != _encryption_pid_to_pnums.end())
01860         {
01861             it = find((*list).begin(), (*list).end(), pnum);
01862 
01863             if (it != (*list).end())
01864                 (*list).erase(it);
01865 
01866             if ((*list).empty())
01867             {
01868                 _encryption_pid_to_pnums.remove(pid);
01869                 _encryption_pid_to_info.remove(pid);
01870             }
01871         }
01872     }
01873 
01874     _encryption_pnum_to_pids.remove(pnum);
01875 }
01876 
01877 bool MPEGStreamData::IsEncryptionTestPID(uint pid) const
01878 {
01879     QMutexLocker locker(&_encryption_lock);
01880 
01881     QMap<uint, CryptInfo>::const_iterator it =
01882         _encryption_pid_to_info.find(pid);
01883 
01884     return it != _encryption_pid_to_info.end();
01885 }
01886 
01887 void MPEGStreamData::TestDecryption(const ProgramMapTable *pmt)
01888 {
01889     QMutexLocker locker(&_encryption_lock);
01890 
01891 #if 0
01892     LOG(VB_RECORD, LOG_DEBUG,
01893         QString("Setting up decryption monitoring for program %1")
01894             .arg(pmt->ProgramNumber()));
01895 #endif
01896 
01897     bool encrypted = pmt->IsProgramEncrypted();
01898     for (uint i = 0; i < pmt->StreamCount(); i++)
01899     {
01900         if (!encrypted && !pmt->IsStreamEncrypted(i))
01901             continue;
01902 
01903         bool is_vid = pmt->IsVideo(i, _sistandard);
01904         bool is_aud = pmt->IsAudio(i, _sistandard);
01905         if (is_vid || is_aud)
01906         {
01907             AddEncryptionTestPID(
01908                 pmt->ProgramNumber(), pmt->StreamPID(i), is_vid);
01909         }
01910     }
01911 }
01912 
01913 void MPEGStreamData::ResetDecryptionMonitoringState(void)
01914 {
01915     QMutexLocker locker(&_encryption_lock);
01916 
01917     _encryption_pid_to_info.clear();
01918     _encryption_pid_to_pnums.clear();
01919     _encryption_pnum_to_pids.clear();
01920 }
01921 
01922 bool MPEGStreamData::IsProgramDecrypted(uint pnum) const
01923 {
01924     QMutexLocker locker(&_encryption_lock);
01925     return _encryption_pnum_to_status[pnum] == kEncDecrypted;
01926 }
01927 
01928 bool MPEGStreamData::IsProgramEncrypted(uint pnum) const
01929 {
01930     QMutexLocker locker(&_encryption_lock);
01931     return _encryption_pnum_to_status[pnum] == kEncEncrypted;
01932 }
01933 
01934 static QString toString(CryptStatus status)
01935 {
01936     if (kEncDecrypted == status)
01937         return "Decrypted";
01938     else if (kEncEncrypted == status)
01939         return "Encrypted";
01940     else
01941         return "Unknown";
01942 }
01943 
01947 void MPEGStreamData::ProcessEncryptedPacket(const TSPacket& tspacket)
01948 {
01949     QMutexLocker locker(&_encryption_lock);
01950 
01951     const uint pid = tspacket.PID();
01952     CryptInfo &info = _encryption_pid_to_info[pid];
01953 
01954     CryptStatus status = kEncUnknown;
01955 
01956     if (tspacket.Scrambled())
01957     {
01958         info.decrypted_packets = 0;
01959 
01960         // If a fair amount of encrypted packets is passed assume that
01961         // the stream is not decryptable
01962         if (++info.encrypted_packets >= info.encrypted_min)
01963             status = kEncEncrypted;
01964     }
01965     else
01966     {
01967         info.encrypted_packets = 0;
01968         if (++info.decrypted_packets > info.decrypted_min)
01969             status = kEncDecrypted;
01970     }
01971 
01972     if (status == info.status)
01973         return; // pid encryption status unchanged
01974 
01975     info.status = status;
01976 
01977     LOG(status != kEncDecrypted ? VB_GENERAL : VB_RECORD, LOG_INFO,
01978         QString("PID 0x%1 status: %2") .arg(pid,0,16).arg(toString(status)));
01979 
01980     uint_vec_t pnum_del_list;
01981     const uint_vec_t &pnums = _encryption_pid_to_pnums[pid];
01982     for (uint i = 0; i < pnums.size(); i++)
01983     {
01984         CryptStatus status = _encryption_pnum_to_status[pnums[i]];
01985 
01986         const uint_vec_t &pids = _encryption_pnum_to_pids[pnums[i]];
01987         if (!pids.empty())
01988         {
01989             uint enc_cnt[3] = { 0, 0, 0 };
01990             for (uint j = 0; j < pids.size(); j++)
01991             {
01992                 CryptStatus stat = _encryption_pid_to_info[pids[j]].status;
01993                 enc_cnt[stat]++;
01994 
01995 #if 0
01996                 LOG(VB_GENERAL, LOG_DEBUG,
01997                     QString("\tpnum %1 PID 0x%2 status: %3")
01998                         .arg(pnums[i]).arg(pids[j],0,16) .arg(toString(stat)));
01999 #endif
02000             }
02001             status = kEncUnknown;
02002 
02003             if (enc_cnt[kEncEncrypted])
02004                 status = kEncEncrypted;
02005             else if (enc_cnt[kEncDecrypted] >= min((size_t) 2, pids.size()))
02006                 status = kEncDecrypted;
02007         }
02008 
02009         if (status == _encryption_pnum_to_status[pnums[i]])
02010             continue; // program encryption status unchanged
02011 
02012         LOG(VB_RECORD, LOG_INFO, QString("Program %1 status: %2")
02013                 .arg(pnums[i]).arg(toString(status)));
02014 
02015         _encryption_pnum_to_status[pnums[i]] = status;
02016 
02017         bool encrypted = kEncUnknown == status || kEncEncrypted == status;
02018         _listener_lock.lock();
02019         for (uint j = 0; j < _mpeg_listeners.size(); j++)
02020             _mpeg_listeners[j]->HandleEncryptionStatus(pnums[i], encrypted);
02021         _listener_lock.unlock();
02022 
02023         if (kEncDecrypted == status)
02024             pnum_del_list.push_back(pnums[i]);
02025     }
02026 
02027     for (uint i = 0; i < pnum_del_list.size(); i++)
02028         RemoveEncryptionTestPIDs(pnums[i]);
02029 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends