#include #include #include #include #include #include #include #include #include #include #include #include #include "event.h" struct ShortEvent { char lang_code[3]; uint8_t event_name_length; u_char data[]; } __attribute__((packed)); struct Component { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ u_char :4; u_char stream_content :4; #else u_char stream_content :4; u_char :4; #endif u_char component_type /*:8*/; u_char component_tag /*:8*/; char lang_code[3]; } __attribute__((packed)); struct Content { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ uint8_t content_nibble_level_1 :4; uint8_t content_nibble_level_2 :4; #else uint8_t content_nibble_level_2 :4; uint8_t content_nibble_level_1 :4; #endif uint8_t user_byte; }; struct ParentalRating { char country_code[3]; uint8_t rating; } __attribute__((packed)); struct EventDescriptor { uint16_t EventId; uint16_t mjd; uint8_t start_time_h; uint8_t start_time_m; uint8_t start_time_s; uint8_t duration_h; uint8_t duration_m; uint8_t duration_s; #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ u_char RunningStatus : 3; u_char FreeCaMode : 1; u_char descriptors_length_hi : 4; #else u_char descriptors_length_hi : 4; u_char FreeCaMode : 1; u_char RunningStatus : 3; #endif u_char descriptors_length_lo; u_char data[]; } __attribute__((packed)); static Glib::RefPtr episodeRegex = Glib::Regex::create("(?:[ (]+|^)(?:\\w+ )?([0-9]+)(?: of |/)([0-9]+)(?:[.)]+|$)"); static Glib::RefPtr seasonEpisodeRegex = Glib::Regex::create("[ ([]*[Ss]\\s*([0-9]+)[ ,]*[Ee][Pp]?\\s*([0-9]+)[).\\]]*"); static Glib::RefPtr yearRegex = Glib::Regex::create("[[(]([0-9]{4})[ ).\\]]+"); static Glib::RefPtr flagsRegex = Glib::Regex::create("[ []+([A-Z,]+)\\]"); void SiEpgParser::parseDescriptor_ShortEvent(DVBSI::EventPtr current, const u_char * data) { auto * evtdesc = reinterpret_cast(data); StrPtr title, subtitle, desc; current->TitleLang = std::string(evtdesc->lang_code, 3); size_t evtlen = evtdesc->event_name_length; if (evtlen) { title = convert((const char *)evtdesc->data, evtlen); } size_t dsclen = evtdesc->data[evtlen]; if (dsclen) { subtitle = convert((const char *)evtdesc->data + evtlen + 1, dsclen); } if (subtitle) { Glib::MatchInfo matches; if (yearRegex->match(*subtitle, matches)) { current->Year = boost::lexical_cast(matches.fetch(1)); *subtitle = yearRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); } if (flagsRegex->match(*subtitle, matches)) { current->Flags = matches.fetch(1); *subtitle = flagsRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); } if (episodeRegex->match(*subtitle, matches)) { current->Episode = boost::lexical_cast(matches.fetch(1)); current->Episodes = boost::lexical_cast(matches.fetch(2)); if (*current->Episode <= *current->Episodes) { // fudge to catch "24/7" *subtitle = episodeRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); } } if (seasonEpisodeRegex->match(*subtitle, matches)) { current->Season = boost::lexical_cast(matches.fetch(1)); current->Episode = boost::lexical_cast(matches.fetch(2)); *subtitle = seasonEpisodeRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); } } if (title && subtitle) { if (boost::algorithm::ends_with(*title, "...") && boost::algorithm::starts_with(*subtitle, "...")) { title->resize(title->length() - 3); *title += " "; size_t dot = subtitle->find('.', 4); if (dot == Glib::ustring::npos) { title->append(*subtitle, 3, subtitle->length() - 3); } else { title->append(*subtitle, 3, dot - 3); subtitle->erase(0, dot + 2); } } size_t colon = subtitle->find(':'); if (colon != Glib::ustring::npos) { desc = StrPtr(new Glib::ustring(*subtitle, colon + 1, subtitle->length() - colon)); subtitle->resize(colon); } else { colon = title->find(':'); desc = subtitle; if (colon != Glib::ustring::npos) { subtitle = StrPtr(new Glib::ustring(*title, colon + 1, title->length() - colon)); title->resize(colon); } else { subtitle.reset(); } } } if (title) { boost::algorithm::trim_if(*title, isspace); current->Title = *title; } if (subtitle) { boost::algorithm::trim_if(*subtitle, isspace); if (!subtitle->empty()) { current->Subtitle = *subtitle; } } if (desc) { boost::algorithm::trim_if(*desc, isspace); if (!desc->empty()) { current->Description = *desc; } } } void SiEpgParser::parseDescriptor_Component(DVBSI::EventPtr current, const u_char * data) { auto * dc = reinterpret_cast(data); switch (dc->stream_content) { case 0x01: // Video Info current->VideoHD = ((dc->component_type - 1) & 0x08) ? 1 : 0; current->VideoFrameRate = ((dc->component_type - 1) & 0x04) ? 30 : 25; current->VideoAspect = ((dc->component_type - 1) & 0x03); break; case 0x02: // Audio Info current->AudioChannels = dc->component_type; current->AudioLanguage = Glib::ustring(dc->lang_code, 3); break; case 0x03: // Teletext Info current->SubtitleLanguage = Glib::ustring(dc->lang_code, 3); break; } } void SiEpgParser::parseDescriptor_Content(DVBSI::EventPtr current, const u_char * data) { auto nc = reinterpret_cast(data); current->Category = nc->content_nibble_level_1; current->SubCategory = nc->content_nibble_level_2; current->UserCategory = nc->user_byte; } void SiEpgParser::parseDescriptor_ParentalRating(DVBSI::EventPtr current, const u_char * data) { auto * pr = reinterpret_cast(data); switch (pr->rating) { case 0x01 ... 0x0F: current->DvbRating = pr->rating + 3; break; case 0x00: /*undefined*/ case 0x10 ... 0xFF: /*broadcaster defined*/ current->DvbRating = NULL; break; } } bool SiEpgParser::CheckTableId(u_char tableId) const { return ((tableId >= 0x50 && tableId <= 0x5f) || (tableId >= 0x60 && tableId <= 0x6f)); } bool SiEpgParser::HandleTable(DVBSI::EitInformationPtr) { return false; } Common::DateTime & operator<<(Common::DateTime & dt, const boost::posix_time::ptime & pt) { dt.Year = pt.date().year(); dt.Month = pt.date().month(); dt.Day = pt.date().day(); dt.Hour = pt.time_of_day().hours(); dt.Minute = pt.time_of_day().minutes(); return dt; } void SiEpgParser::ParseSiTable(const EventInformation * eit, DVBSI::EitInformationPtr ei) { ei->ServiceId = ntohs(eit->header.content_id); ei->TransportStreamId = ntohs(eit->TransportStreamId); ei->OriginalNetworkId = ntohs(eit->OriginalNetworkId); LoopOver(eit->data, HILO(eit->header.section_length) - 15, [this,ei](const EventDescriptor * ed) { DVBSI::EventPtr e = new DVBSI::Event(); e->EventId = ntohs(ed->EventId); e->ServiceId = ei->ServiceId; // boost::gregorian::date startDate(boost::gregorian::gregorian_calendar::from_modjulian_day_number(ntohs(ed->mjd))); boost::posix_time::ptime time(startDate); time += boost::posix_time::time_duration(BcdCharToInt(ed->start_time_h), BcdCharToInt(ed->start_time_m), BcdCharToInt(ed->start_time_s)); e->StartTime << time; time += boost::posix_time::time_duration(BcdCharToInt(ed->duration_h), BcdCharToInt(ed->duration_m), BcdCharToInt(ed->duration_s)); e->StopTime << time; // ParseDescriptors(ed->data, HILO(ed->descriptors_length), 0x4d, boost::bind(&SiEpgParser::parseDescriptor_ShortEvent, e, _1), 0x50, boost::bind(&SiEpgParser::parseDescriptor_Component, e, _1), 0x54, boost::bind(&SiEpgParser::parseDescriptor_Content, e, _1), 0x55, boost::bind(&SiEpgParser::parseDescriptor_ParentalRating, e, _1)); HandleTable(e); }); }