MythTV  0.26-pre
dvbdescriptors.cpp
Go to the documentation of this file.
00001 #include <unistd.h>
00002 
00003 #include <QTextCodec>
00004 
00005 #include "dvbdescriptors.h"
00006 #include "iso6937tables.h"
00007 #include "freesat_huffman.h"
00008 #include "mythlogging.h"
00009 
00010 
00011 static QString decode_iso6937(const unsigned char *buf, uint length)
00012 {
00013     // ISO/IEC 6937 to unicode (UCS2) convertor...
00014     // This is a composed encoding - accent first then plain character
00015     QString result = "";
00016     ushort ch = 0x20;
00017     for (uint i = 0; (i < length) && buf[i]; i++)
00018     {
00019         if (ch == 0xFFFF)
00020         {
00021             // Process second byte of two byte character
00022             ch = iso6937table_secondary[buf[i-1]][buf[i]];
00023             if (ch == 0xFFFF)
00024             {
00025                 // If no valid code found in secondary table,
00026                 // reprocess this second byte as first byte.
00027                 ch = iso6937table_base[buf[i]];
00028                 if (ch == 0xFFFF)
00029                     continue; // process second byte
00030             }
00031         }
00032         else
00033         {
00034             // Process first character of two possible characters.
00035             // double byte characters have a sentinel (0xffff) in this table.
00036             ch = iso6937table_base[buf[i]];
00037             if (ch == 0xFFFF)
00038                 continue; // process second byte
00039 
00040         }
00041         result += QChar(ch);
00042     }
00043     return result;
00044 }
00045 
00046 static QString decode_text(const unsigned char *buf, uint length);
00047 
00048 // Decode a text string according to ETSI EN 300 468 Annex A
00049 QString dvb_decode_text(const unsigned char *src, uint raw_length,
00050                         const unsigned char *encoding_override,
00051                         uint encoding_override_length)
00052 {
00053     if (!raw_length)
00054         return "";
00055 
00056     if (src[0] == 0x1f)
00057         return freesat_huffman_to_string(src, raw_length);
00058 
00059     if (((0x10 < src[0]) && (src[0] < 0x15)) ||
00060         ((0x15 < src[0]) && (src[0] < 0x20)))
00061     {
00062         // TODO: Handle multi-byte encodings
00063         LOG(VB_SIPARSER, LOG_ERR, 
00064             "dvb_decode_text: Multi-byte coded text is not yet supported.");
00065         return "";
00066     }
00067 
00068     // if a override encoding is specified and the default ISO 6937 encoding
00069     // would be used copy the override encoding in front of the text
00070     unsigned char *dst =
00071         new unsigned char[raw_length + encoding_override_length];
00072 
00073     uint length = 0;
00074     if (encoding_override && src[0] >= 0x20) {
00075         memcpy(dst, encoding_override, encoding_override_length);
00076         length = encoding_override_length;
00077     }
00078 
00079     // Strip formatting characters
00080     for (uint i = 0; i < raw_length; i++)
00081     {
00082         if ((src[i] < 0x80) || (src[i] > 0x9F))
00083             dst[length++] = src[i];
00084         // replace CR/LF with a space
00085         else if (src[i] == 0x8A)
00086             dst[length++] = 0x20;
00087     }
00088 
00089     // Exit on empty string, sans formatting.
00090 
00091     QString sStr = (!length) ? "" : decode_text(dst, length);
00092 
00093     delete [] dst;
00094 
00095     return sStr;
00096 }
00097 
00098 static QString decode_text(const unsigned char *buf, uint length)
00099 {
00100     // Only some of the QTextCodec calls are reentrant.
00101     // If you use this please verify that you are using a reentrant call.
00102     static const QTextCodec *iso8859_codecs[16] =
00103     {
00104         QTextCodec::codecForName("Latin1"),
00105         QTextCodec::codecForName("ISO8859-1"),  // Western
00106         QTextCodec::codecForName("ISO8859-2"),  // Central European
00107         QTextCodec::codecForName("ISO8859-3"),  // Central European
00108         QTextCodec::codecForName("ISO8859-4"),  // Baltic
00109         QTextCodec::codecForName("ISO8859-5"),  // Cyrillic
00110         QTextCodec::codecForName("ISO8859-6"),  // Arabic
00111         QTextCodec::codecForName("ISO8859-7"),  // Greek
00112         QTextCodec::codecForName("ISO8859-8"),  // Hebrew, visually ordered
00113         QTextCodec::codecForName("ISO8859-9"),  // Turkish
00114         QTextCodec::codecForName("ISO8859-10"),
00115         QTextCodec::codecForName("ISO8859-11"),
00116         QTextCodec::codecForName("ISO8859-12"),
00117         QTextCodec::codecForName("ISO8859-13"),
00118         QTextCodec::codecForName("ISO8859-14"),
00119         QTextCodec::codecForName("ISO8859-15"), // Western
00120     };
00121 
00122     // Decode using the correct text codec
00123     if (buf[0] >= 0x20)
00124     {
00125         return decode_iso6937(buf, length);
00126     }
00127     else if ((buf[0] >= 0x01) && (buf[0] <= 0x0B))
00128     {
00129         return iso8859_codecs[4 + buf[0]]->toUnicode((char*)(buf + 1), length - 1);
00130     }
00131     else if (buf[0] == 0x10)
00132     {
00133         // If the first byte of the text field has a value "0x10"
00134         // then the following two bytes carry a 16-bit value (uimsbf) N
00135         // to indicate that the remaining data of the text field is
00136         // coded using the character code table specified by
00137         // ISO Standard 8859, parts 1 to 9
00138 
00139         uint code = buf[1] << 8 | buf[2];
00140         if (code <= 15)
00141             return iso8859_codecs[code]->toUnicode((char*)(buf + 3), length - 3);
00142         else
00143             return QString::fromLocal8Bit((char*)(buf + 3), length - 3);
00144     }
00145     else if (buf[0] == 0x15) // Already Unicode
00146     {
00147         return QString::fromUtf8((char*)(buf + 1), length - 1);
00148     }
00149     else
00150     {
00151         // Unknown/invalid encoding - assume local8Bit
00152         return QString::fromLocal8Bit((char*)(buf + 1), length - 1);
00153     }
00154 }
00155 
00156 
00157 QString dvb_decode_short_name(const unsigned char *src, uint raw_length)
00158 {
00159     if (raw_length > 50)
00160     {
00161         LOG(VB_SIPARSER, LOG_WARNING,
00162             QString("dvb_decode_short_name: name is %1 chars "
00163                     "long. Unlikely to be a short name.")
00164                 .arg(raw_length));
00165         return "";
00166     }
00167 
00168     if (((0x10 < src[0]) && (src[0] < 0x15)) ||
00169         ((0x15 < src[0]) && (src[0] < 0x20)))
00170     {
00171         // TODO: Handle multi-byte encodings
00172         LOG(VB_SIPARSER, LOG_ERR, "dvb_decode_short_name: "
00173                          "Multi-byte coded text is not yet supported.");
00174         return "";
00175     }
00176 
00177     unsigned char *dst = new unsigned char[raw_length];
00178     uint length = 0;
00179 
00180     // check for emphasis control codes
00181     for (uint i = 0; i < raw_length; i++)
00182         if (src[i] == 0x86)
00183             while ((++i < raw_length) && (src[i] != 0x87))
00184             {
00185                 if ((src[i] < 0x80) || (src[i] > 0x9F))
00186                     dst[length++] = src[i];
00187                 // replace CR/LF with a space
00188                 else if (src[i] == 0x8A)
00189                     dst[length++] = 0x20;
00190             }
00191 
00192     QString sStr = (!length) ? dvb_decode_text(src, raw_length) 
00193                              : decode_text(dst, length);
00194                                      
00195     delete [] dst;
00196 
00197     return sStr;
00198 }
00199 
00200 QMutex             ContentDescriptor::categoryLock;
00201 QMap<uint,QString> ContentDescriptor::categoryDesc;
00202 volatile bool      ContentDescriptor::categoryDescExists = false;
00203 
00204 QString myth_category_type_to_string(uint category_type)
00205 {
00206     static const char *cattype[] =
00207         { "", "movie", "series", "sports", "tvshow", };
00208 
00209     if ((category_type > kCategoryNone) && (category_type < kCategoryLast))
00210         return QString(cattype[category_type]);
00211 
00212     return "";
00213 }
00214 
00215 MythCategoryType string_to_myth_category_type(const QString &category_type)
00216 {
00217     static const char *cattype[] =
00218         { "", "movie", "series", "sports", "tvshow", };
00219 
00220     for (uint i = 1; i < 5; i++)
00221         if (category_type == cattype[i])
00222             return (MythCategoryType) i;
00223     return kCategoryNone;
00224 }
00225 
00226 MythCategoryType ContentDescriptor::GetMythCategory(uint i) const
00227 {
00228     if (0x1 == Nibble1(i))
00229         return kCategoryMovie;
00230     if (0x4 == Nibble1(i))
00231         return kCategorySports;
00232     return kCategoryTVShow;
00233 }
00234 
00235 const char *linkage_types[] =
00236 {
00237     "Reserved(0x00)",
00238     "Information Service",
00239     "EPG Service",
00240     "CA Replacement Service",
00241     "TS Containing Complete Network/Bouquet SI",
00242     "Service Replacement Service",
00243     "Data Broadcast Service",
00244     "RCS Map",
00245     "Mobile Hand-Over",
00246     "System Software Update Service",
00247     "TS Containing SSU, BAT or NIT",
00248     "IP/MAC Notification Service",
00249     "TS Containing INT, BAT or NIT",
00250     "Event Linkage",
00251 };
00252 
00253 
00254 QString LinkageDescriptor::LinkageTypeString(void) const
00255 {
00256     if (LinkageType() < (sizeof(linkage_types) / sizeof(const char*)))
00257         return QString(linkage_types[LinkageType()]);
00258     if ((LinkageType() <= 0x7f) || (LinkageType() == 0x7f))
00259         return QString("Reserved(0x%1)").arg(LinkageType(),2,16,QChar('0'));
00260     return QString("User Defined(0x%1)").arg(LinkageType(),2,16,QChar('0'));
00261 }
00262 
00263 QString LinkageDescriptor::MobileHandOverTypeString(void) const
00264 {
00265     if (kHandOverIdentical == MobileHandOverType())
00266         return "Hand-Over to an Identical Service";
00267     if (kHandOverLocalVariation == MobileHandOverType())
00268         return "Hand-Over to a Local Variation";
00269     if (kHandOverAssociatedService == MobileHandOverType())
00270         return "Hand-over to an Associated Service";
00271     return "Reserved";
00272 }
00273 
00274 QString ContentDescriptor::GetDescription(uint i) const
00275 {
00276     if (!categoryDescExists)
00277         Init();
00278 
00279     QMutexLocker locker(&categoryLock);
00280 
00281     // Try to get detailed description
00282     QMap<uint,QString>::const_iterator it = categoryDesc.find(Nibble(i));
00283     if (it != categoryDesc.end())
00284         return *it;
00285 
00286     // Fall back to category description
00287     it = categoryDesc.find(Nibble1(i)<<4);
00288     if (it != categoryDesc.end())
00289         return *it;
00290 
00291     // Found nothing? Just return empty string.
00292     return "";
00293 }
00294 
00295 QString ContentDescriptor::toString() const
00296 {
00297     QString tmp("ContentDescriptor: ");
00298     for (uint i = 0; i < Count(); i++)
00299         tmp += GetMythCategory(i) + " : " + GetDescription(i) + ", ";
00300     return tmp;
00301 }
00302 
00303 void ContentDescriptor::Init(void)
00304 {
00305     QMutexLocker locker(&categoryLock);
00306 
00307     if (categoryDescExists)
00308         return;
00309 
00310     categoryDesc[0x10] = QObject::tr("Movie");
00311     categoryDesc[0x11] = QObject::tr("Movie") + " - " +
00312         QObject::tr("Detective/Thriller");
00313     categoryDesc[0x12] = QObject::tr("Movie")+ " - " +
00314         QObject::tr("Adventure/Western/War");
00315     categoryDesc[0x13] = QObject::tr("Movie")+ " - " +
00316         QObject::tr("Science Fiction/Fantasy/Horror");
00317     categoryDesc[0x14] = QObject::tr("Movie")+ " - " +
00318         QObject::tr("Comedy");
00319     categoryDesc[0x15] = QObject::tr("Movie")+ " - " +
00320         QObject::tr("Soap/melodrama/folkloric");
00321     categoryDesc[0x16] = QObject::tr("Movie")+ " - " +
00322         QObject::tr("Romance");
00323     categoryDesc[0x17] = QObject::tr("Movie")+ " - " +
00324         QObject::tr("Serious/Classical/Religious/Historical Movie/Drama");
00325     categoryDesc[0x18] = QObject::tr("Movie")+ " - " +
00326         QObject::tr("Adult", "Adult Movie");
00327 
00328     categoryDesc[0x20] = QObject::tr("News");
00329     categoryDesc[0x21] = QObject::tr("News/weather report");
00330     categoryDesc[0x22] = QObject::tr("News magazine");
00331     categoryDesc[0x23] = QObject::tr("Documentary");
00332     categoryDesc[0x24] = QObject::tr("Intelligent Programs");
00333 
00334     categoryDesc[0x30] = QObject::tr("Entertainment");
00335     categoryDesc[0x31] = QObject::tr("Game Show");
00336     categoryDesc[0x32] = QObject::tr("Variety Show");
00337     categoryDesc[0x33] = QObject::tr("Talk Show");
00338 
00339     categoryDesc[0x40] = QObject::tr("Sports");
00340     categoryDesc[0x41] =
00341         QObject::tr("Special Events (World Cup, World Series, etc)");
00342     categoryDesc[0x42] = QObject::tr("Sports Magazines");
00343     categoryDesc[0x43] = QObject::tr("Football (Soccer)");
00344     categoryDesc[0x44] = QObject::tr("Tennis/Squash");
00345     categoryDesc[0x45] =
00346         QObject::tr("Misc. Team Sports"); // not football/soccer
00347     categoryDesc[0x46] = QObject::tr("Athletics");
00348     categoryDesc[0x47] = QObject::tr("Motor Sport");
00349     categoryDesc[0x48] = QObject::tr("Water Sport");
00350     categoryDesc[0x49] = QObject::tr("Winter Sports");
00351     categoryDesc[0x4A] = QObject::tr("Equestrian");
00352     categoryDesc[0x4B] = QObject::tr("Martial Sports");
00353 
00354     categoryDesc[0x50] = QObject::tr("Kids");
00355     categoryDesc[0x51] = QObject::tr("Pre-School Children's Programs");
00356     categoryDesc[0x52] = QObject::tr("Entertainment Programs for 6 to 14");
00357     categoryDesc[0x53] = QObject::tr("Entertainment Programs for 10 to 16");
00358     categoryDesc[0x54] = QObject::tr("Informational/Educational");
00359     categoryDesc[0x55] = QObject::tr("Cartoons/Puppets");
00360 
00361     categoryDesc[0x60] = QObject::tr("Music/Ballet/Dance");
00362     categoryDesc[0x61] = QObject::tr("Rock/Pop");
00363     categoryDesc[0x62] = QObject::tr("Classical Music");
00364     categoryDesc[0x63] = QObject::tr("Folk Music");
00365     categoryDesc[0x64] = QObject::tr("Jazz");
00366     categoryDesc[0x65] = QObject::tr("Musical/Opera");
00367     categoryDesc[0x66] = QObject::tr("Ballet");
00368 
00369     categoryDesc[0x70] = QObject::tr("Arts/Culture");
00370     categoryDesc[0x71] = QObject::tr("Performing Arts");
00371     categoryDesc[0x72] = QObject::tr("Fine Arts");
00372     categoryDesc[0x73] = QObject::tr("Religion");
00373     categoryDesc[0x74] = QObject::tr("Popular Culture/Traditional Arts");
00374     categoryDesc[0x75] = QObject::tr("Literature");
00375     categoryDesc[0x76] = QObject::tr("Film/Cinema");
00376     categoryDesc[0x77] = QObject::tr("Experimental Film/Video");
00377     categoryDesc[0x78] = QObject::tr("Broadcasting/Press");
00378     categoryDesc[0x79] = QObject::tr("New Media");
00379     categoryDesc[0x7A] = QObject::tr("Arts/Culture Magazines");
00380     categoryDesc[0x7B] = QObject::tr("Fashion");
00381 
00382     categoryDesc[0x80] = QObject::tr("Social/Policical/Economics");
00383     categoryDesc[0x81] = QObject::tr("Magazines/Reports/Documentary");
00384     categoryDesc[0x82] = QObject::tr("Economics/Social Advisory");
00385     categoryDesc[0x83] = QObject::tr("Remarkable People");
00386 
00387     categoryDesc[0x90] = QObject::tr("Education/Science/Factual");
00388     categoryDesc[0x91] = QObject::tr("Nature/animals/Environment");
00389     categoryDesc[0x92] = QObject::tr("Technology/Natural Sciences");
00390     categoryDesc[0x93] = QObject::tr("Medicine/Physiology/Psychology");
00391     categoryDesc[0x94] = QObject::tr("Foreign Countries/Expeditions");
00392     categoryDesc[0x95] = QObject::tr("Social/Spiritual Sciences");
00393     categoryDesc[0x96] = QObject::tr("Further Education");
00394     categoryDesc[0x97] = QObject::tr("Languages");
00395 
00396     categoryDesc[0xA0] = QObject::tr("Leisure/Hobbies");
00397     categoryDesc[0xA1] = QObject::tr("Tourism/Travel");
00398     categoryDesc[0xA2] = QObject::tr("Handicraft");
00399     categoryDesc[0xA3] = QObject::tr("Motoring");
00400     categoryDesc[0xA4] = QObject::tr("Fitness & Health");
00401     categoryDesc[0xA5] = QObject::tr("Cooking");
00402     categoryDesc[0xA6] = QObject::tr("Advertizement/Shopping");
00403     categoryDesc[0xA7] = QObject::tr("Gardening");
00404     // Special
00405     categoryDesc[0xB0] = QObject::tr("Original Language");
00406     categoryDesc[0xB1] = QObject::tr("Black & White");
00407     categoryDesc[0xB2] = QObject::tr("\"Unpublished\" Programs");
00408     categoryDesc[0xB3] = QObject::tr("Live Broadcast");
00409     // UK Freeview custom id
00410     categoryDesc[0xF0] = QObject::tr("Drama");
00411 
00412     categoryDescExists = true;
00413 }
00414 
00415 QString FrequencyListDescriptor::toString() const
00416 {
00417     QString str = "FrequencyListDescriptor: frequencies: ";
00418 
00419     for (uint i = 0; i < FrequencyCount(); i++)
00420         str.append(QString(" %1").arg(FrequencyHz(i)));
00421 
00422     return str;
00423 }
00424 
00425 QString ServiceDescriptorMapping::toString() const
00426 {
00427     QString str = "";
00428 
00429     if (IsDTV())
00430         str.append(" (TV)");
00431     else if (IsDigitalAudio())
00432         str.append(" (Radio)");
00433     else if (IsHDTV())
00434         str.append(" (HDTV)");
00435     else if (IsTeletext())
00436         str.append(" (Teletext)");
00437     else
00438         str.append(QString(" (Unknown %1)").arg(ServiceType(),2,16));
00439 
00440     return str;
00441 }
00442 
00443 QString CableDeliverySystemDescriptor::toString() const
00444 {
00445     QString str = QString("CableDeliverySystemDescriptor: ");
00446 
00447     str.append(QString("Frequency: %1\n").arg(FrequencyHz()));
00448     str.append(QString("      Mod=%1, SymbR=%2, FECInner=%3, FECOuter=%4")
00449         .arg(ModulationString())
00450         .arg(SymbolRateHz())
00451         .arg(FECInnerString())
00452         .arg(FECOuterString()));
00453 
00454     return str;
00455 }
00456 
00457 QString SatelliteDeliverySystemDescriptor::toString() const
00458 {
00459     QString str = QString("SatelliteDeliverySystemDescriptor: ");
00460 
00461     str.append(QString("Frequency: %1\n").arg(FrequencyHz()));
00462     str.append(QString("      Mod=%1, SymbR=%2, FECInner=%3, Orbit=%4, Pol=%5")
00463         .arg(ModulationString())
00464         .arg(SymbolRateHz())
00465         .arg(FECInnerString())
00466         .arg(OrbitalPositionString())
00467         .arg(PolarizationString()));
00468 
00469     return str;
00470 }
00471 
00472 QString TerrestrialDeliverySystemDescriptor::toString() const
00473 {
00474     QString str = QString("TerrestrialDeliverySystemDescriptor: ");
00475 
00476     str.append(QString("Frequency: %1\n").arg(FrequencyHz()));
00477     str.append(QString("      BW=%1k, C=%2, HP=%3, LP=%4, GI=%5, TransMode=%6k")
00478         .arg(BandwidthString())
00479         .arg(ConstellationString())
00480         .arg(CodeRateHPString())
00481         .arg(CodeRateLPString())
00482         .arg(GuardIntervalString())
00483         .arg(TransmissionModeString()));
00484 
00485     return str;
00486 }
00487 
00488 QString UKChannelListDescriptor::toString() const
00489 {
00490     QString ret = "UKChannelListDescriptor sid->chan_num: ";
00491     for (uint i = 0; i < ChannelCount(); i++)
00492     {
00493         ret += QString("%1->%2").arg(ServiceID(i)).arg(ChannelNumber(i));
00494         ret += (i+1<ChannelCount()) ? ", " : "";
00495     }
00496     return ret;
00497 }
00498 
00499 QString CAIdentifierDescriptor::toString(void) const
00500 {
00501     QString ret = QString("CAIdentifierDescriptor ");
00502     for (uint i = 0; i < CASystemCount(); ++i)
00503     {
00504         ret += QString("ca_system_id(0x%1) ")
00505             .arg(CASystemId(i), 0, 16);
00506     }
00507     return ret;
00508 }
00509 
00510 QString DataBroadcastDescriptor::toString(void) const
00511 {
00512     QString ret = QString("DataBroadcastDescriptor: "
00513                                 "data_broadcast_id(%1) "
00514                                 "component_tag(%1) ")
00515             .arg(DataBroadcastId(), 0, 10)
00516             .arg(DataComponentTag(), 0, 10);
00517 
00518     ret += QString("selector(0x ");
00519     for (uint i = 0; i < SelectorLength(); i++)
00520         ret += QString("%1 ").arg(Selector()[i], 0, 16);
00521     ret += ") ";
00522     
00523     ret += QString("ISO_639_language_code(%1) ")
00524         .arg(LanguageString());
00525 
00526     ret += QString("text(%1) ") + QString(Text());
00527 
00528     return ret;
00529 }
00530 
00531 QString LocalTimeOffsetDescriptor::toString(void) const
00532 {
00533     QString ret = QString("LocalTimeOffsetDescriptor ");
00534     uint count = Count();
00535     for (uint i = 0; i < count; ++i)
00536     {
00537         ret += QString("country_code(%1) country_region_id(0x%2) "
00538                        "local_time_offset_with_polarity(%3) "
00539                        "time_of_change(TODO)")
00540             .arg(CountryCodeString(i))
00541             .arg(CountryRegionId(i), 0, 16)
00542             .arg(LocalTimeOffsetWithPolarity(i));
00543         // TODO add time of change
00544     }
00545     return ret;
00546 }
00547 
00548 QString NVODReferenceDescriptor::toString(void) const
00549 {
00550     QString ret = QString("NVODReferenceDescriptor ");
00551     for (uint i = 0; i < Count(); ++i)
00552     {
00553         ret += QString("transport_stream_id(0x%1) original_network_id(0x%2) "
00554                        "service_id(0x%3) ")
00555             .arg(TransportStreamId(i), 0, 16)
00556             .arg(OriginalNetworkId(i), 0, 16)
00557             .arg(ServiceId(i), 0, 16);
00558     }
00559     return ret;
00560 }
00561 
00562 QString PartialTransportStreamDescriptor::toString(void) const
00563 {
00564     return QString("PartialTransportStreamDescriptor peak_rate(%1) "
00565                    "min_overall_smooth_rate(%2) max_overall_smooth_buf(3)")
00566         .arg(PeakRate()).arg(SmoothRate()).arg(SmoothBuf());
00567 }
00568 
00569 QString AC3Descriptor::toString(void) const
00570 {
00571     QString ret = QString("AC3DescriptorDescriptor ");
00572     if (HasComponentType())
00573         ret += QString("component_type(%1) ")
00574         .arg(ComponentType(), 0, 10);
00575     if (HasBSID())
00576         ret += QString("bsid(0x%1) ").arg(BSID(),0,16);
00577     if (HasMainID())
00578         ret += QString("mainid(0x%1) ").arg(MainID(),0,16);
00579     if (HasASVC())
00580         ret += QString("asvc(%1) ").arg(ASVC());
00581     return ret;
00582 }
00583 
00584 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends