1 /*
2 * Service Mapper functions
3 * Copyright (C) 2007 Andreas Öman
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 <assert.h>
20 #include <pthread.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26
27 #include "tvheadend.h"
28 #include "channels.h"
29 #include "subscriptions.h"
30 #include "service_mapper.h"
31 #include "streaming.h"
32 #include "service.h"
33 #include "profile.h"
34 #include "bouquet.h"
35 #include "api.h"
36
37 typedef struct service_mapper_item {
38 TAILQ_ENTRY(service_mapper_item) link;
39 service_t *s;
40 service_mapper_conf_t conf;
41 } service_mapper_item_t;
42
43 static service_mapper_status_t service_mapper_stat;
44 static tvh_cond_t service_mapper_cond;
45 static TAILQ_HEAD(, service_mapper_item) service_mapper_queue;
46 service_mapper_t service_mapper_conf;
47
48 static void *service_mapper_thread ( void *p );
49
50 /*
51 * Get status
52 */
53 service_mapper_status_t
service_mapper_status(void)54 service_mapper_status ( void )
55 {
56 return service_mapper_stat;
57 }
58
59 /*
60 * Start a new mapping
61 */
62 void
service_mapper_start(const service_mapper_conf_t * conf,htsmsg_t * uuids)63 service_mapper_start ( const service_mapper_conf_t *conf, htsmsg_t *uuids )
64 {
65 int e, tr, qd = 0;
66 service_mapper_item_t *smi;
67 service_t *s;
68 char ubuf[UUID_HEX_SIZE];
69
70 /* Reset stat counters */
71 if (TAILQ_EMPTY(&service_mapper_queue))
72 service_mapper_reset_stats();
73
74 /* Check each service */
75 TAILQ_FOREACH(s, &service_all, s_all_link) {
76 if (uuids) {
77 htsmsg_field_t *f;
78 const char *str;
79 const char *uuid = idnode_uuid_as_str(&s->s_id, ubuf);
80 HTSMSG_FOREACH(f, uuids) {
81 if (!(str = htsmsg_field_get_str(f))) continue;
82 if (!strcmp(str, uuid)) break;
83 }
84 if (!f) continue;
85 }
86 tvhtrace(LS_SERVICE_MAPPER, "check service %s (%s)",
87 s->s_nicename, idnode_uuid_as_str(&s->s_id, ubuf));
88
89 /* Already mapped (or in progress) */
90 if (s->s_sm_onqueue) continue;
91 if (LIST_FIRST(&s->s_channels)) continue;
92 tvhtrace(LS_SERVICE_MAPPER, " not mapped");
93 service_mapper_stat.total++;
94 service_mapper_stat.ignore++;
95
96 /* Disabled */
97 if (!s->s_is_enabled(s, 0)) continue;
98 tvhtrace(LS_SERVICE_MAPPER, " enabled");
99
100 /* Get service info */
101 pthread_mutex_lock(&s->s_stream_mutex);
102 e = service_is_encrypted(s);
103 tr = service_is_tv(s) || service_is_radio(s);
104 pthread_mutex_unlock(&s->s_stream_mutex);
105
106 /* Skip non-TV / Radio */
107 if (!tr) continue;
108 tvhtrace(LS_SERVICE_MAPPER, " radio or tv");
109
110 /* Skip encrypted */
111 if (!conf->encrypted && e) continue;
112 service_mapper_stat.ignore--;
113
114 /* Queue */
115 if (conf->check_availability) {
116 tvhtrace(LS_SERVICE_MAPPER, " queue for checking");
117 qd = 1;
118 smi = malloc(sizeof(*smi));
119 smi->s = s;
120 smi->conf = *conf;
121 TAILQ_INSERT_TAIL(&service_mapper_queue, smi, link);
122 s->s_sm_onqueue = 1;
123
124 /* Process */
125 } else {
126 tvhtrace(LS_SERVICE_MAPPER, " process");
127 service_mapper_process(conf, s, NULL);
128 }
129 }
130
131 /* Notify */
132 api_service_mapper_notify();
133
134 /* Signal */
135 if (qd) tvh_cond_signal(&service_mapper_cond, 0);
136 }
137
138 /*
139 * Stop everything
140 */
141 void
service_mapper_stop(void)142 service_mapper_stop ( void )
143 {
144 service_mapper_item_t *smi;
145 while ((smi = TAILQ_FIRST(&service_mapper_queue))) {
146 service_mapper_stat.total--;
147 TAILQ_REMOVE(&service_mapper_queue, smi, link);
148 free(smi);
149 }
150
151 /* Notify */
152 api_service_mapper_notify();
153 }
154
155 /*
156 * Remove service
157 */
158 void
service_mapper_remove(service_t * s)159 service_mapper_remove ( service_t *s )
160 {
161 service_mapper_item_t *smi;
162
163 if (s->s_sm_onqueue) {
164 TAILQ_FOREACH(smi, &service_mapper_queue, link)
165 if (smi->s == s) break;
166 assert(smi);
167 TAILQ_REMOVE(&service_mapper_queue, smi, link);
168 free(smi);
169 s->s_sm_onqueue = 0;
170 }
171
172 /* Notify */
173 api_service_mapper_notify();
174 }
175
176 /*
177 * Link service and channel
178 */
179 int
service_mapper_link(service_t * s,channel_t * c,void * origin)180 service_mapper_link ( service_t *s, channel_t *c, void *origin )
181 {
182 idnode_list_mapping_t *ilm;
183
184 ilm = idnode_list_link(&s->s_id, &s->s_channels,
185 &c->ch_id, &c->ch_services,
186 origin, 2);
187 if (ilm) {
188 service_mapped(s);
189 return 1;
190 }
191 return 0;
192 }
193
194 int
service_mapper_create(idnode_t * s,idnode_t * c,void * origin)195 service_mapper_create ( idnode_t *s, idnode_t *c, void *origin )
196 {
197 return service_mapper_link((service_t *)s, (channel_t *)c, origin);
198 }
199
200 /*
201 * Process a service
202 */
203 channel_t *
service_mapper_process(const service_mapper_conf_t * conf,service_t * s,bouquet_t * bq)204 service_mapper_process
205 ( const service_mapper_conf_t *conf, service_t *s, bouquet_t *bq )
206 {
207 channel_t *chn = NULL;
208 const char *name, *tagname;
209 htsmsg_field_t *f;
210 htsmsg_t *m;
211
212 /* Ignore */
213 if (s->s_status == SERVICE_ZOMBIE) {
214 if (!bq)
215 service_mapper_stat.ignore++;
216 goto exit;
217 }
218
219 /* Safety check (in-case something has been mapped manually in the interim) */
220 if (!bq && LIST_FIRST(&s->s_channels)) {
221 service_mapper_stat.ignore++;
222 goto exit;
223 }
224
225 /* Find existing channel */
226 name = service_get_channel_name(s);
227 if (!bq && conf->merge_same_name && name && *name)
228 chn = channel_find_by_name(name);
229 if (!chn) {
230 chn = channel_create(NULL, NULL, NULL);
231 chn->ch_bouquet = bq;
232 }
233
234 /* Map */
235 if (chn) {
236 const char *prov;
237 service_mapper_link(s, chn, chn);
238
239 /* Type tags */
240 if (conf->type_tags) {
241 if (service_is_uhdtv(s)) {
242 channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn);
243 channel_tag_map(channel_tag_find_by_name("UHDTV", 1), chn, chn);
244 } else if (service_is_hdtv(s)) {
245 channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn);
246 channel_tag_map(channel_tag_find_by_name("HDTV", 1), chn, chn);
247 } else if (service_is_sdtv(s)) {
248 channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn);
249 channel_tag_map(channel_tag_find_by_name("SDTV", 1), chn, chn);
250 } else if (service_is_radio(s)) {
251 if (!channel_tag_map(channel_tag_find_by_name("Radio", 0), chn, chn))
252 channel_tag_map(channel_tag_find_by_name("Radio channels", 1), chn, chn);
253 }
254 }
255
256 /* Custom tags */
257 if (s->s_channel_tags && (m = s->s_channel_tags(s)) != NULL)
258 HTSMSG_FOREACH(f, m)
259 if ((tagname = htsmsg_field_get_str(f)) != NULL)
260 channel_tag_map(channel_tag_find_by_name(tagname, 1), chn, chn);
261
262 /* Provider */
263 if (conf->provider_tags)
264 if ((prov = s->s_provider_name(s)))
265 channel_tag_map(channel_tag_find_by_name(prov, 1), chn, chn);
266
267 /* Network */
268 if (conf->network_tags) {
269 source_info_t si;
270 s->s_setsourceinfo(s, &si);
271 channel_tag_map(channel_tag_find_by_name(si.si_network, 1), chn, chn);
272 }
273
274 /* save */
275 idnode_changed(&chn->ch_id);
276 }
277 if (!bq) {
278 service_mapper_stat.ok++;
279 tvhinfo(LS_SERVICE_MAPPER, "%s: success", s->s_nicename);
280 } else {
281 tvhinfo(LS_BOUQUET, "%s: mapped service from %s", s->s_nicename, bq->bq_name ?: "<unknown>");
282 }
283
284 /* Remove */
285 exit:
286 service_mapper_remove(s);
287 return chn;
288 }
289
290 /**
291 *
292 */
293 static void *
service_mapper_thread(void * aux)294 service_mapper_thread ( void *aux )
295 {
296 service_t *s;
297 service_mapper_item_t *smi;
298 profile_chain_t prch;
299 th_subscription_t *sub;
300 int run, working = 0;
301 streaming_queue_t *sq;
302 streaming_message_t *sm;
303 const char *err = NULL;
304 uint64_t timeout;
305
306 profile_chain_init(&prch, NULL, NULL, 1);
307 prch.prch_st = &prch.prch_sq.sq_st;
308 sq = &prch.prch_sq;
309
310 pthread_mutex_lock(&global_lock);
311
312 while (tvheadend_is_running()) {
313
314 /* Wait for work */
315 while (!(smi = TAILQ_FIRST(&service_mapper_queue))) {
316 if (working) {
317 working = 0;
318 tvhinfo(LS_SERVICE_MAPPER, "idle");
319 }
320 tvh_cond_wait(&service_mapper_cond, &global_lock);
321 if (!tvheadend_is_running())
322 break;
323 }
324 if (!tvheadend_is_running())
325 break;
326 s = smi->s;
327 service_mapper_remove(s);
328
329 if (!working) {
330 working = 1;
331 tvhinfo(LS_SERVICE_MAPPER, "starting");
332 }
333
334 /* Subscribe */
335 tvhinfo(LS_SERVICE_MAPPER, "checking %s", s->s_nicename);
336 prch.prch_id = s;
337 sub = subscription_create_from_service(&prch, NULL,
338 SUBSCRIPTION_PRIO_MAPPER,
339 "service_mapper",
340 SUBSCRIPTION_PACKET,
341 NULL, NULL, "service_mapper", NULL);
342
343 /* Failed */
344 if (!sub) {
345 tvhinfo(LS_SERVICE_MAPPER, "%s: could not subscribe", s->s_nicename);
346 continue;
347 }
348
349 tvhinfo(LS_SERVICE_MAPPER, "waiting for input");
350 service_ref(s);
351 service_mapper_stat.active = s;
352 api_service_mapper_notify();
353 pthread_mutex_unlock(&global_lock);
354
355 /* Wait */
356 run = 1;
357 pthread_mutex_lock(&sq->sq_mutex);
358 timeout = mclk() + sec2mono(30);
359 while(tvheadend_is_running() && run) {
360
361 if (timeout < mclk()) {
362 run = 0;
363 err = streaming_code2txt(SM_CODE_BAD_SOURCE);
364 break;
365 }
366
367 /* Wait for message */
368 while((sm = TAILQ_FIRST(&sq->sq_queue)) == NULL) {
369 tvh_cond_wait(&sq->sq_cond, &sq->sq_mutex);
370 if (!tvheadend_is_running())
371 break;
372 }
373 if (!tvheadend_is_running())
374 break;
375
376 streaming_queue_remove(sq, sm);
377 pthread_mutex_unlock(&sq->sq_mutex);
378
379 switch (sm->sm_type) {
380 case SMT_GRACE:
381 timeout += sec2mono(sm->sm_code);
382 break;
383 case SMT_PACKET:
384 run = 0;
385 err = NULL;
386 break;
387 case SMT_SERVICE_STATUS:
388 if(sm->sm_code & TSS_ERRORS) {
389 run = 0;
390 err = service_tss2text(sm->sm_code);
391 }
392 break;
393 case SMT_NOSTART:
394 run = 0;
395 err = streaming_code2txt(sm->sm_code);
396 break;
397 default:
398 break;
399 }
400
401 streaming_msg_free(sm);
402 pthread_mutex_lock(&sq->sq_mutex);
403 }
404 if (!tvheadend_is_running())
405 break;
406
407 streaming_queue_clear(&sq->sq_queue);
408 pthread_mutex_unlock(&sq->sq_mutex);
409
410 pthread_mutex_lock(&global_lock);
411 subscription_unsubscribe(sub, UNSUBSCRIBE_FINAL);
412
413 if(err) {
414 tvhinfo(LS_SERVICE_MAPPER, "%s: failed [reason: %s]", s->s_nicename, err);
415 service_mapper_stat.fail++;
416 } else
417 service_mapper_process(&smi->conf, s, NULL);
418
419 service_unref(s);
420 service_mapper_stat.active = NULL;
421 api_service_mapper_notify();
422 }
423
424 pthread_mutex_unlock(&global_lock);
425 profile_chain_close(&prch);
426 return NULL;
427 }
428
429 void
service_mapper_reset_stats(void)430 service_mapper_reset_stats (void)
431 {
432 service_mapper_stat.total = 0;
433 service_mapper_stat.ok = 0;
434 service_mapper_stat.ignore = 0;
435 service_mapper_stat.fail = 0;
436 service_mapper_stat.active = NULL;
437 }
438
439 /*
440 * Save settings
441 */
442 static htsmsg_t *
service_mapper_conf_class_save(idnode_t * self,char * filename,size_t fsize)443 service_mapper_conf_class_save ( idnode_t *self, char *filename, size_t fsize )
444 {
445 htsmsg_t *m;
446
447 m = htsmsg_create_map();
448 idnode_save(&service_mapper_conf.idnode, m);
449 snprintf(filename, fsize, "service_mapper/config");
450
451 if (!htsmsg_is_empty(service_mapper_conf.services))
452 service_mapper_start(&service_mapper_conf.d, service_mapper_conf.services);
453 htsmsg_destroy(service_mapper_conf.services);
454 service_mapper_conf.services = NULL;
455
456 return m;
457 }
458
459 /*
460 * Class
461 */
462
463 static const void *
service_mapper_services_get(void * obj)464 service_mapper_services_get ( void *obj )
465 {
466 return NULL;
467 }
468
469 static char *
service_mapper_services_rend(void * obj,const char * lang)470 service_mapper_services_rend ( void *obj, const char *lang )
471 {
472 return strdup("");
473 }
474
475 static int
service_mapper_services_set(void * obj,const void * p)476 service_mapper_services_set ( void *obj, const void *p )
477 {
478 service_mapper_t *sm = obj;
479 htsmsg_destroy(sm->services);
480 sm->services = htsmsg_copy((htsmsg_t *)p);
481 return 1;
482 }
483
484 static htsmsg_t *
service_mapper_services_enum(void * obj,const char * lang)485 service_mapper_services_enum ( void *obj, const char *lang )
486 {
487 htsmsg_t *e, *m = htsmsg_create_map();
488 htsmsg_add_str(m, "type", "api");
489 htsmsg_add_str(m, "uri", "service/list");
490 htsmsg_add_str(m, "event", "service");
491 e = htsmsg_create_map();
492 htsmsg_add_bool(e, "enum", 1);
493 htsmsg_add_msg(m, "params", e);
494 return m;
495 }
496
497 CLASS_DOC(service_mapper)
498
499 static const idclass_t service_mapper_conf_class = {
500 .ic_snode = &service_mapper_conf.idnode,
501 .ic_class = "service_mapper",
502 .ic_caption = N_("Service Mapping (Map services to channels)"),
503 .ic_doc = tvh_doc_service_mapper_class,
504 .ic_event = "service_mapper",
505 .ic_perm_def = ACCESS_ADMIN,
506 .ic_save = service_mapper_conf_class_save,
507 .ic_properties = (const property_t[]){
508 {
509 .type = PT_STR,
510 .islist = 1,
511 .id = "services",
512 .name = N_("Services"),
513 .desc = N_("Select/Selected services to map."),
514 .get = service_mapper_services_get,
515 .set = service_mapper_services_set,
516 .list = service_mapper_services_enum,
517 .rend = service_mapper_services_rend
518 },
519 {
520 .type = PT_BOOL,
521 .id = "check_availability",
522 .name = N_("Check availability"),
523 .desc = N_("Check services for availability. If enabled, "
524 "services that are not currently broadcasting (or "
525 "can't be decrypted) will be ignored. Leave disabled "
526 "if you want Tvheadend to also map offline services."),
527 .off = offsetof(service_mapper_t, d.check_availability),
528 .opts = PO_ADVANCED
529 },
530 {
531 .type = PT_BOOL,
532 .id = "encrypted",
533 .name = N_("Map encrypted services"),
534 .desc = N_("Ignore encryption flag, include encrypted services "
535 "anyway."),
536 .off = offsetof(service_mapper_t, d.encrypted),
537 },
538 {
539 .type = PT_BOOL,
540 .id = "merge_same_name",
541 .name = N_("Merge same name"),
542 .desc = N_("Merge services with the same name into one channel."),
543 .off = offsetof(service_mapper_t, d.merge_same_name),
544 },
545 {
546 .type = PT_BOOL,
547 .id = "type_tags",
548 .name = N_("Create type-based tags"),
549 .desc = N_("Create SDTV/HDTV/Radio tags."),
550 .off = offsetof(service_mapper_t, d.type_tags),
551 .opts = PO_ADVANCED
552 },
553 {
554 .type = PT_BOOL,
555 .id = "provider_tags",
556 .name = N_("Create provider name tags"),
557 .desc = N_("Create a provider name tag."),
558 .off = offsetof(service_mapper_t, d.provider_tags),
559 .opts = PO_ADVANCED
560 },
561 {
562 .type = PT_BOOL,
563 .id = "network_tags",
564 .name = N_("Create network name tags"),
565 .desc = N_("Create network name tags (set by provider)."),
566 .off = offsetof(service_mapper_t, d.network_tags),
567 .opts = PO_ADVANCED
568 },
569 {}
570 }
571 };
572
573 /*
574 *
575 */
576
577 pthread_t service_mapper_tid;
578
service_mapper_init(void)579 void service_mapper_init ( void )
580 {
581 htsmsg_t *m;
582
583 TAILQ_INIT(&service_mapper_queue);
584 idclass_register(&service_mapper_conf_class);
585 tvh_cond_init(&service_mapper_cond);
586 tvhthread_create(&service_mapper_tid, NULL, service_mapper_thread, NULL, "svcmap");
587
588 /* Defaults */
589 memset(&service_mapper_conf, 0, sizeof(service_mapper_conf));
590 service_mapper_conf.idnode.in_class = &service_mapper_conf_class;
591 service_mapper_conf.d.type_tags = 1;
592 service_mapper_conf.d.encrypted = 1;
593
594 /* Load settings */
595 if ((m = hts_settings_load("service_mapper/config"))) {
596 idnode_load(&service_mapper_conf.idnode, m);
597 htsmsg_destroy(m);
598 }
599 }
600
601
service_mapper_done(void)602 void service_mapper_done ( void )
603 {
604 tvh_cond_signal(&service_mapper_cond, 0);
605 pthread_join(service_mapper_tid, NULL);
606 htsmsg_destroy(service_mapper_conf.services);
607 service_mapper_conf.services = NULL;
608 }
609