summaryrefslogtreecommitdiff
path: root/p2pvr/scanner/eitRows.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'p2pvr/scanner/eitRows.cpp')
-rw-r--r--p2pvr/scanner/eitRows.cpp493
1 files changed, 493 insertions, 0 deletions
diff --git a/p2pvr/scanner/eitRows.cpp b/p2pvr/scanner/eitRows.cpp
new file mode 100644
index 0000000..aea639f
--- /dev/null
+++ b/p2pvr/scanner/eitRows.cpp
@@ -0,0 +1,493 @@
+/*
+ * tv_grab_dvb - dump dvb epg info in xmltv
+ * Version 0.2 - 20/04/2004 - First Public Release
+ *
+ * Copyright (C) 2004 Mark Bryars <dvb at darkskiez d0t co d0t uk>
+ *
+ * DVB code Mercilessly ripped off from dvddate
+ * dvbdate Copyright (C) Laurence Culhane 2002 <dvbdate@holmes.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <time.h>
+#include <boost/bind.hpp>
+#include <boost/date_time/gregorian_calendar.hpp>
+
+#include <linux/dvb/dmx.h>
+#include "si_tables.h"
+
+#include "rowProcessor.h"
+#include "eitRows.h"
+
+struct EitProgram {
+ VariableType serviceID;
+ VariableType eventID;
+ VariableType title;
+ VariableType titleLang;
+ VariableType subtitle;
+ VariableType descLang;
+ VariableType desc1;
+ VariableType desc2;
+ VariableType desc3;
+ VariableType videoAspect;
+ VariableType videoFrameRate;
+ VariableType videoHD;
+ VariableType audioChannels;
+ VariableType language;
+ VariableType teletextSubtitleLang;
+ VariableType category;
+ VariableType dvbRating;
+ VariableType contentItemID;
+ VariableType contentSeriesID;
+ VariableType contentRecommendation;
+ VariableType startTime;
+ VariableType stopTime;
+};
+
+Glib::ustring title("title");
+template <class C, class M>
+const M &
+getMember(M C::*t, C * p) {
+ return p->*t;
+}
+SimpleMessageException(NoSuchAttribute);
+
+void
+EitRows::loadComplete(const CommonObjects *)
+{
+}
+
+void
+EitRows::setFilter(const Glib::ustring &)
+{
+}
+
+unsigned int
+EitRows::columnCount() const
+{
+ return 1;
+}
+
+const Glib::ustring &
+EitRows::getColumnName(unsigned int) const {
+ return title;
+}
+
+VariableType
+EitRows::getCurrentValue(const Glib::ustring &) const {
+ return current->title;
+}
+
+VariableType
+EitRows::getCurrentValue(unsigned int) const {
+ return current->title;
+}
+
+bool
+EitRows::isNull(unsigned int ) const {
+ return false;
+}
+
+bool
+EitRows::isNull(const Glib::ustring & ) const {
+ return false;
+}
+
+#define returnAttr(name) if (attrName == #name) return boost::bind(getMember<EitProgram, VariableType>, &EitProgram::name, boost::ref(current))
+RowSet::RowAttribute
+EitRows::resolveAttr(const Glib::ustring & attrName) const {
+ returnAttr(serviceID);
+ returnAttr(eventID);
+ returnAttr(title);
+ returnAttr(titleLang);
+ returnAttr(subtitle);
+ returnAttr(descLang);
+ returnAttr(desc1);
+ returnAttr(desc2);
+ returnAttr(desc3);
+ returnAttr(videoAspect);
+ returnAttr(videoFrameRate);
+ returnAttr(videoHD);
+ returnAttr(audioChannels);
+ returnAttr(language);
+ returnAttr(teletextSubtitleLang);
+ returnAttr(category);
+ returnAttr(dvbRating);
+ returnAttr(contentItemID);
+ returnAttr(contentSeriesID);
+ returnAttr(contentRecommendation);
+ returnAttr(startTime);
+ returnAttr(stopTime);
+ throw NoSuchAttribute(attrName);
+}
+
+DECLARE_LOADER("eitrows", EitRows);
+
+EitRows::EitRows(const xmlpp::Element * p) :
+ RowSet(p),
+ DvbSiReaderHelper(p),
+ current(NULL)
+{
+}
+
+EitRows::~EitRows()
+{
+}
+
+static int time_offset = 0;
+static int chan_filter = 0;
+static int chan_filter_mask = 0;
+
+void
+EitRows::parseEventDescription(const u_char *data) const {
+ assert(GetDescriptorTag(data) == 0x4D);
+ const struct descr_short_event *evtdesc = reinterpret_cast<const struct descr_short_event *>(data);
+
+ size_t evtlen = evtdesc->event_name_length;
+ if (evtlen) {
+ current->titleLang = Glib::ustring((const char *)&evtdesc->lang_code1, 3);
+ current->title = convert((const char *)evtdesc->data, evtlen);
+ }
+
+ size_t dsclen = evtdesc->data[evtlen];
+ if (dsclen) {
+ current->subtitle = convert((const char *)evtdesc->data + evtlen + 1, dsclen);
+ }
+}
+
+/* Parse 0x4E Extended Event Descriptor. {{{ */
+void
+EitRows::parseLongEventDescription(const u_char *data) const {
+ assert(GetDescriptorTag(data) == 0x4E);
+ const struct descr_extended_event *levt = reinterpret_cast<const struct descr_extended_event *>(data);
+ bool non_empty = (levt->descriptor_number || levt->last_descriptor_number || levt->length_of_items || levt->data[0]);
+
+ if (non_empty && levt->descriptor_number == 0) {
+ current->descLang = Glib::ustring((const char *)&levt->lang_code1, 3);
+
+ const u_char *p = reinterpret_cast<const u_char *>(&levt->data);
+#ifndef NDEBUG
+ const void *data_end = data + DESCR_GEN_LEN + GetDescriptorLength(data);
+#endif
+ while (p < levt->data + levt->length_of_items) {
+ const struct item_extended_event *name = reinterpret_cast<const struct item_extended_event *>(p);
+ size_t name_len = name->item_description_length;
+ assert(p + ITEM_EXTENDED_EVENT_LEN + name_len < data_end);
+ current->desc1 = convert((const char *)name->data, name_len);
+
+ p += ITEM_EXTENDED_EVENT_LEN + name_len;
+
+ const struct item_extended_event *value = reinterpret_cast<const struct item_extended_event *>(p);
+ size_t value_len = value->item_description_length;
+ assert(p + ITEM_EXTENDED_EVENT_LEN + value_len < data_end);
+ current->desc2 = convert((const char *)value->data, value_len);
+
+ p += ITEM_EXTENDED_EVENT_LEN + value_len;
+ }
+ const struct item_extended_event *text = reinterpret_cast<const struct item_extended_event *>(p);
+ size_t len = text->item_description_length;
+ if (non_empty && len) {
+ current->desc3 = convert((const char *)text->data, len);
+ }
+
+ }
+} /*}}}*/
+
+/* Parse 0x50 Component Descriptor. {{{
+ video is a flag, 1=> output the video information, 0=> output the
+ audio information. seen is a pointer to a counter to ensure we
+ only output the first one of each (XMLTV can't cope with more than
+ one) */
+void
+EitRows::parseComponentDescription(const u_char *data) const {
+ assert(GetDescriptorTag(data) == 0x50);
+ const struct descr_component *dc = reinterpret_cast<const struct descr_component *>(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->language = Glib::ustring((const char *)&dc->lang_code1, 3);
+ break;
+ case 0x03: // Teletext Info
+ // FIXME: is there a suitable XMLTV output for this?
+ // if ((dc->component_type)&0x10) //subtitles
+ // if ((dc->component_type)&0x20) //subtitles for hard of hearing
+ current->teletextSubtitleLang = Glib::ustring((const char *)&dc->lang_code1, 3);
+ break;
+ // case 0x04: // AC3 info
+ }
+} /*}}}*/
+
+void
+EitRows::parseContentDescription(const u_char *data) const {
+ assert(GetDescriptorTag(data) == 0x54);
+ const struct descr_content * dc = reinterpret_cast<const struct descr_content *>(data);
+ for (const u_char * p = reinterpret_cast<const u_char*>(&dc->data); p < data + DESCR_GEN_LEN + dc->descriptor_length; p += NIBBLE_CONTENT_LEN) {
+ const struct nibble_content *nc = reinterpret_cast<const nibble_content *>(p);
+ int c1 = (nc->content_nibble_level_1 << 4) + nc->content_nibble_level_2;
+ // This is weird in the uk, they use user but not content, and almost the same values
+ current->category = c1 ? c1 : (nc->user_nibble_1 << 4) + nc->user_nibble_2;
+ }
+}
+
+void
+EitRows::parseRatingDescription(const u_char *data) const {
+ assert(GetDescriptorTag(data) == 0x55);
+ const struct descr_parental_rating * pr = reinterpret_cast<const struct descr_parental_rating *>(data);
+ for (const u_char * p = reinterpret_cast<const u_char *>(&pr->data); p < data + DESCR_GEN_LEN + pr->descriptor_length; p += PARENTAL_RATING_ITEM_LEN) {
+ const struct parental_rating_item *pr = reinterpret_cast<const struct parental_rating_item *>(p);
+ switch (pr->rating) {
+ case 0x00: /*undefined*/
+ break;
+ case 0x01 ... 0x0F:
+ current->dvbRating = pr->rating + 3;
+ break;
+ case 0x10 ... 0xFF: /*broadcaster defined*/
+ break;
+ }
+ }
+}
+
+int parsePrivateDataSpecifier(const u_char *data) {
+ assert(GetDescriptorTag(data) == 0x5F);
+ return GetPrivateDataSpecifier(data);
+}
+
+/* Parse 0x76 Content Identifier Descriptor. {{{ */
+/* See ETSI TS 102 323, section 12 */
+void
+EitRows::parseContentIdentifierDescription(const u_char *data) const {
+ assert(GetDescriptorTag(data) == 0x76);
+ const struct descr_content_identifier *ci = reinterpret_cast<const struct descr_content_identifier *>(data);
+ for (const u_char * p = reinterpret_cast<const u_char *>(&ci->data); p < data + DESCR_GEN_LEN + ci->descriptor_length; p += DESCR_GEN_LEN + ci->descriptor_length) {
+ const struct descr_content_identifier_crid *crid = reinterpret_cast<const struct descr_content_identifier_crid *>(p);
+
+ switch (crid->crid_location)
+ {
+ case 0x01: /* Carried in Content Identifier Table (CIT) */
+ default:
+ break;
+ case 0x00: /* Carried explicitly within descriptor */
+ struct descr_content_identifier_crid_local * crid_data = (descr_content_identifier_crid_local_t *)&crid->crid_ref_data;
+ size_t len = crid_data->crid_length;
+ switch (crid->crid_type) {
+ case 0x01:
+ case 0x31:
+ current->contentItemID = Glib::ustring((const char *)crid_data->crid_byte, len);
+ break;
+ case 0x02:
+ case 0x32:
+ current->contentSeriesID = Glib::ustring((const char *)crid_data->crid_byte, len);
+ break;
+ case 0x03:
+ case 0x33:
+ current->contentRecommendation = Glib::ustring((const char *)crid_data->crid_byte, len);
+ break;
+ }
+ break;
+ }
+ }
+} /*}}}*/
+
+/* Parse Descriptor. {{{
+ * Tags should be output in this order:
+
+'title', 'sub-title', 'desc', 'credits', 'date', 'category', 'language',
+'orig-language', 'length', 'icon', 'url', 'country', 'episode-num',
+'video', 'audio', 'previously-shown', 'premiere', 'last-chance',
+'new', 'subtitles', 'rating', 'star-rating'
+*/
+void
+EitRows::parseDescription(const u_char * data, size_t len) const {
+ int pds = 0;
+ for (const u_char * p = data; p < data + len; p += DESCR_GEN_LEN + GetDescriptorLength(p)) {
+ const struct descr_gen *desc = reinterpret_cast<const struct descr_gen *>(p);
+ switch (GetDescriptorTag(desc)) {
+ case 0:
+ break;
+ case 0x4D: //short evt desc, [title] [sub-title]
+ // there can be multiple language versions of these
+ parseEventDescription(p);
+ break;
+ case 0x4E: //long evt descriptor [desc]
+ parseLongEventDescription(p);
+ break;
+ case 0x50: //component desc [language] [video] [audio] [subtitles]
+ parseComponentDescription(p);
+ break;
+ case 0x53: // CA Identifier Descriptor
+ break;
+ case 0x54: // content desc [category]
+ parseContentDescription(p);
+ break;
+ case 0x55: // Parental Rating Descriptor [rating]
+ parseRatingDescription(p);
+ break;
+ case 0x5f: // Private Data Specifier
+ pds = parsePrivateDataSpecifier(p);
+ break;
+ case 0x64: // Data broadcast desc - Text Desc for Data components
+ break;
+ case 0x69: // Programm Identification Label
+ break;
+ case 0x81: // TODO ???
+ if (pds == 5) // ARD_ZDF_ORF
+ break;
+ case 0x82: // VPS (ARD, ZDF, ORF)
+ if (pds == 5) // ARD_ZDF_ORF
+ // TODO: <programme @vps-start="???">
+ break;
+ case 0x4F: // Time Shifted Event
+ case 0x52: // Stream Identifier Descriptor
+ case 0x5E: // Multi Lingual Component Descriptor
+ case 0x83: // Logical Channel Descriptor (some kind of news-ticker on ARD-MHP-Data?)
+ case 0x84: // Preferred Name List Descriptor
+ case 0x85: // Preferred Name Identifier Descriptor
+ case 0x86: // Eacem Stream Identifier Descriptor
+ break;
+ case 0x76: // Content identifier descriptor
+ parseContentIdentifierDescription(p);
+ break;
+ default:
+ break;
+ }
+ }
+} /*}}}*/
+
+/* Check that program has at least a title as is required by xmltv.dtd. {{{ */
+static bool validateDescription(const u_char *data, size_t len) {
+ for (const u_char * p = data; p < data + len; p += DESCR_GEN_LEN + GetDescriptorLength(p)) {
+ const struct descr_gen *desc = reinterpret_cast<const struct descr_gen *>(p);
+ if (GetDescriptorTag(desc) == 0x4D) {
+ const struct descr_short_event *evtdesc = reinterpret_cast<const struct descr_short_event *>(p);
+ // make sure that title isn't empty
+ if (evtdesc->event_name_length) return true;
+ }
+ }
+ return false;
+} /*}}}*/
+
+class SeenProgram {
+ public:
+ SeenProgram(int sid, int eid);
+ bool operator<(const SeenProgram &) const;
+ const int sid;
+ const int eid;
+};
+SeenProgram::SeenProgram(int s, int e) :
+ sid(s),
+ eid(e)
+{
+}
+bool
+SeenProgram::operator<(const SeenProgram & o) const
+{
+ return ((this->sid < o.sid) || ((this->sid == o.sid) && (this->eid < o.eid)));
+}
+
+typedef std::set<SeenProgram> SeenPrograms;
+SeenPrograms seenPrograms;
+bool
+EitRows::parseSIT(const u_char *data, size_t len, const RowProcessor * rp) const {
+ const struct eit *e = reinterpret_cast<const struct eit *>(data);
+
+ len -= 4; //remove CRC
+
+ // For each event listing
+ bool found = false;
+ for (const u_char *p = reinterpret_cast<const u_char *>(&e->data); p < data + len; p += EIT_EVENT_LEN + GetEITDescriptorsLoopLength(p)) {
+ const struct eit_event *evt = reinterpret_cast<const struct eit_event *>(p);
+ SeenProgram sp(HILO(e->service_id), HILO(evt->event_id));
+ if (seenPrograms.find(sp) != seenPrograms.end()) {
+ continue;
+ }
+ seenPrograms.insert(sp);
+
+ // No program info at end! Just skip it
+ if (GetEITDescriptorsLoopLength(evt) == 0) {
+ continue;
+ }
+
+ boost::gregorian::date startDate(boost::gregorian::gregorian_calendar::from_modjulian_day_number(HILO(evt->mjd)));
+ boost::posix_time::ptime startTime(startDate);
+ startTime += boost::posix_time::time_duration(
+ BcdCharToInt(evt->start_time_h) + time_offset,
+ BcdCharToInt(evt->start_time_m),
+ BcdCharToInt(evt->start_time_s));
+ EitProgram results;
+ current = &results;
+ results.startTime = startTime;
+ results.stopTime = startTime + boost::posix_time::time_duration(
+ BcdCharToInt(evt->duration_h),
+ BcdCharToInt(evt->duration_m),
+ BcdCharToInt(evt->duration_s));
+
+ // a program must have a title that isn't empty
+ if (!validateDescription(reinterpret_cast<const u_char *>(&evt->data), GetEITDescriptorsLoopLength(evt))) {
+ continue;
+ }
+
+
+ results.serviceID = HILO(e->service_id);
+ results.eventID = HILO(evt->event_id);
+
+ parseDescription(reinterpret_cast<const u_char *>(&evt->data), GetEITDescriptorsLoopLength(evt));
+ rp->rowReady();
+ rowNum += 1;
+ found = true;
+ }
+ return found;
+}
+
+SimpleMessageException(DemuxSetFilterFailure);
+
+void
+EitRows::filterInput(int fd) const
+{
+ struct dmx_sct_filter_params sctFilterParams;
+ memset(&sctFilterParams, 0, sizeof(dmx_sct_filter_params));
+ sctFilterParams.pid = 18; // EIT data
+ sctFilterParams.timeout = 0;
+ sctFilterParams.flags = DMX_IMMEDIATE_START;
+ sctFilterParams.filter.filter[0] = chan_filter; // 4e is now/next this multiplex, 4f others
+ sctFilterParams.filter.mask[0] = chan_filter_mask;
+
+ if (ioctl(fd, DMX_SET_FILTER, &sctFilterParams) < 0) {
+ throw DemuxSetFilterFailure(strerror(errno));
+ }
+}
+
+void
+EitRows::execute(const RowProcessor * rp) const
+{
+ openInput();
+ readTables(rp);
+ closeInput();
+}
+