1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
#include <time.h>
#include <glibmm/regex.h>
#include <boost/regex.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/gregorian_calendar.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#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<Glib::Regex> episodeRegex = Glib::Regex::create("(?:[ (]+|^)(?:\\w+ )?([0-9]+)(?: of |/)([0-9]+)[.)]+");
static Glib::RefPtr<Glib::Regex> yearRegex = Glib::Regex::create("\\(([0-9]{4})[ )]+");
static Glib::RefPtr<Glib::Regex> flagsRegex = Glib::Regex::create("[ []+([A-Z,]+)\\]");
void
SiEpgParser::parseDescriptor_ShortEvent(DVBSI::EventPtr current, const u_char * data)
{
auto * evtdesc = reinterpret_cast<const struct ShortEvent *>(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 (episodeRegex->match(*subtitle, matches)) {
current->Episode = boost::lexical_cast<int>(matches.fetch(1));
current->Episodes = boost::lexical_cast<int>(matches.fetch(2));
*subtitle = episodeRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY);
}
if (yearRegex->match(*subtitle, matches)) {
current->Year = boost::lexical_cast<int>(matches.fetch(1));
*subtitle = yearRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY);
}
if (flagsRegex->match(*subtitle, matches)) {
current->Flags = matches.fetch(1);
*subtitle = yearRegex->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<const struct 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->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<const Content *>(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<const ParentalRating *>(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<EventDescriptor>(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;
// <ew>
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;
// </ew>
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);
});
}
|