1
|
/*
|
2
|
* Electronic Program Guide - EPG grabber OTA functions
|
3
|
* Copyright (C) 2012 Adam Sutton
|
4
|
*
|
5
|
* This program is free software: you can redistribute it and/or modify
|
6
|
* it under the terms of the GNU General Public License as published by
|
7
|
* the Free Software Foundation, either version 3 of the License, or
|
8
|
* (at your option) any later version.
|
9
|
*
|
10
|
* This program is distributed in the hope that it will be useful,
|
11
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
* GNU General Public License for more details.
|
14
|
*
|
15
|
* You should have received a copy of the GNU General Public License
|
16
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
*/
|
18
|
|
19
|
#include "tvheadend.h"
|
20
|
#include "queue.h"
|
21
|
#include "settings.h"
|
22
|
#include "epg.h"
|
23
|
#include "epggrab.h"
|
24
|
#include "epggrab/private.h"
|
25
|
#include "input.h"
|
26
|
#include "subscriptions.h"
|
27
|
#include "cron.h"
|
28
|
#include "dbus.h"
|
29
|
|
30
|
#include <string.h>
|
31
|
#include <sys/types.h>
|
32
|
#include <sys/stat.h>
|
33
|
#include <unistd.h>
|
34
|
|
35
|
#define EPGGRAB_OTA_MIN_TIMEOUT 30
|
36
|
#define EPGGRAB_OTA_MAX_TIMEOUT 7200
|
37
|
|
38
|
#define EPGGRAB_OTA_DONE_COMPLETE 0
|
39
|
#define EPGGRAB_OTA_DONE_TIMEOUT 1
|
40
|
#define EPGGRAB_OTA_DONE_NO_DATA 2
|
41
|
#define EPGGRAB_OTA_DONE_STOLEN 3
|
42
|
|
43
|
typedef TAILQ_HEAD(epggrab_ota_head,epggrab_ota_mux) epggrab_ota_head_t;
|
44
|
|
45
|
cron_multi_t *epggrab_ota_cron_multi;
|
46
|
|
47
|
RB_HEAD(,epggrab_ota_mux) epggrab_ota_all;
|
48
|
epggrab_ota_head_t epggrab_ota_pending;
|
49
|
epggrab_ota_head_t epggrab_ota_active;
|
50
|
|
51
|
mtimer_t epggrab_ota_kick_timer;
|
52
|
gtimer_t epggrab_ota_start_timer;
|
53
|
|
54
|
int epggrab_ota_running;
|
55
|
int epggrab_ota_pending_flag;
|
56
|
|
57
|
pthread_mutex_t epggrab_ota_mutex;
|
58
|
|
59
|
SKEL_DECLARE(epggrab_ota_mux_skel, epggrab_ota_mux_t);
|
60
|
SKEL_DECLARE(epggrab_svc_link_skel, epggrab_ota_svc_link_t);
|
61
|
|
62
|
static void epggrab_ota_kick ( int delay );
|
63
|
|
64
|
static void epggrab_ota_timeout_cb ( void *p );
|
65
|
static void epggrab_ota_data_timeout_cb ( void *p );
|
66
|
static void epggrab_ota_kick_cb ( void *p );
|
67
|
|
68
|
static void epggrab_mux_start ( mpegts_mux_t *mm, void *p );
|
69
|
|
70
|
static void epggrab_ota_save ( epggrab_ota_mux_t *ota );
|
71
|
|
72
|
static void epggrab_ota_free ( epggrab_ota_head_t *head, epggrab_ota_mux_t *ota );
|
73
|
|
74
|
/* **************************************************************************
|
75
|
* Utilities
|
76
|
* *************************************************************************/
|
77
|
|
78
|
static int
|
79
|
om_id_cmp ( epggrab_ota_mux_t *a, epggrab_ota_mux_t *b )
|
80
|
{
|
81
|
return strcmp(a->om_mux_uuid, b->om_mux_uuid);
|
82
|
}
|
83
|
|
84
|
static int
|
85
|
om_mux_cmp ( epggrab_ota_mux_t *a, epggrab_ota_mux_t *b )
|
86
|
{
|
87
|
mpegts_mux_t *a1 = mpegts_mux_find(a->om_mux_uuid);
|
88
|
mpegts_mux_t *b1 = mpegts_mux_find(b->om_mux_uuid);
|
89
|
if (a1 == NULL || b1 == NULL) {
|
90
|
if (a1 == NULL && b1 == NULL)
|
91
|
return 0;
|
92
|
return a1 == NULL ? 1 : -1;
|
93
|
}
|
94
|
return mpegts_mux_compare(a1, b1);
|
95
|
}
|
96
|
|
97
|
static int
|
98
|
om_svcl_cmp ( epggrab_ota_svc_link_t *a, epggrab_ota_svc_link_t *b )
|
99
|
{
|
100
|
return strcmp(a->uuid, b->uuid);
|
101
|
}
|
102
|
|
103
|
static int
|
104
|
epggrab_ota_timeout_get ( void )
|
105
|
{
|
106
|
int timeout = epggrab_conf.ota_timeout;
|
107
|
|
108
|
if (timeout < EPGGRAB_OTA_MIN_TIMEOUT)
|
109
|
timeout = EPGGRAB_OTA_MIN_TIMEOUT;
|
110
|
if (timeout > EPGGRAB_OTA_MAX_TIMEOUT)
|
111
|
timeout = EPGGRAB_OTA_MAX_TIMEOUT;
|
112
|
|
113
|
return timeout;
|
114
|
}
|
115
|
|
116
|
static int
|
117
|
epggrab_ota_queue_one( epggrab_ota_mux_t *om )
|
118
|
{
|
119
|
om->om_done = 0;
|
120
|
om->om_requeue = 1;
|
121
|
if (om->om_q_type != EPGGRAB_OTA_MUX_IDLE)
|
122
|
return 0;
|
123
|
TAILQ_INSERT_SORTED(&epggrab_ota_pending, om, om_q_link, om_mux_cmp);
|
124
|
om->om_q_type = EPGGRAB_OTA_MUX_PENDING;
|
125
|
return 1;
|
126
|
}
|
127
|
|
128
|
void
|
129
|
epggrab_ota_queue_mux( mpegts_mux_t *mm )
|
130
|
{
|
131
|
const char *id;
|
132
|
epggrab_ota_mux_t *om;
|
133
|
int epg_flag;
|
134
|
char ubuf[UUID_HEX_SIZE];
|
135
|
|
136
|
if (!mm)
|
137
|
return;
|
138
|
|
139
|
lock_assert(&global_lock);
|
140
|
|
141
|
id = idnode_uuid_as_str(&mm->mm_id, ubuf);
|
142
|
epg_flag = mm->mm_is_epg(mm);
|
143
|
if (epg_flag < 0 || epg_flag == MM_EPG_DISABLE)
|
144
|
return;
|
145
|
RB_FOREACH(om, &epggrab_ota_all, om_global_link)
|
146
|
if (!strcmp(om->om_mux_uuid, id)) {
|
147
|
if (epggrab_ota_queue_one(om))
|
148
|
epggrab_ota_kick(4);
|
149
|
break;
|
150
|
}
|
151
|
}
|
152
|
|
153
|
static void
|
154
|
epggrab_ota_requeue ( void )
|
155
|
{
|
156
|
epggrab_ota_mux_t *om;
|
157
|
|
158
|
/*
|
159
|
* enqueue all muxes, but ommit the delayed ones (active+pending)
|
160
|
*/
|
161
|
RB_FOREACH(om, &epggrab_ota_all, om_global_link)
|
162
|
epggrab_ota_queue_one(om);
|
163
|
}
|
164
|
|
165
|
static void
|
166
|
epggrab_ota_kick ( int delay )
|
167
|
{
|
168
|
/* next round is pending? queue rest of ota muxes */
|
169
|
if (epggrab_ota_pending_flag) {
|
170
|
epggrab_ota_pending_flag = 0;
|
171
|
epggrab_ota_requeue();
|
172
|
}
|
173
|
|
174
|
if (TAILQ_EMPTY(&epggrab_ota_pending))
|
175
|
return;
|
176
|
|
177
|
mtimer_arm_rel(&epggrab_ota_kick_timer, epggrab_ota_kick_cb, NULL, sec2mono(delay));
|
178
|
}
|
179
|
|
180
|
static void
|
181
|
epggrab_ota_done ( epggrab_ota_mux_t *om, int reason )
|
182
|
{
|
183
|
static const char *reasons[] = {
|
184
|
[EPGGRAB_OTA_DONE_COMPLETE] = "complete",
|
185
|
[EPGGRAB_OTA_DONE_TIMEOUT] = "timeout",
|
186
|
[EPGGRAB_OTA_DONE_NO_DATA] = "no data",
|
187
|
[EPGGRAB_OTA_DONE_STOLEN] = "stolen"
|
188
|
};
|
189
|
char name[256];
|
190
|
mpegts_mux_t *mm;
|
191
|
epggrab_ota_map_t *map;
|
192
|
|
193
|
if (om->om_save)
|
194
|
epggrab_ota_save(om);
|
195
|
|
196
|
mm = mpegts_mux_find(om->om_mux_uuid);
|
197
|
mpegts_mux_nice_name(mm, name, sizeof(name));
|
198
|
tvhdebug(LS_EPGGRAB, "grab done for %s (%s)", name, reasons[reason]);
|
199
|
|
200
|
mtimer_disarm(&om->om_timer);
|
201
|
mtimer_disarm(&om->om_data_timer);
|
202
|
|
203
|
assert(om->om_q_type == EPGGRAB_OTA_MUX_ACTIVE);
|
204
|
TAILQ_REMOVE(&epggrab_ota_active, om, om_q_link);
|
205
|
om->om_q_type = EPGGRAB_OTA_MUX_IDLE;
|
206
|
if (reason == EPGGRAB_OTA_DONE_STOLEN) {
|
207
|
/* Do not requeue completed muxes */
|
208
|
if (!om->om_done && om->om_requeue) {
|
209
|
TAILQ_INSERT_HEAD(&epggrab_ota_pending, om, om_q_link);
|
210
|
om->om_q_type = EPGGRAB_OTA_MUX_PENDING;
|
211
|
} else {
|
212
|
om->om_requeue = 0;
|
213
|
}
|
214
|
} else if (reason == EPGGRAB_OTA_DONE_TIMEOUT) {
|
215
|
om->om_requeue = 0;
|
216
|
LIST_FOREACH(map, &om->om_modules, om_link)
|
217
|
if (!map->om_complete)
|
218
|
tvhwarn(LS_EPGGRAB, "%s - data completion timeout for %s", map->om_module->name, name);
|
219
|
} else {
|
220
|
om->om_requeue = 0;
|
221
|
}
|
222
|
|
223
|
/* Remove subscriber */
|
224
|
if (mm)
|
225
|
mpegts_mux_unsubscribe_by_name(mm, "epggrab");
|
226
|
|
227
|
/* Kick - try start waiting muxes */
|
228
|
epggrab_ota_kick(1);
|
229
|
}
|
230
|
|
231
|
static void
|
232
|
epggrab_ota_complete_mark ( epggrab_ota_mux_t *om, int done )
|
233
|
{
|
234
|
om->om_done = 1;
|
235
|
if (!om->om_complete) {
|
236
|
om->om_complete = 1;
|
237
|
epggrab_ota_save(om);
|
238
|
}
|
239
|
}
|
240
|
|
241
|
static void
|
242
|
epggrab_ota_start ( epggrab_ota_mux_t *om, mpegts_mux_t *mm )
|
243
|
{
|
244
|
epggrab_module_t *m;
|
245
|
epggrab_ota_map_t *map;
|
246
|
char *modname = om->om_force_modname;
|
247
|
mpegts_mux_instance_t *mmi = mm->mm_active;
|
248
|
int grace;
|
249
|
|
250
|
/* In pending queue? Remove.. */
|
251
|
if (om->om_q_type == EPGGRAB_OTA_MUX_PENDING)
|
252
|
TAILQ_REMOVE(&epggrab_ota_pending, om, om_q_link);
|
253
|
else
|
254
|
assert(om->om_q_type == EPGGRAB_OTA_MUX_IDLE);
|
255
|
|
256
|
TAILQ_INSERT_TAIL(&epggrab_ota_active, om, om_q_link);
|
257
|
om->om_q_type = EPGGRAB_OTA_MUX_ACTIVE;
|
258
|
grace = mpegts_input_grace(mmi->mmi_input, mm);
|
259
|
mtimer_arm_rel(&om->om_timer, epggrab_ota_timeout_cb, om,
|
260
|
sec2mono(epggrab_ota_timeout_get() + grace));
|
261
|
mtimer_arm_rel(&om->om_data_timer, epggrab_ota_data_timeout_cb, om,
|
262
|
sec2mono(30 + grace)); /* 30 seconds to receive any EPG info */
|
263
|
if (modname) {
|
264
|
LIST_FOREACH(m, &epggrab_modules, link)
|
265
|
if (!strcmp(m->id, modname)) {
|
266
|
epggrab_ota_register((epggrab_module_ota_t *)m, om, mm);
|
267
|
break;
|
268
|
}
|
269
|
}
|
270
|
LIST_FOREACH(map, &om->om_modules, om_link) {
|
271
|
map->om_first = 1;
|
272
|
map->om_forced = 0;
|
273
|
if (modname && !strcmp(modname, map->om_module->id))
|
274
|
map->om_forced = 1;
|
275
|
map->om_complete = 0;
|
276
|
if (map->om_module->start(map, mm) < 0) {
|
277
|
map->om_complete = 1;
|
278
|
} else
|
279
|
tvhdebug(map->om_module->subsys, "%s: grab started", map->om_module->id);
|
280
|
}
|
281
|
}
|
282
|
|
283
|
/* **************************************************************************
|
284
|
* MPEG-TS listener
|
285
|
* *************************************************************************/
|
286
|
|
287
|
static void
|
288
|
epggrab_mux_start ( mpegts_mux_t *mm, void *p )
|
289
|
{
|
290
|
epggrab_module_t *m;
|
291
|
epggrab_ota_mux_t *ota;
|
292
|
char ubuf[UUID_HEX_SIZE];
|
293
|
const char *uuid = idnode_uuid_as_str(&mm->mm_id, ubuf);
|
294
|
|
295
|
/* Already started */
|
296
|
TAILQ_FOREACH(ota, &epggrab_ota_active, om_q_link)
|
297
|
if (!strcmp(ota->om_mux_uuid, uuid))
|
298
|
return;
|
299
|
|
300
|
/* Register all modules */
|
301
|
ota = NULL;
|
302
|
LIST_FOREACH(m, &epggrab_modules, link) {
|
303
|
if (m->type == EPGGRAB_OTA && m->enabled)
|
304
|
ota = epggrab_ota_register((epggrab_module_ota_t *)m, ota, mm);
|
305
|
}
|
306
|
|
307
|
if (ota)
|
308
|
epggrab_ota_start(ota, mm);
|
309
|
}
|
310
|
|
311
|
static void
|
312
|
epggrab_mux_stop ( mpegts_mux_t *mm, void *p, int reason )
|
313
|
{
|
314
|
epggrab_ota_mux_t *ota;
|
315
|
char ubuf[UUID_HEX_SIZE], name[256];
|
316
|
const char *uuid = idnode_uuid_as_str(&mm->mm_id, ubuf);
|
317
|
int done = EPGGRAB_OTA_DONE_STOLEN;
|
318
|
|
319
|
if (reason == SM_CODE_NO_INPUT)
|
320
|
done = EPGGRAB_OTA_DONE_NO_DATA;
|
321
|
|
322
|
if (tvhtrace_enabled()) {
|
323
|
mpegts_mux_nice_name(mm, name, sizeof(name));
|
324
|
tvhtrace(LS_EPGGRAB, "mux %s (%p) stop", name, mm);
|
325
|
}
|
326
|
TAILQ_FOREACH(ota, &epggrab_ota_active, om_q_link)
|
327
|
if (!strcmp(ota->om_mux_uuid, uuid)) {
|
328
|
epggrab_ota_done(ota, done);
|
329
|
break;
|
330
|
}
|
331
|
}
|
332
|
|
333
|
/* **************************************************************************
|
334
|
* Module methods
|
335
|
* *************************************************************************/
|
336
|
|
337
|
epggrab_ota_mux_t *
|
338
|
epggrab_ota_register
|
339
|
( epggrab_module_ota_t *mod, epggrab_ota_mux_t *ota, mpegts_mux_t *mm )
|
340
|
{
|
341
|
int save = 0;
|
342
|
epggrab_ota_map_t *map;
|
343
|
|
344
|
if (!atomic_get(&epggrab_ota_running))
|
345
|
return NULL;
|
346
|
|
347
|
if (ota == NULL) {
|
348
|
/* Find mux entry */
|
349
|
char ubuf[UUID_HEX_SIZE];
|
350
|
const char *uuid = idnode_uuid_as_str(&mm->mm_id, ubuf);
|
351
|
SKEL_ALLOC(epggrab_ota_mux_skel);
|
352
|
epggrab_ota_mux_skel->om_mux_uuid = (char*)uuid;
|
353
|
|
354
|
ota = RB_INSERT_SORTED(&epggrab_ota_all, epggrab_ota_mux_skel, om_global_link, om_id_cmp);
|
355
|
if (!ota) {
|
356
|
char buf[256];
|
357
|
mpegts_mux_nice_name(mm, buf, sizeof(buf));
|
358
|
tvhinfo(mod->subsys, "%s: registering mux %s", buf, mod->id);
|
359
|
ota = epggrab_ota_mux_skel;
|
360
|
SKEL_USED(epggrab_ota_mux_skel);
|
361
|
ota->om_mux_uuid = strdup(uuid);
|
362
|
TAILQ_INSERT_SORTED(&epggrab_ota_pending, ota, om_q_link, om_mux_cmp);
|
363
|
ota->om_q_type = EPGGRAB_OTA_MUX_PENDING;
|
364
|
if (TAILQ_FIRST(&epggrab_ota_pending) == ota)
|
365
|
epggrab_ota_kick(1);
|
366
|
save = 1;
|
367
|
}
|
368
|
}
|
369
|
|
370
|
/* Find module entry */
|
371
|
LIST_FOREACH(map, &ota->om_modules, om_link)
|
372
|
if (map->om_module == mod)
|
373
|
break;
|
374
|
if (!map) {
|
375
|
map = calloc(1, sizeof(epggrab_ota_map_t));
|
376
|
RB_INIT(&map->om_svcs);
|
377
|
map->om_module = mod;
|
378
|
LIST_INSERT_HEAD(&ota->om_modules, map, om_link);
|
379
|
save = 1;
|
380
|
}
|
381
|
|
382
|
/* Save config */
|
383
|
if (save) epggrab_ota_save(ota);
|
384
|
|
385
|
return ota;
|
386
|
}
|
387
|
|
388
|
void
|
389
|
epggrab_ota_complete
|
390
|
( epggrab_module_ota_t *mod, epggrab_ota_mux_t *ota )
|
391
|
{
|
392
|
int done = 1;
|
393
|
epggrab_ota_map_t *map;
|
394
|
lock_assert(&global_lock);
|
395
|
|
396
|
if (!ota->om_complete)
|
397
|
tvhdebug(mod->subsys, "%s: grab complete", mod->id);
|
398
|
|
399
|
/* Test for completion */
|
400
|
LIST_FOREACH(map, &ota->om_modules, om_link) {
|
401
|
if (map->om_module == mod) {
|
402
|
map->om_complete = 1;
|
403
|
} else if (!map->om_complete && !map->om_first) {
|
404
|
done = 0;
|
405
|
}
|
406
|
tvhtrace(LS_EPGGRAB, "%s complete %i first %i",
|
407
|
map->om_module->id, map->om_complete, map->om_first);
|
408
|
}
|
409
|
|
410
|
epggrab_ota_complete_mark(ota, done);
|
411
|
|
412
|
if (!done) return;
|
413
|
|
414
|
/* Done */
|
415
|
if (ota->om_q_type == EPGGRAB_OTA_MUX_ACTIVE)
|
416
|
epggrab_ota_done(ota, EPGGRAB_OTA_DONE_COMPLETE);
|
417
|
else if (ota->om_q_type == EPGGRAB_OTA_MUX_PENDING) {
|
418
|
TAILQ_REMOVE(&epggrab_ota_pending, ota, om_q_link);
|
419
|
ota->om_q_type = EPGGRAB_OTA_MUX_IDLE;
|
420
|
}
|
421
|
}
|
422
|
|
423
|
/* **************************************************************************
|
424
|
* Timer callbacks
|
425
|
* *************************************************************************/
|
426
|
|
427
|
static void
|
428
|
epggrab_ota_timeout_cb ( void *p )
|
429
|
{
|
430
|
epggrab_ota_mux_t *om = p;
|
431
|
|
432
|
lock_assert(&global_lock);
|
433
|
|
434
|
if (!om)
|
435
|
return;
|
436
|
|
437
|
/* Abort */
|
438
|
epggrab_ota_done(om, EPGGRAB_OTA_DONE_TIMEOUT);
|
439
|
/* Not completed, but no further data for a long period */
|
440
|
/* wait for a manual mux tuning */
|
441
|
epggrab_ota_complete_mark(om, 1);
|
442
|
}
|
443
|
|
444
|
static void
|
445
|
epggrab_ota_data_timeout_cb ( void *p )
|
446
|
{
|
447
|
epggrab_ota_mux_t *om = p;
|
448
|
epggrab_ota_map_t *map;
|
449
|
|
450
|
lock_assert(&global_lock);
|
451
|
|
452
|
if (!om)
|
453
|
return;
|
454
|
|
455
|
/* Test for any valid data reception */
|
456
|
LIST_FOREACH(map, &om->om_modules, om_link) {
|
457
|
if (!map->om_first)
|
458
|
break;
|
459
|
}
|
460
|
|
461
|
if (map == NULL) {
|
462
|
/* Abort */
|
463
|
epggrab_ota_done(om, EPGGRAB_OTA_DONE_NO_DATA);
|
464
|
/* Not completed, but no data - wait for a manual mux tuning */
|
465
|
epggrab_ota_complete_mark(om, 1);
|
466
|
} else {
|
467
|
tvhtrace(LS_EPGGRAB, "data timeout check succeed");
|
468
|
}
|
469
|
}
|
470
|
|
471
|
static void
|
472
|
epggrab_ota_kick_cb ( void *p )
|
473
|
{
|
474
|
extern const idclass_t mpegts_mux_class;
|
475
|
epggrab_ota_map_t *map;
|
476
|
epggrab_ota_mux_t *om = TAILQ_FIRST(&epggrab_ota_pending);
|
477
|
epggrab_ota_mux_t *first = NULL;
|
478
|
mpegts_mux_t *mm;
|
479
|
char name[256];
|
480
|
struct {
|
481
|
mpegts_network_t *net;
|
482
|
uint8_t failed;
|
483
|
uint8_t fatal;
|
484
|
} networks[64], *net; /* more than 64 networks? - you're a king */
|
485
|
int i, r, networks_count = 0, epg_flag, kick = 1;
|
486
|
const char *modname;
|
487
|
static const char *modnames[] = {
|
488
|
[MM_EPG_DISABLE] = NULL,
|
489
|
[MM_EPG_ENABLE] = NULL,
|
490
|
[MM_EPG_FORCE] = NULL,
|
491
|
[MM_EPG_ONLY_EIT] = "eit",
|
492
|
[MM_EPG_ONLY_PSIP] = "psip",
|
493
|
[MM_EPG_ONLY_UK_FREESAT] = "uk_freesat",
|
494
|
[MM_EPG_ONLY_UK_FREEVIEW] = "uk_freeview",
|
495
|
[MM_EPG_ONLY_VIASAT_BALTIC] = "viasat_baltic",
|
496
|
[MM_EPG_ONLY_BULSATCOM_39E] = "Bulsatcom_39E",
|
497
|
[MM_EPG_ONLY_UK_CABLE] = "uk_cable",
|
498
|
[MM_EPG_ONLY_OPENTV_SKY_UK] = "opentv-skyuk",
|
499
|
[MM_EPG_ONLY_OPENTV_SKY_ITALIA] = "opentv-skyit",
|
500
|
[MM_EPG_ONLY_OPENTV_SKY_AUSAT] = "opentv-ausat",
|
501
|
};
|
502
|
|
503
|
lock_assert(&global_lock);
|
504
|
|
505
|
if (!om)
|
506
|
return;
|
507
|
|
508
|
tvhtrace(LS_EPGGRAB, "ota - kick callback");
|
509
|
|
510
|
next_one:
|
511
|
/* Find the mux */
|
512
|
mm = mpegts_mux_find(om->om_mux_uuid);
|
513
|
if (!mm) {
|
514
|
epggrab_ota_free(&epggrab_ota_pending, om);
|
515
|
goto done;
|
516
|
}
|
517
|
|
518
|
assert(om->om_q_type == EPGGRAB_OTA_MUX_PENDING);
|
519
|
TAILQ_REMOVE(&epggrab_ota_pending, om, om_q_link);
|
520
|
om->om_q_type = EPGGRAB_OTA_MUX_IDLE;
|
521
|
|
522
|
/* Check if this network failed before */
|
523
|
for (i = 0, net = NULL; i < networks_count; i++) {
|
524
|
net = &networks[i];
|
525
|
if (net->net == mm->mm_network) {
|
526
|
if (net->fatal)
|
527
|
goto done;
|
528
|
if (net->failed) {
|
529
|
TAILQ_INSERT_TAIL(&epggrab_ota_pending, om, om_q_link);
|
530
|
om->om_q_type = EPGGRAB_OTA_MUX_PENDING;
|
531
|
goto done;
|
532
|
}
|
533
|
break;
|
534
|
}
|
535
|
}
|
536
|
if (i >= networks_count) {
|
537
|
net = &networks[networks_count++];
|
538
|
net->net = mm->mm_network;
|
539
|
net->failed = 0;
|
540
|
net->fatal = 0;
|
541
|
}
|
542
|
|
543
|
epg_flag = MM_EPG_DISABLE;
|
544
|
if (mm->mm_is_enabled(mm)) {
|
545
|
epg_flag = mm->mm_is_epg(mm);
|
546
|
if (epg_flag > MM_EPG_LAST)
|
547
|
epg_flag = MM_EPG_ENABLE;
|
548
|
modname = epg_flag >= 0 ? modnames[epg_flag] : NULL;
|
549
|
}
|
550
|
|
551
|
if (epg_flag < 0 || epg_flag == MM_EPG_DISABLE) {
|
552
|
if (tvhtrace_enabled()) {
|
553
|
mpegts_mux_nice_name(mm, name, sizeof(name));
|
554
|
tvhtrace(LS_EPGGRAB, "epg mux %s is disabled, skipping", name);
|
555
|
}
|
556
|
goto done;
|
557
|
}
|
558
|
|
559
|
/* Check we have modules attached and enabled */
|
560
|
i = r = 0;
|
561
|
LIST_FOREACH(map, &om->om_modules, om_link) {
|
562
|
if (map->om_module->tune(map, om, mm)) {
|
563
|
i++;
|
564
|
if (modname && !strcmp(modname, map->om_module->id))
|
565
|
r = 1;
|
566
|
}
|
567
|
}
|
568
|
if ((i == 0 || (r == 0 && modname)) && epg_flag != MM_EPG_FORCE) {
|
569
|
mpegts_mux_nice_name(mm, name, sizeof(name));
|
570
|
tvhdebug(LS_EPGGRAB, "no OTA modules active for %s, check again next time", name);
|
571
|
goto done;
|
572
|
}
|
573
|
|
574
|
/* Some init stuff */
|
575
|
free(om->om_force_modname);
|
576
|
om->om_force_modname = modname ? strdup(modname) : NULL;
|
577
|
|
578
|
/* Subscribe to the mux */
|
579
|
om->om_requeue = 1;
|
580
|
if ((r = mpegts_mux_subscribe(mm, NULL, "epggrab",
|
581
|
SUBSCRIPTION_PRIO_EPG,
|
582
|
SUBSCRIPTION_EPG |
|
583
|
SUBSCRIPTION_ONESHOT |
|
584
|
SUBSCRIPTION_TABLES))) {
|
585
|
if (r != SM_CODE_NO_ADAPTERS) {
|
586
|
if (tvhtrace_enabled()) {
|
587
|
mpegts_mux_nice_name(mm, name, sizeof(name));
|
588
|
tvhtrace(LS_EPGGRAB, "subscription failed for %s (result %d)", name, r);
|
589
|
}
|
590
|
TAILQ_INSERT_TAIL(&epggrab_ota_pending, om, om_q_link);
|
591
|
om->om_q_type = EPGGRAB_OTA_MUX_PENDING;
|
592
|
if (r == SM_CODE_NO_FREE_ADAPTER)
|
593
|
net->failed = 1;
|
594
|
if (first == NULL)
|
595
|
first = om;
|
596
|
} else {
|
597
|
if (tvhtrace_enabled()) {
|
598
|
mpegts_mux_nice_name(mm, name, sizeof(name));
|
599
|
tvhtrace(LS_EPGGRAB, "no free adapter for %s (subscribe)", name);
|
600
|
}
|
601
|
net->fatal = 1;
|
602
|
}
|
603
|
} else {
|
604
|
if (tvhtrace_enabled()) {
|
605
|
mpegts_mux_nice_name(mm, name, sizeof(name));
|
606
|
tvhtrace(LS_EPGGRAB, "mux %s (%p), started", name, mm);
|
607
|
}
|
608
|
kick = 0;
|
609
|
/* note: it is possible that the mux_start listener is not called */
|
610
|
/* for reshared mux subscriptions, so call it (maybe second time) here.. */
|
611
|
epggrab_mux_start(mm, NULL);
|
612
|
}
|
613
|
|
614
|
done:
|
615
|
om = TAILQ_FIRST(&epggrab_ota_pending);
|
616
|
if (networks_count < ARRAY_SIZE(networks) && om && first != om)
|
617
|
goto next_one;
|
618
|
if (kick)
|
619
|
epggrab_ota_kick(64); /* a random number? */
|
620
|
|
621
|
if (tvhtrace_enabled()) {
|
622
|
i = r = 0;
|
623
|
RB_FOREACH(om, &epggrab_ota_all, om_global_link)
|
624
|
i++;
|
625
|
TAILQ_FOREACH(om, &epggrab_ota_pending, om_q_link)
|
626
|
r++;
|
627
|
tvhtrace(LS_EPGGRAB, "mux stats - all %i pending %i", i, r);
|
628
|
}
|
629
|
}
|
630
|
|
631
|
/*
|
632
|
* Start times management
|
633
|
*/
|
634
|
|
635
|
static void
|
636
|
epggrab_ota_start_cb ( void *p );
|
637
|
|
638
|
static void
|
639
|
epggrab_ota_next_arm( time_t next )
|
640
|
{
|
641
|
tvhtrace(LS_EPGGRAB, "next ota start event in %li seconds", next - time(NULL));
|
642
|
gtimer_arm_absn(&epggrab_ota_start_timer, epggrab_ota_start_cb, NULL, next);
|
643
|
dbus_emit_signal_s64("/epggrab/ota", "next", next);
|
644
|
}
|
645
|
|
646
|
static void
|
647
|
epggrab_ota_start_cb ( void *p )
|
648
|
{
|
649
|
time_t next;
|
650
|
|
651
|
tvhtrace(LS_EPGGRAB, "ota start callback");
|
652
|
|
653
|
epggrab_ota_pending_flag = 1;
|
654
|
|
655
|
epggrab_ota_kick(1);
|
656
|
|
657
|
pthread_mutex_lock(&epggrab_ota_mutex);
|
658
|
if (!cron_multi_next(epggrab_ota_cron_multi, gclk(), &next))
|
659
|
epggrab_ota_next_arm(next);
|
660
|
else
|
661
|
tvhwarn(LS_EPGGRAB, "ota cron config invalid or unset");
|
662
|
pthread_mutex_unlock(&epggrab_ota_mutex);
|
663
|
}
|
664
|
|
665
|
static void
|
666
|
epggrab_ota_arm ( time_t last )
|
667
|
{
|
668
|
time_t next;
|
669
|
|
670
|
pthread_mutex_lock(&epggrab_ota_mutex);
|
671
|
|
672
|
if (!cron_multi_next(epggrab_ota_cron_multi, time(NULL), &next)) {
|
673
|
/* do not trigger the next EPG scan for 1/2 hour */
|
674
|
if (last != (time_t)-1 && last + 1800 > next)
|
675
|
next = last + 1800;
|
676
|
epggrab_ota_next_arm(next);
|
677
|
} else {
|
678
|
tvhwarn(LS_EPGGRAB, "ota cron config invalid or unset");
|
679
|
}
|
680
|
|
681
|
pthread_mutex_unlock(&epggrab_ota_mutex);
|
682
|
}
|
683
|
|
684
|
/*
|
685
|
* Service management
|
686
|
*/
|
687
|
|
688
|
static void
|
689
|
epggrab_ota_service_trace ( epggrab_ota_mux_t *ota,
|
690
|
epggrab_ota_svc_link_t *svcl,
|
691
|
const char *op )
|
692
|
{
|
693
|
char buf[256];
|
694
|
mpegts_mux_t *mm;
|
695
|
mpegts_service_t *svc;
|
696
|
|
697
|
if (!tvhtrace_enabled())
|
698
|
return;
|
699
|
|
700
|
mm = mpegts_mux_find(ota->om_mux_uuid);
|
701
|
svc = mpegts_service_find_by_uuid(svcl->uuid);
|
702
|
if (mm && svc) {
|
703
|
mpegts_mux_nice_name(mm, buf, sizeof(buf));
|
704
|
tvhtrace(LS_EPGGRAB, "ota %s %s service %s", buf, op, svc->s_nicename);
|
705
|
} else if (tvheadend_is_running())
|
706
|
tvhtrace(LS_EPGGRAB, "ota %s, problem? (%p %p)", op, mm, svc);
|
707
|
}
|
708
|
|
709
|
void
|
710
|
epggrab_ota_service_add ( epggrab_ota_map_t *map, epggrab_ota_mux_t *ota,
|
711
|
const char *uuid, int save )
|
712
|
{
|
713
|
epggrab_ota_svc_link_t *svcl;
|
714
|
|
715
|
if (uuid == NULL || !atomic_get(&epggrab_ota_running))
|
716
|
return;
|
717
|
SKEL_ALLOC(epggrab_svc_link_skel);
|
718
|
epggrab_svc_link_skel->uuid = (char *)uuid;
|
719
|
svcl = RB_INSERT_SORTED(&map->om_svcs, epggrab_svc_link_skel, link, om_svcl_cmp);
|
720
|
if (svcl == NULL) {
|
721
|
svcl = epggrab_svc_link_skel;
|
722
|
SKEL_USED(epggrab_svc_link_skel);
|
723
|
svcl->uuid = strdup(uuid);
|
724
|
if (save)
|
725
|
ota->om_save = 1;
|
726
|
epggrab_ota_service_trace(ota, svcl, "add new");
|
727
|
}
|
728
|
svcl->last_tune_count = map->om_tune_count;
|
729
|
}
|
730
|
|
731
|
void
|
732
|
epggrab_ota_service_del ( epggrab_ota_map_t *map, epggrab_ota_mux_t *ota,
|
733
|
epggrab_ota_svc_link_t *svcl, int save )
|
734
|
{
|
735
|
if (svcl == NULL || (!atomic_get(&epggrab_ota_running) && save))
|
736
|
return;
|
737
|
epggrab_ota_service_trace(ota, svcl, "delete");
|
738
|
RB_REMOVE(&map->om_svcs, svcl, link);
|
739
|
free(svcl->uuid);
|
740
|
free(svcl);
|
741
|
if (save)
|
742
|
ota->om_save = 1;
|
743
|
}
|
744
|
|
745
|
/* **************************************************************************
|
746
|
* Config
|
747
|
* *************************************************************************/
|
748
|
|
749
|
static void
|
750
|
epggrab_ota_save ( epggrab_ota_mux_t *ota )
|
751
|
{
|
752
|
epggrab_ota_map_t *map;
|
753
|
epggrab_ota_svc_link_t *svcl;
|
754
|
htsmsg_t *e, *l, *l2, *c = htsmsg_create_map();
|
755
|
|
756
|
ota->om_save = 0;
|
757
|
htsmsg_add_u32(c, "complete", ota->om_complete);
|
758
|
l = htsmsg_create_list();
|
759
|
LIST_FOREACH(map, &ota->om_modules, om_link) {
|
760
|
e = htsmsg_create_map();
|
761
|
htsmsg_add_str(e, "id", map->om_module->id);
|
762
|
if (RB_FIRST(&map->om_svcs)) {
|
763
|
l2 = htsmsg_create_list();
|
764
|
RB_FOREACH(svcl, &map->om_svcs, link)
|
765
|
if (svcl->uuid)
|
766
|
htsmsg_add_str(l2, NULL, svcl->uuid);
|
767
|
htsmsg_add_msg(e, "services", l2);
|
768
|
}
|
769
|
htsmsg_add_msg(l, NULL, e);
|
770
|
}
|
771
|
htsmsg_add_msg(c, "modules", l);
|
772
|
hts_settings_save(c, "epggrab/otamux/%s", ota->om_mux_uuid);
|
773
|
htsmsg_destroy(c);
|
774
|
}
|
775
|
|
776
|
static void
|
777
|
epggrab_ota_load_one
|
778
|
( const char *uuid, htsmsg_t *c )
|
779
|
{
|
780
|
htsmsg_t *l, *l2, *e;
|
781
|
htsmsg_field_t *f, *f2;
|
782
|
mpegts_mux_t *mm;
|
783
|
epggrab_module_ota_t *mod;
|
784
|
epggrab_ota_mux_t *ota;
|
785
|
epggrab_ota_map_t *map;
|
786
|
const char *id;
|
787
|
|
788
|
mm = mpegts_mux_find(uuid);
|
789
|
if (!mm) {
|
790
|
hts_settings_remove("epggrab/otamux/%s", uuid);
|
791
|
return;
|
792
|
}
|
793
|
if (tvhtrace_enabled()) {
|
794
|
char name[256];
|
795
|
mpegts_mux_nice_name(mm, name, sizeof(name));
|
796
|
tvhtrace(LS_EPGGRAB, "loading config for %s", name);
|
797
|
}
|
798
|
|
799
|
ota = calloc(1, sizeof(epggrab_ota_mux_t));
|
800
|
ota->om_mux_uuid = strdup(uuid);
|
801
|
if (RB_INSERT_SORTED(&epggrab_ota_all, ota, om_global_link, om_id_cmp)) {
|
802
|
free(ota->om_mux_uuid);
|
803
|
free(ota);
|
804
|
return;
|
805
|
}
|
806
|
ota->om_complete = htsmsg_get_u32_or_default(c, "complete", 0) != 0;
|
807
|
|
808
|
if (!(l = htsmsg_get_list(c, "modules"))) return;
|
809
|
HTSMSG_FOREACH(f, l) {
|
810
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
811
|
if (!(id = htsmsg_get_str(e, "id"))) continue;
|
812
|
if (!(mod = (epggrab_module_ota_t*)epggrab_module_find_by_id(id)))
|
813
|
continue;
|
814
|
|
815
|
map = calloc(1, sizeof(epggrab_ota_map_t));
|
816
|
RB_INIT(&map->om_svcs);
|
817
|
map->om_module = mod;
|
818
|
if ((l2 = htsmsg_get_list(e, "services")) != NULL) {
|
819
|
HTSMSG_FOREACH(f2, l2)
|
820
|
epggrab_ota_service_add(map, ota, htsmsg_field_get_str(f2), 0);
|
821
|
}
|
822
|
LIST_INSERT_HEAD(&ota->om_modules, map, om_link);
|
823
|
}
|
824
|
}
|
825
|
|
826
|
void
|
827
|
epggrab_ota_init ( void )
|
828
|
{
|
829
|
htsmsg_t *c, *m;
|
830
|
htsmsg_field_t *f;
|
831
|
char path[1024];
|
832
|
struct stat st;
|
833
|
|
834
|
epggrab_conf.ota_initial = 1;
|
835
|
epggrab_conf.ota_timeout = 600;
|
836
|
epggrab_conf.ota_cron = strdup("# Default config (02:04 and 14:04 everyday)\n4 2 * * *\n4 14 * * *");
|
837
|
epggrab_ota_cron_multi = cron_multi_set(epggrab_conf.ota_cron);
|
838
|
epggrab_ota_pending_flag = 0;
|
839
|
|
840
|
RB_INIT(&epggrab_ota_all);
|
841
|
TAILQ_INIT(&epggrab_ota_pending);
|
842
|
TAILQ_INIT(&epggrab_ota_active);
|
843
|
|
844
|
pthread_mutex_init(&epggrab_ota_mutex, NULL);
|
845
|
|
846
|
/* Add listener */
|
847
|
static mpegts_listener_t ml = {
|
848
|
.ml_mux_start = epggrab_mux_start,
|
849
|
.ml_mux_stop = epggrab_mux_stop,
|
850
|
};
|
851
|
mpegts_add_listener(&ml);
|
852
|
|
853
|
/* Delete old config */
|
854
|
hts_settings_buildpath(path, sizeof(path), "epggrab/otamux");
|
855
|
if (!lstat(path, &st))
|
856
|
if (!S_ISDIR(st.st_mode))
|
857
|
hts_settings_remove("epggrab/otamux");
|
858
|
|
859
|
atomic_set(&epggrab_ota_running, 1);
|
860
|
|
861
|
/* Load config */
|
862
|
if ((c = hts_settings_load_r(1, "epggrab/otamux"))) {
|
863
|
HTSMSG_FOREACH(f, c) {
|
864
|
if (!(m = htsmsg_field_get_map(f))) continue;
|
865
|
epggrab_ota_load_one(f->hmf_name, m);
|
866
|
}
|
867
|
htsmsg_destroy(c);
|
868
|
}
|
869
|
}
|
870
|
|
871
|
void
|
872
|
epggrab_ota_trigger ( int secs )
|
873
|
{
|
874
|
lock_assert(&global_lock);
|
875
|
|
876
|
/* notify another system layers, that we will do EPG OTA */
|
877
|
secs = MIN(1, MAX(secs, 7*24*3600));
|
878
|
dbus_emit_signal_s64("/epggrab/ota", "next", time(NULL) + secs);
|
879
|
epggrab_ota_pending_flag = 1;
|
880
|
epggrab_ota_kick(secs);
|
881
|
}
|
882
|
|
883
|
void
|
884
|
epggrab_ota_post ( void )
|
885
|
{
|
886
|
time_t t = (time_t)-1;
|
887
|
|
888
|
/* Init timer (call after full init - wait for network tuners) */
|
889
|
if (epggrab_conf.ota_initial) {
|
890
|
epggrab_ota_trigger(15);
|
891
|
t = time(NULL);
|
892
|
}
|
893
|
|
894
|
/* arm the first scheduled time */
|
895
|
epggrab_ota_arm(t);
|
896
|
}
|
897
|
|
898
|
static void
|
899
|
epggrab_ota_free ( epggrab_ota_head_t *head, epggrab_ota_mux_t *ota )
|
900
|
{
|
901
|
epggrab_ota_map_t *map;
|
902
|
epggrab_ota_svc_link_t *svcl;
|
903
|
|
904
|
mtimer_disarm(&ota->om_timer);
|
905
|
mtimer_disarm(&ota->om_data_timer);
|
906
|
if (head != NULL)
|
907
|
TAILQ_REMOVE(head, ota, om_q_link);
|
908
|
RB_REMOVE(&epggrab_ota_all, ota, om_global_link);
|
909
|
while ((map = LIST_FIRST(&ota->om_modules)) != NULL) {
|
910
|
LIST_REMOVE(map, om_link);
|
911
|
while ((svcl = RB_FIRST(&map->om_svcs)) != NULL)
|
912
|
epggrab_ota_service_del(map, ota, svcl, 0);
|
913
|
free(map);
|
914
|
}
|
915
|
free(ota->om_mux_uuid);
|
916
|
free(ota->om_force_modname);
|
917
|
free(ota);
|
918
|
}
|
919
|
|
920
|
void
|
921
|
epggrab_ota_shutdown ( void )
|
922
|
{
|
923
|
epggrab_ota_mux_t *ota;
|
924
|
|
925
|
atomic_set(&epggrab_ota_running, 0);
|
926
|
while ((ota = TAILQ_FIRST(&epggrab_ota_active)) != NULL)
|
927
|
epggrab_ota_free(&epggrab_ota_active, ota);
|
928
|
while ((ota = TAILQ_FIRST(&epggrab_ota_pending)) != NULL)
|
929
|
epggrab_ota_free(&epggrab_ota_pending, ota);
|
930
|
while ((ota = RB_FIRST(&epggrab_ota_all)) != NULL)
|
931
|
epggrab_ota_free(NULL, ota);
|
932
|
SKEL_FREE(epggrab_ota_mux_skel);
|
933
|
SKEL_FREE(epggrab_svc_link_skel);
|
934
|
free(epggrab_ota_cron_multi);
|
935
|
epggrab_ota_cron_multi = NULL;
|
936
|
}
|
937
|
|
938
|
/*
|
939
|
* Global configuration handlers
|
940
|
*/
|
941
|
|
942
|
void
|
943
|
epggrab_ota_set_cron ( void )
|
944
|
{
|
945
|
lock_assert(&global_lock);
|
946
|
|
947
|
pthread_mutex_lock(&epggrab_ota_mutex);
|
948
|
free(epggrab_ota_cron_multi);
|
949
|
epggrab_ota_cron_multi = cron_multi_set(epggrab_conf.ota_cron);
|
950
|
pthread_mutex_unlock(&epggrab_ota_mutex);
|
951
|
epggrab_ota_arm((time_t)-1);
|
952
|
}
|
953
|
|
954
|
/******************************************************************************
|
955
|
* Editor Configuration
|
956
|
*
|
957
|
* vim:sts=2:ts=2:sw=2:et
|
958
|
*****************************************************************************/
|