Initial take on making tvheadend multithreaded instead of relying on a

poll/dispatcher framework.

The top reasons for going multithreaded is:

* Take adventage of multicore CPUs
* No need to put slow operations in ugly helper threads now that the entire app is designed for multithreads
* Some of the timing sensetive tasks (IPTV output / RTP / UDP pacing) can run on RT priority.
This commit is contained in:
Andreas Öman 2008-08-29 15:29:33 +00:00
parent a18df3bc78
commit cb8266e3ea
35 changed files with 1697 additions and 1892 deletions

View file

@ -1,46 +1,22 @@
-include ../config.mak
SRCS = main.c dispatch.c channels.c transports.c teletext.c psi.c \
subscriptions.c mux.c tsdemux.c buffer.c tcp.c \
resolver.c tsmux.c parsers.c bitstream.c parser_h264.c spawn.c \
notify.c intercom.c access.c serviceprobe.c dtable.c
SRCS = main.c access.c dtable.c tcp.c http.c notify.c
SRCS += http.c
SRCS += buffer.c channels.c subscriptions.c transports.c
SRCS += htsp.c htsp_muxer.c rpc.c
SRCS += pvr.c autorec.c ffmuxer.c
SRCS += epg.c epg_xmltv.c
SRCS += psi.c parsers.c parser_h264.c tsdemux.c bitstream.c
VPATH += dvb
SRCS += dvb.c dvb_support.c dvb_dvr.c dvb_preconf.c dvb_fe.c dvb_tables.c \
diseqc.c dvb_adapter.c dvb_multiplex.c dvb_transport.c
SRCS += dvb.c dvb_support.c dvb_fe.c dvb_tables.c \
diseqc.c dvb_adapter.c dvb_multiplex.c dvb_transport.c dvb_preconf.c
SRCS += iptv_input.c iptv_output.c
SRCS += avgen.c file_input.c
SRCS += rtsp.c rtp.c
SRCS += v4l.c
SRCS += cwc.c krypt.c
VPATH += ffdecsa
SRCS += FFdecsa.c
#
# Primary web interface
#
VPATH += webui
SRCS += webui.c extjs.c comet.c simpleui.c
JSSRCS += tvheadend.js extensions.js acleditor.js cwceditor.js \
dvb.js
CSSSRCS += ext.css
SRCS += webui.c comet.c extjs.c
PROGPATH = $(HTS_BUILD_ROOT)/tvheadend
PROG = tvheadend

View file

@ -57,8 +57,8 @@ static int store_tally;
static void storage_wipe(void);
static void storage_mem_enq(th_pkt_t *pkt);
static void storage_disk_enq(th_pkt_t *pkt);
static void storage_mem_enq(th_stream_t *st, th_pkt_t *pkt);
static void storage_disk_enq(th_stream_t *st, th_pkt_t *pkt);
static void storage_deref(th_storage_t *s);
@ -68,7 +68,7 @@ static void storage_deref(th_storage_t *s);
void
pkt_init(void)
{
store_path = config_get_str("trickplay-path", "/storage/streambuffer");
store_path = NULL;
if(store_path != NULL)
storage_wipe();
@ -148,11 +148,11 @@ pkt_alloc(void *data, size_t datalen, int64_t pts, int64_t dts)
*
*/
th_pkt_t *
pkt_copy(th_pkt_t *orig)
pkt_copy(th_stream_t *st, th_pkt_t *orig)
{
th_pkt_t *pkt;
pkt_load(orig);
pkt_load(st, orig);
if(orig->pkt_payload == NULL)
return NULL;
@ -174,10 +174,8 @@ pkt_copy(th_pkt_t *orig)
*
*/
void
pkt_store(th_pkt_t *pkt)
pkt_store(th_stream_t *st, th_pkt_t *pkt)
{
th_stream_t *st = pkt->pkt_stream;
if(pkt->pkt_on_stream_queue)
return;
@ -187,8 +185,8 @@ pkt_store(th_pkt_t *pkt)
/* Persistent buffer management */
storage_mem_enq(pkt);
storage_disk_enq(pkt);
storage_mem_enq(st, pkt);
storage_disk_enq(st, pkt);
if(pkt->pkt_storage)
pwrite(pkt->pkt_storage->ts_fd, pkt->pkt_payload, pkt->pkt_payloadlen,
@ -200,10 +198,8 @@ pkt_store(th_pkt_t *pkt)
* Force flush of a packet
*/
void
pkt_unstore(th_pkt_t *pkt)
pkt_unstore(th_stream_t *st, th_pkt_t *pkt)
{
th_stream_t *st = pkt->pkt_stream;
assert(pkt->pkt_on_stream_queue == 1);
TAILQ_REMOVE(&st->st_pktq, pkt, pkt_queue_link);
pkt->pkt_on_stream_queue = 0;
@ -227,14 +223,14 @@ pkt_unstore(th_pkt_t *pkt)
*
*/
int
pkt_load(th_pkt_t *pkt)
pkt_load(th_stream_t *st, th_pkt_t *pkt)
{
if(pkt->pkt_payload == NULL && pkt->pkt_storage != NULL) {
pkt->pkt_payload = malloc(pkt->pkt_payloadlen +
FF_INPUT_BUFFER_PADDING_SIZE);
pread(pkt->pkt_storage->ts_fd, pkt->pkt_payload, pkt->pkt_payloadlen,
pkt->pkt_storage_offset);
storage_mem_enq(pkt);
storage_mem_enq(st, pkt);
}
return pkt->pkt_payload == NULL ? -1 : 0;
}
@ -267,7 +263,7 @@ storage_deref(th_storage_t *s)
*
*/
static void
storage_mem_enq(th_pkt_t *pkt)
storage_mem_enq(th_stream_t *st, th_pkt_t *pkt)
{
TAILQ_INSERT_TAIL(&store_mem_queue, pkt, pkt_mem_link);
store_mem_size += pkt->pkt_payloadlen;
@ -282,7 +278,7 @@ storage_mem_enq(th_pkt_t *pkt)
pkt->pkt_payload = NULL;
if(pkt->pkt_storage == NULL)
pkt_unstore(pkt);
pkt_unstore(st, pkt);
}
}
@ -293,7 +289,7 @@ storage_mem_enq(th_pkt_t *pkt)
*
*/
static void
storage_disk_enq(th_pkt_t *pkt)
storage_disk_enq(th_stream_t *st, th_pkt_t *pkt)
{
th_storage_t *s;
char fbuf[500];
@ -332,7 +328,7 @@ storage_disk_enq(th_pkt_t *pkt)
pkt = TAILQ_FIRST(&store_disk_queue);
if(pkt->pkt_refcount > 1)
printf("UNSTORE of reference packet %p\n", pkt);
pkt_unstore(pkt);
pkt_unstore(st, pkt);
}
}

View file

@ -27,13 +27,13 @@ void pkt_init(void);
th_pkt_t *pkt_alloc(void *data, size_t datalen, int64_t pts, int64_t dts);
th_pkt_t *pkt_copy(th_pkt_t *pkt);
th_pkt_t *pkt_copy(th_stream_t *st, th_pkt_t *pkt);
void pkt_store(th_pkt_t *pkt);
void pkt_store(th_stream_t *st, th_pkt_t *pkt);
void pkt_unstore(th_pkt_t *pkt);
void pkt_unstore(th_stream_t *st, th_pkt_t *pkt);
int pkt_load(th_pkt_t *pkt);
int pkt_load(th_stream_t *st, th_pkt_t *pkt);
void *pkt_payload(th_pkt_t *pkt);

View file

@ -29,12 +29,9 @@
#include <string.h>
#include <dirent.h>
#include <libhts/htscfg.h>
#include <libhts/htssettings.h>
#include "tvhead.h"
#include "v4l.h"
#include "iptv_input.h"
#include "psi.h"
#include "channels.h"
#include "transports.h"
@ -167,6 +164,9 @@ channel_t *
channel_find_by_name(const char *name, int create)
{
channel_t skel, *ch;
lock_assert(&global_lock);
skel.ch_name = (char *)name;
ch = RB_FIND(&channel_name_tree, &skel, ch_name_link, channelcmp);
if(ch != NULL || create == 0)
@ -182,6 +182,9 @@ channel_t *
channel_find_by_identifier(int id)
{
channel_t skel, *ch;
lock_assert(&global_lock);
skel.ch_id = id;
ch = RB_FIND(&channel_identifier_tree, &skel, ch_identifier_link, chidcmp);
return ch;
@ -214,6 +217,9 @@ static void
channel_save(channel_t *ch)
{
htsmsg_t *m = htsmsg_create();
lock_assert(&global_lock);
if(ch->ch_icon != NULL)
htsmsg_add_str(m, "icon", ch->ch_icon);
@ -232,6 +238,8 @@ channel_rename(channel_t *ch, const char *newname)
{
th_transport_t *t;
lock_assert(&global_lock);
if(channel_find_by_name(newname, 0))
return -1;
@ -262,13 +270,15 @@ channel_delete(channel_t *ch)
th_transport_t *t;
th_subscription_t *s;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" deleted",
ch->ch_name);
pvr_destroy_by_channel(ch);
abort();//pvr_destroy_by_channel(ch);
while((t = LIST_FIRST(&ch->ch_transports)) != NULL) {
transport_unmap_channel(t);
abort();//transport_unmap_channel(t);
t->tht_config_change(t);
}
@ -277,9 +287,9 @@ channel_delete(channel_t *ch)
s->ths_channel = NULL;
}
epg_destroy_by_channel(ch);
abort();//epg_destroy_by_channel(ch);
autorec_destroy_by_channel(ch);
abort();//autorec_destroy_by_channel(ch);
hts_settings_remove("channels/%s", ch->ch_name);
@ -304,14 +314,16 @@ void
channel_merge(channel_t *dst, channel_t *src)
{
th_transport_t *t;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" merged into \"%s\"",
src->ch_name, dst->ch_name);
while((t = LIST_FIRST(&src->ch_transports)) != NULL) {
transport_unmap_channel(t);
abort();//transport_unmap_channel(t);
transport_map_channel(t, dst);
abort();//transport_map_channel(t, dst);
t->tht_config_change(t);
}
@ -324,6 +336,8 @@ channel_merge(channel_t *dst, channel_t *src)
void
channel_set_icon(channel_t *ch, const char *icon)
{
lock_assert(&global_lock);
free(ch->ch_icon);
ch->ch_icon = icon ? strdup(icon) : NULL;
channel_save(ch);

6
cwc.h
View file

@ -26,7 +26,7 @@ TAILQ_HEAD(cwc_queue, cwc);
extern struct cwc_queue cwcs;
typedef struct cwc {
tcp_session_t cwc_tcp_session; /* Must be first */
// tcp_session_t cwc_tcp_session; /* Must be first */
TAILQ_ENTRY(cwc) cwc_link;
@ -59,9 +59,9 @@ typedef struct cwc {
char *cwc_password_salted; /* salted version */
char *cwc_comment;
dtimer_t cwc_idle_timer;
// dtimer_t cwc_idle_timer;
dtimer_t cwc_send_ka_timer;
// dtimer_t cwc_send_ka_timer;
char *cwc_id;

View file

@ -1,251 +0,0 @@
/*
* 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
* 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <libhts/htsq.h>
#include "dispatch.h"
#define EPOLL_FDS_PER_ROUND 100
time_t dispatch_clock;
static int epoll_fd;
typedef struct epoll_entry {
void (*callback)(int events, void *opaque, int fd);
void *opaque;
struct epoll_event event;
int fd;
int refcnt;
} epoll_entry_t;
LIST_HEAD(, dtimer) dispatch_timers;
static int
stimercmp(dtimer_t *a, dtimer_t *b)
{
if(a->dti_expire < b->dti_expire)
return -1;
else if(a->dti_expire > b->dti_expire)
return 1;
return 0;
}
void
dtimer_arm_hires(dtimer_t *ti, dti_callback_t *callback, void *aux, int64_t t)
{
if(ti->dti_callback != NULL)
LIST_REMOVE(ti, dti_link);
ti->dti_expire = t;
ti->dti_opaque = aux;
ti->dti_callback = callback;
LIST_INSERT_SORTED(&dispatch_timers, ti, dti_link, stimercmp);
}
void
dtimer_arm(dtimer_t *ti, dti_callback_t *callback, void *opaque, int delta)
{
time_t now;
time(&now);
dtimer_arm_hires(ti, callback, opaque,
(uint64_t)(now + delta) * 1000000ULL);
}
static int
stimer_next(void)
{
dtimer_t *ti = LIST_FIRST(&dispatch_timers);
int delta;
if(ti == NULL)
return -1;
delta = (ti->dti_expire - getclock_hires() + 999) / 1000;
return delta > 0 ? delta : 0;
}
static void
stimer_dispatch(int64_t now)
{
dtimer_t *ti;
dti_callback_t *cb;
while((ti = LIST_FIRST(&dispatch_timers)) != NULL) {
if(ti->dti_expire > now + 100ULL)
break;
LIST_REMOVE(ti, dti_link);
cb = ti->dti_callback;
ti->dti_callback = NULL;
cb(ti->dti_opaque, now);
}
}
int
dispatch_init(void)
{
epoll_fd = epoll_create(100);
if(epoll_fd == -1) {
perror("epoll_create()");
exit(1);
}
return 0;
}
void *
dispatch_addfd(int fd, void (*callback)(int events, void *opaque, int fd),
void *opaque, int flags)
{
struct epoll_entry *e;
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
e = calloc(1, sizeof(struct epoll_entry));
e->callback = callback;
e->opaque = opaque;
e->fd = fd;
e->event.events = EPOLLERR | EPOLLHUP;
e->event.data.ptr = e;
e->refcnt = 1;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, e->fd, &e->event);
dispatch_set(e, flags);
return e;
}
void
dispatch_set(void *handle, int flags)
{
struct epoll_entry *e = handle;
if(flags & DISPATCH_PRI)
e->event.events |= EPOLLPRI;
if(flags & DISPATCH_READ)
e->event.events |= EPOLLIN;
if(flags & DISPATCH_WRITE)
e->event.events |= EPOLLOUT;
epoll_ctl(epoll_fd, EPOLL_CTL_MOD, e->fd, &e->event);
}
void
dispatch_clr(void *handle, int flags)
{
struct epoll_entry *e = handle;
if(flags & DISPATCH_PRI)
e->event.events &= ~EPOLLPRI;
if(flags & DISPATCH_READ)
e->event.events &= ~EPOLLIN;
if(flags & DISPATCH_WRITE)
e->event.events &= ~EPOLLOUT;
epoll_ctl(epoll_fd, EPOLL_CTL_MOD, e->fd, &e->event);
}
static void
dispatch_deref(struct epoll_entry *e)
{
if(e->refcnt > 1)
e->refcnt--;
else
free(e);
}
int
dispatch_delfd(void *handle)
{
struct epoll_entry *e = handle;
int fd = e->fd;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &e->event);
e->callback = NULL;
e->opaque = NULL;
dispatch_deref(e);
return fd;
}
void
dispatcher(void)
{
struct epoll_entry *e;
int i, n, delta;
struct timeval tv;
int64_t now;
static struct epoll_event events[EPOLL_FDS_PER_ROUND];
delta = stimer_next();
n = epoll_wait(epoll_fd, events, EPOLL_FDS_PER_ROUND, delta);
gettimeofday(&tv, NULL);
dispatch_clock = tv.tv_sec;
now = (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
for(i = 0; i < n; i++) {
e = events[i].data.ptr;
e->refcnt++;
}
for(i = 0; i < n; i++) {
e = events[i].data.ptr;
if(e->callback == NULL)
continue; /* poll entry has been free'd during loop */
e->callback(((events[i].events & (EPOLLERR | EPOLLHUP)) ?
DISPATCH_ERR : 0 ) |
((events[i].events & EPOLLIN) ? DISPATCH_READ : 0 ) |
((events[i].events & EPOLLOUT) ? DISPATCH_WRITE : 0 ) |
((events[i].events & EPOLLPRI) ? DISPATCH_PRI : 0 ),
e->opaque, e->fd);
}
for(i = 0; i < n; i++) {
e = events[i].data.ptr;
dispatch_deref(e);
}
stimer_dispatch(now);
}

View file

@ -1,68 +0,0 @@
/*
* socket fd dispathcer
* 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 DISPATCH_H
#define DISPATCH_H
#include <sys/time.h>
#include "tvhead.h"
extern time_t dispatch_clock;
#define DISPATCH_READ 0x1
#define DISPATCH_WRITE 0x2
#define DISPATCH_ERR 0x4
#define DISPATCH_PRI 0x8
static inline int64_t
getclock_hires(void)
{
int64_t now;
struct timeval tv;
gettimeofday(&tv, NULL);
now = (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
return now;
}
int dispatch_init(void);
void *dispatch_addfd(int fd,
void (*callback)(int events, void *opaque, int fd),
void *opaque, int flags);
int dispatch_delfd(void *handle);
void dispatch_set(void *handle, int flags);
void dispatch_clr(void *handle, int flags);
void dispatcher(void);
void dtimer_arm(dtimer_t *ti, dti_callback_t *callback, void *aux, int delta);
void dtimer_arm_hires(dtimer_t *ti, dti_callback_t *callback,
void *aux, int64_t t);
static inline void
dtimer_disarm(dtimer_t *ti)
{
if(ti->dti_callback) {
LIST_REMOVE(ti, dti_link);
ti->dti_callback = NULL;
}
}
#define dtimer_isarmed(ti) ((ti)->dti_callback ? 1 : 0)
#endif /* DISPATCH_H */

View file

@ -16,25 +16,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include "tvhead.h"
#include "dvb.h"
void
dvb_init(void)
{

View file

@ -38,7 +38,7 @@ void dvb_init(void);
*/
void dvb_adapter_init(void);
void dvb_adapter_mux_scanner(void *aux, int64_t now);
void dvb_adapter_mux_scanner(void *aux);
void dvb_adapter_notify_reload(th_dvb_adapter_t *tda);
@ -57,12 +57,8 @@ void dvb_mux_save(th_dvb_mux_instance_t *tdmi);
void dvb_mux_load(th_dvb_adapter_t *tda);
void dvb_mux_unref(th_dvb_mux_instance_t *tdmi);
void dvb_mux_destroy(th_dvb_mux_instance_t *tdmi);
void dvb_mux_fastswitch(th_dvb_mux_instance_t *tdmi);
th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda,
struct dvb_frontend_parameters *fe_param,
int polarisation, int switchport,
@ -71,40 +67,33 @@ th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda,
void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *name);
void dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog,
tdmi_state_t state);
/**
* DVB Transport (aka DVB service)
*/
void dvb_transport_load(th_dvb_mux_instance_t *tdmi);
th_transport_t *dvb_transport_find(th_dvb_mux_instance_t *tdmi,
uint16_t sid, int pmt_pid, int *created);
void tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi);
void tdmi_stop(th_dvb_mux_instance_t *tdmi);
/**
* DVB Frontend
*/
void dvb_fe_start(th_dvb_adapter_t *tda);
void dvb_fe_tune(th_dvb_mux_instance_t *tdmi);
void dvb_fe_flush(th_dvb_mux_instance_t *tdmi);
void dvb_fe_stop(th_dvb_mux_instance_t *tdmi);
/**
* DVB Tables
*/
void dvb_table_init(th_dvb_adapter_t *tda);
void dvb_table_add_default(th_dvb_mux_instance_t *tdmi);
void dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t,
int pmt_pid);
void dvb_tdt_destroy(th_dvb_table_t *tdt);
void dvb_table_flush_all(th_dvb_mux_instance_t *tdmi);
#endif /* DVB_H_ */

View file

@ -29,7 +29,6 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
@ -38,20 +37,17 @@
#include "tvhead.h"
#include "dispatch.h"
#include "dvb.h"
#include "channels.h"
#include "transports.h"
#include "subscriptions.h"
#include "teletext.h"
#include "epg.h"
#include "psi.h"
#include "dvb.h"
#include "dvb_support.h"
#include "dvb_dvr.h"
#include "tsdemux.h"
#include "notify.h"
struct th_dvb_adapter_queue dvb_adapters;
struct th_dvb_mux_instance_tree dvb_muxes;
static void dvb_fec_monitor(void *aux, int64_t now);
//static void dvb_fec_monitor(void *aux, int64_t now);
static void *dvb_adapter_input_dvr(void *aux);
/**
@ -61,9 +57,7 @@ static th_dvb_adapter_t *
tda_alloc(void)
{
th_dvb_adapter_t *tda = calloc(1, sizeof(th_dvb_adapter_t));
pthread_mutex_init(&tda->tda_lock, NULL);
pthread_cond_init(&tda->tda_cond, NULL);
TAILQ_INIT(&tda->tda_fe_cmd_queue);
pthread_mutex_init(&tda->tda_delivery_mutex, NULL);
return tda;
}
@ -76,6 +70,8 @@ tda_save(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
lock_assert(&global_lock);
htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type));
htsmsg_add_str(m, "displayname", tda->tda_displayname);
htsmsg_add_u32(m, "autodiscovery", tda->tda_autodiscovery);
@ -92,6 +88,8 @@ dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s)
{
htsmsg_t *m;
lock_assert(&global_lock);
if(!strcmp(s, tda->tda_displayname))
return;
@ -121,6 +119,8 @@ dvb_adapter_set_auto_discovery(th_dvb_adapter_t *tda, int on)
if(tda->tda_autodiscovery == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" mux autodiscovery set to %s",
tda->tda_displayname, on ? "On" : "Off");
@ -139,6 +139,7 @@ tda_add(const char *path)
int fe, i, r;
th_dvb_adapter_t *tda;
char buf[400];
pthread_t ptid;
snprintf(fname, sizeof(fname), "%s/frontend0", path);
@ -172,12 +173,6 @@ tda_add(const char *path)
tda->tda_type = tda->tda_fe_info->type;
if(dvb_dvr_init(tda) < 0) {
close(fe);
free(tda);
return;
}
snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath,
tda->tda_fe_info->name);
@ -199,8 +194,8 @@ tda_add(const char *path)
"Found adapter %s (%s)", path, tda->tda_fe_info->name);
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
dvb_fe_start(tda);
pthread_create(&ptid, NULL, dvb_adapter_input_dvr, tda);
}
@ -261,34 +256,6 @@ dvb_adapter_init(void)
}
/**
*
*/
static void
dvb_notify_mux_quality(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
notify_by_msg("dvbmux", m);
}
/**
*
*/
static void
dvb_notify_mux_status(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_str(m, "status", tdmi->tdmi_last_status);
notify_by_msg("dvbmux", m);
}
/**
*
*/
@ -303,6 +270,9 @@ dvb_adapter_notify_reload(th_dvb_adapter_t *tda)
}
#if 0
/**
*
*/
@ -371,6 +341,7 @@ dvb_fec_monitor(void *aux, int64_t now)
if(savemux)
dvb_mux_save(tdmi);
}
#endif
/**
@ -378,12 +349,12 @@ dvb_fec_monitor(void *aux, int64_t now)
* and EIT updates
*/
void
dvb_adapter_mux_scanner(void *aux, int64_t now)
dvb_adapter_mux_scanner(void *aux)
{
th_dvb_adapter_t *tda = aux;
th_dvb_mux_instance_t *tdmi;
dtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 10);
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 10);
if(transport_compute_weight(&tda->tda_transports) > 0)
return; /* someone is here */
@ -393,7 +364,7 @@ dvb_adapter_mux_scanner(void *aux, int64_t now)
if((tdmi = RB_FIRST(&tda->tda_muxes_qscan_waiting)) != NULL) {
RB_REMOVE(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link);
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_RUNNING;
dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN);
dvb_fe_tune(tdmi);
return;
}
@ -409,9 +380,10 @@ dvb_adapter_mux_scanner(void *aux, int64_t now)
if(tdmi == NULL)
return; /* no instances */
dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN);
dvb_fe_tune(tdmi);
}
/**
*
*/
@ -422,6 +394,8 @@ dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
th_transport_t *t_src, *t_dst;
th_stream_t *st_src, *st_dst;
lock_assert(&global_lock);
while((tdmi_dst = RB_FIRST(&dst->tda_muxes)) != NULL)
dvb_mux_destroy(tdmi_dst);
@ -489,15 +463,12 @@ dvb_adapter_destroy(th_dvb_adapter_t *tda)
{
th_dvb_mux_instance_t *tdmi;
char buf[400];
if(tda->tda_rootpath != NULL)
return -1;
snprintf(buf, sizeof(buf), "%s/dvbadapters/%s",
settings_dir, tda->tda_identifier);
lock_assert(&global_lock);
unlink(buf);
hts_settings_remove("dvbadapters/%s", tda->tda_identifier);
while((tdmi = RB_FIRST(&tda->tda_muxes)) != NULL)
dvb_mux_destroy(tdmi);
@ -512,3 +483,50 @@ dvb_adapter_destroy(th_dvb_adapter_t *tda)
return 0;
}
/**
*
*/
void
dvb_adapter_clean(th_dvb_adapter_t *tda)
{
th_transport_t *t;
lock_assert(&global_lock);
while((t = LIST_FIRST(&tda->tda_transports)) != NULL)
transport_remove_subscriber(t, NULL); /* Flushes all subscribers */
}
/**
*
*/
static void *
dvb_adapter_input_dvr(void *aux)
{
th_dvb_adapter_t *tda = aux;
int fd, i, r;
uint8_t tsb[188 * 10];
th_transport_t *t;
fd = open(tda->tda_dvr_path, O_RDONLY);
if(fd == -1) {
tvhlog(LOG_ALERT, "dvb", "%s: unable to open dvr", tda->tda_dvr_path);
return NULL;
}
while(1) {
r = read(fd, tsb, sizeof(tsb));
pthread_mutex_lock(&tda->tda_delivery_mutex);
for(i = 0; i < r; i += 188)
LIST_FOREACH(t, &tda->tda_transports, tht_active_link)
if(t->tht_dvb_mux_instance == tda->tda_mux_current)
ts_recv_packet1(t, tsb + i);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
}
}

View file

@ -21,8 +21,8 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
@ -32,7 +32,6 @@
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <libhts/htscfg.h>
#include <syslog.h>
#include "tvhead.h"
@ -42,6 +41,8 @@
#include "diseqc.h"
#include "notify.h"
#if 0
typedef struct dvb_fe_cmd {
TAILQ_ENTRY(dvb_fe_cmd) link;
th_dvb_mux_instance_t *tdmi;
@ -189,6 +190,7 @@ dvb_fe_manager(void *aux)
}
/**
* Startup the FE management thread
*/
@ -199,92 +201,204 @@ dvb_fe_start(th_dvb_adapter_t *tda)
pthread_create(&ptid, NULL, dvb_fe_manager, tda);
}
#endif
/**
*
*/
#if 0
static void
dvb_notify_mux_quality(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
notify_by_msg("dvbmux", m);
}
#endif
/**
*
*/
static void
dvb_notify_mux_status(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_str(m, "status", dvb_mux_status(tdmi));
notify_by_msg("dvbmux", m);
}
/**
* Front end monitor
*
* Monitor status every second
*/
static void
dvb_fe_monitor(void *aux)
{
th_dvb_adapter_t *tda = aux;
fe_status_t fe_status;
int status, v, savemux = 0, vv, i, fec;
th_dvb_mux_instance_t *tdmi = tda->tda_mux_current;
assert(tdmi != NULL);
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
/**
* Read out front end status
*/
ioctl(tda->tda_fe_fd, FE_READ_STATUS, &fe_status);
if(fe_status & FE_HAS_LOCK)
status = -1;
else if(fe_status & (FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_CARRIER))
status = TDMI_FE_BAD_SIGNAL;
else if(fe_status & FE_HAS_SIGNAL)
status = TDMI_FE_FAINT_SIGNAL;
else
status = TDMI_FE_NO_SIGNAL;
if(tdmi->tdmi_fe_status == TDMI_FE_UNKNOWN) {
/* Previously unknown */
if(status == -1) /* We have a lock, don't hold off */
tda->tda_fe_monitor_hold = 0;
if(tda->tda_fe_monitor_hold > 0) {
tda->tda_fe_monitor_hold--;
return;
}
/* Reset FEC counter */
ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &fec);
}
if(status == -1) {
/* Read FEC counter (delta) */
ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &fec);
tdmi->tdmi_fec_err_histogram[tdmi->tdmi_fec_err_ptr++] = fec;
if(tdmi->tdmi_fec_err_ptr == TDMI_FEC_ERR_HISTOGRAM_SIZE)
tdmi->tdmi_fec_err_ptr = 0;
v = vv = 0;
for(i = 0; i < TDMI_FEC_ERR_HISTOGRAM_SIZE; i++) {
if(tdmi->tdmi_fec_err_histogram[i] > DVB_FEC_ERROR_LIMIT)
v++;
vv += tdmi->tdmi_fec_err_histogram[i];
}
vv = vv / TDMI_FEC_ERR_HISTOGRAM_SIZE;
if(v == 0) {
status = TDMI_FE_OK;
} else if(v == 1) {
status = TDMI_FE_BURSTY_FEC;
} else {
status = TDMI_FE_CONTANT_FEC;
}
}
if(status != tdmi->tdmi_fe_status) {
tdmi->tdmi_fe_status = status;
dvb_notify_mux_status(tdmi);
savemux = 1;
}
if(savemux)
dvb_mux_save(tdmi);
}
/**
* Stop the given TDMI
*/
void
tdmi_stop(th_dvb_mux_instance_t *tdmi)
dvb_fe_stop(th_dvb_mux_instance_t *tdmi)
{
th_dvb_table_t *tdt;
tdmi->tdmi_adapter->tda_mux_current = NULL;
pthread_mutex_lock(&tdmi->tdmi_table_lock);
dvb_table_flush_all(tdmi);
while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL)
dvb_tdt_destroy(tdt);
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
tdmi->tdmi_state = TDMI_IDLE;
// notify_tdmi_state_change(tdmi);
time(&tdmi->tdmi_lost_adapter);
}
/**
* Tune an adapter to a mux instance (but only if needed)
*
*/
void
dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state)
dvb_fe_tune(th_dvb_mux_instance_t *tdmi)
{
dvb_fe_cmd_t *c;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
char buf[100];
struct dvb_frontend_parameters p = tdmi->tdmi_fe_params;
char buf[256];
int r;
if(tdmi->tdmi_state != state) {
tdmi->tdmi_state = state;
// notify_tdmi_state_change(tdmi);
}
lock_assert(&global_lock);
if(tda->tda_mux_current == tdmi)
return;
if(tda->tda_mux_current != NULL)
tdmi_stop(tda->tda_mux_current);
dvb_fe_stop(tda->tda_mux_current);
if(tda->tda_type == FE_QPSK) {
/* DVB-S */
int lowfreq, hifreq, switchfreq, hiband;
// lowfreq = atoi(config_get_str("lnb_lowfreq", "9750000" ));
// hifreq = atoi(config_get_str("lnb_hifreq", "10600000"));
// switchfreq = atoi(config_get_str("lnb_switchfreq", "11700000"));
lowfreq = 9750000;
hifreq = 10600000;
switchfreq = 11700000;
hiband = switchfreq && p.frequency > switchfreq;
diseqc_setup(tda->tda_fe_fd,
0, /* switch position */
tdmi->tdmi_polarisation == POLARISATION_HORIZONTAL,
hiband);
usleep(50000);
if(hiband)
p.frequency = abs(p.frequency - hifreq);
else
p.frequency = abs(p.frequency - lowfreq);
}
r = ioctl(tda->tda_fe_fd, FE_SET_FRONTEND, &p);
if(r != 0) {
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_ERR, "dvb", "\"%s\" tuning to \"%s\""
" -- Front configuration failed -- %s",
tda->tda_rootpath, buf, strerror(errno));
}
tda->tda_mux_current = tdmi;
if(maylog) {
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_DEBUG, "dvb",
"\"%s\" tuning to mux \"%s\"", tda->tda_rootpath, buf);
}
/* Add tables which will be activated once the tuning is completed */
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
dvb_table_add_default(tdmi);
/* Send command to the thread */
c = malloc(sizeof(dvb_fe_cmd_t));
c->tdmi = tdmi;
pthread_mutex_lock(&tda->tda_lock);
tdmi->tdmi_refcnt++;
TAILQ_INSERT_TAIL(&tda->tda_fe_cmd_queue, c, link);
pthread_cond_signal(&tda->tda_cond);
pthread_mutex_unlock(&tda->tda_lock);
}
/**
* Flush pending tuning commands for frontend
*
* tda_lock must be held
*/
void
dvb_fe_flush(th_dvb_mux_instance_t *tdmi)
{
dvb_fe_cmd_t *c;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
TAILQ_FOREACH(c, &tda->tda_fe_cmd_queue, link)
if(c->tdmi == tdmi)
break;
if(c == NULL)
return;
TAILQ_REMOVE(&tda->tda_fe_cmd_queue, c, link);
dvb_mux_unref(tdmi);
free(c);
}

View file

@ -51,6 +51,26 @@
struct th_dvb_mux_instance_tree dvb_muxes;
static struct strtab muxfestatustab[] = {
{ "Unknown", TDMI_FE_UNKNOWN },
{ "No signal", TDMI_FE_NO_SIGNAL },
{ "Faint signal", TDMI_FE_FAINT_SIGNAL },
{ "Bad signal", TDMI_FE_BAD_SIGNAL },
{ "Constant FEC", TDMI_FE_CONTANT_FEC },
{ "Bursty FEC", TDMI_FE_BURSTY_FEC },
{ "OK", TDMI_FE_OK },
};
/**
* Return a readable status text for the given mux
*/
const char *
dvb_mux_status(th_dvb_mux_instance_t *tdmi)
{
return val2str(tdmi->tdmi_fe_status, muxfestatustab) ?: "Invalid";
}
@ -93,6 +113,8 @@ dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
char qpsktxt[20];
int entries_before = tda->tda_muxes.entries;
lock_assert(&global_lock);
if(skel == NULL)
skel = calloc(1, sizeof(th_dvb_mux_instance_t));
@ -107,24 +129,19 @@ dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
tdmi = skel;
skel = NULL;
tdmi->tdmi_refcnt = 1;
RB_INSERT_SORTED(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link,
tdmi_cmp);
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_WAITING;
pthread_mutex_init(&tdmi->tdmi_table_lock, NULL);
tdmi->tdmi_state = TDMI_IDLE;
tdmi->tdmi_transport_stream_id = tsid;
tdmi->tdmi_adapter = tda;
tdmi->tdmi_network = network ? strdup(network) : NULL;
tdmi->tdmi_last_status = strdup("Initializing");
tdmi->tdmi_quality = 100;
if(entries_before == 0 && tda->tda_rootpath != NULL) {
/* First mux on adapter with backing hardware, start scanner */
dtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1);
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1);
}
memcpy(&tdmi->tdmi_fe_params, fe_param,
@ -156,22 +173,6 @@ dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
return tdmi;
}
/**
* Unref a TDMI and optionally free it
*/
void
dvb_mux_unref(th_dvb_mux_instance_t *tdmi)
{
if(tdmi->tdmi_refcnt > 1) {
tdmi->tdmi_refcnt--;
return;
}
free(tdmi->tdmi_network);
free(tdmi->tdmi_identifier);
free(tdmi);
}
/**
* Destroy a DVB mux (it might come back by itself very soon though :)
*/
@ -180,19 +181,18 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_transport_t *t;
char buf[400];
snprintf(buf, sizeof(buf), "%s/dvbmuxes/%s",
settings_dir, tdmi->tdmi_identifier);
unlink(buf);
lock_assert(&global_lock);
hts_settings_remove("dvbmuxes/%s/%s",
tda->tda_identifier, tdmi->tdmi_identifier);
while((t = LIST_FIRST(&tdmi->tdmi_transports)) != NULL)
transport_destroy(t);
if(tda->tda_mux_current == tdmi)
tdmi_stop(tda->tda_mux_current);
dvb_fe_stop(tda->tda_mux_current);
dtimer_disarm(&tdmi->tdmi_initial_scan_timer);
RB_REMOVE(&dvb_muxes, tdmi, tdmi_global_link);
RB_REMOVE(&tda->tda_muxes, tdmi, tdmi_adapter_link);
@ -201,38 +201,9 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
pthread_mutex_lock(&tda->tda_lock);
dvb_fe_flush(tdmi);
dvb_mux_unref(tdmi);
pthread_mutex_unlock(&tda->tda_lock);
}
/**
*
*/
void
dvb_mux_fastswitch(th_dvb_mux_instance_t *tdmi)
{
th_dvb_table_t *tdt;
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_NONE)
return;
pthread_mutex_lock(&tdmi->tdmi_table_lock);
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) {
if(tdt->tdt_quickreq && tdt->tdt_count == 0)
break;
}
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
if(tdt != NULL)
return; /* Still tables we've not seen */
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_NONE;
dvb_adapter_mux_scanner(tdmi->tdmi_adapter, 0);
free(tdmi->tdmi_network);
free(tdmi->tdmi_identifier);
free(tdmi);
}
@ -244,6 +215,8 @@ dvb_mux_find_by_identifier(const char *identifier)
{
th_dvb_mux_instance_t skel;
lock_assert(&global_lock);
skel.tdmi_identifier = (char *)identifier;
return RB_FIND(&dvb_muxes, &skel, tdmi_global_link, tdmi_global_cmp);
}
@ -324,7 +297,7 @@ dvb_mux_save(th_dvb_mux_instance_t *tdmi)
htsmsg_t *m = htsmsg_create();
htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
htsmsg_add_str(m, "status", tdmi->tdmi_last_status);
htsmsg_add_str(m, "status", dvb_mux_status(tdmi));
htsmsg_add_u32(m, "transportstreamid", tdmi->tdmi_transport_stream_id);
if(tdmi->tdmi_network != NULL)
@ -490,11 +463,8 @@ tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m)
tdmi = dvb_mux_create(tda, &f, polarisation, switchport,
tsid, htsmsg_get_str(m, "network"), NULL);
if(tdmi != NULL) {
s = htsmsg_get_str(m, "status");
if(s != NULL) {
free((void *)tdmi->tdmi_last_status);
tdmi->tdmi_last_status = strdup(s);
}
if((s = htsmsg_get_str(m, "status")) != NULL)
tdmi->tdmi_fe_status = str2val(s, muxfestatustab);
if(!htsmsg_get_u32(m, "quality", &u32))
tdmi->tdmi_quality = u32;

View file

@ -265,51 +265,6 @@ dvb_adapter_find_by_identifier(const char *identifier)
}
/**
* Return a readable status text for the given mux
*/
const char *
dvb_mux_status(th_dvb_mux_instance_t *tdmi)
{
int i, v = 0;
for(i = 0; i < TDMI_FEC_ERR_HISTOGRAM_SIZE; i++)
v += tdmi->tdmi_fec_err_histogram[i] > DVB_FEC_ERROR_LIMIT;
if(v == TDMI_FEC_ERR_HISTOGRAM_SIZE)
return "Constant FEC";
else if(v > 0)
return "Bursty FEC";
return tdmi->tdmi_status ?: "OK";
}
/**
* Return a badness value (0-3)
*/
int
dvb_mux_badness(th_dvb_mux_instance_t *tdmi)
{
int i, v = 0;
if(tdmi->tdmi_status)
return 3;
for(i = 0; i < TDMI_FEC_ERR_HISTOGRAM_SIZE; i++)
v += tdmi->tdmi_fec_err_histogram[i] > DVB_FEC_ERROR_LIMIT;
if(v == TDMI_FEC_ERR_HISTOGRAM_SIZE)
return 2;
else if(v > 0)
return 1;
return 0;
}
/**
*
*/

View file

@ -21,9 +21,9 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@ -32,9 +32,7 @@
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <libhts/htscfg.h>
#include "tvhead.h"
#include "dispatch.h"
#include "dvb.h"
#include "dvb_support.h"
#include "epg.h"
@ -43,64 +41,145 @@
#include "psi.h"
#include "notify.h"
#define TDT_NOW 0x1
#define TDT_QUICKREQ 0x2
static int tid_tally;
/**
*
*/
void
dvb_tdt_destroy(th_dvb_table_t *tdt)
{
free(tdt->tdt_fparams);
LIST_REMOVE(tdt, tdt_link);
close(dispatch_delfd(tdt->tdt_handle));
free(tdt->tdt_name);
free(tdt);
}
typedef struct th_dvb_table {
LIST_ENTRY(th_dvb_table) tdt_link;
char *tdt_name;
void *tdt_opaque;
void (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
uint8_t tableid, void *opaque);
int tdt_fd;
int tdt_quickreq;
int tdt_count;
int tdt_id;
} th_dvb_table_t;
/**
*
*/
static void
dvb_table_recv(int events, void *opaque, int fd)
dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi)
{
th_dvb_table_t *tdt = opaque;
uint8_t sec[4096], *ptr;
int r, len;
uint8_t tableid;
th_dvb_table_t *tdt;
if(!(events & DISPATCH_READ))
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_NONE)
return;
r = read(fd, sec, sizeof(sec));
if(r < 3)
return;
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) {
if(tdt->tdt_quickreq && tdt->tdt_count == 0)
break;
}
/* It seems some hardware (or is it the dvb API?) does not honour the
DMX_CHECK_CRC flag, so we check it again */
if(tdt != NULL)
return; /* Still tables we've not seen */
if(psi_crc32(sec, r))
return;
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_NONE;
// dvb_adapter_mux_scanner(tdmi->tdmi_adapter, 0);
r -= 3;
tableid = sec[0];
len = ((sec[1] & 0x0f) << 8) | sec[2];
if(len < r)
return;
ptr = &sec[3];
len -= 4; /* Strip trailing CRC */
tdt->tdt_count++;
tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque);
dvb_mux_fastswitch(tdt->tdt_tdmi);
}
/**
*
*/
static void *
dvb_table_input(void *aux)
{
th_dvb_adapter_t *tda = aux;
int r, i, tid, fd, tableid, len;
struct epoll_event ev[1];
uint8_t sec[4096], *ptr;
th_dvb_mux_instance_t *tdmi;
th_dvb_table_t *tdt;
while(1) {
r = epoll_wait(tda->tda_table_epollfd, ev, sizeof(ev) / sizeof(ev[0]), -1);
for(i = 0; i < r; i++) {
fd = ev[i].data.u64;
tid = ev[i].data.u64 >> 32;
if(ev[i].events & EPOLLIN) {
if((r = read(fd, sec, sizeof(sec))) < 3)
continue;
/* It seems some hardware (or is it the dvb API?) does not
honour the DMX_CHECK_CRC flag, so we check it again */
if(psi_crc32(sec, r))
continue;
r -= 3;
tableid = sec[0];
len = ((sec[1] & 0x0f) << 8) | sec[2];
if(len < r)
continue;
ptr = &sec[3];
len -= 4; /* Strip trailing CRC */
pthread_mutex_lock(&global_lock);
tdmi = tda->tda_mux_current;
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
if(tdt->tdt_id == tid)
break;
if(tdt != NULL) {
tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque);
dvb_table_fastswitch(tdmi);
}
} else {
fprintf(stderr, "DVB table thread, spurious poll event %x on fd %d\n",
ev[i].events, fd);
}
}
}
}
/**
*
*/
void
dvb_table_init(th_dvb_adapter_t *tda)
{
pthread_t ptid;
tda->tda_table_epollfd = epoll_create(50);
pthread_create(&ptid, NULL, dvb_table_input, tda);
}
/**
*
*/
static void
dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_table_t *tdt)
{
epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL);
free(tdt->tdt_name);
LIST_REMOVE(tdt, tdt_link);
close(tdt->tdt_fd);
free(tdt);
}
@ -116,6 +195,7 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_dvb_table_t *tdt;
int fd;
struct epoll_event e;
if((fd = open(tda->tda_demux_path, O_RDWR)) == -1)
return;
@ -125,21 +205,18 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
tdt->tdt_name = strdup(name);
tdt->tdt_callback = callback;
tdt->tdt_opaque = opaque;
tdt->tdt_tdmi = tdmi;
tdt->tdt_handle = dispatch_addfd(fd, dvb_table_recv, tdt, DISPATCH_READ);
tdt->tdt_id = ++tid_tally;
e.events = EPOLLIN;
e.data.u64 = ((uint64_t)tdt->tdt_id << 32) | fd;
epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, fd, &e);
tdt->tdt_quickreq = flags & TDT_QUICKREQ ? 1 : 0;
if(flags & TDT_NOW) {
ioctl(fd, DMX_SET_FILTER, fparams);
free(fparams);
} else {
tdt->tdt_fparams = fparams;
}
pthread_mutex_lock(&tdmi->tdmi_table_lock);
ioctl(fd, DMX_SET_FILTER, fparams);
free(fparams);
LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link);
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
}
@ -300,9 +377,10 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
break;
case DVB_DESC_CONTENT:
if(dlen >= 2)
if(dlen >= 2) {
/* We only support one content type per event atm. */
ect = epg_content_type_find_by_dvbcode(*ptr);
// ect = epg_content_type_find_by_dvbcode(*ptr);
}
break;
}
@ -310,8 +388,8 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
}
if(duration > 0) {
epg_update_event_by_id(ch, event_id, start_time, duration,
title, desc, ect);
//epg_update_event_by_id(ch, event_id, start_time, duration,
//title, desc, ect);
}
}
@ -812,5 +890,20 @@ dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t,
fp = dvb_fparams_alloc(pmt_pid, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
fp->filter.filter[0] = 0x02;
fp->filter.mask[0] = 0xff;
tdt_add(tdmi, fp, dvb_pmt_callback, t, pmtname, TDT_NOW | TDT_QUICKREQ);
tdt_add(tdmi, fp, dvb_pmt_callback, t, pmtname, TDT_QUICKREQ);
}
/**
*
*/
void
dvb_table_flush_all(th_dvb_mux_instance_t *tdmi)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_dvb_table_t *tdt;
while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL)
dvb_tdt_destroy(tda, tdt);
}

View file

@ -48,6 +48,116 @@
#include "notify.h"
/*
* Switch the adapter (which is implicitly tied to our transport)
* to receive the given transport.
*
* But we only do this if 'weight' is higher than all of the current
* transports that is subscribing to the adapter
*/
static int
dvb_transport_start(th_transport_t *t, unsigned int weight, int status,
int force_start)
{
struct dmx_pes_filter_params dmx_param;
th_stream_t *st;
int w, fd, pid;
th_dvb_adapter_t *tda = t->tht_dvb_mux_instance->tdmi_adapter;
th_dvb_mux_instance_t *tdmi = tda->tda_mux_current;
lock_assert(&global_lock);
if(tda->tda_rootpath == NULL)
return 1; /* hardware not present */
/* Check if adapter is idle, or already tuned */
if(tdmi != NULL && tdmi != t->tht_dvb_mux_instance && !force_start) {
/* Nope .. */
if(tdmi->tdmi_fe_status > TDMI_FE_UNKNOWN &&
tdmi->tdmi_fe_status < TDMI_FE_CONTANT_FEC)
return 1; /* Not good enough signal here, can't use it */
w = transport_compute_weight(&tdmi->tdmi_adapter->tda_transports);
if(w > weight)
return 1; /* We are outranked by weight, cant use it */
dvb_adapter_clean(tda);
}
tdmi = t->tht_dvb_mux_instance;
LIST_FOREACH(st, &t->tht_streams, st_link) {
fd = open(tda->tda_demux_path, O_RDWR);
pid = st->st_pid;
st->st_cc_valid = 0;
if(fd == -1) {
st->st_demuxer_fd = -1;
tvhlog(LOG_ERR, "dvb",
"\"%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)) {
tvhlog(LOG_ERR, "dvb",
"\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s",
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
close(fd);
fd = -1;
}
st->st_demuxer_fd = fd;
}
pthread_mutex_lock(&tda->tda_delivery_mutex);
LIST_INSERT_HEAD(&tda->tda_transports, t, tht_active_link);
t->tht_runstatus = status;
dvb_fe_tune(tdmi);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
return 0;
}
/**
*
*/
static void
dvb_transport_stop(th_transport_t *t)
{
th_dvb_adapter_t *tda = t->tht_dvb_mux_instance->tdmi_adapter;
th_stream_t *st;
lock_assert(&global_lock);
pthread_mutex_lock(&tda->tda_delivery_mutex);
LIST_REMOVE(t, tht_active_link);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
LIST_FOREACH(st, &t->tht_streams, st_link) {
close(st->st_demuxer_fd);
st->st_demuxer_fd = -1;
}
t->tht_runstatus = TRANSPORT_IDLE;
}
/**
* Load config for the given mux
@ -62,6 +172,8 @@ dvb_transport_load(th_dvb_mux_instance_t *tdmi)
unsigned int u32;
th_transport_t *t;
lock_assert(&global_lock);
if((l = hts_settings_load("dvbtransports/%s", tdmi->tdmi_identifier)) == NULL)
return;
@ -111,6 +223,7 @@ dvb_transport_save(th_transport_t *t)
{
htsmsg_t *m = htsmsg_create();
lock_assert(&global_lock);
htsmsg_add_u32(m, "service_id", t->tht_dvb_service_id);
htsmsg_add_u32(m, "pmt", t->tht_pmt);
@ -148,6 +261,9 @@ static int
dvb_transport_quality(th_transport_t *t)
{
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
lock_assert(&global_lock);
return tdmi->tdmi_quality;
}
@ -160,6 +276,8 @@ dvb_transport_sourcename(th_transport_t *t)
{
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
lock_assert(&global_lock);
return tdmi->tdmi_adapter->tda_displayname;
}
@ -172,6 +290,8 @@ dvb_transport_networkname(th_transport_t *t)
{
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
lock_assert(&global_lock);
return tdmi->tdmi_network;
}
@ -190,6 +310,8 @@ dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
th_transport_t *t;
char tmp[200];
lock_assert(&global_lock);
if(created != NULL)
*created = 0;
@ -211,8 +333,8 @@ dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
t->tht_dvb_service_id = sid;
t->tht_pmt = pmt_pid;
t->tht_start_feed = dvb_start_feed;
t->tht_stop_feed = dvb_stop_feed;
t->tht_start_feed = dvb_transport_start;
t->tht_stop_feed = dvb_transport_stop;
t->tht_config_change = dvb_transport_save;
t->tht_sourcename = dvb_transport_sourcename;
t->tht_networkname = dvb_transport_networkname;

681
http.c
View file

@ -32,14 +32,10 @@
#include <libavutil/base64.h>
#include "tvhead.h"
#include "channels.h"
#include "subscriptions.h"
#include "dispatch.h"
#include "tcp.h"
#include "http.h"
#include "rtsp.h"
#include "access.h"
int http_port;
static LIST_HEAD(, http_path) http_paths;
@ -57,10 +53,9 @@ static struct strtab HTTP_cmdtab[] = {
static struct strtab HTTP_versiontab[] = {
{ "HTTP/0.9", HTTP_VERSION_0_9 },
{ "HTTP/1.0", HTTP_VERSION_1_0 },
{ "HTTP/1.1", HTTP_VERSION_1_1 },
{ "RTSP/1.0", RTSP_VERSION_1_0 }, /* not enabled yet */
{ "RTSP/1.0", RTSP_VERSION_1_0 },
};
static void http_parse_get_args(http_connection_t *hc, char *args);
@ -145,171 +140,114 @@ static const char *cachemonths[12] = {
"Dec"
};
/**
*
*/
static void
http_destroy_reply(http_connection_t *hc, http_reply_t *hr)
{
if(hr->hr_destroy != NULL)
hr->hr_destroy(hr, hr->hr_opaque);
TAILQ_REMOVE(&hc->hc_replies, hr, hr_link);
free(hr->hr_location);
htsbuf_queue_flush(&hr->hr_q);
free(hr);
}
/**
* Transmit a HTTP reply
*
* Return non-zero if we should disconnect (no more keep-alive)
*/
static int
http_send_reply(http_connection_t *hc, http_reply_t *hr)
static void
http_send_reply(http_connection_t *hc, int rc, const char *content,
const char *encoding, const char *location, int maxage)
{
struct tm tm0, *tm;
htsbuf_queue_t hdrs;
time_t t;
htsbuf_queue_t *hq = &hr->hr_q;
int r;
if(hr->hr_version >= HTTP_VERSION_1_0) {
http_printf(hc, "%s %d %s\r\n", val2str(hr->hr_version, HTTP_versiontab),
hr->hr_rc, http_rc2str(hr->hr_rc));
htsbuf_queue_init(&hdrs, 0);
http_printf(hc, "Server: HTS/tvheadend\r\n");
htsbuf_qprintf(&hdrs, "%s %d %s\r\n", val2str(hc->hc_version, HTTP_versiontab),
rc, http_rc2str(rc));
if(hr->hr_maxage == 0) {
http_printf(hc, "Cache-Control: no-cache\r\n");
} else {
t = dispatch_clock;
htsbuf_qprintf(&hdrs, "Server: HTS/tvheadend\r\n");
if(maxage == 0) {
htsbuf_qprintf(&hdrs, "Cache-Control: no-cache\r\n");
} else {
time(&t);
tm = gmtime_r(&t, &tm0);
htsbuf_qprintf(&hdrs,
"Last-Modified: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
cachedays[tm->tm_wday], tm->tm_year + 1900,
cachemonths[tm->tm_mon], tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
t += maxage;
tm = gmtime_r(&t, &tm0);
htsbuf_qprintf(&hdrs,
"Expires: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
cachedays[tm->tm_wday], tm->tm_year + 1900,
cachemonths[tm->tm_mon], tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
tm = gmtime_r(&t, &tm0);
http_printf(hc,
"Last-Modified: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
cachedays[tm->tm_wday], tm->tm_year + 1900,
cachemonths[tm->tm_mon], tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
t += hr->hr_maxage;
tm = gmtime_r(&t, &tm0);
http_printf(hc,
"Expires: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
cachedays[tm->tm_wday], tm->tm_year + 1900,
cachemonths[tm->tm_mon], tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
http_printf(hc, "Cache-Control: max-age=%d\r\n", hr->hr_maxage);
}
http_printf(hc, "Connection: %s\r\n",
hr->hr_keep_alive ? "Keep-Alive" : "Close");
if(hr->hr_rc == HTTP_STATUS_UNAUTHORIZED)
http_printf(hc, "WWW-Authenticate: Basic realm=\"tvheadend\"\r\n");
if(hr->hr_encoding != NULL)
http_printf(hc, "Content-Encoding: %s\r\n", hr->hr_encoding);
if(hr->hr_location != NULL)
http_printf(hc, "Location: %s\r\n", hr->hr_location);
http_printf(hc,
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"\r\n",
hr->hr_content, hq->hq_size);
htsbuf_qprintf(&hdrs, "Cache-Control: max-age=%d\r\n", maxage);
}
tcp_output_queue(&hc->hc_tcp_session, 0, hq);
if(rc == HTTP_STATUS_UNAUTHORIZED)
htsbuf_qprintf(&hdrs, "WWW-Authenticate: Basic realm=\"tvheadend\"\r\n");
r = !hr->hr_keep_alive;
htsbuf_qprintf(&hdrs, "Connection: %s\r\n",
hc->hc_keep_alive ? "Keep-Alive" : "Close");
if(encoding != NULL)
htsbuf_qprintf(&hdrs, "Content-Encoding: %s\r\n", encoding);
http_destroy_reply(hc, hr);
return r;
if(location != NULL)
htsbuf_qprintf(&hdrs, "Location: %s\r\n", location);
htsbuf_qprintf(&hdrs,
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"\r\n",
content, hc->hc_reply.hq_size);
tcp_write_queue(hc->hc_fd, &hdrs);
tcp_write_queue(hc->hc_fd, &hc->hc_reply);
}
/**
* Send HTTP replies
*
* Return non-zero if we should disconnect
*/
static int
http_xmit_queue(http_connection_t *hc)
{
http_reply_t *hr;
while((hr = TAILQ_FIRST(&hc->hc_replies)) != NULL) {
if(hr->hr_destroy != NULL)
break; /* Pending reply, cannot send this yet */
if(http_send_reply(hc, hr))
return 1;
}
return 0;
}
/**
* Continue HTTP session, called by deferred replies
*/
void
http_continue(http_connection_t *hc)
{
if(http_xmit_queue(hc))
tcp_disconnect(&hc->hc_tcp_session, 0);
}
/**
* Send HTTP error back
*/
void
http_error(http_connection_t *hc, http_reply_t *hr, int error)
http_error(http_connection_t *hc, int error)
{
const char *errtxt = http_rc2str(error);
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_flush(hq);
htsbuf_queue_flush(&hc->hc_reply);
htsbuf_qprintf(hq,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<HTML><HEAD>\r\n"
"<TITLE>%d %s</TITLE>\r\n"
"</HEAD><BODY>\r\n"
"<H1>%d %s</H1>\r\n"
"</BODY></HTML>\r\n",
error, errtxt, error, errtxt);
htsbuf_qprintf(&hc->hc_reply,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<HTML><HEAD>\r\n"
"<TITLE>%d %s</TITLE>\r\n"
"</HEAD><BODY>\r\n"
"<H1>%d %s</H1>\r\n"
"</BODY></HTML>\r\n",
error, errtxt, error, errtxt);
hr->hr_destroy = NULL;
hr->hr_rc = error;
hr->hr_content = "text/html";
http_send_reply(hc, error, "text/html", NULL, NULL, 0);
}
/**
* Send an HTTP OK
* Send an HTTP OK, simple version for text/html
*/
void
http_output(http_connection_t *hc, http_reply_t *hr, const char *content,
const char *encoding, int maxage)
http_output_html(http_connection_t *hc)
{
hr->hr_rc = HTTP_STATUS_OK;
hr->hr_destroy = NULL;
hr->hr_encoding = encoding;
hr->hr_content = content;
hr->hr_maxage = maxage;
return http_send_reply(hc, HTTP_STATUS_OK, "text/html; charset=UTF-8",
NULL, NULL, 0);
}
/**
* Send an HTTP OK, simple version for text/html
*/
void
http_output_html(http_connection_t *hc, http_reply_t *hr)
http_output_content(http_connection_t *hc, const char *content)
{
hr->hr_rc = HTTP_STATUS_OK;
hr->hr_destroy = NULL;
hr->hr_content = "text/html; charset=UTF-8";
return http_send_reply(hc, HTTP_STATUS_OK, content, NULL, NULL, 0);
}
@ -318,22 +256,20 @@ http_output_html(http_connection_t *hc, http_reply_t *hr)
* Send an HTTP REDIRECT
*/
void
http_redirect(http_connection_t *hc, http_reply_t *hr, const char *location)
http_redirect(http_connection_t *hc, const char *location)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_flush(&hc->hc_reply);
htsbuf_qprintf(hq,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<HTML><HEAD>\r\n"
"<TITLE>Redirect</TITLE>\r\n"
"</HEAD><BODY>\r\n"
"Please follow <a href=\"%s\">%s</a>\r\n"
"</BODY></HTML>\r\n",
location, location);
htsbuf_qprintf(&hc->hc_reply,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<HTML><HEAD>\r\n"
"<TITLE>Redirect</TITLE>\r\n"
"</HEAD><BODY>\r\n"
"Please follow <a href=\"%s\">%s</a>\r\n"
"</BODY></HTML>\r\n",
location, location);
hr->hr_location = strdup(location);
hr->hr_rc = HTTP_STATUS_FOUND;
hr->hr_content = "text/html";
http_send_reply(hc, HTTP_STATUS_FOUND, "text/html", NULL, location, 0);
}
@ -343,35 +279,18 @@ http_redirect(http_connection_t *hc, http_reply_t *hr, const char *location)
* Returns 1 if we should disconnect
*
*/
static int
http_exec(http_connection_t *hc, http_path_t *hp, char *remain, int err)
static void
http_exec(http_connection_t *hc, http_path_t *hp, char *remain)
{
http_reply_t *hr = calloc(1, sizeof(http_reply_t));
int err;
if(access_verify(hc->hc_username, hc->hc_password,
(struct sockaddr *)hc->hc_peer, hp->hp_accessmask)) {
http_error(hc, HTTP_STATUS_UNAUTHORIZED);
return;
}
/* Insert reply in order */
TAILQ_INSERT_TAIL(&hc->hc_replies, hr, hr_link);
htsbuf_queue_init(&hr->hr_q, INT32_MAX);
hr->hr_connection = hc;
hr->hr_version = hc->hc_version;
hr->hr_keep_alive = hc->hc_keep_alive;
if(!err &&
access_verify(hc->hc_username, hc->hc_password,
(struct sockaddr *)&hc->hc_tcp_session.tcp_peer_addr,
hp->hp_accessmask))
err = HTTP_STATUS_UNAUTHORIZED;
if(!err)
err = hp->hp_callback(hc, hr, remain, hp->hp_opaque);
if(err)
http_error(hc, hr, err);
if(hr->hr_destroy != NULL)
return 0; /* New entry is delayed, do not transmit anything */
return http_xmit_queue(hc);
if((err = hp->hp_callback(hc, remain, hp->hp_opaque)) != 0)
http_error(hc, err);
}
@ -387,50 +306,16 @@ http_cmd_get(http_connection_t *hc)
char *args;
hp = http_resolve(hc, &remain, &args);
if(hp == NULL)
return http_exec(hc, NULL, NULL, HTTP_STATUS_NOT_FOUND);
if(hp == NULL) {
http_error(hc, HTTP_STATUS_NOT_FOUND);
return 0;
}
if(args != NULL)
http_parse_get_args(hc, args);
return http_exec(hc, hp, remain, 0);
}
/**
* Check if a HTTP POST is fully received, and if so, continue processing
*
* Return non-zero if we should disconnect
*/
static int
http_post_check(http_connection_t *hc)
{
http_path_t *hp;
char *remain, *args, *v, *argv[2];
int n;
if(hc->hc_post_ptr != hc->hc_post_len)
return 0;
hc->hc_state = HTTP_CON_WAIT_REQUEST;
/* Parse content-type */
v = http_arg_get(&hc->hc_args, "Content-Type");
if(v == NULL)
return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
n = http_tokenize(v, argv, 2, ';');
if(n == 0)
return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
http_parse_get_args(hc, hc->hc_post_data);
hp = http_resolve(hc, &remain, &args);
if(hp == NULL)
return http_exec(hc, NULL, NULL, HTTP_STATUS_NOT_FOUND);
return http_exec(hc, hp, remain, 0);
http_exec(hc, hp, remain);
return 0;
}
@ -443,20 +328,25 @@ http_post_check(http_connection_t *hc)
* Return non-zero if we should disconnect
*/
static int
http_cmd_post(http_connection_t *hc)
http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
{
char *v;
http_path_t *hp;
char *remain, *args, *v, *argv[2];
int n;
/* Set keep-alive status */
v = http_arg_get(&hc->hc_args, "Content-Length");
if(v == NULL)
return 1; /* No content length in POST, make us disconnect */
hc->hc_post_len = atoi(v);
if(hc->hc_post_len > 16 * 1024 * 1024)
return 1; /* Bail out if POST data > 16 Mb */
if(v == NULL) {
/* No content length in POST, make us disconnect */
return -1;
}
hc->hc_state = HTTP_CON_POST_DATA;
hc->hc_post_len = atoi(v);
if(hc->hc_post_len > 16 * 1024 * 1024) {
/* Bail out if POST data > 16 Mb */
hc->hc_keep_alive = 0;
return -1;
}
/* Allocate space for data, we add a terminating null char to ease
string processing on the content */
@ -464,50 +354,48 @@ http_cmd_post(http_connection_t *hc)
hc->hc_post_data = malloc(hc->hc_post_len + 1);
hc->hc_post_data[hc->hc_post_len] = 0;
hc->hc_post_ptr = 0;
if(tcp_read_data(hc->hc_fd, hc->hc_post_data, hc->hc_post_len, spill) < 0)
return -1;
/* We need to drain the line parser of any excess data */
hc->hc_post_ptr = tcp_line_drain(&hc->hc_tcp_session, hc->hc_post_data,
hc->hc_post_len);
return http_post_check(hc);
}
/**
* Read POST data directly from socket
*/
static void
http_consume_post_data(http_connection_t *hc)
{
tcp_session_t *tcp = &hc->hc_tcp_session;
int togo = hc->hc_post_len - hc->hc_post_ptr;
int r;
r = read(tcp->tcp_fd, hc->hc_post_data + hc->hc_post_ptr, togo);
if(r < 1) {
tcp_disconnect(tcp, r == 0 ? ECONNRESET : errno);
return;
/* Parse content-type */
v = http_arg_get(&hc->hc_args, "Content-Type");
if(v == NULL) {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return 0;
}
n = http_tokenize(v, argv, 2, ';');
if(n == 0) {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return 0;
}
hc->hc_post_ptr += r;
if(http_post_check(hc))
tcp_disconnect(tcp, 0);
if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
http_parse_get_args(hc, hc->hc_post_data);
hp = http_resolve(hc, &remain, &args);
if(hp == NULL) {
http_error(hc, HTTP_STATUS_NOT_FOUND);
return 0;
}
http_exec(hc, hp, remain);
return 0;
}
/**
* Process a HTTP request
*/
static int
http_process_request(http_connection_t *hc)
http_process_request(http_connection_t *hc, htsbuf_queue_t *spill)
{
switch(hc->hc_cmd) {
default:
return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return 0;
case HTTP_CMD_GET:
return http_cmd_get(hc);
case HTTP_CMD_POST:
return http_cmd_post(hc);
return http_cmd_post(hc, spill);
}
}
@ -516,7 +404,7 @@ http_process_request(http_connection_t *hc)
* clean up
*/
static int
process_request(http_connection_t *hc)
process_request(http_connection_t *hc, htsbuf_queue_t *spill)
{
char *v, *argv[2];
int n;
@ -530,10 +418,6 @@ process_request(http_connection_t *hc)
hc->hc_keep_alive = 1;
break;
case HTTP_VERSION_0_9:
hc->hc_keep_alive = 0;
break;
case HTTP_VERSION_1_0:
/* Keep-alive is default off, but can be enabled */
hc->hc_keep_alive = v != NULL && !strcasecmp(v, "keep-alive");
@ -545,12 +429,6 @@ process_request(http_connection_t *hc)
break;
}
free(hc->hc_username);
hc->hc_username = NULL;
free(hc->hc_password);
hc->hc_password = NULL;
/* Extract authorization */
if((v = http_arg_get(&hc->hc_args, "Authorization")) != NULL) {
if((n = http_tokenize(v, argv, 2, -1)) == 2) {
@ -563,175 +441,21 @@ process_request(http_connection_t *hc)
}
}
switch(hc->hc_version) {
case RTSP_VERSION_1_0:
rtsp_process_request(hc);
return -1;
// rtsp_process_request(hc);
return 0;
case HTTP_VERSION_0_9:
case HTTP_VERSION_1_0:
case HTTP_VERSION_1_1:
return http_process_request(hc) ? -1 : 0;
return http_process_request(hc, spill);
}
return -1;
}
/*
* HTTP connection state machine & parser
*
* If we return non zero we will disconnect
*/
static int
http_con_parse(void *aux, char *buf)
{
http_connection_t *hc = aux;
int n, v;
char *argv[3], *c;
// printf("HTTP INPUT: %s\n", buf);
switch(hc->hc_state) {
case HTTP_CON_WAIT_REQUEST:
if(hc->hc_post_data != NULL) {
free(hc->hc_post_data);
hc->hc_post_data = NULL;
}
http_arg_flush(&hc->hc_args);
http_arg_flush(&hc->hc_req_args);
if(hc->hc_url != NULL) {
free(hc->hc_url);
hc->hc_url = NULL;
}
n = http_tokenize(buf, argv, 3, -1);
if(n < 2)
return EBADRQC;
hc->hc_cmd = str2val(argv[0], HTTP_cmdtab);
hc->hc_url = strdup(argv[1]);
if(n == 3) {
v = str2val(argv[2], HTTP_versiontab);
if(v == -1)
return EBADRQC;
hc->hc_version = v;
hc->hc_state = HTTP_CON_READ_HEADER;
} else {
hc->hc_version = HTTP_VERSION_0_9;
return process_request(hc);
}
break;
case HTTP_CON_READ_HEADER:
if(*buf == 0) {
/* Empty crlf line, end of header lines */
hc->hc_state = HTTP_CON_WAIT_REQUEST;
return process_request(hc);
}
n = http_tokenize(buf, argv, 2, -1);
if(n < 2)
break;
c = strrchr(argv[0], ':');
if(c == NULL)
break;
*c = 0;
http_arg_set(&hc->hc_args, argv[0], argv[1]);
break;
case HTTP_CON_POST_DATA:
abort();
case HTTP_CON_END:
break;
}
return 0;
}
/*
* disconnect
*/
static void
http_disconnect(http_connection_t *hc)
{
http_reply_t *hr;
while((hr = TAILQ_FIRST(&hc->hc_replies)) != NULL)
http_destroy_reply(hc, hr);
free(hc->hc_post_data);
free(hc->hc_username);
free(hc->hc_password);
rtsp_disconncet(hc);
http_arg_flush(&hc->hc_args);
http_arg_flush(&hc->hc_req_args);
free(hc->hc_url);
}
/*
*
*/
static void
http_tcp_callback(tcpevent_t event, void *tcpsession)
{
http_connection_t *hc = tcpsession;
switch(event) {
case TCP_CONNECT:
TAILQ_INIT(&hc->hc_replies);
TAILQ_INIT(&hc->hc_args);
TAILQ_INIT(&hc->hc_req_args);
break;
case TCP_DISCONNECT:
http_disconnect(hc);
break;
case TCP_INPUT:
if(hc->hc_state == HTTP_CON_POST_DATA)
http_consume_post_data(hc);
else
tcp_line_read(&hc->hc_tcp_session, http_con_parse);
break;
}
}
/*
* Fire up HTTP server
*/
void
http_start(int port)
{
rtsp_init();
http_port = port;
tcp_create_server(port, sizeof(http_connection_t), "http",
http_tcp_callback);
}
/*
* Delete all arguments associated with a connection
@ -904,37 +628,112 @@ http_parse_get_args(http_connection_t *hc, char *args)
}
}
/**
* HTTP embedded resource
*
*/
typedef struct http_resource {
const void *data;
size_t len;
const char *content;
const char *encoding;
} http_resource_t;
static int
deliver_resource(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
static void
http_serve_requests(http_connection_t *hc, htsbuf_queue_t *spill)
{
http_resource_t *hres = opaque;
char cmdline[1024];
char hdrline[1024];
char *argv[3], *c;
int n;
htsbuf_queue_init(&hc->hc_reply, 0);
htsbuf_append(&hr->hr_q, hres->data, hres->len);
http_output(hc, hr, hres->content, hres->encoding, 15);
return 0;
do {
if(tcp_read_line(hc->hc_fd, cmdline, sizeof(cmdline), spill) < 0)
return;
if((n = http_tokenize(cmdline, argv, 3, -1)) != 3)
return;
if((hc->hc_cmd = str2val(argv[0], HTTP_cmdtab)) == -1)
return;
hc->hc_url = argv[1];
if((hc->hc_version = str2val(argv[2], HTTP_versiontab)) == -1)
return;
/* parse header */
while(1) {
if(tcp_read_line(hc->hc_fd, hdrline, sizeof(hdrline), spill) < 0)
return;
if(hdrline[0] == 0)
break; /* header complete */
if((n = http_tokenize(hdrline, argv, 2, -1)) < 2)
return;
if((c = strrchr(argv[0], ':')) == NULL)
return;
*c = 0;
http_arg_set(&hc->hc_args, argv[0], argv[1]);
}
if(process_request(hc, spill))
break;
free(hc->hc_post_data);
hc->hc_post_data = NULL;
http_arg_flush(&hc->hc_args);
http_arg_flush(&hc->hc_req_args);
htsbuf_queue_flush(&hc->hc_reply);
free(hc->hc_username);
hc->hc_username = NULL;
free(hc->hc_password);
hc->hc_password = NULL;
} while(hc->hc_keep_alive);
}
/**
*
*/
static void
http_serve(int fd, void *opaque, struct sockaddr_in *source)
{
htsbuf_queue_t spill;
http_connection_t hc;
memset(&hc, 0, sizeof(http_connection_t));
TAILQ_INIT(&hc.hc_args);
TAILQ_INIT(&hc.hc_req_args);
hc.hc_fd = fd;
hc.hc_peer = source;
htsbuf_queue_init(&spill, 0);
http_serve_requests(&hc, &spill);
free(hc.hc_post_data);
free(hc.hc_username);
free(hc.hc_password);
// rtsp_disconncet(hc);
http_arg_flush(&hc.hc_args);
http_arg_flush(&hc.hc_req_args);
htsbuf_queue_flush(&spill);
close(fd);
}
/**
* Fire up HTTP server
*/
void
http_resource_add(const char *path, const void *ptr, size_t len,
const char *content, const char *encoding)
http_server_init(void)
{
http_resource_t *hres = malloc(sizeof(http_resource_t));
hres->data = ptr;
hres->len = len;
hres->content = content;
hres->encoding = encoding;
http_path_add(path, hres, deliver_resource, ACCESS_WEB_INTERFACE);
tcp_server_create(9981, http_serve, NULL);
}

77
http.h
View file

@ -19,9 +19,15 @@
#ifndef HTTP_H_
#define HTTP_H_
extern int http_port;
#include <libhts/htsbuf.h>
#include "tcp.h"
TAILQ_HEAD(http_arg_list, http_arg);
typedef struct http_arg {
TAILQ_ENTRY(http_arg) link;
char *key;
char *val;
} http_arg_t;
#define HTTP_STATUS_OK 200
#define HTTP_STATUS_FOUND 302
@ -32,48 +38,13 @@ extern int http_port;
LIST_HEAD(rtsp_session_head, rtsp_session);
#define http_printf(x, fmt...) tcp_printf(&(x)->hc_tcp_session, fmt)
TAILQ_HEAD(http_arg_list, http_arg);
typedef struct http_arg {
TAILQ_ENTRY(http_arg) link;
char *key;
char *val;
} http_arg_t;
TAILQ_HEAD(http_reply_queue, http_reply);
typedef struct http_reply {
TAILQ_ENTRY(http_reply) hr_link;
void *hr_opaque;
void (*hr_destroy)(struct http_reply *hr, void *opaque);
struct http_connection *hr_connection;
int hr_version; /* HTTP version */
int hr_keep_alive;
char *hr_location;
int hr_rc; /* Return code */
int hr_maxage;
const char *hr_encoding;
const char *hr_content;
htsbuf_queue_t hr_q;
} http_reply_t;
typedef struct http_connection {
tcp_session_t hc_tcp_session; /* Must be first */
int hc_fd;
struct sockaddr_in *hc_peer;
char *hc_url;
int hc_keep_alive;
struct http_reply_queue hc_replies;
htsbuf_queue_t hc_reply;
struct http_arg_list hc_args;
@ -98,7 +69,6 @@ typedef struct http_connection {
} hc_cmd;
enum {
HTTP_VERSION_0_9,
HTTP_VERSION_1_0,
HTTP_VERSION_1_1,
RTSP_VERSION_1_0,
@ -107,7 +77,7 @@ typedef struct http_connection {
char *hc_username;
char *hc_password;
struct rtsp_session_head hc_rtsp_sessions;
// struct rtsp_session_head hc_rtsp_sessions;
int hc_authenticated; /* Used by RTSP, it seems VLC does not
send authentication headers for each
@ -120,14 +90,10 @@ typedef struct http_connection {
char *hc_post_data;
unsigned int hc_post_len;
unsigned int hc_post_ptr;
} http_connection_t;
void http_start(int port);
void http_arg_flush(struct http_arg_list *list);
char *http_arg_get(struct http_arg_list *list, const char *name);
@ -136,19 +102,15 @@ void http_arg_set(struct http_arg_list *list, char *key, char *val);
int http_tokenize(char *buf, char **vec, int vecsize, int delimiter);
void http_error(http_connection_t *hc, http_reply_t *hr, int error);
void http_error(http_connection_t *hc, int error);
void http_output(http_connection_t *hc, http_reply_t *hr,
const char *content, const char *encoding, int maxage);
void http_output_html(http_connection_t *hc);
void http_output_html(http_connection_t *hc, http_reply_t *hr);
void http_output_content(http_connection_t *hc, const char *content);
void http_continue(http_connection_t *hc);
void http_redirect(http_connection_t *hc, const char *location);
void http_redirect(http_connection_t *hc, http_reply_t *hr,
const char *location);
typedef int (http_callback_t)(http_connection_t *hc, http_reply_t *hr,
typedef int (http_callback_t)(http_connection_t *hc,
const char *remain, void *opaque);
typedef struct http_path {
@ -163,7 +125,8 @@ typedef struct http_path {
http_path_t *http_path_add(const char *path, void *opaque,
http_callback_t *callback, uint32_t accessmask);
void http_resource_add(const char *path, const void *ptr, size_t len,
const char *content, const char *encoding);
void http_server_init(void);
#endif /* HTTP_H_ */

352
main.c
View file

@ -37,65 +37,23 @@
#include <libavformat/avformat.h>
#include "tvhead.h"
#include "dvb/dvb.h"
#include "v4l.h"
#include "channels.h"
#include "epg.h"
#include "epg_xmltv.h"
#include "pvr.h"
#include "dispatch.h"
#include "transports.h"
#include "subscriptions.h"
#include "iptv_output.h"
#include "rtsp.h"
#include "http.h"
#include "htsp.h"
#include "buffer.h"
#include "htmlui.h"
#include "avgen.h"
#include "file_input.h"
#include "cwc.h"
#include "autorec.h"
#include "spawn.h"
#include "ffmuxer.h"
#include "xbmsp.h"
//#include "ajaxui/ajaxui.h"
#include "webui/webui.h"
#include "tcp.h"
#include "access.h"
#include "serviceprobe.h"
#include "http.h"
#include "webui/webui.h"
#include "dvb/dvb.h"
#include <libhts/htsparachute.h>
#include <libhts/htssettings.h>
int running;
int startupcounter;
const char *settings_dir;
const char *sys_warning;
extern const char *htsversion;
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;
}
time_t dispatch_clock;
static LIST_HEAD(, gtimer) gtimers;
pthread_mutex_t global_lock;
static void
doexit(int x)
{
running = 0;
}
static void
handle_sigpipe(int x)
@ -103,6 +61,12 @@ handle_sigpipe(int x)
return;
}
static void
doexit(int x)
{
running = 0;
}
static void
pull_chute (int sig)
{
@ -113,6 +77,90 @@ pull_chute (int sig)
sig, pwd);
}
/**
*
*/
static int
gtimercmp(gtimer_t *a, gtimer_t *b)
{
if(a->gti_expire < b->gti_expire)
return -1;
else if(a->gti_expire > b->gti_expire)
return 1;
return 0;
}
/**
*
*/
void
gtimer_arm(gtimer_t *gti, gti_callback_t *callback, void *opaque, int delta)
{
time_t now;
time(&now);
lock_assert(&global_lock);
if(gti->gti_callback != NULL)
LIST_REMOVE(gti, gti_link);
gti->gti_callback = callback;
gti->gti_opaque = opaque;
gti->gti_expire = now + delta;
LIST_INSERT_SORTED(&gtimers, gti, gti_link, gtimercmp);
}
/**
*
*/
void
gtimer_disarm(gtimer_t *gti)
{
if(gti->gti_callback) {
LIST_REMOVE(gti, gti_link);
gti->gti_callback = NULL;
}
}
/**
*
*/
static void
mainloop(void)
{
gtimer_t *gti;
gti_callback_t *cb;
while(running) {
sleep(1);
time(&dispatch_clock);
comet_flush(); /* Flush idle comet mailboxes */
pthread_mutex_lock(&global_lock);
while((gti = LIST_FIRST(&gtimers)) != NULL) {
if(gti->gti_expire > dispatch_clock)
break;
cb = gti->gti_callback;
LIST_REMOVE(gti, gti_link);
gti->gti_callback = NULL;
cb(gti->gti_opaque);
}
pthread_mutex_unlock(&global_lock);
}
}
/**
*
*/
int
main(int argc, char **argv)
{
@ -126,16 +174,11 @@ main(int argc, char **argv)
char *cfgfile = NULL;
int logfacility = LOG_DAEMON;
int disable_dvb = 0;
int p;
char buf[128];
char buf2[128];
char buf3[128];
char *settingspath = NULL;
const char *homedir;
sigset_t set;
signal(SIGPIPE, handle_sigpipe);
while((c = getopt(argc, argv, "c:fu:g:ds:")) != -1) {
while((c = getopt(argc, argv, "c:fu:g:d")) != -1) {
switch(c) {
case 'd':
disable_dvb = 1;
@ -152,14 +195,9 @@ main(int argc, char **argv)
case 'g':
groupnam = optarg;
break;
case 's':
settingspath = optarg;
break;
}
}
config_open_by_prgname("tvheadend", cfgfile);
if(forkaway) {
if(daemon(0, 0)) {
exit(2);
@ -189,142 +227,51 @@ main(int argc, char **argv)
umask(0);
}
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, NULL);
openlog("tvheadend", LOG_PID, logfacility);
hts_settings_init("tvheadend2");
pthread_mutex_init(&global_lock, NULL);
if(settingspath == NULL && (homedir = getenv("HOME")) != NULL) {
snprintf(buf2, sizeof(buf2), "%s/.hts", homedir);
if(mkdir(buf2, 0777) == 0 || errno == EEXIST)
settingspath = buf2;
else
syslog(LOG_ERR,
"Unable to create directory for storing settings \"%s\" -- %s",
buf, strerror(errno));
}
pthread_mutex_lock(&global_lock);
if(settingspath == NULL) {
settingspath = "/tmp";
sys_warning =
"All settings are stored in "
"'/tmp/' and may not survive a system restart. "
"Please see the configuration manual for how to setup this correctly.";
syslog(LOG_ERR, "%s", sys_warning);
}
snprintf(buf3, sizeof(buf3), "%s/tvheadend", settingspath);
settings_dir = buf3;
if(!(mkdir(settings_dir, 0777) == 0 || errno == EEXIST)) {
syslog(LOG_ERR,
"Unable to create directory for storing settings \"%s\" -- %s",
settings_dir, strerror(errno));
}
snprintf(buf, sizeof(buf), "%s/channels", settings_dir);
mkdir(buf, 0777);
snprintf(buf, sizeof(buf), "%s/transports", settings_dir);
mkdir(buf, 0777);
snprintf(buf, sizeof(buf), "%s/recordings", settings_dir);
mkdir(buf, 0777);
snprintf(buf, sizeof(buf), "%s/autorec", settings_dir);
mkdir(buf, 0777);
snprintf(buf, sizeof(buf), "%s/dvbadapters", settings_dir);
mkdir(buf, 0777);
snprintf(buf, sizeof(buf), "%s/dvbadapters", settings_dir);
mkdir(buf, 0777);
snprintf(buf, sizeof(buf), "%s/dvbmuxes", settings_dir);
mkdir(buf, 0777);
syslog(LOG_NOTICE,
"Starting HTS Tvheadend (%s), settings stored in \"%s\"",
htsversion, settings_dir);
if(!forkaway)
fprintf(stderr,
"\nStarting HTS Tvheadend (%s)\nSettings stored in \"%s\"\n",
htsversion, settings_dir);
dispatch_init();
htsparachute_init(pull_chute);
// signal(SIGTERM, doexit);
// signal(SIGINT, doexit);
access_init();
htsparachute_init(pull_chute);
tcp_server_init();
dvb_init();
http_server_init();
webui_init();
pthread_mutex_unlock(&global_lock);
/**
* Wait for SIGTERM / SIGINT, but only in this thread
*/
running = 1;
sigemptyset(&set);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGINT);
signal(SIGTERM, doexit);
signal(SIGINT, doexit);
channels_load();
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
spawn_init();
av_register_all();
av_log_set_level(AV_LOG_INFO);
tffm_init();
pkt_init();
serviceprobe_setup();
if(!disable_dvb)
dvb_init();
v4l_init();
autorec_init();
epg_init();
// xmltv_init();
subscriptions_init();
// htmlui_start();
webui_start();
// ajaxui_start();
avgen_init();
file_input_init();
cwc_init();
running = 1;
while(running) {
if(startupcounter == 0) {
syslog(LOG_NOTICE,
"Initial input setup completed, starting output modules");
if(!forkaway)
fprintf(stderr,
"\nInitial input setup completed, starting output modules\n");
startupcounter = -1;
pvr_init();
output_multicast_setup();
p = atoi(config_get_str("http-server-port", "9981"));
if(p)
http_start(p);
p = atoi(config_get_str("htsp-server-port", "9910"));
if(p)
htsp_start(p);
#if 0
p = atoi(config_get_str("xbmsp-server-port", "0"));
if(p)
xbmsp_start(p);
#endif
}
dispatcher();
}
mainloop();
syslog(LOG_NOTICE, "Exiting HTS Tvheadend");
@ -336,26 +283,6 @@ main(int argc, char **argv)
}
config_entry_t *
find_mux_config(const char *muxtype, const char *muxname)
{
config_entry_t *ce;
const char *s;
TAILQ_FOREACH(ce, &config_list, ce_link) {
if(ce->ce_type == CFG_SUB && !strcasecmp(muxtype, ce->ce_key)) {
if((s = config_get_str_sub(&ce->ce_sub, "name", NULL)) == NULL)
continue;
if(!strcmp(s, muxname))
break;
}
}
return ce;
}
static char *
convert_to(const char *in, const char *target_encoding)
@ -420,25 +347,6 @@ utf8tofilename(const char *in)
}
FILE *
settings_open_for_write(const char *name)
{
FILE *fp;
int fd;
if((fd = open(name, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
syslog(LOG_ALERT, "Unable to open settings file \"%s\" -- %s",
name, strerror(errno));
return NULL;
}
if((fp = fdopen(fd, "w+")) == NULL)
syslog(LOG_ALERT, "Unable to open settings file \"%s\" -- %s",
name, strerror(errno));
return fp;
}
/**
@ -477,6 +385,6 @@ tvhlog(int severity, const char *subsys, const char *fmt, ...)
m = htsmsg_create();
htsmsg_add_str(m, "notificationClass", "logmessage");
htsmsg_add_str(m, "logtxt", buf2);
comet_mailbox_add_message(m);
// comet_mailbox_add_message(m);
htsmsg_destroy(m);
}

10
mux.c
View file

@ -63,9 +63,14 @@ void
muxer_play(th_muxer_t *tm, int64_t toffset)
{
th_subscription_t *s = tm->tm_subscription;
th_transport_t *t = s->ths_transport;
transport_link_muxer(t, tm);
if(!tm->tm_linked) {
pthread_mutex_lock(&t->tht_delivery_mutex);
LIST_INSERT_HEAD(&s->ths_transport->tht_muxers, tm, tm_transport_link);
pthread_mutex_unlock(&t->tht_delivery_mutex);
tm->tm_linked = 1;
}
@ -193,11 +198,14 @@ void
muxer_deinit(th_muxer_t *tm, th_subscription_t *s)
{
th_muxstream_t *tms;
th_transport_t *t;
s->ths_raw_input = NULL;
s->ths_muxer = NULL;
if(tm->tm_linked)
if(tm->tm_linked) {
pthread_mutex_lock(&t->tht_delivery_mutex);
LIST_REMOVE(tm, tm_transport_link);
while((tms = LIST_FIRST(&tm->tm_streams)) != NULL)

View file

@ -40,6 +40,7 @@ notify_by_msg(const char *class, htsmsg_t *m)
void
notify_transprot_status_change(struct th_transport *t)
{
#if 0
th_subscription_t *s;
LIST_FOREACH(s, &t->tht_subscriptions, ths_transport_link)
@ -47,4 +48,5 @@ notify_transprot_status_change(struct th_transport *t)
s->ths_status_callback(s, t->tht_last_status, s->ths_opaque);
// ajax_mailbox_transport_status_change(t);
#endif
}

View file

@ -779,7 +779,7 @@ parser_compute_duration(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
static void
parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
{
th_muxer_t *tm, *next;
th_muxer_t *tm;
int64_t dts, pts, ptsoff;
assert(pkt->pkt_dts != AV_NOPTS_VALUE);
@ -828,7 +828,6 @@ parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
pkt->pkt_pts,
pkt->pkt_duration);
#endif
pkt->pkt_stream = st;
avgstat_add(&st->st_rate, pkt->pkt_payloadlen, dispatch_clock);
@ -836,20 +835,19 @@ parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
/**
* We've got something, disarm the transport timeout timer
*/
dtimer_disarm(&t->tht_receive_timer);
// dtimer_disarm(&t->tht_receive_timer);
/**
* Input is ok
*/
transport_signal_status(t, TRANSPORT_STATUS_OK);
/* Alert all muxers tied to us that a new packet has arrived.
Muxers may remove themself as a direct action of receiving a packet
(serviceprober), so we need to traverse in a safe manner */
for(tm = LIST_FIRST(&t->tht_muxers); tm != NULL; tm = next) {
next = LIST_NEXT(tm, tm_transport_link);
/* Alert all muxers tied to us that a new packet has arrived */
pthread_mutex_lock(&t->tht_delivery_mutex);
LIST_FOREACH(tm, &t->tht_muxers, tm_transport_link)
tm->tm_new_pkt(tm, st, pkt);
}
pthread_mutex_unlock(&t->tht_delivery_mutex);
/* Unref (and possibly free) the packet, muxers are supposed
to increase refcount or copy packet if they need anything */

2
psi.c
View file

@ -182,6 +182,8 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid)
if(len < 9)
return -1;
lock_assert(&global_lock);
sid = ptr[0] << 8 | ptr[1];
pcr_pid = (ptr[5] & 0x1f) << 8 | ptr[6];
dllen = (ptr[7] & 0xf) << 8 | ptr[8];

2
pvr.h
View file

@ -66,7 +66,7 @@ typedef struct pvr_rec {
th_subscription_t *pvrr_s;
pthread_t pvrr_ptid;
dtimer_t pvrr_timer;
//dtimer_t pvrr_timer;
th_ffmuxer_t pvrr_tffm;

View file

@ -31,31 +31,31 @@
#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 "teletext.h"
#include "transports.h"
#include "subscriptions.h"
#include "v4l.h"
#include "iptv_input.h"
#include "psi.h"
struct th_subscription_list subscriptions;
static dtimer_t auto_reschedule_timer;
static int
subscription_sort(th_subscription_t *a, th_subscription_t *b)
{
return b->ths_weight - a->ths_weight;
}
/**
*
*/
void
subscription_reschedule(void)
{
th_subscription_t *s;
th_transport_t *t;
lock_assert(&global_lock);
LIST_FOREACH(s, &subscriptions, ths_global_link) {
if(s->ths_transport != NULL)
continue; /* Got a transport, we're happy */
@ -74,37 +74,23 @@ subscription_reschedule(void)
}
}
static void
auto_reschedule(void *aux, int64_t now)
{
dtimer_arm(&auto_reschedule_timer, auto_reschedule, NULL, 10);
subscription_reschedule();
}
void
subscription_stop(th_subscription_t *s)
{
s->ths_callback(s, TRANSPORT_UNAVAILABLE, s->ths_opaque);
LIST_REMOVE(s, ths_transport_link);
s->ths_transport = NULL;
}
/**
*
*/
void
subscription_unsubscribe(th_subscription_t *s)
{
th_transport_t *t = s->ths_transport;
lock_assert(&global_lock);
LIST_REMOVE(s, ths_global_link);
if(s->ths_channel != NULL)
LIST_REMOVE(s, ths_channel_link);
if(t != NULL) {
subscription_stop(s);
transport_stop(t, 0);
}
if(t != NULL)
transport_remove_subscriber(t, s);
free(s->ths_title);
free(s);
@ -113,16 +99,9 @@ subscription_unsubscribe(th_subscription_t *s)
}
static int
subscription_sort(th_subscription_t *a, th_subscription_t *b)
{
return b->ths_weight - a->ths_weight;
}
/**
*
*/
th_subscription_t *
subscription_create(channel_t *ch, unsigned int weight, const char *name,
subscription_callback_t *cb, void *opaque, uint32_t u32)
@ -157,23 +136,12 @@ subscription_create(channel_t *ch, unsigned int weight, const char *name,
}
void
subscription_set_weight(th_subscription_t *s, unsigned int weight)
{
if(s->ths_weight == weight)
return;
LIST_REMOVE(s, ths_global_link);
s->ths_weight = weight;
LIST_INSERT_SORTED(&subscriptions, s, ths_global_link, subscription_sort);
subscription_reschedule();
}
/**
*
*/
void
subscriptions_init(void)
{
dtimer_arm(&auto_reschedule_timer, auto_reschedule, NULL, 10);
}

293
tcp.c
View file

@ -17,6 +17,7 @@
*/
#include <pthread.h>
#include <sys/epoll.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
@ -29,8 +30,11 @@
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include "dispatch.h"
#include "tcp.h"
#if 0
#include "dispatch.h"
#include "resolver.h"
static void tcp_client_reconnect_timeout(void *aux, int64_t now);
@ -198,6 +202,7 @@ tcp_send_msg(tcp_session_t *ses, int hiprio, void *data, size_t len)
return 0;
}
/**
*
*/
@ -627,3 +632,289 @@ tcp_set_hostname(tcp_session_t *ses, const char *hostname)
ses->tcp_hostname = strdup(hostname);
}
#endif
int
tcp_write_queue(int fd, htsbuf_queue_t *q)
{
htsbuf_data_t *hd;
int l, r;
while((hd = TAILQ_FIRST(&q->hq_q)) != NULL) {
TAILQ_REMOVE(&q->hq_q, hd, hd_link);
l = hd->hd_data_len - hd->hd_data_off;
r = write(fd, hd->hd_data + hd->hd_data_off, l);
if(l != r) {
fprintf(stderr, "write error\n");
}
free(hd->hd_data);
free(hd);
}
q->hq_size = 0;
return 0;
}
/**
*
*/
static int
tcp_fill_htsbuf_from_fd(int fd, htsbuf_queue_t *hq)
{
htsbuf_data_t *hd = TAILQ_LAST(&hq->hq_q, htsbuf_data_queue);
int c;
if(hd != NULL) {
/* Fill out any previous buffer */
c = hd->hd_data_size - hd->hd_data_len;
if(c > 0) {
c = read(fd, hd->hd_data + hd->hd_data_len, c);
if(c < 1)
return -1;
hd->hd_data_len += c;
hq->hq_size += c;
return 0;
}
}
hd = malloc(sizeof(htsbuf_data_t));
hd->hd_data_size = 1000;
hd->hd_data = malloc(hd->hd_data_size);
c = read(fd, hd->hd_data, hd->hd_data_size);
if(c < 1) {
free(hd->hd_data);
free(hd);
return -1;
}
hd->hd_data_len = c;
hd->hd_data_off = 0;
TAILQ_INSERT_TAIL(&hq->hq_q, hd, hd_link);
hq->hq_size += c;
return 0;
}
/**
*
*/
int
tcp_read_line(int fd, char *buf, const size_t bufsize, htsbuf_queue_t *spill)
{
int len;
while(1) {
len = htsbuf_find(spill, 0xa);
if(len == -1) {
if(tcp_fill_htsbuf_from_fd(fd, spill) < 0)
return -1;
continue;
}
if(len >= bufsize - 1)
return -1;
htsbuf_read(spill, buf, len);
buf[len] = 0;
while(len > 0 && buf[len - 1] < 32)
buf[--len] = 0;
htsbuf_drop(spill, 1); /* Drop the \n */
return 0;
}
}
/**
*
*/
int
tcp_read_data(int fd, char *buf, const size_t bufsize, htsbuf_queue_t *spill)
{
int x, tot = htsbuf_read(spill, buf, bufsize);
if(tot == bufsize)
return 0;
x = recv(fd, buf + tot, bufsize - tot, MSG_WAITALL);
if(x != bufsize - tot)
return -1;
return 0;
}
/**
*
*/
static int tcp_server_epoll_fd;
typedef struct tcp_server {
tcp_server_callback_t *start;
void *opaque;
int serverfd;
} tcp_server_t;
typedef struct tcp_server_launch_t {
tcp_server_callback_t *start;
void *opaque;
int fd;
struct sockaddr_in source;
} tcp_server_launch_t;
/**
*
*/
static void *
tcp_server_start(void *aux)
{
tcp_server_launch_t *tsl = aux;
int val;
val = 1;
setsockopt(tsl->fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
val = 30;
setsockopt(tsl->fd, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(val));
val = 15;
setsockopt(tsl->fd, SOL_TCP, TCP_KEEPINTVL, &val, sizeof(val));
val = 5;
setsockopt(tsl->fd, SOL_TCP, TCP_KEEPCNT, &val, sizeof(val));
val = 1;
setsockopt(tsl->fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
tsl->start(tsl->fd, tsl->opaque, &tsl->source);
free(tsl);
return NULL;
}
/**
*
*/
static void *
tcp_server_loop(void *aux)
{
int r, i;
struct epoll_event ev[1];
tcp_server_t *ts;
tcp_server_launch_t *tsl;
pthread_attr_t attr;
pthread_t tid;
socklen_t slen;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
while(1) {
r = epoll_wait(tcp_server_epoll_fd, ev, sizeof(ev) / sizeof(ev[0]), -1);
if(r == -1) {
perror("tcp_server: epoll_wait");
sleep(1);
continue;
}
for(i = 0; i < r; i++) {
ts = ev[i].data.ptr;
if(ev[i].events & EPOLLHUP) {
close(ts->serverfd);
free(ts);
continue;
}
if(ev[i].events & EPOLLIN) {
tsl = malloc(sizeof(tcp_server_launch_t));
tsl->start = ts->start;
tsl->opaque = ts->opaque;
slen = sizeof(struct sockaddr_in);
tsl->fd = accept(ts->serverfd,
(struct sockaddr *)&tsl->source, &slen);
if(tsl->fd == -1) {
perror("accept");
free(tsl);
sleep(1);
continue;
}
pthread_create(&tid, &attr, tcp_server_start, tsl);
}
}
}
}
/**
*
*/
void *
tcp_server_create(int port, tcp_server_callback_t *start, void *opaque)
{
int fd, x;
struct epoll_event e;
tcp_server_t *ts;
struct sockaddr_in s;
int one = 1;
memset(&e, 0, sizeof(e));
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1)
return NULL;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
memset(&s, 0, sizeof(s));
s.sin_family = AF_INET;
s.sin_port = htons(port);
x = bind(fd, (struct sockaddr *)&s, sizeof(s));
if(x < 0) {
close(fd);
return NULL;
}
listen(fd, 1);
ts = malloc(sizeof(tcp_server_t));
ts->serverfd = fd;
ts->start = start;
ts->opaque = opaque;
e.events = EPOLLIN;
e.data.ptr = ts;
epoll_ctl(tcp_server_epoll_fd, EPOLL_CTL_ADD, fd, &e);
printf("Adding fd %d, listening on port %d\n", fd, port);
return ts;
}
/**
*
*/
void
tcp_server_init(void)
{
pthread_t tid;
tcp_server_epoll_fd = epoll_create(10);
pthread_create(&tid, NULL, tcp_server_loop, NULL);
}

22
tcp.h
View file

@ -21,6 +21,9 @@
#include <libhts/htsbuf.h>
#if 0
typedef enum {
TCP_CONNECT,
TCP_DISCONNECT,
@ -52,7 +55,7 @@ typedef struct tcp_session {
/* These are only used when we spawn as a client */
int tcp_enabled;
dtimer_t tcp_timer;
// dtimer_t tcp_timer;
char *tcp_name;
int tcp_port;
char *tcp_hostname;
@ -98,4 +101,21 @@ void tcp_set_hostname(tcp_session_t *ses, const char *hostname);
int tcp_send_msg(tcp_session_t *ses, int hiprio, void *data, size_t len);
#endif
void tcp_server_init(void);
typedef void (tcp_server_callback_t)(int fd, void *opaque,
struct sockaddr_in *source);
void *tcp_server_create(int port, tcp_server_callback_t *start, void *opaque);
int tcp_read_line(int fd, char *buf, const size_t bufsize,
htsbuf_queue_t *spill);
int tcp_read_data(int fd, char *buf, const size_t bufsize,
htsbuf_queue_t *spill);
int tcp_write_queue(int fd, htsbuf_queue_t *q);
#endif /* TCP_H_ */

View file

@ -31,20 +31,13 @@
#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 "teletext.h"
#include "transports.h"
#include "subscriptions.h"
#include "tsdemux.h"
#include "v4l.h"
#include "iptv_input.h"
#include "psi.h"
#include "buffer.h"
#include "channels.h"
@ -56,45 +49,40 @@
static struct th_transport_list transporthash[TRANSPORT_HASH_WIDTH];
static void transport_data_timeout(void *aux, int64_t now);
//static void transport_data_timeout(void *aux, int64_t now);
//static dtimer_t transport_monitor_timer;
//static const char *transport_settings_path(th_transport_t *t);
//static void transport_monitor(void *aux, int64_t now);
void
transport_stop(th_transport_t *t, int flush_subscriptions)
/**
* Transport lock must be held
*/
static void
transport_stop(th_transport_t *t)
{
th_subscription_t *s;
th_descrambler_t *td;
th_stream_t *st;
th_pkt_t *pkt;
if(flush_subscriptions) {
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
subscription_stop(s);
} else {
if(LIST_FIRST(&t->tht_subscriptions))
return;
}
dtimer_disarm(&t->tht_receive_timer);
// dtimer_disarm(&t->tht_receive_timer);
// dtimer_disarm(&transport_monitor_timer, transport_monitor, t, 1);
t->tht_stop_feed(t);
while((td = LIST_FIRST(&t->tht_descramblers)) != NULL)
td->td_stop(td);
td->td_stop(td);
t->tht_tt_commercial_advice = COMMERCIAL_UNKNOWN;
assert(LIST_FIRST(&t->tht_muxers) == NULL);
/*
/**
* Clean up each stream
*/
LIST_FOREACH(st, &t->tht_streams, st_link) {
if(st->st_parser != NULL)
@ -144,13 +132,87 @@ transport_stop(th_transport_t *t, int flush_subscriptions)
/* Flush framestore */
while((pkt = TAILQ_FIRST(&st->st_pktq)) != NULL)
pkt_unstore(pkt);
pkt_unstore(st, pkt);
}
}
/**
*
*/
static void
remove_subscriber(th_subscription_t *s)
{
s->ths_callback(s, TRANSPORT_UNAVAILABLE, s->ths_opaque);
LIST_REMOVE(s, ths_transport_link);
s->ths_transport = NULL;
}
/*
/**
* Remove the given subscriber from the transport
*
* if s == NULL all subscribers will be removed
*
* Global lock must be held
*/
void
transport_remove_subscriber(th_transport_t *t, th_subscription_t *s)
{
lock_assert(&global_lock);
if(s == NULL) {
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
remove_subscriber(s);
} else {
remove_subscriber(s);
}
if(LIST_FIRST(&t->tht_subscriptions) == NULL)
transport_stop(t);
}
/**
*
*/
void
transport_link_muxer(th_transport_t *t, th_muxer_t *tm)
{
lock_assert(&global_lock);
if(tm->tm_transport != NULL) {
assert(tm->tm_transport == t);
return;
}
pthread_mutex_lock(&t->tht_delivery_mutex);
LIST_INSERT_HEAD(&t->tht_muxers, tm, tm_transport_link);
pthread_mutex_unlock(&t->tht_delivery_mutex);
tm->tm_transport = t;
}
/**
*
*/
void
transport_unlink_muxer(th_muxer_t *tm)
{
th_transport_t *t = tm->tm_transport;
if(t == NULL)
return;
lock_assert(&global_lock);
pthread_mutex_lock(&t->tht_delivery_mutex);
LIST_REMOVE(tm, tm_transport_link);
pthread_mutex_unlock(&t->tht_delivery_mutex);
tm->tm_transport = NULL;
}
/**
*
*/
int
@ -160,6 +222,8 @@ transport_start(th_transport_t *t, unsigned int weight, int force_start)
AVCodec *c;
enum CodecID id;
lock_assert(&global_lock);
assert(t->tht_runstatus != TRANSPORT_RUNNING);
if(t->tht_start_feed(t, weight, TRANSPORT_RUNNING, force_start))
@ -205,8 +269,8 @@ transport_start(th_transport_t *t, unsigned int weight, int force_start)
}
}
cwc_transport_start(t);
dtimer_arm(&t->tht_receive_timer, transport_data_timeout, t, 4);
// cwc_transport_start(t);
// dtimer_arm(&t->tht_receive_timer, transport_data_timeout, t, 4);
transport_signal_status(t, TRANSPORT_STATUS_STARTING);
return 0;
}
@ -286,6 +350,8 @@ transport_find(channel_t *ch, unsigned int weight)
th_transport_t *t, **vec;
int cnt = 0, i;
lock_assert(&global_lock);
/* First, sort all transports in order */
LIST_FOREACH(t, &ch->ch_transports, tht_ch_link)
@ -326,22 +392,9 @@ transport_find(channel_t *ch, unsigned int weight)
}
/*
*
/**
*
*/
static void
transport_flush_subscribers(th_transport_t *t)
{
th_subscription_t *s;
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
subscription_stop(s);
}
unsigned int
transport_compute_weight(struct th_transport_list *head)
{
@ -349,6 +402,8 @@ transport_compute_weight(struct th_transport_list *head)
th_subscription_t *s;
int w = 0;
lock_assert(&global_lock);
LIST_FOREACH(t, head, tht_active_link) {
LIST_FOREACH(s, &t->tht_subscriptions, ths_transport_link) {
if(s->ths_weight > w)
@ -360,76 +415,6 @@ transport_compute_weight(struct th_transport_list *head)
#if 0
static void
transport_monitor(void *aux, int64_t now)
{
th_transport_t *t = aux;
int v;
dtimer_arm(&transport_monitor_timer, transport_monitor, t, 1);
if(t->tht_status == TRANSPORT_IDLE)
return;
if(t->tht_monitor_suspend > 0) {
t->tht_monitor_suspend--;
return;
}
v = avgstat_read_and_expire(&t->tht_rate, dispatch_clock) * 8 / 1000 / 10;
if(v < 500) {
switch(t->tht_rate_error_log_limiter) {
case 0:
syslog(LOG_WARNING, "\"%s\" on \"%s\", very low bitrate: %d kb/s",
t->tht_channel->ch_name, t->tht_name, v);
/* FALLTHRU */
case 1 ... 9:
t->tht_rate_error_log_limiter++;
break;
}
} else {
switch(t->tht_rate_error_log_limiter) {
case 0:
break;
case 1:
syslog(LOG_NOTICE, "\"%s\" on \"%s\", bitrate ok (%d kb/s)",
t->tht_channel->ch_name, t->tht_name, v);
/* FALLTHRU */
default:
t->tht_rate_error_log_limiter--;
}
}
v = avgstat_read(&t->tht_cc_errors, 10, dispatch_clock);
if(v > t->tht_cc_error_log_limiter) {
t->tht_cc_error_log_limiter += v * 3;
syslog(LOG_WARNING, "\"%s\" on \"%s\", "
"%.2f continuity errors/s (10 sec average)",
t->tht_channel->ch_name, t->tht_name, (float)v / 10.);
} else if(v == 0) {
switch(t->tht_cc_error_log_limiter) {
case 0:
break;
case 1:
syslog(LOG_NOTICE, "\"%s\" on \"%s\", no continuity errors",
t->tht_channel->ch_name, t->tht_name);
/* FALLTHRU */
default:
t->tht_cc_error_log_limiter--;
}
}
}
#endif
/**
* Timer that fires if transport is not receiving any data
*/
@ -439,7 +424,7 @@ transport_data_timeout(void *aux, int64_t now)
th_transport_t *t = aux;
transport_signal_status(t, TRANSPORT_STATUS_NO_INPUT);
}
#endif
/**
* Destroy a transport
@ -448,8 +433,14 @@ void
transport_destroy(th_transport_t *t)
{
th_stream_t *st;
th_subscription_t *s;
lock_assert(&global_lock);
dtimer_disarm(&t->tht_receive_timer);
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
remove_subscriber(s);
//dtimer_disarm(&t->tht_receive_timer);
free((void *)t->tht_name);
@ -460,11 +451,9 @@ transport_destroy(th_transport_t *t)
LIST_REMOVE(t, tht_mux_link);
LIST_REMOVE(t, tht_hash_link);
transport_flush_subscribers(t);
if(t->tht_runstatus != TRANSPORT_IDLE)
transport_stop(t, 0);
transport_stop(t);
free(t->tht_identifier);
@ -477,7 +466,7 @@ transport_destroy(th_transport_t *t)
free(st);
}
serviceprobe_delete(t);
abort();// serviceprobe_delete(t);
free(t);
}
@ -491,6 +480,10 @@ transport_create(const char *identifier, int type, int source_type)
{
unsigned int hash = tvh_strhash(identifier, TRANSPORT_HASH_WIDTH);
th_transport_t *t = calloc(1, sizeof(th_transport_t));
lock_assert(&global_lock);
pthread_mutex_init(&t->tht_delivery_mutex, NULL);
t->tht_identifier = strdup(identifier);
t->tht_type = type;
t->tht_source_type = source_type;
@ -507,6 +500,9 @@ transport_find_by_identifier(const char *identifier)
{
th_transport_t *t;
unsigned int hash = tvh_strhash(identifier, TRANSPORT_HASH_WIDTH);
lock_assert(&global_lock);
LIST_FOREACH(t, &transporthash[hash], tht_hash_link)
if(!strcmp(t->tht_identifier, identifier))
break;
@ -524,6 +520,8 @@ transport_add_stream(th_transport_t *t, int pid, tv_streamtype_t type)
th_stream_t *st;
int i = 0;
lock_assert(&global_lock);
LIST_FOREACH(st, &t->tht_streams, st_link) {
i++;
if(pid != -1 && st->st_pid == pid)
@ -553,6 +551,9 @@ transport_add_stream(th_transport_t *t, int pid, tv_streamtype_t type)
void
transport_map_channel(th_transport_t *t, channel_t *ch)
{
lock_assert(&global_lock);
assert(t->tht_ch == NULL);
if(ch == NULL) {
@ -579,6 +580,8 @@ transport_map_channel(th_transport_t *t, channel_t *ch)
void
transport_unmap_channel(th_transport_t *t)
{
lock_assert(&global_lock);
t->tht_ch = NULL;
LIST_REMOVE(t, tht_ch_link);
}
@ -635,7 +638,7 @@ transport_signal_status(th_transport_t *t, int newstatus)
return;
t->tht_last_status = newstatus;
notify_transprot_status_change(t);
//notify_transprot_status_change(t);
}

View file

@ -25,8 +25,6 @@ unsigned int transport_compute_weight(struct th_transport_list *head);
int transport_start(th_transport_t *t, unsigned int weight, int force_start);
void transport_stop(th_transport_t *t, int flush_subscriptions);
th_transport_t *transport_create(const char *identifier, int type,
int source_type);
@ -57,4 +55,10 @@ void transport_signal_status(th_transport_t *t, int newstatus);
const char *transport_status_to_text(int status);
void transport_remove_subscriber(th_transport_t *t, th_subscription_t *s);
void transport_link_muxer(th_transport_t *t, th_muxer_t *tm);
void transport_unlink_muxer(th_muxer_t *tm);
#endif /* TRANSPORTS_H */

View file

@ -35,8 +35,6 @@
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <libhts/htscfg.h>
#include "tvhead.h"
#include "dispatch.h"
#include "teletext.h"
@ -130,7 +128,7 @@ ts_recv_packet0(th_transport_t *t, th_stream_t *st, uint8_t *tsb)
break;
case HTSTV_TELETEXT:
teletext_input(t, tsb);
// teletext_input(t, tsb);
break;
default:

149
tvhead.h
View file

@ -20,16 +20,34 @@
#define TV_HEAD_H
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <libhts/htsq.h>
#include <libhts/htstv.h>
#include <libhts/htscfg.h>
#include <libhts/avg.h>
#include <libhts/hts_strtab.h>
#include <libavcodec/avcodec.h>
#include <libhts/redblack.h>
#include <linux/dvb/frontend.h>
extern pthread_mutex_t global_lock;
static inline void
lock_assert0(pthread_mutex_t *l, const char *file, int line)
{
if(pthread_mutex_trylock(l) == EBUSY)
return;
fprintf(stderr, "Mutex not held at %s:%d\n", file, line);
abort();
}
#define lock_assert(l) lock_assert0(l, __FILE__, __LINE__)
/*
* Commercial status
*/
@ -40,35 +58,29 @@ typedef enum {
} th_commercial_advice_t;
/**
* Auxiliary data for plugins
*/
typedef struct pluginaux {
LIST_ENTRY(pluginaux) pa_link;
struct th_plugin *pa_plugin;
} pluginaux_t;
LIST_HEAD(pluginaux_list, pluginaux);
/*
* Dispatch timer
* global timer
*/
typedef void (dti_callback_t)(void *opaque, int64_t now);
typedef struct dtimer {
LIST_ENTRY(dtimer) dti_link;
dti_callback_t *dti_callback;
void *dti_opaque;
int64_t dti_expire;
} dtimer_t;
typedef void (gti_callback_t)(void *opaque);
typedef struct gtimer {
LIST_ENTRY(gtimer) gti_link;
gti_callback_t *gti_callback;
void *gti_opaque;
time_t gti_expire;
} gtimer_t;
void gtimer_arm(gtimer_t *gti, gti_callback_t *callback, void *opaque,
int delta);
void gtimer_disarm(gtimer_t *gti);
/*
* List / Queue header declarations
*/
LIST_HEAD(th_subscription_list, th_subscription);
RB_HEAD(channel_tree, channel);
TAILQ_HEAD(channel_queue, channel);
@ -132,21 +144,10 @@ typedef struct th_v4l_adapter {
} th_v4l_adapter_t;
/*
* Mux instance modes
*/
typedef enum {
TDMI_IDLE, /* Not tuned */
TDMI_RUNNING, /* Tuned with a subscriber */
TDMI_IDLESCAN, /* Just scanning */
} tdmi_state_t;
/*
* DVB Mux instance
*/
typedef struct th_dvb_mux_instance {
int tdmi_refcnt;
enum {
TDMI_QUICKSCAN_NONE,
@ -170,13 +171,15 @@ typedef struct th_dvb_mux_instance {
time_t tdmi_time;
LIST_HEAD(, th_dvb_table) tdmi_tables;
pthread_mutex_t tdmi_table_lock;
tdmi_state_t tdmi_state;
dtimer_t tdmi_initial_scan_timer;
const char *tdmi_status;
char *tdmi_last_status; /* For notification updates */
enum {
TDMI_FE_UNKNOWN,
TDMI_FE_NO_SIGNAL,
TDMI_FE_FAINT_SIGNAL,
TDMI_FE_BAD_SIGNAL,
TDMI_FE_CONTANT_FEC,
TDMI_FE_BURSTY_FEC,
TDMI_FE_OK,
} tdmi_fe_status;
int tdmi_quality;
@ -197,27 +200,6 @@ typedef struct th_dvb_mux_instance {
} th_dvb_mux_instance_t;
/*
*
*/
typedef struct th_dvb_table {
LIST_ENTRY(th_dvb_table) tdt_link;
char *tdt_name;
void *tdt_handle;
struct th_dvb_mux_instance *tdt_tdmi;
void *tdt_opaque;
void (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
uint8_t tableid, void *opaque);
int tdt_fd;
struct dmx_sct_filter_params *tdt_fparams;
int tdt_quickreq;
int tdt_count;
} th_dvb_table_t;
/*
* DVB Adapter (one of these per physical adapter)
*/
@ -230,16 +212,13 @@ typedef struct th_dvb_adapter {
th_dvb_mux_instance_t *tda_mux_current;
int tda_table_epollfd;
const char *tda_rootpath;
char *tda_identifier;
uint32_t tda_autodiscovery;
char *tda_displayname;
pthread_mutex_t tda_lock;
pthread_cond_t tda_cond;
TAILQ_HEAD(, dvb_fe_cmd) tda_fe_cmd_queue;
int tda_fe_errors;
int tda_fe_fd;
int tda_type;
struct dvb_frontend_info *tda_fe_info;
@ -248,10 +227,13 @@ typedef struct th_dvb_adapter {
char *tda_dvr_path;
gtimer_t tda_mux_scanner_timer;
pthread_mutex_t tda_delivery_mutex;
struct th_transport_list tda_transports; /* Currently bound transports */
dtimer_t tda_fec_monitor_timer;
dtimer_t tda_mux_scanner_timer;
gtimer_t tda_fe_monitor_timer;
int tda_fe_monitor_hold;
} th_dvb_adapter_t;
@ -445,6 +427,8 @@ typedef struct th_transport {
int (*tht_quality_index)(struct th_transport *t);
pthread_mutex_t tht_delivery_mutex;
struct th_muxer_list tht_muxers; /* muxers */
/*
@ -528,9 +512,6 @@ typedef struct th_transport {
/**
* Last known status (or error)
*/
dtimer_t tht_receive_timer; /* we use this timer to trig when a transport
does not receive any data at all */
int tht_last_status;
#define TRANSPORT_STATUS_UNKNOWN 0
@ -587,14 +568,11 @@ typedef struct th_pkt {
uint8_t pkt_frametype;
uint8_t pkt_commercial;
th_stream_t *pkt_stream;
int64_t pkt_dts;
int64_t pkt_pts;
int pkt_duration;
int pkt_refcount;
th_storage_t *pkt_storage;
TAILQ_ENTRY(th_pkt) pkt_disk_link;
int pkt_storage_offset;
@ -669,7 +647,7 @@ typedef struct th_muxstream {
int64_t tms_delta;
int64_t tms_mux_offset;
dtimer_t tms_mux_timer;
//dtimer_t tms_mux_timer;
/* MPEG TS multiplex stuff */
@ -704,7 +682,7 @@ typedef struct th_muxer {
th_mux_newpkt_t *tm_new_pkt;
LIST_ENTRY(th_muxer) tm_transport_link;
int tm_linked;
th_transport_t *tm_transport;
int64_t tm_offset;
@ -780,6 +758,9 @@ typedef struct tt_decoder {
*/
typedef struct channel {
int ch_refcount;
int ch_zombie;
RB_ENTRY(channel) ch_name_link;
char *ch_name;
char *ch_sname;
@ -913,15 +894,9 @@ typedef struct event {
} event_t;
config_entry_t *find_mux_config(const char *muxtype, const char *muxname);
char *utf8toprintable(const char *in);
char *utf8tofilename(const char *in);
const char *htstvstreamtype2txt(tv_streamtype_t s);
uint32_t tag_get(void);
extern const char *settings_dir;
FILE *settings_open_for_write(const char *name);
FILE *settings_open_for_read(const char *name);
extern const char *sys_warning;
static inline unsigned int tvh_strhash(const char *s, unsigned int mod)
{
@ -945,4 +920,16 @@ void tvhlog(int severity, const char *subsys, const char *fmt, ...);
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
static inline int64_t
getclock_hires(void)
{
int64_t now;
struct timeval tv;
gettimeofday(&tv, NULL);
now = (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
return now;
}
#endif /* TV_HEAD_H */

View file

@ -35,6 +35,9 @@
#include "webui/webui.h"
#include "access.h"
static pthread_mutex_t comet_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t comet_cond = PTHREAD_COND_INITIALIZER;
#define MAILBOX_UNUSED_TIMEOUT 20
#define MAILBOX_EMPTY_REPLY_TIMEOUT 10
@ -47,13 +50,9 @@ static LIST_HEAD(, comet_mailbox) mailboxes;
int mailbox_tally;
typedef struct comet_mailbox {
char *cmb_boxid; /* an md5hash */
dtimer_t cmb_timer;
http_reply_t *cmb_hr; /* Pending request */
htsmsg_t *cmb_messages; /* A vector */
time_t cmb_last_used;
LIST_ENTRY(comet_mailbox) cmb_link;
} comet_mailbox_t;
@ -72,36 +71,29 @@ cmb_destroy(comet_mailbox_t *cmb)
LIST_REMOVE(cmb, cmb_link);
dtimer_disarm(&cmb->cmb_timer);
free(cmb->cmb_boxid);
free(cmb);
}
/**
*
*/
static void
comet_mailbox_unused(void *opaque, int64_t now)
void
comet_flush(void)
{
comet_mailbox_t *cmb = opaque;
assert(cmb->cmb_hr == NULL);
cmb_destroy(cmb);
}
comet_mailbox_t *cmb, *next;
/**
*
*/
static void
comet_mailbox_connection_lost(http_reply_t *hr, void *opaque)
{
comet_mailbox_t *cmb = opaque;
assert(hr == cmb->cmb_hr);
cmb_destroy(cmb);
}
pthread_mutex_lock(&comet_mutex);
for(cmb = LIST_FIRST(&mailboxes); cmb != NULL; cmb = next) {
next = LIST_NEXT(cmb, cmb_link);
if(cmb->cmb_last_used && cmb->cmb_last_used + 60 < dispatch_clock)
cmb_destroy(cmb);
}
pthread_mutex_unlock(&comet_mutex);
}
/**
@ -134,66 +126,29 @@ comet_mailbox_create(void)
id[32] = 0;
cmb->cmb_boxid = strdup(id);
time(&cmb->cmb_last_used);
mailbox_tally++;
LIST_INSERT_HEAD(&mailboxes, cmb, cmb_link);
dtimer_arm(&cmb->cmb_timer, comet_mailbox_unused, cmb,
MAILBOX_UNUSED_TIMEOUT);
return cmb;
}
/**
*
*/
static void
comet_mailbox_reply(comet_mailbox_t *cmb, http_reply_t *hr)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "boxid", cmb->cmb_boxid);
htsmsg_add_msg(m, "messages", cmb->cmb_messages ?: htsmsg_create_array());
cmb->cmb_messages = NULL;
htsmsg_json_serialize(m, &hr->hr_q, 0);
htsmsg_destroy(m);
http_output(hr->hr_connection, hr, "text/x-json; charset=UTF-8", NULL, 0);
cmb->cmb_hr = NULL;
/* Arm a timer in case the browser won't call back */
dtimer_arm(&cmb->cmb_timer, comet_mailbox_unused, cmb,
MAILBOX_UNUSED_TIMEOUT);
}
/**
*
*/
static void
comet_mailbox_empty_reply(void *opaque, int64_t now)
{
comet_mailbox_t *cmb = opaque;
http_reply_t *hr = cmb->cmb_hr;
comet_mailbox_reply(cmb, hr);
http_continue(hr->hr_connection);
}
/**
* Poll callback
*
* Prepare the mailbox for reply
*/
static int
comet_mailbox_poll(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
comet_mailbox_poll(http_connection_t *hc, const char *remain, void *opaque)
{
comet_mailbox_t *cmb = NULL;
const char *cometid = http_arg_get(&hc->hc_req_args, "boxid");
time_t reqtime;
struct timespec ts;
htsmsg_t *m;
usleep(100000); /* Always sleep 0.1 sec to avoid comet storms */
pthread_mutex_lock(&comet_mutex);
if(cometid != NULL)
LIST_FOREACH(cmb, &mailboxes, cmb_link)
@ -203,57 +158,32 @@ comet_mailbox_poll(http_connection_t *hc, http_reply_t *hr,
if(cmb == NULL)
cmb = comet_mailbox_create();
if(cmb->cmb_hr != NULL) {
mbdebug("mailbox already processing\n");
return 409;
}
time(&reqtime);
if(cmb->cmb_messages != NULL) {
/* Pending letters, direct reply */
mbdebug("direct reply\n");
comet_mailbox_reply(cmb, hr);
return 0;
}
ts.tv_sec = reqtime + 10;
ts.tv_nsec = 0;
mbdebug("nothing in queue, waiting\n");
cmb->cmb_last_used = 0; /* Make sure we're not flushed out */
cmb->cmb_hr = hr;
if(cmb->cmb_messages == NULL)
pthread_cond_timedwait(&comet_cond, &comet_mutex, &ts);
hr->hr_opaque = cmb;
hr->hr_destroy = comet_mailbox_connection_lost;
m = htsmsg_create();
htsmsg_add_str(m, "boxid", cmb->cmb_boxid);
htsmsg_add_msg(m, "messages", cmb->cmb_messages ?: htsmsg_create_array());
cmb->cmb_messages = NULL;
cmb->cmb_last_used = dispatch_clock;
dtimer_arm(&cmb->cmb_timer, comet_mailbox_empty_reply, cmb,
MAILBOX_EMPTY_REPLY_TIMEOUT);
pthread_mutex_unlock(&comet_mutex);
htsmsg_json_serialize(m, &hc->hc_reply, 0);
htsmsg_destroy(m);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
/**
*
*/
#if 0
static dtimer_t comet_clock;
static void
comet_ticktack(void *opaque, int64_t now)
{
char buf[64];
htsmsg_t *x;
snprintf(buf, sizeof(buf), "The clock is now %lluµs past 1970",
now);
x = htsmsg_create();
htsmsg_add_str(x, "type", "logmessage");
htsmsg_add_str(x, "logtxt", buf);
comet_mailbox_add_message(x);
htsmsg_destroy(x);
dtimer_arm(&comet_clock, comet_ticktack, NULL, 1);
}
#endif
/**
*
*/
@ -261,25 +191,9 @@ void
comet_init(void)
{
http_path_add("/comet", NULL, comet_mailbox_poll, ACCESS_WEB_INTERFACE);
// dtimer_arm(&comet_clock, comet_ticktack, NULL, 1);
}
/**
* Delayed delivery of mailbox replies
*/
static void
comet_mailbox_deliver(void *opaque, int64_t now)
{
comet_mailbox_t *cmb = opaque;
http_connection_t *hc;
hc = cmb->cmb_hr->hr_connection;
comet_mailbox_reply(cmb, cmb->cmb_hr);
http_continue(hc);
}
/**
*
*/
@ -288,14 +202,15 @@ comet_mailbox_add_message(htsmsg_t *m)
{
comet_mailbox_t *cmb;
pthread_mutex_lock(&comet_mutex);
LIST_FOREACH(cmb, &mailboxes, cmb_link) {
if(cmb->cmb_messages == NULL)
cmb->cmb_messages = htsmsg_create_array();
htsmsg_add_msg(cmb->cmb_messages, NULL, htsmsg_copy(m));
if(cmb->cmb_hr != NULL)
dtimer_arm_hires(&cmb->cmb_timer, comet_mailbox_deliver, cmb,
getclock_hires() + 100000);
}
pthread_cond_broadcast(&comet_cond);
pthread_mutex_unlock(&comet_mutex);
}

View file

@ -75,10 +75,9 @@ extjs_exec(htsbuf_queue_t *hq, const char *fmt, ...)
* PVR info, deliver info about the given PVR entry
*/
static int
extjs_root(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
extjs_root(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
#define EXTJSPATH "static/extjs"
@ -141,7 +140,7 @@ extjs_root(http_connection_t *hc, http_reply_t *hr,
"<div id=\"systemlog\"></div>\n"
"</body></html>\n",
htsversion);
http_output_html(hc, hr);
http_output_html(hc);
return 0;
}
@ -150,10 +149,9 @@ extjs_root(http_connection_t *hc, http_reply_t *hr,
*
*/
static int
extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
dtable_t *dt;
htsmsg_t *out = NULL, *in, *array;
@ -166,6 +164,8 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
pthread_mutex_lock(&global_lock);
if(!strcmp(op, "create")) {
out = dtable_record_create(dt);
@ -177,20 +177,24 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
} else if(!strcmp(op, "update")) {
if(in == NULL)
return HTTP_STATUS_BAD_REQUEST;
goto bad;
dtable_record_update_by_array(dt, in);
} else if(!strcmp(op, "delete")) {
if(in == NULL)
return HTTP_STATUS_BAD_REQUEST;
goto bad;
dtable_record_delete_by_array(dt, in);
} else {
bad:
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_BAD_REQUEST;
}
pthread_mutex_unlock(&global_lock);
if(in != NULL)
htsmsg_destroy(in);
@ -198,7 +202,7 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
}
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
@ -207,10 +211,9 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
*
*/
static int
extjs_chlist(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
extjs_chlist(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
htsmsg_t *out, *array, *c;
channel_t *ch;
@ -218,6 +221,8 @@ extjs_chlist(http_connection_t *hc, http_reply_t *hr,
array = htsmsg_create_array();
pthread_mutex_lock(&global_lock);
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
c = htsmsg_create();
htsmsg_add_str(c, "name", ch->ch_name);
@ -225,11 +230,13 @@ extjs_chlist(http_connection_t *hc, http_reply_t *hr,
htsmsg_add_msg(array, NULL, c);
}
pthread_mutex_unlock(&global_lock);
htsmsg_add_msg(out, "entries", array);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
@ -261,10 +268,9 @@ extjs_dvbtree_node(htsmsg_t *array, int leaf, const char *id, const char *name,
*
*/
static int
extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
extjs_dvbtree(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
const char *s = http_arg_get(&hc->hc_req_args, "node");
htsmsg_t *out = NULL;
char buf[200];
@ -276,6 +282,7 @@ extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
return HTTP_STATUS_BAD_REQUEST;
out = htsmsg_create_array();
pthread_mutex_lock(&global_lock);
if(!strcmp(s, "root")) {
/**
@ -299,7 +306,7 @@ extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
extjs_dvbtree_node(out, 0,
tdmi->tdmi_identifier, buf, "DVB Mux",
tdmi->tdmi_last_status,
dvb_mux_status(tdmi),
tdmi->tdmi_quality, "mux");
}
} else if((tdmi = dvb_mux_find_by_identifier(s)) != NULL) {
@ -317,9 +324,10 @@ extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
}
}
pthread_mutex_unlock(&global_lock);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
@ -328,10 +336,9 @@ extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
*
*/
static int
extjs_dvbnetworks(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
const char *s = http_arg_get(&hc->hc_req_args, "node");
const char *a = http_arg_get(&hc->hc_req_args, "adapter");
th_dvb_adapter_t *tda;
@ -340,14 +347,20 @@ extjs_dvbnetworks(http_connection_t *hc, http_reply_t *hr,
if(s == NULL || a == NULL)
return HTTP_STATUS_BAD_REQUEST;
if((tda = dvb_adapter_find_by_identifier(a)) == NULL)
pthread_mutex_lock(&global_lock);
if((tda = dvb_adapter_find_by_identifier(a)) == NULL) {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_BAD_REQUEST;
}
pthread_mutex_unlock(&global_lock);
out = dvb_mux_preconf_get_node(tda->tda_type, s);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
@ -372,21 +385,22 @@ json_single_record(htsmsg_t *rec, const char *root)
*
*/
static int
extjs_dvbadapter(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
const char *s = http_arg_get(&hc->hc_req_args, "adapterId");
const char *op = http_arg_get(&hc->hc_req_args, "op");
th_dvb_adapter_t *tda = s ? dvb_adapter_find_by_identifier(s) : NULL;
th_dvb_mux_instance_t *tdmi;
th_transport_t *t;
//th_dvb_mux_instance_t *tdmi;
// th_transport_t *t;
htsmsg_t *r, *out;
if(tda == NULL)
return HTTP_STATUS_BAD_REQUEST;
pthread_mutex_lock(&global_lock);
if(!strcmp(op, "load")) {
r = htsmsg_create();
htsmsg_add_str(r, "id", tda->tda_identifier);
@ -418,24 +432,27 @@ extjs_dvbadapter(http_connection_t *hc, http_reply_t *hr,
tvhlog(LOG_NOTICE, "web interface",
"Service probe started on \"%s\"", tda->tda_displayname);
#if 0
RB_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
serviceprobe_add(t);
}
}
#endif
out = htsmsg_create();
htsmsg_add_u32(out, "success", 1);
} else {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_BAD_REQUEST;
}
pthread_mutex_unlock(&global_lock);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
@ -537,20 +554,24 @@ build_transport_msg(th_transport_t *t)
*
*/
static int
extjs_channel(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
extjs_channel(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
const char *s = http_arg_get(&hc->hc_req_args, "chid");
const char *op = http_arg_get(&hc->hc_req_args, "op");
channel_t *ch = s ? channel_find_by_identifier(atoi(s)) : NULL;
channel_t *ch;
channel_t *ch2;
th_transport_t *t;
int reloadchlist = 0;
htsmsg_t *out, *array, *r;
if(ch == NULL)
pthread_mutex_lock(&global_lock);
ch = s ? channel_find_by_identifier(atoi(s)) : NULL;
if(ch == NULL) {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_BAD_REQUEST;
}
if(!strcmp(op, "load")) {
r = htsmsg_create();
@ -611,13 +632,17 @@ extjs_channel(http_connection_t *hc, http_reply_t *hr,
htsmsg_add_u32(out, "success", 1);
} else {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_BAD_REQUEST;
}
response:
pthread_mutex_unlock(&global_lock);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}

View file

@ -84,10 +84,10 @@ eventcmp(const void *A, const void *B)
* on if it is a full blown browser or just some mobile app
*/
static int
page_simple(http_connection_t *hc, http_reply_t *hr,
page_simple(http_connection_t *hc,
const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
const char *s = http_arg_get(&hc->hc_req_args, "s");
struct event_list events;
event_t *e, **ev;
@ -217,7 +217,7 @@ page_simple(http_connection_t *hc, http_reply_t *hr,
}
htsbuf_qprintf(hq, "</body></html>");
http_output_html(hc, hr);
http_output_html(hc);
return 0;
}
@ -225,10 +225,9 @@ page_simple(http_connection_t *hc, http_reply_t *hr,
* Event info, deliver info about the given event
*/
static int
page_einfo(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
page_einfo(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
event_t *e;
struct tm a, b;
time_t stop;
@ -299,7 +298,7 @@ page_einfo(http_connection_t *hc, http_reply_t *hr,
htsbuf_qprintf(hq, "<hr><a href=\"/simple.html\">New search</a><br>");
htsbuf_qprintf(hq, "</body></html>");
http_output_html(hc, hr);
http_output_html(hc);
return 0;
}
@ -308,10 +307,9 @@ page_einfo(http_connection_t *hc, http_reply_t *hr,
* PVR info, deliver info about the given PVR entry
*/
static int
page_pvrinfo(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
page_pvrinfo(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
htsbuf_queue_t *hq = &hc->hc_reply;
struct tm a, b;
time_t stop;
pvr_rec_t *pvrr = NULL;
@ -324,7 +322,7 @@ page_pvrinfo(http_connection_t *hc, http_reply_t *hr,
if((http_arg_get(&hc->hc_req_args, "clear")) != NULL) {
pvr_clear(pvrr);
pvrr = NULL;
http_redirect(hc, hr, "/simple.html");
http_redirect(hc, "/simple.html");
return 0;
} else if((http_arg_get(&hc->hc_req_args, "cancel")) != NULL) {
pvr_abort(pvrr);
@ -380,7 +378,7 @@ page_pvrinfo(http_connection_t *hc, http_reply_t *hr,
htsbuf_qprintf(hq, "<hr><a href=\"/simple.html\">New search</a><br>");
htsbuf_qprintf(hq, "</body></html>");
http_output_html(hc, hr);
http_output_html(hc);
return 0;
}

View file

@ -54,13 +54,12 @@ is_client_simple(http_connection_t *hc)
* on if it is a full blown browser or just some mobile app
*/
static int
page_root(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
page_root(http_connection_t *hc, const char *remain, void *opaque)
{
if(is_client_simple(hc)) {
http_redirect(hc, hr, "/simple.html");
http_redirect(hc, "/simple.html");
} else {
http_redirect(hc, hr, "/extjs.html");
http_redirect(hc, "/extjs.html");
}
return 0;
}
@ -70,15 +69,13 @@ page_root(http_connection_t *hc, http_reply_t *hr,
* on if it is a full blown browser or just some mobile app
*/
static int
page_static(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
page_static(http_connection_t *hc, const char *remain, void *opaque)
{
int fd, r;
const char *rootpath = HTS_BUILD_ROOT "/tvheadend/webui/static";
char path[500];
struct stat st;
void *buf;
htsbuf_queue_t *hq = &hr->hr_q;
const char *content = NULL, *postfix;
if(strstr(remain, ".."))
@ -111,8 +108,8 @@ page_static(http_connection_t *hc, http_reply_t *hr,
content = "text/javascript; charset=UTF-8";
}
htsbuf_append_prealloc(hq, buf, st.st_size);
http_output(hc, hr, content, NULL, 0);
htsbuf_append_prealloc(&hc->hc_reply, buf, st.st_size);
http_output_content(hc, content);
return 0;
}
@ -121,13 +118,13 @@ page_static(http_connection_t *hc, http_reply_t *hr,
* WEB user interface
*/
void
webui_start(void)
webui_init(void)
{
http_path_add("/", NULL, page_root, ACCESS_WEB_INTERFACE);
http_path_add("/static", NULL, page_static, ACCESS_WEB_INTERFACE);
simpleui_start();
// simpleui_start();
extjs_start();
comet_init();

View file

@ -21,15 +21,21 @@
#include <libhts/htsmsg.h>
void webui_start(void);
void webui_init(void);
void simpleui_start(void);
void extjs_start(void);
/**
*
*/
void comet_init(void);
void comet_mailbox_add_message(htsmsg_t *m);
void comet_flush(void);
#endif /* WEBUI_H_ */