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:
parent
920fa134ab
commit
fa329cec17
25 changed files with 2646 additions and 948 deletions
2
Makefile
2
Makefile
|
@ -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
|
||||
|
|
142
channels.c
142
channels.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
147
dispatch.c
147
dispatch.c
|
@ -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;
|
||||
|
|
|
@ -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
233
dvb_dvr.c
Normal 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
28
dvb_dvr.h
Normal 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
149
dvb_pmt.c
Normal 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
26
dvb_pmt.h
Normal 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
160
dvb_support.c
Normal 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
47
dvb_support.h
Normal 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
461
epg.c
|
@ -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
30
epg.h
|
@ -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 */
|
||||
|
|
480
epg_xmltv.c
480
epg_xmltv.c
|
@ -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 = ν
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1002
input_dvb.c
1002
input_dvb.c
File diff suppressed because it is too large
Load diff
11
input_dvb.h
11
input_dvb.h
|
@ -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
95
main.c
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
194
output_client.c
194
output_client.c
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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
32
pvr.c
|
@ -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
4
pvr.h
|
@ -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);
|
||||
|
||||
|
|
63
pvr_rec.c
63
pvr_rec.c
|
@ -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:
|
||||
|
|
75
transports.c
75
transports.c
|
@ -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);
|
||||
}
|
||||
|
|
10
transports.h
10
transports.h
|
@ -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
190
tvhead.h
|
@ -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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue