Revamp EPG framework. Much simpler (and faster) now.

This commit is contained in:
Andreas Öman 2008-09-02 20:32:54 +00:00
parent fee21455dc
commit 3f66f6d810
7 changed files with 96 additions and 391 deletions

View file

@ -1,6 +1,6 @@
-include ../config.mak
SRCS = main.c access.c dtable.c tcp.c http.c notify.c
SRCS = main.c access.c dtable.c tcp.c http.c notify.c epg.c
SRCS += buffer.c channels.c subscriptions.c transports.c

View file

@ -146,7 +146,7 @@ channel_create(const char *name)
}
ch = calloc(1, sizeof(channel_t));
TAILQ_INIT(&ch->ch_epg_events);
RB_INIT(&ch->ch_epg_events);
channel_set_name(ch, name);

View file

@ -308,6 +308,10 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
char desc[5000];
epg_content_type_t *ect;
event_t *e;
lock_assert(&global_lock);
// printf("EIT!, tid = %x\n", tableid);
if(tableid < 0x4e || tableid > 0x6f || len < 11)
@ -356,6 +360,9 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(dllen > len)
break;
e = epg_event_find_by_start(ch, start_time, 1);
e->e_duration = duration;
ect = NULL;
*title = 0;
*desc = 0;
@ -370,28 +377,25 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
switch(dtag) {
case DVB_DESC_SHORT_EVENT:
if(dvb_desc_short_event(ptr, dlen,
title, sizeof(title),
desc, sizeof(desc)) < 0)
duration = 0;
if(!dvb_desc_short_event(ptr, dlen,
title, sizeof(title),
desc, sizeof(desc))) {
epg_event_set_title(e, title);
epg_event_set_desc(e, desc);
}
break;
case DVB_DESC_CONTENT:
if(dlen >= 2) {
/* We only support one content type per event atm. */
// ect = epg_content_type_find_by_dvbcode(*ptr);
ect = epg_content_type_find_by_dvbcode(*ptr);
epg_event_set_content_type(e, ect);
}
break;
}
len -= dlen; ptr += dlen; dllen -= dlen;
}
if(duration > 0) {
//epg_update_event_by_id(ch, event_id, start_time, duration,
//title, desc, ect);
}
}
}

407
epg.c
View file

@ -25,26 +25,33 @@
#include "tvhead.h"
#include "channels.h"
#include "epg.h"
#include "dispatch.h"
#include "htsp.h"
#include "autorec.h"
#define EPG_MAX_AGE 86400
#define EPG_HASH_ID_WIDTH 256
struct event_list epg_hash[EPG_HASH_ID_WIDTH];
static dtimer_t epg_channel_maintain_timer;
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)
{
printf("title=%s\n", title);
free((void *)e->e_title);
e->e_title = strdup(title);
}
/**
*
*/
void
epg_event_set_desc(event_t *e, const char *desc)
{
@ -52,7 +59,11 @@ epg_event_set_desc(event_t *e, const char *desc)
e->e_desc = strdup(desc);
}
static void
/**
*
*/
void
epg_event_set_content_type(event_t *e, epg_content_type_t *ect)
{
if(e->e_content_type != NULL)
@ -63,99 +74,61 @@ epg_event_set_content_type(event_t *e, epg_content_type_t *ect)
LIST_INSERT_HEAD(&ect->ect_events, e, e_content_type_link);
}
/**
*
*/
event_t *
epg_event_find_by_time0(struct event_queue *q, time_t start)
epg_event_find_by_start(channel_t *ch, time_t start, int create)
{
event_t *e;
static event_t *skel, *e;
TAILQ_FOREACH(e, q, e_channel_link)
if(start >= e->e_start && start < e->e_start + e->e_duration)
break;
return e;
}
lock_assert(&global_lock);
event_t *
epg_event_find_by_time(channel_t *ch, time_t start)
{
return epg_event_find_by_time0(&ch->ch_epg_events, start);
}
if(skel == NULL)
skel = calloc(1, sizeof(event_t));
event_t *
epg_event_find_by_tag(uint32_t tag)
{
event_t *e;
unsigned int l = tag % EPG_HASH_ID_WIDTH;
skel->e_start = start;
LIST_FOREACH(e, &epg_hash[l], e_hash_link)
if(e->e_tag == tag)
break;
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_get_current(channel_t *ch)
epg_event_find_by_time(channel_t *ch, time_t t)
{
event_t *e;
event_t skel, *e;
time_t now;
time(&now);
e = ch->ch_epg_cur_event;
if(e == NULL || now < e->e_start || now > e->e_start + e->e_duration)
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;
}
event_t *
epg_event_find_current_or_upcoming(channel_t *ch)
{
event_t *e;
TAILQ_FOREACH(e, &ch->ch_epg_events, e_channel_link)
if(e->e_start + e->e_duration > dispatch_clock)
break;
return e;
}
static int
startcmp(event_t *a, event_t *b)
{
return a->e_start - b->e_start;
}
event_t *
epg_event_build(struct event_queue *head, time_t start, int duration)
{
time_t now;
event_t *e;
time(&now);
if(duration < 1 || start + duration < now - EPG_MAX_AGE)
return NULL;
TAILQ_FOREACH(e, head, e_channel_link)
if(start == e->e_start && duration == e->e_duration)
return e;
e = calloc(1, sizeof(event_t));
e->e_duration = duration;
e->e_start = start;
TAILQ_INSERT_SORTED(head, e, e_channel_link, startcmp);
return e;
}
/**
*
*/
void
epg_event_free(event_t *e)
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);
@ -164,245 +137,12 @@ epg_event_free(event_t *e)
free(e);
}
static void
epg_event_destroy(channel_t *ch, event_t *e)
{
// printf("epg: flushed event %s\n", e->e_title);
if(ch->ch_epg_cur_event == e)
ch->ch_epg_cur_event = NULL;
TAILQ_REMOVE(&ch->ch_epg_events, e, e_channel_link);
LIST_REMOVE(e, e_hash_link);
epg_event_free(e);
}
#if 0
/**
*
*/
void
event_time_txt(time_t start, int duration, char *out, int outlen)
{
char tmp1[40];
char tmp2[40];
char *c;
time_t stop = start + duration;
ctime_r(&start, tmp1);
c = strchr(tmp1, '\n');
if(c)
*c = 0;
ctime_r(&stop, tmp2);
c = strchr(tmp2, '\n');
if(c)
*c = 0;
snprintf(out, outlen, "[%s - %s]", tmp1, tmp2);
}
static int
check_overlap0(channel_t *ch, event_t *a)
{
char atime[100];
char btime[100];
event_t *b;
int overshot;
b = TAILQ_NEXT(a, e_channel_link);
if(b == NULL)
return 0;
overshot = a->e_start + a->e_duration - b->e_start;
if(overshot < 1)
return 0;
event_time_txt(a->e_start, a->e_duration, atime, sizeof(atime));
event_time_txt(b->e_start, b->e_duration, btime, sizeof(btime));
if(a->e_source > b->e_source) {
b->e_start += overshot;
b->e_duration -= overshot;
if(b->e_duration < 1) {
epg_event_destroy(ch, b);
return 1;
}
} else {
a->e_duration -= overshot;
if(a->e_duration < 1) {
epg_event_destroy(ch, a);
return 1;
}
}
return 0;
}
static int
check_overlap(channel_t *ch, event_t *e)
{
event_t *p;
p = TAILQ_PREV(e, event_queue, e_channel_link);
if(p != NULL) {
if(check_overlap0(ch, p))
return 1;
}
return check_overlap0(ch, e);
}
static void
epg_event_create(channel_t *ch, time_t start, int duration,
const char *title, const char *desc, int source,
uint16_t id, epg_content_type_t *ect)
{
unsigned int l;
time_t now;
event_t *e;
time(&now);
if(duration < 1 || start + duration < now - EPG_MAX_AGE)
return;
TAILQ_FOREACH(e, &ch->ch_epg_events, e_channel_link) {
if(start == e->e_start && duration == e->e_duration)
break;
if(start == e->e_start && !strcmp(e->e_title ?: "", title))
break;
}
if(e == NULL) {
e = calloc(1, sizeof(event_t));
e->e_start = start;
TAILQ_INSERT_SORTED(&ch->ch_epg_events, e, e_channel_link, startcmp);
e->e_channel = ch;
e->e_event_id = id;
e->e_duration = duration;
e->e_tag = tag_get();
l = e->e_tag % EPG_HASH_ID_WIDTH;
LIST_INSERT_HEAD(&epg_hash[l], e, e_hash_link);
}
if(source > e->e_source) {
e->e_source = source;
if(e->e_duration != duration) {
char before[100];
char after[100];
event_time_txt(e->e_start, e->e_duration, before, sizeof(before));
event_time_txt(e->e_start, duration, after, sizeof(after));
e->e_duration = duration;
}
if(title != NULL) epg_event_set_title(e, title);
if(desc != NULL) epg_event_set_desc(e, desc);
if(ect != NULL) epg_event_set_content_type(e, ect);
}
check_overlap(ch, e);
}
void
epg_update_event_by_id(channel_t *ch, uint16_t event_id,
time_t start, int duration, const char *title,
const char *desc, epg_content_type_t *ect)
{
event_t *e;
TAILQ_FOREACH(e, &ch->ch_epg_events, e_channel_link)
if(e->e_event_id == event_id)
break;
if(e != NULL) {
/* We already have information about this event */
if(e->e_duration != duration || e->e_start != start) {
char before[100];
char after[100];
event_time_txt(e->e_start, e->e_duration, before, sizeof(before));
event_time_txt(start, duration, after, sizeof(after));
TAILQ_REMOVE(&ch->ch_epg_events, e, e_channel_link);
e->e_duration = duration;
e->e_start = start;
TAILQ_INSERT_SORTED(&ch->ch_epg_events, e, e_channel_link, startcmp);
if(check_overlap(ch, e))
return; /* event was destroyed, return at once */
}
epg_event_set_title(e, title);
epg_event_set_desc(e, desc);
epg_event_set_content_type(e, ect);
} else {
epg_event_create(ch, start, duration, title, desc,
EVENT_SRC_DVB, event_id, ect);
}
}
static void
epg_set_current_event(channel_t *ch, event_t *e)
{
if(e == ch->ch_epg_cur_event)
return;
ch->ch_epg_cur_event = e;
/* Notify clients that a new programme is on */
htsp_async_channel_update(ch);
if(e == NULL)
return;
/* Let autorecorder check this event */
autorec_check_new_event(e);
e = TAILQ_NEXT(e, e_channel_link);
/* .. and next one, to make better scheduling */
if(e != NULL)
autorec_check_new_event(e);
}
static void
epg_locate_current_event(channel_t *ch, time_t now)
{
event_t *e;
e = epg_event_find_by_time(ch, now);
epg_set_current_event(ch, e);
}
static void
epg_channel_maintain(void *aux, int64_t clk)
epg_channel_maintain()
{
channel_t *ch;
event_t *e, *cur;
@ -440,6 +180,7 @@ epg_channel_maintain(void *aux, int64_t clk)
epg_locate_current_event(ch, now);
}
}
#endif
/**
@ -450,33 +191,15 @@ epg_destroy_by_channel(channel_t *ch)
{
event_t *e;
while((e = TAILQ_FIRST(&ch->ch_epg_events)) != NULL)
epg_event_destroy(ch, e);
while((e = ch->ch_epg_events.root) != NULL)
epg_event_destroy(e);
}
/**
*
*/
void
epg_transfer_events(channel_t *ch, struct event_queue *src,
const char *srcname, char *icon)
{
event_t *e;
int cnt = 0;
if(strcmp(icon ?: "", ch->ch_icon ?: ""))
channel_set_icon(ch, icon);
TAILQ_FOREACH(e, src, e_channel_link) {
epg_event_create(ch, e->e_start, e->e_duration, e->e_title,
e->e_desc, EVENT_SRC_XMLTV, 0,
e->e_content_type);
cnt++;
}
}
static const char *groupnames[16] = {
[0] = "Unclassified",
[1] = "Movie / Drama",
@ -544,6 +267,7 @@ epg_content_group_find_by_name(const char *name)
*
* 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,
@ -592,6 +316,7 @@ epg_search(struct event_list *h, const char *title,
return num;
}
#endif
/*
*
@ -603,7 +328,5 @@ epg_init(void)
for(i = 0x0; i < 0x100; i+=16)
epg_content_type_find_by_dvbcode(i);
dtimer_arm(&epg_channel_maintain_timer, epg_channel_maintain, NULL, 5);
}

35
epg.h
View file

@ -19,44 +19,29 @@
#ifndef EPG_H
#define EPG_H
extern epg_content_group_t *epg_content_groups[16];
void epg_init(void);
event_t *epg_event_find_by_time0(struct event_queue *q, time_t start);
event_t *epg_event_find_by_time(channel_t *ch, time_t start);
event_t *epg_event_find_by_tag(uint32_t id);
event_t *epg_event_get_current(channel_t *ch);
event_t *epg_event_build(struct event_queue *head, time_t start, int duration);
void epg_event_free(event_t *e);
void epg_event_set_title(event_t *e, const char *title);
void epg_event_set_desc(event_t *e, const char *desc);
void epg_update_event_by_id(channel_t *ch, uint16_t event_id,
time_t start, int duration, const char *title,
const char *desc, epg_content_type_t *ect);
void epg_event_set_content_type(event_t *e, epg_content_type_t *ect);
void epg_transfer_events(channel_t *ch, struct event_queue *src,
const char *srcname, char *icon);
event_t *epg_event_find_by_start(channel_t *ch, time_t start, int create);
void event_time_txt(time_t start, int duration, char *out, int outlen);
event_t *epg_event_find_by_time(channel_t *ch, time_t t);
event_t *epg_event_find_current_or_upcoming(channel_t *ch);
void epg_event_destroy(event_t *e);
void epg_destroy_by_channel(channel_t *ch);
/**
*
*/
epg_content_type_t *epg_content_type_find_by_dvbcode(uint8_t dvbcode);
epg_content_group_t *epg_content_group_find_by_name(const char *name);
int epg_search(struct event_list *h, const char *title,
epg_content_group_t *s_ecg, channel_t *s_ch);
void epg_destroy_by_channel(channel_t *ch);
#endif /* EPG_H */

2
pvr.c
View file

@ -62,7 +62,7 @@ static void postrec(pvr_rec_t *pvrr);
/**
* Initialize PVR framework
*/
*/
void
pvr_init(void)
{

View file

@ -87,7 +87,7 @@ TAILQ_HEAD(channel_queue, channel);
TAILQ_HEAD(th_dvb_adapter_queue, th_dvb_adapter);
LIST_HEAD(th_v4l_adapter_list, th_v4l_adapter);
LIST_HEAD(event_list, event);
TAILQ_HEAD(event_queue, event);
RB_HEAD(event_tree, event);
LIST_HEAD(pvr_rec_list, pvr_rec);
TAILQ_HEAD(ref_update_queue, ref_update);
LIST_HEAD(th_transport_list, th_transport);
@ -778,7 +778,7 @@ typedef struct channel {
COMMERCIAL_DETECT_TTP192,
} ch_commercial_detection;
struct event_queue ch_epg_events;
struct event_tree ch_epg_events;
struct event *ch_epg_cur_event;
char *ch_icon;
@ -868,10 +868,7 @@ typedef struct epg_content_type {
*/
typedef struct event {
channel_t *e_channel;
TAILQ_ENTRY(event) e_channel_link;
LIST_ENTRY(event) e_hash_link;
LIST_ENTRY(event) e_tmp_link;
RB_ENTRY(event) e_channel_link;
LIST_ENTRY(event) e_content_type_link;
epg_content_type_t *e_content_type;
@ -882,10 +879,6 @@ typedef struct event {
const char *e_title; /* UTF-8 encoded */
const char *e_desc; /* UTF-8 encoded */
uint16_t e_event_id; /* DVB event id */
uint32_t e_tag;
int e_source; /* higer is better, and we never downgrade */
#define EVENT_SRC_XMLTV 1