Initial take on making tvheadend multithreaded instead of relying on a
poll/dispatcher framework. The top reasons for going multithreaded is: * Take adventage of multicore CPUs * No need to put slow operations in ugly helper threads now that the entire app is designed for multithreads * Some of the timing sensetive tasks (IPTV output / RTP / UDP pacing) can run on RT priority.
This commit is contained in:
parent
a18df3bc78
commit
cb8266e3ea
35 changed files with 1697 additions and 1892 deletions
36
Makefile
36
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
|
||||
|
|
34
buffer.c
34
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
8
buffer.h
8
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);
|
||||
|
||||
|
|
32
channels.c
32
channels.c
|
@ -29,12 +29,9 @@
|
|||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
#include <libhts/htssettings.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "v4l.h"
|
||||
#include "iptv_input.h"
|
||||
#include "psi.h"
|
||||
#include "channels.h"
|
||||
#include "transports.h"
|
||||
|
@ -167,6 +164,9 @@ channel_t *
|
|||
channel_find_by_name(const char *name, int create)
|
||||
{
|
||||
channel_t skel, *ch;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
skel.ch_name = (char *)name;
|
||||
ch = RB_FIND(&channel_name_tree, &skel, ch_name_link, channelcmp);
|
||||
if(ch != NULL || create == 0)
|
||||
|
@ -182,6 +182,9 @@ channel_t *
|
|||
channel_find_by_identifier(int id)
|
||||
{
|
||||
channel_t skel, *ch;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
skel.ch_id = id;
|
||||
ch = RB_FIND(&channel_identifier_tree, &skel, ch_identifier_link, chidcmp);
|
||||
return ch;
|
||||
|
@ -214,6 +217,9 @@ static void
|
|||
channel_save(channel_t *ch)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(ch->ch_icon != NULL)
|
||||
htsmsg_add_str(m, "icon", ch->ch_icon);
|
||||
|
||||
|
@ -232,6 +238,8 @@ channel_rename(channel_t *ch, const char *newname)
|
|||
{
|
||||
th_transport_t *t;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(channel_find_by_name(newname, 0))
|
||||
return -1;
|
||||
|
||||
|
@ -262,13 +270,15 @@ channel_delete(channel_t *ch)
|
|||
th_transport_t *t;
|
||||
th_subscription_t *s;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" deleted",
|
||||
ch->ch_name);
|
||||
|
||||
pvr_destroy_by_channel(ch);
|
||||
abort();//pvr_destroy_by_channel(ch);
|
||||
|
||||
while((t = LIST_FIRST(&ch->ch_transports)) != NULL) {
|
||||
transport_unmap_channel(t);
|
||||
abort();//transport_unmap_channel(t);
|
||||
t->tht_config_change(t);
|
||||
}
|
||||
|
||||
|
@ -277,9 +287,9 @@ channel_delete(channel_t *ch)
|
|||
s->ths_channel = NULL;
|
||||
}
|
||||
|
||||
epg_destroy_by_channel(ch);
|
||||
abort();//epg_destroy_by_channel(ch);
|
||||
|
||||
autorec_destroy_by_channel(ch);
|
||||
abort();//autorec_destroy_by_channel(ch);
|
||||
|
||||
hts_settings_remove("channels/%s", ch->ch_name);
|
||||
|
||||
|
@ -304,14 +314,16 @@ void
|
|||
channel_merge(channel_t *dst, channel_t *src)
|
||||
{
|
||||
th_transport_t *t;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" merged into \"%s\"",
|
||||
src->ch_name, dst->ch_name);
|
||||
|
||||
while((t = LIST_FIRST(&src->ch_transports)) != NULL) {
|
||||
transport_unmap_channel(t);
|
||||
abort();//transport_unmap_channel(t);
|
||||
|
||||
transport_map_channel(t, dst);
|
||||
abort();//transport_map_channel(t, dst);
|
||||
t->tht_config_change(t);
|
||||
}
|
||||
|
||||
|
@ -324,6 +336,8 @@ channel_merge(channel_t *dst, channel_t *src)
|
|||
void
|
||||
channel_set_icon(channel_t *ch, const char *icon)
|
||||
{
|
||||
lock_assert(&global_lock);
|
||||
|
||||
free(ch->ch_icon);
|
||||
ch->ch_icon = icon ? strdup(icon) : NULL;
|
||||
channel_save(ch);
|
||||
|
|
6
cwc.h
6
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;
|
||||
|
||||
|
|
251
dispatch.c
251
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <libhts/htsq.h>
|
||||
|
||||
#include "dispatch.h"
|
||||
|
||||
#define EPOLL_FDS_PER_ROUND 100
|
||||
time_t dispatch_clock;
|
||||
|
||||
static int epoll_fd;
|
||||
|
||||
typedef struct epoll_entry {
|
||||
void (*callback)(int events, void *opaque, int fd);
|
||||
void *opaque;
|
||||
struct epoll_event event;
|
||||
int fd;
|
||||
int refcnt;
|
||||
} epoll_entry_t;
|
||||
|
||||
LIST_HEAD(, dtimer) dispatch_timers;
|
||||
|
||||
static int
|
||||
stimercmp(dtimer_t *a, dtimer_t *b)
|
||||
{
|
||||
if(a->dti_expire < b->dti_expire)
|
||||
return -1;
|
||||
else if(a->dti_expire > b->dti_expire)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
dtimer_arm_hires(dtimer_t *ti, dti_callback_t *callback, void *aux, int64_t t)
|
||||
{
|
||||
if(ti->dti_callback != NULL)
|
||||
LIST_REMOVE(ti, dti_link);
|
||||
ti->dti_expire = t;
|
||||
ti->dti_opaque = aux;
|
||||
ti->dti_callback = callback;
|
||||
LIST_INSERT_SORTED(&dispatch_timers, ti, dti_link, stimercmp);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
dtimer_arm(dtimer_t *ti, dti_callback_t *callback, void *opaque, int delta)
|
||||
{
|
||||
time_t now;
|
||||
time(&now);
|
||||
dtimer_arm_hires(ti, callback, opaque,
|
||||
(uint64_t)(now + delta) * 1000000ULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
stimer_next(void)
|
||||
{
|
||||
dtimer_t *ti = LIST_FIRST(&dispatch_timers);
|
||||
int delta;
|
||||
|
||||
if(ti == NULL)
|
||||
return -1;
|
||||
|
||||
delta = (ti->dti_expire - getclock_hires() + 999) / 1000;
|
||||
|
||||
return delta > 0 ? delta : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
stimer_dispatch(int64_t now)
|
||||
{
|
||||
dtimer_t *ti;
|
||||
dti_callback_t *cb;
|
||||
|
||||
while((ti = LIST_FIRST(&dispatch_timers)) != NULL) {
|
||||
if(ti->dti_expire > now + 100ULL)
|
||||
break;
|
||||
LIST_REMOVE(ti, dti_link);
|
||||
cb = ti->dti_callback;
|
||||
ti->dti_callback = NULL;
|
||||
cb(ti->dti_opaque, now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
dispatch_init(void)
|
||||
{
|
||||
epoll_fd = epoll_create(100);
|
||||
if(epoll_fd == -1) {
|
||||
perror("epoll_create()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *
|
||||
dispatch_addfd(int fd, void (*callback)(int events, void *opaque, int fd),
|
||||
void *opaque, int flags)
|
||||
{
|
||||
struct epoll_entry *e;
|
||||
|
||||
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
|
||||
|
||||
e = calloc(1, sizeof(struct epoll_entry));
|
||||
e->callback = callback;
|
||||
e->opaque = opaque;
|
||||
e->fd = fd;
|
||||
e->event.events = EPOLLERR | EPOLLHUP;
|
||||
e->event.data.ptr = e;
|
||||
e->refcnt = 1;
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, e->fd, &e->event);
|
||||
dispatch_set(e, flags);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
dispatch_set(void *handle, int flags)
|
||||
{
|
||||
struct epoll_entry *e = handle;
|
||||
|
||||
if(flags & DISPATCH_PRI)
|
||||
e->event.events |= EPOLLPRI;
|
||||
|
||||
if(flags & DISPATCH_READ)
|
||||
e->event.events |= EPOLLIN;
|
||||
|
||||
if(flags & DISPATCH_WRITE)
|
||||
e->event.events |= EPOLLOUT;
|
||||
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_MOD, e->fd, &e->event);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
dispatch_clr(void *handle, int flags)
|
||||
{
|
||||
struct epoll_entry *e = handle;
|
||||
|
||||
if(flags & DISPATCH_PRI)
|
||||
e->event.events &= ~EPOLLPRI;
|
||||
|
||||
if(flags & DISPATCH_READ)
|
||||
e->event.events &= ~EPOLLIN;
|
||||
|
||||
if(flags & DISPATCH_WRITE)
|
||||
e->event.events &= ~EPOLLOUT;
|
||||
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_MOD, e->fd, &e->event);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_deref(struct epoll_entry *e)
|
||||
{
|
||||
if(e->refcnt > 1)
|
||||
e->refcnt--;
|
||||
else
|
||||
free(e);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
dispatch_delfd(void *handle)
|
||||
{
|
||||
struct epoll_entry *e = handle;
|
||||
int fd = e->fd;
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &e->event);
|
||||
|
||||
e->callback = NULL;
|
||||
e->opaque = NULL;
|
||||
dispatch_deref(e);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
dispatcher(void)
|
||||
{
|
||||
struct epoll_entry *e;
|
||||
int i, n, delta;
|
||||
struct timeval tv;
|
||||
int64_t now;
|
||||
|
||||
static struct epoll_event events[EPOLL_FDS_PER_ROUND];
|
||||
|
||||
delta = stimer_next();
|
||||
n = epoll_wait(epoll_fd, events, EPOLL_FDS_PER_ROUND, delta);
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
dispatch_clock = tv.tv_sec;
|
||||
now = (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
|
||||
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
e = events[i].data.ptr;
|
||||
e->refcnt++;
|
||||
}
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
e = events[i].data.ptr;
|
||||
if(e->callback == NULL)
|
||||
continue; /* poll entry has been free'd during loop */
|
||||
|
||||
e->callback(((events[i].events & (EPOLLERR | EPOLLHUP)) ?
|
||||
DISPATCH_ERR : 0 ) |
|
||||
((events[i].events & EPOLLIN) ? DISPATCH_READ : 0 ) |
|
||||
((events[i].events & EPOLLOUT) ? DISPATCH_WRITE : 0 ) |
|
||||
((events[i].events & EPOLLPRI) ? DISPATCH_PRI : 0 ),
|
||||
e->opaque, e->fd);
|
||||
}
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
e = events[i].data.ptr;
|
||||
dispatch_deref(e);
|
||||
}
|
||||
|
||||
stimer_dispatch(now);
|
||||
}
|
68
dispatch.h
68
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DISPATCH_H
|
||||
#define DISPATCH_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include "tvhead.h"
|
||||
|
||||
extern time_t dispatch_clock;
|
||||
|
||||
#define DISPATCH_READ 0x1
|
||||
#define DISPATCH_WRITE 0x2
|
||||
#define DISPATCH_ERR 0x4
|
||||
#define DISPATCH_PRI 0x8
|
||||
|
||||
static inline int64_t
|
||||
getclock_hires(void)
|
||||
{
|
||||
int64_t now;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
now = (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
|
||||
return now;
|
||||
}
|
||||
|
||||
int dispatch_init(void);
|
||||
void *dispatch_addfd(int fd,
|
||||
void (*callback)(int events, void *opaque, int fd),
|
||||
void *opaque, int flags);
|
||||
int dispatch_delfd(void *handle);
|
||||
void dispatch_set(void *handle, int flags);
|
||||
void dispatch_clr(void *handle, int flags);
|
||||
void dispatcher(void);
|
||||
|
||||
void dtimer_arm(dtimer_t *ti, dti_callback_t *callback, void *aux, int delta);
|
||||
void dtimer_arm_hires(dtimer_t *ti, dti_callback_t *callback,
|
||||
void *aux, int64_t t);
|
||||
|
||||
|
||||
static inline void
|
||||
dtimer_disarm(dtimer_t *ti)
|
||||
{
|
||||
if(ti->dti_callback) {
|
||||
LIST_REMOVE(ti, dti_link);
|
||||
ti->dti_callback = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define dtimer_isarmed(ti) ((ti)->dti_callback ? 1 : 0)
|
||||
|
||||
#endif /* DISPATCH_H */
|
15
dvb/dvb.c
15
dvb/dvb.c
|
@ -16,25 +16,10 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dvb.h"
|
||||
|
||||
|
||||
void
|
||||
dvb_init(void)
|
||||
{
|
||||
|
|
21
dvb/dvb.h
21
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_ */
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
|
@ -38,20 +37,17 @@
|
|||
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
#include "dvb.h"
|
||||
#include "channels.h"
|
||||
#include "transports.h"
|
||||
#include "subscriptions.h"
|
||||
#include "teletext.h"
|
||||
#include "epg.h"
|
||||
#include "psi.h"
|
||||
#include "dvb.h"
|
||||
#include "dvb_support.h"
|
||||
#include "dvb_dvr.h"
|
||||
#include "tsdemux.h"
|
||||
#include "notify.h"
|
||||
|
||||
struct th_dvb_adapter_queue dvb_adapters;
|
||||
struct th_dvb_mux_instance_tree dvb_muxes;
|
||||
static void dvb_fec_monitor(void *aux, int64_t now);
|
||||
//static void dvb_fec_monitor(void *aux, int64_t now);
|
||||
static void *dvb_adapter_input_dvr(void *aux);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -61,9 +57,7 @@ static th_dvb_adapter_t *
|
|||
tda_alloc(void)
|
||||
{
|
||||
th_dvb_adapter_t *tda = calloc(1, sizeof(th_dvb_adapter_t));
|
||||
pthread_mutex_init(&tda->tda_lock, NULL);
|
||||
pthread_cond_init(&tda->tda_cond, NULL);
|
||||
TAILQ_INIT(&tda->tda_fe_cmd_queue);
|
||||
pthread_mutex_init(&tda->tda_delivery_mutex, NULL);
|
||||
return tda;
|
||||
}
|
||||
|
||||
|
@ -76,6 +70,8 @@ tda_save(th_dvb_adapter_t *tda)
|
|||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type));
|
||||
htsmsg_add_str(m, "displayname", tda->tda_displayname);
|
||||
htsmsg_add_u32(m, "autodiscovery", tda->tda_autodiscovery);
|
||||
|
@ -92,6 +88,8 @@ dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s)
|
|||
{
|
||||
htsmsg_t *m;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(!strcmp(s, tda->tda_displayname))
|
||||
return;
|
||||
|
||||
|
@ -121,6 +119,8 @@ dvb_adapter_set_auto_discovery(th_dvb_adapter_t *tda, int on)
|
|||
if(tda->tda_autodiscovery == on)
|
||||
return;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" mux autodiscovery set to %s",
|
||||
tda->tda_displayname, on ? "On" : "Off");
|
||||
|
||||
|
@ -139,6 +139,7 @@ tda_add(const char *path)
|
|||
int fe, i, r;
|
||||
th_dvb_adapter_t *tda;
|
||||
char buf[400];
|
||||
pthread_t ptid;
|
||||
|
||||
snprintf(fname, sizeof(fname), "%s/frontend0", path);
|
||||
|
||||
|
@ -172,12 +173,6 @@ tda_add(const char *path)
|
|||
|
||||
tda->tda_type = tda->tda_fe_info->type;
|
||||
|
||||
if(dvb_dvr_init(tda) < 0) {
|
||||
close(fe);
|
||||
free(tda);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath,
|
||||
tda->tda_fe_info->name);
|
||||
|
||||
|
@ -199,8 +194,8 @@ tda_add(const char *path)
|
|||
"Found adapter %s (%s)", path, tda->tda_fe_info->name);
|
||||
|
||||
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
|
||||
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
|
||||
dvb_fe_start(tda);
|
||||
|
||||
pthread_create(&ptid, NULL, dvb_adapter_input_dvr, tda);
|
||||
}
|
||||
|
||||
|
||||
|
@ -261,34 +256,6 @@ dvb_adapter_init(void)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_notify_mux_quality(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
|
||||
|
||||
htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
|
||||
notify_by_msg("dvbmux", m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_notify_mux_status(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
|
||||
|
||||
htsmsg_add_str(m, "status", tdmi->tdmi_last_status);
|
||||
notify_by_msg("dvbmux", m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -303,6 +270,9 @@ dvb_adapter_notify_reload(th_dvb_adapter_t *tda)
|
|||
}
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -371,6 +341,7 @@ dvb_fec_monitor(void *aux, int64_t now)
|
|||
if(savemux)
|
||||
dvb_mux_save(tdmi);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
|
@ -378,12 +349,12 @@ dvb_fec_monitor(void *aux, int64_t now)
|
|||
* and EIT updates
|
||||
*/
|
||||
void
|
||||
dvb_adapter_mux_scanner(void *aux, int64_t now)
|
||||
dvb_adapter_mux_scanner(void *aux)
|
||||
{
|
||||
th_dvb_adapter_t *tda = aux;
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
|
||||
dtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 10);
|
||||
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 10);
|
||||
|
||||
if(transport_compute_weight(&tda->tda_transports) > 0)
|
||||
return; /* someone is here */
|
||||
|
@ -393,7 +364,7 @@ dvb_adapter_mux_scanner(void *aux, int64_t now)
|
|||
if((tdmi = RB_FIRST(&tda->tda_muxes_qscan_waiting)) != NULL) {
|
||||
RB_REMOVE(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link);
|
||||
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_RUNNING;
|
||||
dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN);
|
||||
dvb_fe_tune(tdmi);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -409,9 +380,10 @@ dvb_adapter_mux_scanner(void *aux, int64_t now)
|
|||
if(tdmi == NULL)
|
||||
return; /* no instances */
|
||||
|
||||
dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN);
|
||||
dvb_fe_tune(tdmi);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -422,6 +394,8 @@ dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
|
|||
th_transport_t *t_src, *t_dst;
|
||||
th_stream_t *st_src, *st_dst;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
while((tdmi_dst = RB_FIRST(&dst->tda_muxes)) != NULL)
|
||||
dvb_mux_destroy(tdmi_dst);
|
||||
|
||||
|
@ -489,15 +463,12 @@ dvb_adapter_destroy(th_dvb_adapter_t *tda)
|
|||
{
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
|
||||
char buf[400];
|
||||
|
||||
if(tda->tda_rootpath != NULL)
|
||||
return -1;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/dvbadapters/%s",
|
||||
settings_dir, tda->tda_identifier);
|
||||
lock_assert(&global_lock);
|
||||
|
||||
unlink(buf);
|
||||
hts_settings_remove("dvbadapters/%s", tda->tda_identifier);
|
||||
|
||||
while((tdmi = RB_FIRST(&tda->tda_muxes)) != NULL)
|
||||
dvb_mux_destroy(tdmi);
|
||||
|
@ -512,3 +483,50 @@ dvb_adapter_destroy(th_dvb_adapter_t *tda)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_adapter_clean(th_dvb_adapter_t *tda)
|
||||
{
|
||||
th_transport_t *t;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
while((t = LIST_FIRST(&tda->tda_transports)) != NULL)
|
||||
transport_remove_subscriber(t, NULL); /* Flushes all subscribers */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void *
|
||||
dvb_adapter_input_dvr(void *aux)
|
||||
{
|
||||
th_dvb_adapter_t *tda = aux;
|
||||
int fd, i, r;
|
||||
uint8_t tsb[188 * 10];
|
||||
th_transport_t *t;
|
||||
|
||||
fd = open(tda->tda_dvr_path, O_RDONLY);
|
||||
if(fd == -1) {
|
||||
tvhlog(LOG_ALERT, "dvb", "%s: unable to open dvr", tda->tda_dvr_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
r = read(fd, tsb, sizeof(tsb));
|
||||
|
||||
pthread_mutex_lock(&tda->tda_delivery_mutex);
|
||||
|
||||
for(i = 0; i < r; i += 188)
|
||||
LIST_FOREACH(t, &tda->tda_transports, tht_active_link)
|
||||
if(t->tht_dvb_mux_instance == tda->tda_mux_current)
|
||||
ts_recv_packet1(t, tsb + i);
|
||||
|
||||
pthread_mutex_unlock(&tda->tda_delivery_mutex);
|
||||
}
|
||||
}
|
||||
|
|
238
dvb/dvb_fe.c
238
dvb/dvb_fe.c
|
@ -21,8 +21,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
@ -32,7 +32,6 @@
|
|||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
|
@ -42,6 +41,8 @@
|
|||
#include "diseqc.h"
|
||||
#include "notify.h"
|
||||
|
||||
#if 0
|
||||
|
||||
typedef struct dvb_fe_cmd {
|
||||
TAILQ_ENTRY(dvb_fe_cmd) link;
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
|
@ -189,6 +190,7 @@ dvb_fe_manager(void *aux)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Startup the FE management thread
|
||||
*/
|
||||
|
@ -199,92 +201,204 @@ dvb_fe_start(th_dvb_adapter_t *tda)
|
|||
pthread_create(&ptid, NULL, dvb_fe_manager, tda);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
#if 0
|
||||
static void
|
||||
dvb_notify_mux_quality(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
|
||||
|
||||
htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
|
||||
notify_by_msg("dvbmux", m);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_notify_mux_status(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
|
||||
|
||||
htsmsg_add_str(m, "status", dvb_mux_status(tdmi));
|
||||
notify_by_msg("dvbmux", m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Front end monitor
|
||||
*
|
||||
* Monitor status every second
|
||||
*/
|
||||
static void
|
||||
dvb_fe_monitor(void *aux)
|
||||
{
|
||||
th_dvb_adapter_t *tda = aux;
|
||||
fe_status_t fe_status;
|
||||
int status, v, savemux = 0, vv, i, fec;
|
||||
th_dvb_mux_instance_t *tdmi = tda->tda_mux_current;
|
||||
|
||||
assert(tdmi != NULL);
|
||||
|
||||
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
|
||||
|
||||
/**
|
||||
* Read out front end status
|
||||
*/
|
||||
ioctl(tda->tda_fe_fd, FE_READ_STATUS, &fe_status);
|
||||
|
||||
if(fe_status & FE_HAS_LOCK)
|
||||
status = -1;
|
||||
else if(fe_status & (FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_CARRIER))
|
||||
status = TDMI_FE_BAD_SIGNAL;
|
||||
else if(fe_status & FE_HAS_SIGNAL)
|
||||
status = TDMI_FE_FAINT_SIGNAL;
|
||||
else
|
||||
status = TDMI_FE_NO_SIGNAL;
|
||||
|
||||
if(tdmi->tdmi_fe_status == TDMI_FE_UNKNOWN) {
|
||||
/* Previously unknown */
|
||||
|
||||
if(status == -1) /* We have a lock, don't hold off */
|
||||
tda->tda_fe_monitor_hold = 0;
|
||||
|
||||
if(tda->tda_fe_monitor_hold > 0) {
|
||||
tda->tda_fe_monitor_hold--;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset FEC counter */
|
||||
ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &fec);
|
||||
}
|
||||
|
||||
if(status == -1) {
|
||||
/* Read FEC counter (delta) */
|
||||
ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &fec);
|
||||
|
||||
tdmi->tdmi_fec_err_histogram[tdmi->tdmi_fec_err_ptr++] = fec;
|
||||
if(tdmi->tdmi_fec_err_ptr == TDMI_FEC_ERR_HISTOGRAM_SIZE)
|
||||
tdmi->tdmi_fec_err_ptr = 0;
|
||||
|
||||
v = vv = 0;
|
||||
for(i = 0; i < TDMI_FEC_ERR_HISTOGRAM_SIZE; i++) {
|
||||
if(tdmi->tdmi_fec_err_histogram[i] > DVB_FEC_ERROR_LIMIT)
|
||||
v++;
|
||||
vv += tdmi->tdmi_fec_err_histogram[i];
|
||||
}
|
||||
vv = vv / TDMI_FEC_ERR_HISTOGRAM_SIZE;
|
||||
|
||||
if(v == 0) {
|
||||
status = TDMI_FE_OK;
|
||||
} else if(v == 1) {
|
||||
status = TDMI_FE_BURSTY_FEC;
|
||||
} else {
|
||||
status = TDMI_FE_CONTANT_FEC;
|
||||
}
|
||||
}
|
||||
|
||||
if(status != tdmi->tdmi_fe_status) {
|
||||
tdmi->tdmi_fe_status = status;
|
||||
dvb_notify_mux_status(tdmi);
|
||||
savemux = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(savemux)
|
||||
dvb_mux_save(tdmi);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Stop the given TDMI
|
||||
*/
|
||||
void
|
||||
tdmi_stop(th_dvb_mux_instance_t *tdmi)
|
||||
dvb_fe_stop(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
th_dvb_table_t *tdt;
|
||||
|
||||
tdmi->tdmi_adapter->tda_mux_current = NULL;
|
||||
|
||||
pthread_mutex_lock(&tdmi->tdmi_table_lock);
|
||||
dvb_table_flush_all(tdmi);
|
||||
|
||||
while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL)
|
||||
dvb_tdt_destroy(tdt);
|
||||
|
||||
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
|
||||
|
||||
tdmi->tdmi_state = TDMI_IDLE;
|
||||
// notify_tdmi_state_change(tdmi);
|
||||
time(&tdmi->tdmi_lost_adapter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tune an adapter to a mux instance (but only if needed)
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state)
|
||||
dvb_fe_tune(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
dvb_fe_cmd_t *c;
|
||||
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
|
||||
char buf[100];
|
||||
struct dvb_frontend_parameters p = tdmi->tdmi_fe_params;
|
||||
char buf[256];
|
||||
int r;
|
||||
|
||||
if(tdmi->tdmi_state != state) {
|
||||
tdmi->tdmi_state = state;
|
||||
// notify_tdmi_state_change(tdmi);
|
||||
}
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(tda->tda_mux_current == tdmi)
|
||||
return;
|
||||
|
||||
|
||||
if(tda->tda_mux_current != NULL)
|
||||
tdmi_stop(tda->tda_mux_current);
|
||||
dvb_fe_stop(tda->tda_mux_current);
|
||||
|
||||
|
||||
if(tda->tda_type == FE_QPSK) {
|
||||
|
||||
/* DVB-S */
|
||||
int lowfreq, hifreq, switchfreq, hiband;
|
||||
|
||||
// lowfreq = atoi(config_get_str("lnb_lowfreq", "9750000" ));
|
||||
// hifreq = atoi(config_get_str("lnb_hifreq", "10600000"));
|
||||
// switchfreq = atoi(config_get_str("lnb_switchfreq", "11700000"));
|
||||
|
||||
lowfreq = 9750000;
|
||||
hifreq = 10600000;
|
||||
switchfreq = 11700000;
|
||||
|
||||
hiband = switchfreq && p.frequency > switchfreq;
|
||||
|
||||
diseqc_setup(tda->tda_fe_fd,
|
||||
0, /* switch position */
|
||||
tdmi->tdmi_polarisation == POLARISATION_HORIZONTAL,
|
||||
hiband);
|
||||
|
||||
usleep(50000);
|
||||
|
||||
if(hiband)
|
||||
p.frequency = abs(p.frequency - hifreq);
|
||||
else
|
||||
p.frequency = abs(p.frequency - lowfreq);
|
||||
}
|
||||
|
||||
r = ioctl(tda->tda_fe_fd, FE_SET_FRONTEND, &p);
|
||||
if(r != 0) {
|
||||
dvb_mux_nicename(buf, sizeof(buf), tdmi);
|
||||
tvhlog(LOG_ERR, "dvb", "\"%s\" tuning to \"%s\""
|
||||
" -- Front configuration failed -- %s",
|
||||
tda->tda_rootpath, buf, strerror(errno));
|
||||
}
|
||||
|
||||
tda->tda_mux_current = tdmi;
|
||||
|
||||
if(maylog) {
|
||||
dvb_mux_nicename(buf, sizeof(buf), tdmi);
|
||||
tvhlog(LOG_DEBUG, "dvb",
|
||||
"\"%s\" tuning to mux \"%s\"", tda->tda_rootpath, buf);
|
||||
}
|
||||
/* Add tables which will be activated once the tuning is completed */
|
||||
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
|
||||
|
||||
dvb_table_add_default(tdmi);
|
||||
|
||||
/* Send command to the thread */
|
||||
|
||||
c = malloc(sizeof(dvb_fe_cmd_t));
|
||||
c->tdmi = tdmi;
|
||||
pthread_mutex_lock(&tda->tda_lock);
|
||||
tdmi->tdmi_refcnt++;
|
||||
TAILQ_INSERT_TAIL(&tda->tda_fe_cmd_queue, c, link);
|
||||
pthread_cond_signal(&tda->tda_cond);
|
||||
pthread_mutex_unlock(&tda->tda_lock);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flush pending tuning commands for frontend
|
||||
*
|
||||
* tda_lock must be held
|
||||
*/
|
||||
void
|
||||
dvb_fe_flush(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
dvb_fe_cmd_t *c;
|
||||
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
|
||||
|
||||
TAILQ_FOREACH(c, &tda->tda_fe_cmd_queue, link)
|
||||
if(c->tdmi == tdmi)
|
||||
break;
|
||||
if(c == NULL)
|
||||
return;
|
||||
|
||||
TAILQ_REMOVE(&tda->tda_fe_cmd_queue, c, link);
|
||||
dvb_mux_unref(tdmi);
|
||||
free(c);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
205
dvb/dvb_tables.c
205
dvb/dvb_tables.c
|
@ -21,9 +21,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -32,9 +32,7 @@
|
|||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
#include "dvb.h"
|
||||
#include "dvb_support.h"
|
||||
#include "epg.h"
|
||||
|
@ -43,64 +41,145 @@
|
|||
#include "psi.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define TDT_NOW 0x1
|
||||
#define TDT_QUICKREQ 0x2
|
||||
|
||||
static int tid_tally;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_tdt_destroy(th_dvb_table_t *tdt)
|
||||
{
|
||||
free(tdt->tdt_fparams);
|
||||
LIST_REMOVE(tdt, tdt_link);
|
||||
close(dispatch_delfd(tdt->tdt_handle));
|
||||
free(tdt->tdt_name);
|
||||
free(tdt);
|
||||
}
|
||||
typedef struct th_dvb_table {
|
||||
LIST_ENTRY(th_dvb_table) tdt_link;
|
||||
char *tdt_name;
|
||||
|
||||
void *tdt_opaque;
|
||||
void (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
|
||||
uint8_t tableid, void *opaque);
|
||||
|
||||
int tdt_fd;
|
||||
|
||||
int tdt_quickreq;
|
||||
int tdt_count;
|
||||
int tdt_id;
|
||||
} th_dvb_table_t;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_table_recv(int events, void *opaque, int fd)
|
||||
dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
th_dvb_table_t *tdt = opaque;
|
||||
uint8_t sec[4096], *ptr;
|
||||
int r, len;
|
||||
uint8_t tableid;
|
||||
th_dvb_table_t *tdt;
|
||||
|
||||
if(!(events & DISPATCH_READ))
|
||||
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_NONE)
|
||||
return;
|
||||
|
||||
r = read(fd, sec, sizeof(sec));
|
||||
if(r < 3)
|
||||
return;
|
||||
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) {
|
||||
if(tdt->tdt_quickreq && tdt->tdt_count == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* It seems some hardware (or is it the dvb API?) does not honour the
|
||||
DMX_CHECK_CRC flag, so we check it again */
|
||||
if(tdt != NULL)
|
||||
return; /* Still tables we've not seen */
|
||||
|
||||
if(psi_crc32(sec, r))
|
||||
return;
|
||||
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_NONE;
|
||||
// dvb_adapter_mux_scanner(tdmi->tdmi_adapter, 0);
|
||||
|
||||
r -= 3;
|
||||
tableid = sec[0];
|
||||
len = ((sec[1] & 0x0f) << 8) | sec[2];
|
||||
|
||||
if(len < r)
|
||||
return;
|
||||
|
||||
ptr = &sec[3];
|
||||
len -= 4; /* Strip trailing CRC */
|
||||
|
||||
tdt->tdt_count++;
|
||||
|
||||
tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque);
|
||||
dvb_mux_fastswitch(tdt->tdt_tdmi);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void *
|
||||
dvb_table_input(void *aux)
|
||||
{
|
||||
th_dvb_adapter_t *tda = aux;
|
||||
int r, i, tid, fd, tableid, len;
|
||||
struct epoll_event ev[1];
|
||||
uint8_t sec[4096], *ptr;
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
th_dvb_table_t *tdt;
|
||||
|
||||
while(1) {
|
||||
|
||||
r = epoll_wait(tda->tda_table_epollfd, ev, sizeof(ev) / sizeof(ev[0]), -1);
|
||||
|
||||
for(i = 0; i < r; i++) {
|
||||
|
||||
fd = ev[i].data.u64;
|
||||
tid = ev[i].data.u64 >> 32;
|
||||
|
||||
if(ev[i].events & EPOLLIN) {
|
||||
|
||||
if((r = read(fd, sec, sizeof(sec))) < 3)
|
||||
continue;
|
||||
|
||||
/* It seems some hardware (or is it the dvb API?) does not
|
||||
honour the DMX_CHECK_CRC flag, so we check it again */
|
||||
if(psi_crc32(sec, r))
|
||||
continue;
|
||||
|
||||
r -= 3;
|
||||
tableid = sec[0];
|
||||
len = ((sec[1] & 0x0f) << 8) | sec[2];
|
||||
|
||||
if(len < r)
|
||||
continue;
|
||||
|
||||
ptr = &sec[3];
|
||||
len -= 4; /* Strip trailing CRC */
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
tdmi = tda->tda_mux_current;
|
||||
|
||||
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
|
||||
if(tdt->tdt_id == tid)
|
||||
break;
|
||||
|
||||
if(tdt != NULL) {
|
||||
tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque);
|
||||
dvb_table_fastswitch(tdmi);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "DVB table thread, spurious poll event %x on fd %d\n",
|
||||
ev[i].events, fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_table_init(th_dvb_adapter_t *tda)
|
||||
{
|
||||
pthread_t ptid;
|
||||
tda->tda_table_epollfd = epoll_create(50);
|
||||
pthread_create(&ptid, NULL, dvb_table_input, tda);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_table_t *tdt)
|
||||
{
|
||||
epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL);
|
||||
free(tdt->tdt_name);
|
||||
LIST_REMOVE(tdt, tdt_link);
|
||||
close(tdt->tdt_fd);
|
||||
free(tdt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -116,6 +195,7 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
|
|||
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
|
||||
th_dvb_table_t *tdt;
|
||||
int fd;
|
||||
struct epoll_event e;
|
||||
|
||||
if((fd = open(tda->tda_demux_path, O_RDWR)) == -1)
|
||||
return;
|
||||
|
@ -125,21 +205,18 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
|
|||
tdt->tdt_name = strdup(name);
|
||||
tdt->tdt_callback = callback;
|
||||
tdt->tdt_opaque = opaque;
|
||||
tdt->tdt_tdmi = tdmi;
|
||||
tdt->tdt_handle = dispatch_addfd(fd, dvb_table_recv, tdt, DISPATCH_READ);
|
||||
tdt->tdt_id = ++tid_tally;
|
||||
|
||||
e.events = EPOLLIN;
|
||||
e.data.u64 = ((uint64_t)tdt->tdt_id << 32) | fd;
|
||||
|
||||
epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, fd, &e);
|
||||
|
||||
tdt->tdt_quickreq = flags & TDT_QUICKREQ ? 1 : 0;
|
||||
|
||||
if(flags & TDT_NOW) {
|
||||
ioctl(fd, DMX_SET_FILTER, fparams);
|
||||
free(fparams);
|
||||
} else {
|
||||
tdt->tdt_fparams = fparams;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&tdmi->tdmi_table_lock);
|
||||
ioctl(fd, DMX_SET_FILTER, fparams);
|
||||
free(fparams);
|
||||
LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link);
|
||||
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
|
||||
}
|
||||
|
||||
|
||||
|
@ -300,9 +377,10 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
break;
|
||||
|
||||
case DVB_DESC_CONTENT:
|
||||
if(dlen >= 2)
|
||||
if(dlen >= 2) {
|
||||
/* We only support one content type per event atm. */
|
||||
ect = epg_content_type_find_by_dvbcode(*ptr);
|
||||
// ect = epg_content_type_find_by_dvbcode(*ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -310,8 +388,8 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
}
|
||||
|
||||
if(duration > 0) {
|
||||
epg_update_event_by_id(ch, event_id, start_time, duration,
|
||||
title, desc, ect);
|
||||
//epg_update_event_by_id(ch, event_id, start_time, duration,
|
||||
//title, desc, ect);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -812,5 +890,20 @@ dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t,
|
|||
fp = dvb_fparams_alloc(pmt_pid, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
|
||||
fp->filter.filter[0] = 0x02;
|
||||
fp->filter.mask[0] = 0xff;
|
||||
tdt_add(tdmi, fp, dvb_pmt_callback, t, pmtname, TDT_NOW | TDT_QUICKREQ);
|
||||
tdt_add(tdmi, fp, dvb_pmt_callback, t, pmtname, TDT_QUICKREQ);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_table_flush_all(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
|
||||
th_dvb_table_t *tdt;
|
||||
|
||||
while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL)
|
||||
dvb_tdt_destroy(tda, tdt);
|
||||
|
||||
}
|
||||
|
|
|
@ -48,6 +48,116 @@
|
|||
#include "notify.h"
|
||||
|
||||
|
||||
/*
|
||||
* Switch the adapter (which is implicitly tied to our transport)
|
||||
* to receive the given transport.
|
||||
*
|
||||
* But we only do this if 'weight' is higher than all of the current
|
||||
* transports that is subscribing to the adapter
|
||||
*/
|
||||
static int
|
||||
dvb_transport_start(th_transport_t *t, unsigned int weight, int status,
|
||||
int force_start)
|
||||
{
|
||||
struct dmx_pes_filter_params dmx_param;
|
||||
th_stream_t *st;
|
||||
int w, fd, pid;
|
||||
th_dvb_adapter_t *tda = t->tht_dvb_mux_instance->tdmi_adapter;
|
||||
th_dvb_mux_instance_t *tdmi = tda->tda_mux_current;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(tda->tda_rootpath == NULL)
|
||||
return 1; /* hardware not present */
|
||||
|
||||
/* Check if adapter is idle, or already tuned */
|
||||
|
||||
if(tdmi != NULL && tdmi != t->tht_dvb_mux_instance && !force_start) {
|
||||
|
||||
/* Nope .. */
|
||||
|
||||
if(tdmi->tdmi_fe_status > TDMI_FE_UNKNOWN &&
|
||||
tdmi->tdmi_fe_status < TDMI_FE_CONTANT_FEC)
|
||||
return 1; /* Not good enough signal here, can't use it */
|
||||
|
||||
w = transport_compute_weight(&tdmi->tdmi_adapter->tda_transports);
|
||||
if(w > weight)
|
||||
return 1; /* We are outranked by weight, cant use it */
|
||||
|
||||
dvb_adapter_clean(tda);
|
||||
}
|
||||
tdmi = t->tht_dvb_mux_instance;
|
||||
|
||||
LIST_FOREACH(st, &t->tht_streams, st_link) {
|
||||
|
||||
fd = open(tda->tda_demux_path, O_RDWR);
|
||||
|
||||
pid = st->st_pid;
|
||||
st->st_cc_valid = 0;
|
||||
|
||||
if(fd == -1) {
|
||||
st->st_demuxer_fd = -1;
|
||||
tvhlog(LOG_ERR, "dvb",
|
||||
"\"%s\" unable to open demuxer \"%s\" for pid %d -- %s",
|
||||
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(&dmx_param, 0, sizeof(dmx_param));
|
||||
dmx_param.pid = pid;
|
||||
dmx_param.input = DMX_IN_FRONTEND;
|
||||
dmx_param.output = DMX_OUT_TS_TAP;
|
||||
dmx_param.pes_type = DMX_PES_OTHER;
|
||||
dmx_param.flags = DMX_IMMEDIATE_START;
|
||||
|
||||
if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) {
|
||||
tvhlog(LOG_ERR, "dvb",
|
||||
"\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s",
|
||||
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
st->st_demuxer_fd = fd;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&tda->tda_delivery_mutex);
|
||||
|
||||
LIST_INSERT_HEAD(&tda->tda_transports, t, tht_active_link);
|
||||
t->tht_runstatus = status;
|
||||
|
||||
|
||||
dvb_fe_tune(tdmi);
|
||||
|
||||
pthread_mutex_unlock(&tda->tda_delivery_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_transport_stop(th_transport_t *t)
|
||||
{
|
||||
th_dvb_adapter_t *tda = t->tht_dvb_mux_instance->tdmi_adapter;
|
||||
th_stream_t *st;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
pthread_mutex_lock(&tda->tda_delivery_mutex);
|
||||
LIST_REMOVE(t, tht_active_link);
|
||||
pthread_mutex_unlock(&tda->tda_delivery_mutex);
|
||||
|
||||
LIST_FOREACH(st, &t->tht_streams, st_link) {
|
||||
close(st->st_demuxer_fd);
|
||||
st->st_demuxer_fd = -1;
|
||||
}
|
||||
t->tht_runstatus = TRANSPORT_IDLE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Load config for the given mux
|
||||
|
@ -62,6 +172,8 @@ dvb_transport_load(th_dvb_mux_instance_t *tdmi)
|
|||
unsigned int u32;
|
||||
th_transport_t *t;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if((l = hts_settings_load("dvbtransports/%s", tdmi->tdmi_identifier)) == NULL)
|
||||
return;
|
||||
|
||||
|
@ -111,6 +223,7 @@ dvb_transport_save(th_transport_t *t)
|
|||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
htsmsg_add_u32(m, "service_id", t->tht_dvb_service_id);
|
||||
htsmsg_add_u32(m, "pmt", t->tht_pmt);
|
||||
|
@ -148,6 +261,9 @@ static int
|
|||
dvb_transport_quality(th_transport_t *t)
|
||||
{
|
||||
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
return tdmi->tdmi_quality;
|
||||
}
|
||||
|
||||
|
@ -160,6 +276,8 @@ dvb_transport_sourcename(th_transport_t *t)
|
|||
{
|
||||
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
return tdmi->tdmi_adapter->tda_displayname;
|
||||
}
|
||||
|
||||
|
@ -172,6 +290,8 @@ dvb_transport_networkname(th_transport_t *t)
|
|||
{
|
||||
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
return tdmi->tdmi_network;
|
||||
}
|
||||
|
||||
|
@ -190,6 +310,8 @@ dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
|
|||
th_transport_t *t;
|
||||
char tmp[200];
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(created != NULL)
|
||||
*created = 0;
|
||||
|
||||
|
@ -211,8 +333,8 @@ dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
|
|||
t->tht_dvb_service_id = sid;
|
||||
t->tht_pmt = pmt_pid;
|
||||
|
||||
t->tht_start_feed = dvb_start_feed;
|
||||
t->tht_stop_feed = dvb_stop_feed;
|
||||
t->tht_start_feed = dvb_transport_start;
|
||||
t->tht_stop_feed = dvb_transport_stop;
|
||||
t->tht_config_change = dvb_transport_save;
|
||||
t->tht_sourcename = dvb_transport_sourcename;
|
||||
t->tht_networkname = dvb_transport_networkname;
|
||||
|
|
681
http.c
681
http.c
|
@ -32,14 +32,10 @@
|
|||
#include <libavutil/base64.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "channels.h"
|
||||
#include "subscriptions.h"
|
||||
#include "dispatch.h"
|
||||
#include "tcp.h"
|
||||
#include "http.h"
|
||||
#include "rtsp.h"
|
||||
#include "access.h"
|
||||
|
||||
int http_port;
|
||||
|
||||
static LIST_HEAD(, http_path) http_paths;
|
||||
|
||||
|
@ -57,10 +53,9 @@ static struct strtab HTTP_cmdtab[] = {
|
|||
|
||||
|
||||
static struct strtab HTTP_versiontab[] = {
|
||||
{ "HTTP/0.9", HTTP_VERSION_0_9 },
|
||||
{ "HTTP/1.0", HTTP_VERSION_1_0 },
|
||||
{ "HTTP/1.1", HTTP_VERSION_1_1 },
|
||||
{ "RTSP/1.0", RTSP_VERSION_1_0 }, /* not enabled yet */
|
||||
{ "RTSP/1.0", RTSP_VERSION_1_0 },
|
||||
};
|
||||
|
||||
static void http_parse_get_args(http_connection_t *hc, char *args);
|
||||
|
@ -145,171 +140,114 @@ static const char *cachemonths[12] = {
|
|||
"Dec"
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
http_destroy_reply(http_connection_t *hc, http_reply_t *hr)
|
||||
{
|
||||
if(hr->hr_destroy != NULL)
|
||||
hr->hr_destroy(hr, hr->hr_opaque);
|
||||
|
||||
TAILQ_REMOVE(&hc->hc_replies, hr, hr_link);
|
||||
free(hr->hr_location);
|
||||
htsbuf_queue_flush(&hr->hr_q);
|
||||
free(hr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit a HTTP reply
|
||||
*
|
||||
* Return non-zero if we should disconnect (no more keep-alive)
|
||||
*/
|
||||
static int
|
||||
http_send_reply(http_connection_t *hc, http_reply_t *hr)
|
||||
static void
|
||||
http_send_reply(http_connection_t *hc, int rc, const char *content,
|
||||
const char *encoding, const char *location, int maxage)
|
||||
{
|
||||
struct tm tm0, *tm;
|
||||
htsbuf_queue_t hdrs;
|
||||
time_t t;
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
int r;
|
||||
|
||||
if(hr->hr_version >= HTTP_VERSION_1_0) {
|
||||
http_printf(hc, "%s %d %s\r\n", val2str(hr->hr_version, HTTP_versiontab),
|
||||
hr->hr_rc, http_rc2str(hr->hr_rc));
|
||||
htsbuf_queue_init(&hdrs, 0);
|
||||
|
||||
http_printf(hc, "Server: HTS/tvheadend\r\n");
|
||||
htsbuf_qprintf(&hdrs, "%s %d %s\r\n", val2str(hc->hc_version, HTTP_versiontab),
|
||||
rc, http_rc2str(rc));
|
||||
|
||||
if(hr->hr_maxage == 0) {
|
||||
http_printf(hc, "Cache-Control: no-cache\r\n");
|
||||
} else {
|
||||
t = dispatch_clock;
|
||||
htsbuf_qprintf(&hdrs, "Server: HTS/tvheadend\r\n");
|
||||
|
||||
if(maxage == 0) {
|
||||
htsbuf_qprintf(&hdrs, "Cache-Control: no-cache\r\n");
|
||||
} else {
|
||||
time(&t);
|
||||
|
||||
tm = gmtime_r(&t, &tm0);
|
||||
htsbuf_qprintf(&hdrs,
|
||||
"Last-Modified: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
|
||||
cachedays[tm->tm_wday], tm->tm_year + 1900,
|
||||
cachemonths[tm->tm_mon], tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
t += maxage;
|
||||
|
||||
tm = gmtime_r(&t, &tm0);
|
||||
htsbuf_qprintf(&hdrs,
|
||||
"Expires: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
|
||||
cachedays[tm->tm_wday], tm->tm_year + 1900,
|
||||
cachemonths[tm->tm_mon], tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
tm = gmtime_r(&t, &tm0);
|
||||
http_printf(hc,
|
||||
"Last-Modified: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
|
||||
cachedays[tm->tm_wday], tm->tm_year + 1900,
|
||||
cachemonths[tm->tm_mon], tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
t += hr->hr_maxage;
|
||||
|
||||
tm = gmtime_r(&t, &tm0);
|
||||
http_printf(hc,
|
||||
"Expires: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
|
||||
cachedays[tm->tm_wday], tm->tm_year + 1900,
|
||||
cachemonths[tm->tm_mon], tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
http_printf(hc, "Cache-Control: max-age=%d\r\n", hr->hr_maxage);
|
||||
}
|
||||
|
||||
http_printf(hc, "Connection: %s\r\n",
|
||||
hr->hr_keep_alive ? "Keep-Alive" : "Close");
|
||||
|
||||
if(hr->hr_rc == HTTP_STATUS_UNAUTHORIZED)
|
||||
http_printf(hc, "WWW-Authenticate: Basic realm=\"tvheadend\"\r\n");
|
||||
|
||||
if(hr->hr_encoding != NULL)
|
||||
http_printf(hc, "Content-Encoding: %s\r\n", hr->hr_encoding);
|
||||
|
||||
if(hr->hr_location != NULL)
|
||||
http_printf(hc, "Location: %s\r\n", hr->hr_location);
|
||||
|
||||
http_printf(hc,
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n",
|
||||
hr->hr_content, hq->hq_size);
|
||||
htsbuf_qprintf(&hdrs, "Cache-Control: max-age=%d\r\n", maxage);
|
||||
}
|
||||
|
||||
tcp_output_queue(&hc->hc_tcp_session, 0, hq);
|
||||
if(rc == HTTP_STATUS_UNAUTHORIZED)
|
||||
htsbuf_qprintf(&hdrs, "WWW-Authenticate: Basic realm=\"tvheadend\"\r\n");
|
||||
|
||||
r = !hr->hr_keep_alive;
|
||||
htsbuf_qprintf(&hdrs, "Connection: %s\r\n",
|
||||
hc->hc_keep_alive ? "Keep-Alive" : "Close");
|
||||
|
||||
if(encoding != NULL)
|
||||
htsbuf_qprintf(&hdrs, "Content-Encoding: %s\r\n", encoding);
|
||||
|
||||
http_destroy_reply(hc, hr);
|
||||
return r;
|
||||
if(location != NULL)
|
||||
htsbuf_qprintf(&hdrs, "Location: %s\r\n", location);
|
||||
|
||||
htsbuf_qprintf(&hdrs,
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n",
|
||||
content, hc->hc_reply.hq_size);
|
||||
|
||||
tcp_write_queue(hc->hc_fd, &hdrs);
|
||||
|
||||
tcp_write_queue(hc->hc_fd, &hc->hc_reply);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send HTTP replies
|
||||
*
|
||||
* Return non-zero if we should disconnect
|
||||
*/
|
||||
static int
|
||||
http_xmit_queue(http_connection_t *hc)
|
||||
{
|
||||
http_reply_t *hr;
|
||||
|
||||
while((hr = TAILQ_FIRST(&hc->hc_replies)) != NULL) {
|
||||
if(hr->hr_destroy != NULL)
|
||||
break; /* Pending reply, cannot send this yet */
|
||||
if(http_send_reply(hc, hr))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Continue HTTP session, called by deferred replies
|
||||
*/
|
||||
void
|
||||
http_continue(http_connection_t *hc)
|
||||
{
|
||||
if(http_xmit_queue(hc))
|
||||
tcp_disconnect(&hc->hc_tcp_session, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send HTTP error back
|
||||
*/
|
||||
void
|
||||
http_error(http_connection_t *hc, http_reply_t *hr, int error)
|
||||
http_error(http_connection_t *hc, int error)
|
||||
{
|
||||
const char *errtxt = http_rc2str(error);
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
|
||||
htsbuf_queue_flush(hq);
|
||||
htsbuf_queue_flush(&hc->hc_reply);
|
||||
|
||||
htsbuf_qprintf(hq,
|
||||
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
|
||||
"<HTML><HEAD>\r\n"
|
||||
"<TITLE>%d %s</TITLE>\r\n"
|
||||
"</HEAD><BODY>\r\n"
|
||||
"<H1>%d %s</H1>\r\n"
|
||||
"</BODY></HTML>\r\n",
|
||||
error, errtxt, error, errtxt);
|
||||
htsbuf_qprintf(&hc->hc_reply,
|
||||
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
|
||||
"<HTML><HEAD>\r\n"
|
||||
"<TITLE>%d %s</TITLE>\r\n"
|
||||
"</HEAD><BODY>\r\n"
|
||||
"<H1>%d %s</H1>\r\n"
|
||||
"</BODY></HTML>\r\n",
|
||||
error, errtxt, error, errtxt);
|
||||
|
||||
hr->hr_destroy = NULL;
|
||||
hr->hr_rc = error;
|
||||
hr->hr_content = "text/html";
|
||||
http_send_reply(hc, error, "text/html", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an HTTP OK
|
||||
* Send an HTTP OK, simple version for text/html
|
||||
*/
|
||||
void
|
||||
http_output(http_connection_t *hc, http_reply_t *hr, const char *content,
|
||||
const char *encoding, int maxage)
|
||||
http_output_html(http_connection_t *hc)
|
||||
{
|
||||
hr->hr_rc = HTTP_STATUS_OK;
|
||||
hr->hr_destroy = NULL;
|
||||
hr->hr_encoding = encoding;
|
||||
hr->hr_content = content;
|
||||
hr->hr_maxage = maxage;
|
||||
return http_send_reply(hc, HTTP_STATUS_OK, "text/html; charset=UTF-8",
|
||||
NULL, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an HTTP OK, simple version for text/html
|
||||
*/
|
||||
void
|
||||
http_output_html(http_connection_t *hc, http_reply_t *hr)
|
||||
http_output_content(http_connection_t *hc, const char *content)
|
||||
{
|
||||
hr->hr_rc = HTTP_STATUS_OK;
|
||||
hr->hr_destroy = NULL;
|
||||
hr->hr_content = "text/html; charset=UTF-8";
|
||||
return http_send_reply(hc, HTTP_STATUS_OK, content, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -318,22 +256,20 @@ http_output_html(http_connection_t *hc, http_reply_t *hr)
|
|||
* Send an HTTP REDIRECT
|
||||
*/
|
||||
void
|
||||
http_redirect(http_connection_t *hc, http_reply_t *hr, const char *location)
|
||||
http_redirect(http_connection_t *hc, const char *location)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_flush(&hc->hc_reply);
|
||||
|
||||
htsbuf_qprintf(hq,
|
||||
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
|
||||
"<HTML><HEAD>\r\n"
|
||||
"<TITLE>Redirect</TITLE>\r\n"
|
||||
"</HEAD><BODY>\r\n"
|
||||
"Please follow <a href=\"%s\">%s</a>\r\n"
|
||||
"</BODY></HTML>\r\n",
|
||||
location, location);
|
||||
htsbuf_qprintf(&hc->hc_reply,
|
||||
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
|
||||
"<HTML><HEAD>\r\n"
|
||||
"<TITLE>Redirect</TITLE>\r\n"
|
||||
"</HEAD><BODY>\r\n"
|
||||
"Please follow <a href=\"%s\">%s</a>\r\n"
|
||||
"</BODY></HTML>\r\n",
|
||||
location, location);
|
||||
|
||||
hr->hr_location = strdup(location);
|
||||
hr->hr_rc = HTTP_STATUS_FOUND;
|
||||
hr->hr_content = "text/html";
|
||||
http_send_reply(hc, HTTP_STATUS_FOUND, "text/html", NULL, location, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -343,35 +279,18 @@ http_redirect(http_connection_t *hc, http_reply_t *hr, const char *location)
|
|||
* Returns 1 if we should disconnect
|
||||
*
|
||||
*/
|
||||
static int
|
||||
http_exec(http_connection_t *hc, http_path_t *hp, char *remain, int err)
|
||||
static void
|
||||
http_exec(http_connection_t *hc, http_path_t *hp, char *remain)
|
||||
{
|
||||
http_reply_t *hr = calloc(1, sizeof(http_reply_t));
|
||||
int err;
|
||||
if(access_verify(hc->hc_username, hc->hc_password,
|
||||
(struct sockaddr *)hc->hc_peer, hp->hp_accessmask)) {
|
||||
http_error(hc, HTTP_STATUS_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Insert reply in order */
|
||||
TAILQ_INSERT_TAIL(&hc->hc_replies, hr, hr_link);
|
||||
|
||||
htsbuf_queue_init(&hr->hr_q, INT32_MAX);
|
||||
hr->hr_connection = hc;
|
||||
hr->hr_version = hc->hc_version;
|
||||
hr->hr_keep_alive = hc->hc_keep_alive;
|
||||
|
||||
if(!err &&
|
||||
access_verify(hc->hc_username, hc->hc_password,
|
||||
(struct sockaddr *)&hc->hc_tcp_session.tcp_peer_addr,
|
||||
hp->hp_accessmask))
|
||||
err = HTTP_STATUS_UNAUTHORIZED;
|
||||
|
||||
if(!err)
|
||||
err = hp->hp_callback(hc, hr, remain, hp->hp_opaque);
|
||||
|
||||
if(err)
|
||||
http_error(hc, hr, err);
|
||||
|
||||
if(hr->hr_destroy != NULL)
|
||||
return 0; /* New entry is delayed, do not transmit anything */
|
||||
|
||||
return http_xmit_queue(hc);
|
||||
if((err = hp->hp_callback(hc, remain, hp->hp_opaque)) != 0)
|
||||
http_error(hc, err);
|
||||
}
|
||||
|
||||
|
||||
|
@ -387,50 +306,16 @@ http_cmd_get(http_connection_t *hc)
|
|||
char *args;
|
||||
|
||||
hp = http_resolve(hc, &remain, &args);
|
||||
if(hp == NULL)
|
||||
return http_exec(hc, NULL, NULL, HTTP_STATUS_NOT_FOUND);
|
||||
if(hp == NULL) {
|
||||
http_error(hc, HTTP_STATUS_NOT_FOUND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(args != NULL)
|
||||
http_parse_get_args(hc, args);
|
||||
|
||||
return http_exec(hc, hp, remain, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a HTTP POST is fully received, and if so, continue processing
|
||||
*
|
||||
* Return non-zero if we should disconnect
|
||||
*/
|
||||
static int
|
||||
http_post_check(http_connection_t *hc)
|
||||
{
|
||||
http_path_t *hp;
|
||||
char *remain, *args, *v, *argv[2];
|
||||
int n;
|
||||
|
||||
if(hc->hc_post_ptr != hc->hc_post_len)
|
||||
return 0;
|
||||
|
||||
hc->hc_state = HTTP_CON_WAIT_REQUEST;
|
||||
|
||||
/* Parse content-type */
|
||||
v = http_arg_get(&hc->hc_args, "Content-Type");
|
||||
if(v == NULL)
|
||||
return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
|
||||
|
||||
n = http_tokenize(v, argv, 2, ';');
|
||||
if(n == 0)
|
||||
return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
|
||||
|
||||
if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
|
||||
http_parse_get_args(hc, hc->hc_post_data);
|
||||
|
||||
hp = http_resolve(hc, &remain, &args);
|
||||
if(hp == NULL)
|
||||
return http_exec(hc, NULL, NULL, HTTP_STATUS_NOT_FOUND);
|
||||
|
||||
return http_exec(hc, hp, remain, 0);
|
||||
http_exec(hc, hp, remain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -443,20 +328,25 @@ http_post_check(http_connection_t *hc)
|
|||
* Return non-zero if we should disconnect
|
||||
*/
|
||||
static int
|
||||
http_cmd_post(http_connection_t *hc)
|
||||
http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
|
||||
{
|
||||
char *v;
|
||||
http_path_t *hp;
|
||||
char *remain, *args, *v, *argv[2];
|
||||
int n;
|
||||
|
||||
/* Set keep-alive status */
|
||||
v = http_arg_get(&hc->hc_args, "Content-Length");
|
||||
if(v == NULL)
|
||||
return 1; /* No content length in POST, make us disconnect */
|
||||
|
||||
hc->hc_post_len = atoi(v);
|
||||
if(hc->hc_post_len > 16 * 1024 * 1024)
|
||||
return 1; /* Bail out if POST data > 16 Mb */
|
||||
if(v == NULL) {
|
||||
/* No content length in POST, make us disconnect */
|
||||
return -1;
|
||||
}
|
||||
|
||||
hc->hc_state = HTTP_CON_POST_DATA;
|
||||
hc->hc_post_len = atoi(v);
|
||||
if(hc->hc_post_len > 16 * 1024 * 1024) {
|
||||
/* Bail out if POST data > 16 Mb */
|
||||
hc->hc_keep_alive = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate space for data, we add a terminating null char to ease
|
||||
string processing on the content */
|
||||
|
@ -464,50 +354,48 @@ http_cmd_post(http_connection_t *hc)
|
|||
hc->hc_post_data = malloc(hc->hc_post_len + 1);
|
||||
hc->hc_post_data[hc->hc_post_len] = 0;
|
||||
|
||||
hc->hc_post_ptr = 0;
|
||||
if(tcp_read_data(hc->hc_fd, hc->hc_post_data, hc->hc_post_len, spill) < 0)
|
||||
return -1;
|
||||
|
||||
/* We need to drain the line parser of any excess data */
|
||||
|
||||
hc->hc_post_ptr = tcp_line_drain(&hc->hc_tcp_session, hc->hc_post_data,
|
||||
hc->hc_post_len);
|
||||
return http_post_check(hc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read POST data directly from socket
|
||||
*/
|
||||
static void
|
||||
http_consume_post_data(http_connection_t *hc)
|
||||
{
|
||||
tcp_session_t *tcp = &hc->hc_tcp_session;
|
||||
int togo = hc->hc_post_len - hc->hc_post_ptr;
|
||||
int r;
|
||||
|
||||
r = read(tcp->tcp_fd, hc->hc_post_data + hc->hc_post_ptr, togo);
|
||||
if(r < 1) {
|
||||
tcp_disconnect(tcp, r == 0 ? ECONNRESET : errno);
|
||||
return;
|
||||
/* Parse content-type */
|
||||
v = http_arg_get(&hc->hc_args, "Content-Type");
|
||||
if(v == NULL) {
|
||||
http_error(hc, HTTP_STATUS_BAD_REQUEST);
|
||||
return 0;
|
||||
}
|
||||
n = http_tokenize(v, argv, 2, ';');
|
||||
if(n == 0) {
|
||||
http_error(hc, HTTP_STATUS_BAD_REQUEST);
|
||||
return 0;
|
||||
}
|
||||
|
||||
hc->hc_post_ptr += r;
|
||||
if(http_post_check(hc))
|
||||
tcp_disconnect(tcp, 0);
|
||||
if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
|
||||
http_parse_get_args(hc, hc->hc_post_data);
|
||||
|
||||
hp = http_resolve(hc, &remain, &args);
|
||||
if(hp == NULL) {
|
||||
http_error(hc, HTTP_STATUS_NOT_FOUND);
|
||||
return 0;
|
||||
}
|
||||
http_exec(hc, hp, remain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process a HTTP request
|
||||
*/
|
||||
static int
|
||||
http_process_request(http_connection_t *hc)
|
||||
http_process_request(http_connection_t *hc, htsbuf_queue_t *spill)
|
||||
{
|
||||
switch(hc->hc_cmd) {
|
||||
default:
|
||||
return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
|
||||
http_error(hc, HTTP_STATUS_BAD_REQUEST);
|
||||
return 0;
|
||||
case HTTP_CMD_GET:
|
||||
return http_cmd_get(hc);
|
||||
case HTTP_CMD_POST:
|
||||
return http_cmd_post(hc);
|
||||
return http_cmd_post(hc, spill);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,7 +404,7 @@ http_process_request(http_connection_t *hc)
|
|||
* clean up
|
||||
*/
|
||||
static int
|
||||
process_request(http_connection_t *hc)
|
||||
process_request(http_connection_t *hc, htsbuf_queue_t *spill)
|
||||
{
|
||||
char *v, *argv[2];
|
||||
int n;
|
||||
|
@ -530,10 +418,6 @@ process_request(http_connection_t *hc)
|
|||
hc->hc_keep_alive = 1;
|
||||
break;
|
||||
|
||||
case HTTP_VERSION_0_9:
|
||||
hc->hc_keep_alive = 0;
|
||||
break;
|
||||
|
||||
case HTTP_VERSION_1_0:
|
||||
/* Keep-alive is default off, but can be enabled */
|
||||
hc->hc_keep_alive = v != NULL && !strcasecmp(v, "keep-alive");
|
||||
|
@ -545,12 +429,6 @@ process_request(http_connection_t *hc)
|
|||
break;
|
||||
}
|
||||
|
||||
free(hc->hc_username);
|
||||
hc->hc_username = NULL;
|
||||
|
||||
free(hc->hc_password);
|
||||
hc->hc_password = NULL;
|
||||
|
||||
/* Extract authorization */
|
||||
if((v = http_arg_get(&hc->hc_args, "Authorization")) != NULL) {
|
||||
if((n = http_tokenize(v, argv, 2, -1)) == 2) {
|
||||
|
@ -563,175 +441,21 @@ process_request(http_connection_t *hc)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch(hc->hc_version) {
|
||||
case RTSP_VERSION_1_0:
|
||||
rtsp_process_request(hc);
|
||||
return -1;
|
||||
// rtsp_process_request(hc);
|
||||
return 0;
|
||||
|
||||
case HTTP_VERSION_0_9:
|
||||
case HTTP_VERSION_1_0:
|
||||
case HTTP_VERSION_1_1:
|
||||
return http_process_request(hc) ? -1 : 0;
|
||||
return http_process_request(hc, spill);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* HTTP connection state machine & parser
|
||||
*
|
||||
* If we return non zero we will disconnect
|
||||
*/
|
||||
static int
|
||||
http_con_parse(void *aux, char *buf)
|
||||
{
|
||||
http_connection_t *hc = aux;
|
||||
int n, v;
|
||||
char *argv[3], *c;
|
||||
|
||||
// printf("HTTP INPUT: %s\n", buf);
|
||||
|
||||
switch(hc->hc_state) {
|
||||
case HTTP_CON_WAIT_REQUEST:
|
||||
if(hc->hc_post_data != NULL) {
|
||||
free(hc->hc_post_data);
|
||||
hc->hc_post_data = NULL;
|
||||
}
|
||||
|
||||
http_arg_flush(&hc->hc_args);
|
||||
http_arg_flush(&hc->hc_req_args);
|
||||
if(hc->hc_url != NULL) {
|
||||
free(hc->hc_url);
|
||||
hc->hc_url = NULL;
|
||||
}
|
||||
|
||||
n = http_tokenize(buf, argv, 3, -1);
|
||||
|
||||
if(n < 2)
|
||||
return EBADRQC;
|
||||
|
||||
hc->hc_cmd = str2val(argv[0], HTTP_cmdtab);
|
||||
hc->hc_url = strdup(argv[1]);
|
||||
|
||||
|
||||
if(n == 3) {
|
||||
v = str2val(argv[2], HTTP_versiontab);
|
||||
if(v == -1)
|
||||
return EBADRQC;
|
||||
|
||||
hc->hc_version = v;
|
||||
hc->hc_state = HTTP_CON_READ_HEADER;
|
||||
} else {
|
||||
hc->hc_version = HTTP_VERSION_0_9;
|
||||
return process_request(hc);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case HTTP_CON_READ_HEADER:
|
||||
if(*buf == 0) {
|
||||
/* Empty crlf line, end of header lines */
|
||||
hc->hc_state = HTTP_CON_WAIT_REQUEST;
|
||||
return process_request(hc);
|
||||
}
|
||||
|
||||
n = http_tokenize(buf, argv, 2, -1);
|
||||
if(n < 2)
|
||||
break;
|
||||
|
||||
c = strrchr(argv[0], ':');
|
||||
if(c == NULL)
|
||||
break;
|
||||
*c = 0;
|
||||
http_arg_set(&hc->hc_args, argv[0], argv[1]);
|
||||
break;
|
||||
|
||||
case HTTP_CON_POST_DATA:
|
||||
abort();
|
||||
|
||||
case HTTP_CON_END:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* disconnect
|
||||
*/
|
||||
static void
|
||||
http_disconnect(http_connection_t *hc)
|
||||
{
|
||||
http_reply_t *hr;
|
||||
|
||||
while((hr = TAILQ_FIRST(&hc->hc_replies)) != NULL)
|
||||
http_destroy_reply(hc, hr);
|
||||
|
||||
free(hc->hc_post_data);
|
||||
free(hc->hc_username);
|
||||
free(hc->hc_password);
|
||||
|
||||
rtsp_disconncet(hc);
|
||||
http_arg_flush(&hc->hc_args);
|
||||
http_arg_flush(&hc->hc_req_args);
|
||||
free(hc->hc_url);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void
|
||||
http_tcp_callback(tcpevent_t event, void *tcpsession)
|
||||
{
|
||||
http_connection_t *hc = tcpsession;
|
||||
|
||||
switch(event) {
|
||||
case TCP_CONNECT:
|
||||
TAILQ_INIT(&hc->hc_replies);
|
||||
TAILQ_INIT(&hc->hc_args);
|
||||
TAILQ_INIT(&hc->hc_req_args);
|
||||
break;
|
||||
|
||||
case TCP_DISCONNECT:
|
||||
http_disconnect(hc);
|
||||
break;
|
||||
|
||||
case TCP_INPUT:
|
||||
|
||||
if(hc->hc_state == HTTP_CON_POST_DATA)
|
||||
http_consume_post_data(hc);
|
||||
else
|
||||
tcp_line_read(&hc->hc_tcp_session, http_con_parse);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fire up HTTP server
|
||||
*/
|
||||
|
||||
void
|
||||
http_start(int port)
|
||||
{
|
||||
rtsp_init();
|
||||
|
||||
http_port = port;
|
||||
tcp_create_server(port, sizeof(http_connection_t), "http",
|
||||
http_tcp_callback);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Delete all arguments associated with a connection
|
||||
|
@ -904,37 +628,112 @@ http_parse_get_args(http_connection_t *hc, char *args)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* HTTP embedded resource
|
||||
*
|
||||
*/
|
||||
typedef struct http_resource {
|
||||
const void *data;
|
||||
size_t len;
|
||||
const char *content;
|
||||
const char *encoding;
|
||||
} http_resource_t;
|
||||
|
||||
static int
|
||||
deliver_resource(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
static void
|
||||
http_serve_requests(http_connection_t *hc, htsbuf_queue_t *spill)
|
||||
{
|
||||
http_resource_t *hres = opaque;
|
||||
char cmdline[1024];
|
||||
char hdrline[1024];
|
||||
char *argv[3], *c;
|
||||
int n;
|
||||
|
||||
htsbuf_queue_init(&hc->hc_reply, 0);
|
||||
|
||||
htsbuf_append(&hr->hr_q, hres->data, hres->len);
|
||||
http_output(hc, hr, hres->content, hres->encoding, 15);
|
||||
return 0;
|
||||
do {
|
||||
if(tcp_read_line(hc->hc_fd, cmdline, sizeof(cmdline), spill) < 0)
|
||||
return;
|
||||
|
||||
if((n = http_tokenize(cmdline, argv, 3, -1)) != 3)
|
||||
return;
|
||||
|
||||
if((hc->hc_cmd = str2val(argv[0], HTTP_cmdtab)) == -1)
|
||||
return;
|
||||
hc->hc_url = argv[1];
|
||||
if((hc->hc_version = str2val(argv[2], HTTP_versiontab)) == -1)
|
||||
return;
|
||||
|
||||
/* parse header */
|
||||
while(1) {
|
||||
if(tcp_read_line(hc->hc_fd, hdrline, sizeof(hdrline), spill) < 0)
|
||||
return;
|
||||
if(hdrline[0] == 0)
|
||||
break; /* header complete */
|
||||
|
||||
if((n = http_tokenize(hdrline, argv, 2, -1)) < 2)
|
||||
return;
|
||||
|
||||
if((c = strrchr(argv[0], ':')) == NULL)
|
||||
return;
|
||||
|
||||
*c = 0;
|
||||
http_arg_set(&hc->hc_args, argv[0], argv[1]);
|
||||
}
|
||||
|
||||
if(process_request(hc, spill))
|
||||
break;
|
||||
|
||||
free(hc->hc_post_data);
|
||||
hc->hc_post_data = NULL;
|
||||
|
||||
http_arg_flush(&hc->hc_args);
|
||||
http_arg_flush(&hc->hc_req_args);
|
||||
|
||||
htsbuf_queue_flush(&hc->hc_reply);
|
||||
|
||||
free(hc->hc_username);
|
||||
hc->hc_username = NULL;
|
||||
|
||||
free(hc->hc_password);
|
||||
hc->hc_password = NULL;
|
||||
|
||||
} while(hc->hc_keep_alive);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
http_serve(int fd, void *opaque, struct sockaddr_in *source)
|
||||
{
|
||||
htsbuf_queue_t spill;
|
||||
http_connection_t hc;
|
||||
|
||||
memset(&hc, 0, sizeof(http_connection_t));
|
||||
|
||||
TAILQ_INIT(&hc.hc_args);
|
||||
TAILQ_INIT(&hc.hc_req_args);
|
||||
|
||||
hc.hc_fd = fd;
|
||||
hc.hc_peer = source;
|
||||
|
||||
htsbuf_queue_init(&spill, 0);
|
||||
|
||||
http_serve_requests(&hc, &spill);
|
||||
|
||||
free(hc.hc_post_data);
|
||||
free(hc.hc_username);
|
||||
free(hc.hc_password);
|
||||
|
||||
// rtsp_disconncet(hc);
|
||||
http_arg_flush(&hc.hc_args);
|
||||
http_arg_flush(&hc.hc_req_args);
|
||||
|
||||
htsbuf_queue_flush(&spill);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fire up HTTP server
|
||||
*/
|
||||
void
|
||||
http_resource_add(const char *path, const void *ptr, size_t len,
|
||||
const char *content, const char *encoding)
|
||||
http_server_init(void)
|
||||
{
|
||||
http_resource_t *hres = malloc(sizeof(http_resource_t));
|
||||
|
||||
hres->data = ptr;
|
||||
hres->len = len;
|
||||
hres->content = content;
|
||||
hres->encoding = encoding;
|
||||
|
||||
http_path_add(path, hres, deliver_resource, ACCESS_WEB_INTERFACE);
|
||||
tcp_server_create(9981, http_serve, NULL);
|
||||
}
|
||||
|
|
77
http.h
77
http.h
|
@ -19,9 +19,15 @@
|
|||
#ifndef HTTP_H_
|
||||
#define HTTP_H_
|
||||
|
||||
extern int http_port;
|
||||
#include <libhts/htsbuf.h>
|
||||
|
||||
#include "tcp.h"
|
||||
TAILQ_HEAD(http_arg_list, http_arg);
|
||||
|
||||
typedef struct http_arg {
|
||||
TAILQ_ENTRY(http_arg) link;
|
||||
char *key;
|
||||
char *val;
|
||||
} http_arg_t;
|
||||
|
||||
#define HTTP_STATUS_OK 200
|
||||
#define HTTP_STATUS_FOUND 302
|
||||
|
@ -32,48 +38,13 @@ extern int http_port;
|
|||
|
||||
LIST_HEAD(rtsp_session_head, rtsp_session);
|
||||
|
||||
#define http_printf(x, fmt...) tcp_printf(&(x)->hc_tcp_session, fmt)
|
||||
|
||||
TAILQ_HEAD(http_arg_list, http_arg);
|
||||
|
||||
typedef struct http_arg {
|
||||
TAILQ_ENTRY(http_arg) link;
|
||||
char *key;
|
||||
char *val;
|
||||
} http_arg_t;
|
||||
|
||||
|
||||
TAILQ_HEAD(http_reply_queue, http_reply);
|
||||
|
||||
typedef struct http_reply {
|
||||
TAILQ_ENTRY(http_reply) hr_link;
|
||||
|
||||
void *hr_opaque;
|
||||
|
||||
void (*hr_destroy)(struct http_reply *hr, void *opaque);
|
||||
|
||||
struct http_connection *hr_connection;
|
||||
int hr_version; /* HTTP version */
|
||||
int hr_keep_alive;
|
||||
|
||||
char *hr_location;
|
||||
|
||||
int hr_rc; /* Return code */
|
||||
int hr_maxage;
|
||||
const char *hr_encoding;
|
||||
const char *hr_content;
|
||||
|
||||
htsbuf_queue_t hr_q;
|
||||
|
||||
} http_reply_t;
|
||||
|
||||
|
||||
typedef struct http_connection {
|
||||
tcp_session_t hc_tcp_session; /* Must be first */
|
||||
int hc_fd;
|
||||
struct sockaddr_in *hc_peer;
|
||||
char *hc_url;
|
||||
int hc_keep_alive;
|
||||
|
||||
struct http_reply_queue hc_replies;
|
||||
htsbuf_queue_t hc_reply;
|
||||
|
||||
struct http_arg_list hc_args;
|
||||
|
||||
|
@ -98,7 +69,6 @@ typedef struct http_connection {
|
|||
} hc_cmd;
|
||||
|
||||
enum {
|
||||
HTTP_VERSION_0_9,
|
||||
HTTP_VERSION_1_0,
|
||||
HTTP_VERSION_1_1,
|
||||
RTSP_VERSION_1_0,
|
||||
|
@ -107,7 +77,7 @@ typedef struct http_connection {
|
|||
char *hc_username;
|
||||
char *hc_password;
|
||||
|
||||
struct rtsp_session_head hc_rtsp_sessions;
|
||||
// struct rtsp_session_head hc_rtsp_sessions;
|
||||
|
||||
int hc_authenticated; /* Used by RTSP, it seems VLC does not
|
||||
send authentication headers for each
|
||||
|
@ -120,14 +90,10 @@ typedef struct http_connection {
|
|||
|
||||
char *hc_post_data;
|
||||
unsigned int hc_post_len;
|
||||
unsigned int hc_post_ptr;
|
||||
|
||||
} http_connection_t;
|
||||
|
||||
|
||||
|
||||
void http_start(int port);
|
||||
|
||||
void http_arg_flush(struct http_arg_list *list);
|
||||
|
||||
char *http_arg_get(struct http_arg_list *list, const char *name);
|
||||
|
@ -136,19 +102,15 @@ void http_arg_set(struct http_arg_list *list, char *key, char *val);
|
|||
|
||||
int http_tokenize(char *buf, char **vec, int vecsize, int delimiter);
|
||||
|
||||
void http_error(http_connection_t *hc, http_reply_t *hr, int error);
|
||||
void http_error(http_connection_t *hc, int error);
|
||||
|
||||
void http_output(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *content, const char *encoding, int maxage);
|
||||
void http_output_html(http_connection_t *hc);
|
||||
|
||||
void http_output_html(http_connection_t *hc, http_reply_t *hr);
|
||||
void http_output_content(http_connection_t *hc, const char *content);
|
||||
|
||||
void http_continue(http_connection_t *hc);
|
||||
void http_redirect(http_connection_t *hc, const char *location);
|
||||
|
||||
void http_redirect(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *location);
|
||||
|
||||
typedef int (http_callback_t)(http_connection_t *hc, http_reply_t *hr,
|
||||
typedef int (http_callback_t)(http_connection_t *hc,
|
||||
const char *remain, void *opaque);
|
||||
|
||||
typedef struct http_path {
|
||||
|
@ -163,7 +125,8 @@ typedef struct http_path {
|
|||
http_path_t *http_path_add(const char *path, void *opaque,
|
||||
http_callback_t *callback, uint32_t accessmask);
|
||||
|
||||
void http_resource_add(const char *path, const void *ptr, size_t len,
|
||||
const char *content, const char *encoding);
|
||||
|
||||
|
||||
void http_server_init(void);
|
||||
|
||||
#endif /* HTTP_H_ */
|
||||
|
|
352
main.c
352
main.c
|
@ -37,65 +37,23 @@
|
|||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dvb/dvb.h"
|
||||
#include "v4l.h"
|
||||
#include "channels.h"
|
||||
#include "epg.h"
|
||||
#include "epg_xmltv.h"
|
||||
#include "pvr.h"
|
||||
#include "dispatch.h"
|
||||
#include "transports.h"
|
||||
#include "subscriptions.h"
|
||||
#include "iptv_output.h"
|
||||
#include "rtsp.h"
|
||||
#include "http.h"
|
||||
#include "htsp.h"
|
||||
#include "buffer.h"
|
||||
#include "htmlui.h"
|
||||
#include "avgen.h"
|
||||
#include "file_input.h"
|
||||
#include "cwc.h"
|
||||
#include "autorec.h"
|
||||
#include "spawn.h"
|
||||
#include "ffmuxer.h"
|
||||
#include "xbmsp.h"
|
||||
//#include "ajaxui/ajaxui.h"
|
||||
#include "webui/webui.h"
|
||||
#include "tcp.h"
|
||||
#include "access.h"
|
||||
#include "serviceprobe.h"
|
||||
#include "http.h"
|
||||
#include "webui/webui.h"
|
||||
#include "dvb/dvb.h"
|
||||
|
||||
#include <libhts/htsparachute.h>
|
||||
#include <libhts/htssettings.h>
|
||||
|
||||
|
||||
int running;
|
||||
int startupcounter;
|
||||
const char *settings_dir;
|
||||
const char *sys_warning;
|
||||
extern const char *htsversion;
|
||||
static pthread_mutex_t tag_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static uint32_t tag_tally;
|
||||
|
||||
uint32_t
|
||||
tag_get(void)
|
||||
{
|
||||
uint32_t r;
|
||||
|
||||
pthread_mutex_lock(&tag_mutex);
|
||||
r = ++tag_tally;
|
||||
if(r == 0)
|
||||
r = ++tag_tally;
|
||||
|
||||
pthread_mutex_unlock(&tag_mutex);
|
||||
return r;
|
||||
}
|
||||
time_t dispatch_clock;
|
||||
static LIST_HEAD(, gtimer) gtimers;
|
||||
pthread_mutex_t global_lock;
|
||||
|
||||
|
||||
static void
|
||||
doexit(int x)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_sigpipe(int x)
|
||||
|
@ -103,6 +61,12 @@ handle_sigpipe(int x)
|
|||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
doexit(int x)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pull_chute (int sig)
|
||||
{
|
||||
|
@ -113,6 +77,90 @@ pull_chute (int sig)
|
|||
sig, pwd);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
gtimercmp(gtimer_t *a, gtimer_t *b)
|
||||
{
|
||||
if(a->gti_expire < b->gti_expire)
|
||||
return -1;
|
||||
else if(a->gti_expire > b->gti_expire)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
gtimer_arm(gtimer_t *gti, gti_callback_t *callback, void *opaque, int delta)
|
||||
{
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(gti->gti_callback != NULL)
|
||||
LIST_REMOVE(gti, gti_link);
|
||||
|
||||
gti->gti_callback = callback;
|
||||
gti->gti_opaque = opaque;
|
||||
gti->gti_expire = now + delta;
|
||||
|
||||
LIST_INSERT_SORTED(>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);
|
||||
}
|
||||
|
|
10
mux.c
10
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)
|
||||
|
|
2
notify.c
2
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
|
||||
}
|
||||
|
|
16
parsers.c
16
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 */
|
||||
|
|
2
psi.c
2
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];
|
||||
|
|
2
pvr.h
2
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;
|
||||
|
||||
|
|
|
@ -31,31 +31,31 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
#include "teletext.h"
|
||||
#include "transports.h"
|
||||
#include "subscriptions.h"
|
||||
|
||||
#include "v4l.h"
|
||||
#include "iptv_input.h"
|
||||
#include "psi.h"
|
||||
|
||||
struct th_subscription_list subscriptions;
|
||||
static dtimer_t auto_reschedule_timer;
|
||||
|
||||
|
||||
static int
|
||||
subscription_sort(th_subscription_t *a, th_subscription_t *b)
|
||||
{
|
||||
return b->ths_weight - a->ths_weight;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
subscription_reschedule(void)
|
||||
{
|
||||
th_subscription_t *s;
|
||||
th_transport_t *t;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
LIST_FOREACH(s, &subscriptions, ths_global_link) {
|
||||
if(s->ths_transport != NULL)
|
||||
continue; /* Got a transport, we're happy */
|
||||
|
@ -74,37 +74,23 @@ subscription_reschedule(void)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
auto_reschedule(void *aux, int64_t now)
|
||||
{
|
||||
dtimer_arm(&auto_reschedule_timer, auto_reschedule, NULL, 10);
|
||||
subscription_reschedule();
|
||||
}
|
||||
|
||||
void
|
||||
subscription_stop(th_subscription_t *s)
|
||||
{
|
||||
s->ths_callback(s, TRANSPORT_UNAVAILABLE, s->ths_opaque);
|
||||
LIST_REMOVE(s, ths_transport_link);
|
||||
s->ths_transport = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
subscription_unsubscribe(th_subscription_t *s)
|
||||
{
|
||||
th_transport_t *t = s->ths_transport;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
LIST_REMOVE(s, ths_global_link);
|
||||
|
||||
if(s->ths_channel != NULL)
|
||||
LIST_REMOVE(s, ths_channel_link);
|
||||
|
||||
if(t != NULL) {
|
||||
subscription_stop(s);
|
||||
transport_stop(t, 0);
|
||||
}
|
||||
if(t != NULL)
|
||||
transport_remove_subscriber(t, s);
|
||||
|
||||
free(s->ths_title);
|
||||
free(s);
|
||||
|
@ -113,16 +99,9 @@ subscription_unsubscribe(th_subscription_t *s)
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int
|
||||
subscription_sort(th_subscription_t *a, th_subscription_t *b)
|
||||
{
|
||||
return b->ths_weight - a->ths_weight;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
th_subscription_t *
|
||||
subscription_create(channel_t *ch, unsigned int weight, const char *name,
|
||||
subscription_callback_t *cb, void *opaque, uint32_t u32)
|
||||
|
@ -157,23 +136,12 @@ subscription_create(channel_t *ch, unsigned int weight, const char *name,
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
subscription_set_weight(th_subscription_t *s, unsigned int weight)
|
||||
{
|
||||
if(s->ths_weight == weight)
|
||||
return;
|
||||
|
||||
LIST_REMOVE(s, ths_global_link);
|
||||
s->ths_weight = weight;
|
||||
LIST_INSERT_SORTED(&subscriptions, s, ths_global_link, subscription_sort);
|
||||
|
||||
subscription_reschedule();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
subscriptions_init(void)
|
||||
{
|
||||
dtimer_arm(&auto_reschedule_timer, auto_reschedule, NULL, 10);
|
||||
|
||||
}
|
||||
|
||||
|
|
293
tcp.c
293
tcp.c
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
@ -29,8 +30,11 @@
|
|||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "dispatch.h"
|
||||
#include "tcp.h"
|
||||
|
||||
#if 0
|
||||
|
||||
#include "dispatch.h"
|
||||
#include "resolver.h"
|
||||
|
||||
static void tcp_client_reconnect_timeout(void *aux, int64_t now);
|
||||
|
@ -198,6 +202,7 @@ tcp_send_msg(tcp_session_t *ses, int hiprio, void *data, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -627,3 +632,289 @@ tcp_set_hostname(tcp_session_t *ses, const char *hostname)
|
|||
|
||||
ses->tcp_hostname = strdup(hostname);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int
|
||||
tcp_write_queue(int fd, htsbuf_queue_t *q)
|
||||
{
|
||||
htsbuf_data_t *hd;
|
||||
int l, r;
|
||||
|
||||
while((hd = TAILQ_FIRST(&q->hq_q)) != NULL) {
|
||||
TAILQ_REMOVE(&q->hq_q, hd, hd_link);
|
||||
|
||||
l = hd->hd_data_len - hd->hd_data_off;
|
||||
r = write(fd, hd->hd_data + hd->hd_data_off, l);
|
||||
if(l != r) {
|
||||
fprintf(stderr, "write error\n");
|
||||
}
|
||||
free(hd->hd_data);
|
||||
free(hd);
|
||||
}
|
||||
q->hq_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
tcp_fill_htsbuf_from_fd(int fd, htsbuf_queue_t *hq)
|
||||
{
|
||||
htsbuf_data_t *hd = TAILQ_LAST(&hq->hq_q, htsbuf_data_queue);
|
||||
int c;
|
||||
|
||||
if(hd != NULL) {
|
||||
/* Fill out any previous buffer */
|
||||
c = hd->hd_data_size - hd->hd_data_len;
|
||||
|
||||
if(c > 0) {
|
||||
|
||||
c = read(fd, hd->hd_data + hd->hd_data_len, c);
|
||||
if(c < 1)
|
||||
return -1;
|
||||
|
||||
hd->hd_data_len += c;
|
||||
hq->hq_size += c;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
hd = malloc(sizeof(htsbuf_data_t));
|
||||
|
||||
hd->hd_data_size = 1000;
|
||||
hd->hd_data = malloc(hd->hd_data_size);
|
||||
|
||||
c = read(fd, hd->hd_data, hd->hd_data_size);
|
||||
if(c < 1) {
|
||||
free(hd->hd_data);
|
||||
free(hd);
|
||||
return -1;
|
||||
}
|
||||
hd->hd_data_len = c;
|
||||
hd->hd_data_off = 0;
|
||||
TAILQ_INSERT_TAIL(&hq->hq_q, hd, hd_link);
|
||||
hq->hq_size += c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int
|
||||
tcp_read_line(int fd, char *buf, const size_t bufsize, htsbuf_queue_t *spill)
|
||||
{
|
||||
int len;
|
||||
|
||||
while(1) {
|
||||
len = htsbuf_find(spill, 0xa);
|
||||
|
||||
if(len == -1) {
|
||||
if(tcp_fill_htsbuf_from_fd(fd, spill) < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(len >= bufsize - 1)
|
||||
return -1;
|
||||
|
||||
htsbuf_read(spill, buf, len);
|
||||
buf[len] = 0;
|
||||
while(len > 0 && buf[len - 1] < 32)
|
||||
buf[--len] = 0;
|
||||
htsbuf_drop(spill, 1); /* Drop the \n */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int
|
||||
tcp_read_data(int fd, char *buf, const size_t bufsize, htsbuf_queue_t *spill)
|
||||
{
|
||||
int x, tot = htsbuf_read(spill, buf, bufsize);
|
||||
|
||||
if(tot == bufsize)
|
||||
return 0;
|
||||
|
||||
x = recv(fd, buf + tot, bufsize - tot, MSG_WAITALL);
|
||||
if(x != bufsize - tot)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int tcp_server_epoll_fd;
|
||||
|
||||
typedef struct tcp_server {
|
||||
tcp_server_callback_t *start;
|
||||
void *opaque;
|
||||
int serverfd;
|
||||
} tcp_server_t;
|
||||
|
||||
typedef struct tcp_server_launch_t {
|
||||
tcp_server_callback_t *start;
|
||||
void *opaque;
|
||||
int fd;
|
||||
struct sockaddr_in source;
|
||||
} tcp_server_launch_t;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void *
|
||||
tcp_server_start(void *aux)
|
||||
{
|
||||
tcp_server_launch_t *tsl = aux;
|
||||
int val;
|
||||
|
||||
val = 1;
|
||||
setsockopt(tsl->fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
|
||||
|
||||
val = 30;
|
||||
setsockopt(tsl->fd, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(val));
|
||||
|
||||
val = 15;
|
||||
setsockopt(tsl->fd, SOL_TCP, TCP_KEEPINTVL, &val, sizeof(val));
|
||||
|
||||
val = 5;
|
||||
setsockopt(tsl->fd, SOL_TCP, TCP_KEEPCNT, &val, sizeof(val));
|
||||
|
||||
val = 1;
|
||||
setsockopt(tsl->fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
|
||||
|
||||
|
||||
tsl->start(tsl->fd, tsl->opaque, &tsl->source);
|
||||
free(tsl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void *
|
||||
tcp_server_loop(void *aux)
|
||||
{
|
||||
int r, i;
|
||||
struct epoll_event ev[1];
|
||||
tcp_server_t *ts;
|
||||
tcp_server_launch_t *tsl;
|
||||
pthread_attr_t attr;
|
||||
pthread_t tid;
|
||||
socklen_t slen;
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
while(1) {
|
||||
r = epoll_wait(tcp_server_epoll_fd, ev, sizeof(ev) / sizeof(ev[0]), -1);
|
||||
if(r == -1) {
|
||||
perror("tcp_server: epoll_wait");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
for(i = 0; i < r; i++) {
|
||||
ts = ev[i].data.ptr;
|
||||
|
||||
if(ev[i].events & EPOLLHUP) {
|
||||
close(ts->serverfd);
|
||||
free(ts);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ev[i].events & EPOLLIN) {
|
||||
tsl = malloc(sizeof(tcp_server_launch_t));
|
||||
tsl->start = ts->start;
|
||||
tsl->opaque = ts->opaque;
|
||||
slen = sizeof(struct sockaddr_in);
|
||||
|
||||
tsl->fd = accept(ts->serverfd,
|
||||
(struct sockaddr *)&tsl->source, &slen);
|
||||
if(tsl->fd == -1) {
|
||||
perror("accept");
|
||||
free(tsl);
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
pthread_create(&tid, &attr, tcp_server_start, tsl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void *
|
||||
tcp_server_create(int port, tcp_server_callback_t *start, void *opaque)
|
||||
{
|
||||
int fd, x;
|
||||
struct epoll_event e;
|
||||
tcp_server_t *ts;
|
||||
struct sockaddr_in s;
|
||||
int one = 1;
|
||||
memset(&e, 0, sizeof(e));
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(fd == -1)
|
||||
return NULL;
|
||||
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.sin_family = AF_INET;
|
||||
s.sin_port = htons(port);
|
||||
|
||||
x = bind(fd, (struct sockaddr *)&s, sizeof(s));
|
||||
if(x < 0) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
listen(fd, 1);
|
||||
|
||||
ts = malloc(sizeof(tcp_server_t));
|
||||
ts->serverfd = fd;
|
||||
ts->start = start;
|
||||
ts->opaque = opaque;
|
||||
|
||||
|
||||
e.events = EPOLLIN;
|
||||
e.data.ptr = ts;
|
||||
|
||||
epoll_ctl(tcp_server_epoll_fd, EPOLL_CTL_ADD, fd, &e);
|
||||
printf("Adding fd %d, listening on port %d\n", fd, port);
|
||||
return ts;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
tcp_server_init(void)
|
||||
{
|
||||
pthread_t tid;
|
||||
|
||||
tcp_server_epoll_fd = epoll_create(10);
|
||||
pthread_create(&tid, NULL, tcp_server_loop, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
|
22
tcp.h
22
tcp.h
|
@ -21,6 +21,9 @@
|
|||
|
||||
#include <libhts/htsbuf.h>
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
typedef enum {
|
||||
TCP_CONNECT,
|
||||
TCP_DISCONNECT,
|
||||
|
@ -52,7 +55,7 @@ typedef struct tcp_session {
|
|||
/* These are only used when we spawn as a client */
|
||||
|
||||
int tcp_enabled;
|
||||
dtimer_t tcp_timer;
|
||||
// dtimer_t tcp_timer;
|
||||
char *tcp_name;
|
||||
int tcp_port;
|
||||
char *tcp_hostname;
|
||||
|
@ -98,4 +101,21 @@ void tcp_set_hostname(tcp_session_t *ses, const char *hostname);
|
|||
|
||||
int tcp_send_msg(tcp_session_t *ses, int hiprio, void *data, size_t len);
|
||||
|
||||
#endif
|
||||
|
||||
void tcp_server_init(void);
|
||||
|
||||
typedef void (tcp_server_callback_t)(int fd, void *opaque,
|
||||
struct sockaddr_in *source);
|
||||
|
||||
void *tcp_server_create(int port, tcp_server_callback_t *start, void *opaque);
|
||||
|
||||
int tcp_read_line(int fd, char *buf, const size_t bufsize,
|
||||
htsbuf_queue_t *spill);
|
||||
|
||||
int tcp_read_data(int fd, char *buf, const size_t bufsize,
|
||||
htsbuf_queue_t *spill);
|
||||
|
||||
int tcp_write_queue(int fd, htsbuf_queue_t *q);
|
||||
|
||||
#endif /* TCP_H_ */
|
||||
|
|
243
transports.c
243
transports.c
|
@ -31,20 +31,13 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
#include "teletext.h"
|
||||
#include "transports.h"
|
||||
#include "subscriptions.h"
|
||||
#include "tsdemux.h"
|
||||
|
||||
#include "v4l.h"
|
||||
#include "iptv_input.h"
|
||||
#include "psi.h"
|
||||
#include "buffer.h"
|
||||
#include "channels.h"
|
||||
|
@ -56,45 +49,40 @@
|
|||
|
||||
static struct th_transport_list transporthash[TRANSPORT_HASH_WIDTH];
|
||||
|
||||
static void transport_data_timeout(void *aux, int64_t now);
|
||||
//static void transport_data_timeout(void *aux, int64_t now);
|
||||
|
||||
//static dtimer_t transport_monitor_timer;
|
||||
|
||||
//static const char *transport_settings_path(th_transport_t *t);
|
||||
//static void transport_monitor(void *aux, int64_t now);
|
||||
|
||||
void
|
||||
transport_stop(th_transport_t *t, int flush_subscriptions)
|
||||
|
||||
/**
|
||||
* Transport lock must be held
|
||||
*/
|
||||
static void
|
||||
transport_stop(th_transport_t *t)
|
||||
{
|
||||
th_subscription_t *s;
|
||||
th_descrambler_t *td;
|
||||
th_stream_t *st;
|
||||
th_pkt_t *pkt;
|
||||
|
||||
if(flush_subscriptions) {
|
||||
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
|
||||
subscription_stop(s);
|
||||
} else {
|
||||
if(LIST_FIRST(&t->tht_subscriptions))
|
||||
return;
|
||||
}
|
||||
|
||||
dtimer_disarm(&t->tht_receive_timer);
|
||||
|
||||
// dtimer_disarm(&t->tht_receive_timer);
|
||||
|
||||
// dtimer_disarm(&transport_monitor_timer, transport_monitor, t, 1);
|
||||
|
||||
t->tht_stop_feed(t);
|
||||
|
||||
while((td = LIST_FIRST(&t->tht_descramblers)) != NULL)
|
||||
td->td_stop(td);
|
||||
td->td_stop(td);
|
||||
|
||||
t->tht_tt_commercial_advice = COMMERCIAL_UNKNOWN;
|
||||
|
||||
assert(LIST_FIRST(&t->tht_muxers) == NULL);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Clean up each stream
|
||||
*/
|
||||
|
||||
LIST_FOREACH(st, &t->tht_streams, st_link) {
|
||||
|
||||
if(st->st_parser != NULL)
|
||||
|
@ -144,13 +132,87 @@ transport_stop(th_transport_t *t, int flush_subscriptions)
|
|||
/* Flush framestore */
|
||||
|
||||
while((pkt = TAILQ_FIRST(&st->st_pktq)) != NULL)
|
||||
pkt_unstore(pkt);
|
||||
pkt_unstore(st, pkt);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
remove_subscriber(th_subscription_t *s)
|
||||
{
|
||||
s->ths_callback(s, TRANSPORT_UNAVAILABLE, s->ths_opaque);
|
||||
LIST_REMOVE(s, ths_transport_link);
|
||||
s->ths_transport = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Remove the given subscriber from the transport
|
||||
*
|
||||
* if s == NULL all subscribers will be removed
|
||||
*
|
||||
* Global lock must be held
|
||||
*/
|
||||
void
|
||||
transport_remove_subscriber(th_transport_t *t, th_subscription_t *s)
|
||||
{
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(s == NULL) {
|
||||
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
|
||||
remove_subscriber(s);
|
||||
} else {
|
||||
remove_subscriber(s);
|
||||
}
|
||||
|
||||
if(LIST_FIRST(&t->tht_subscriptions) == NULL)
|
||||
transport_stop(t);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
transport_link_muxer(th_transport_t *t, th_muxer_t *tm)
|
||||
{
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if(tm->tm_transport != NULL) {
|
||||
assert(tm->tm_transport == t);
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&t->tht_delivery_mutex);
|
||||
LIST_INSERT_HEAD(&t->tht_muxers, tm, tm_transport_link);
|
||||
pthread_mutex_unlock(&t->tht_delivery_mutex);
|
||||
tm->tm_transport = t;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
transport_unlink_muxer(th_muxer_t *tm)
|
||||
{
|
||||
th_transport_t *t = tm->tm_transport;
|
||||
|
||||
if(t == NULL)
|
||||
return;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
pthread_mutex_lock(&t->tht_delivery_mutex);
|
||||
LIST_REMOVE(tm, tm_transport_link);
|
||||
pthread_mutex_unlock(&t->tht_delivery_mutex);
|
||||
tm->tm_transport = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int
|
||||
|
@ -160,6 +222,8 @@ transport_start(th_transport_t *t, unsigned int weight, int force_start)
|
|||
AVCodec *c;
|
||||
enum CodecID id;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
assert(t->tht_runstatus != TRANSPORT_RUNNING);
|
||||
|
||||
if(t->tht_start_feed(t, weight, TRANSPORT_RUNNING, force_start))
|
||||
|
@ -205,8 +269,8 @@ transport_start(th_transport_t *t, unsigned int weight, int force_start)
|
|||
}
|
||||
}
|
||||
|
||||
cwc_transport_start(t);
|
||||
dtimer_arm(&t->tht_receive_timer, transport_data_timeout, t, 4);
|
||||
// cwc_transport_start(t);
|
||||
// dtimer_arm(&t->tht_receive_timer, transport_data_timeout, t, 4);
|
||||
transport_signal_status(t, TRANSPORT_STATUS_STARTING);
|
||||
return 0;
|
||||
}
|
||||
|
@ -286,6 +350,8 @@ transport_find(channel_t *ch, unsigned int weight)
|
|||
th_transport_t *t, **vec;
|
||||
int cnt = 0, i;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
/* First, sort all transports in order */
|
||||
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_ch_link)
|
||||
|
@ -326,22 +392,9 @@ transport_find(channel_t *ch, unsigned int weight)
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
transport_flush_subscribers(th_transport_t *t)
|
||||
{
|
||||
th_subscription_t *s;
|
||||
|
||||
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
|
||||
subscription_stop(s);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
transport_compute_weight(struct th_transport_list *head)
|
||||
{
|
||||
|
@ -349,6 +402,8 @@ transport_compute_weight(struct th_transport_list *head)
|
|||
th_subscription_t *s;
|
||||
int w = 0;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
LIST_FOREACH(t, head, tht_active_link) {
|
||||
LIST_FOREACH(s, &t->tht_subscriptions, ths_transport_link) {
|
||||
if(s->ths_weight > w)
|
||||
|
@ -360,76 +415,6 @@ transport_compute_weight(struct th_transport_list *head)
|
|||
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
static void
|
||||
transport_monitor(void *aux, int64_t now)
|
||||
{
|
||||
th_transport_t *t = aux;
|
||||
int v;
|
||||
|
||||
dtimer_arm(&transport_monitor_timer, transport_monitor, t, 1);
|
||||
|
||||
if(t->tht_status == TRANSPORT_IDLE)
|
||||
return;
|
||||
|
||||
if(t->tht_monitor_suspend > 0) {
|
||||
t->tht_monitor_suspend--;
|
||||
return;
|
||||
}
|
||||
|
||||
v = avgstat_read_and_expire(&t->tht_rate, dispatch_clock) * 8 / 1000 / 10;
|
||||
|
||||
if(v < 500) {
|
||||
switch(t->tht_rate_error_log_limiter) {
|
||||
case 0:
|
||||
syslog(LOG_WARNING, "\"%s\" on \"%s\", very low bitrate: %d kb/s",
|
||||
t->tht_channel->ch_name, t->tht_name, v);
|
||||
/* FALLTHRU */
|
||||
case 1 ... 9:
|
||||
t->tht_rate_error_log_limiter++;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(t->tht_rate_error_log_limiter) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
syslog(LOG_NOTICE, "\"%s\" on \"%s\", bitrate ok (%d kb/s)",
|
||||
t->tht_channel->ch_name, t->tht_name, v);
|
||||
/* FALLTHRU */
|
||||
default:
|
||||
t->tht_rate_error_log_limiter--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
v = avgstat_read(&t->tht_cc_errors, 10, dispatch_clock);
|
||||
if(v > t->tht_cc_error_log_limiter) {
|
||||
t->tht_cc_error_log_limiter += v * 3;
|
||||
|
||||
syslog(LOG_WARNING, "\"%s\" on \"%s\", "
|
||||
"%.2f continuity errors/s (10 sec average)",
|
||||
t->tht_channel->ch_name, t->tht_name, (float)v / 10.);
|
||||
|
||||
} else if(v == 0) {
|
||||
switch(t->tht_cc_error_log_limiter) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
syslog(LOG_NOTICE, "\"%s\" on \"%s\", no continuity errors",
|
||||
t->tht_channel->ch_name, t->tht_name);
|
||||
/* FALLTHRU */
|
||||
default:
|
||||
t->tht_cc_error_log_limiter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Timer that fires if transport is not receiving any data
|
||||
*/
|
||||
|
@ -439,7 +424,7 @@ transport_data_timeout(void *aux, int64_t now)
|
|||
th_transport_t *t = aux;
|
||||
transport_signal_status(t, TRANSPORT_STATUS_NO_INPUT);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Destroy a transport
|
||||
|
@ -448,8 +433,14 @@ void
|
|||
transport_destroy(th_transport_t *t)
|
||||
{
|
||||
th_stream_t *st;
|
||||
th_subscription_t *s;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
dtimer_disarm(&t->tht_receive_timer);
|
||||
while((s = LIST_FIRST(&t->tht_subscriptions)) != NULL)
|
||||
remove_subscriber(s);
|
||||
|
||||
//dtimer_disarm(&t->tht_receive_timer);
|
||||
|
||||
free((void *)t->tht_name);
|
||||
|
||||
|
@ -460,11 +451,9 @@ transport_destroy(th_transport_t *t)
|
|||
|
||||
LIST_REMOVE(t, tht_mux_link);
|
||||
LIST_REMOVE(t, tht_hash_link);
|
||||
|
||||
transport_flush_subscribers(t);
|
||||
|
||||
if(t->tht_runstatus != TRANSPORT_IDLE)
|
||||
transport_stop(t, 0);
|
||||
transport_stop(t);
|
||||
|
||||
|
||||
free(t->tht_identifier);
|
||||
|
@ -477,7 +466,7 @@ transport_destroy(th_transport_t *t)
|
|||
free(st);
|
||||
}
|
||||
|
||||
serviceprobe_delete(t);
|
||||
abort();// serviceprobe_delete(t);
|
||||
|
||||
free(t);
|
||||
}
|
||||
|
@ -491,6 +480,10 @@ transport_create(const char *identifier, int type, int source_type)
|
|||
{
|
||||
unsigned int hash = tvh_strhash(identifier, TRANSPORT_HASH_WIDTH);
|
||||
th_transport_t *t = calloc(1, sizeof(th_transport_t));
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
pthread_mutex_init(&t->tht_delivery_mutex, NULL);
|
||||
t->tht_identifier = strdup(identifier);
|
||||
t->tht_type = type;
|
||||
t->tht_source_type = source_type;
|
||||
|
@ -507,6 +500,9 @@ transport_find_by_identifier(const char *identifier)
|
|||
{
|
||||
th_transport_t *t;
|
||||
unsigned int hash = tvh_strhash(identifier, TRANSPORT_HASH_WIDTH);
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
LIST_FOREACH(t, &transporthash[hash], tht_hash_link)
|
||||
if(!strcmp(t->tht_identifier, identifier))
|
||||
break;
|
||||
|
@ -524,6 +520,8 @@ transport_add_stream(th_transport_t *t, int pid, tv_streamtype_t type)
|
|||
th_stream_t *st;
|
||||
int i = 0;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
LIST_FOREACH(st, &t->tht_streams, st_link) {
|
||||
i++;
|
||||
if(pid != -1 && st->st_pid == pid)
|
||||
|
@ -553,6 +551,9 @@ transport_add_stream(th_transport_t *t, int pid, tv_streamtype_t type)
|
|||
void
|
||||
transport_map_channel(th_transport_t *t, channel_t *ch)
|
||||
{
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
assert(t->tht_ch == NULL);
|
||||
|
||||
if(ch == NULL) {
|
||||
|
@ -579,6 +580,8 @@ transport_map_channel(th_transport_t *t, channel_t *ch)
|
|||
void
|
||||
transport_unmap_channel(th_transport_t *t)
|
||||
{
|
||||
lock_assert(&global_lock);
|
||||
|
||||
t->tht_ch = NULL;
|
||||
LIST_REMOVE(t, tht_ch_link);
|
||||
}
|
||||
|
@ -635,7 +638,7 @@ transport_signal_status(th_transport_t *t, int newstatus)
|
|||
return;
|
||||
|
||||
t->tht_last_status = newstatus;
|
||||
notify_transprot_status_change(t);
|
||||
//notify_transprot_status_change(t);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -35,8 +35,6 @@
|
|||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
#include "teletext.h"
|
||||
|
@ -130,7 +128,7 @@ ts_recv_packet0(th_transport_t *t, th_stream_t *st, uint8_t *tsb)
|
|||
break;
|
||||
|
||||
case HTSTV_TELETEXT:
|
||||
teletext_input(t, tsb);
|
||||
// teletext_input(t, tsb);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
149
tvhead.h
149
tvhead.h
|
@ -20,16 +20,34 @@
|
|||
#define TV_HEAD_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/time.h>
|
||||
#include <libhts/htsq.h>
|
||||
#include <libhts/htstv.h>
|
||||
#include <libhts/htscfg.h>
|
||||
#include <libhts/avg.h>
|
||||
#include <libhts/hts_strtab.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libhts/redblack.h>
|
||||
#include <linux/dvb/frontend.h>
|
||||
|
||||
extern pthread_mutex_t global_lock;
|
||||
|
||||
static inline void
|
||||
lock_assert0(pthread_mutex_t *l, const char *file, int line)
|
||||
{
|
||||
if(pthread_mutex_trylock(l) == EBUSY)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "Mutex not held at %s:%d\n", file, line);
|
||||
abort();
|
||||
}
|
||||
|
||||
#define lock_assert(l) lock_assert0(l, __FILE__, __LINE__)
|
||||
|
||||
|
||||
/*
|
||||
* Commercial status
|
||||
*/
|
||||
|
@ -40,35 +58,29 @@ typedef enum {
|
|||
} th_commercial_advice_t;
|
||||
|
||||
|
||||
/**
|
||||
* Auxiliary data for plugins
|
||||
*/
|
||||
typedef struct pluginaux {
|
||||
LIST_ENTRY(pluginaux) pa_link;
|
||||
struct th_plugin *pa_plugin;
|
||||
} pluginaux_t;
|
||||
|
||||
LIST_HEAD(pluginaux_list, pluginaux);
|
||||
|
||||
|
||||
/*
|
||||
* Dispatch timer
|
||||
* global timer
|
||||
*/
|
||||
typedef void (dti_callback_t)(void *opaque, int64_t now);
|
||||
|
||||
typedef struct dtimer {
|
||||
LIST_ENTRY(dtimer) dti_link;
|
||||
dti_callback_t *dti_callback;
|
||||
void *dti_opaque;
|
||||
int64_t dti_expire;
|
||||
} dtimer_t;
|
||||
|
||||
typedef void (gti_callback_t)(void *opaque);
|
||||
|
||||
typedef struct gtimer {
|
||||
LIST_ENTRY(gtimer) gti_link;
|
||||
gti_callback_t *gti_callback;
|
||||
void *gti_opaque;
|
||||
time_t gti_expire;
|
||||
} gtimer_t;
|
||||
|
||||
void gtimer_arm(gtimer_t *gti, gti_callback_t *callback, void *opaque,
|
||||
int delta);
|
||||
|
||||
void gtimer_disarm(gtimer_t *gti);
|
||||
|
||||
|
||||
/*
|
||||
* List / Queue header declarations
|
||||
*/
|
||||
|
||||
LIST_HEAD(th_subscription_list, th_subscription);
|
||||
RB_HEAD(channel_tree, channel);
|
||||
TAILQ_HEAD(channel_queue, channel);
|
||||
|
@ -132,21 +144,10 @@ typedef struct th_v4l_adapter {
|
|||
|
||||
} th_v4l_adapter_t;
|
||||
|
||||
|
||||
/*
|
||||
* Mux instance modes
|
||||
*/
|
||||
typedef enum {
|
||||
TDMI_IDLE, /* Not tuned */
|
||||
TDMI_RUNNING, /* Tuned with a subscriber */
|
||||
TDMI_IDLESCAN, /* Just scanning */
|
||||
} tdmi_state_t;
|
||||
|
||||
/*
|
||||
* DVB Mux instance
|
||||
*/
|
||||
typedef struct th_dvb_mux_instance {
|
||||
int tdmi_refcnt;
|
||||
|
||||
enum {
|
||||
TDMI_QUICKSCAN_NONE,
|
||||
|
@ -170,13 +171,15 @@ typedef struct th_dvb_mux_instance {
|
|||
time_t tdmi_time;
|
||||
LIST_HEAD(, th_dvb_table) tdmi_tables;
|
||||
|
||||
pthread_mutex_t tdmi_table_lock;
|
||||
|
||||
tdmi_state_t tdmi_state;
|
||||
|
||||
dtimer_t tdmi_initial_scan_timer;
|
||||
const char *tdmi_status;
|
||||
char *tdmi_last_status; /* For notification updates */
|
||||
enum {
|
||||
TDMI_FE_UNKNOWN,
|
||||
TDMI_FE_NO_SIGNAL,
|
||||
TDMI_FE_FAINT_SIGNAL,
|
||||
TDMI_FE_BAD_SIGNAL,
|
||||
TDMI_FE_CONTANT_FEC,
|
||||
TDMI_FE_BURSTY_FEC,
|
||||
TDMI_FE_OK,
|
||||
} tdmi_fe_status;
|
||||
|
||||
int tdmi_quality;
|
||||
|
||||
|
@ -197,27 +200,6 @@ typedef struct th_dvb_mux_instance {
|
|||
} th_dvb_mux_instance_t;
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
typedef struct th_dvb_table {
|
||||
LIST_ENTRY(th_dvb_table) tdt_link;
|
||||
char *tdt_name;
|
||||
void *tdt_handle;
|
||||
struct th_dvb_mux_instance *tdt_tdmi;
|
||||
void *tdt_opaque;
|
||||
void (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
|
||||
uint8_t tableid, void *opaque);
|
||||
|
||||
int tdt_fd;
|
||||
struct dmx_sct_filter_params *tdt_fparams;
|
||||
|
||||
int tdt_quickreq;
|
||||
int tdt_count;
|
||||
|
||||
} th_dvb_table_t;
|
||||
|
||||
|
||||
/*
|
||||
* DVB Adapter (one of these per physical adapter)
|
||||
*/
|
||||
|
@ -230,16 +212,13 @@ typedef struct th_dvb_adapter {
|
|||
|
||||
th_dvb_mux_instance_t *tda_mux_current;
|
||||
|
||||
int tda_table_epollfd;
|
||||
|
||||
const char *tda_rootpath;
|
||||
char *tda_identifier;
|
||||
uint32_t tda_autodiscovery;
|
||||
char *tda_displayname;
|
||||
|
||||
pthread_mutex_t tda_lock;
|
||||
pthread_cond_t tda_cond;
|
||||
TAILQ_HEAD(, dvb_fe_cmd) tda_fe_cmd_queue;
|
||||
int tda_fe_errors;
|
||||
|
||||
int tda_fe_fd;
|
||||
int tda_type;
|
||||
struct dvb_frontend_info *tda_fe_info;
|
||||
|
@ -248,10 +227,13 @@ typedef struct th_dvb_adapter {
|
|||
|
||||
char *tda_dvr_path;
|
||||
|
||||
gtimer_t tda_mux_scanner_timer;
|
||||
|
||||
pthread_mutex_t tda_delivery_mutex;
|
||||
struct th_transport_list tda_transports; /* Currently bound transports */
|
||||
|
||||
dtimer_t tda_fec_monitor_timer;
|
||||
dtimer_t tda_mux_scanner_timer;
|
||||
gtimer_t tda_fe_monitor_timer;
|
||||
int tda_fe_monitor_hold;
|
||||
|
||||
} th_dvb_adapter_t;
|
||||
|
||||
|
@ -445,6 +427,8 @@ typedef struct th_transport {
|
|||
|
||||
int (*tht_quality_index)(struct th_transport *t);
|
||||
|
||||
pthread_mutex_t tht_delivery_mutex;
|
||||
|
||||
struct th_muxer_list tht_muxers; /* muxers */
|
||||
|
||||
/*
|
||||
|
@ -528,9 +512,6 @@ typedef struct th_transport {
|
|||
/**
|
||||
* Last known status (or error)
|
||||
*/
|
||||
|
||||
dtimer_t tht_receive_timer; /* we use this timer to trig when a transport
|
||||
does not receive any data at all */
|
||||
int tht_last_status;
|
||||
|
||||
#define TRANSPORT_STATUS_UNKNOWN 0
|
||||
|
@ -587,14 +568,11 @@ typedef struct th_pkt {
|
|||
uint8_t pkt_frametype;
|
||||
uint8_t pkt_commercial;
|
||||
|
||||
th_stream_t *pkt_stream;
|
||||
|
||||
int64_t pkt_dts;
|
||||
int64_t pkt_pts;
|
||||
int pkt_duration;
|
||||
int pkt_refcount;
|
||||
|
||||
|
||||
th_storage_t *pkt_storage;
|
||||
TAILQ_ENTRY(th_pkt) pkt_disk_link;
|
||||
int pkt_storage_offset;
|
||||
|
@ -669,7 +647,7 @@ typedef struct th_muxstream {
|
|||
int64_t tms_delta;
|
||||
int64_t tms_mux_offset;
|
||||
|
||||
dtimer_t tms_mux_timer;
|
||||
//dtimer_t tms_mux_timer;
|
||||
|
||||
/* MPEG TS multiplex stuff */
|
||||
|
||||
|
@ -704,7 +682,7 @@ typedef struct th_muxer {
|
|||
th_mux_newpkt_t *tm_new_pkt;
|
||||
|
||||
LIST_ENTRY(th_muxer) tm_transport_link;
|
||||
int tm_linked;
|
||||
th_transport_t *tm_transport;
|
||||
|
||||
int64_t tm_offset;
|
||||
|
||||
|
@ -780,6 +758,9 @@ typedef struct tt_decoder {
|
|||
*/
|
||||
typedef struct channel {
|
||||
|
||||
int ch_refcount;
|
||||
int ch_zombie;
|
||||
|
||||
RB_ENTRY(channel) ch_name_link;
|
||||
char *ch_name;
|
||||
char *ch_sname;
|
||||
|
@ -913,15 +894,9 @@ typedef struct event {
|
|||
|
||||
} event_t;
|
||||
|
||||
config_entry_t *find_mux_config(const char *muxtype, const char *muxname);
|
||||
char *utf8toprintable(const char *in);
|
||||
char *utf8tofilename(const char *in);
|
||||
const char *htstvstreamtype2txt(tv_streamtype_t s);
|
||||
uint32_t tag_get(void);
|
||||
extern const char *settings_dir;
|
||||
FILE *settings_open_for_write(const char *name);
|
||||
FILE *settings_open_for_read(const char *name);
|
||||
extern const char *sys_warning;
|
||||
|
||||
static inline unsigned int tvh_strhash(const char *s, unsigned int mod)
|
||||
{
|
||||
|
@ -945,4 +920,16 @@ void tvhlog(int severity, const char *subsys, const char *fmt, ...);
|
|||
#define LOG_INFO 6 /* informational */
|
||||
#define LOG_DEBUG 7 /* debug-level messages */
|
||||
|
||||
|
||||
static inline int64_t
|
||||
getclock_hires(void)
|
||||
{
|
||||
int64_t now;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
now = (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
|
||||
return now;
|
||||
}
|
||||
|
||||
#endif /* TV_HEAD_H */
|
||||
|
|
179
webui/comet.c
179
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);
|
||||
}
|
||||
|
|
|
@ -75,10 +75,9 @@ extjs_exec(htsbuf_queue_t *hq, const char *fmt, ...)
|
|||
* PVR info, deliver info about the given PVR entry
|
||||
*/
|
||||
static int
|
||||
extjs_root(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
extjs_root(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
|
||||
#define EXTJSPATH "static/extjs"
|
||||
|
||||
|
@ -141,7 +140,7 @@ extjs_root(http_connection_t *hc, http_reply_t *hr,
|
|||
"<div id=\"systemlog\"></div>\n"
|
||||
"</body></html>\n",
|
||||
htsversion);
|
||||
http_output_html(hc, hr);
|
||||
http_output_html(hc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -150,10 +149,9 @@ extjs_root(http_connection_t *hc, http_reply_t *hr,
|
|||
*
|
||||
*/
|
||||
static int
|
||||
extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
dtable_t *dt;
|
||||
htsmsg_t *out = NULL, *in, *array;
|
||||
|
||||
|
@ -166,6 +164,8 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
|
|||
|
||||
in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if(!strcmp(op, "create")) {
|
||||
out = dtable_record_create(dt);
|
||||
|
||||
|
@ -177,20 +177,24 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
|
|||
|
||||
} else if(!strcmp(op, "update")) {
|
||||
if(in == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
goto bad;
|
||||
|
||||
dtable_record_update_by_array(dt, in);
|
||||
|
||||
} else if(!strcmp(op, "delete")) {
|
||||
if(in == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
goto bad;
|
||||
|
||||
dtable_record_delete_by_array(dt, in);
|
||||
|
||||
} else {
|
||||
bad:
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
if(in != NULL)
|
||||
htsmsg_destroy(in);
|
||||
|
||||
|
@ -198,7 +202,7 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
|
|||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
}
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -207,10 +211,9 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
|
|||
*
|
||||
*/
|
||||
static int
|
||||
extjs_chlist(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
extjs_chlist(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
htsmsg_t *out, *array, *c;
|
||||
channel_t *ch;
|
||||
|
||||
|
@ -218,6 +221,8 @@ extjs_chlist(http_connection_t *hc, http_reply_t *hr,
|
|||
|
||||
array = htsmsg_create_array();
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
|
||||
c = htsmsg_create();
|
||||
htsmsg_add_str(c, "name", ch->ch_name);
|
||||
|
@ -225,11 +230,13 @@ extjs_chlist(http_connection_t *hc, http_reply_t *hr,
|
|||
htsmsg_add_msg(array, NULL, c);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
htsmsg_add_msg(out, "entries", array);
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -261,10 +268,9 @@ extjs_dvbtree_node(htsmsg_t *array, int leaf, const char *id, const char *name,
|
|||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
extjs_dvbtree(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
const char *s = http_arg_get(&hc->hc_req_args, "node");
|
||||
htsmsg_t *out = NULL;
|
||||
char buf[200];
|
||||
|
@ -276,6 +282,7 @@ extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
|
|||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
out = htsmsg_create_array();
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if(!strcmp(s, "root")) {
|
||||
/**
|
||||
|
@ -299,7 +306,7 @@ extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
|
|||
|
||||
extjs_dvbtree_node(out, 0,
|
||||
tdmi->tdmi_identifier, buf, "DVB Mux",
|
||||
tdmi->tdmi_last_status,
|
||||
dvb_mux_status(tdmi),
|
||||
tdmi->tdmi_quality, "mux");
|
||||
}
|
||||
} else if((tdmi = dvb_mux_find_by_identifier(s)) != NULL) {
|
||||
|
@ -317,9 +324,10 @@ extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
|
|||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -328,10 +336,9 @@ extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
|
|||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvbnetworks(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
const char *s = http_arg_get(&hc->hc_req_args, "node");
|
||||
const char *a = http_arg_get(&hc->hc_req_args, "adapter");
|
||||
th_dvb_adapter_t *tda;
|
||||
|
@ -340,14 +347,20 @@ extjs_dvbnetworks(http_connection_t *hc, http_reply_t *hr,
|
|||
if(s == NULL || a == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
if((tda = dvb_adapter_find_by_identifier(a)) == NULL)
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if((tda = dvb_adapter_find_by_identifier(a)) == NULL) {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
out = dvb_mux_preconf_get_node(tda->tda_type, s);
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -372,21 +385,22 @@ json_single_record(htsmsg_t *rec, const char *root)
|
|||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvbadapter(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
const char *s = http_arg_get(&hc->hc_req_args, "adapterId");
|
||||
const char *op = http_arg_get(&hc->hc_req_args, "op");
|
||||
th_dvb_adapter_t *tda = s ? dvb_adapter_find_by_identifier(s) : NULL;
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
th_transport_t *t;
|
||||
//th_dvb_mux_instance_t *tdmi;
|
||||
// th_transport_t *t;
|
||||
|
||||
htsmsg_t *r, *out;
|
||||
|
||||
if(tda == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if(!strcmp(op, "load")) {
|
||||
r = htsmsg_create();
|
||||
htsmsg_add_str(r, "id", tda->tda_identifier);
|
||||
|
@ -418,24 +432,27 @@ extjs_dvbadapter(http_connection_t *hc, http_reply_t *hr,
|
|||
|
||||
tvhlog(LOG_NOTICE, "web interface",
|
||||
"Service probe started on \"%s\"", tda->tda_displayname);
|
||||
|
||||
#if 0
|
||||
RB_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
|
||||
LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
|
||||
serviceprobe_add(t);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
out = htsmsg_create();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -537,20 +554,24 @@ build_transport_msg(th_transport_t *t)
|
|||
*
|
||||
*/
|
||||
static int
|
||||
extjs_channel(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
extjs_channel(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
const char *s = http_arg_get(&hc->hc_req_args, "chid");
|
||||
const char *op = http_arg_get(&hc->hc_req_args, "op");
|
||||
channel_t *ch = s ? channel_find_by_identifier(atoi(s)) : NULL;
|
||||
channel_t *ch;
|
||||
channel_t *ch2;
|
||||
th_transport_t *t;
|
||||
int reloadchlist = 0;
|
||||
htsmsg_t *out, *array, *r;
|
||||
|
||||
if(ch == NULL)
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
ch = s ? channel_find_by_identifier(atoi(s)) : NULL;
|
||||
if(ch == NULL) {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if(!strcmp(op, "load")) {
|
||||
r = htsmsg_create();
|
||||
|
@ -611,13 +632,17 @@ extjs_channel(http_connection_t *hc, http_reply_t *hr,
|
|||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
response:
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -84,10 +84,10 @@ eventcmp(const void *A, const void *B)
|
|||
* on if it is a full blown browser or just some mobile app
|
||||
*/
|
||||
static int
|
||||
page_simple(http_connection_t *hc, http_reply_t *hr,
|
||||
page_simple(http_connection_t *hc,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
const char *s = http_arg_get(&hc->hc_req_args, "s");
|
||||
struct event_list events;
|
||||
event_t *e, **ev;
|
||||
|
@ -217,7 +217,7 @@ page_simple(http_connection_t *hc, http_reply_t *hr,
|
|||
}
|
||||
|
||||
htsbuf_qprintf(hq, "</body></html>");
|
||||
http_output_html(hc, hr);
|
||||
http_output_html(hc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -225,10 +225,9 @@ page_simple(http_connection_t *hc, http_reply_t *hr,
|
|||
* Event info, deliver info about the given event
|
||||
*/
|
||||
static int
|
||||
page_einfo(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
page_einfo(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
event_t *e;
|
||||
struct tm a, b;
|
||||
time_t stop;
|
||||
|
@ -299,7 +298,7 @@ page_einfo(http_connection_t *hc, http_reply_t *hr,
|
|||
|
||||
htsbuf_qprintf(hq, "<hr><a href=\"/simple.html\">New search</a><br>");
|
||||
htsbuf_qprintf(hq, "</body></html>");
|
||||
http_output_html(hc, hr);
|
||||
http_output_html(hc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -308,10 +307,9 @@ page_einfo(http_connection_t *hc, http_reply_t *hr,
|
|||
* PVR info, deliver info about the given PVR entry
|
||||
*/
|
||||
static int
|
||||
page_pvrinfo(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
page_pvrinfo(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
struct tm a, b;
|
||||
time_t stop;
|
||||
pvr_rec_t *pvrr = NULL;
|
||||
|
@ -324,7 +322,7 @@ page_pvrinfo(http_connection_t *hc, http_reply_t *hr,
|
|||
if((http_arg_get(&hc->hc_req_args, "clear")) != NULL) {
|
||||
pvr_clear(pvrr);
|
||||
pvrr = NULL;
|
||||
http_redirect(hc, hr, "/simple.html");
|
||||
http_redirect(hc, "/simple.html");
|
||||
return 0;
|
||||
} else if((http_arg_get(&hc->hc_req_args, "cancel")) != NULL) {
|
||||
pvr_abort(pvrr);
|
||||
|
@ -380,7 +378,7 @@ page_pvrinfo(http_connection_t *hc, http_reply_t *hr,
|
|||
|
||||
htsbuf_qprintf(hq, "<hr><a href=\"/simple.html\">New search</a><br>");
|
||||
htsbuf_qprintf(hq, "</body></html>");
|
||||
http_output_html(hc, hr);
|
||||
http_output_html(hc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -21,15 +21,21 @@
|
|||
|
||||
#include <libhts/htsmsg.h>
|
||||
|
||||
void webui_start(void);
|
||||
void webui_init(void);
|
||||
|
||||
void simpleui_start(void);
|
||||
|
||||
void extjs_start(void);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void comet_init(void);
|
||||
|
||||
void comet_mailbox_add_message(htsmsg_t *m);
|
||||
|
||||
void comet_flush(void);
|
||||
|
||||
|
||||
#endif /* WEBUI_H_ */
|
||||
|
|
Loading…
Add table
Reference in a new issue