Major rework, as follows:

- Add automatic DVB mux scanning
- Rename "programme" to "event" (more consistent with DVB specs)
- Improve showtime client interface
- Add decent timers
- Fix UTF-8 conversion issues at various places
- Improve EPG (Join DVB and XMLTV to same lists)
This commit is contained in:
Andreas Öman 2007-08-16 10:59:06 +00:00
parent 920fa134ab
commit fa329cec17
25 changed files with 2646 additions and 948 deletions

View file

@ -6,6 +6,8 @@ SRCS += pvr.c pvr_rec.c
SRCS += epg.c epg_xmltv.c
SRCS += dvb_support.c dvb_pmt.c dvb_dvr.c
SRCS += input_dvb.c input_iptv.c #input_v4l.c
SRCS += output_client.c output_multicast.c

View file

@ -28,9 +28,6 @@
#include <stdlib.h>
#include <string.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <libhts/htscfg.h>
#include "tvhead.h"
@ -38,37 +35,35 @@
#include "input_v4l.h"
#include "input_iptv.h"
#include "dvb_pmt.h"
#include "channels.h"
#include "transports.h"
struct th_channel_queue channels;
static int channel_tally;
struct th_transport_list all_transports;
static int chtally;
#define MAXCHANNELS 256
static th_channel_t *charray[MAXCHANNELS];
static int numchannels;
void scanner_init(void);
th_channel_t *
channel_find(const char *name, int create)
{
th_channel_t *ch;
TAILQ_FOREACH(ch, &channels, ch_global_link) {
if(!strcmp(name, ch->ch_name))
TAILQ_FOREACH(ch, &channels, ch_global_link)
if(!strcasecmp(name, ch->ch_name))
return ch;
}
if(create == 0)
return NULL;
ch = calloc(1, sizeof(th_channel_t));
ch->ch_name = strdup(name);
TAILQ_INSERT_TAIL(&channels, ch, ch_global_link);
ch->ch_index = channel_tally++;
ch->ch_ref = ++reftally;
ch->ch_index = ++chtally;
TAILQ_INIT(&ch->ch_epg_events);
charray[ch->ch_index] = ch;
numchannels++;
TAILQ_INSERT_TAIL(&channels, ch, ch_global_link);
ch->ch_tag = tag_get();
return ch;
}
@ -80,15 +75,39 @@ transportcmp(th_transport_t *a, th_transport_t *b)
}
int
transport_set_channel(th_transport_t *t, const char *name)
{
th_channel_t *ch;
th_pid_t *tp;
if(LIST_FIRST(&t->tht_pids) == NULL)
return -1;
if(t->tht_channel != NULL)
return 0;
ch = channel_find(name, 1);
t->tht_channel = ch;
LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
syslog(LOG_DEBUG, "Added service \"%s\" for channel \"%s\"",
t->tht_name, ch->ch_name);
LIST_FOREACH(tp, &t->tht_pids, tp_link)
syslog(LOG_DEBUG, " Pid %5d [%s]",
tp->tp_pid, htstvstreamtype2txt(tp->tp_type));
return 0;
}
static void
service_load(struct config_head *head)
{
const char *name, *v;
pidinfo_t pids[10];
int i, npids = 0;
th_transport_t *t;
pidinfo_t *pi;
th_channel_t *ch;
if((name = config_get_str_sub(head, "channel", NULL)) == NULL)
@ -125,59 +144,42 @@ service_load(struct config_head *head)
return;
}
transport_monitor_init(t);
if((v = config_get_str_sub(head, "service_id", NULL)) != NULL)
t->tht_dvb_service_id = strtol(v, NULL, 0);
if(t->tht_npids == 0) {
if((v = config_get_str_sub(head, "network_id", NULL)) != NULL)
t->tht_dvb_network_id = strtol(v, NULL, 0);
if((v = config_get_str_sub(head, "video", NULL)) != NULL) {
pids[npids].pid = strtol(v, NULL, 0);
pids[npids++].type = HTSTV_MPEG2VIDEO;
}
if((v = config_get_str_sub(head, "transport_id", NULL)) != NULL)
t->tht_dvb_transport_id = strtol(v, NULL, 0);
if((v = config_get_str_sub(head, "h264", NULL)) != NULL) {
pids[npids].pid = strtol(v, NULL, 0);
pids[npids++].type = HTSTV_H264;
}
if((v = config_get_str_sub(head, "video", NULL)) != NULL)
transport_add_pid(t, strtol(v, NULL, 0), HTSTV_MPEG2VIDEO);
if((v = config_get_str_sub(head, "audio", NULL)) != NULL) {
pids[npids].pid = strtol(v, NULL, 0);
pids[npids++].type = HTSTV_MPEG2AUDIO;
}
if((v = config_get_str_sub(head, "h264", NULL)) != NULL)
transport_add_pid(t, strtol(v, NULL, 0), HTSTV_H264);
if((v = config_get_str_sub(head, "ac3", NULL)) != NULL) {
pids[npids].pid = strtol(v, NULL, 0);
pids[npids++].type = HTSTV_AC3;
}
if((v = config_get_str_sub(head, "audio", NULL)) != NULL)
transport_add_pid(t, strtol(v, NULL, 0), HTSTV_MPEG2AUDIO);
if((v = config_get_str_sub(head, "ac3", NULL)) != NULL)
transport_add_pid(t, strtol(v, NULL, 0), HTSTV_AC3);
if((v = config_get_str_sub(head, "teletext", NULL)) != NULL) {
pids[npids].pid = strtol(v, NULL, 0);
pids[npids++].type = HTSTV_TELETEXT;
}
if((v = config_get_str_sub(head, "teletext", NULL)) != NULL)
transport_add_pid(t, strtol(v, NULL, 0), HTSTV_TELETEXT);
t->tht_pids = calloc(1, npids * sizeof(pidinfo_t));
for(i = 0; i < npids; i++) {
pi = t->tht_pids + i;
pi->pid = pids[i].pid;
pi->type = pids[i].type;
pi->demuxer_fd = -1;
pi->cc_valid = 0;
}
t->tht_npids = npids;
}
t->tht_prio = atoi(config_get_str_sub(head, "prio", ""));
syslog(LOG_DEBUG, "Added service \"%s\" for channel \"%s\"",
t->tht_name, ch->ch_name);
transport_set_channel(t, name);
t->tht_channel = ch;
LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
transport_monitor_init(t);
LIST_INSERT_HEAD(&all_transports, t, tht_global_link);
}
static void
channel_load(struct config_head *head)
{
@ -218,21 +220,13 @@ channels_load(void)
th_channel_t *
channel_by_id(unsigned int id)
channel_by_index(uint32_t index)
{
return id < MAXCHANNELS ? charray[id] : NULL;
}
int
id_by_channel(th_channel_t *ch)
{
return ch->ch_index;
}
int
channel_get_channels(void)
{
return numchannels;
th_channel_t *ch;
TAILQ_FOREACH(ch, &channels, ch_global_link)
if(ch->ch_index == index)
return ch;
return NULL;
}

View file

@ -19,9 +19,11 @@
#ifndef CHANNELS_H
#define CHANNELS_H
extern struct th_channel_queue channels;
void channels_load(void);
th_channel_t *channel_by_id(unsigned int id);
th_channel_t *channel_by_index(uint32_t id);
int id_by_channel(th_channel_t *ch);

View file

@ -1,5 +1,5 @@
/*
* socket fd dispathcer
* socket fd dispatcher and (very simple) timer handling
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
@ -28,7 +29,6 @@
#include "dispatch.h"
static int epoll_fd;
typedef struct epoll_entry {
@ -39,20 +39,100 @@ typedef struct epoll_entry {
} epoll_entry_t;
typedef struct stimer {
LIST_ENTRY(stimer) link;
void (*callback)(void *aux);
void *aux;
time_t t;
} stimer_t;
LIST_HEAD(, stimer) dispatch_timers;
static int
stimercmp(stimer_t *a, stimer_t *b)
{
return a->t - b->t;
}
void *
stimer_add(void (*callback)(void *aux), void *aux, int delta)
{
time_t now;
stimer_t *ti = malloc(sizeof(stimer_t));
time(&now);
ti->t = now + delta;
ti->aux = aux;
ti->callback = callback;
LIST_INSERT_SORTED(&dispatch_timers, ti, link, stimercmp);
return ti;
}
void
stimer_del(void *handle)
{
stimer_t *ti = handle;
LIST_REMOVE(ti, link);
free(ti);
}
static int
stimer_next(void)
{
stimer_t *ti = LIST_FIRST(&dispatch_timers);
struct timeval tv;
int64_t next, now, delta;
if(ti == NULL)
return -1;
next = (uint64_t)ti->t * 1000000ULL;
gettimeofday(&tv, NULL);
now = (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
delta = next - now;
return delta < 0 ? 0 : delta / 1000ULL;
}
static void
stimer_dispatch(time_t now)
{
stimer_t *ti;
while((ti = LIST_FIRST(&dispatch_timers)) != NULL) {
if(ti->t > now)
break;
LIST_REMOVE(ti, link);
ti->callback(ti->aux);
free(ti);
}
}
int
dispatch_init(void)
{
int fdflag;
epoll_fd = epoll_create(100);
if(epoll_fd == -1) {
perror("epoll_create()");
exit(1);
}
fdflag = fcntl(epoll_fd, F_GETFD);
fdflag |= FD_CLOEXEC;
fcntl(epoll_fd, F_SETFD, fdflag);
return 0;
}
@ -105,53 +185,19 @@ dispatch_clr(void *handle, int flags)
void
int
dispatch_delfd(void *handle)
{
struct epoll_entry *e = handle;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, e->fd, &e->event);
int fd = e->fd;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &e->event);
free(e);
return fd;
}
typedef struct dtimer {
LIST_ENTRY(dtimer) link;
void (*callback)(void *aux);
void *aux;
} dtimer_t;
LIST_HEAD(, dtimer) dispatcher_timers;
void *
dispatch_add_1sec_event(void (*callback)(void *aux), void *aux)
{
dtimer_t *ti = malloc(sizeof(dtimer_t));
LIST_INSERT_HEAD(&dispatcher_timers, ti, link);
ti->callback = callback;
ti->aux = aux;
return ti;
}
void
dispatch_del_1sec_event(void *p)
{
dtimer_t *ti = p;
LIST_REMOVE(ti, link);
free(ti);
}
static void
run_1sec_events(void)
{
dtimer_t *ti;
LIST_FOREACH(ti, &dispatcher_timers, link)
ti->callback(ti->aux);
}
#define EPOLL_FDS_PER_ROUND 100
@ -162,17 +208,14 @@ dispatcher(void)
{
struct epoll_entry *e;
int i, n;
time_t now;
static struct epoll_event events[EPOLL_FDS_PER_ROUND];
n = epoll_wait(epoll_fd, events, EPOLL_FDS_PER_ROUND, 1000);
n = epoll_wait(epoll_fd, events, EPOLL_FDS_PER_ROUND, stimer_next());
time(&now);
if(now != dispatch_clock) {
run_1sec_events();
dispatch_clock = now;
}
time(&dispatch_clock);
stimer_dispatch(dispatch_clock);
for(i = 0; i < n; i++) {
e = events[i].data.ptr;

View file

@ -27,12 +27,12 @@ int dispatch_init(void);
void *dispatch_addfd(int fd,
void (*callback)(int events, void *opaque, int fd),
void *opaque, int flags);
void dispatch_delfd(void *handle);
int dispatch_delfd(void *handle);
void dispatch_set(void *handle, int flags);
void dispatch_clr(void *handle, int flags);
void dispatcher(void);
void *dispatch_add_1sec_event(void (*callback)(void *aux), void *aux);
void dispatch_del_1sec_event(void *p);
void *stimer_add(void (*callback)(void *aux), void *aux, int delta);
void stimer_del(void *handle);
#endif /* DISPATCH_H */

233
dvb_dvr.c Normal file
View file

@ -0,0 +1,233 @@
/*
* TV Input - Linux DVB interface - Feed receiver 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 <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <libhts/htscfg.h>
#include "tvhead.h"
#include "dispatch.h"
#include "input_dvb.h"
#include "dvb_dvr.h"
#include "channels.h"
#include "transports.h"
#include "dvb_support.h"
static void dvr_fd_callback(int events, void *opaque, int fd);
int
dvb_dvr_init(th_dvb_adapter_t *tda)
{
int dvr;
dvr = open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK);
if(dvr == -1) {
syslog(LOG_ALERT, "%s: unable to open dvr\n", tda->tda_dvr_path);
return -1;
}
dispatch_addfd(dvr, dvr_fd_callback, tda, DISPATCH_READ);
return 0;
}
/*
*
*/
static void
dvb_dvr_process_packets(th_dvb_adapter_t *tda, uint8_t *tsb, int r)
{
int pid;
th_transport_t *t;
while(r >= 188) {
pid = (tsb[1] & 0x1f) << 8 | tsb[2];
LIST_FOREACH(t, &tda->tda_transports, tht_adapter_link)
transport_recv_tsb(t, pid, tsb);
r -= 188;
tsb += 188;
}
}
/*
*
*/
static void
dvr_fd_callback(int events, void *opaque, int fd)
{
th_dvb_adapter_t *tda = opaque;
uint8_t tsb0[188 * 10];
int r;
if(!(events & DISPATCH_READ))
return;
r = read(fd, tsb0, sizeof(tsb0));
dvb_dvr_process_packets(tda, tsb0, r);
}
/*
*
*/
static void
dvb_adapter_clean(th_dvb_adapter_t *tda)
{
th_transport_t *t;
while((t = LIST_FIRST(&tda->tda_transports)) != NULL)
dvb_stop_feed(t);
}
/*
*
*/
void
dvb_stop_feed(th_transport_t *t)
{
th_pid_t *tp;
t->tht_dvb_adapter = NULL;
LIST_REMOVE(t, tht_adapter_link);
LIST_FOREACH(tp, &t->tht_pids, tp_link) {
close(tp->tp_demuxer_fd);
tp->tp_demuxer_fd = -1;
}
t->tht_status = TRANSPORT_IDLE;
transport_flush_subscribers(t);
}
/*
*
*/
int
dvb_start_feed(th_transport_t *t, unsigned int weight)
{
th_dvb_adapter_t *tda, *cand = NULL;
struct dmx_pes_filter_params dmx_param;
th_pid_t *tp;
int w, fd, pid;
LIST_FOREACH(tda, &dvb_adapters_running, tda_link) {
w = transport_compute_weight(&tda->tda_transports);
if(w < weight)
cand = tda;
if(tda->tda_mux_current != NULL &&
tda->tda_mux_current->tdmi_mux == t->tht_dvb_mux)
break;
}
if(tda == NULL) {
if(cand == NULL)
return 1;
dvb_adapter_clean(cand);
tda = cand;
}
LIST_FOREACH(tp, &t->tht_pids, tp_link) {
fd = open(tda->tda_demux_path, O_RDWR);
pid = tp->tp_pid;
tp->tp_cc_valid = 0;
if(fd == -1) {
tp->tp_demuxer_fd = -1;
syslog(LOG_ERR,
"\"%s\" unable to open demuxer \"%s\" for pid %d -- %s",
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
continue;
}
memset(&dmx_param, 0, sizeof(dmx_param));
dmx_param.pid = pid;
dmx_param.input = DMX_IN_FRONTEND;
dmx_param.output = DMX_OUT_TS_TAP;
dmx_param.pes_type = DMX_PES_OTHER;
dmx_param.flags = DMX_IMMEDIATE_START;
if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) {
syslog(LOG_ERR,
"\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s",
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
close(fd);
fd = -1;
}
tp->tp_demuxer_fd = fd;
}
LIST_INSERT_HEAD(&tda->tda_transports, t, tht_adapter_link);
t->tht_dvb_adapter = tda;
t->tht_status = TRANSPORT_RUNNING;
dvb_tune(tda, t->tht_dvb_mux, 1);
return 0;
}
/*
*
*/
int
dvb_configure_transport(th_transport_t *t, const char *muxname)
{
th_dvb_mux_t *tdm;
LIST_FOREACH(tdm, &dvb_muxes, tdm_global_link)
if(!strcmp(tdm->tdm_name, muxname))
break;
if(tdm == NULL)
return -1;
t->tht_type = TRANSPORT_DVB;
t->tht_dvb_mux = tdm;
t->tht_name = strdup(tdm->tdm_title);
return 0;
}

28
dvb_dvr.h Normal file
View file

@ -0,0 +1,28 @@
/*
* TV Input - Linux DVB interface
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef DVB_DVR_H
#define DVB_DVR_H
int dvb_dvr_init(th_dvb_adapter_t *tda);
int dvb_start_feed(th_transport_t *t, unsigned int weight);
void dvb_stop_feed(th_transport_t *t);
#endif /* DVB_DVR_H */

149
dvb_pmt.c Normal file
View file

@ -0,0 +1,149 @@
/*
* TV Input - DVB - Program Map Table parser
* 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 <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "tvhead.h"
#include "transports.h"
#include "dvb_support.h"
const char *
htstvstreamtype2txt(tv_streamtype_t s)
{
switch(s) {
case HTSTV_MPEG2VIDEO: return "mpeg2video";
case HTSTV_MPEG2AUDIO: return "mpeg2audio";
case HTSTV_H264: return "h264";
case HTSTV_AC3: return "AC-3";
case HTSTV_TELETEXT: return "teletext";
case HTSTV_SUBTITLES: return "subtitles";
default: return "<unknown>";
}
}
int
dvb_parse_pmt(th_transport_t *t, uint8_t *ptr, int len)
{
uint16_t pcr_pid, pid;
uint8_t estype;
int dllen;
uint8_t dtag, dlen;
uint16_t sid;
tv_streamtype_t hts_stream_type;
if(len < 9)
return -1;
sid = ptr[0] << 8 | ptr[1];
pcr_pid = (ptr[5] & 0x1f) << 8 | ptr[6];
dllen = (ptr[7] & 0xf) << 8 | ptr[8];
ptr += 9;
len -= 9;
if(sid != t->tht_dvb_service_id)
return -1;
while(dllen > 2) {
dtag = ptr[0];
dlen = ptr[1];
len -= 2; ptr += 2; dllen -= 2;
if(dlen > len)
break;
len -= dlen; ptr += dlen; dllen -= dlen;
}
while(len >= 5) {
estype = ptr[0];
pid = (ptr[1] & 0x1f) << 8 | ptr[2];
dllen = (ptr[3] & 0xf) << 8 | ptr[4];
ptr += 5;
len -= 5;
hts_stream_type = 0;
switch(estype) {
case 0x01:
case 0x02:
hts_stream_type = HTSTV_MPEG2VIDEO;
break;
case 0x03:
case 0x04:
case 0x81:
hts_stream_type = HTSTV_MPEG2AUDIO;
break;
case 0x1b:
hts_stream_type = HTSTV_H264;
break;
}
while(dllen > 2) {
dtag = ptr[0];
dlen = ptr[1];
len -= 2; ptr += 2; dllen -= 2;
if(dlen > len)
break;
switch(dtag) {
case DVB_DESC_TELETEXT:
if(estype == 0x06)
hts_stream_type = HTSTV_TELETEXT;
break;
case DVB_DESC_SUBTITLE:
break;
case DVB_DESC_AC3:
if(estype == 0x06)
hts_stream_type = HTSTV_AC3;
break;
}
len -= dlen; ptr += dlen; dllen -= dlen;
}
if(hts_stream_type != 0)
transport_add_pid(t, pid, hts_stream_type);
}
return 0;
}

26
dvb_pmt.h Normal file
View file

@ -0,0 +1,26 @@
/*
* TV Input - Linux DVB interface
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef DVB_PMT_H
#define DVB_PMT_H
int dvb_parse_pmt(th_transport_t *t, uint8_t *ptr, int len);
const char *htstvstreamtype2txt(tv_streamtype_t s);
#endif /* DVB_PMT_H */

160
dvb_support.c Normal file
View file

@ -0,0 +1,160 @@
/*
* TV Input - DVB - Support 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 <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <iconv.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "tvhead.h"
#include "dvb_support.h"
/*
* DVB String conversion according to EN 300 468, Annex A
* Not all character sets are supported, but it should cover most of them
*/
int
dvb_get_string(char *dst, size_t dstlen, const uint8_t *src,
size_t srclen, const char *target_encoding)
{
iconv_t ic;
const char *encoding;
char encbuf[20];
int len;
char *in, *out;
size_t inlen, outlen;
int utf8 = 0;
int i;
unsigned char *tmp;
int r;
if(srclen < 1) {
*dst = 0;
return 0;
}
switch(src[0]) {
case 0:
return -1;
case 0x01 ... 0x0b:
snprintf(encbuf, sizeof(encbuf), "ISO_8859-%d", src[0] + 4);
encoding = encbuf;
src++; srclen--;
break;
case 0x0c ... 0x0f:
return -1;
case 0x10: /* Table A.4 */
if(srclen < 3 || src[1] != 0 || src[2] == 0 || src[2] > 0x0f)
return -1;
snprintf(encbuf, sizeof(encbuf), "ISO_8859-%d", src[2]);
encoding = encbuf;
src+=3; srclen-=3;
break;
case 0x11 ... 0x14:
return -1;
case 0x15:
encoding = "UTF8";
utf8 = 1;
break;
case 0x16 ... 0x1f:
return -1;
default:
encoding = "LATIN1"; /* Default to latin-1 */
break;
}
if(srclen < 1) {
*dst = 0;
return 0;
}
tmp = alloca(srclen + 1);
memcpy(tmp, src, srclen);
tmp[srclen] = 0;
/* Escape control codes */
if(!utf8) {
for(i = 0; i < srclen; i++) {
if(tmp[i] >= 0x80 && tmp[i] <= 0x9f)
tmp[i] = ' ';
}
}
ic = iconv_open(target_encoding, encoding);
if(ic == (iconv_t) -1) {
return -1;
}
inlen = srclen;
outlen = dstlen - 1;
out = dst;
in = (char *)tmp;
while(inlen > 0) {
r = iconv(ic, &in, &inlen, &out, &outlen);
if(r == (size_t) -1) {
if(errno == EILSEQ) {
in++;
inlen--;
continue;
}
}
}
iconv_close(ic);
len = dstlen - outlen - 1;
dst[len] = 0;
return 0;
}
int
dvb_get_string_with_len(char *dst, size_t dstlen,
const uint8_t *buf, size_t buflen,
const char *target_encoding)
{
int l = buf[0];
if(l + 1 > buflen)
return -1;
if(dvb_get_string(dst, dstlen, buf + 1, l, target_encoding))
return -1;
return l + 1;
}

47
dvb_support.h Normal file
View file

@ -0,0 +1,47 @@
/*
* TV Input - Linux DVB interface - Support
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* Based on:
*
* ITU-T Recommendation H.222.0 / ISO standard 13818-1
* EN 300 468 - V1.7.1
*/
#ifndef DVB_SUPPORT_H
#define DVB_SUPPORT_H
/* Descriptors defined in EN 300 468 */
#define DVB_DESC_NETWORK_NAME 0x40
#define DVB_DESC_SERVICE_LIST 0x41
#define DVB_DESC_SHORT_EVENT 0x4d
#define DVB_DESC_SERVICE 0x48
#define DVB_DESC_TELETEXT 0x56
#define DVB_DESC_SUBTITLE 0x59
#define DVB_DESC_AC3 0x6a
int dvb_get_string(char *dst, size_t dstlen, const uint8_t *src,
const size_t srclen, const char *target_encoding);
int dvb_get_string_with_len(char *dst, size_t dstlen,
const uint8_t *buf, size_t buflen,
const char *target_encoding);
#endif /* DVB_SUPPORT_H */

461
epg.c
View file

@ -25,66 +25,453 @@
#include "channels.h"
#include "epg.h"
#define EPG_MAX_AGE 86400
programme_t *
epg_find_programme(th_channel_t *ch, time_t start, time_t stop)
#define EPG_HASH_ID_WIDTH 256
static pthread_mutex_t epg_mutex = PTHREAD_MUTEX_INITIALIZER;
struct event_list epg_hash[EPG_HASH_ID_WIDTH];
void
epg_lock(void)
{
th_proglist_t *tpl = &ch->ch_xmltv;
programme_t *pr;
pthread_mutex_lock(&epg_mutex);
}
/* XXX: use binary search */
LIST_FOREACH(pr, &tpl->tpl_programs, pr_link) {
if(pr->pr_start >= start && pr->pr_stop <= stop)
break;
}
return pr;
void
epg_unlock(void)
{
pthread_mutex_unlock(&epg_mutex);
}
programme_t *
epg_find_programme_by_time(th_channel_t *ch, time_t t)
void
epg_event_set_title(event_t *e, const char *title)
{
th_proglist_t *tpl = &ch->ch_xmltv;
programme_t *pr;
tpl = &ch->ch_xmltv;
/* XXX: use binary search */
free((void *)e->e_title);
e->e_title = strdup(title);
}
LIST_FOREACH(pr, &tpl->tpl_programs, pr_link) {
if(t >= pr->pr_start && t < pr->pr_stop)
break;
}
return pr;
void
epg_event_set_desc(event_t *e, const char *desc)
{
free((void *)e->e_desc);
e->e_desc = strdup(desc);
}
programme_t *
epg_get_prog_by_id(th_channel_t *ch, unsigned int progid)
event_t *
epg_event_find_by_time0(struct event_queue *q, time_t start)
{
th_proglist_t *tpl = &ch->ch_xmltv;
event_t *e;
if(progid >= tpl->tpl_nprograms)
TAILQ_FOREACH(e, q, e_link)
if(start >= e->e_start && start < e->e_start + e->e_duration)
break;
return e;
}
event_t *
epg_event_find_by_time(th_channel_t *ch, time_t start)
{
return epg_event_find_by_time0(&ch->ch_epg_events, start);
}
event_t *
epg_event_find_by_tag(uint32_t tag)
{
event_t *e;
unsigned int l = tag % EPG_HASH_ID_WIDTH;
LIST_FOREACH(e, &epg_hash[l], e_hash_link)
if(e->e_tag == tag)
break;
return e;
}
event_t *
epg_event_get_current(th_channel_t *ch)
{
event_t *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)
return NULL;
return tpl->tpl_prog_vec[progid];
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_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_link, startcmp);
return e;
}
programme_t *
epg_get_cur_prog(th_channel_t *ch)
void
epg_event_free(event_t *e)
{
th_proglist_t *tpl = &ch->ch_xmltv;
programme_t *p = tpl->tpl_prog_current;
free((void *)e->e_title);
free((void *)e->e_desc);
free(e);
}
static void
epg_event_destroy(th_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_link);
LIST_REMOVE(e, e_hash_link);
epg_event_free(e);
}
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 void
check_overlap0(th_channel_t *ch, event_t *a)
{
char atime[100];
char btime[100];
event_t *b;
int overshot;
b = TAILQ_NEXT(a, e_link);
if(b == NULL)
return;
overshot = a->e_start + a->e_duration - b->e_start;
if(overshot < 1)
return;
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) {
syslog(LOG_WARNING,
"\"%s\": Event \"%s\" %s with higest "
"precedence extends over \"%s\" %s",
ch->ch_name, a->e_title, atime, b->e_title, btime);
b->e_start += overshot;
b->e_duration -= overshot;
if(b->e_duration < 1) {
syslog(LOG_WARNING,
"\"%s\": Event \"%s\" destroyed",
ch->ch_name, b->e_title);
epg_event_destroy(ch, b);
} else {
syslog(LOG_WARNING,
"\"%s\": Event \"%s\" delayed and shortened by %ds",
ch->ch_name, b->e_title, overshot);
}
} else {
syslog(LOG_WARNING,
"\"%s\": Event \"%s\" %s with higest "
"precedence extends over \"%s\" %s",
ch->ch_name, b->e_title, btime, a->e_title, atime);
a->e_duration -= overshot;
if(a->e_duration < 1) {
syslog(LOG_WARNING,
"\"%s\": Event \"%s\" destroyed",
ch->ch_name, a->e_title);
epg_event_destroy(ch, a);
} else {
syslog(LOG_WARNING,
"\"%s\": Event \"%s\" shortened by %ds",
ch->ch_name, a->e_title, overshot);
}
}
}
static void
check_overlap(th_channel_t *ch, event_t *e)
{
event_t *p;
p = TAILQ_PREV(e, event_queue, e_link);
if(p != NULL)
check_overlap0(ch, p);
check_overlap0(ch, e);
}
static void
epg_event_create(th_channel_t *ch, time_t start, int duration,
const char *title, const char *desc, int source,
uint16_t id)
{
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_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_link, startcmp);
e->e_ch = 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));
syslog(LOG_DEBUG, "\"%s\": \"%s\" %s changed duration to %s",
ch->ch_name, e->e_title ?: "<unnamed>", before, after);
e->e_duration = duration;
}
if(title != NULL) epg_event_set_title(e, title);
if(desc != NULL) epg_event_set_desc(e, desc);
}
check_overlap(ch, e);
}
void
epg_update_event_by_id(th_channel_t *ch, uint16_t event_id,
time_t start, int duration, const char *title,
const char *desc)
{
event_t *e;
TAILQ_FOREACH(e, &ch->ch_epg_events, e_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));
syslog(LOG_DEBUG, "\"%s\": \"%s\" (serviceid = 0x04x) "
"%s changed duration to %s",
ch->ch_name, e->e_title ?: "<unnamed>",
before, after);
TAILQ_REMOVE(&ch->ch_epg_events, e, e_link);
e->e_start = start;
TAILQ_INSERT_SORTED(&ch->ch_epg_events, e, e_link, startcmp);
check_overlap(ch, e);
}
epg_event_set_title(e, title);
epg_event_set_desc(e, desc);
} else {
epg_event_create(ch, start, duration, title, desc,
EVENT_SRC_DVB, event_id);
}
}
static void
epg_locate_current_event(th_channel_t *ch, time_t now)
{
ch->ch_epg_cur_event = epg_event_find_by_time(ch, now);
}
static void
epg_channel_maintain(void)
{
th_channel_t *ch;
event_t *e;
time_t now;
time(&now);
if(p == NULL || p->pr_stop < now || p->pr_start > now)
return NULL;
TAILQ_FOREACH(ch, &channels, ch_global_link) {
return p;
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);
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_link);
if(now >= e->e_start && now < e->e_start + e->e_duration) {
ch->ch_epg_cur_event = e;
continue;
}
epg_locate_current_event(ch, now);
}
}
/*
*
*/
void
epg_transfer_events(th_channel_t *ch, struct event_queue *src,
const char *srcname)
{
event_t *e;
int cnt = 0;
epg_lock();
TAILQ_FOREACH(e, src, e_link) {
epg_event_create(ch, e->e_start, e->e_duration, e->e_title,
e->e_desc, EVENT_SRC_XMLTV, 0);
cnt++;
}
epg_unlock();
syslog(LOG_DEBUG,
"Transfered %d events from \"%s\" to \"%s\"\n",
cnt, srcname, ch->ch_name);
}
/*
*
*/
static void *
epg_thread(void *aux)
{
while(1) {
sleep(5);
epg_lock();
epg_channel_maintain();
epg_unlock();
}
}
/*
*
*/
void
epg_init(void)
{
pthread_t ptid;
pthread_create(&ptid, NULL, epg_thread, NULL);
}

30
epg.h
View file

@ -19,17 +19,33 @@
#ifndef EPG_H
#define EPG_H
programme_t *epg_find_programme(th_channel_t *ch,
time_t start, time_t stop);
void epg_init(void);
programme_t *epg_find_programme_by_time(th_channel_t *ch, time_t t);
void epg_lock(void);
/* XXX: this is an ugly one */
void epg_unlock(void);
programme_t *epg_get_prog_by_id(th_channel_t *ch, unsigned int progid);
event_t *epg_event_find_by_time0(struct event_queue *q, time_t start);
/* epg_get_cur_prog must be called while holding ch_prg_mutex */
event_t *epg_event_find_by_time(th_channel_t *ch, time_t start);
programme_t *epg_get_cur_prog(th_channel_t *ch);
event_t *epg_event_find_by_tag(uint32_t id);
event_t *epg_event_get_current(th_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(th_channel_t *ch, uint16_t event_id,
time_t start, int duration, const char *title,
const char *desc);
void epg_transfer_events(th_channel_t *ch, struct event_queue *src,
const char *srcname);
#endif /* EPG_H */

View file

@ -33,11 +33,56 @@
#include "tvhead.h"
#include "channels.h"
#include "epg.h"
#include "epg_xmltv.h"
#include "output_client.h"
extern int xmltvreload;
LIST_HEAD(, xmltv_channel) xmltv_channel_list;
typedef struct xmltv_map {
LIST_ENTRY(xmltv_map) xm_link;
th_channel_t *xm_channel; /* Set if we have resolved the channel */
int xm_isupdated;
} xmltv_map_t;
typedef struct xmltv_channel {
LIST_ENTRY(xmltv_channel) xc_link;
const char *xc_name;
const char *xc_displayname;
const char *xc_icon;
LIST_HEAD(, xmltv_map) xc_maps;
struct event_queue xc_events;
} xmltv_channel_t;
static xmltv_channel_t *
xc_find(const char *name)
{
xmltv_channel_t *xc;
LIST_FOREACH(xc, &xmltv_channel_list, xc_link)
if(!strcmp(xc->xc_name, name))
return xc;
xc = calloc(1, sizeof(xmltv_channel_t));
xc->xc_name = strdup(name);
TAILQ_INIT(&xc->xc_events);
LIST_INSERT_HEAD(&xmltv_channel_list, xc, xc_link);
return xc;
}
#define XML_FOREACH(n) for(; (n) != NULL; (n) = (n)->next)
@ -45,56 +90,28 @@ static void
xmltv_parse_channel(xmlNode *n, char *chid)
{
char *t, *c;
char *dname = NULL;
char *iname = NULL;
th_channel_t *tdc;
th_proglist_t *tpl;
xmltv_channel_t *xc;
xc = xc_find(chid);
XML_FOREACH(n) {
c = (char *)xmlNodeGetContent(n);
if(!strcmp((char *)n->name, "display-name")) {
if(dname != NULL)
free(dname);
dname = strdup(c);
free((void *)xc->xc_displayname);
xc->xc_displayname = strdup(c);
}
if(!strcmp((char *)n->name, "icon")) {
t = (char *)xmlGetProp(n, (unsigned char *)"src");
iname = t ? strdup(t) : NULL;
free((void *)xc->xc_icon);
xc->xc_icon = strdup(t);
xmlFree(t);
}
xmlFree(c);
}
if(dname != NULL) {
TAILQ_FOREACH(tdc, &channels, ch_global_link) {
if(strcmp(tdc->ch_name, dname))
continue;
pthread_mutex_lock(&tdc->ch_epg_mutex);
tpl = &tdc->ch_xmltv;
if(tpl->tpl_refname != NULL)
free((void *)tpl->tpl_refname);
tpl->tpl_refname = strdup(chid);
if(tdc->ch_icon != NULL)
free((void *)tdc->ch_icon);
tdc->ch_icon = iname ? strdup(iname) : NULL;
pthread_mutex_unlock(&tdc->ch_epg_mutex);
break;
}
free(dname);
}
if(iname != NULL)
free(iname);
}
@ -128,73 +145,38 @@ str2time(char *str)
static void
xmltv_parse_programme(xmlNode *n, char *chid, char *start, char *stop)
xmltv_parse_programme(xmlNode *n, char *chid, char *startstr, char *stopstr)
{
char *c;
th_channel_t *tdc;
programme_t *pr, *t;
th_proglist_t *tpl;
event_t *e;
time_t start, stop;
int duration;
xmltv_channel_t *xc;
TAILQ_FOREACH(tdc, &channels, ch_global_link) {
pthread_mutex_lock(&tdc->ch_epg_mutex);
tpl = &tdc->ch_xmltv;
if(tpl->tpl_refname != NULL && !strcmp(tpl->tpl_refname, chid))
break;
pthread_mutex_unlock(&tdc->ch_epg_mutex);
}
xc = xc_find(chid);
start = str2time(startstr);
stop = str2time(stopstr);
if(tdc == NULL)
duration = stop - start;
e = epg_event_build(&xc->xc_events, start, duration);
if(e == NULL)
return;
pr = calloc(1, sizeof(programme_t));
pr->pr_start = str2time(start);
pr->pr_stop = str2time(stop);
XML_FOREACH(n) {
c = (char *)xmlNodeGetContent(n);
if(!strcmp((char *)n->name, "title")) {
pr->pr_title = strdup(c);
epg_event_set_title(e, c);
} else if(!strcmp((char *)n->name, "desc")) {
pr->pr_desc = strdup(c);
epg_event_set_desc(e, c);
}
xmlFree(c);
}
LIST_FOREACH(t, &tpl->tpl_programs, pr_link) {
if(pr->pr_start == t->pr_start &&
pr->pr_stop == t->pr_stop)
break;
}
if(t != NULL) {
free(t->pr_title);
free(t->pr_desc);
t->pr_title = pr->pr_title;
t->pr_desc = pr->pr_desc;
free(pr);
t->pr_delete_me = 0;
} else {
tpl->tpl_nprograms++;
pr->pr_ref = ++reftally;
pr->pr_ch = tdc;
LIST_INSERT_HEAD(&tpl->tpl_programs, pr, pr_link);
}
pthread_mutex_unlock(&tdc->ch_epg_mutex);
}
static void
xmltv_parse_tv(xmlNode *n)
{
@ -240,233 +222,28 @@ xmltv_parse_root(xmlNode *n)
/*
* Sorting functions
*
*/
static int
xmltvpcmp(const void *A, const void *B)
{
programme_t *a = *(programme_t **)A;
programme_t *b = *(programme_t **)B;
return a->pr_start - b->pr_start;
}
static void
xmltv_sort_channel(th_proglist_t *tpl)
{
programme_t *pr;
int i;
if(tpl->tpl_prog_vec != NULL)
free(tpl->tpl_prog_vec);
if(tpl->tpl_nprograms == 0)
return;
tpl->tpl_prog_vec = malloc(tpl->tpl_nprograms * sizeof(void *));
i = 0;
LIST_FOREACH(pr, &tpl->tpl_programs, pr_link)
tpl->tpl_prog_vec[i++] = pr;
qsort(tpl->tpl_prog_vec, tpl->tpl_nprograms, sizeof(void *), xmltvpcmp);
for(i = 0; i < tpl->tpl_nprograms; i++) {
tpl->tpl_prog_vec[i]->pr_index = i;
}
}
static void
xmltv_insert_dummies(th_channel_t *ch, th_proglist_t *tpl)
{
programme_t *pr2, *pr;
int i, delta = 0;
time_t nu, *prev;
struct tm tm;
if(tpl->tpl_nprograms < 1)
return;
nu = tpl->tpl_prog_vec[0]->pr_start;
localtime_r(&nu, &tm);
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
nu = mktime(&tm);
prev = &nu;
for(i = 0; i < tpl->tpl_nprograms - 1; i++) {
nu = *prev;
pr2 = tpl->tpl_prog_vec[i];
if(nu != pr2->pr_start) {
pr = calloc(1, sizeof(programme_t));
pr->pr_start = nu;
pr->pr_stop = pr2->pr_start;
pr->pr_title = NULL;
pr->pr_desc = NULL;
delta++;
pr->pr_ch = ch;
pr->pr_ref = ++reftally;
LIST_INSERT_HEAD(&tpl->tpl_programs, pr, pr_link);
}
prev = &pr2->pr_stop;
}
tpl->tpl_nprograms += delta;
}
static void
xmltv_purge_channel(th_proglist_t *tpl)
{
programme_t *pr, *t;
for(pr = LIST_FIRST(&tpl->tpl_programs) ; pr != NULL; pr = t) {
t = LIST_NEXT(pr, pr_link);
if(pr->pr_delete_me == 0)
continue;
free(pr->pr_title);
free(pr->pr_desc);
LIST_REMOVE(pr, pr_link);
free(pr);
tpl->tpl_nprograms--;
}
}
static void
xmltv_prep_reload(void)
{
programme_t *pr;
th_channel_t *tdc;
th_proglist_t *tpl;
TAILQ_FOREACH(tdc, &channels, ch_global_link) {
pthread_mutex_lock(&tdc->ch_epg_mutex);
tpl = &tdc->ch_xmltv;
LIST_FOREACH(pr, &tpl->tpl_programs, pr_link)
pr->pr_delete_me = 1;
pthread_mutex_unlock(&tdc->ch_epg_mutex);
}
}
static void
xmltv_set_current(th_proglist_t *tpl)
{
time_t now;
programme_t *pr;
programme_t **vec = tpl->tpl_prog_vec;
int i, len = tpl->tpl_nprograms;
time(&now);
pr = NULL;
for(i = len - 1; i >= 0; i--) {
pr = vec[i];
if(pr->pr_start < now)
break;
}
if(i == len || pr == NULL || pr->pr_start > now) {
tpl->tpl_prog_current = NULL;
} else {
tpl->tpl_prog_current = pr;
}
}
static void
xmltv_sort_programs(void)
{
th_channel_t *tdc;
th_proglist_t *tpl;
TAILQ_FOREACH(tdc, &channels, ch_global_link) {
pthread_mutex_lock(&tdc->ch_epg_mutex);
tpl = &tdc->ch_xmltv;
if(tpl->tpl_refname != NULL) {
syslog(LOG_DEBUG, "xmltv: %s (%s) %d programs loaded",
tdc->ch_name, tpl->tpl_refname, tpl->tpl_nprograms);
xmltv_purge_channel(tpl);
xmltv_sort_channel(tpl);
xmltv_insert_dummies(tdc, tpl);
xmltv_sort_channel(tpl);
xmltv_set_current(tpl);
}
pthread_mutex_unlock(&tdc->ch_epg_mutex);
clients_enq_ref(tdc->ch_ref);
}
}
void
xmltv_update_current(void)
xmltv_flush(void)
{
th_channel_t *tdc;
th_proglist_t *tpl;
programme_t *pr;
time_t now;
int idx;
xmltv_channel_t *xc;
xmltv_map_t *xm;
event_t *e;
time(&now);
TAILQ_FOREACH(tdc, &channels, ch_global_link) {
pthread_mutex_lock(&tdc->ch_epg_mutex);
tpl = &tdc->ch_xmltv;
pr = tpl->tpl_prog_current;
if(pr != NULL && pr->pr_stop < now) {
clients_enq_ref(pr->pr_ref);
idx = pr->pr_index + 1;
if(idx < tpl->tpl_nprograms) {
pr = tpl->tpl_prog_vec[idx];
tpl->tpl_prog_current = pr;
clients_enq_ref(pr->pr_ref);
clients_enq_ref(tdc->ch_ref);
}
LIST_FOREACH(xc, &xmltv_channel_list, xc_link) {
while((e = TAILQ_FIRST(&xc->xc_events)) != NULL) {
TAILQ_REMOVE(&xc->xc_events, e, e_link);
epg_event_free(e);
}
while((xm = LIST_FIRST(&xc->xc_maps)) != NULL) {
LIST_REMOVE(xm, xm_link);
free(xm);
}
pthread_mutex_unlock(&tdc->ch_epg_mutex);
}
}
/*
*
*/
@ -485,17 +262,109 @@ xmltv_load(void)
return;
}
xmltv_prep_reload();
xmltv_flush();
root_element = xmlDocGetRootElement(doc);
xmltv_parse_root(root_element);
xmlFreeDoc(doc);
xmlCleanupParser();
}
xmltv_sort_programs();
/*
*
*/
static int
xmltv_map(xmltv_channel_t *xc, th_channel_t *ch)
{
xmltv_map_t *xm;
LIST_FOREACH(xm, &xc->xc_maps, xm_link)
if(xm->xm_channel == ch)
return -1;
xm = calloc(1, sizeof(xmltv_map_t));
xm->xm_channel = ch;
LIST_INSERT_HEAD(&xc->xc_maps, xm, xm_link);
return 0;
}
/*
*
*/
static void
xmltv_resolve_by_events(xmltv_channel_t *xc)
{
th_channel_t *ch;
event_t *ec, *ex;
time_t now;
int thres;
int i;
time(&now);
for(i = 0; i < 4; i++) {
now += 7200;
ex = epg_event_find_by_time0(&xc->xc_events, now);
if(ex == NULL)
break;
TAILQ_FOREACH(ch, &channels, ch_global_link) {
ec = epg_event_find_by_time0(&ch->ch_epg_events, now);
thres = 10;
while(1) {
if(ec == NULL || ex == NULL)
break;
if(thres == 0) {
if(xmltv_map(xc, ch) == 0)
syslog(LOG_DEBUG,
"xmltv: Heuristically mapped \"%s\" (%s) to \"%s\"",
xc->xc_displayname, xc->xc_name, ch->ch_name);
break;
}
if(ec->e_start != ex->e_start || ec->e_duration != ex->e_duration)
break;
ec = TAILQ_NEXT(ec, e_link);
ex = TAILQ_NEXT(ex, e_link);
thres--;
}
}
}
}
/*
*
*/
void
xmltv_transfer(void)
{
xmltv_channel_t *xc;
xmltv_map_t *xm;
th_channel_t *ch;
LIST_FOREACH(xc, &xmltv_channel_list, xc_link) {
ch = channel_find(xc->xc_displayname, 0);
if(ch != NULL)
xmltv_map(xc, ch);
xmltv_resolve_by_events(xc);
LIST_FOREACH(xm, &xc->xc_maps, xm_link) {
if(xm->xm_isupdated)
continue;
epg_transfer_events(xm->xm_channel, &xc->xc_events, xc->xc_name);
xm->xm_isupdated = 1;
}
}
}
/*
@ -514,7 +383,8 @@ xmltv_thread(void *aux)
xmltv_load();
}
xmltv_update_current();
xmltv_transfer();
}
}

File diff suppressed because it is too large Load diff

View file

@ -19,15 +19,14 @@
#ifndef INPUT_DVB_H
#define INPUT_DVB_H
void dvb_add_adapters(void);
extern struct th_dvb_adapter_list dvb_adapters_probing;
extern struct th_dvb_adapter_list dvb_adapters_running;
extern struct th_dvb_mux_list dvb_muxes;
th_dvb_adapter_t *dvb_alloc_adapter(struct dvb_frontend_parameters *params,
int force);
void dvb_init(void);
int dvb_configure_transport(th_transport_t *t, const char *muxname);
int dvb_start_feed(th_transport_t *t, unsigned int weight);
void dvb_stop_feed(th_transport_t *t);
int dvb_tune(th_dvb_adapter_t *tda, th_dvb_mux_t *tdm, int maylog);
#endif /* INPUT_DVB_H */

95
main.c
View file

@ -19,12 +19,13 @@
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iconv.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
@ -40,16 +41,34 @@
#include "input_v4l.h"
#include "channels.h"
#include "output_client.h"
#include "epg.h"
#include "epg_xmltv.h"
#include "pvr.h"
#include "dispatch.h"
#include "output_multicast.h"
int reftally;
int running;
int xmltvreload;
static pthread_mutex_t tag_mutex = PTHREAD_MUTEX_INITIALIZER;
static uint32_t tag_tally;
uint32_t
tag_get(void)
{
uint32_t r;
pthread_mutex_lock(&tag_mutex);
r = ++tag_tally;
if(r == 0)
r = ++tag_tally;
pthread_mutex_unlock(&tag_mutex);
return r;
}
static void
xmltvdoreload(int x)
{
@ -145,15 +164,16 @@ main(int argc, char **argv)
signal(SIGINT, doexit);
av_register_all();
av_log_set_level(AV_LOG_QUIET);
av_log_set_level(AV_LOG_DEBUG);
client_start();
dvb_add_adapters();
dvb_init();
// v4l_add_adapters();
channels_load();
epg_init();
xmltv_init();
pvr_init();
@ -196,3 +216,66 @@ find_mux_config(const char *muxtype, const char *muxname)
}
static char *
convert_to(const char *in, const char *target_encoding)
{
iconv_t ic;
size_t inlen, outlen, alloced, pos;
char *out, *start;
int r;
inlen = strlen(in);
alloced = 100;
outlen = alloced;
ic = iconv_open(target_encoding, "UTF8");
if(ic == NULL)
return NULL;
out = start = malloc(alloced + 1);
while(inlen > 0) {
r = iconv(ic, (char **)&in, &inlen, &out, &outlen);
if(r == (size_t) -1) {
if(errno == EILSEQ) {
in++;
inlen--;
continue;
}
if(errno == E2BIG) {
pos = alloced - outlen;
alloced *= 2;
start = realloc(start, alloced + 1);
out = start + pos;
outlen = alloced - pos;
continue;
}
break;
}
}
iconv_close(ic);
pos = alloced - outlen;
start[pos] = 0;
return start;
}
char *
utf8toprintable(const char *in)
{
return convert_to(in, "ISO_8859-1");
}
char *
utf8tofilename(const char *in)
{
return convert_to(in, "LATIN1");
}

View file

@ -54,8 +54,7 @@ cprintf(client_t *c, const char *fmt, ...)
static void
client_ip_streamer(struct th_subscription *s, uint8_t *pkt, pidinfo_t *pi,
int streamindex)
client_ip_streamer(struct th_subscription *s, uint8_t *pkt, th_pid_t *pi)
{
client_t *c = s->ths_opaque;
char stoppkt[4];
@ -138,8 +137,8 @@ cr_show(client_t *c, char **argv, int argc)
th_subscription_t *s;
th_transport_t *t;
th_channel_t *ch;
programme_t *p;
int chid = 0;
event_t *e;
char *tmp;
char *txt;
int v, remain;
@ -164,20 +163,42 @@ cr_show(client_t *c, char **argv, int argc)
if(!strcasecmp(subcmd, "channel")) {
while((ch = channel_by_id(chid++)) != NULL) {
TAILQ_FOREACH(ch, &channels, ch_global_link) {
pthread_mutex_lock(&ch->ch_epg_mutex);
p = epg_get_cur_prog(ch);
tmp = utf8toprintable(ch->ch_name);
cprintf(c, "%3d: \"%s\"\n", ch->ch_index, tmp);
free(tmp);
txt = p && p->pr_title ? p->pr_title: "<no current programme>";
remain = p ? p->pr_stop - time(NULL) : 0;
remain /= 60;
epg_lock();
cprintf(c, "%3d: \"%s\": \"%s\" %d:%02d remaining\n",
ch->ch_index, ch->ch_name, txt, remain / 60, remain % 60);
e = epg_event_get_current(ch);
pthread_mutex_unlock(&ch->ch_epg_mutex);
if(e != NULL) {
tmp = utf8toprintable(e->e_title ?: "<no current event>");
remain = e->e_start + e->e_duration - time(NULL);
remain /= 60;
switch(e->e_source) {
case EVENT_SRC_XMLTV:
txt = "xmltv";
break;
case EVENT_SRC_DVB:
txt = "dvb";
break;
default:
txt = "???";
break;
}
cprintf(c, "\tNow: %-40s %2d:%02d [%s] tag: %d\n",
tmp, remain / 60, remain % 60, txt, e->e_tag);
free(tmp);
}
epg_unlock();
LIST_FOREACH(t, &ch->ch_transports, tht_channel_link) {
@ -232,17 +253,6 @@ cr_show(client_t *c, char **argv, int argc)
/*
*
*/
static int
cr_channels_total(client_t *c, char **argv, int argc)
{
cprintf(c, "%d\n", channel_get_channels());
return 0;
}
/*
*
*/
@ -252,10 +262,10 @@ cr_channel_reftag(client_t *c, char **argv, int argc)
{
th_channel_t *ch;
if(argc != 1 || (ch = channel_by_id(atoi(argv[0]))) == NULL)
if(argc != 1 || (ch = channel_by_index(atoi(argv[0]))) == NULL)
return 1;
cprintf(c, "%d\n", ch->ch_ref);
cprintf(c, "%u\n", ch->ch_tag);
return 0;
}
@ -271,16 +281,16 @@ cr_channel_info(client_t *c, char **argv, int argc)
if(argc < 1)
return 1;
if((ch = channel_by_id(atoi(argv[0]))) == NULL)
if((ch = channel_by_index(atoi(argv[0]))) == NULL)
return 1;
cprintf(c,
"displayname = %s\n"
"icon = %s\n"
"reftag = %d\n",
"tag = %d\n",
ch->ch_name,
ch->ch_icon ?: "",
ch->ch_ref);
ch->ch_tag);
return 0;
}
@ -339,7 +349,7 @@ cr_channel_subscribe(client_t *c, char **argv, int argc)
}
}
if((ch = channel_by_id(chindex)) == NULL)
if((ch = channel_by_index(chindex)) == NULL)
return 1;
s = channel_subscribe(ch, c, client_ip_streamer, weight, "client");
@ -377,44 +387,53 @@ cr_streamport(client_t *c, char **argv, int argc)
*/
static int
cr_programme_info(client_t *c, char **argv, int argc)
cr_event_info(client_t *c, char **argv, int argc)
{
th_channel_t *ch;
programme_t *pr;
event_t *e, *x;
uint32_t tag, prev, next;
if(argc < 1)
return 1;
if((ch = channel_by_id(atoi(argv[0]))) == NULL)
return 1;
epg_lock();
pthread_mutex_lock(&ch->ch_epg_mutex);
e = epg_event_find_by_tag(atoi(argv[0]));
pr = argc == 2 ? epg_get_prog_by_id(ch, atoi(argv[1])) :
epg_get_cur_prog(ch);
if(pr == NULL) {
pthread_mutex_unlock(&ch->ch_epg_mutex);
if(e == NULL) {
epg_unlock();
return 1;
}
tag = e->e_tag;
x = TAILQ_PREV(e, event_queue, e_link);
prev = x != NULL ? x->e_tag : 0;
x = TAILQ_NEXT(e, e_link);
next = x != NULL ? x->e_tag : 0;
cprintf(c,
"index = %d\n"
"title = %s\n"
"start = %ld\n"
"stop = %ld\n"
"title = %s\n"
"desc = %s\n"
"reftag = %d\n"
"tag = %u\n"
"prev = %u\n"
"next = %u\n"
"pvrstatus = %c\n",
pr->pr_index,
pr->pr_title ?: "",
pr->pr_start,
pr->pr_stop,
pr->pr_desc ?: "",
pr->pr_ref,
pvr_prog_status(pr));
pthread_mutex_unlock(&ch->ch_epg_mutex);
e->e_start,
e->e_start + e->e_duration,
e->e_title ?: "",
e->e_desc ?: "",
tag,
prev,
next,
pvr_prog_status(e));
epg_unlock();
return 0;
}
@ -423,30 +442,28 @@ cr_programme_info(client_t *c, char **argv, int argc)
*/
static int
cr_programme_bytime(client_t *c, char **argv, int argc)
cr_event_bytime(client_t *c, char **argv, int argc)
{
th_channel_t *ch;
programme_t *pr;
time_t t;
event_t *e;
if(argc < 2)
return 1;
if((ch = channel_by_id(atoi(argv[0]))) == NULL)
if((ch = channel_by_index(atoi(argv[0]))) == NULL)
return 1;
t = atoi(argv[1]);
epg_lock();
pthread_mutex_lock(&ch->ch_epg_mutex);
e = epg_event_find_by_time(ch, atoi(argv[1]));
pr = epg_find_programme_by_time(ch, t);
if(pr == NULL) {
pthread_mutex_unlock(&ch->ch_epg_mutex);
if(e == NULL) {
epg_unlock();
return 1;
}
cprintf(c, "%d\n", pr->pr_index);
pthread_mutex_unlock(&ch->ch_epg_mutex);
cprintf(c, "%u\n", e->e_tag);
epg_unlock();
return 0;
}
@ -455,30 +472,24 @@ cr_programme_bytime(client_t *c, char **argv, int argc)
*/
static int
cr_programme_record(client_t *c, char **argv, int argc)
cr_event_record(client_t *c, char **argv, int argc)
{
th_channel_t *ch;
programme_t *pr;
event_t *e;
if(argc < 1)
return 1;
if((ch = channel_by_id(atoi(argv[0]))) == NULL)
return 1;
epg_lock();
pthread_mutex_lock(&ch->ch_epg_mutex);
pr = argc == 2 ? epg_get_prog_by_id(ch, atoi(argv[1])) :
epg_get_cur_prog(ch);
if(pr == NULL) {
pthread_mutex_unlock(&ch->ch_epg_mutex);
e = epg_event_find_by_tag(atoi(argv[0]));
if(e == NULL) {
epg_unlock();
return 1;
}
pvr_add_recording_by_pr(ch, pr);
pthread_mutex_unlock(&ch->ch_epg_mutex);
pvr_add_recording_by_event(e->e_ch, e);
epg_unlock();
return 0;
}
@ -488,7 +499,7 @@ cr_programme_record(client_t *c, char **argv, int argc)
static int
cr_pvr_entry(client_t *c, pvr_rec_t *pvrr)
{
programme_t *pr;
event_t *e;
if(pvrr == NULL)
return 1;
@ -498,7 +509,7 @@ cr_pvr_entry(client_t *c, pvr_rec_t *pvrr)
"start = %ld\n"
"stop = %ld\n"
"desc = %s\n"
"reftag = %d\n"
"tag = %d\n"
"pvrstatus = %c\n"
"filename = %s\n"
"channel = %d\n",
@ -512,10 +523,9 @@ cr_pvr_entry(client_t *c, pvr_rec_t *pvrr)
pvrr->pvrr_channel->ch_index);
pr = epg_find_programme(pvrr->pvrr_channel, pvrr->pvrr_start,
pvrr->pvrr_stop);
if(pr != NULL)
cprintf(c, "index = %d\n", pr->pr_index);
e = epg_event_find_by_time(pvrr->pvrr_channel, pvrr->pvrr_start);
if(e != NULL)
cprintf(c, "event_tag = %d\n", e->e_tag);
return 0;
}
@ -564,14 +574,13 @@ const struct {
} cr_cmds[] = {
{ "show", cr_show },
{ "streamport", cr_streamport },
{ "channels.total", cr_channels_total },
{ "channel.reftag", cr_channel_reftag },
{ "channel.info", cr_channel_info },
{ "channel.subscribe", cr_channel_subscribe },
{ "channel.unsubscribe", cr_channel_unsubscribe },
{ "programme.info", cr_programme_info },
{ "programme.bytime", cr_programme_bytime },
{ "programme.record", cr_programme_record },
{ "event.info", cr_event_info },
{ "event.bytime", cr_event_bytime },
{ "event.record", cr_event_record },
{ "pvr.getlog", cr_pvr_getlog },
{ "pvr.gettag", cr_pvr_gettag },
};
@ -847,7 +856,7 @@ client_status_update(void)
th_channel_t *ch;
th_dvb_adapter_t *dvb;
th_v4l_adapter_t *v4l;
const char *info;
// const char *info;
th_subscription_t *s;
th_transport_t *t;
int ccerr;
@ -882,7 +891,7 @@ client_status_update(void)
continue;
}
#if 0
if(dvb->tda_fe_status & FE_HAS_LOCK) {
csprintf(c, ch,
"status = 1\n"
@ -912,7 +921,7 @@ client_status_update(void)
info = "No lock, but faint signal";
else
info = "No signal";
csprintf(c, ch,
"status = 0"
"info = %s"
@ -921,6 +930,7 @@ client_status_update(void)
info,
dvb->tda_name,
t->tht_name);
#endif
break;

View file

@ -44,8 +44,7 @@ typedef struct output_multicast {
static void
om_ip_streamer(struct th_subscription *s, uint8_t *pkt, pidinfo_t *pi,
int streamindex)
om_ip_streamer(struct th_subscription *s, uint8_t *pkt, th_pid_t *pi)
{
output_multicast_t *om = s->ths_opaque;
struct sockaddr_in sin;

32
pvr.c
View file

@ -22,7 +22,6 @@
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <iconv.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@ -72,14 +71,14 @@ pvr_init(void)
}
char
pvr_prog_status(programme_t *pr)
pvr_prog_status(event_t *e)
{
pvr_rec_t *pvrr;
LIST_FOREACH(pvrr, &pvrr_global_list, pvrr_global_link) {
if(pvrr->pvrr_start >= pr->pr_start &&
pvrr->pvrr_stop <= pr->pr_stop &&
pvrr->pvrr_channel == pr->pr_ch) {
if(pvrr->pvrr_start >= e->e_start &&
pvrr->pvrr_stop <= e->e_start + e->e_duration &&
pvrr->pvrr_channel == e->e_ch) {
return pvrr->pvrr_status;
}
}
@ -169,15 +168,14 @@ pvr_main_thread(void *aux)
void
pvr_inform_status_change(pvr_rec_t *pvrr)
{
programme_t *pr;
event_t *e;
clients_enq_ref(pvrr->pvrr_ref);
pr = epg_find_programme(pvrr->pvrr_channel, pvrr->pvrr_start,
pvrr->pvrr_stop);
if(pr != NULL)
clients_enq_ref(pr->pr_ref);
e = epg_event_find_by_time(pvrr->pvrr_channel, pvrr->pvrr_start);
if(e != NULL)
clients_enq_ref(e->e_tag);
}
@ -250,7 +248,7 @@ pvr_link_pvrr(pvr_rec_t *pvrr)
break;
}
pvrr->pvrr_ref = ++reftally;
pvrr->pvrr_ref = tag_get();
LIST_INSERT_SORTED(&pvrr_global_list, pvrr, pvrr_global_link, pvr_glob_cmp);
LIST_INSERT_SORTED(l, pvrr, pvrr_work_link, pvr_rec_cmp);
@ -260,10 +258,10 @@ pvr_link_pvrr(pvr_rec_t *pvrr)
void
pvr_add_recording_by_pr(th_channel_t *ch, programme_t *pr)
pvr_add_recording_by_event(th_channel_t *ch, event_t *e)
{
time_t start = pr->pr_start;
time_t stop = pr->pr_stop;
time_t start = e->e_start;
time_t stop = e->e_start + e->e_duration;
time_t now;
pvr_rec_t *pvrr;
@ -287,8 +285,8 @@ pvr_add_recording_by_pr(th_channel_t *ch, programme_t *pr)
pvrr->pvrr_channel = ch;
pvrr->pvrr_start = start;
pvrr->pvrr_stop = stop;
pvrr->pvrr_title = pr->pr_title ? strdup(pr->pr_title) : NULL;
pvrr->pvrr_desc = pr->pr_desc ? strdup(pr->pr_desc) : NULL;
pvrr->pvrr_title = e->e_title ? strdup(e->e_title) : NULL;
pvrr->pvrr_desc = e->e_desc ? strdup(e->e_desc) : NULL;
pvr_link_pvrr(pvrr);
pvr_database_save();

4
pvr.h
View file

@ -26,9 +26,9 @@ extern struct pvr_rec_list pvrr_global_list;
void pvr_init(void);
void pvr_add_recording_by_pr(th_channel_t *ch, programme_t *pr);
void pvr_add_recording_by_event(th_channel_t *ch, event_t *e);
char pvr_prog_status(programme_t *pr);
char pvr_prog_status(event_t *e);
pvr_rec_t *pvr_get_log_entry(int e);

View file

@ -81,7 +81,7 @@ static int pwo_end(pvr_rec_t *pvrr);
static void pvr_generate_filename(pvr_rec_t *pvrr);
static void pvr_record_callback(struct th_subscription *s, uint8_t *pkt,
pidinfo_t *pi, int streamindex);
th_pid_t *pi);
@ -290,8 +290,7 @@ pvr_recorder_thread(void *aux)
*/
static void
pvr_record_callback(struct th_subscription *s, uint8_t *pkt, pidinfo_t *pi,
int streamindex)
pvr_record_callback(struct th_subscription *s, uint8_t *pkt, th_pid_t *pi)
{
pvr_data_t *pd;
pvr_rec_t *pvrr = s->ths_opaque;
@ -301,7 +300,6 @@ pvr_record_callback(struct th_subscription *s, uint8_t *pkt, pidinfo_t *pi,
pd = malloc(sizeof(pvr_data_t));
pd->tsb = malloc(188);
pd->streamindex = streamindex;
memcpy(pd->tsb, pkt, 188);
pd->pi = *pi;
pthread_mutex_lock(&pvrr->pvrr_dq_mutex);
@ -333,15 +331,16 @@ deslashify(char *s)
static void
pvr_generate_filename(pvr_rec_t *pvrr)
{
char tmp1[200];
char fullname[500];
char *x, *chname, *out = tmp1;
size_t ibl, tmp1len = sizeof(tmp1) - 1;
char fullname[1000];
char *x;
int tally = 0;
struct stat st;
iconv_t ic;
char *name = pvrr->pvrr_title;
char *chname;
char *filename;
if(pvrr->pvrr_filename != NULL) {
free(pvrr->pvrr_filename);
pvrr->pvrr_filename = NULL;
@ -350,35 +349,14 @@ pvr_generate_filename(pvr_rec_t *pvrr)
free(pvrr->pvrr_format);
pvrr->pvrr_format = strdup("asf");
if(name != NULL && name[0] != 0) {
/* Convert from utf8 */
filename = utf8tofilename(name && name[0] ? name : "untitled");
deslashify(filename);
ic = iconv_open("ISO_8859-1", "UTF8");
if(ic != (iconv_t) -1) {
memset(tmp1, 0, sizeof(tmp1));
ibl = strlen(pvrr->pvrr_title);
iconv(ic, &name, &ibl, &out, &tmp1len);
iconv_close(ic);
out = tmp1;
} else {
out = name;
}
deslashify(out);
} else {
strcpy(tmp1, "untitled");
out = tmp1;
}
chname = alloca(strlen(pvrr->pvrr_channel->ch_name) + 1);
strcpy(chname, pvrr->pvrr_channel->ch_name);
chname = utf8tofilename(pvrr->pvrr_channel->ch_name);
deslashify(chname);
snprintf(fullname, sizeof(fullname), "%s/%s-%s.%s",
config_get_str("pvrdir", "."), chname, out, pvrr->pvrr_format);
config_get_str("pvrdir", "."), chname, filename, pvrr->pvrr_format);
while(1) {
if(stat(fullname, &st) == -1) {
@ -392,7 +370,7 @@ pvr_generate_filename(pvr_rec_t *pvrr)
tally++;
snprintf(fullname, sizeof(fullname), "%s/%s-%s-%d.%s",
config_get_str("pvrdir", "."), chname, out, tally,
config_get_str("pvrdir", "."), chname, filename, tally,
pvrr->pvrr_format);
}
@ -404,6 +382,9 @@ pvr_generate_filename(pvr_rec_t *pvrr)
x = strrchr(pvrr->pvrr_filename, '/');
pvrr->pvrr_printname = strdup(x ? x + 1 : pvrr->pvrr_filename);
free(filename);
free(chname);
}
@ -496,7 +477,7 @@ pvr_proc_tsb(pvr_rec_t *pvrr, struct ts_pid_head *pidlist, pvr_data_t *pd,
sc = getu32(b, l);
getu16(b, l); /* Skip len */
if(pwo_writepkt(pvrr, s, sc, pd->streamindex, b, l, pd->pi.type))
if(pwo_writepkt(pvrr, s, sc, pd->pi.tp_index, b, l, pd->pi.tp_type))
return 1;
}
tsp->tsp_bufptr = 0;
@ -564,7 +545,7 @@ pwo_init(th_subscription_t *s, pvr_rec_t *pvrr)
int i, err;
th_transport_t *t = s->ths_transport;
pwo_ffmpeg_t *pf;
pidinfo_t *p;
th_pid_t *p;
AVStream *st;
AVCodec *codec;
const char *cname;
@ -607,10 +588,10 @@ pwo_init(th_subscription_t *s, pvr_rec_t *pvrr)
av_set_parameters(pf->fctx, NULL); /* Fix NULL -stuff */
for(i = 0; i < t->tht_npids; i++) {
p = t->tht_pids + i;
LIST_FOREACH(p, &t->tht_pids, tp_link) {
i = p->tp_index;
switch(p->type) {
switch(p->tp_type) {
case HTSTV_MPEG2VIDEO:
break;
case HTSTV_MPEG2AUDIO:
@ -629,7 +610,7 @@ pwo_init(th_subscription_t *s, pvr_rec_t *pvrr)
st->codec = avcodec_alloc_context();
switch(p->type) {
switch(p->tp_type) {
default:
continue;
case HTSTV_MPEG2VIDEO:

View file

@ -36,7 +36,7 @@
#include "tvhead.h"
#include "dispatch.h"
#include "input_dvb.h"
#include "dvb_dvr.h"
#include "input_v4l.h"
#include "input_iptv.h"
#include "teletext.h"
@ -48,6 +48,21 @@
static pthread_mutex_t subscription_mutex = PTHREAD_MUTEX_INITIALIZER;
/*
*
*/
void
subscription_lock(void)
{
pthread_mutex_lock(&subscription_mutex);
}
void
subscription_unlock(void)
{
pthread_mutex_unlock(&subscription_mutex);
}
/*
*
@ -186,7 +201,7 @@ subscription_unsubscribe(th_subscription_t *s)
{
pthread_mutex_lock(&subscription_mutex);
s->ths_callback(s, NULL, NULL, 0);
s->ths_callback(s, NULL, NULL);
LIST_REMOVE(s, ths_global_link);
LIST_REMOVE(s, ths_channel_link);
@ -221,8 +236,7 @@ subscription_sort(th_subscription_t *a, th_subscription_t *b)
th_subscription_t *
channel_subscribe(th_channel_t *ch, void *opaque,
void (*callback)(struct th_subscription *s,
uint8_t *pkt, pidinfo_t *pi,
int streamindex),
uint8_t *pkt, th_pid_t *pi),
unsigned int weight,
const char *name)
{
@ -289,7 +303,7 @@ transport_flush_subscribers(th_transport_t *t)
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL) {
LIST_REMOVE(s, ths_transport_link);
s->ths_transport = NULL;
s->ths_callback(s, NULL, NULL, 0);
s->ths_callback(s, NULL, NULL);
}
}
@ -320,17 +334,14 @@ transport_compute_weight(struct th_transport_list *head)
void
transport_recv_tsb(th_transport_t *t, int pid, uint8_t *tsb)
{
pidinfo_t *pi = NULL;
th_pid_t *pi = NULL;
th_subscription_t *s;
th_channel_t *ch;
int i, cc, err = 0;
int cc, err = 0;
for(i = 0; i < t->tht_npids; i++) {
if(t->tht_pids[i].pid == pid) {
pi = t->tht_pids + i;
LIST_FOREACH(pi, &t->tht_pids, tp_link)
if(pi->tp_pid == pid)
break;
}
}
if(pi == NULL)
return;
@ -338,44 +349,45 @@ transport_recv_tsb(th_transport_t *t, int pid, uint8_t *tsb)
cc = tsb[3] & 0xf;
if(tsb[3] & 0x10) {
if(pi->cc_valid && cc != pi->cc) {
if(pi->tp_cc_valid && cc != pi->tp_cc) {
/* Incorrect CC */
avgstat_add(&t->tht_cc_errors, 1, dispatch_clock);
err = 1;
}
pi->cc_valid = 1;
pi->cc = (cc + 1) & 0xf;
pi->tp_cc_valid = 1;
pi->tp_cc = (cc + 1) & 0xf;
}
avgstat_add(&t->tht_rate, 188, dispatch_clock);
ch = t->tht_channel;
if(pi->type == HTSTV_TELETEXT) {
if(pi->tp_type == HTSTV_TELETEXT) {
/* teletext */
teletext_input(t, tsb);
return;
}
tsb[0] = pi->type;
tsb[0] = pi->tp_type;
pthread_mutex_lock(&subscription_mutex);
LIST_FOREACH(s, &t->tht_subscriptions, ths_transport_link) {
s->ths_total_err += err;
s->ths_callback(s, tsb, pi, i);
s->ths_callback(s, tsb, pi);
}
pthread_mutex_unlock(&subscription_mutex);
}
static void
transport_monitor(void *aux)
{
{
th_transport_t *t = aux;
int v;
stimer_add(transport_monitor, t, 1);
if(t->tht_status == TRANSPORT_IDLE)
return;
@ -405,5 +417,26 @@ transport_monitor_init(th_transport_t *t)
avgstat_init(&t->tht_cc_errors, 3600);
avgstat_init(&t->tht_rate, 10);
dispatch_add_1sec_event(transport_monitor, t);
stimer_add(transport_monitor, t, 5);
}
void
transport_add_pid(th_transport_t *t, uint16_t pid, tv_streamtype_t type)
{
th_pid_t *pi;
int i = 0;
LIST_FOREACH(pi, &t->tht_pids, tp_link) {
i++;
if(pi->tp_pid == pid)
return;
}
pi = calloc(1, sizeof(th_pid_t));
pi->tp_index = i;
pi->tp_pid = pid;
pi->tp_type = type;
pi->tp_demuxer_fd = -1;
LIST_INSERT_HEAD(&t->tht_pids, pi, tp_link);
}

View file

@ -23,6 +23,9 @@
void subscription_unsubscribe(th_subscription_t *s);
void subscription_set_weight(th_subscription_t *s, unsigned int weight);
void subscription_lock(void);
void subscription_unlock(void);
unsigned int transport_compute_weight(struct th_transport_list *head);
void transport_flush_subscribers(th_transport_t *t);
@ -31,11 +34,14 @@ void transport_recv_tsb(th_transport_t *t, int pid, uint8_t *tsb);
void transport_monitor_init(th_transport_t *t);
void transport_add_pid(th_transport_t *t, uint16_t pid, tv_streamtype_t type);
int transport_set_channel(th_transport_t *th, const char *name);
th_subscription_t *channel_subscribe(th_channel_t *ch, void *opaque,
void (*ths_callback)
(struct th_subscription *s,
uint8_t *pkt, pidinfo_t *pi,
int streamindex),
uint8_t *pkt, th_pid_t *pi),
unsigned int weight,
const char *name);

190
tvhead.h
View file

@ -35,14 +35,19 @@ TAILQ_HEAD(th_channel_queue, th_channel);
LIST_HEAD(th_dvb_adapter_list, th_dvb_adapter);
LIST_HEAD(th_v4l_adapter_list, th_v4l_adapter);
LIST_HEAD(client_list, client);
LIST_HEAD(programme_list, programme);
LIST_HEAD(event_list, event);
TAILQ_HEAD(event_queue, event);
LIST_HEAD(pvr_rec_list, pvr_rec);
TAILQ_HEAD(ref_update_queue, ref_update);
LIST_HEAD(th_transport_list, th_transport);
LIST_HEAD(th_dvb_mux_list, th_dvb_mux);
LIST_HEAD(th_dvb_mux_instance_list, th_dvb_mux_instance);
LIST_HEAD(th_pid_list, th_pid);
extern time_t dispatch_clock;
extern struct th_transport_list all_transports;
extern int reftally;
uint32_t tag_get(void);
typedef struct th_v4l_adapter {
@ -64,36 +69,102 @@ typedef struct th_v4l_adapter {
} th_v4l_adapter_t;
/*
*
*/
typedef struct th_dvb_mux_instance {
LIST_ENTRY(th_dvb_mux_instance) tdmi_mux_link;
LIST_ENTRY(th_dvb_mux_instance) tdmi_adapter_link;
struct th_dvb_mux *tdmi_mux;
struct th_dvb_adapter *tdmi_adapter;
fe_status_t tdmi_fe_status;
uint16_t tdmi_snr, tdmi_signal;
uint32_t tdmi_ber, tdmi_uncorrected_blocks;
time_t tdmi_time;
LIST_HEAD(, th_dvb_table) tdmi_tables;
enum {
TDMI_CONFIGURED,
TDMI_INITIAL_SCAN,
TDMI_ACTIVE,
} tdmi_state;
void *tdmi_initial_scan_timer;
const char *tdmi_status;
} th_dvb_mux_instance_t;
/*
*
*/
typedef struct th_dvb_table {
LIST_ENTRY(th_dvb_table) tdt_link;
void *tdt_handle;
struct th_dvb_mux_instance *tdt_tdmi;
int tdt_count; /* times seen */
void *tdt_opaque;
int (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
uint8_t tableid, void *opaque);
} th_dvb_table_t;
/*
*
*/
typedef struct th_dvb_mux {
LIST_ENTRY(th_dvb_mux) tdm_global_link;
struct th_dvb_mux_instance_list tdm_instances;
struct dvb_frontend_parameters tdm_fe_params;
const char *tdm_name;
const char *tdm_title;
} th_dvb_mux_t;
typedef struct th_dvb_adapter {
LIST_ENTRY(th_dvb_adapter) tda_adapter_to_mux_link;
struct th_dvb_mux_instance_list tda_muxes_configured;
struct th_dvb_mux_instance_list tda_muxes_active;
th_dvb_mux_instance_t *tda_mux_current;
const char *tda_path;
const char *tda_name;
LIST_ENTRY(th_dvb_adapter) tda_link;
LIST_HEAD(, th_dvb_mux) tda_muxes;
int tda_running;
struct dvb_frontend_parameters tda_fe_params;
struct th_transport_list tda_transports;
int tda_fe_fd;
struct dvb_frontend_info tda_fe_info;
fe_status_t tda_fe_status;
uint16_t tda_snr, tda_signal;
uint32_t tda_ber, tda_uncorrected_blocks;
char *tda_demux_path;
int tda_dvr_fd;
char *tda_dvr_path;
time_t tda_time;
struct th_transport_list tda_transports; /* Currently bound transports */
} th_dvb_adapter_t;
/*
*
*
@ -112,15 +183,17 @@ typedef struct th_iptv_adapter {
typedef struct pidinfo {
int pid;
tv_streamtype_t type;
int demuxer_fd;
typedef struct th_pid {
LIST_ENTRY(th_pid) tp_link;
uint16_t tp_pid;
uint8_t tp_cc; /* Last CC */
uint8_t tp_cc_valid; /* Is CC valid at all? */
int cc; /* Last CC */
int cc_valid; /* Is CC valid at all? */
tv_streamtype_t tp_type;
int tp_demuxer_fd;
int tp_index;
} pidinfo_t;
} th_pid_t;
typedef enum {
COMMERCIAL_UNKNOWN,
@ -133,6 +206,8 @@ typedef struct th_transport {
const char *tht_name;
LIST_ENTRY(th_transport) tht_global_link;
enum {
TRANSPORT_DVB,
TRANSPORT_IPTV,
@ -150,11 +225,13 @@ typedef struct th_transport {
int tht_tt_rundown_content_length;
time_t tht_tt_clock; /* Network clock as determined by teletext
decoder */
int tht_prio;
pidinfo_t *tht_pids;
int tht_npids;
struct th_pid_list tht_pids;
uint16_t tht_dvb_network_id;
uint16_t tht_dvb_transport_id;
uint16_t tht_dvb_service_id;
avgstat_t tht_cc_errors;
avgstat_t tht_rate;
@ -165,13 +242,12 @@ typedef struct th_transport {
LIST_ENTRY(th_transport) tht_channel_link;
struct th_channel *tht_channel;
LIST_HEAD(, th_subscription) tht_subscriptions;
union {
struct {
struct dvb_frontend_parameters fe_params;
struct th_dvb_mux *mux;
struct th_dvb_adapter *adapter;
} dvb;
@ -202,7 +278,7 @@ typedef struct th_transport {
#define tht_v4l_frequency u.v4l.frequency
#define tht_v4l_adapter u.v4l.adapter
#define tht_dvb_fe_params u.dvb.fe_params
#define tht_dvb_mux u.dvb.mux
#define tht_dvb_adapter u.dvb.adapter
#define tht_iptv_group_addr u.iptv.group_addr
@ -240,17 +316,6 @@ typedef struct tt_decoder {
} tt_decoder_t;
typedef struct th_proglist {
struct programme_list tpl_programs; // linked list of all programs
unsigned int tpl_nprograms; // number of programs
struct programme **tpl_prog_vec; // array pointing to all programs
struct programme *tpl_prog_current; // pointer to current programme
const char *tpl_refname; // channel reference name
} th_proglist_t;
/*
* Channel definition
*/
@ -272,27 +337,15 @@ typedef struct th_channel {
struct tt_decoder ch_tt;
int ch_ref;
int ch_tag;
int ch_teletext_rundown;
pthread_mutex_t ch_epg_mutex; ///< protects the epg fields
th_proglist_t ch_xmltv;
struct event_queue ch_epg_events;
struct event *ch_epg_cur_event;
} th_channel_t;
/*
* XXXX: DELETE ME?
*/
typedef struct ref_update {
TAILQ_ENTRY(ref_update) link;
int ref;
} ref_update_t;
/*
* Subscription
*/
@ -313,8 +366,8 @@ typedef struct th_subscription {
LIST_ENTRY(th_subscription) ths_subscriber_link; /* Caller is responsible
for this link */
void (*ths_callback)(struct th_subscription *s, uint8_t *pkt, pidinfo_t *pi,
int streamindex);
void (*ths_callback)(struct th_subscription *s, uint8_t *pkt, th_pid_t *pi);
void *ths_opaque;
char *ths_pkt;
@ -361,26 +414,31 @@ typedef struct client {
/*
* EPG Programme
* EPG event
*/
typedef struct programme {
LIST_ENTRY(programme) pr_link;
typedef struct event {
TAILQ_ENTRY(event) e_link;
LIST_ENTRY(event) e_hash_link;
time_t pr_start;
time_t pr_stop;
time_t e_start; /* UTC time */
int e_duration; /* in seconds */
char *pr_title;
char *pr_desc;
const char *e_title; /* UTF-8 encoded */
const char *e_desc; /* UTF-8 encoded */
th_channel_t *pr_ch;
uint16_t e_event_id; /* DVB event id */
int pr_ref;
int pr_index;
uint32_t e_tag;
int pr_delete_me;
th_channel_t *e_ch;
} programme_t;
int e_source; /* higer is better, and we never downgrade */
#define EVENT_SRC_XMLTV 1
#define EVENT_SRC_DVB 2
} event_t;
/*
@ -391,8 +449,7 @@ typedef struct programme {
typedef struct pvr_data {
TAILQ_ENTRY(pvr_data) link;
uint8_t *tsb;
pidinfo_t pi;
int streamindex;
th_pid_t pi;
} pvr_data_t;
@ -439,4 +496,7 @@ extern struct pvr_rec_list pvrr_global_list;
config_entry_t *find_mux_config(const char *muxtype, const char *muxname);
char *utf8toprintable(const char *in);
char *utf8tofilename(const char *in);
#endif /* TV_HEAD_H */