/* * Electronic Program Guide - Common functions * Copyright (C) 2007 Andreas Öman * * 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 3 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, see . */ #include #include #include #include #include #include "tvhead.h" #include "channels.h" #include "epg.h" #define EPG_MAX_AGE 86400 epg_content_group_t *epg_content_groups[16]; static int e_ch_cmp(const event_t *a, const event_t *b) { return a->e_start - b->e_start; } /** * */ void epg_event_set_title(event_t *e, const char *title) { free((void *)e->e_title); e->e_title = strdup(title); } /** * */ void epg_event_set_desc(event_t *e, const char *desc) { free((void *)e->e_desc); e->e_desc = strdup(desc); } /** * */ void epg_event_set_content_type(event_t *e, epg_content_type_t *ect) { if(e->e_content_type != NULL) LIST_REMOVE(e, e_content_type_link); e->e_content_type = ect; if(ect != NULL) LIST_INSERT_HEAD(&ect->ect_events, e, e_content_type_link); } /** * */ event_t * epg_event_find_by_start(channel_t *ch, time_t start, int create) { static event_t *skel, *e; lock_assert(&global_lock); if(skel == NULL) skel = calloc(1, sizeof(event_t)); skel->e_start = start; if(!create) return RB_FIND(&ch->ch_epg_events, skel, e_channel_link, e_ch_cmp); e = RB_INSERT_SORTED(&ch->ch_epg_events, skel, e_channel_link, e_ch_cmp); if(e == NULL) { /* New entry was inserted */ e = skel; skel = NULL; } return e; } /** * */ event_t * epg_event_find_by_time(channel_t *ch, time_t t) { event_t skel, *e; skel.e_start = t; e = RB_FIND_LE(&ch->ch_epg_events, &skel, e_channel_link, e_ch_cmp); if(e == NULL || e->e_start + e->e_duration < t) return NULL; return e; } /** * */ void epg_event_destroy(event_t *e) { channel_t *ch = e->e_channel; RB_REMOVE(&ch->ch_epg_events, e, e_channel_link); if(e->e_content_type != NULL) LIST_REMOVE(e, e_content_type_link); free((void *)e->e_title); free((void *)e->e_desc); free(e); } #if 0 /** * */ void epg_channel_maintain() { channel_t *ch; event_t *e, *cur; time_t now; dtimer_arm(&epg_channel_maintain_timer, epg_channel_maintain, NULL, 5); now = dispatch_clock; RB_FOREACH(ch, &channel_name_tree, ch_name_link) { /* Age out any old events */ e = TAILQ_FIRST(&ch->ch_epg_events); if(e != NULL && e->e_start + e->e_duration < now - EPG_MAX_AGE) epg_event_destroy(ch, e); cur = ch->ch_epg_cur_event; e = ch->ch_epg_cur_event; if(e == NULL) { epg_locate_current_event(ch, now); continue; } if(now >= e->e_start && now < e->e_start + e->e_duration) continue; e = TAILQ_NEXT(e, e_channel_link); if(e != NULL && now >= e->e_start && now < e->e_start + e->e_duration) { epg_set_current_event(ch, e); continue; } epg_locate_current_event(ch, now); } } #endif /** * */ void epg_destroy_by_channel(channel_t *ch) { event_t *e; while((e = ch->ch_epg_events.root) != NULL) epg_event_destroy(e); } /** * */ static const char *groupnames[16] = { [0] = "Unclassified", [1] = "Movie / Drama", [2] = "News / Current affairs", [3] = "Show / Games", [4] = "Sports", [5] = "Children's/Youth", [6] = "Music", [7] = "Art/Culture", [8] = "Social/Political issues/Economics", [9] = "Education/Science/Factual topics", [10] = "Leisure hobbies", [11] = "Special characteristics", }; /** * Find a content type */ epg_content_type_t * epg_content_type_find_by_dvbcode(uint8_t dvbcode) { epg_content_group_t *ecg; epg_content_type_t *ect; int group = dvbcode >> 4; int type = dvbcode & 0xf; char buf[20]; ecg = epg_content_groups[group]; if(ecg == NULL) { ecg = epg_content_groups[group] = calloc(1, sizeof(epg_content_group_t)); ecg->ecg_name = groupnames[group]; } ect = ecg->ecg_types[type]; if(ect == NULL) { ect = ecg->ecg_types[type] = calloc(1, sizeof(epg_content_type_t)); ect->ect_group = ecg; snprintf(buf, sizeof(buf), "type%d", type); ect->ect_name = strdup(buf); } return ect; } /** * */ epg_content_group_t * epg_content_group_find_by_name(const char *name) { epg_content_group_t *ecg; int i; for(i = 0; i < 16; i++) { ecg = epg_content_groups[i]; if(ecg->ecg_name && !strcmp(name, ecg->ecg_name)) return ecg; } return NULL; } /** * Given the arguments, search all EPG events and enlist them * * XXX: Optimize if we know channel, group, etc */ #if 0 int epg_search(struct event_list *h, const char *title, epg_content_group_t *s_ecg, channel_t *s_ch) { channel_t *ch; event_t *e; int num = 0; regex_t preg; if(title != NULL && regcomp(&preg, title, REG_ICASE | REG_EXTENDED | REG_NOSUB)) return -1; RB_FOREACH(ch, &channel_name_tree, ch_name_link) { if(LIST_FIRST(&ch->ch_transports) == NULL) continue; if(s_ch != NULL && s_ch != ch) continue; TAILQ_FOREACH(e, &ch->ch_epg_events, e_channel_link) { if(e->e_start + e->e_duration < dispatch_clock) continue; /* old */ if(s_ecg != NULL) { if(e->e_content_type == NULL) continue; if(e->e_content_type->ect_group != s_ecg) continue; } if(title != NULL) { if(regexec(&preg, e->e_title, 0, NULL, 0)) continue; } LIST_INSERT_HEAD(h, e, e_tmp_link); num++; } } if(title != NULL) regfree(&preg); return num; } #endif /* * */ void epg_init(void) { int i; for(i = 0x0; i < 0x100; i+=16) epg_content_type_find_by_dvbcode(i); }