diff --git a/Makefile b/Makefile index f3699845..a6a4797f 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/buffer.c b/buffer.c index 2188726c..7ef28b02 100644 --- a/buffer.c +++ b/buffer.c @@ -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); } } diff --git a/buffer.h b/buffer.h index 675e3827..2492d798 100644 --- a/buffer.h +++ b/buffer.h @@ -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); diff --git a/channels.c b/channels.c index 4823ce62..db89b126 100644 --- a/channels.c +++ b/channels.c @@ -29,12 +29,9 @@ #include #include -#include #include #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); diff --git a/cwc.h b/cwc.h index d915ca6c..19678ec2 100644 --- a/cwc.h +++ b/cwc.h @@ -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; diff --git a/dispatch.c b/dispatch.c index b70b0776..e69de29b 100644 --- a/dispatch.c +++ b/dispatch.c @@ -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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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); -} diff --git a/dispatch.h b/dispatch.h index c8a02f0e..e69de29b 100644 --- a/dispatch.h +++ b/dispatch.h @@ -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 . - */ - -#ifndef DISPATCH_H -#define DISPATCH_H - -#include -#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 */ diff --git a/dvb/dvb.c b/dvb/dvb.c index 4c4cdd7e..824672a6 100644 --- a/dvb/dvb.c +++ b/dvb/dvb.c @@ -16,25 +16,10 @@ * along with this program. If not, see . */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "tvhead.h" #include "dvb.h" - void dvb_init(void) { diff --git a/dvb/dvb.h b/dvb/dvb.h index 6f42c393..2c657cac 100644 --- a/dvb/dvb.h +++ b/dvb/dvb.h @@ -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_ */ diff --git a/dvb/dvb_adapter.c b/dvb/dvb_adapter.c index d418c3f4..b0a4f992 100644 --- a/dvb/dvb_adapter.c +++ b/dvb/dvb_adapter.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -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); + } +} diff --git a/dvb/dvb_fe.c b/dvb/dvb_fe.c index 858fd7f2..ba90f291 100644 --- a/dvb/dvb_fe.c +++ b/dvb/dvb_fe.c @@ -21,8 +21,8 @@ #include #include #include -#include #include +#include #include #include @@ -32,7 +32,6 @@ #include #include -#include #include #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); } diff --git a/dvb/dvb_multiplex.c b/dvb/dvb_multiplex.c index 90cd2399..cd2f11b1 100644 --- a/dvb/dvb_multiplex.c +++ b/dvb/dvb_multiplex.c @@ -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; diff --git a/dvb/dvb_support.c b/dvb/dvb_support.c index 199403a9..571fbf42 100644 --- a/dvb/dvb_support.c +++ b/dvb/dvb_support.c @@ -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; -} - - /** * */ diff --git a/dvb/dvb_tables.c b/dvb/dvb_tables.c index 951bc5c5..bcff69a3 100644 --- a/dvb/dvb_tables.c +++ b/dvb/dvb_tables.c @@ -21,9 +21,9 @@ #include #include #include +#include #include #include - #include #include #include @@ -32,9 +32,7 @@ #include #include -#include #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); + } diff --git a/dvb/dvb_transport.c b/dvb/dvb_transport.c index 04741fdf..d1e42870 100644 --- a/dvb/dvb_transport.c +++ b/dvb/dvb_transport.c @@ -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; diff --git a/http.c b/http.c index 4f5c0afb..2f35668d 100644 --- a/http.c +++ b/http.c @@ -32,14 +32,10 @@ #include #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, - "\r\n" - "\r\n" - "%d %s\r\n" - "\r\n" - "

%d %s

\r\n" - "\r\n", - error, errtxt, error, errtxt); + htsbuf_qprintf(&hc->hc_reply, + "\r\n" + "\r\n" + "%d %s\r\n" + "\r\n" + "

%d %s

\r\n" + "\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, - "\r\n" - "\r\n" - "Redirect\r\n" - "\r\n" - "Please follow %s\r\n" - "\r\n", - location, location); + htsbuf_qprintf(&hc->hc_reply, + "\r\n" + "\r\n" + "Redirect\r\n" + "\r\n" + "Please follow %s\r\n" + "\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); } diff --git a/http.h b/http.h index b6be9ee9..37da6dbc 100644 --- a/http.h +++ b/http.h @@ -19,9 +19,15 @@ #ifndef HTTP_H_ #define HTTP_H_ -extern int http_port; +#include -#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_ */ diff --git a/main.c b/main.c index 2378b150..bbe60678 100644 --- a/main.c +++ b/main.c @@ -37,65 +37,23 @@ #include #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 #include 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(>imers, 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(>imers)) != 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); } diff --git a/mux.c b/mux.c index 82862dc7..52f16260 100644 --- a/mux.c +++ b/mux.c @@ -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) diff --git a/notify.c b/notify.c index ca4660da..cbd090c7 100644 --- a/notify.c +++ b/notify.c @@ -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 } diff --git a/parsers.c b/parsers.c index bb33c4c5..2caa2777 100644 --- a/parsers.c +++ b/parsers.c @@ -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 */ diff --git a/psi.c b/psi.c index c991428a..df6f4ee1 100644 --- a/psi.c +++ b/psi.c @@ -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]; diff --git a/pvr.h b/pvr.h index 864162f2..79a32779 100644 --- a/pvr.h +++ b/pvr.h @@ -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; diff --git a/subscriptions.c b/subscriptions.c index 91bd0bc6..b8b1bbe2 100644 --- a/subscriptions.c +++ b/subscriptions.c @@ -31,31 +31,31 @@ #include #include -#include -#include - -#include - #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); + } diff --git a/tcp.c b/tcp.c index e4174525..5a2089df 100644 --- a/tcp.c +++ b/tcp.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -29,8 +30,11 @@ #include #include -#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); +} + + diff --git a/tcp.h b/tcp.h index c47008f5..a7447907 100644 --- a/tcp.h +++ b/tcp.h @@ -21,6 +21,9 @@ #include +#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_ */ diff --git a/transports.c b/transports.c index af2f60d5..dbf5d545 100644 --- a/transports.c +++ b/transports.c @@ -31,20 +31,13 @@ #include #include -#include -#include - -#include - #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); } diff --git a/transports.h b/transports.h index 807e840d..4eaabdcf 100644 --- a/transports.h +++ b/transports.h @@ -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 */ diff --git a/tsdemux.c b/tsdemux.c index e42916e2..bc0a7fb7 100644 --- a/tsdemux.c +++ b/tsdemux.c @@ -35,8 +35,6 @@ #include #include -#include - #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: diff --git a/tvhead.h b/tvhead.h index 51310f60..d98fd7a5 100644 --- a/tvhead.h +++ b/tvhead.h @@ -20,16 +20,34 @@ #define TV_HEAD_H #include +#include +#include +#include #include +#include #include #include -#include #include #include #include #include #include +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 */ diff --git a/webui/comet.c b/webui/comet.c index 20b4febf..2ae016f3 100644 --- a/webui/comet.c +++ b/webui/comet.c @@ -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); } diff --git a/webui/extjs.c b/webui/extjs.c index a250e0fc..a49fe9f1 100644 --- a/webui/extjs.c +++ b/webui/extjs.c @@ -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, "
\n" "\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; } diff --git a/webui/simpleui.c b/webui/simpleui.c index 27684565..250882ab 100644 --- a/webui/simpleui.c +++ b/webui/simpleui.c @@ -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, ""); - 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, "
New search
"); htsbuf_qprintf(hq, ""); - 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, "
New search
"); htsbuf_qprintf(hq, ""); - http_output_html(hc, hr); + http_output_html(hc); return 0; } diff --git a/webui/webui.c b/webui/webui.c index 13ad173c..8581e46d 100644 --- a/webui/webui.c +++ b/webui/webui.c @@ -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(); diff --git a/webui/webui.h b/webui/webui.h index 65a9def2..ca9ceb97 100644 --- a/webui/webui.h +++ b/webui/webui.h @@ -21,15 +21,21 @@ #include -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_ */