Compare commits

..

1 commit

Author SHA1 Message Date
Adam Sutton
69ac1e378d mpegts: add service and mux cleaning code 2014-11-16 19:53:25 +00:00
240 changed files with 6582 additions and 22108 deletions

View file

@ -39,7 +39,7 @@
],
"buildcmd": [
"./configure --disable-dvbscan --enable-libffmpeg_static --disable-libffmpeg_static_x264 --enable-hdhomerun_static",
"./configure --disable-dvbscan --enable-libffmpeg_static --enable-hdhomerun_static",
"make -j ${PARALLEL}"
]
},
@ -82,7 +82,7 @@
],
"buildcmd": [
"./configure --disable-dvbscan --enable-libffmpeg_static --disable-libffmpeg_static_x264 --enable-hdhomerun_static",
"./configure --disable-dvbscan --enable-libffmpeg_static --enable-hdhomerun_static",
"make -j ${PARALLEL}"
]
}

View file

@ -61,7 +61,7 @@ LDFLAGS += -L${ROOTDIR}/libav_static/build/ffmpeg/lib -Wl,-Bstatic \
-lavresample -lswresample -lswscale \
-lavutil -lavformat -lavcodec -lavutil \
-lvorbisenc -lvorbis -logg -lx264 -lvpx \
-Wl,-Bdynamic -ldl
-Wl,-Bdynamic
endif
ifeq ($(CONFIG_HDHOMERUN_STATIC),yes)
CFLAGS += -I${ROOTDIR}/libhdhomerun_static
@ -153,18 +153,11 @@ SRCS = src/version.c \
src/esfilter.c \
src/intlconv.c \
src/profile.c \
src/bouquet.c \
src/lock.c
src/bouquet.c
SRCS-${CONFIG_UPNP} += \
src/upnp.c
# SATIP Server
SRCS-${CONFIG_SATIP_SERVER} += \
src/satip/server.c \
src/satip/rtsp.c \
src/satip/rtp.c
SRCS += \
src/api.c \
src/api/api_status.c \
@ -225,15 +218,12 @@ SRCS += src/muxer.c \
# Optional code
#
# MPEGTS core, order by usage (psi lib, tsdemux)
# MPEGTS core
SRCS-$(CONFIG_MPEGTS) += \
src/descrambler/descrambler.c \
src/descrambler/caclient.c \
src/input/mpegts.c \
src/input/mpegts/mpegts_pid.c \
src/input/mpegts/mpegts_input.c \
src/input/mpegts/tsdemux.c \
src/input/mpegts/dvb_psi_lib.c \
src/input/mpegts/mpegts_network.c \
src/input/mpegts/mpegts_mux.c \
src/input/mpegts/mpegts_service.c \
@ -242,8 +232,9 @@ SRCS-$(CONFIG_MPEGTS) += \
src/input/mpegts/dvb_charset.c \
src/input/mpegts/dvb_psi.c \
src/input/mpegts/fastscan.c \
src/input/mpegts/tsdemux.c \
src/input/mpegts/mpegts_mux_sched.c \
src/input/mpegts/mpegts_network_scan.c
src/input/mpegts/mpegts_network_scan.c \
# MPEGTS DVB
SRCS-${CONFIG_MPEGTS_DVB} += \
@ -269,7 +260,7 @@ SRCS-${CONFIG_LINUXDVB} += \
src/input/mpegts/linuxdvb/linuxdvb_rotor.c \
src/input/mpegts/linuxdvb/linuxdvb_en50494.c
# SATIP Client
# SATIP
SRCS-${CONFIG_SATIP_CLIENT} += \
src/input/mpegts/satip/satip.c \
src/input/mpegts/satip/satip_frontend.c \
@ -289,8 +280,6 @@ SRCS-${CONFIG_IPTV} += \
src/input/mpegts/iptv/iptv_service.c \
src/input/mpegts/iptv/iptv_http.c \
src/input/mpegts/iptv/iptv_udp.c \
src/input/mpegts/iptv/iptv_rtsp.c \
src/input/mpegts/iptv/iptv_pipe.c
# TSfile
SRCS-$(CONFIG_TSFILE) += \
@ -338,15 +327,6 @@ SRCS-${CONFIG_CAPMT} += \
SRCS-${CONFIG_CONSTCW} += \
src/descrambler/constcw.c
# DVB CAM
SRCS-${CONFIG_LINUXDVB_CA} += \
src/input/mpegts/linuxdvb/linuxdvb_ca.c \
src/descrambler/dvbcam.c
# TSDEBUGCW
SRCS-${CONFIG_TSDEBUG} += \
src/descrambler/tsdebugcw.c
# FFdecsa
ifneq ($(CONFIG_DVBCSA),yes)
FFDECSA-$(CONFIG_CAPMT) = yes
@ -476,8 +456,7 @@ ${BUILDDIR}/libffmpeg_stamp: ${ROOTDIR}/libav_static/build/ffmpeg/lib/libavcodec
@touch $@
${ROOTDIR}/libav_static/build/ffmpeg/lib/libavcodec.a:
CONFIG_LIBFFMPEG_STATIC_X264=$(CONFIG_LIBFFMPEG_STATIC_X264) \
$(MAKE) -f Makefile.ffmpeg build
$(MAKE) -f Makefile.ffmpeg build
# Static HDHOMERUN library

View file

@ -48,12 +48,10 @@ export PATH := $(LIBAVDIR)/build/ffmpeg/bin:$(PATH)
ECFLAGS = -I$(LIBAVDIR)/build/ffmpeg/include
ELIBS = -L$(LIBAVDIR)/build/ffmpeg/lib -ldl
CONFIGURE = PKG_CONFIG=/tmp/nobin/pkg-config ./configure
FFMPEG = ffmpeg-2.6.2
FFMPEG = ffmpeg-2.4.2
FFMPEG_TB = $(FFMPEG).tar.bz2
FFMPEG_URL = http://ffmpeg.org/releases/$(FFMPEG_TB)
FFMPEG_SHA1 = 65470c9b967485f72f81758a7bad44cf7a1763db
FFMPEG_SHA1 = 8fedc6f235d8510f716bca1784faa8cbe5d9cf78
EXTLIBS = libx264 libvorbis libvpx
COMPONENTS = avutil avformat avcodec swresample swscale avresample
@ -72,10 +70,10 @@ LIBVORBIS_TB = $(LIBVORBIS).tar.gz
LIBVORBIS_URL = http://downloads.xiph.org/releases/vorbis/$(LIBVORBIS_TB)
LIBVORBIS_SHA1 = 1602716c187593ffe4302124535240cec2079df3
LIBX264 = x264-snapshot-20141218-2245
LIBX264 = x264-snapshot-20141012-2245
LIBX264_TB = $(LIBX264).tar.bz2
LIBX264_URL = http://ftp.via.ecp.fr/pub/videolan/x264/snapshots/$(LIBX264_TB)
LIBX264_SHA1 = 24a3b20e2c49a112e40df9f64885cbd81250298a
LIBX264_URL = ftp://ftp.videolan.org/pub/x264/snapshots/$(LIBX264_TB)
LIBX264_SHA1 = 392cd6b0e723192e009d731fe44db01b55c97fba
LIBVPX = libvpx-v1.3.0
LIBVPX_TB = $(LIBVPX).tar.bz2
@ -101,10 +99,10 @@ $(LIBAVDIR)/$(YASM)/.tvh_download:
$(LIBAVDIR)/$(YASM)/.tvh_build: \
$(LIBAVDIR)/$(YASM)/.tvh_download
cd $(LIBAVDIR)/$(YASM) && $(CONFIGURE) \
cd $(LIBAVDIR)/$(YASM) && ./configure \
--prefix=/ffmpeg
DESTDIR=$(LIBAVDIR)/build \
$(MAKE) -C $(LIBAVDIR)/$(YASM) install
make -C $(LIBAVDIR)/$(YASM) install
@touch $@
#
@ -119,12 +117,12 @@ $(LIBAVDIR)/$(LIBOGG)/.tvh_download:
$(LIBAVDIR)/$(LIBOGG)/.tvh_build: \
$(LIBAVDIR)/$(YASM)/.tvh_build \
$(LIBAVDIR)/$(LIBOGG)/.tvh_download
cd $(LIBAVDIR)/$(LIBOGG) && $(CONFIGURE) \
cd $(LIBAVDIR)/$(LIBOGG) && ./configure \
--prefix=/ffmpeg \
--enable-static \
--disable-shared
DESTDIR=$(LIBAVDIR)/build \
$(MAKE) -C $(LIBAVDIR)/$(LIBOGG) install
make -C $(LIBAVDIR)/$(LIBOGG) install
@touch $@
$(LIBAVDIR)/$(LIBVORBIS)/.tvh_download: \
@ -137,23 +135,25 @@ $(LIBAVDIR)/$(LIBVORBIS)/.tvh_build: \
$(LIBAVDIR)/$(LIBVORBIS)/.tvh_download \
$(LIBAVDIR)/$(YASM)/.tvh_build \
$(LIBAVDIR)/$(LIBOGG)/.tvh_build
cd $(LIBAVDIR)/$(LIBVORBIS) && $(CONFIGURE) \
cd $(LIBAVDIR)/$(LIBVORBIS) && ./configure \
--prefix=/ffmpeg \
--enable-static \
--disable-shared \
--with-ogg=$(LIBAVDIR)/build/ffmpeg
DESTDIR=$(LIBAVDIR)/build \
$(MAKE) -C $(LIBAVDIR)/$(LIBVORBIS) install
make -C $(LIBAVDIR)/$(LIBVORBIS) install
@touch $@
#
# libx264
#
ifneq (yes,$(CONFIG_LIBFFMPEG_STATIC_X264))
ARCH = $(shell $(CC) -dumpmachine | cut -d '-' -f 1)
ifneq (,$(filter i386 i486 i586 i686 pentium,$(ARCH)))
$(LIBAVDIR)/$(LIBX264)/.tvh_download:
@echo "***** LIBX264 STATIC BUILD IS DISABLED, USING INSTALLED PACKAGE *****"
@echo "***** PLEASE FIX !!!! libx264 build for i386 *****"
@mkdir -p $(LIBAVDIR)/$(LIBX264)
@touch $@
@ -172,7 +172,7 @@ $(LIBAVDIR)/$(LIBX264)/.tvh_download:
$(LIBAVDIR)/$(LIBX264)/.tvh_build: \
$(LIBAVDIR)/$(LIBX264)/.tvh_download \
$(LIBAVDIR)/$(YASM)/.tvh_build
cd $(LIBAVDIR)/$(LIBX264) && $(CONFIGURE) \
cd $(LIBAVDIR)/$(LIBX264) && ./configure \
--prefix=/ffmpeg \
--enable-static \
--disable-shared \
@ -183,7 +183,7 @@ $(LIBAVDIR)/$(LIBX264)/.tvh_build: \
--disable-gpac \
--disable-lsmash
DESTDIR=$(LIBAVDIR)/build \
$(MAKE) -C $(LIBAVDIR)/$(LIBX264) install
make -C $(LIBAVDIR)/$(LIBX264) install
@touch $@
endif
@ -201,12 +201,12 @@ $(LIBAVDIR)/$(LIBVPX)/.tvh_download:
$(LIBAVDIR)/$(LIBVPX)/.tvh_build: \
$(LIBAVDIR)/$(LIBVPX)/.tvh_download \
$(LIBAVDIR)/$(YASM)/.tvh_build
cd $(LIBAVDIR)/$(LIBVPX) && $(CONFIGURE) \
cd $(LIBAVDIR)/$(LIBVPX) && ./configure \
--prefix=/ffmpeg \
--enable-static \
--disable-shared
DIST_DIR=$(LIBAVDIR)/build/ffmpeg \
$(MAKE) -C $(LIBAVDIR)/$(LIBVPX) install
make -C $(LIBAVDIR)/$(LIBVPX) install
@touch $@
#
@ -225,7 +225,7 @@ $(LIBAVDIR)/$(FFMPEG)/.tvh_build: \
$(LIBAVDIR)/$(LIBX264)/.tvh_build \
$(LIBAVDIR)/$(LIBVPX)/.tvh_build \
$(LIBAVDIR)/$(FFMPEG)/.tvh_download
cd $(LIBAVDIR)/$(FFMPEG) && $(CONFIGURE) \
cd $(LIBAVDIR)/$(FFMPEG) && ./configure \
--prefix=/ffmpeg \
--disable-all \
--enable-static \
@ -240,7 +240,7 @@ $(LIBAVDIR)/$(FFMPEG)/.tvh_build: \
$(foreach muxer,$(MUXERS),--enable-muxer=$(muxer)) \
$(foreach bsf,$(BSFS),--enable-bsf=$(bsf))
DESTDIR=$(LIBAVDIR)/build \
$(MAKE) -C $(LIBAVDIR)/$(FFMPEG) install
make -C $(LIBAVDIR)/$(FFMPEG) install
@touch $@
.PHONY: static_libav_clean

View file

@ -45,10 +45,10 @@ LIBHDHRDIR = $(ROOTDIR)/libhdhomerun_static
export PATH := $(LIBHDHRDIR)/build/bin:$(PATH)
LIBHDHR = libhdhomerun_20150406
LIBHDHR = libhdhomerun_20140604
LIBHDHR_TB = $(LIBHDHR).tgz
LIBHDHR_URL = http://download.silicondust.com/hdhomerun/$(LIBHDHR_TB)
LIBHDHR_SHA1 = f0d5da744d981a80becea6cc862b5e2519e1c3c6
LIBHDHR_SHA1 = b8d910a5721c484a30b81662fd6991e3546ed67c
.PHONY: build
build: $(LIBHDHRDIR)/$(LIBHDHR)/.tvh_build

View file

@ -1,6 +1,6 @@
Tvheadend
========================================
(c) 2006 - 2015 Tvheadend Foundation CIC
====================================
(c) 2006 - 2014 Tvheadend Foundation CIC
What it is
@ -10,22 +10,14 @@ Tvheadend is a TV streaming server and digital video recorder.
It supports the following inputs:
* DVB-C(2)
* DVB-C
* DVB-T(2)
* DVB-S(2)
* ATSC
* SAT>IP
* HDHomeRun
* IPTV
* UDP
* HTTP
It support the following outputs:
* HTTP
* HTSP (own protocol)
* SAT>IP
How to build for Linux
----------------------

84
configure vendored
View file

@ -10,7 +10,6 @@
# ###########################################################################
ROOTDIR=$(cd "$(dirname "$0")"; pwd)
test -z "$PKG_CONFIG" && PKG_CONFIG=pkg-config
#
# Options
@ -20,8 +19,8 @@ OPTIONS=(
"cwc:yes"
"capmt:yes"
"constcw:yes"
"v4l:no"
"linuxdvb:yes"
"satip_server:yes"
"satip_client:yes"
"hdhomerun_client:auto"
"hdhomerun_static:no"
@ -35,7 +34,6 @@ OPTIONS=(
"zlib:auto"
"libav:auto"
"libffmpeg_static:no"
"libffmpeg_static_x264:yes"
"inotify:auto"
"epoll:auto"
"uriparser:auto"
@ -43,12 +41,9 @@ OPTIONS=(
"tvhcsa:auto"
"bundle:no"
"dvbcsa:no"
"dvben50221:auto"
"kqueue:no"
"dbus_1:auto"
"android:no"
"tsdebug:no"
"gtimer_check:no"
)
#
@ -165,17 +160,6 @@ int test(void)
}
'
check_cc_snippet stime '
#include <time.h>
#define TEST test
int test(void)
{
time_t t = 1;
stime(&t);
return 0;
}
'
check_cc_snippet recvmmsg '
#define _GNU_SOURCE
#include <stdlib.h>
@ -188,18 +172,6 @@ int test(void)
}
'
check_cc_snippet sendmmsg '
#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/socket.h>
#define TEST test
int test(void)
{
sendmmsg(0, NULL, 0, 0);
return 0;
}
'
check_cc_snippet libiconv '
#include <iconv.h>
int test(void)
@ -209,23 +181,6 @@ int test(void)
}
' -liconv
if enabled_or_auto dvben50221; then
check_cc_snippet libdvben50221 '
#include <libdvben50221/en50221_session.h>
#define TEST test
int test(void)
{
struct en50221_transport_layer *tl = en50221_tl_create(5, 32);
return 0;
}
' '-ldvben50221 -ldvbapi -lucsi'
if enabled libdvben50221; then
enable dvben50221
else
disable dvben50221
fi
fi
#
# Python
#
@ -301,13 +256,6 @@ if enabled_or_auto zlib; then
fi
fi
#
# SAT>IP server
#
if enabled_or_auto satip_server; then
enable upnp
fi
#
# SAT>IP client
#
@ -359,8 +307,7 @@ else
if enabled_or_auto libav; then
has_libav=true
ffmpeg=$(${PKG_CONFIG} --modversion libavcodec | cut -d '.' -f 3)
test -z "$ffmpeg" && ffmpeg=0
ffmpeg=$(pkg-config --modversion libavcodec | cut -d '.' -f 3)
printf "$TAB" "checking for ffmpeg libraries ..."
if test $ffmpeg -lt 100; then
ffmpeg=
@ -462,14 +409,6 @@ if enabled cwc || enabled capmt || enabled constcw; then
fi
fi
#
# libdvben50221
#
if enabled libdvben50221; then
LDFLAGS="$LDFLAGS -ldvben50221 -ldvbapi -lucsi"
enable linuxdvb_ca
fi
#
# Icon caching
#
@ -505,19 +444,23 @@ if [ ${PLATFORM} = "freebsd" ] || [ ${PLATFORM} = "darwin" ]; then
fi
#
# MPEGTS support
# MPEGTS/PS support
#
disable mpegts
disable mpegps
disable mpegts_dvb
if enabled linuxdvb || enabled iptv || enabled tsfile || enabled satip_client || \
enabled hdhomerun_client || enabled satip_server;
if enabled linuxdvb || enabled iptv || enabled tsfile || enabled satip_client || enabled hdhomerun_client;
then
enable mpegts
fi
if enabled linuxdvb || enabled satip_client || enabled hdhomerun_client || enabled satip_server; then
if enabled linuxdvb || enabled satip_client || enabled hdhomerun_client; then
enable mpegts_dvb
fi
if enabled v4l; then
enable mpegps
fi
#
# DBus
#
@ -529,13 +472,6 @@ if enabled_or_auto dbus_1; then
fi
fi
#
# TSDebug
#
if enabled_or_auto tsdebug; then
enable mpegts_dvb
fi
# ###########################################################################
# Write config

File diff suppressed because it is too large Load diff

View file

@ -1,257 +0,0 @@
[ 0, 0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
16,
17,
18,
19,
20,
16,
22,
23,
23,
16,
19,
23,
23,
23,
18,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
32,
33,
34,
35,
36,
144,
35,
144,
32,
145,
115,
146,
118,
148,
144,
32,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
48,
49,
51,
51,
48,
48,
48,
48,
48,
48,
48,
48,
49,
48,
51,
50,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
69,
64,
64,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
80,
81,
82,
83,
84,
85,
80,
80,
84,
85,
80,
80,
80,
80,
80,
80,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
96,
97,
98,
99,
100,
101,
102,
96,
96,
96,
96,
96,
96,
96,
96,
96,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
123,
0,
0,
0,
112,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]

View file

@ -5,7 +5,6 @@
"nid": 4096,
"tsid": 17,
"sid": 17008,
"bouquetid": 0,
"channel" : [
17
],

View file

@ -5,7 +5,6 @@
"nid": 64511,
"tsid": 5800,
"sid": 3635,
"bouquetid": 0,
"channel" : [
17
],

View file

@ -1,24 +0,0 @@
{
"name": "Sky NZ",
"dict": "skynz",
"genre": "skynz",
"nid": 169,
"tsid": 3,
"sid": 9003,
"bouquetid": 0,
"channel" : [
17
],
"title": [
48, 49, 50, 51, 52, 53, 54, 55
],
"summary": [
64, 65, 66, 67, 68, 69, 70, 71
],
"season_num": [
" *\\(S ?([0-9]+),? Ep? ?[0-9]+\\)"
],
"episode_num": [
" *\\(S ?[0-9]+,? Ep? ?([0-9]+)\\)"
]
}

View file

@ -5,7 +5,6 @@
"nid": 2,
"tsid": 2004,
"sid": 4152,
"bouquetid": 4101,
"channel" : [
17
],

View file

@ -1,155 +1,50 @@
[
{
"name": "Canal Digitaal SD",
"name": "Canal Digitaal",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 900
},
{
"name": "Canal Digitaal HD",
"name": "TV Vlaanderen",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 901
},
{
"name": "TV Vlaanderen SD",
"name": "TéléSAT",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 910
},
{
"name": "TV Vlaanderen HD",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 911
},
{
"name": "TéléSAT SD",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 920
},
{
"name": "TéléSAT HD",
"name": "Mobistar NL",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 921
},
{
"name": "Mobistar NL Astra1",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 930
},
{
"name": "Mobistar FR Astra1",
"name": "Mobistar FR",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 940
},
{
"name": "AustriaSat Astra1",
"name": "AustriaSat",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 950
},
{
"name": "Canal Digitaal HD",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 901
},
{
"name": "TV Vlaanderen HD",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 911
},
{
"name": "TéléSAT HD",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 921
},
{
"name": "Mobistar NL Astra3",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 930
},
{
"name": "Mobistar FR Astra3",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 940
},
{
"name": "AustriaSat Astra3",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 950
},
{
"name": "Skylink: Czech Republic",
"position": 235,
"frequency": 12070000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 30
},
{
"name": "Skylink: Slovak Republic",
"position": 235,
"frequency": 12070000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 31
}
]

View file

@ -1,638 +0,0 @@
[
{
"name": "NSS 9",
"pos": -1770
},
{
"name": "AMC 8",
"pos": -1390
},
{
"name": "AMC 7",
"pos": -1370
},
{
"name": "AMC 10",
"pos": -1350
},
{
"name": "Galaxy 15",
"pos": -1330
},
{
"name": "AMC 11",
"pos": -1310
},
{
"name": "Ciel 2/Galaxy 12",
"pos": -1290
},
{
"name": "Galaxy 13/Horizons 1",
"pos": -1270
},
{
"name": "AMC 21/Galaxy 14",
"pos": -1250
},
{
"name": "Galaxy 18",
"pos": -1230
},
{
"name": "EchoStar 9/Galaxy 23",
"pos": -1210
},
{
"name": "Anik F3/DirecTV 7S/EchoStar 14",
"pos": -1190
},
{
"name": "Eutelsat 117 West A",
"pos": -1168
},
{
"name": "Eutelsat 115 West A",
"pos": -1149
},
{
"name": "Eutelsat 113 West A",
"pos": -1130
},
{
"name": "Anik F2",
"pos": -1111
},
{
"name": "DirecTV 5/EchoStar 10/11",
"pos": -1100
},
{
"name": "Anik F1R/G1",
"pos": -1073
},
{
"name": "AMC 15/18",
"pos": -1050
},
{
"name": "AMC 1/SES 3/Spaceway 1 &amp; DirecTV 10/12",
"pos": -1030
},
{
"name": "DirecTV 4S/8/SES 1",
"pos": -1010
},
{
"name": "DirecTV 14/Galaxy 16/Spaceway 2 &amp; DirecTV 11",
"pos": -992
},
{
"name": "Galaxy 19",
"pos": -970
},
{
"name": "Galaxy 3C/Spaceway 3",
"pos": -950
},
{
"name": "Galaxy 25",
"pos": -931
},
{
"name": "Galaxy 17/Nimiq 6",
"pos": -910
},
{
"name": "Galaxy 28",
"pos": -890
},
{
"name": "TKSat 1",
"pos": -872
},
{
"name": "SES 2",
"pos": -870
},
{
"name": "AMC 16",
"pos": -850
},
{
"name": "Brasilsat B4",
"pos": -840
},
{
"name": "AMC 9",
"pos": -830
},
{
"name": "Nimiq 4",
"pos": -820
},
{
"name": "Simon Bolivar",
"pos": -780
},
{
"name": "EchoStar 8/QuetzSat 1",
"pos": -770
},
{
"name": "Star One C3",
"pos": -750
},
{
"name": "Nimiq 5",
"pos": -727
},
{
"name": "AMC 6",
"pos": -720
},
{
"name": "Star One C2",
"pos": -700
},
{
"name": "AMC 4",
"pos": -670
},
{
"name": "Star One C1",
"pos": -650
},
{
"name": "Telstar 14R",
"pos": -630
},
{
"name": "EchoStar 12/16",
"pos": -615
},
{
"name": "Amazonas 2/3/4A",
"pos": -610
},
{
"name": "Intelsat 21",
"pos": -580
},
{
"name": "Amazonas 1/Galaxy 11/Intelsat 805",
"pos": -555
},
{
"name": "Intelsat 23",
"pos": -530
},
{
"name": "Intelsat 1R",
"pos": -500
},
{
"name": "NSS 806",
"pos": -475
},
{
"name": "Intelsat 14",
"pos": -450
},
{
"name": "Intelsat 9/11",
"pos": -431
},
{
"name": "SES 6",
"pos": -405
},
{
"name": "NSS 10/Telstar 11N",
"pos": -375
},
{
"name": "Intelsat 903",
"pos": -345
},
{
"name": "Hylas 1",
"pos": -335
},
{
"name": "Intelsat 25",
"pos": -315
},
{
"name": "Hispasat 1D/1E",
"pos": -300
},
{
"name": "Intelsat 907",
"pos": -275
},
{
"name": "Intelsat 905",
"pos": -245
},
{
"name": "SES 4",
"pos": -220
},
{
"name": "NSS 7",
"pos": -200
},
{
"name": "Intelsat 901",
"pos": -180
},
{
"name": "Telstar 12",
"pos": -150
},
{
"name": "Express A4",
"pos": -140
},
{
"name": "Eutelsat 12 West A",
"pos": -125
},
{
"name": "Express AM44",
"pos": -110
},
{
"name": "Eutelsat 8 West A",
"pos": -80
},
{
"name": "Eutelsat 7 West A/Eutelsat 8 West C/Nilesat 102/201",
"pos": -73
},
{
"name": "Eutelsat 5 West A",
"pos": -50
},
{
"name": "Amos 2/3",
"pos": -40
},
{
"name": "Thor 5/6/Intelsat 10-02",
"pos": -8
},
{
"name": "Eutelsat 3B/Rascom QAF 1R",
"pos": 31
},
{
"name": "Astra 4A/SES 5",
"pos": 49
},
{
"name": "Eutelsat 7A/7B",
"pos": 70
},
{
"name": "Eutelsat 9A/Ka-Sat 9A",
"pos": 90
},
{
"name": "Eutelsat 10A",
"pos": 100
},
{
"name": "Eutelsat Hot Bird 13B/13C/13D",
"pos": 130
},
{
"name": "Eutelsat 16A",
"pos": 160
},
{
"name": "Amos 5",
"pos": 170
},
{
"name": "Astra 1KR/1L/1M/1N",
"pos": 192
},
{
"name": "Arabsat 5C",
"pos": 200
},
{
"name": "Eutelsat 21B",
"pos": 215
},
{
"name": "Astra 3B",
"pos": 235
},
{
"name": "Eutelsat 25B/Es'hail 1",
"pos": 255
},
{
"name": "Badr 4/5/6",
"pos": 260
},
{
"name": "Astra 2A/2C/2E/2F/Eutelsat 28A",
"pos": 282
},
{
"name": "Arabsat 5A",
"pos": 305
},
{
"name": "Eutelsat 31A",
"pos": 308
},
{
"name": "Astra 5B",
"pos": 315
},
{
"name": "Eutelsat 33B/Intelsat 28",
"pos": 330
},
{
"name": "Eutelsat 36A/36B",
"pos": 360
},
{
"name": "Paksat 1R",
"pos": 380
},
{
"name": "Hellas Sat 2",
"pos": 390
},
{
"name": "Türksat 2A/3A/4A",
"pos": 420
},
{
"name": "Intelsat 12",
"pos": 450
},
{
"name": "AzerSpace 1/Africasat 1a",
"pos": 460
},
{
"name": "Intelsat 10",
"pos": 475
},
{
"name": "Afghansat 1",
"pos": 480
},
{
"name": "Yamal 202",
"pos": 490
},
{
"name": "NSS 5",
"pos": 505
},
{
"name": "Y1A",
"pos": 525
},
{
"name": "Express AM22",
"pos": 530
},
{
"name": "G-Sat 8/Yamal 402",
"pos": 549
},
{
"name": "Express AT1",
"pos": 560
},
{
"name": "NSS 12",
"pos": 570
},
{
"name": "Intelsat 904",
"pos": 600
},
{
"name": "Intelsat 902",
"pos": 620
},
{
"name": "Intelsat 906",
"pos": 642
},
{
"name": "Intelsat 17",
"pos": 660
},
{
"name": "Intelsat 20",
"pos": 685
},
{
"name": "Eutelsat 70B",
"pos": 705
},
{
"name": "Intelsat 22",
"pos": 721
},
{
"name": "Insat 3C/4CR",
"pos": 740
},
{
"name": "ABS 2",
"pos": 750
},
{
"name": "Apstar 7",
"pos": 765
},
{
"name": "Thaicom 5/6",
"pos": 785
},
{
"name": "G-Sat 10/Insat 4A",
"pos": 830
},
{
"name": "Horizons 2/Intelsat 15",
"pos": 851
},
{
"name": "KazSat 2",
"pos": 865
},
{
"name": "ChinaSat 12",
"pos": 875
},
{
"name": "ST 2",
"pos": 880
},
{
"name": "Yamal 300K/401",
"pos": 900
},
{
"name": "Measat 3/3a/3b",
"pos": 915
},
{
"name": "ChinaSat 9",
"pos": 922
},
{
"name": "Insat 3A/4B",
"pos": 935
},
{
"name": "NSS 6/SES 8",
"pos": 950
},
{
"name": "Express AM33",
"pos": 965
},
{
"name": "AsiaSat 5",
"pos": 1005
},
{
"name": "Express AM3",
"pos": 1030
},
{
"name": "AsiaSat 7/8",
"pos": 1055
},
{
"name": "NSS 11/SES 7/Telkom 1",
"pos": 1082
},
{
"name": "BSAT 3A/3C/JCSAT 110R/N-Sat 110",
"pos": 1100
},
{
"name": "ChinaSat 10",
"pos": 1105
},
{
"name": "Koreasat 5/Palapa D",
"pos": 1130
},
{
"name": "ChinaSat 6B",
"pos": 1155
},
{
"name": "ABS 7/Koreasat 6",
"pos": 1160
},
{
"name": "AsiaSat 3S/Telkom 2",
"pos": 1180
},
{
"name": "Thaicom 4",
"pos": 1195
},
{
"name": "AsiaSat 4",
"pos": 1222
},
{
"name": "JCSAT 4B",
"pos": 1240
},
{
"name": "ChinaSat 6A",
"pos": 1250
},
{
"name": "JCSAT 3A",
"pos": 1280
},
{
"name": "JCSAT 5A/Vinasat 1/2",
"pos": 1320
},
{
"name": "Apstar 6",
"pos": 1340
},
{
"name": "Telstar 18",
"pos": 1380
},
{
"name": "Express AM5/AT2",
"pos": 1400
},
{
"name": "Superbird C2",
"pos": 1440
},
{
"name": "JCSAT 1B",
"pos": 1500
},
{
"name": "Optus D2",
"pos": 1520
},
{
"name": "JCSAT 2A",
"pos": 1540
},
{
"name": "Optus C1/D3",
"pos": 1560
},
{
"name": "ABS 6",
"pos": 1590
},
{
"name": "Optus D1",
"pos": 1600
},
{
"name": "Superbird B2",
"pos": 1620
},
{
"name": "Optus 10/B3",
"pos": 1640
},
{
"name": "Intelsat 19",
"pos": 1660
},
{
"name": "Intelsat 8",
"pos": 1690
},
{
"name": "Eutelsat 172A",
"pos": 1720
},
{
"name": "Intelsat 18",
"pos": 1800
}
]

View file

@ -55,7 +55,29 @@ The columns have the following functions:
<dd>
IPv4 prefix for matching based on source IP address.
If set to 0.0.0.0/0 it will match everything.
The multiple networks can be delimited using comma or semicolon.
<dt><b>Streaming</b>
<dd>
Enables access to streaming functionality. This permission is enough to stream over HTSP to VLC, Showtime and similar.
<dt><b>Advanced Streaming</b>
<dd>
Enables access to advanced streaming function for HTTP - like direct
service or whole MPEG-TS stream (mux)..
<dt><b>Streaming Profile</b>
<dd>
Specify a streaming profile to be used when this user logs in; use the (default) stream if not specified.
<dt><b>Video Recorder</b>
<dd>
Enables access to all video recording functions. This also include administration of the auto recordings.
<dt><b>DVR Config Profile</b>
<dd>
If set, the user will only be able to use the DVR config profile
equal to this value.
Note that this field is unset when the DVR Config Profile is removed.
<dt><b>Web interface</b>
<dd>
@ -64,51 +86,11 @@ The columns have the following functions:
<dt><b>Admin</b>
<dd>
Enables access to the Configuration tab.
<dt><b>Streaming</b>
<dd>
Enables access to streaming functionality for HTTP (web).
<dt><b>Advanced Streaming</b>
<dd>
Enables access to advanced streaming function for HTTP (web) - like direct
service or whole MPEG-TS stream (mux)..
<dt><b>HTSP Streaming</b>
<dd>
Enables access to streaming for the HTSP protocol (Movian, Kodi etc.).
<dt><b>Streaming Profile</b>
<dd>
Specify a streaming profile to be used when this user logs in; use the (default) stream if not specified.
<dt><b>Limit Connections</b>
<dd>
If set, this will limit the number of concurrent streaming connections a user is permitted to have. 0=disabled
<dt><b>Video Recorder</b>
<dd>
Enables access to all video recording functions. This also include administration of the auto recordings.
<dt><b>HTSP DVR</b>
<dd>
Enables access to video recording functions for the HTSP protocol (Movian, Kodi etc.).
<dt><b>All DVR</b>
<dd>
Enable to access to DVR entries created by other users (read-only).
<dt><b>All DVR (rw)</b>
<dd>
Enable to access to DVR entries created by other users with the ability to
remove the DVR entries.
<dt><b>DVR Config Profile</b>
<dd>
If set, the user will only be able to use the DVR config profile
equal to this value.
Note that this field is unset when the DVR Config Profile is removed.
<dt><b>Min Channel Num</b>
<dd>
If non-zero, this sets the lower limit of the channels accessible by a user, i.e. the user will only be able to access channels where the channel number is equal to or greater than this value.
@ -138,7 +120,7 @@ Let's also take a look at an example:
<p>
First line gives clients originating from 192.168.0.0 - 192.168.0.255 network
access to streaming functions. Typically you would use this for your
local media players at home (All though Movian can prompt for username & password
local media players at home (All though Showtime can prompt for username & password
in its HTSP client)
<p>
The second line adds a user with world wide access who might want to modify

View file

@ -1,10 +1,5 @@
<div class="hts-doc-text">
<p>
To use bouquets, ensure to add and scan all available muxes using
the predefined muxes or manual configuration.
</p>
<p>
The bouquets are obtained automatically from the DVB source during
the mux scan period. Note that bouquets may use more muxes and only

View file

@ -155,18 +155,6 @@
pmt_mode = 4
</dl>
<dt>mode 5 (new OSCam since revision 10087)
<dd>Similar to mode 3 (TCP), but uses a new network protocol which also added
client/server greeting messages and protocol version information (to be able
to smoothly detect enhancements in the future).<br><br>
<b>This is currently the preferred mode (the other may be removed in future)!</b><br><br>
The following lines are required in <b>[dvbapi]</b> section of oscam.conf file:
<dl>
<dt>boxtype = pc<br>
pmt_mode = 4<br>
listen_port = 9000 # or your preferred port<br>
</dl>
<dt>Comment
<dd>Allows the administrator to set a comment only visible in this editor.
It does not serve any active purpose.

View file

@ -21,23 +21,6 @@
The columns have the following functions:
<dl>
<dt>Play
<dd>Direct play link using the HTTP streaming.
<dl>
<dt>URL to the stream using a channel name
<dd>http://<i>host:port</i>/play/stream/channelname/<i>channel_name</i>
<dt>URL to the stream using a channel number
<dd>http://<i>host:port</i>/play/stream/channelnumber/<i>channel_number</i>
</dl>
<dt>Enabled
<dd>Whether or not the mux is enabled and thus available.
<dd>Channel number. This is not used by Tvheadend internally, but rather
intended to be used by HTSP clients for mapping to remote control
buttons, presentation order, etc
<dt>Number
<dd>Channel number. This is not used by Tvheadend internally, but rather
intended to be used by HTSP clients for mapping to remote control
@ -53,12 +36,6 @@
display a direct link that can be used to open in preferred media
player.
<dt>Auto EPG Channel
<dd>Auto-link EPG channels from XMLTV and OpenTV EPG grabbers using
the channel name for matching. If you turn this option off, only
OTA EPG grabber will be used for this channel unless the EPG Grab
Source option (bellow) is not set manually.
<dt>EPG Grab Source
<dd>Name of the Internet-based EPG provider (typically XMLTV) channel
that should be used to update this channels EPG info.
@ -77,13 +54,10 @@
in the EPG if you have many channels. The tags are also presented
in a Media player.
<dt>User Icon
<dt>Icon
<dd>An URL pointing to an image representing the channel.
The icon URL will be set automatically when importing data from
XMLTV, when picon path is set or when channel icon path is set
in the general config. This field allows the user to edit it manually. The
reset icon action allows to re-set the automatic URL for
selected channel (e.g. after configuration change).
XMLTV. This field allows the user to edit it manually.
<dt>DVR Pre-Start
<dd>Allows the user to specify an amount of extra time that should

View file

@ -60,13 +60,8 @@
<dd>If checked, broadcasts with matching title and matching non-zero episode number
are considered duplicates.
<dt>EPG update window
<dd>Maximum difference between event start times when the EPG event is changed.
TVHeadend uses a fuzzy match logic (using title, start times,
duration, episode) to check when the event was changed.
<dt>Post-processor command
<dd>Command to run after finishing a recording. The command will be run in background and is executed even if a recording is aborted or an error occurred. Use the %e error formatting string to check for errors, the error string is "OK" if recording finished successfully.
<dd>Command to run after finishing a recording. The command will be run in background and is executed even if a recording is aborted or an error occurred. Use the %e error formatting string to check for errors, the error string is empty if recording finished successfully.
<br><br>
Support format strings:<br>
<table class="hts-doc-text" border="0">
@ -176,19 +171,10 @@
<dd>If checked, insert the episode number before the data and time rather than after (assumes <i>Include date</i>, <i>Include time</i> and <i>Include episode</i> options are set).
<dt>Remove all unsafe characters from filename
<dd>If checked, all characters that could possibly cause problems for filenaming will be replaced with '_'.<br>
Applies to characters:<br>
* not supported by Windows characters: <i>/ : \ < > | * ? ' "</i><br>
* control characters (ASCII code below 32)<br>
* control and national characters (ASCII code above 122)<br>
<dd>If checked, all characters that could possibly cause problems for filenaming will be removed.
<dt>Replace whitespace in title with '-'
<dd>If checked, whitespace characters (spaces and tabs) will be replaced with '-'.
<dt>Use Windows-compatible filenames
<dd>If checked:<br>
* special characters not supported by Windows like: <i>/ : \ < > | * ? ' "</i> will be replaced with '_'<br>
* trailing spaces ' ' and dots '.' will be removed
<dd>If checked, all whitespace characters will be replaced with '-'.
</dl>
Changes to any of these settings must be confirmed by pressing the 'Save configuration' button before taking effect.

View file

@ -1,7 +1,7 @@
<div class="hts-doc-text">
This table defines rules to filter and order the elementary streams
(PIDs) like video or audio from the input feed.
like video or audio from the input feed.
<p>
The execution order of commands is granted. It means that first rule
@ -90,20 +90,10 @@ The columns have the following functions:
<dt>USE
<dd>Use this elementary stream.
<dt>ONE_TIME
<dd>Use this elementary stream only one time per service type (like
video, audio, subtitles) and language. The first sucessfully
compared rule wins. For example, when one AC3 elementary stream
is marked to be used with 'eng' language and another rule with
the ONE_TIME action was matched, the new AC3 elementary stream
will not be added if the language for new AC3 elementary stream
is 'eng'. Note that the second rule might not have the language
filter (column) set.
<p>
For the CA filter, this rule means that the new CA elementary stream
is added only if another CA is not already used.
</p>
<dt>ONCE
<dd>Use this elementary stream only once per service type.
The language is distinguished, too.
The first successfully compared rule wins.
<dt>EXCLUSIVE
<dd>Use only this elementary stream. No other elementary streams

View file

@ -60,44 +60,16 @@
<dt>Update tolerance (milliseconds)</dt>
<dd>Only update the system clock (doesn't affect NTP driver) if the delta
between the system clock and DVB time is greater than this. This can help
stop excessive oscillations on the system clock.</dd>
stop horrible oscillations on the system clock.</dd>
<br><br>
<hr>
<b>Picon</b>
<hr>
<dd>Picons (from p ersonal icons) are collections of similar icons that can be automatically
matched against your channels based on a number of technical parameters that will uniquely
define a channel. The use of these parameters (e.g. mux, frequency, orbital position)
removes the ambiguity of using names - it's not case sensitive, it doesn't care if there
are spaces or not, and so on.
You can generate picons yourself from existing images, or you can usually find sets
pre-made on the Internet if you search for them. They're a good way to get large numbers
of icons matched quickly, and usually in a similar style (such as square, x * y pixels, with
a consistent highlight/reflection effect).</dd>
<dl>
<dt>Prefer picons over channel name:</dt>
<dd>If both a picon and a channel-specific (e.g. channelname.jpg) icon are defined, use the picon.</dd>
<dt>Channel icon path</dt>
<dd>Path to an icon for this channel. This can be named however you wish, as a local (file://) or remote (http://) image.
The following placeholders are available:<br>
<ul>
<li>%C - the transliterated channel name in ASCII (safe characters, no spaces etc.)</li>
<li>%c - the channel name (URL encoded ASCII)</li>
</ul>
Example: file:///tmp/icons/%C.png or http://example.com/%c.png</dd>
<dt>Picon path</dt>
<dd>Path to a directory (folder) containing your picon collection. This can be named however
you wish, as a local (file://) or remote (http://) location - however, remember that it's pointing
to a directory as the picon names are automatically generated from the service parameters
frequency, orbital position, etc.).<br>
Example: file:///home/hts/picons</dd>
</dl>
<br><br>
<hr>
<b>Transcoding</b>
<hr>
<dd>If enabled at build time (src/plumbing/transcoding.c), this allows you to switch transcoding support on and off.</dd>
<br><br>
<hr>
@ -131,72 +103,5 @@
</dl>
<br><br>
<hr>
<b>SAT&gt;IP Server</b>
<hr>
<dd>SAT&gt;IP Server is something like DVB network tuner. TVHeadend can
forward mpegts input streams including on-the-fly descramling to SAT&gt;IP
clients.</dd>
<dd>Only networks with the "SAT>IP Source" field set are exported
through the SAT&gt;IP protocol. This field is matched through the "src"
parameter asked from the SAT>IP client. Usually (and by default) this value is 1.
For satellite tuners, this value determines the satellite source (dish).
By specification position 1 = DiseqC AA, 2 = DiseqC AB, 3 = DiseqC BA,
4 = DiseqC BB, but any numbers may be used - depends on the SAT&gt;IP client.
Note that if you use a similar number for multiple networks, the first matched
network containing the mux with requested parameters will win
(also for unknown mux).</dd>
<dl>
<dt>RTSP Port
<dd>
Select RTSP port (TCP) for realtime commands from SAT&gt;IP clients. Usually
(as defined in the specification) this port is 554. But as extension,
TVHeadend can use any TCP port value (which is default 9983 for non-root
users). But the SAT&gt;IP client must allow to set this value (TVHeadend
client will obtain the RTSP port number automatically using the XML
description). If the RTSP port value is zero, the SAT&gt;IP server
functionality is not enabled.
<dt>Subscription Weight
<dd>
Subscription weight value. Default value is 100 (standard streaming). Note
that the default value for DVR is 300 (normal priority).
<dt>Descramble Services
<dd>
The maximum limit of services descrambled per a mux. If zero, the
descrambling functionality is disabled.
<dt>Muxes Handling
<dd>
When SAT&gt;IP client requests new mux configuration, tvheadend can handle it
in three ways. The auto (0) configuration means that if the mux does not exists,
a temporary mux is created and removed when the client closes the
connection. The keep (1) configuration will remember all successfuly scanned muxes.
The reject (2) configuration will reject unknown muxes.
<dt>Exported DVB-T/T2 Tuners
<dd>
Exported DVB-T/T2 tuners - streaming instances.
<dt>Exported DVB-S/S2 Tuners
<dd>
Exported DVB-S/S2 tuners - streaming instances.
<dt>Exported DVB-C/C2 Tuners
<dd>
Exported DVB-C/C2 tuners - streaming instances.
<dt>Exported ATSC/DVB-C(AnnexB) Tuners
<dd>
Exported ATSC/DVB-C(AnnexB) - streaming instances.
</dl>
</dl>
</div>

View file

@ -73,35 +73,6 @@
<dt>URL
<dd>Mux URL.
<dl>
<dt>udp://
<dd>Raw MPEG-TS UDP packets
<dt>rtp://
<dd>MPEG-TS UDP packets with RTP header
<dt>http://
<dd>HTTP stream (MPEG-TS)
<dt>https://
<dd>Secure HTTP stream (MPEG-TS)
<dt>pipe://
<dd>Read standard output from an external program.
If the program name does not have the first
character '/', the PATH environment variable
is used to find the program name in all
directories specified by PATH.
Additional arguments may be separated
using spaces. A raw MPEG-TS stream is
expected. The string ${service_name}
is substituted with the service name
field contents. The \ (backslash) character means
"take the next character asis" (usually
space or the backslash itself.
</dl>
<dt># Services
<dd>The number of services found on this mux.
@ -125,12 +96,5 @@
<dt>Streaming Priority
<dd>IPTV : The mux priority value for streamed channels through HTTP or HTSP (higher value = higher priority to use services from this mux). Value 0 means use the standard streaming network priority value.
<dt>Environment (pipe)
<dd>IPTV : List of environment variables for pipe (like PATH=/bin:/sbin)
separated by spaces. The backslash character is handled like
in URL.
<dt>Respawn (pipe)
<dd>IPTV : Respawn the executed process when it dies.
</dl>
</div>

View file

@ -55,11 +55,7 @@ Buttons have the following functions:
<dd>Whether automatic discovery is enabled for this network, i.e. whether Tvheadend looks for muxes or simply stays with the list of muxes as defined initially.
<p>
<dt><b>Skip initial Scan</b>
<dd>Don't scan all muxes in this network at Tvheadend start.
The initial scan procedure is not a blind scan. Only known muxes
registered to this network are scanned. If Network Discovery
is enabled and new muxes are discovered using DVB
descriptors, these muxes will be scanned too.
<dd>Don't scan this network for muxes at Tvheadend start.
<p>
<dt><b>Idle Scan Muxes</b>
<dd>When nothing else happens Tvheadend will continuously rotate among all muxes and tune to them to verify that they are still working
@ -83,23 +79,6 @@ Buttons have the following functions:
<dd>If you experience problems caused by overlaps between multiple network
providers this option can be used to filter which network ID is received
by a given adapter.
<p>
<dt><b>Ignore Provider's Channel Numbers</b>
<dd>Do not use the local channel numbers defined by provider.
<p>
<dt><b>SAT&gt;IP Source Number</b>
<dd>This field is matched through the "src" parameter asked from the
SAT&gt;IP client. Usually (and by default) this value is 1.
For satellite tuners, this value determines the satellite source (dish).
By specification position 1 = DiseqC AA, 2 = DiseqC AB, 3 = DiseqC BA,
4 = DiseqC BB, but any numbers may be used - depends on the SAT&gt;IP
client. Note that if you use same number for multiple networks,
the first matched network containing the mux with requested parameters
will win (also for unknown mux). If this field is set to zero,
the network cannot be used by the SAT&gt;IP server.</dd>
<p>
<dt><b>EIT Local Time</b>
<dd>EPG (EIT) events uses local time instead UTC.
<p>
<dt><b>Character Set</b>
<dd>The character encoding for this network (e.g. UTF-8).

View file

@ -18,17 +18,6 @@
<dt>Enabled
<dd>Whether or not this service is available for use
<dt>Automatic Checking
<dd>Check for the service presence. If service is no longer broadcasted,
this field will become as "Missing In PAT/SDT". The
check can be also disabled for given service using this
column.
<dt>Priority
<dd>Define priority (range 0-10) for this service. The higher value means more preferred.
Note that this value is _added_ to the input (tuner) priority.
Take this in account when you set the input priorities.
<dt>Channel
<dd>The channel to which the service is mapped

View file

@ -1,41 +0,0 @@
<div class="hts-doc-text">
<p>
<br>
<hr>
<b>Buttons</b>
<hr>
Buttons have the following functions:
<br><br>
<dl>
<dt><b>Add</b>
<dd>
Add a new profile. You can choose from any of the types from the list.
<p>
<dt><b>Delete</b>
<dd>
Delete an existing profile.
<p>
<dt><b>Save</b>
<dd>
Saves any changes.
<p>
<dt><b>Undo</b>
<dd>
Undoes any changes.
<p>
</dl>
<p>
<br>
<hr>
<b>Columns</b>
<hr>
The columns have the following functions:
<dl>
<dt><b>Stream Profile Name</b>
<dd>This column contains the name of Stream Profile.
</dl>
</div>

View file

@ -7,12 +7,12 @@
Tags are used to define a set of channels.
Notice that nothing prohibits a channel to be a member of multiple tags.
Also, there is no requirement to configure tags for running Tvheadend
itself. It is, however, required if you run Tvheadend together with Movian.
itself. It is, however, required if you run Tvheadend together with Showtime.
<p>
The tag-sets are used for:
<ul>
<li>Searches in the EPG.
<li>Display of channel groups in the Movian Media player.
<li>Display of channel groups in the Showtime Media player.
</ul>
<p>
@ -48,16 +48,9 @@
automatic recordings, groups, etc.
<dt>Internal
<dd>Tags are exported via HTSP/HTTP and used there for grouping of
TV channels. If you do not wish to export a tag you can flag
it as internal only.
<dt>Private
<dd>Tags are exported via HTSP/HTTP and used there for grouping of TV
channels. If you do not wish to export a tag to other users you can
flag it as private only. Only users with this tag configured
in the access configuration (or users with not set tags) can
use it.
<dd>Tags are exported via HTSP (to the Showtime Media player) and used
there for grouping of TV channels. If you do not wish to export a
tag you can flag it as internal only.
<dt>Icon
<dd>Full path to an icon used to depict the tag. This can be a TV network

View file

@ -36,11 +36,6 @@
specify an unlimited period its highly recommended you specifying a value
here.
<dt>Max. RAM Size (MegaBytes)
<dd>Specifies the maximum RAM (system memory) size for timeshift buffers.
When free RAM buffers are available, they are used instead storage to
save the timeshift data.
<dt>Unlimited:
<dd>If checked, this allows the combined size of all timeshift buffers to
potentially grow unbounded until your storage media runs out of space

View file

@ -30,6 +30,20 @@ The rows have the following functions
<p>
<dt><b>Networks</b></dt>
<dd>Associate this device with one or more networks.</dd>
<p>
<dt><b>Init Scan</b></dt>
<dd>Allow the initial scan tuning on this device.</dd>
<p>
<dt><b>Idle Scan</b></dt>
<dd>Allow the idle scan tuning on this device.</dd>
<p>
<dt><b>Power Save</b></dt>
<dd>If enabled, allows the tuner to go to sleep when idle.</dd>
<p>
<dt><b>Skip Initial Bytes</b></dt>
<dd>If set, first bytes from the MPEG-TS stream are discarded. It may be
required for some drivers / hardware which does not flush completely
the MPEG-TS buffers after a frequency/parameters change.</dd>
<p>
</dl>
<dt><u><i><b>Advanced Settings</b></i></u></dt>
@ -42,90 +56,9 @@ The rows have the following functions
<dd>The tuner priority value for streamed channels through HTTP or HTSP
(higher value = higher priority to use this tuner). If not set (zero),
the standard priority value is used.</dd>
<p>
<dt><b>Init Scan</b></dt>
<dd>Allow the initial scan tuning on this device. See to
Skip initial Scan in the network settings for the further description.</dd>
<p>
<dt><b>Idle Scan</b></dt>
<dd>Allow the idle scan tuning on this device.</dd>
<p>
<dt><b>Linked Input</b></dt>
<dd>Always make alive also the linked input. The subscriptions are named as "keep".</dd>
</dl>
</p>
<br>
<hr>
<b>LinuxDVB Specific Rows</b>
<hr>
<dl>
<dt><b>Power Save</b></dt>
<dd>If enabled, allows the tuner to go to sleep when idle.</dd>
<p>
<dt><b>Tune Before DiseqC</b></dt>
<dd>If set, one tune request (setup) is proceed before the DiseqC
sequence (voltage, tone settings). Some linux drivers require this
procedure.</dd>
<p>
<dt><b>Tune Repeats</b></dt>
<dd>If set, the tune requests are repeated using this number. Zero means
one tune requests, one two tune requests etc.</dd>
<p>
<dt><b>Skip Initial Bytes</b></dt>
<dd>If set, first bytes from the MPEG-TS stream are discarded. It may be
required for some drivers / hardware which does not flush completely
the MPEG-TS buffers after a frequency/parameters change.</dd>
<p>
<dt><b>Input Buffer (Bytes)</b></dt>
<dd>By default, linuxdvb input buffer is 18800 bytes long. The accepted
range is 18800-1880000 bytes.</dd>
<p>
<dt><b>Status Period</b></dt>
<dd>By default, linuxdvb status read period is 1000ms (one second). The
accepted range is 250ms to 8000ms. Note that for some hardware /
drivers (like USB), the status operations takes too much time and CPU.
In this case, increase the default value. For fast hardware, this value
might be descreased to make the decision of the re-tune algorithm
based on the signal status faster.</dd>
<p>
<dt><b>Force old status</b></dt>
<dd>Always use the old ioctls to read the linuxdvb status (signal strenght,
SNR, error counters). Some drivers are not matured enough to provide
the correct values using the new v5 linuxdvb API.</dd>
</dl>
<br>
<hr>
<b>LinuxDVB Satellite Config Rows</b>
<hr>
<dl>
<dt><b>DiseqC repeats</b></dt>
<dd>Number of repeats for the DiseqC commands (default is zero - no DiseqC repeats).</dd>
<p>
<dt><b>Full DiseqC</b></dt>
<dd>Always sent the whole DiseqC sequence including LNB setup (voltage, tone).
If this is not checked, only changed settings is set. It may cause
issues with some drivers. If the tuning is not reliable, try to
activate this option.</dd>
<p>
<dt><b>Turn off LNB when idle</b></dt>
<dd>Turn off LNB when it is not used. It may save some power.</dd>
<p>
<dt><b>Switch Then Rotor</b></dt>
<dd>If the DiseqC switch is before rotor (tuner - switch - rotor), enable this.</dd>
<p>
<dt><b>Init Rotor Time (seconds)</b></dt>
<dd>Upon new start, tvheadend does not know the last rotor position. This
value defined the initial rotor movement. TVHeadend waits the
specified seconds when the first movement is requested.</dd>
<dt><b>Min Rotor Time (seconds)</b></dt>
<dd>The minimum delay after the rotor movement command is send.</dd>
</dl>
<p>
<br>
<hr>
<b>SAT>IP Specific Rows</b>
@ -152,9 +85,6 @@ setting this to 100.</dd>
<p>
<dt><b>PIDs in setup</b></dt>
<dd>Enable, if the SAT>IP box requires pids=0 parameter in the SETUP RTSP command.</dd>
<p>
<dt><b>Double RTSP Shutdown</b></dt>
<dd>Enable, if the SAT>IP box might require to send twice the RTSP SHUTDOWN command.</dd>
<p>
<dt><b>Force pilot for DVB-S2</b></dt>
<dd>Enable, if the SAT>IP box requiest plts=on parameter in the SETUP RTSP
@ -194,6 +124,5 @@ setting this to 100.</dd>
quick continuous tuning.</dd>
</dl>
</p>
</div>

View file

@ -51,22 +51,10 @@ Check or clear this box to enable or disable this rule.
<dd>
The name you've given to the rule, e.g. 'Stuff involving Jeremy Clarkson'.
<p>
<dt><b>Directory</b>
<dd>
When specified, this setting overrides the subdirectory rules (except the base directory) specified by the DVR configuration
and puts all recordings done by this entry into the specified subdirectory. Useful for e.g. recording multiple different news
broadcasts into one common subdirectory called "News". The backshlash and other special characters are escaped, so it is possible
to create only one sublevel subdirectories (the base path for the target directory is always taken from the DVR configuration).
<p>
<dt><b>Title (Regexp)</b>
<dd>
The title of the programme to look for. Note that this accepts case-insensitive regular expressions, so you can use pattern matching as Tvheadend scans the EPG for programmes to record.
<p>
<dt><b>Fulltext</b>
<dd>
When the fulltext is checked, the title pattern is matched against title,
subtitle, summary and description.
<p>
<dt><b>Channel</b>
<dd>
The channel on which this rule applies, i.e. the channel you're aiming to record.
@ -91,13 +79,15 @@ The maximal duration of a matching event - in other words, only match programmes
<dd>
On which specific days of the week to find matching programmes.
<p>
<dt><b>Start After</b>
<dt><b>Starting Around</b>
<dd>
An event which starts between this "start after" and "start before" will be matched (including boundary values).
<p>
<dt><b>Start Before</b>
<dd>
An event which starts between this "start after" and "start before" will be matched (including boundary values).
An approximate starting time for matching programmes.
<br>
<br>
I'd need to check the code to see how this works to expand on this any further. It used to be:
<br>
<br>
Only record events if they are scheduled +-15 minutes from this given time.
<p>
<dt><b>Priority</b>
<dd>

View file

@ -50,25 +50,16 @@ Check or clear this box to enable or disable this rule.
<dd>
The name you've given to the rule, e.g. 'Evening cartoons for the children'.
<p>
<dt><b>Directory</b>
<dd>
When specified, this setting overrides the subdirectory rules specified by the DVR configuration and puts all recordings done by this entry into the specified subdirectory. Useful for e.g. recording multiple different news broadcasts into one common subdirectory called "News".
<p>
<dt><b>Title</b>
<dd>
The title is used in the filename that is created for the recording
Not sure how this differs from **Name* *
<br>
<br>
The default format string suggests Time-[date]_[time]:
The default format string suggests Time-[date]-[time]:
<br>
<br>
The escape-codes use <a href="http://man7.org/linux/man-pages/man3/strftime.3.html">strftime</a> format. Examples:
<br>
%F The date in ISO-format (e.g. 2015-02-28)
<br>
%R The time in 24h HH:MM format (e.g. 19:45)
<br>
%x The date, formatted according to your locale settings
%x The local date, formatted according to your locale settings
%R The time in HH;MM format
<p>
<dt><b>Channel</b>
<dd>

View file

@ -20,8 +20,7 @@ sorted based on start time.</p>
Only display events that match the given title. The filter uses case-insensitive
regular expressions. If you don't know what a regular expression is, this simply
means that you can type just parts of the title and filter on that - there's no need
for full, exact matching. If the fulltext checkbox is checked, the title
text is matched against title, subtitle, summary and description.</dd>
for full, exact matching.</dd>
<dt>[Filter channel...]</dt>
<dd>
Only display events from the selected channel. Channels in the drop down are

View file

@ -2,12 +2,12 @@
<dl>
<dt>Why does Tvheadend deliver data over TCP to Movian? I thought it was
<dt>Why does Tvheadend deliver data over TCP to Showtime? I thought it was
bad to use TCP for realtime sensitive traffic?
<dd>
'HTSP' - the protocol used for streaming TV, sending meta information
updates and RPC between Tvheadend and Movian uses a transmission
updates and RPC between Tvheadend and Showtime uses a transmission
scheduler with multiple queues on the Tvheadend side. This means that
Tvheadend can measure the available bandwidth between itself and the
mediaplayer and when congestion happens it's even capable of dropping
@ -15,7 +15,7 @@
links and DSL connections with zero picture/audio artifacts.
<p>
It's possible to get view drop statistics and bitrates directly in
Movian. (Open the menu when watching a TV-channel and switch on
Showtime. (Open the menu when watching a TV-channel and switch on
'Detailed Information')
</div>

View file

@ -7,7 +7,7 @@
<dt>Input sources
<dl>
<dt>DVB-T, DVB-C, DVB-S, DVB-S2, ATSC and SAT>IP.
<dt>DVB-T, DVB-C, DVB-S, DVB-S2 and ATSC.
<dd>
Multiple adapters are supported.
Each adapter can receive all programs available on the currently
@ -20,17 +20,13 @@
</dd>
<dt>Analog TV
<dd>
The IPTV extension URL - pipe:// allow to process any MPEG-TS input.
FFMPEG or LIBAV library can be used to produce analog to digital
conversion.
Using the Video4Linux2 API. Currently, only PAL is supported.
</dd>
</dl>
<dt>Output targets
<dl>
<dt>HTTP (Web Protocol), supported by <a href="http://www.videolan.org/vlc/">VLC</a>, <a href="http://www.mplayerhq.hu">MPlayer</a>
<dt>HTSP (Home TV Streaming Protocol), supported by <a href="http://movian.tv">Movian Media player</a> and <a href="http://kodi.tv/">Kodi</a>
<dt>SAT>IP Server
<dt>HTSP (Home TV Streaming Protocol), supported by Showtime Media player and <a href="http://www.xbmc.org/">XBMC</a>
<dt>The Built-in Digital Video Recorder
</dl>
@ -44,7 +40,7 @@
All setup and configuration is done from the built in web user interface.
Even so, all settings are stored in human readable text files.
<dt>Fully integrated with HTS Movian Media player or Kodi HTS PVR addon.
<dt>Fully integrated with HTS Showtime Media player.
<dd>
All channel data and their grouping, EPG and TV streaming is conducted over a
single TCP connection.
@ -83,10 +79,6 @@
<dd>
Requires a card server (newcamd and capmt protocol is supported).
<dt>Hardware based CSA descrambling
<dd>
Requires the standard dvben50221 linuxdvb library.
<dt>Internationalization
<dd>
All text is encoded in UTF-8 to provide full international support. All major

View file

@ -32,11 +32,7 @@ to Showtime, XBMC and various other clients.
%build
echo %{version} > %{_builddir}/%{buildsubdir}/rpm/version
%ifarch %arm
%configure --disable-lockowner --enable-bundle --disable-libffmpeg_static
%else
%configure --disable-lockowner --enable-bundle --enable-libffmpeg_static
%endif
%configure --disable-lockowner --enable-bundle --enable-libffmpeg_static
%{__make}
%install
@ -74,18 +70,3 @@ exit 0
%{_bindir}/*
%{_sysconfdir}/sysconfig/*
%{_unitdir}/*
%changelog
* Wed Mar 25 2015 Bob Lightfoot <boblfoot@gmail.com> 3.9-2658-gb427d7e
- Patching rpm spec file so the arm architecture builds properly
* Mon Oct 13 2014 Jaroslav Kysela <perex@perex.cz> 3.9-1806-g6f3324e
- RPM: Typo fixes
* Mon Oct 13 2014 Jaroslav Kysela <perex@perex.cz> 3.9-1805-g14a7de8
- RPM build - config fixes
* Mon Oct 13 2014 Jaroslav Kysela <perex@perex.cz> 3.9-1803-g392dec0
- Add basic RPM build support
~

View file

@ -149,19 +149,11 @@ access_t *
access_ticket_verify2(const char *id, const char *resource)
{
access_ticket_t *at;
char buf[256], *r;
if((at = access_ticket_find(id)) == NULL)
return NULL;
if (tvheadend_webroot) {
snprintf(buf, sizeof(buf), "%s%s", tvheadend_webroot, at->at_resource);
r = buf;
} else {
r = at->at_resource;
}
if(strcmp(r, resource))
if(strcmp(at->at_resource, resource))
return NULL;
return access_copy(at->at_access);
@ -339,9 +331,7 @@ access_verify(const char *username, const char *password,
bits = 0;
}
return (mask & ACCESS_OR) ?
((mask & bits) ? 0 : -1) :
((mask & bits) == mask ? 0 : -1);
return (mask & bits) == mask ? 0 : -1;
}
/*
@ -352,25 +342,20 @@ static void
access_dump_a(access_t *a)
{
htsmsg_field_t *f;
size_t l = 0;
char buf[1024];
int first;
tvh_strlcatf(buf, sizeof(buf), l,
"%s:%s [%c%c%c%c%c%c%c%c%c], conn=%u, chmin=%llu, chmax=%llu%s",
snprintf(buf, sizeof(buf),
"%s:%s [%s%s%s%s%s], conn=%u, chmin=%u, chmax=%u%s",
a->aa_representative ?: "<no-id>",
a->aa_username ?: "<no-user>",
a->aa_rights & ACCESS_STREAMING ? 'S' : ' ',
a->aa_rights & ACCESS_ADVANCED_STREAMING ? 'A' : ' ',
a->aa_rights & ACCESS_HTSP_STREAMING ? 'T' : ' ',
a->aa_rights & ACCESS_WEB_INTERFACE ? 'W' : ' ',
a->aa_rights & ACCESS_RECORDER ? 'R' : ' ',
a->aa_rights & ACCESS_HTSP_RECORDER ? 'E' : ' ',
a->aa_rights & ACCESS_ALL_RECORDER ? 'L' : ' ',
a->aa_rights & ACCESS_ALL_RW_RECORDER ? 'D' : ' ',
a->aa_rights & ACCESS_ADMIN ? '*' : ' ',
a->aa_rights & ACCESS_STREAMING ? "S" : "",
a->aa_rights & ACCESS_ADVANCED_STREAMING ? "A" : "",
a->aa_rights & ACCESS_WEB_INTERFACE ? "W" : "",
a->aa_rights & ACCESS_RECORDER ? "R" : "",
a->aa_rights & ACCESS_ADMIN ? "*" : "",
a->aa_conn_limit,
(long long)a->aa_chmin, (long long)a->aa_chmax,
a->aa_chmin, a->aa_chmax,
a->aa_match ? ", matched" : "");
if (a->aa_profiles) {
@ -379,14 +364,14 @@ access_dump_a(access_t *a)
profile_t *pro = profile_find_by_uuid(htsmsg_field_get_str(f) ?: "");
if (pro) {
if (first)
tvh_strlcatf(buf, sizeof(buf), l, ", profile=");
tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", profile=");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s'%s'",
first ? "" : ",", pro->pro_name ?: "");
first = 0;
}
}
} else {
tvh_strlcatf(buf, sizeof(buf), l, ", profile=ANY");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", profile=ANY");
}
if (a->aa_dvrcfgs) {
@ -395,14 +380,14 @@ access_dump_a(access_t *a)
dvr_config_t *cfg = dvr_config_find_by_uuid(htsmsg_field_get_str(f) ?: "");
if (cfg) {
if (first)
tvh_strlcatf(buf, sizeof(buf), l, ", dvr=");
tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", dvr=");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s'%s'",
first ? "" : ",", cfg->dvr_config_name ?: "");
first = 0;
}
}
} else {
tvh_strlcatf(buf, sizeof(buf), l, ", dvr=ANY");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", dvr=ANY");
}
if (a->aa_chtags) {
@ -411,14 +396,14 @@ access_dump_a(access_t *a)
channel_tag_t *ct = channel_tag_find_by_uuid(htsmsg_field_get_str(f) ?: "");
if (ct) {
if (first)
tvh_strlcatf(buf, sizeof(buf), l, ", tags=");
tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", tags=");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s'%s'",
first ? "" : ",", ct->ct_name ?: "");
first = 0;
}
}
} else {
tvh_strlcatf(buf, sizeof(buf), l, ", tag=ANY");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", tag=ANY");
}
tvhtrace("access", "%s", buf);
@ -808,20 +793,12 @@ access_entry_update_rights(access_entry_t *ae)
r |= ACCESS_STREAMING;
if (ae->ae_adv_streaming)
r |= ACCESS_ADVANCED_STREAMING;
if (ae->ae_htsp_streaming)
r |= ACCESS_HTSP_STREAMING;
if (ae->ae_dvr)
r |= ACCESS_RECORDER;
if (ae->ae_htsp_dvr)
r |= ACCESS_HTSP_RECORDER;
if (ae->ae_all_dvr)
r |= ACCESS_ALL_RECORDER;
if (ae->ae_webui)
r |= ACCESS_WEB_INTERFACE;
if (ae->ae_admin)
r |= ACCESS_ADMIN;
if (ae->ae_all_rw_dvr)
r |= ACCESS_ALL_RW_RECORDER;
ae->ae_rights = r;
}
@ -852,10 +829,6 @@ access_entry_create(const char *uuid, htsmsg_t *conf)
TAILQ_INIT(&ae->ae_ipmasks);
if (conf) {
/* defaults */
ae->ae_htsp_streaming = 1;
ae->ae_htsp_dvr = 1;
ae->ae_all_dvr = 1;
idnode_load(&ae->ae_id, conf);
/* note password has PO_NOSAVE, thus it must be set manually */
if ((s = htsmsg_get_str(conf, "password")) != NULL)
@ -926,7 +899,7 @@ access_destroy_by_profile(profile_t *pro, int delconf)
while ((ae = LIST_FIRST(&pro->pro_accesses)) != NULL) {
LIST_REMOVE(ae, ae_profile_link);
ae->ae_profile = NULL;
ae->ae_dvr_config = NULL;
if (delconf)
access_entry_save(ae);
}
@ -942,7 +915,7 @@ access_destroy_by_dvr_config(dvr_config_t *cfg, int delconf)
while ((ae = LIST_FIRST(&cfg->dvr_accesses)) != NULL) {
LIST_REMOVE(ae, ae_dvr_config_link);
ae->ae_dvr_config = NULL;
ae->ae_profile = profile_find_by_name(NULL, NULL);
if (delconf)
access_entry_save(ae);
}
@ -1075,7 +1048,7 @@ access_entry_class_prefix_get(void *o)
s_addr = htonl(ai->ai_network);
inet_ntop(AF_INET, &s_addr, addrbuf, sizeof(addrbuf));
}
tvh_strlcatf(buf, sizeof(buf), pos, ",%s/%d", addrbuf, ai->ai_prefixlen);
pos += snprintf(buf+pos, sizeof(buf)-pos, ",%s/%d", addrbuf, ai->ai_prefixlen);
}
return &ret;
}
@ -1279,12 +1252,6 @@ const idclass_t access_entry_class = {
.name = "Advanced Streaming",
.off = offsetof(access_entry_t, ae_adv_streaming),
},
{
.type = PT_BOOL,
.id = "htsp_streaming",
.name = "HTSP Streaming",
.off = offsetof(access_entry_t, ae_htsp_streaming),
},
{
.type = PT_STR,
.id = "profile",
@ -1299,24 +1266,6 @@ const idclass_t access_entry_class = {
.name = "Video Recorder",
.off = offsetof(access_entry_t, ae_dvr),
},
{
.type = PT_BOOL,
.id = "htsp_dvr",
.name = "HTSP DVR",
.off = offsetof(access_entry_t, ae_htsp_dvr),
},
{
.type = PT_BOOL,
.id = "all_dvr",
.name = "All DVR",
.off = offsetof(access_entry_t, ae_all_dvr),
},
{
.type = PT_BOOL,
.id = "all_rw_dvr",
.name = "All DVR (rw)",
.off = offsetof(access_entry_t, ae_all_rw_dvr),
},
{
.type = PT_STR,
.id = "dvr_config",
@ -1344,15 +1293,13 @@ const idclass_t access_entry_class = {
.off = offsetof(access_entry_t, ae_conn_limit),
},
{
.type = PT_S64,
.intsplit = CHANNEL_SPLIT,
.type = PT_U32,
.id = "channel_min",
.name = "Min Channel Num",
.off = offsetof(access_entry_t, ae_chmin),
},
{
.type = PT_S64,
.intsplit = CHANNEL_SPLIT,
.type = PT_U32,
.id = "channel_max",
.name = "Max Channel Num",
.off = offsetof(access_entry_t, ae_chmax),
@ -1412,23 +1359,19 @@ access_init(int createdefault, int noacl)
access_entry_reindex();
}
if(createdefault && TAILQ_FIRST(&access_entries) == NULL) {
if(TAILQ_FIRST(&access_entries) == NULL) {
/* No records available */
ae = access_entry_create(NULL, NULL);
free(ae->ae_comment);
ae->ae_comment = strdup("Default access entry");
ae->ae_enabled = 1;
ae->ae_streaming = 1;
ae->ae_adv_streaming = 1;
ae->ae_htsp_streaming = 1;
ae->ae_dvr = 1;
ae->ae_htsp_dvr = 1;
ae->ae_all_dvr = 1;
ae->ae_all_rw_dvr = 1;
ae->ae_webui = 1;
ae->ae_admin = 1;
ae->ae_enabled = 1;
ae->ae_streaming = 1;
ae->ae_adv_streaming = 1;
ae->ae_dvr = 1;
ae->ae_webui = 1;
ae->ae_admin = 1;
access_entry_update_rights(ae);
TAILQ_INIT(&ae->ae_ipmasks);

View file

@ -57,7 +57,6 @@ typedef struct access_entry {
int ae_streaming;
int ae_adv_streaming;
int ae_htsp_streaming;
struct profile *ae_profile;
LIST_ENTRY(access_entry) ae_profile_link;
@ -65,17 +64,14 @@ typedef struct access_entry {
uint32_t ae_conn_limit;
int ae_dvr;
int ae_htsp_dvr;
int ae_all_dvr;
int ae_all_rw_dvr;
struct dvr_config *ae_dvr_config;
LIST_ENTRY(access_entry) ae_dvr_config_link;
int ae_webui;
int ae_admin;
uint64_t ae_chmin;
uint64_t ae_chmax;
uint32_t ae_chmin;
uint32_t ae_chmax;
struct channel_tag *ae_chtag;
LIST_ENTRY(access_entry) ae_channel_tag_link;
@ -93,8 +89,8 @@ typedef struct access {
uint32_t aa_rights;
htsmsg_t *aa_profiles;
htsmsg_t *aa_dvrcfgs;
uint64_t aa_chmin;
uint64_t aa_chmax;
uint32_t aa_chmin;
uint32_t aa_chmax;
htsmsg_t *aa_chtags;
int aa_match;
uint32_t aa_conn_limit;
@ -117,20 +113,13 @@ typedef struct access_ticket {
#define ACCESS_ANONYMOUS 0
#define ACCESS_STREAMING (1<<0)
#define ACCESS_ADVANCED_STREAMING (1<<1)
#define ACCESS_HTSP_STREAMING (1<<2)
#define ACCESS_WEB_INTERFACE (1<<3)
#define ACCESS_RECORDER (1<<4)
#define ACCESS_HTSP_RECORDER (1<<5)
#define ACCESS_ALL_RECORDER (1<<6)
#define ACCESS_ADMIN (1<<7)
#define ACCESS_ALL_RW_RECORDER (1<<8)
#define ACCESS_OR (1<<30)
#define ACCESS_WEB_INTERFACE (1<<2)
#define ACCESS_RECORDER (1<<3)
#define ACCESS_ADMIN (1<<4)
#define ACCESS_FULL \
(ACCESS_STREAMING | ACCESS_ADVANCED_STREAMING | \
ACCESS_HTSP_STREAMING | ACCESS_WEB_INTERFACE | \
ACCESS_RECORDER | ACCESS_HTSP_RECORDER | \
ACCESS_ALL_RECORDER | ACCESS_ADMIN | ACCESS_ALL_RW_RECORDER)
ACCESS_WEB_INTERFACE | ACCESS_RECORDER | ACCESS_ADMIN)
/**
* Create a new ticket for the requested resource and generate a id for it
@ -164,9 +153,7 @@ int access_verify(const char *username, const char *password,
struct sockaddr *src, uint32_t mask);
static inline int access_verify2(access_t *a, uint32_t mask)
{ return (mask & ACCESS_OR) ?
((a->aa_rights & mask) ? 0 : -1) :
((a->aa_rights & mask) == mask ? 0 : -1); }
{ return (a->aa_rights & mask) == mask ? 0 : -1; }
int access_verify_list(htsmsg_t *list, const char *item);

View file

@ -25,42 +25,21 @@
#include "access.h"
#include "api.h"
static void
api_channel_key_val(htsmsg_t *dst, const char *key, const char *val)
{
htsmsg_t *e = htsmsg_create_map();
htsmsg_add_str(e, "key", key);
htsmsg_add_str(e, "val", val ?: "");
htsmsg_add_msg(dst, NULL, e);
}
static int
api_channel_is_all(access_t *perm, htsmsg_t *args)
{
return htsmsg_get_bool_or_default(args, "all", 0) &&
!access_verify2(perm, ACCESS_ADMIN);
}
// TODO: this will need converting to an idnode system
static int
api_channel_list
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
channel_t *ch;
htsmsg_t *l;
int cfg = api_channel_is_all(perm, args);
char buf[128];
htsmsg_t *l, *e;
l = htsmsg_create_list();
pthread_mutex_lock(&global_lock);
CHANNEL_FOREACH(ch) {
if (!cfg && !channel_access(ch, perm, 0)) continue;
if (!ch->ch_enabled) {
snprintf(buf, sizeof(buf), "{%s}", channel_get_name(ch));
api_channel_key_val(l, idnode_uuid_as_str(&ch->ch_id), buf);
} else {
api_channel_key_val(l, idnode_uuid_as_str(&ch->ch_id), channel_get_name(ch));
}
e = htsmsg_create_map();
htsmsg_add_str(e, "key", idnode_uuid_as_str(&ch->ch_id));
htsmsg_add_str(e, "val", channel_get_name(ch));
htsmsg_add_msg(l, NULL, e);
}
pthread_mutex_unlock(&global_lock);
*resp = htsmsg_create_map();
@ -71,14 +50,12 @@ api_channel_list
static void
api_channel_grid
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf )
{
channel_t *ch;
int cfg = api_channel_is_all(perm, args);
CHANNEL_FOREACH(ch)
if (cfg || channel_access(ch, perm, 0))
idnode_set_add(ins, (idnode_t*)ch, &conf->filter);
idnode_set_add(ins, (idnode_t*)ch, &conf->filter);
}
static int
@ -105,20 +82,15 @@ api_channel_tag_list
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
channel_tag_t *ct;
htsmsg_t *l;
int cfg = api_channel_is_all(perm, args);
char buf[128];
htsmsg_t *l, *e;
l = htsmsg_create_list();
TAILQ_FOREACH(ct, &channel_tags, ct_link)
if (cfg || channel_tag_access(ct, perm, 0)) {
if (ct->ct_enabled) {
api_channel_key_val(l, idnode_uuid_as_str(&ct->ct_id), ct->ct_name);
} else {
snprintf(buf, sizeof(buf), "{%s}", ct->ct_name);
api_channel_key_val(l, idnode_uuid_as_str(&ct->ct_id), buf);
}
}
TAILQ_FOREACH(ct, &channel_tags, ct_link) {
e = htsmsg_create_map();
htsmsg_add_str(e, "key", idnode_uuid_as_str(&ct->ct_id));
htsmsg_add_str(e, "val", ct->ct_name);
htsmsg_add_msg(l, NULL, e);
}
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
return 0;
@ -126,14 +98,12 @@ api_channel_tag_list
static void
api_channel_tag_grid
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf )
{
channel_tag_t *ct;
int cfg = api_channel_is_all(perm, args);
TAILQ_FOREACH(ct, &channel_tags, ct_link)
if (cfg || channel_tag_access(ct, perm, 0))
idnode_set_add(ins, (idnode_t*)ct, &conf->filter);
idnode_set_add(ins, (idnode_t*)ct, &conf->filter);
}
static int

View file

@ -160,19 +160,16 @@ static htsmsg_t *
api_dvr_entry_create_from_single(htsmsg_t *args)
{
htsmsg_t *entries, *m;
const char *s1, *s2, *s3;
const char *s1, *s2;
if (!(s1 = htsmsg_get_str(args, "config_uuid")))
return NULL;
if (!(s2 = htsmsg_get_str(args, "event_id")))
return NULL;
s3 = htsmsg_get_str(args, "comment");
entries = htsmsg_create_list();
m = htsmsg_create_map();
htsmsg_add_str(m, "config_uuid", s1);
htsmsg_add_str(m, "event_id", s2);
if (s3)
htsmsg_add_str(m, "comment", s3);
htsmsg_add_msg(entries, NULL, m);
return entries;
}
@ -182,7 +179,7 @@ api_dvr_entry_create_by_event
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
dvr_entry_t *de;
const char *config_uuid, *comment;
const char *config_uuid;
epg_broadcast_t *e;
htsmsg_t *entries, *entries2 = NULL, *m;
htsmsg_field_t *f;
@ -202,17 +199,14 @@ api_dvr_entry_create_by_event
continue;
config_uuid = htsmsg_get_str(m, "config_uuid");
comment = htsmsg_get_str(m, "comment");
pthread_mutex_lock(&global_lock);
if ((e = epg_broadcast_find_by_id(strtoll(s, NULL, 10)))) {
dvr_config_t *cfg = dvr_config_find_by_list(perm->aa_dvrcfgs, config_uuid);
if (cfg) {
de = dvr_entry_create_by_event(idnode_uuid_as_str(&cfg->dvr_id),
e, 0, 0,
perm->aa_username,
perm->aa_representative,
NULL, DVR_PRIO_NORMAL, 0, comment);
e, 0, 0, perm->aa_representative,
NULL, DVR_PRIO_NORMAL, 0);
if (de)
dvr_entry_save(de);
}
@ -259,8 +253,6 @@ api_dvr_autorec_create
if (!(conf = htsmsg_get_map(args, "conf")))
return EINVAL;
if (perm->aa_username)
htsmsg_set_str(conf, "owner", perm->aa_username);
if (perm->aa_representative)
htsmsg_set_str(conf, "creator", perm->aa_representative);
@ -305,9 +297,7 @@ api_dvr_autorec_create_by_series
dvr_config_t *cfg = dvr_config_find_by_list(perm->aa_dvrcfgs, config_uuid);
if (cfg) {
dae = dvr_autorec_add_series_link(idnode_uuid_as_str(&cfg->dvr_id),
e,
perm->aa_username,
perm->aa_representative,
e, perm->aa_representative,
"Created from EPG query");
if (dae) {
dvr_autorec_save(dae);
@ -344,8 +334,6 @@ api_dvr_timerec_create
if (!(conf = htsmsg_get_map(args, "conf")))
return EINVAL;
if (perm->aa_username)
htsmsg_set_str(conf, "owner", perm->aa_username);
if (perm->aa_representative)
htsmsg_set_str(conf, "creator", perm->aa_representative);
@ -363,10 +351,8 @@ api_dvr_timerec_create
void api_dvr_init ( void )
{
static api_hook_t ah[] = {
{ "dvr/config/class", ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER,
api_idnode_class, (void*)&dvr_config_class },
{ "dvr/config/grid", ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER,
api_idnode_grid, api_dvr_config_grid },
{ "dvr/config/class", ACCESS_RECORDER, api_idnode_class, (void*)&dvr_config_class },
{ "dvr/config/grid", ACCESS_RECORDER, api_idnode_grid, api_dvr_config_grid },
{ "dvr/config/create", ACCESS_ADMIN, api_dvr_config_create, NULL },
{ "dvr/entry/class", ACCESS_RECORDER, api_idnode_class, (void*)&dvr_entry_class },

View file

@ -289,12 +289,11 @@ api_epg_grid
memset(&eq, 0, sizeof(eq));
lang = htsmsg_get_str(args, "lang");
if (lang)
eq.lang = strdup(lang);
eq.lang = lang ? strdup(lang) : NULL;
str = htsmsg_get_str(args, "title");
if (str)
eq.stitle = strdup(str);
eq.fulltext = htsmsg_get_bool_or_default(args, "fulltext", 0);
str = htsmsg_get_str(args, "channel");
if (str)
eq.channel = strdup(str);
@ -423,7 +422,7 @@ api_epg_grid
/* Query the EPG */
pthread_mutex_lock(&global_lock);
epg_query(&eq, perm);
epg_query(&eq);
/* Build response */
start = MIN(eq.entries, start);

View file

@ -49,8 +49,6 @@ api_mpegts_input_network_list
if (!mi)
goto exit;
tvhtrace("mpegts", "network-list: found input '%s'", mi->mi_name ?: "");
htsmsg_t *l = htsmsg_create_list();
if ((is = mi->mi_network_list(mi))) {
for (i = 0; i < is->is_count; i++) {
@ -136,43 +134,6 @@ api_mpegts_network_create
return err;
}
static int
api_mpegts_network_scan
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
htsmsg_field_t *f;
htsmsg_t *uuids;
mpegts_network_t *mn;
const char *uuid;
if (!(f = htsmsg_field_find(args, "uuid")))
return -EINVAL;
if ((uuids = htsmsg_field_get_list(f))) {
HTSMSG_FOREACH(f, uuids) {
if (!(uuid = htsmsg_field_get_str(f))) continue;
mn = mpegts_network_find(uuid);
if (mn) {
pthread_mutex_lock(&global_lock);
mpegts_network_scan(mn);
pthread_mutex_unlock(&global_lock);
}
}
} else if ((uuid = htsmsg_field_get_str(f))) {
mn = mpegts_network_find(uuid);
if (mn) {
pthread_mutex_lock(&global_lock);
mpegts_network_scan(mn);
pthread_mutex_unlock(&global_lock);
}
else
return -ENOENT;
} else {
return -EINVAL;
}
return 0;
}
static int
api_mpegts_network_muxclass
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
@ -327,43 +288,6 @@ api_mpegts_mux_sched_create
return err;
}
#if ENABLE_MPEGTS_DVB
static int
api_dvb_orbitalpos_list
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
htsmsg_t *l, *e, *c;
htsmsg_field_t *f;
const char *s;
int satpos, i;
char buf[128];
if (!satellites)
return 0;
l = htsmsg_create_list();
HTSMSG_FOREACH(f, satellites) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
if(htsmsg_get_s32(c, "pos", &satpos))
continue;
if((s = htsmsg_get_str(c, "name")) == NULL)
continue;
e = htsmsg_create_map();
dvb_sat_position_to_str(satpos, buf, sizeof(buf));
htsmsg_add_str(e, "key", buf);
i = strlen(buf);
snprintf(buf + i, sizeof(buf) - i, " : %s", s);
htsmsg_add_str(e, "val", buf);
htsmsg_add_msg(l, NULL, e);
}
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
return 0;
}
#endif
#if ENABLE_MPEGTS_DVB
static int
api_dvb_scanfile_list
@ -371,7 +295,6 @@ api_dvb_scanfile_list
{
char buf[512];
const char *type = htsmsg_get_str(args, "type");
int satpos = htsmsg_get_s32_or_default(args, "satpos", INT_MAX);
scanfile_region_list_t *list = NULL;
htsmsg_t *l, *e;
scanfile_region_t *r;
@ -394,7 +317,6 @@ api_dvb_scanfile_list
l = htsmsg_create_list();
LIST_FOREACH(r, list, sfr_link) {
LIST_FOREACH(n, &r->sfr_networks, sfn_link) {
if (satpos != INT_MAX && n->sfn_satpos != satpos) continue;
e = htsmsg_create_map();
sprintf(buf, "%s/%s/%s", type, r->sfr_id, n->sfn_id);
htsmsg_add_str(e, "key", buf);
@ -432,7 +354,6 @@ api_mpegts_init ( void )
{ "mpegts/network/create", ACCESS_ADMIN, api_mpegts_network_create, NULL },
{ "mpegts/network/mux_class", ACCESS_ADMIN, api_mpegts_network_muxclass, NULL },
{ "mpegts/network/mux_create", ACCESS_ADMIN, api_mpegts_network_muxcreate, NULL },
{ "mpegts/network/scan", ACCESS_ADMIN, api_mpegts_network_scan, NULL },
{ "mpegts/mux/grid", ACCESS_ADMIN, api_idnode_grid, api_mpegts_mux_grid },
{ "mpegts/mux/class", ACCESS_ADMIN, api_idnode_class, (void*)&mpegts_mux_class },
{ "mpegts/service/grid", ACCESS_ADMIN, api_idnode_grid, api_mpegts_service_grid },
@ -441,7 +362,6 @@ api_mpegts_init ( void )
{ "mpegts/mux_sched/grid", ACCESS_ADMIN, api_idnode_grid, api_mpegts_mux_sched_grid },
{ "mpegts/mux_sched/create", ACCESS_ADMIN, api_mpegts_mux_sched_create, NULL },
#if ENABLE_MPEGTS_DVB
{ "dvb/orbitalpos/list", ACCESS_ADMIN, api_dvb_orbitalpos_list, NULL },
{ "dvb/scanfile/list", ACCESS_ADMIN, api_dvb_scanfile_list, NULL },
#endif
{ NULL },

View file

@ -37,10 +37,8 @@ api_status_inputs
tvh_input_stream_t *st;
tvh_input_stream_list_t stl = { 0 };
pthread_mutex_lock(&global_lock);
TVH_INPUT_FOREACH(ti)
ti->ti_get_streams(ti, &stl);
pthread_mutex_unlock(&global_lock);
l = htsmsg_create_list();
while ((st = LIST_FIRST(&stl))) {
@ -69,13 +67,11 @@ api_status_subscriptions
l = htsmsg_create_list();
c = 0;
pthread_mutex_lock(&global_lock);
LIST_FOREACH(ths, &subscriptions, ths_global_link) {
e = subscription_create_msg(ths);
htsmsg_add_msg(l, NULL, e);
c++;
}
pthread_mutex_unlock(&global_lock);
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
@ -124,48 +120,12 @@ api_connections_cancel
return 0;
}
static void
input_clear_stats(const char *uuid)
{
tvh_input_instance_t *tii;
pthread_mutex_lock(&global_lock);
if ((tii = tvh_input_instance_find_by_uuid(uuid)) != NULL)
if (tii->tii_clear_stats)
tii->tii_clear_stats(tii);
pthread_mutex_unlock(&global_lock);
}
static int
api_status_input_clear_stats
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
htsmsg_field_t *f;
htsmsg_t *ids;
const char *uuid;
if (!(f = htsmsg_field_find(args, "uuid")))
return EINVAL;
if (!(ids = htsmsg_field_get_list(f))) {
if ((uuid = htsmsg_field_get_str(f)) == NULL)
return EINVAL;
input_clear_stats(uuid);
} else {
HTSMSG_FOREACH(f, ids) {
if ((uuid = htsmsg_field_get_str(f)) == NULL) continue;
input_clear_stats(uuid);
}
}
return 0;
}
void api_status_init ( void )
{
static api_hook_t ah[] = {
{ "status/connections", ACCESS_ADMIN, api_status_connections, NULL },
{ "status/subscriptions", ACCESS_ADMIN, api_status_subscriptions, NULL },
{ "status/inputs", ACCESS_ADMIN, api_status_inputs, NULL },
{ "status/inputclrstats", ACCESS_ADMIN, api_status_input_clear_stats, NULL },
{ "connections/cancel", ACCESS_ADMIN, api_connections_cancel, NULL },
{ NULL },
};

View file

@ -163,7 +163,6 @@ bouquet_find_by_source(const char *name, const char *src, int create)
tvhwarn("bouquet", "bouquet name '%s' changed to '%s'", bq->bq_name ?: "", name);
free(bq->bq_name);
bq->bq_name = strdup(name);
bouquet_save(bq, 1);
}
return bq;
}
@ -226,8 +225,6 @@ bouquet_map_channel(bouquet_t *bq, service_t *t)
channel_t *ch = NULL;
channel_service_mapping_t *csm;
if (!t->s_enabled)
return;
if (!bq->bq_mapradio && service_is_radio(t))
return;
if (!bq->bq_mapnolcn &&
@ -322,25 +319,6 @@ bouquet_unmap_channel(bouquet_t *bq, service_t *t)
}
}
/**
*
*/
void
bouquet_notify_service_enabled(service_t *t)
{
bouquet_t *bq;
lock_assert(&global_lock);
RB_FOREACH(bq, &bouquets, bq_link)
if (idnode_set_exists(bq->bq_services, &t->s_id)) {
if (!t->s_enabled)
bouquet_unmap_channel(bq, t);
else if (bq->bq_enabled && bq->bq_maptoch)
bouquet_map_channel(bq, t);
}
}
/*
*
*/

View file

@ -73,8 +73,6 @@ bouquet_t * bouquet_create(const char *uuid, htsmsg_t *conf,
void bouquet_destroy_by_service(service_t *t);
void bouquet_destroy_by_channel_tag(channel_tag_t *ct);
void bouquet_notify_service_enabled(service_t *t);
static inline bouquet_t *
bouquet_find_by_uuid(const char *uuid)
{ return (bouquet_t *)idnode_find(uuid, &bouquet_class, NULL); }

View file

@ -1,6 +1,6 @@
/*
* tvheadend, channel functions
* Copyright (C) 2007 Andreas Öman
* 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
@ -45,8 +45,6 @@
#include "bouquet.h"
#include "intlconv.h"
#define CHANNEL_BLANK_NAME "{name-not-set}"
struct channel_tree channels;
struct channel_tag_queue channel_tags;
@ -195,12 +193,9 @@ htsmsg_t *
channel_class_get_list(void *o)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_t *p = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "channel/list");
htsmsg_add_str(m, "event", "channel");
htsmsg_add_u32(p, "all", 1);
htsmsg_add_msg(m, "params", p);
return m;
}
@ -250,11 +245,9 @@ channel_class_epggrab_set ( void *o, const void *v )
}
/* Link */
if (l) {
HTSMSG_FOREACH(f, l) {
if ((ec = epggrab_channel_find_by_id(htsmsg_field_get_str(f))))
save |= epggrab_channel_link(ec, ch);
}
HTSMSG_FOREACH(f, l) {
if ((ec = epggrab_channel_find_by_id(htsmsg_field_get_str(f))))
save |= epggrab_channel_link(ec, ch);
}
/* Delete */
@ -316,12 +309,14 @@ const idclass_t channel_class = {
.ic_get_title = channel_class_get_title,
.ic_delete = channel_class_delete,
.ic_properties = (const property_t[]){
#if 0
{
.type = PT_BOOL,
.id = "enabled",
.name = "Enabled",
.off = offsetof(channel_t, ch_enabled),
},
#endif
{
.type = PT_STR,
.id = "name",
@ -352,12 +347,6 @@ const idclass_t channel_class = {
.get = channel_class_get_icon,
.opts = PO_RDONLY | PO_NOSAVE | PO_HIDDEN,
},
{
.type = PT_BOOL,
.id = "epgauto",
.name = "Auto EPG Channel",
.off = offsetof(channel_t, ch_epgauto),
},
{
.type = PT_STR,
.islist = 1,
@ -428,7 +417,7 @@ channel_find_by_name ( const char *name )
if (name == NULL)
return NULL;
CHANNEL_FOREACH(ch)
if (ch->ch_enabled && !strcmp(channel_get_name(ch), name))
if (!strcmp(channel_get_name(ch), name))
break;
return ch;
}
@ -468,23 +457,17 @@ channel_find_by_number ( const char *no )
* Check if user can access the channel
*/
int
channel_access(channel_t *ch, access_t *a, int disabled)
channel_access(channel_t *ch, access_t *a, const char *username)
{
if (!ch)
return 0;
if (!disabled && !ch->ch_enabled)
return 0;
/* Channel number check */
if (a->aa_chmin || a->aa_chmax) {
int64_t chnum = channel_get_number(ch);
if (ch && (a->aa_chmin || a->aa_chmax)) {
int chnum = channel_get_number(ch);
if (chnum < a->aa_chmin || chnum > a->aa_chmax)
return 0;
}
/* Channel tag check */
if (a->aa_chtags) {
if (ch && a->aa_chtags) {
channel_tag_mapping_t *ctm;
htsmsg_field_t *f;
HTSMSG_FOREACH(f, a->aa_chtags) {
@ -568,7 +551,7 @@ channel_set_tags_by_list ( channel_t *ch, htsmsg_t *tags )
const char *
channel_get_name ( channel_t *ch )
{
static const char *blank = CHANNEL_BLANK_NAME;
static const char *blank = "";
const char *s;
channel_service_mapping_t *csm;
if (ch->ch_name && *ch->ch_name) return ch->ch_name;
@ -578,19 +561,6 @@ channel_get_name ( channel_t *ch )
return blank;
}
int
channel_set_name ( channel_t *ch, const char *name )
{
int save = 0;
if (!ch || !name) return 0;
if (!ch->ch_name || strcmp(ch->ch_name, name) ) {
if (ch->ch_name) free(ch->ch_name);
ch->ch_name = strdup(name);
save = 1;
}
return save;
}
int64_t
channel_get_number ( channel_t *ch )
{
@ -615,19 +585,6 @@ channel_get_number ( channel_t *ch )
return 0;
}
int
channel_set_number ( channel_t *ch, uint32_t major, uint32_t minor )
{
int save = 0;
int64_t chnum = (uint64_t)major * CHANNEL_SPLIT + (uint64_t)minor;
if (!ch || !chnum) return 0;
if (!ch->ch_number || ch->ch_number != chnum) {
ch->ch_number = chnum;
save = 1;
}
return save;
}
static int
check_file( const char *url )
{
@ -661,18 +618,20 @@ channel_get_icon ( channel_t *ch )
/* No user icon - try to get the channel icon by name */
if (!pick && chicon && chicon[0] >= ' ' && chicon[0] <= 122 &&
(chname = channel_get_name(ch)) != NULL && chname[0] &&
strcmp(chname, CHANNEL_BLANK_NAME)) {
(chname = channel_get_name(ch)) != NULL && chname[0]) {
const char *chi, *send, *sname, *s;
chi = strdup(chicon);
/* Check for and replace placeholders */
if ((send = strstr(chi, "%C"))) {
send = strstr(chi, "%C");
if (send == NULL) {
buf[0] = '\0';
sname = "";
} else {
*(char *)send = '\0';
send += 2;
sname = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
chname, strlen(chname) * 2);
if (sname == NULL)
sname = strdup(chname);
/* Remove problematic characters */
s = sname;
while (s && *s) {
@ -682,26 +641,6 @@ channel_get_icon ( channel_t *ch )
s++;
}
}
else if((send = strstr(chi, "%c"))) {
char *aname = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
chname, strlen(chname) * 2);
if (aname == NULL)
aname = strdup(chname);
sname = url_encode(aname);
free((char *)aname);
}
else {
buf[0] = '\0';
sname = "";
}
if (send) {
*(char *)send = '\0';
send += 2;
}
snprintf(buf, sizeof(buf), "%s%s%s", chi, sname ?: "", send ?: "");
if (send)
free((char *)sname);
@ -723,7 +662,7 @@ channel_get_icon ( channel_t *ch )
continue;
snprintf(buf2, sizeof(buf2), "%s/%s", picon, icn+8);
if (i > 1 || check_file(buf2)) {
icon = ch->ch_icon = strdup(icn);
ch->ch_icon = strdup(icn);
channel_save(ch);
idnode_notify_simple(&ch->ch_id);
break;
@ -755,18 +694,6 @@ channel_get_icon ( channel_t *ch )
return buf;
}
int channel_set_icon ( channel_t *ch, const char *icon )
{
int save = 0;
if (!ch || !icon) return 0;
if (!ch->ch_icon || strcmp(ch->ch_icon, icon) ) {
if (ch->ch_icon) free(ch->ch_icon);
ch->ch_icon = strdup(icon);
save = 1;
}
return save;
}
/* **************************************************************************
* Creation/Deletion
* *************************************************************************/
@ -795,10 +722,6 @@ channel_create0
abort();
}
/* Defaults */
ch->ch_enabled = 1;
ch->ch_epgauto = 1;
if (conf) {
ch->ch_load = 1;
idnode_load(&ch->ch_id, conf);
@ -1097,7 +1020,6 @@ channel_tag_save(channel_tag_t *ct)
idnode_save(&ct->ct_id, c);
hts_settings_save(c, "channel/tag/%s", idnode_uuid_as_str(&ct->ct_id));
htsmsg_destroy(c);
htsp_tag_update(ct);
}
@ -1119,35 +1041,6 @@ channel_tag_get_icon(channel_tag_t *ct)
return icon;
}
/**
* Check if user can access the channel tag
*/
int
channel_tag_access(channel_tag_t *ct, access_t *a, int disabled)
{
if (!ct)
return 0;
if (!disabled && (!ct->ct_enabled || ct->ct_internal))
return 0;
if (!ct->ct_private)
return 1;
/* Channel tag check */
if (a->aa_chtags) {
htsmsg_field_t *f;
const char *uuid = idnode_uuid_as_str(&ct->ct_id);
HTSMSG_FOREACH(f, a->aa_chtags)
if (!strcmp(htsmsg_field_get_str(f) ?: "", uuid))
goto chtags_ok;
return 0;
}
chtags_ok:
return 1;
}
/* **************************************************************************
* Channel Tag Class definition
* **************************************************************************/
@ -1190,12 +1083,9 @@ htsmsg_t *
channel_tag_class_get_list(void *o)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_t *p = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "channeltag/list");
htsmsg_add_str(m, "event", "channeltag");
htsmsg_add_u32(p, "all", 1);
htsmsg_add_msg(m, "params", p);
return m;
}
@ -1213,12 +1103,6 @@ const idclass_t channel_tag_class = {
.name = "Enabled",
.off = offsetof(channel_tag_t, ct_enabled),
},
{
.type = PT_U32,
.id = "index",
.name = "Sort Index",
.off = offsetof(channel_tag_t, ct_index),
},
{
.type = PT_STR,
.id = "name",
@ -1231,12 +1115,6 @@ const idclass_t channel_tag_class = {
.name = "Internal",
.off = offsetof(channel_tag_t, ct_internal),
},
{
.type = PT_BOOL,
.id = "private",
.name = "Private",
.off = offsetof(channel_tag_t, ct_private),
},
{
.type = PT_STR,
.id = "icon",

View file

@ -1,6 +1,6 @@
/*
* tvheadend, channel functions
* Copyright (C) 2007 Andreas Öman
* 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
@ -43,13 +43,12 @@ typedef struct channel
idnode_t ch_id;
RB_ENTRY(channel) ch_link;
int ch_refcount;
int ch_zombie;
int ch_load;
/* Channel info */
int ch_enabled;
char *ch_name; // Note: do not access directly!
int64_t ch_number;
char *ch_icon;
@ -68,7 +67,6 @@ typedef struct channel
gtimer_t ch_epg_timer_head;
gtimer_t ch_epg_timer_current;
int ch_epgauto;
LIST_HEAD(,epggrab_channel_link) ch_epggrab;
/* DVR */
@ -91,9 +89,7 @@ typedef struct channel_tag {
TAILQ_ENTRY(channel_tag) ct_link;
int ct_enabled;
uint32_t ct_index;
int ct_internal;
int ct_private;
int ct_titled_icon;
char *ct_name;
char *ct_comment;
@ -180,17 +176,15 @@ htsmsg_t * channel_tag_class_get_list(void *o);
const char * channel_tag_get_icon(channel_tag_t *ct);
int channel_access(channel_t *ch, struct access *a, int disabled);
int channel_access(channel_t *ch, struct access *a, const char *username);
int channel_tag_map(channel_t *ch, channel_tag_t *ct);
void channel_tag_unmap(channel_t *ch, channel_tag_t *ct);
int channel_tag_access(channel_tag_t *ct, struct access *a, int disabled);
void channel_save(channel_t *ch);
const char *channel_get_name ( channel_t *ch );
int channel_set_name ( channel_t *ch, const char *name );
int channel_set_name ( channel_t *ch, const char *s );
#define CHANNEL_SPLIT 1000000
@ -198,7 +192,6 @@ static inline uint32_t channel_get_major ( int64_t chnum ) { return chnum / CHAN
static inline uint32_t channel_get_minor ( int64_t chnum ) { return chnum % CHANNEL_SPLIT; }
int64_t channel_get_number ( channel_t *ch );
int channel_set_number ( channel_t *ch, uint32_t major, uint32_t minor );
const char *channel_get_icon ( channel_t *ch );
int channel_set_icon ( channel_t *ch, const char *icon );

View file

@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>
@ -26,16 +25,12 @@
#include "uuid.h"
#include "htsbuf.h"
#include "spawn.h"
#include "lock.h"
#include "profile.h"
/* *************************************************************************
* Global data
* ************************************************************************/
static htsmsg_t *config;
static char config_lock[PATH_MAX];
static int config_lock_fd;
/* *************************************************************************
* Config migration
@ -1104,87 +1099,6 @@ config_migrate_v15 ( void )
}
}
static int
config_dvr_autorec_start_set(const char *s, int *tm)
{
int t;
if(s == NULL || s[0] == '\0' || !isdigit(s[0]))
t = -1;
else if(strchr(s, ':') != NULL)
// formatted time string - convert
t = (atoi(s) * 60) + atoi(s + 3);
else {
t = atoi(s);
}
if (t >= 24 * 60)
t = -1;
if (t != *tm) {
*tm = t;
return 1;
}
return 0;
}
static void
config_modify_dvrauto( htsmsg_t *c )
{
int tm = -1, tw = -1;
char buf[16];
if (config_dvr_autorec_start_set(htsmsg_get_str(c, "start"), &tm) > 0 && tm >= 0) {
tm -= 15;
if (tm < 0)
tm += 24 * 60;
tw = tm + 30;
if (tw >= 24 * 60)
tw -= 24 * 60;
snprintf(buf, sizeof(buf), "%02d:%02d", tm / 60, tm % 60);
htsmsg_set_str(c, "start", buf);
snprintf(buf, sizeof(buf), "%02d:%02d", tw / 60, tw % 60);
htsmsg_set_str(c, "start_window", buf);
} else {
htsmsg_delete_field(c, "start");
}
}
static void
config_migrate_v16 ( void )
{
htsmsg_t *c, *e;
htsmsg_field_t *f;
if ((c = hts_settings_load("dvr/autorec")) != NULL) {
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_field_get_map(f))) continue;
config_modify_dvrauto(e);
hts_settings_save(e, "dvr/autorec/%s", f->hmf_name);
}
htsmsg_destroy(c);
}
}
static void
config_migrate_v17 ( void )
{
htsmsg_t *c, *e;
htsmsg_field_t *f;
int i, p;
if ((c = hts_settings_load("profile")) != NULL) {
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_field_get_map(f))) continue;
if (htsmsg_get_s32(e, "priority", &i)) {
p = PROFILE_SPRIO_NORMAL;
if (strcmp(htsmsg_get_str(e, "name") ?: "", "htsp") == 0)
p = PROFILE_SPRIO_IMPORTANT;
htsmsg_set_s32(e, "priority", p);
hts_settings_save(e, "profile/%s", f->hmf_name);
}
}
}
}
/*
* Perform backup
*/
@ -1200,11 +1114,8 @@ dobackup(const char *oldver)
const char *root = hts_settings_get_root();
char errtxt[128];
const char **arg;
pid_t pid;
int code;
assert(root);
tvhinfo("config", "backup: migrating config from %s (running %s)",
oldver, tvheadend_version);
@ -1225,7 +1136,7 @@ dobackup(const char *oldver)
}
snprintf(outfile, sizeof(outfile), "%s/backup", root);
if (makedirs(outfile, 0700, -1, -1))
if (makedirs(outfile, 0700))
goto fatal;
if (chdir(root)) {
tvherror("config", "unable to find directory '%s'", root);
@ -1236,14 +1147,10 @@ dobackup(const char *oldver)
root, oldver);
tvhinfo("config", "backup: running, output file %s", outfile);
if (spawnv(argv[0], (void *)argv, &pid, 1, 1)) {
code = -ENOENT;
} else {
while ((code = spawn_reap(pid, errtxt, sizeof(errtxt))) == -EAGAIN)
usleep(20000);
if (code == -ECHILD)
code = 0;
}
spawnv(argv[0], (void *)argv);
while ((code = spawn_reap(errtxt, sizeof(errtxt))) == -EAGAIN)
usleep(20000);
if (code) {
htsbuf_queue_t q;
@ -1295,9 +1202,7 @@ static const config_migrate_t config_migrate_table[] = {
config_migrate_v12,
config_migrate_v13,
config_migrate_v14,
config_migrate_v15,
config_migrate_v16,
config_migrate_v17
config_migrate_v15
};
/*
@ -1388,32 +1293,24 @@ config_check ( void )
* Initialisation / Shutdown / Saving
* *************************************************************************/
static int config_newcfg = 0;
void
config_boot ( const char *path, gid_t gid, uid_t uid )
config_init ( const char *path, int backup )
{
struct stat st;
char buf[1024];
htsmsg_t *config2;
config = htsmsg_create_map();
const char *homedir = getenv("HOME");
int new = 0;
/* Generate default */
if (!path) {
const char *homedir = getenv("HOME");
if (homedir == NULL) {
tvherror("START", "environment variable HOME is not set");
exit(EXIT_FAILURE);
}
snprintf(buf, sizeof(buf), "%s/.hts/tvheadend", homedir);
path = buf;
}
/* Ensure directory exists */
if (stat(path, &st)) {
config_newcfg = 1;
if (makedirs(path, 0700, gid, uid)) {
new = 1;
if (makedirs(path, 0700)) {
tvhwarn("START", "failed to create settings directory %s,"
" settings will not be saved", path);
return;
@ -1432,39 +1329,15 @@ config_boot ( const char *path, gid_t gid, uid_t uid )
/* Configure settings routines */
hts_settings_init(path);
/* Lock it */
hts_settings_buildpath(config_lock, sizeof(config_lock), ".lock");
if ((config_lock_fd = file_lock(config_lock, 3)) < 0)
exit(78); /* config error */
if (chown(config_lock, uid, gid))
tvhwarn("config", "unable to chown lock file %s UID:%d GID:%d", config_lock, uid, gid);
/* Load global settings */
config2 = hts_settings_load("config");
if (!config2) {
config = hts_settings_load("config");
if (!config) {
tvhlog(LOG_DEBUG, "config", "no configuration, loading defaults");
} else {
htsmsg_destroy(config);
config = config2;
}
}
void
config_init ( int backup )
{
const char *path = hts_settings_get_root();
if (path == NULL || access(path, R_OK | W_OK)) {
tvhwarn("START", "configuration path %s is not r/w"
" for UID:%d GID:%d [e=%s],"
" settings will not be saved",
path, getuid(), getgid(), strerror(errno));
return;
config = htsmsg_create_map();
}
/* Store version number */
if (config_newcfg) {
if (new) {
htsmsg_set_u32(config, "version", ARRAY_SIZE(config_migrate_table));
htsmsg_set_str(config, "fullversion", tvheadend_version);
config_save();
@ -1474,14 +1347,11 @@ config_init ( int backup )
if (config_migrate(backup))
config_check();
}
tvhinfo("config", "loaded");
}
void config_done ( void )
{
/* note: tvhlog is inactive !!! */
htsmsg_destroy(config);
file_unlock(config_lock, config_lock_fd);
}
void config_save ( void )
@ -1498,14 +1368,8 @@ htsmsg_t *config_get_all ( void )
return htsmsg_copy(config);
}
const char *
config_get_str ( const char *fld )
{
return htsmsg_get_str(config, fld);
}
int
config_set_str ( const char *fld, const char *val )
static int
_config_set_str ( const char *fld, const char *val )
{
const char *c = htsmsg_get_str(config, fld);
if (!c || strcmp(c, val)) {
@ -1516,26 +1380,6 @@ config_set_str ( const char *fld, const char *val )
return 0;
}
int
config_get_int ( const char *fld, int deflt )
{
return htsmsg_get_s32_or_default(config, fld, deflt);
}
int
config_set_int ( const char *fld, int val )
{
const char *c = htsmsg_get_str(config, fld);
char buf[16];
snprintf(buf, sizeof(buf), "%d", val);
if (!c || strcmp(c, buf)) {
if (c) htsmsg_delete_field(config, fld);
htsmsg_add_s32(config, fld, val);
return 1;
}
return 0;
}
const char *config_get_language ( void )
{
return htsmsg_get_str(config, "language");
@ -1543,7 +1387,7 @@ const char *config_get_language ( void )
int config_set_language ( const char *lang )
{
return config_set_str("language", lang);
return _config_set_str("language", lang);
}
const char *config_get_muxconfpath ( void )
@ -1553,7 +1397,7 @@ const char *config_get_muxconfpath ( void )
int config_set_muxconfpath ( const char *path )
{
return config_set_str("muxconfpath", path);
return _config_set_str("muxconfpath", path);
}
int config_get_prefer_picon ( void )
@ -1565,7 +1409,7 @@ int config_get_prefer_picon ( void )
int config_set_prefer_picon ( const char *str )
{
return config_set_str("prefer_picon", str);
return _config_set_str("prefer_picon", str);
}
const char *config_get_chicon_path ( void )
@ -1575,7 +1419,7 @@ const char *config_get_chicon_path ( void )
int config_set_chicon_path ( const char *str )
{
return config_set_str("chiconpath", str);
return _config_set_str("chiconpath", str);
}
const char *config_get_picon_path ( void )
@ -1585,5 +1429,5 @@ const char *config_get_picon_path ( void )
int config_set_picon_path ( const char *str )
{
return config_set_str("piconpath", str);
return _config_set_str("piconpath", str);
}

View file

@ -21,21 +21,14 @@
#ifndef __TVH_CONFIG__H__
#define __TVH_CONFIG__H__
#include <unistd.h>
#include "htsmsg.h"
void config_boot ( const char *path, gid_t gid, uid_t uid );
void config_init ( int backup );
void config_init ( const char *path, int backup );
void config_done ( void );
void config_save ( void );
htsmsg_t *config_get_all ( void );
const char *config_get_str ( const char *fld );
int config_set_str ( const char *fld, const char *val );
int config_get_int ( const char *fld, int dflt );
int config_set_int ( const char *fld, int val );
const char *config_get_muxconfpath ( void );
int config_set_muxconfpath ( const char *str )
__attribute__((warn_unused_result));

5
src/descrambler.h Normal file → Executable file
View file

@ -66,7 +66,6 @@ typedef struct th_descrambler_runtime {
uint32_t dr_key_first:1;
uint8_t dr_key_index;
uint8_t dr_key_valid;
uint8_t dr_key_changed;
time_t dr_key_start;
time_t dr_key_timestamp[2];
time_t dr_ecm_start;
@ -74,8 +73,6 @@ typedef struct th_descrambler_runtime {
time_t dr_last_err;
sbuf_t dr_buf;
tvhlog_limit_t dr_loglimit_key;
uint8_t dr_key_even[16];
uint8_t dr_key_odd[16];
} th_descrambler_runtime_t;
typedef void (*descrambler_section_callback_t)
@ -167,7 +164,7 @@ void descrambler_keys ( th_descrambler_t *t, int type,
const uint8_t *even, const uint8_t *odd );
int descrambler_descramble ( struct service *t,
struct elementary_stream *st,
const uint8_t *tsb, int len );
const uint8_t *tsb );
int descrambler_open_pid ( struct mpegts_mux *mux, void *opaque, int pid,
descrambler_section_callback_t callback,
struct service *service );

View file

@ -296,9 +296,6 @@ caclient_start ( struct service *t )
if (cac->cac_enabled)
cac->cac_start(cac, t);
pthread_mutex_unlock(&caclients_mutex);
#if ENABLE_TSDEBUG
tsdebugcw_service_start(t);
#endif
}
void
@ -347,9 +344,6 @@ caclient_init(void)
pthread_mutex_init(&caclients_mutex, NULL);
TAILQ_INIT(&caclients);
#if ENABLE_TSDEBUG
tsdebugcw_init();
#endif
if (!(c = hts_settings_load("caclient")))
return;

View file

@ -75,14 +75,8 @@ const char *caclient_get_status(caclient_t *cac);
void caclient_init(void);
void caclient_done(void);
void tsdebugcw_service_start(struct service *t);
void tsdebugcw_new_keys(struct service *t, int type, uint8_t *odd, uint8_t *even);
void tsdebugcw_go(void);
void tsdebugcw_init(void);
caclient_t *cwc_create(void);
caclient_t *capmt_create(void);
caclient_t *constcw_create(void);
caclient_t *tsdebugcw_create(void);
#endif /* __TVH_CACLIENT_H__ */

View file

@ -67,8 +67,6 @@ typedef struct dmx_filter {
uint8_t mode[DMX_FILTER_SIZE];
} dmx_filter_t;
#define DVBAPI_PROTOCOL_VERSION 1
#define CA_SET_DESCR 0x40106f86
#define CA_SET_DESCR_X 0x866f1040
#define CA_SET_DESCR_AES 0x40106f87
@ -79,10 +77,6 @@ typedef struct dmx_filter {
#define DMX_STOP_X 0x2a6f0000
#define DMX_SET_FILTER 0x403c6f2b
#define DMX_SET_FILTER_X 0x2b6f3c40
#define DVBAPI_FILTER_DATA 0xFFFF0000
#define DVBAPI_CLIENT_INFO 0xFFFF0001
#define DVBAPI_SERVER_INFO 0xFFFF0002
// ca_pmt_list_management values:
#define CAPMT_LIST_MORE 0x00 // append a 'MORE' CAPMT object the list and start receiving the next object
@ -108,20 +102,18 @@ typedef struct dmx_filter {
#define CAPMT_MSG_CLEAR 0x02
// limits
#define MAX_CA 16
#define MAX_INDEX 64
#define MAX_FILTER 64
#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux
#define MAX_PIDS 64 // max opened pids
#define MAX_INFO_LEN 255
#define MAX_CA 16
#define MAX_INDEX 64
#define MAX_FILTER 64
#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux
#define MAX_PIDS 64 // max opened pids
typedef enum {
CAPMT_OSCAM_SO_WRAPPER,
CAPMT_OSCAM_OLD,
CAPMT_OSCAM_MULTILIST,
CAPMT_OSCAM_TCP,
CAPMT_OSCAM_UNIX_SOCKET,
CAPMT_OSCAM_NET_PROTO
CAPMT_OSCAM_UNIX_SOCKET
} capmt_oscam_mode_t;
/**
@ -288,7 +280,6 @@ static void capmt_notify_server(capmt_t *capmt, capmt_service_t *ct, int force);
static void capmt_send_request(capmt_service_t *ct, int lm);
static void capmt_table_input(void *opaque, int pid,
const uint8_t *data, int len);
static void capmt_send_client_info(capmt_t *capmt);
/**
*
@ -449,7 +440,7 @@ capmt_connect(capmt_t *capmt, int i)
if (!capmt->capmt_running)
return -1;
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP) {
char errbuf[256];
@ -492,8 +483,6 @@ capmt_connect(capmt_t *capmt, int i)
tvhlog(LOG_DEBUG, "capmt", "%s: Created socket %d", capmt_name(capmt), fd);
capmt->capmt_sock[i] = fd;
capmt->capmt_sock_reconnect[i]++;
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO)
capmt_send_client_info(capmt);
capmt_poll_add(capmt, fd, i + 1);
}
@ -739,27 +728,6 @@ capmt_send_stop(capmt_service_t *t)
}
}
/**
*
*/
static void
capmt_send_stop_descrambling(capmt_t *capmt)
{
uint8_t buf[8];
buf[0] = 0x9F;
buf[1] = 0x80;
buf[2] = 0x3F;
buf[3] = 0x04;
buf[4] = 0x83;
buf[5] = 0x02;
buf[6] = 0x00;
buf[7] = 0xFF; //wildcard demux id
capmt_write_msg(capmt, 0, 0, buf, 8);
}
/**
* global_lock is held
* s_stream_mutex is held
@ -808,21 +776,6 @@ capmt_service_destroy(th_descrambler_t *td)
free(ct);
}
static void
capmt_send_client_info(capmt_t *capmt)
{
char buf[MAX_INFO_LEN + 7];
*(uint32_t *)(buf + 0) = htonl(DVBAPI_CLIENT_INFO);
*(uint16_t *)(buf + 4) = htons(DVBAPI_PROTOCOL_VERSION); //supported protocol version
int len = snprintf(buf + 7, sizeof(buf) - 7, "Tvheadend %s", tvheadend_version);
if (len >= sizeof(buf) - 7)
len = sizeof(buf) - 7 - 1;
buf[6] = len;
capmt_queue_msg(capmt, 0, 0, (uint8_t *)&buf, len + 7, CAPMT_MSG_FAST);
}
static void
capmt_filter_data(capmt_t *capmt, uint8_t adapter, uint8_t demux_index,
uint8_t filter_index, const uint8_t *data, int len,
@ -830,7 +783,8 @@ capmt_filter_data(capmt_t *capmt, uint8_t adapter, uint8_t demux_index,
{
uint8_t *buf = alloca(len + 6);
*(uint32_t *)(buf + 0) = htonl(DVBAPI_FILTER_DATA);
buf[0] = buf[1] = 0xff;
buf[2] = buf[3] = 0;
buf[4] = demux_index;
buf[5] = filter_index;
memcpy(buf + 6, data, len);
@ -867,8 +821,6 @@ capmt_set_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
if (filter->pid && pid != filter->pid)
capmt_pid_remove(capmt, adapter, filter->pid);
filter->pid = pid;
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO)
offset = 1; //filter data starts +1 byte because of adapter no
memcpy(&filter->filter, sbuf_peek(sb, offset + 8), sizeof(filter->filter));
tvhlog_hexdump("capmt", filter->filter.filter, DMX_FILTER_SIZE);
tvhlog_hexdump("capmt", filter->filter.mask, DMX_FILTER_SIZE);
@ -902,15 +854,10 @@ capmt_stop_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
{
uint8_t demux_index = sbuf_peek_u8 (sb, offset + 4);
uint8_t filter_index = sbuf_peek_u8 (sb, offset + 5);
int16_t pid;
int16_t pid = sbuf_peek_s16be(sb, offset + 6);
capmt_dmx_t *filter;
capmt_filters_t *cf;
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO)
pid = sbuf_peek_s16 (sb, offset + 6);
else
pid = sbuf_peek_s16be(sb, offset + 6);
tvhtrace("capmt", "%s: stopping filter: adapter=%d, demux=%d, filter=%d, pid=%d",
capmt_name(capmt), adapter, demux_index, filter_index, pid);
if (adapter >= MAX_CA ||
@ -939,10 +886,6 @@ capmt_stop_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
static void
capmt_notify_server(capmt_t *capmt, capmt_service_t *ct, int force)
{
/* flush out the greeting */
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO)
capmt_flush_queue(capmt, 0);
pthread_mutex_lock(&capmt->capmt_mutex);
if (capmt_oscam_new(capmt)) {
if (!LIST_EMPTY(&capmt->capmt_services))
@ -1029,40 +972,32 @@ static int
capmt_msg_size(capmt_t *capmt, sbuf_t *sb, int offset)
{
uint32_t cmd;
uint8_t adapter_byte = 0;
int oscam_new = capmt_oscam_new(capmt);
if (sb->sb_ptr - offset < 4)
return 0;
cmd = sbuf_peek_u32(sb, offset);
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
adapter_byte = 1; //we need to take into account the adapter index byte which is now after the cmd
} else {
if (!sb->sb_bswap && !sb->sb_err) {
if (cmd == CA_SET_PID_X ||
cmd == CA_SET_DESCR_X ||
cmd == CA_SET_DESCR_AES_X ||
cmd == DMX_SET_FILTER_X ||
cmd == DMX_STOP_X) {
sb->sb_bswap = 1;
cmd = sbuf_peek_u32(sb, offset);
}
if (!sb->sb_bswap && !sb->sb_err) {
if (cmd == CA_SET_PID_X ||
cmd == CA_SET_DESCR_X ||
cmd == CA_SET_DESCR_AES_X ||
cmd == DMX_SET_FILTER_X ||
cmd == DMX_STOP_X) {
sb->sb_bswap = 1;
cmd = sbuf_peek_u32(sb, offset);
}
}
sb->sb_err = 1; /* "first seen" flag for the moment */
if (cmd == CA_SET_PID)
return 4 + 8 + adapter_byte;
return 4 + 8;
else if (cmd == CA_SET_DESCR)
return 4 + 16 + adapter_byte;
return 4 + 16;
else if (cmd == CA_SET_DESCR_AES)
return 4 + 32;
else if (oscam_new && cmd == DMX_SET_FILTER)
//when using network protocol the dmx_sct_filter_params fields are added seperately to avoid padding problems, so we substract 2 bytes:
return 4 + 2 + 60 + adapter_byte + (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO ? -2 : 0);
return 4 + 2 + 60;
else if (oscam_new && cmd == DMX_STOP)
return 4 + 4 + adapter_byte;
else if (oscam_new && cmd == DVBAPI_SERVER_INFO && sb->sb_ptr > 6)
return 4 + 2 + 1 + sbuf_peek_u8(sb, 6);
return 4 + 4;
else {
sb->sb_err = 0;
return -1; /* fatal */
@ -1077,11 +1012,6 @@ capmt_analyze_cmd(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
cmd = sbuf_peek_u32(sb, offset);
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO && cmd != DVBAPI_SERVER_INFO) {
adapter = sbuf_peek_u8(sb, 4);
offset = 1;
}
if (cmd == CA_SET_PID) {
uint32_t seq = sbuf_peek_u32(sb, offset + 4);
@ -1158,23 +1088,13 @@ capmt_analyze_cmd(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
capmt_stop_filter(capmt, adapter, sb, offset);
} else if (cmd == DVBAPI_SERVER_INFO) {
uint16_t protocol_version = sbuf_peek_u16(sb, offset + 4);
uint8_t len = sbuf_peek_u8(sb, offset + 4 + 2);
unsigned char oscam_info[len+1];
memcpy(&oscam_info, sbuf_peek(sb, offset + 4 + 2 + 1), len);
oscam_info[len] = 0; //null-terminating the string
tvhlog(LOG_INFO, "capmt", "%s: connected to %s, using network protocol_version = %d", capmt_name(capmt), oscam_info, protocol_version);
}
}
static void
show_connection(capmt_t *capmt, const char *what)
{
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP) {
tvhlog(LOG_INFO, "capmt",
"%s: mode %i connected to %s:%i (%s)",
capmt_name(capmt),
@ -1302,7 +1222,7 @@ handle_ca0(capmt_t *capmt) {
static void
handle_single(capmt_t *capmt)
{
int ret, recvsock, adapter, nfds, cmd_size, reconnect, offset;
int ret, recvsock, adapter, nfds, cmd_size, reconnect;
uint8_t buf[256];
sbuf_t buffer;
tvhpoll_event_t ev;
@ -1364,24 +1284,18 @@ handle_single(capmt_t *capmt)
while (buffer.sb_ptr > 0) {
cmd_size = 0;
adapter = -1;
offset = 0;
while (buffer.sb_ptr > 0) {
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
buffer.sb_bswap = 1;
} else {
adapter = buffer.sb_data[0];
offset = 1;
}
if (adapter < MAX_CA) {
cmd_size = capmt_msg_size(capmt, &buffer, offset);
if (cmd_size > 0)
cmd_size = capmt_msg_size(capmt, &buffer, 1);
if (cmd_size >= 0)
break;
}
sbuf_cut(&buffer, 1);
}
if (cmd_size + offset <= buffer.sb_ptr) {
capmt_analyze_cmd(capmt, adapter, &buffer, offset);
sbuf_cut(&buffer, cmd_size + offset);
if (cmd_size + 1 <= buffer.sb_ptr) {
capmt_analyze_cmd(capmt, adapter, &buffer, 1);
sbuf_cut(&buffer, cmd_size + 1);
} else {
break;
}
@ -1488,7 +1402,6 @@ capmt_thread(void *aux)
/* Accessible */
if (capmt->capmt_sockfile && capmt->capmt_oscam != CAPMT_OSCAM_TCP &&
capmt->capmt_oscam != CAPMT_OSCAM_NET_PROTO &&
!access(capmt->capmt_sockfile, R_OK | W_OK))
caclient_set_status((caclient_t *)capmt, CACLIENT_STATUS_NONE);
else
@ -1546,7 +1459,6 @@ capmt_thread(void *aux)
}
#else
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP ||
capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO ||
capmt->capmt_oscam == CAPMT_OSCAM_UNIX_SOCKET) {
handle_single(capmt);
} else {
@ -1666,7 +1578,7 @@ capmt_caid_change(th_descrambler_t *td)
lock_assert(&t->s_stream_mutex);
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
if (t->s_dvb_prefcapid_lock == PREFCAPID_FORCE &&
if (t->s_dvb_prefcapid_lock == 2 &&
t->s_dvb_prefcapid != st->es_pid)
continue;
LIST_FOREACH(c, &st->es_caids, link) {
@ -1799,13 +1711,11 @@ capmt_send_request(capmt_service_t *ct, int lm)
}
memcpy(&buf[pos], &cad, cad.cad_length + 2);
pos += cad.cad_length + 2;
tvhlog(LOG_DEBUG, "capmt", "%s: adding ECMPID=0x%X (%d), "
"CAID=0x%X (%d) PROVID=0x%X (%d), SID=%d, ADAPTER=%d",
tvhlog(LOG_DEBUG, "capmt", "%s: adding ECMPID=0x%X (%d), CAID=0x%X (%d) PROVID=0x%X (%d)",
capmt_name(capmt),
cce2->cce_ecmpid, cce2->cce_ecmpid,
cce2->cce_caid, cce2->cce_caid,
cce2->cce_providerid, cce2->cce_providerid,
sid, adapter_num);
cce2->cce_providerid, cce2->cce_providerid);
}
uint8_t end[] = {
@ -1853,12 +1763,7 @@ capmt_enumerate_services(capmt_t *capmt, int force)
tvhlog(LOG_DEBUG, "capmt", "%s: %s: no subscribed services, closing socket, fd=%d", capmt_name(capmt), __FUNCTION__, capmt->capmt_sock[0]);
if (capmt->capmt_sock[0] >= 0)
caclient_set_status((caclient_t *)capmt, CACLIENT_STATUS_READY);
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
capmt_send_stop_descrambling(capmt);
capmt_pid_flush(capmt);
}
else
capmt_socket_close(capmt, 0);
capmt_socket_close(capmt, 0);
}
else if (force || (res_srv_count != all_srv_count)) {
LIST_FOREACH(ct, &capmt->capmt_services, ct_link) {
@ -1912,7 +1817,6 @@ capmt_service_start(caclient_t *cac, service_t *s)
goto fin;
if (tuner < 0 && capmt->capmt_oscam != CAPMT_OSCAM_TCP &&
capmt->capmt_oscam != CAPMT_OSCAM_NET_PROTO &&
capmt->capmt_oscam != CAPMT_OSCAM_UNIX_SOCKET) {
tvhlog(LOG_WARNING, "capmt",
"%s: Virtual adapters are supported only in modes 3 and 4 (service \"%s\")",
@ -1958,7 +1862,7 @@ capmt_service_start(caclient_t *cac, service_t *s)
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
caid_t *c;
if (t->s_dvb_prefcapid_lock == PREFCAPID_FORCE &&
if (t->s_dvb_prefcapid_lock == 2 &&
t->s_dvb_prefcapid != st->es_pid)
continue;
LIST_FOREACH(c, &st->es_caids, link) {
@ -2016,7 +1920,7 @@ capmt_free(caclient_t *cac)
tvhlog(LOG_INFO, "capmt", "%s: mode %i %s %s port %i destroyed",
capmt_name(capmt),
capmt->capmt_oscam,
capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO ? "IP address" : "sockfile",
capmt->capmt_oscam == CAPMT_OSCAM_TCP ? "IP address" : "sockfile",
capmt->capmt_sockfile, capmt->capmt_port);
capmt_flush_queue(capmt, 1);
free(capmt->capmt_sockfile);
@ -2067,12 +1971,11 @@ static htsmsg_t *
caclient_capmt_class_oscam_mode_list ( void *o )
{
static const struct strtab tab[] = {
{ "OSCam net protocol (rev >= 10389)", CAPMT_OSCAM_NET_PROTO },
{ "OSCam pc-nodmx (rev >= 9756)", CAPMT_OSCAM_UNIX_SOCKET },
{ "OSCam TCP (rev >= 9574)", CAPMT_OSCAM_TCP },
{ "OSCam (rev >= 9095)", CAPMT_OSCAM_MULTILIST },
{ "Older OSCam", CAPMT_OSCAM_OLD },
{ "Wrapper (capmt_ca.so)", CAPMT_OSCAM_SO_WRAPPER },
{ "OSCam pc-nodmx (rev >= 9756)", CAPMT_OSCAM_UNIX_SOCKET},
{ "OSCam TCP (rev >= 9574)", CAPMT_OSCAM_TCP },
{ "OSCam (rev >= 9095)", CAPMT_OSCAM_MULTILIST },
{ "Older OSCam", CAPMT_OSCAM_OLD },
{ "Wrapper (capmt_ca.so)", CAPMT_OSCAM_SO_WRAPPER },
};
return strtab2htsmsg(tab);
}
@ -2099,7 +2002,7 @@ const idclass_t caclient_capmt_class =
.def.s = "/tmp/camd.socket",
},
{
.type = PT_INT,
.type = PT_U16,
.id = "port",
.name = "Listen/Connect Port",
.off = offsetof(capmt_t, capmt_port),

157
src/descrambler/cwc.c Normal file → Executable file
View file

@ -272,7 +272,6 @@ typedef struct cwc {
*
*/
static void cwc_service_pid_free(cwc_service_t *ct);
static void cwc_service_destroy(th_descrambler_t *td);
void cwc_emm_conax(cwc_t *cwc, struct cs_card_data *pcard, const uint8_t *data, int len);
void cwc_emm_irdeto(cwc_t *cwc, struct cs_card_data *pcard, const uint8_t *data, int len);
@ -667,20 +666,16 @@ static int
cwc_ecm_reset(th_descrambler_t *th)
{
cwc_service_t *ct = (cwc_service_t *)th;
cwc_t *cwc = ct->cs_cwc;
ecm_pid_t *ep;
ecm_section_t *es;
if (ct->cs_constcw)
return 1; /* keys will not change */
pthread_mutex_lock(&cwc->cwc_mutex);
ct->td_keystate = DS_UNKNOWN;
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
LIST_FOREACH(es, &ep->ep_sections, es_link)
es->es_keystate = ES_UNKNOWN;
ct->ecm_state = ECM_RESET;
pthread_mutex_unlock(&cwc->cwc_mutex);
return 0;
}
@ -688,16 +683,13 @@ static void
cwc_ecm_idle(th_descrambler_t *th)
{
cwc_service_t *ct = (cwc_service_t *)th;
cwc_t *cwc = ct->cs_cwc;
ecm_pid_t *ep;
ecm_section_t *es;
pthread_mutex_lock(&cwc->cwc_mutex);
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
LIST_FOREACH(es, &ep->ep_sections, es_link)
es->es_keystate = ES_IDLE;
ct->ecm_state = ECM_RESET;
pthread_mutex_unlock(&cwc->cwc_mutex);
}
static void
@ -705,7 +697,6 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg,
int len, int seq)
{
mpegts_service_t *t = (mpegts_service_t *)ct->td_service;
cwc_t *cwc = ct->cs_cwc;
ecm_pid_t *ep;
ecm_section_t *es2;
char chaninfo[32];
@ -778,7 +769,7 @@ forbid:
ct->td_keystate = DS_FORBIDDEN;
ct->ecm_state = ECM_RESET;
/* this pid is not valid, force full scan */
if (t->s_dvb_prefcapid == ct->cs_channel && t->s_dvb_prefcapid_lock == PREFCAPID_OFF)
if (t->s_dvb_prefcapid == ct->cs_channel && t->s_dvb_prefcapid_lock == 0)
t->s_dvb_prefcapid = 0;
}
return;
@ -791,7 +782,7 @@ forbid:
if(t->s_dvb_prefcapid == 0 ||
(t->s_dvb_prefcapid != ct->cs_channel &&
t->s_dvb_prefcapid_lock == PREFCAPID_OFF)) {
t->s_dvb_prefcapid_lock == 0)) {
t->s_dvb_prefcapid = ct->cs_channel;
tvhlog(LOG_DEBUG, "cwc", "Saving prefered PID %d for %s",
t->s_dvb_prefcapid, ct->td_nicename);
@ -818,9 +809,7 @@ forbid:
es->es_keystate = ES_RESOLVED;
es->es_resolved = 1;
pthread_mutex_unlock(&cwc->cwc_mutex);
descrambler_keys((th_descrambler_t *)ct, DESCRAMBLER_DES, msg + 3, msg + 3 + 8);
pthread_mutex_lock(&cwc->cwc_mutex);
} else {
tvhlog(LOG_DEBUG, "cwc",
"Received ECM reply%s for service \"%s\" "
@ -844,9 +833,7 @@ forbid:
es->es_keystate = ES_RESOLVED;
es->es_resolved = 1;
pthread_mutex_unlock(&cwc->cwc_mutex);
descrambler_keys((th_descrambler_t *)ct, DESCRAMBLER_AES, msg + 3, msg + 3 + 16);
pthread_mutex_lock(&cwc->cwc_mutex);
}
}
}
@ -1619,6 +1606,7 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
ecm_section_t *es;
char chaninfo[32];
struct cs_card_data *pcard = NULL;
int i_break;
caid_t *c;
uint16_t caid;
uint32_t providerid;
@ -1626,37 +1614,33 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
if (data == NULL)
return;
if (ct->td_keystate == DS_IDLE)
return;
if(len > 4096)
return;
if((data[0] & 0xf0) != 0x80)
return;
pthread_mutex_lock(&t->s_stream_mutex);
pthread_mutex_lock(&cwc->cwc_mutex);
if (ct->td_keystate == DS_IDLE)
goto end;
if (ct->ecm_state == ECM_RESET) {
/* clean all */
cwc_service_pid_free(ct);
/* move to init state */
ct->ecm_state = ECM_INIT;
ct->cs_channel = -1;
t->s_dvb_prefcapid = 0;
tvhlog(LOG_DEBUG, "cwc", "Reset after unexpected or no reply for service \"%s\"", t->s_dvb_svcname);
LIST_FOREACH(ep, &ct->cs_pids, ep_link) {
if(ep->ep_pid == pid)
break;
}
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
if(ep->ep_pid == pid) break;
pthread_mutex_lock(&t->s_stream_mutex);
if(ep == NULL) {
tvhlog(LOG_DEBUG, "cwc", "ECM state %i", ct->ecm_state);
if (ct->ecm_state == ECM_RESET) {
ct->ecm_state = ECM_INIT;
ct->cs_channel = -1;
t->s_dvb_prefcapid = 0;
tvhlog(LOG_DEBUG, "cwc", "Reset after unexpected or no reply for service \"%s\"", t->s_dvb_svcname);
}
if (ct->ecm_state == ECM_INIT) {
// Validate prefered ECM PID
tvhlog(LOG_DEBUG, "cwc", "ECM state INIT");
if(t->s_dvb_prefcapid != PREFCAPID_OFF) {
if(t->s_dvb_prefcapid != 0) {
struct elementary_stream *prefca
= service_stream_find((service_t*)t, t->s_dvb_prefcapid);
if (!prefca || prefca->es_type != SCT_CA) {
@ -1665,32 +1649,52 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
}
}
if(t->s_dvb_prefcapid == pid || t->s_dvb_prefcapid == 0) {
if(t->s_dvb_prefcapid == pid) {
ep = calloc(1, sizeof(ecm_pid_t));
ep->ep_pid = pid;
ep->ep_pid = t->s_dvb_prefcapid;
LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link);
tvhlog(LOG_DEBUG, "cwc", "Insert %s ECM (PID %d) for service \"%s\"",
t->s_dvb_prefcapid ? "preferred" : "new", pid, t->s_dvb_svcname);
tvhlog(LOG_DEBUG, "cwc", "Insert prefered ECM (PID %d) for service \"%s\"", t->s_dvb_prefcapid, t->s_dvb_svcname);
}
else if(t->s_dvb_prefcapid == 0) {
ep = calloc(1, sizeof(ecm_pid_t));
ep->ep_pid = pid;
LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link);
tvhlog(LOG_DEBUG, "cwc", "Insert new ECM (PID %d) for service \"%s\"", pid, t->s_dvb_svcname);
}
}
}
if(ep == NULL)
goto end;
st = service_stream_find((service_t *)t, pid);
if (st) {
LIST_FOREACH(c, &st->es_caids, link)
LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card)
if(pcard->cwc_caid == c->caid && verify_provider(pcard, c->providerid))
goto found;
if(ep == NULL) {
pthread_mutex_unlock(&t->s_stream_mutex);
return;
}
goto end;
i_break = 0;
c = NULL;
TAILQ_FOREACH(st, &t->s_components, es_link) {
if (st->es_pid != pid) continue;
LIST_FOREACH(c, &st->es_caids, link) {
LIST_FOREACH(pcard,&cwc->cwc_cards, cs_card) {
if(pcard->cwc_caid == c->caid && verify_provider(pcard, c->providerid)){
i_break = 1;
break;
}
}
if (i_break == 1) break;
}
if (i_break == 1) break;
}
if(c == NULL) {
pthread_mutex_unlock(&t->s_stream_mutex);
return;
}
found:
caid = c->caid;
providerid = c->providerid;
pthread_mutex_unlock(&t->s_stream_mutex);
switch(data[0]) {
case 0x80:
case 0x81:
@ -1733,7 +1737,7 @@ found:
if(ct->cs_channel >= 0 && channel != -1 &&
ct->cs_channel != channel) {
tvhlog(LOG_DEBUG, "cwc", "Filtering ECM (PID %d)", channel);
goto end;
return;
}
es->es_seq = cwc_send_msg(cwc, data, len, sid, 1, caid, providerid);
@ -1750,10 +1754,6 @@ found:
cwc_send_msg(cwc, data, len, sid, 1, 0, 0);
break;
}
end:
pthread_mutex_unlock(&cwc->cwc_mutex);
pthread_mutex_unlock(&t->s_stream_mutex);
}
/**
@ -1947,10 +1947,16 @@ cwc_emm_bulcrypt(cwc_t *cwc, struct cs_card_data *pcard, const uint8_t *data, in
* s_stream_mutex is held
*/
static void
cwc_service_pid_free(cwc_service_t *ct)
cwc_service_destroy(th_descrambler_t *td)
{
cwc_service_t *ct = (cwc_service_t *)td;
ecm_pid_t *ep;
ecm_section_t *es;
int i;
for (i = 0; i < CWC_ES_PIDS; i++)
if (ct->cs_epids[i])
descrambler_close_pid(ct->cs_mux, ct, ct->cs_epids[i]);
while((ep = LIST_FIRST(&ct->cs_pids)) != NULL) {
while ((es = LIST_FIRST(&ep->ep_sections)) != NULL) {
@ -1960,23 +1966,6 @@ cwc_service_pid_free(cwc_service_t *ct)
LIST_REMOVE(ep, ep_link);
free(ep);
}
}
/**
* cwc_mutex is held
* s_stream_mutex is held
*/
static void
cwc_service_destroy(th_descrambler_t *td)
{
cwc_service_t *ct = (cwc_service_t *)td;
int i;
for (i = 0; i < CWC_ES_PIDS; i++)
if (ct->cs_epids[i])
descrambler_close_pid(ct->cs_mux, ct, ct->cs_epids[i]);
cwc_service_pid_free(ct);
LIST_REMOVE(td, td_service_link);
@ -2007,16 +1996,16 @@ cwc_service_start(caclient_t *cac, service_t *t)
if (!idnode_is_instance(&t->s_id, &mpegts_service_class))
return;
pthread_mutex_lock(&t->s_stream_mutex);
pthread_mutex_lock(&cwc->cwc_mutex);
LIST_FOREACH(ct, &cwc->cwc_services, cs_link) {
if (ct->td_service == t && ct->cs_cwc == cwc)
break;
}
pthread_mutex_lock(&t->s_stream_mutex);
LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card) {
if (pcard->cwc_caid == 0) continue;
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
if (((mpegts_service_t *)t)->s_dvb_prefcapid_lock == PREFCAPID_FORCE &&
if (((mpegts_service_t *)t)->s_dvb_prefcapid_lock == 2 &&
((mpegts_service_t *)t)->s_dvb_prefcapid != st->es_pid)
continue;
LIST_FOREACH(c, &st->es_caids, link) {
@ -2031,10 +2020,16 @@ cwc_service_start(caclient_t *cac, service_t *t)
}
if (!pcard) {
if (ct) cwc_service_destroy((th_descrambler_t*)ct);
goto end;
pthread_mutex_unlock(&t->s_stream_mutex);
pthread_mutex_unlock(&cwc->cwc_mutex);
return;
}
pthread_mutex_unlock(&t->s_stream_mutex);
if (ct) {
pthread_mutex_unlock(&cwc->cwc_mutex);
return;
}
if (ct)
goto end;
ct = calloc(1, sizeof(cwc_service_t));
ct->cs_cwc = cwc;
@ -2044,7 +2039,7 @@ cwc_service_start(caclient_t *cac, service_t *t)
ct->cs_constcw = pcard->cwc_caid == 0x2600;
td = (th_descrambler_t *)ct;
snprintf(buf, sizeof(buf), "cwc-%s-%i-%04X", cwc->cwc_hostname, cwc->cwc_port, pcard->cwc_caid);
snprintf(buf, sizeof(buf), "cwc-%s-%i", cwc->cwc_hostname, cwc->cwc_port);
td->td_nicename = strdup(buf);
td->td_service = t;
td->td_stop = cwc_service_destroy;
@ -2054,6 +2049,7 @@ cwc_service_start(caclient_t *cac, service_t *t)
LIST_INSERT_HEAD(&cwc->cwc_services, ct, cs_link);
pthread_mutex_lock(&t->s_stream_mutex);
i = 0;
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
LIST_FOREACH(c, &st->es_caids, link)
@ -2063,6 +2059,7 @@ cwc_service_start(caclient_t *cac, service_t *t)
}
if (i == CWC_ES_PIDS) break;
}
pthread_mutex_unlock(&t->s_stream_mutex);
for (i = 0; i < CWC_ES_PIDS; i++)
if (ct->cs_epids[i])
@ -2073,9 +2070,7 @@ cwc_service_start(caclient_t *cac, service_t *t)
tvhlog(LOG_DEBUG, "cwc", "%s using CWC %s:%d",
service_nicename(t), cwc->cwc_hostname, cwc->cwc_port);
end:
pthread_mutex_unlock(&cwc->cwc_mutex);
pthread_mutex_unlock(&t->s_stream_mutex);
}
@ -2093,9 +2088,7 @@ cwc_free(caclient_t *cac)
while((ct = LIST_FIRST(&cwc->cwc_services)) != NULL) {
t = (mpegts_service_t *)ct->td_service;
pthread_mutex_lock(&t->s_stream_mutex);
pthread_mutex_lock(&cwc->cwc_mutex);
cwc_service_destroy((th_descrambler_t *)&ct);
pthread_mutex_lock(&cwc->cwc_mutex);
pthread_mutex_unlock(&t->s_stream_mutex);
}

211
src/descrambler/descrambler.c Normal file → Executable file
View file

@ -21,8 +21,6 @@
#include "caclient.h"
#include "ffdecsa/FFdecsa.h"
#include "input.h"
#include "input/mpegts/tsdemux.h"
#include "dvbcam.h"
struct caid_tab {
const char *name;
@ -85,9 +83,6 @@ descrambler_init ( void )
ffdecsa_init();
#endif
caclient_init();
#if ENABLE_LINUXDVB_CA
dvbcam_init();
#endif
}
void
@ -127,10 +122,6 @@ descrambler_service_start ( service_t *t )
tvhcsa_init(&dr->dr_csa);
}
caclient_start(t);
#if ENABLE_LINUXDVB_CA
dvbcam_service_start(t);
#endif
}
void
@ -139,10 +130,6 @@ descrambler_service_stop ( service_t *t )
th_descrambler_t *td;
th_descrambler_runtime_t *dr = t->s_descramble;
#if ENABLE_LINUXDVB_CA
dvbcam_service_stop(t);
#endif
while ((td = LIST_FIRST(&t->s_descramblers)) != NULL)
td->td_stop(td);
t->s_descramble = NULL;
@ -179,11 +166,10 @@ void
descrambler_keys ( th_descrambler_t *td, int type,
const uint8_t *even, const uint8_t *odd )
{
static uint8_t empty[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
service_t *t = td->td_service;
th_descrambler_runtime_t *dr;
th_descrambler_t *td2;
int j = 0;
int i, j = 0;
if (t == NULL || (dr = t->s_descramble) == NULL) {
td->td_keystate = DS_FORBIDDEN;
@ -209,20 +195,22 @@ descrambler_keys ( th_descrambler_t *td, int type,
goto fin;
}
if (memcmp(empty, even, dr->dr_csa.csa_keylen)) {
j++;
memcpy(dr->dr_key_even, even, dr->dr_csa.csa_keylen);
dr->dr_key_changed |= 1;
dr->dr_key_valid |= 0x40;
dr->dr_key_timestamp[0] = dispatch_clock;
}
if (memcmp(empty, odd, dr->dr_csa.csa_keylen)) {
j++;
memcpy(dr->dr_key_odd, odd, dr->dr_csa.csa_keylen);
dr->dr_key_changed |= 2;
dr->dr_key_valid |= 0x80;
dr->dr_key_timestamp[1] = dispatch_clock;
}
for (i = 0; i < dr->dr_csa.csa_keylen; i++)
if (even[i]) {
j++;
tvhcsa_set_key_even(&dr->dr_csa, even);
dr->dr_key_valid |= 0x40;
dr->dr_key_timestamp[0] = dispatch_clock;
break;
}
for (i = 0; i < dr->dr_csa.csa_keylen; i++)
if (odd[i]) {
j++;
tvhcsa_set_key_odd(&dr->dr_csa, odd);
dr->dr_key_valid |= 0x80;
dr->dr_key_timestamp[1] = dispatch_clock;
break;
}
if (j) {
if (td->td_keystate != DS_RESOLVED)
@ -264,40 +252,6 @@ descrambler_keys ( th_descrambler_t *td, int type,
fin:
pthread_mutex_unlock(&t->s_stream_mutex);
#if ENABLE_TSDEBUG
if (j) {
tsdebug_packet_t *tp = malloc(sizeof(*tp));
uint16_t keylen = dr->dr_csa.csa_keylen;
uint16_t sid = ((mpegts_service_t *)td->td_service)->s_dvb_service_id;
uint32_t pos = 0, crc;
mpegts_mux_t *mm = ((mpegts_service_t *)td->td_service)->s_dvb_mux;
if (!mm->mm_active)
return;
pthread_mutex_lock(&mm->mm_active->mmi_input->mi_output_lock);
tp->pos = mm->mm_tsdebug_pos;
memset(tp->pkt, 0xff, sizeof(tp->pkt));
tp->pkt[pos++] = 0x47; /* sync byte */
tp->pkt[pos++] = 0x1f; /* PID MSB */
tp->pkt[pos++] = 0xff; /* PID LSB */
tp->pkt[pos++] = 0x00; /* CC */
memcpy(tp->pkt + pos, "TVHeadendDescramblerKeys", 24);
pos += 24;
tp->pkt[pos++] = type & 0xff;
tp->pkt[pos++] = keylen & 0xff;
tp->pkt[pos++] = (sid >> 8) & 0xff;
tp->pkt[pos++] = sid & 0xff;
memcpy(tp->pkt + pos, even, keylen);
memcpy(tp->pkt + pos + keylen, odd, keylen);
pos += 2 * keylen;
crc = tvh_crc32(tp->pkt, pos, 0x859aa5ba);
tp->pkt[pos++] = (crc >> 24) & 0xff;
tp->pkt[pos++] = (crc >> 16) & 0xff;
tp->pkt[pos++] = (crc >> 8) & 0xff;
tp->pkt[pos++] = crc & 0xff;
TAILQ_INSERT_HEAD(&mm->mm_tsdebug_packets, tp, link);
pthread_mutex_unlock(&mm->mm_active->mmi_input->mi_output_lock);
}
#endif
}
static void
@ -334,7 +288,7 @@ key_update( th_descrambler_runtime_t *dr, uint8_t key )
if (dr->dr_key_start)
dr->dr_key_start = dispatch_clock;
else
/* We don't know the exact start key switch time */
/* We don't knoe the exact start key switch time */
dr->dr_key_start = dispatch_clock - 60;
}
@ -360,82 +314,37 @@ key_late( th_descrambler_runtime_t *dr, uint8_t ki )
return dr->dr_ecm_key_time + 2 < dr->dr_key_start;
}
static int
ecm_reset( service_t *t, th_descrambler_runtime_t *dr )
{
th_descrambler_t *td;
int ret = 0;
/* reset the reader ECM state */
LIST_FOREACH(td, &t->s_descramblers, td_service_link) {
if (!td->td_ecm_reset(td)) {
dr->dr_key_valid = 0;
ret = 1;
}
}
return ret;
}
int
descrambler_descramble ( service_t *t,
elementary_stream_t *st,
const uint8_t *tsb,
int len )
const uint8_t *tsb )
{
th_descrambler_t *td;
th_descrambler_runtime_t *dr = t->s_descramble;
int count, failed, resolved, off, len2, len3, flush_data = 0;
const uint8_t *tsb2;
uint8_t ki;
int count, failed, off, size, flush_data = 0;
uint8_t *tsb2, ki;
lock_assert(&t->s_stream_mutex);
if (dr == NULL) {
if ((tsb[3] & 0x80) == 0) {
ts_recv_packet2((mpegts_service_t *)t, tsb, len);
return 1;
}
if (dr == NULL)
return -1;
}
if (dr->dr_csa.csa_type == DESCRAMBLER_NONE && dr->dr_buf.sb_ptr == 0)
if ((tsb[3] & 0x80) == 0) {
ts_recv_packet2((mpegts_service_t *)t, tsb, len);
return 1;
}
count = failed = resolved = 0;
count = failed = 0;
LIST_FOREACH(td, &t->s_descramblers, td_service_link) {
count++;
switch (td->td_keystate) {
case DS_FORBIDDEN: failed++; break;
case DS_RESOLVED : resolved++; break;
default: break;
if (td->td_keystate == DS_FORBIDDEN) {
failed++;
continue;
}
}
if (resolved) {
/* update the keys */
if (dr->dr_key_changed) {
dr->dr_csa.csa_flush(&dr->dr_csa, (mpegts_service_t *)t);
if (dr->dr_key_changed & 1)
tvhcsa_set_key_even(&dr->dr_csa, dr->dr_key_even);
if (dr->dr_key_changed & 2)
tvhcsa_set_key_odd(&dr->dr_csa, dr->dr_key_odd);
dr->dr_key_changed = 0;
}
/* process the queued TS packets */
if (td->td_keystate != DS_RESOLVED)
continue;
if (dr->dr_buf.sb_ptr > 0) {
for (tsb2 = dr->dr_buf.sb_data, len2 = dr->dr_buf.sb_ptr;
len2 > 0; tsb2 += len3, len2 -= len3) {
for (off = 0, size = dr->dr_buf.sb_ptr; off < size; off += 188) {
tsb2 = dr->dr_buf.sb_data + off;
ki = tsb2[3];
if ((ki & 0x80) != 0x00) {
if (key_valid(dr, ki) == 0) {
sbuf_cut(&dr->dr_buf, tsb2 - dr->dr_buf.sb_data);
flush_data = 1;
goto next;
sbuf_cut(&dr->dr_buf, off);
goto next2;
}
if (dr->dr_key_index != (ki & 0x40) &&
dr->dr_key_start + 2 < dispatch_clock) {
@ -443,24 +352,23 @@ descrambler_descramble ( service_t *t,
(ki & 0x40) ? "odd" : "even",
((mpegts_service_t *)t)->s_dvb_svcname);
if (key_late(dr, ki)) {
if (ecm_reset(t, dr)) {
sbuf_cut(&dr->dr_buf, tsb2 - dr->dr_buf.sb_data);
flush_data = 1;
sbuf_cut(&dr->dr_buf, off);
if (!td->td_ecm_reset(td)) {
dr->dr_key_valid = 0;
goto next;
}
}
key_update(dr, ki);
}
}
len3 = mpegts_word_count(tsb2, len2, 0xFF0000C0);
dr->dr_csa.csa_descramble(&dr->dr_csa, (mpegts_service_t *)t, tsb2, len3);
dr->dr_csa.csa_descramble(&dr->dr_csa,
(mpegts_service_t *)td->td_service,
tsb2);
}
if (len2 == 0)
if (off > 0)
service_reset_streaming_status_flags(t, TSS_NO_ACCESS);
sbuf_free(&dr->dr_buf);
}
/* check for key change */
ki = tsb[3];
if ((ki & 0x80) != 0x00) {
if (key_valid(dr, ki) == 0) {
@ -469,7 +377,7 @@ descrambler_descramble ( service_t *t,
((mpegts_service_t *)t)->s_dvb_svcname,
(ki & 0x40) ? "odd stream key is not valid" :
"even stream key is not valid");
goto next;
continue;
}
if (dr->dr_key_index != (ki & 0x40) &&
dr->dr_key_start + 2 < dispatch_clock) {
@ -477,22 +385,27 @@ descrambler_descramble ( service_t *t,
(ki & 0x40) ? "odd" : "even",
((mpegts_service_t *)t)->s_dvb_svcname);
if (key_late(dr, ki)) {
tvherror("descrambler", "ECM late (%ld seconds) for service \"%s\"",
tvhtrace("descrambler", "ECM late (%ld seconds) for service \"%s\"",
dispatch_clock - dr->dr_ecm_key_time,
((mpegts_service_t *)t)->s_dvb_svcname);
if (ecm_reset(t, dr)) {
flush_data = 1;
if (!td->td_ecm_reset(td)) {
dr->dr_key_valid = 0;
goto next;
}
}
key_update(dr, ki);
}
}
dr->dr_csa.csa_descramble(&dr->dr_csa, (mpegts_service_t *)t, tsb, len);
dr->dr_csa.csa_descramble(&dr->dr_csa,
(mpegts_service_t *)td->td_service,
tsb);
service_reset_streaming_status_flags(t, TSS_NO_ACCESS);
return 1;
}
next:
flush_data = 1;
next2:
continue;
}
if (dr->dr_ecm_start) { /* ECM sent */
ki = tsb[3];
if ((ki & 0x80) != 0x00) {
@ -535,7 +448,7 @@ next:
((mpegts_service_t *)t)->s_dvb_svcname);
}
}
sbuf_append(&dr->dr_buf, tsb, len);
sbuf_append(&dr->dr_buf, tsb, 188);
service_set_streaming_status_flags(t, TSS_NO_ACCESS);
}
} else {
@ -556,22 +469,17 @@ descrambler_table_callback
descrambler_section_t *ds;
descrambler_ecmsec_t *des;
th_descrambler_runtime_t *dr;
int emm = (mt->mt_flags & MT_FAST) == 0;
if (len < 6)
return 0;
pthread_mutex_lock(&mt->mt_mux->mm_descrambler_lock);
TAILQ_FOREACH(ds, &dt->sections, link) {
if (!emm) {
LIST_FOREACH(des, &ds->ecmsecs, link)
if (des->number == ptr[4])
break;
} else {
des = LIST_FIRST(&ds->ecmsecs);
}
LIST_FOREACH(des, &ds->ecmsecs, link)
if (des->number == ptr[4])
break;
if (des == NULL) {
des = calloc(1, sizeof(*des));
des->number = emm ? 0 : ptr[4];
des->number = ptr[4];
LIST_INSERT_HEAD(&ds->ecmsecs, des, link);
}
if (des->last_data == NULL || len != des->last_data_len ||
@ -585,7 +493,7 @@ descrambler_table_callback
des->last_data_len = 0;
}
ds->callback(ds->opaque, mt->mt_pid, ptr, len);
if (!emm) { /* ECM */
if ((mt->mt_flags & MT_FAST) != 0) { /* ECM */
mpegts_service_t *t = mt->mt_service;
if (t) {
/* The keys are requested from this moment */
@ -598,8 +506,6 @@ descrambler_table_callback
} else
tvhtrace("descrambler", "Unknown fast table message (section %d, len %d, pid %d)",
des->number, len, mt->mt_pid);
} else {
tvhtrace("descrambler", "EMM message (len %d, pid %d)", len, mt->mt_pid);
}
}
}
@ -636,8 +542,7 @@ descrambler_open_pid_( mpegts_mux_t *mux, void *opaque, int pid,
TAILQ_INIT(&dt->sections);
dt->table = mpegts_table_add(mux, 0, 0, descrambler_table_callback,
dt, "descrambler",
MT_FULL | MT_DEFER | flags, pid,
MPS_WEIGHT_CA);
MT_FULL | MT_DEFER | flags, pid);
if (dt->table)
dt->table->mt_service = (mpegts_service_t *)service;
TAILQ_INSERT_TAIL(&mux->mm_descrambler_tables, dt, link);
@ -797,7 +702,7 @@ next:
caid = emm->caid;
pid = emm->pid;
tvhtrace("descrambler", "close emm caid %04X (%i) pid %04X (%i)", caid, caid, pid, pid);
descrambler_close_pid_(mux, emm->opaque, pid);
descrambler_close_pid(mux, emm->opaque, pid);
}
TAILQ_REMOVE(&mux->mm_descrambler_emms, emm, link);
TAILQ_INSERT_TAIL(&removing, emm, link);

View file

@ -1,215 +0,0 @@
/*
* tvheadend, DVB CAM interface
* Copyright (C) 2014 Damjan Marion
*
* 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 <ctype.h>
#include "tvheadend.h"
#include "caclient.h"
#include "service.h"
#include "input.h"
#include "dvbcam.h"
#include "input/mpegts/linuxdvb/linuxdvb_private.h"
#if ENABLE_LINUXDVB_CA
#define CAIDS_PER_CA_SLOT 16
typedef struct dvbcam_active_service {
TAILQ_ENTRY(dvbcam_active_service) link;
service_t *t;
uint8_t *last_pmt;
int last_pmt_len;
linuxdvb_ca_t *ca;
uint8_t slot;
} dvbcam_active_service_t;
typedef struct dvbcam_active_caid {
TAILQ_ENTRY(dvbcam_active_caid) link;
uint16_t caids[CAIDS_PER_CA_SLOT];
int num_caids;
linuxdvb_ca_t *ca;
uint8_t slot;
} dvbcam_active_caid_t;
TAILQ_HEAD(,dvbcam_active_service) dvbcam_active_services;
TAILQ_HEAD(,dvbcam_active_caid) dvbcam_active_caids;
pthread_mutex_t dvbcam_mutex;
void
dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids,
int num_caids)
{
dvbcam_active_caid_t *ac;
num_caids = MIN(CAIDS_PER_CA_SLOT, num_caids);
if ((ac = malloc(sizeof(*ac))) == NULL)
return;
ac->ca = lca;
ac->slot = slot;
memcpy(ac->caids, caids, num_caids * sizeof(uint16_t));
ac->num_caids = num_caids;
pthread_mutex_lock(&dvbcam_mutex);
TAILQ_INSERT_TAIL(&dvbcam_active_caids, ac, link);
pthread_mutex_unlock(&dvbcam_mutex);
}
void
dvbcam_unregister_cam(linuxdvb_ca_t * lca, uint8_t slot)
{
dvbcam_active_caid_t *ac, *ac_tmp;
dvbcam_active_service_t *as;
pthread_mutex_lock(&dvbcam_mutex);
/* remove pointer to this CAM in all active services */
TAILQ_FOREACH(as, &dvbcam_active_services, link)
if (as->ca == lca && as->slot == slot)
as->ca = NULL;
/* delete entry */
for (ac = TAILQ_FIRST(&dvbcam_active_caids); ac != NULL; ac = ac_tmp) {
ac_tmp = TAILQ_NEXT(ac, link);
if(ac && ac->ca == lca && ac->slot == slot) {
TAILQ_REMOVE(&dvbcam_active_caids, ac, link);
free(ac);
}
}
pthread_mutex_unlock(&dvbcam_mutex);
}
void
dvbcam_pmt_data(mpegts_service_t *s, const uint8_t *ptr, int len)
{
linuxdvb_frontend_t *lfe;
dvbcam_active_caid_t *ac;
dvbcam_active_service_t *as = NULL, *as2;
elementary_stream_t *st;
caid_t *c;
int i;
lfe = (linuxdvb_frontend_t*) s->s_dvb_active_input;
if (!lfe)
return;
pthread_mutex_lock(&dvbcam_mutex);
TAILQ_FOREACH(as2, &dvbcam_active_services, link)
if (as2->t == (service_t *) s) {
as = as2;
break;
}
pthread_mutex_unlock(&dvbcam_mutex);
if (!as)
return;
if(as->last_pmt)
free(as->last_pmt);
as->last_pmt = malloc(len + 3);
memcpy(as->last_pmt, ptr-3, len + 3);
as->last_pmt_len = len + 3;
as->ca = NULL;
pthread_mutex_lock(&dvbcam_mutex);
/* check all ellementary streams for CAIDs, if any send PMT to CAM */
TAILQ_FOREACH(st, &s->s_components, es_link) {
LIST_FOREACH(c, &st->es_caids, link) {
TAILQ_FOREACH(ac, &dvbcam_active_caids, link) {
for(i=0;i<ac->num_caids;i++) {
if(ac->ca && ac->ca->lca_adapter == lfe->lfe_adapter &&
ac->caids[i] == c->caid)
{
as->ca = ac->ca;
as->slot = ac->slot;
break;
}
}
}
}
}
pthread_mutex_unlock(&dvbcam_mutex);
/* this service doesn't have assigned CAM */
if (!as->ca)
return;
linuxdvb_ca_send_capmt(as->ca, as->slot, as->last_pmt, as->last_pmt_len);
}
void
dvbcam_service_start(service_t *t)
{
dvbcam_active_service_t *as;
TAILQ_FOREACH(as, &dvbcam_active_services, link)
if (as->t == t)
return;
if ((as = malloc(sizeof(*as))) == NULL)
return;
as->t = t;
as->last_pmt = NULL;
as->last_pmt_len = 0;
pthread_mutex_lock(&dvbcam_mutex);
TAILQ_INSERT_TAIL(&dvbcam_active_services, as, link);
pthread_mutex_unlock(&dvbcam_mutex);
}
void
dvbcam_service_stop(service_t *t)
{
dvbcam_active_service_t *as, *as_tmp;
pthread_mutex_lock(&dvbcam_mutex);
for (as = TAILQ_FIRST(&dvbcam_active_services); as != NULL; as = as_tmp) {
as_tmp = TAILQ_NEXT(as, link);
if(as && as->t == t) {
TAILQ_REMOVE(&dvbcam_active_services, as, link);
if(as->last_pmt)
free(as->last_pmt);
free(as);
}
}
pthread_mutex_unlock(&dvbcam_mutex);
}
void
dvbcam_init(void)
{
pthread_mutex_init(&dvbcam_mutex, NULL);
TAILQ_INIT(&dvbcam_active_services);
TAILQ_INIT(&dvbcam_active_caids);
}
#endif /* ENABLE_LINUXDVB_CA */

View file

@ -1,37 +0,0 @@
/*
* tvheadend - CSA wrapper
* Copyright (C) 2013 Adam Sutton
*
* 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 __DVBCAM_H__
#define __DVBCAM_H__
struct mpegts_service;
struct elementary_stream;
#if ENABLE_LINUXDVB_CA
typedef struct linuxdvb_ca linuxdvb_ca_t;
void dvbcam_init(void);
void dvbcam_service_start(struct service *t);
void dvbcam_service_stop(struct service *t);
void dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids, int num_caids);
void dvbcam_unregister_cam(linuxdvb_ca_t * lca, uint8_t slot);
void dvbcam_pmt_data(mpegts_service_t *s, const uint8_t *ptr, int len);
#endif
#endif /* __DVBCAM_H__ */

0
src/descrambler/libaesdec/libaesdec.c Normal file → Executable file
View file

0
src/descrambler/libaesdec/libaesdec.h Normal file → Executable file
View file

View file

@ -1,169 +0,0 @@
/*
* tvheadend, tsdebug code word interface
* Copyright (C) 2014 Jaroslav Kysela
*
* 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 <ctype.h>
#include "tvheadend.h"
#include "caclient.h"
#include "service.h"
#include "input.h"
/**
*
*/
typedef struct tsdebugcw_service {
th_descrambler_t;
int tdcw_type;
uint8_t tdcw_key_even[16]; /* DES or AES key */
uint8_t tdcw_key_odd [16]; /* DES or AES key */
} tsdebugcw_service_t;
typedef struct tsdebugcw_request {
TAILQ_ENTRY(tsdebugcw_request) link;
tsdebugcw_service_t *ct;
} tsdebugcw_request_t;
pthread_mutex_t tsdebugcw_mutex;
TAILQ_HEAD(,tsdebugcw_request) tsdebugcw_requests;
/*
*
*/
static int
tsdebugcw_ecm_reset(th_descrambler_t *th)
{
return 1;
}
/**
* s_stream_mutex is held
*/
static void
tsdebugcw_service_destroy(th_descrambler_t *td)
{
tsdebugcw_service_t *ct = (tsdebugcw_service_t *)td;
tsdebugcw_request_t *ctr, *ctrnext;
pthread_mutex_lock(&tsdebugcw_mutex);
for (ctr = TAILQ_FIRST(&tsdebugcw_requests); ctr; ctr = ctrnext) {
ctrnext = TAILQ_NEXT(ctr, link);
if (ctr->ct == ct) {
TAILQ_REMOVE(&tsdebugcw_requests, ctr, link);
free(ctr);
}
}
pthread_mutex_unlock(&tsdebugcw_mutex);
LIST_REMOVE(td, td_service_link);
free(ct->td_nicename);
free(ct);
}
/**
* global_lock is held. Not that we care about that, but either way, it is.
*/
void
tsdebugcw_service_start(service_t *t)
{
tsdebugcw_service_t *ct;
th_descrambler_t *td;
char buf[128];
extern const idclass_t mpegts_service_class;
if (!idnode_is_instance(&t->s_id, &mpegts_service_class))
return;
LIST_FOREACH(td, &t->s_descramblers, td_service_link)
if (td->td_stop == tsdebugcw_service_destroy)
break;
if (td)
return;
ct = calloc(1, sizeof(tsdebugcw_service_t));
td = (th_descrambler_t *)ct;
snprintf(buf, sizeof(buf), "tsdebugcw");
td->td_nicename = strdup(buf);
td->td_service = t;
td->td_stop = tsdebugcw_service_destroy;
td->td_ecm_reset = tsdebugcw_ecm_reset;
LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
}
/*
*
*/
void
tsdebugcw_new_keys(service_t *t, int type, uint8_t *odd, uint8_t *even)
{
static char empty[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
th_descrambler_t *td;
tsdebugcw_service_t *ct;
tsdebugcw_request_t *ctr;
int keylen = type == DESCRAMBLER_AES ? 16 : 8;
LIST_FOREACH(td, &t->s_descramblers, td_service_link)
if (td->td_stop == tsdebugcw_service_destroy)
break;
if (!td)
return;
ct = (tsdebugcw_service_t *)td;
ct->tdcw_type = type;
if (memcmp(empty, odd, keylen))
memcpy(ct->tdcw_key_odd, odd, keylen);
if (memcmp(empty, even, keylen))
memcpy(ct->tdcw_key_even, even, keylen);
ctr = malloc(sizeof(*ctr));
ctr->ct = ct;
pthread_mutex_lock(&tsdebugcw_mutex);
TAILQ_INSERT_TAIL(&tsdebugcw_requests, ctr, link);
pthread_mutex_unlock(&tsdebugcw_mutex);
}
/*
*
*/
void
tsdebugcw_go(void)
{
tsdebugcw_request_t *ctr;
tsdebugcw_service_t *ct;
while (1) {
pthread_mutex_lock(&tsdebugcw_mutex);
ctr = TAILQ_FIRST(&tsdebugcw_requests);
if (ctr)
TAILQ_REMOVE(&tsdebugcw_requests, ctr, link);
pthread_mutex_unlock(&tsdebugcw_mutex);
if (!ctr) break;
ct = ctr->ct;
descrambler_keys((th_descrambler_t *)ct, ct->tdcw_type,
ct->tdcw_key_odd, ct->tdcw_key_even);
free(ctr);
}
}
/*
*
*/
void
tsdebugcw_init(void)
{
pthread_mutex_init(&tsdebugcw_mutex, NULL);
TAILQ_INIT(&tsdebugcw_requests);
}

View file

@ -24,29 +24,71 @@
#include <unistd.h>
#include <assert.h>
static void
tvhcsa_aes_flush
( tvhcsa_t *csa, struct mpegts_service *s )
{
/* empty - no queue */
}
static void
tvhcsa_aes_descramble
( tvhcsa_t *csa, struct mpegts_service *s, const uint8_t *tsb, int len )
( tvhcsa_t *csa, struct mpegts_service *s, const uint8_t *tsb )
{
const uint8_t *tsb2, *end2;
for (tsb2 = tsb, end2 = tsb + len; tsb2 < end2; tsb2 += 188)
aes_decrypt_packet(csa->csa_aes_keys, (unsigned char *)tsb2);
ts_recv_packet2(s, tsb, len);
aes_decrypt_packet(csa->csa_aes_keys, (unsigned char *)tsb);
ts_recv_packet2(s, tsb);
}
static void
tvhcsa_des_flush
( tvhcsa_t *csa, struct mpegts_service *s )
tvhcsa_des_descramble
( tvhcsa_t *csa, struct mpegts_service *s, const uint8_t *tsb )
{
#if ENABLE_DVBCSA
uint8_t *pkt;
int xc0;
int ev_od;
int len;
int offset;
int n;
int i;
const uint8_t *t0;
pkt = csa->csa_tsbcluster + csa->csa_fill * 188;
memcpy(pkt, tsb, 188);
csa->csa_fill++;
do { // handle this packet
xc0 = pkt[3] & 0xc0;
if(xc0 == 0x00) { // clear
break;
}
if(xc0 == 0x40) { // reserved
break;
}
if(xc0 == 0x80 || xc0 == 0xc0) { // encrypted
ev_od = (xc0 & 0x40) >> 6; // 0 even, 1 odd
pkt[3] &= 0x3f; // consider it decrypted now
if(pkt[3] & 0x20) { // incomplete packet
offset = 4 + pkt[4] + 1;
len = 188 - offset;
n = len >> 3;
// FIXME: //residue = len - (n << 3);
if(n == 0) { // decrypted==encrypted!
break; // this doesn't need more processing
}
} else {
len = 184;
offset = 4;
// FIXME: //n = 23;
// FIXME: //residue = 0;
}
if(ev_od == 0) {
csa->csa_tsbbatch_even[csa->csa_fill_even].data = pkt + offset;
csa->csa_tsbbatch_even[csa->csa_fill_even].len = len;
csa->csa_fill_even++;
} else {
csa->csa_tsbbatch_odd[csa->csa_fill_odd].data = pkt + offset;
csa->csa_tsbbatch_odd[csa->csa_fill_odd].len = len;
csa->csa_fill_odd++;
}
}
} while(0);
if(csa->csa_fill != csa->csa_cluster_size)
return;
if(csa->csa_fill_even) {
csa->csa_tsbbatch_even[csa->csa_fill_even].data = NULL;
@ -59,112 +101,51 @@ tvhcsa_des_flush
csa->csa_fill_odd = 0;
}
ts_recv_packet2(s, csa->csa_tsbcluster, csa->csa_fill * 188);
t0 = csa->csa_tsbcluster;
for(i = 0; i < csa->csa_fill; i++) {
ts_recv_packet2(s, t0);
t0 += 188;
}
csa->csa_fill = 0;
#else
int r, l;
int r;
unsigned char *vec[3];
vec[0] = csa->csa_tsbcluster;
vec[1] = csa->csa_tsbcluster + csa->csa_fill * 188;
vec[2] = NULL;
memcpy(csa->csa_tsbcluster + csa->csa_fill * 188, tsb, 188);
csa->csa_fill++;
r = decrypt_packets(csa->csa_keys, vec);
if(r > 0) {
ts_recv_packet2(s, csa->csa_tsbcluster, r * 188);
if(csa->csa_fill != csa->csa_cluster_size)
return;
l = csa->csa_fill - r;
assert(l >= 0);
while(1) {
if(l > 0)
memmove(csa->csa_tsbcluster, csa->csa_tsbcluster + r * 188, l * 188);
csa->csa_fill = l;
} else {
csa->csa_fill = 0;
vec[0] = csa->csa_tsbcluster;
vec[1] = csa->csa_tsbcluster + csa->csa_fill * 188;
vec[2] = NULL;
r = decrypt_packets(csa->csa_keys, vec);
if(r > 0) {
int i;
const uint8_t *t0 = csa->csa_tsbcluster;
for(i = 0; i < r; i++) {
ts_recv_packet2(s, t0);
t0 += 188;
}
r = csa->csa_fill - r;
assert(r >= 0);
if(r > 0)
memmove(csa->csa_tsbcluster, t0, r * 188);
csa->csa_fill = r;
} else {
csa->csa_fill = 0;
}
break;
}
#endif
}
static void
tvhcsa_des_descramble
( tvhcsa_t *csa, struct mpegts_service *s, const uint8_t *tsb, int tsb_len )
{
const uint8_t *tsb_end = tsb + tsb_len;
assert(csa->csa_fill >= 0 && csa->csa_fill < csa->csa_cluster_size);
#if ENABLE_DVBCSA
uint8_t *pkt;
int xc0;
int ev_od;
int len;
int offset;
int n;
for ( ; tsb < tsb_end; tsb += 188) {
pkt = csa->csa_tsbcluster + csa->csa_fill * 188;
memcpy(pkt, tsb, 188);
csa->csa_fill++;
do { // handle this packet
xc0 = pkt[3] & 0xc0;
if(xc0 == 0x00) { // clear
break;
}
if(xc0 == 0x40) { // reserved
break;
}
if(xc0 == 0x80 || xc0 == 0xc0) { // encrypted
ev_od = (xc0 & 0x40) >> 6; // 0 even, 1 odd
pkt[3] &= 0x3f; // consider it decrypted now
if(pkt[3] & 0x20) { // incomplete packet
offset = 4 + pkt[4] + 1;
len = 188 - offset;
n = len >> 3;
// FIXME: //residue = len - (n << 3);
if(n == 0) { // decrypted==encrypted!
break; // this doesn't need more processing
}
} else {
len = 184;
offset = 4;
// FIXME: //n = 23;
// FIXME: //residue = 0;
}
if(ev_od == 0) {
csa->csa_tsbbatch_even[csa->csa_fill_even].data = pkt + offset;
csa->csa_tsbbatch_even[csa->csa_fill_even].len = len;
csa->csa_fill_even++;
} else {
csa->csa_tsbbatch_odd[csa->csa_fill_odd].data = pkt + offset;
csa->csa_tsbbatch_odd[csa->csa_fill_odd].len = len;
csa->csa_fill_odd++;
}
}
} while(0);
if(csa->csa_fill == csa->csa_cluster_size)
tvhcsa_des_flush(csa, s);
}
#else
for ( ; tsb < tsb_end; tsb += 188 ) {
memcpy(csa->csa_tsbcluster + csa->csa_fill * 188, tsb, 188);
csa->csa_fill++;
if(csa->csa_fill == csa->csa_cluster_size)
tvhcsa_des_flush(csa, s);
}
#endif
}
@ -178,12 +159,10 @@ tvhcsa_set_type( tvhcsa_t *csa, int type )
switch (type) {
case DESCRAMBLER_DES:
csa->csa_descramble = tvhcsa_des_descramble;
csa->csa_flush = tvhcsa_des_flush;
csa->csa_keylen = 8;
break;
case DESCRAMBLER_AES:
csa->csa_descramble = tvhcsa_aes_descramble;
csa->csa_flush = tvhcsa_aes_flush;
csa->csa_keylen = 16;
break;
default:
@ -241,10 +220,7 @@ tvhcsa_init ( tvhcsa_t *csa )
#else
csa->csa_cluster_size = get_suggested_cluster_size();
#endif
/* Note: the optimized routines might read memory after last TS packet */
/* allocate safe memory and fill it with zeros */
csa->csa_tsbcluster = malloc((csa->csa_cluster_size + 1) * 188);
memset(csa->csa_tsbcluster + csa->csa_cluster_size * 188, 0, 188);
csa->csa_tsbcluster = malloc(csa->csa_cluster_size * 188);
#if ENABLE_DVBCSA
csa->csa_tsbbatch_even = malloc((csa->csa_cluster_size + 1) *
sizeof(struct dvbcsa_bs_batch_s));

View file

@ -41,10 +41,7 @@ typedef struct tvhcsa
int csa_type; /*< see DESCRAMBLER_* defines */
int csa_keylen;
void (*csa_descramble)
( struct tvhcsa *csa, struct mpegts_service *s,
const uint8_t *tsb, int len );
void (*csa_flush)
( struct tvhcsa *csa, struct mpegts_service *s );
( struct tvhcsa *csa, struct mpegts_service *s, const uint8_t *tsb );
int csa_cluster_size;
uint8_t *csa_tsbcluster;
@ -65,8 +62,6 @@ typedef struct tvhcsa
} tvhcsa_t;
#if ENABLE_TVHCSA
int tvhcsa_set_type( tvhcsa_t *csa, int type );
void tvhcsa_set_key_even( tvhcsa_t *csa, const uint8_t *even );
@ -75,16 +70,4 @@ void tvhcsa_set_key_odd ( tvhcsa_t *csa, const uint8_t *odd );
void tvhcsa_init ( tvhcsa_t *csa );
void tvhcsa_destroy ( tvhcsa_t *csa );
#else
static inline int tvhcsa_set_type( tvhcsa_t *csa, int type ) { return -1; }
static inline void tvhcsa_set_key_even( tvhcsa_t *csa, const uint8_t *even ) { };
static inline void tvhcsa_set_key_odd ( tvhcsa_t *csa, const uint8_t *odd ) { };
static inline void tvhcsa_init ( tvhcsa_t *csa ) { };
static inline void tvhcsa_destroy ( tvhcsa_t *csa ) { };
#endif
#endif /* __TVH_CSA_H__ */

View file

@ -63,7 +63,7 @@ typedef struct dvr_config {
int dvr_skip_commercials;
int dvr_subtitle_in_title;
int dvr_episode_before_date;
int dvr_windows_compatible_filenames;
int dvr_episode_duplicate;
/* Series link support */
int dvr_sl_brand_lock;
@ -154,15 +154,10 @@ typedef struct dvr_entry {
time_t de_start_extra;
time_t de_stop_extra;
char *de_owner;
char *de_creator;
char *de_comment;
char *de_filename; /* Initially null if no filename has been
generated yet */
char *de_directory; /* Can be set for autorec entries, will override any
directory setting from the configuration */
lang_str_t *de_title; /* Title in UTF-8 (from EPG) */
lang_str_t *de_subtitle; /* Subtitle in UTF-8 (from EPG) */
lang_str_t *de_desc; /* Description in UTF-8 (from EPG) */
uint32_t de_content_type; /* Content type (from EPG) (only code) */
@ -177,7 +172,6 @@ typedef struct dvr_entry {
* EPG information / links
*/
epg_broadcast_t *de_bcast;
char *de_episode;
/**
* Major State
@ -194,11 +188,6 @@ typedef struct dvr_entry {
*/
uint32_t de_errors;
/**
* Number of data errors (only to be modified by the recording thread)
*/
uint32_t de_data_errors;
/**
* Last error, see SM_CODE_ defines
*/
@ -235,25 +224,10 @@ typedef struct dvr_entry {
LIST_ENTRY(dvr_entry) de_inotify_link;
#endif
/**
* Entry change notification timer
*/
time_t de_last_notify;
} dvr_entry_t;
#define DVR_CH_NAME(e) ((e)->de_channel == NULL ? (e)->de_channel_name : channel_get_name((e)->de_channel))
typedef enum {
DVR_AUTOREC_RECORD_ALL = 0,
DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER = 1,
DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE = 2,
DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION = 3,
DVR_AUTOREC_RECORD_ONCE_PER_WEEK = 4,
DVR_AUTOREC_RECORD_ONCE_PER_DAY = 5
} dvr_autorec_dedup_t;
/**
* Autorec entry
*/
@ -263,23 +237,19 @@ typedef struct dvr_autorec_entry {
TAILQ_ENTRY(dvr_autorec_entry) dae_link;
char *dae_name;
char *dae_directory;
dvr_config_t *dae_config;
LIST_ENTRY(dvr_autorec_entry) dae_config_link;
int dae_enabled;
char *dae_owner;
char *dae_creator;
char *dae_comment;
char *dae_title;
regex_t dae_title_preg;
int dae_fulltext;
uint32_t dae_content_type;
int dae_start; /* Minutes from midnight */
int dae_start_window; /* Minutes (duration) */
int dae_start; /* Minutes from midnight */
uint32_t dae_weekdays;
@ -304,9 +274,6 @@ typedef struct dvr_autorec_entry {
time_t dae_start_extra;
time_t dae_stop_extra;
int dae_record;
} dvr_autorec_entry_t;
TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry);
@ -322,12 +289,10 @@ typedef struct dvr_timerec_entry {
TAILQ_ENTRY(dvr_timerec_entry) dte_link;
char *dte_name;
char *dte_directory;
dvr_config_t *dte_config;
LIST_ENTRY(dvr_timerec_entry) dte_config_link;
int dte_enabled;
char *dte_owner;
char *dte_creator;
char *dte_comment;
@ -436,25 +401,22 @@ dvr_entry_t *
dvr_entry_create_by_event( const char *dvr_config_uuid,
epg_broadcast_t *e,
time_t start_extra, time_t stop_extra,
const char *owner, const char *creator,
const char *creator,
dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention,
const char *comment );
dvr_prio_t pri, int retention );
dvr_entry_t *
dvr_entry_create_htsp( const char *dvr_config_uuid,
channel_t *ch, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
const char *title, const char* subtitle, const char *description,
const char *title, const char *description,
const char *lang, epg_genre_t *content_type,
const char *owner, const char *creator,
dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention,
const char *comment );
const char *creator, dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention );
dvr_entry_t *
dvr_entry_update( dvr_entry_t *de,
const char* de_title, const char* de_subtitle, const char *de_desc, const char *lang,
const char* de_title, const char *de_desc, const char *lang,
time_t de_start, time_t de_stop,
time_t de_start_extra, time_t de_stop_extra,
dvr_prio_t pri, int retention );
@ -495,19 +457,6 @@ htsmsg_t *dvr_entry_class_pri_list(void *o);
htsmsg_t *dvr_entry_class_config_name_list(void *o);
htsmsg_t *dvr_entry_class_duration_list(void *o, const char *not_set, int max, int step);
static inline int dvr_entry_verify(dvr_entry_t *de, access_t *a, int readonly)
{
if (readonly && !access_verify2(a, ACCESS_ALL_RECORDER))
return 0;
if (!access_verify2(a, ACCESS_ALL_RW_RECORDER))
return 0;
if (strcmp(de->de_owner ?: "", a->aa_username ?: ""))
return -1;
return 0;
}
/**
*
*/
@ -519,26 +468,24 @@ dvr_entry_t *
dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
channel_t *ch, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
const char *title, const char* subtitle, const char *description,
const char *title, const char *description,
const char *lang, epg_genre_t *content_type,
const char *owner, const char *creator,
dvr_autorec_entry_t *dae, dvr_timerec_entry_t *tae,
dvr_prio_t pri, int retention, const char *comment);
const char *creator, dvr_autorec_entry_t *dae,
dvr_timerec_entry_t *tae,
dvr_prio_t pri, int retention);
dvr_autorec_entry_t *
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int fulltext,
channel_t *ch, uint32_t enabled, int32_t start,
int32_t start_window, uint32_t days, time_t start_extra,
time_t stop_extra, dvr_prio_t pri, int retention,
int min_duration, int max_duration, dvr_autorec_dedup_t dup_detect,
const char *owner, const char *creator,
const char *comment, const char *name, const char *directory);
dvr_autorec_entry_t*
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
channel_t *ch, uint32_t aroundTime, uint32_t days,
time_t start_extra, time_t stop_extra,
dvr_prio_t pri, int retention,
int min_duration, int max_duration,
const char *creator, const char *comment);
dvr_autorec_entry_t *
dvr_autorec_add_series_link(const char *dvr_config_name,
epg_broadcast_t *event,
const char *owner, const char *creator,
const char *comment);
const char *creator, const char *comment);
void dvr_autorec_save(dvr_autorec_entry_t *dae);
@ -573,13 +520,6 @@ void dvr_autorec_done(void);
void dvr_autorec_update(void);
static inline int dvr_autorec_entry_verify(dvr_autorec_entry_t *dae, access_t *a)
{
if (strcmp(dae->dae_owner ?: "", a->aa_username ?: ""))
return -1;
return 0;
}
/**
*
*/
@ -587,13 +527,6 @@ static inline int dvr_autorec_entry_verify(dvr_autorec_entry_t *dae, access_t *a
dvr_timerec_entry_t *
dvr_timerec_create(const char *uuid, htsmsg_t *conf);
dvr_timerec_entry_t*
dvr_timerec_create_htsp(const char *dvr_config_name, const char *title,
channel_t *ch, uint32_t enabled, uint32_t start, uint32_t stop,
uint32_t weekdays, dvr_prio_t pri, int retention,
const char *owner, const char *creator, const char *comment,
const char *name, const char *directory);
static inline dvr_timerec_entry_t *
dvr_timerec_find_by_uuid(const char *uuid)
{ return (dvr_timerec_entry_t*)idnode_find(uuid, &dvr_timerec_entry_class, NULL); }
@ -615,13 +548,6 @@ void dvr_timerec_done(void);
void dvr_timerec_update(void);
static inline int dvr_timerec_entry_verify(dvr_timerec_entry_t *dte, access_t *a)
{
if (strcmp(dte->dte_owner ?: "", a->aa_username ?: ""))
return -1;
return 0;
}
/**
*
*/

View file

@ -1,6 +1,6 @@
/*
* tvheadend, Automatic recordings
* Copyright (C) 2010 Andreas <EFBFBD>man
* Copyright (C) 2010 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
@ -88,33 +88,17 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
// if configured
if(dae->dae_serieslink) {
if (!e->serieslink || dae->dae_serieslink != e->serieslink) return 0;
} else {
if(dae->dae_season)
if (!e->episode->season || dae->dae_season != e->episode->season) return 0;
if(dae->dae_brand)
if (!e->episode->brand || dae->dae_brand != e->episode->brand) return 0;
return 1;
}
if(dae->dae_season)
if (!e->episode->season || dae->dae_season != e->episode->season) return 0;
if(dae->dae_brand)
if (!e->episode->brand || dae->dae_brand != e->episode->brand) return 0;
if(dae->dae_title != NULL && dae->dae_title[0] != '\0') {
lang_str_ele_t *ls;
if (!dae->dae_fulltext) {
if(!e->episode->title) return 0;
RB_FOREACH(ls, e->episode->title, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
} else {
ls = NULL;
if (e->episode->title)
RB_FOREACH(ls, e->episode->title, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
if (!ls && e->episode->subtitle)
RB_FOREACH(ls, e->episode->subtitle, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
if (!ls && e->summary)
RB_FOREACH(ls, e->summary, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
if (!ls && e->description)
RB_FOREACH(ls, e->description, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
}
if(!e->episode->title) return 0;
RB_FOREACH(ls, e->episode->title, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
if (!ls) return 0;
}
@ -122,13 +106,9 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
if ((cfg = dae->dae_config) == NULL)
return 0;
if (cfg->dvr_sl_quality_lock)
if(dae->dae_channel != NULL) {
if (dae->dae_channel != e->channel &&
dae->dae_channel->ch_enabled)
return 0;
if (!dae->dae_channel->ch_enabled)
return 0;
}
if(dae->dae_channel != NULL &&
dae->dae_channel != e->channel)
return 0;
if(dae->dae_channel_tag != NULL) {
LIST_FOREACH(ctm, &dae->dae_channel_tag->ct_ctms, ctm_tag_link)
@ -146,29 +126,15 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
return 0;
}
if(dae->dae_start >= 0 && dae->dae_start_window >= 0 &&
dae->dae_start < 24*60 && dae->dae_start_window < 24*60) {
struct tm a_time, ev_time;
time_t ta, te, tad;
if(dae->dae_start >= 0) {
struct tm a_time;
struct tm ev_time;
localtime_r(&e->start, &a_time);
ev_time = a_time;
localtime_r(&e->start, &ev_time);
a_time.tm_min = dae->dae_start % 60;
a_time.tm_hour = dae->dae_start / 60;
ta = mktime(&a_time);
te = mktime(&ev_time);
if(dae->dae_start > dae->dae_start_window) {
ta -= 24 * 3600; /* 24 hours */
tad = ((24 * 60) - dae->dae_start + dae->dae_start_window) * 60;
if(ta > te || te > ta + tad) {
ta += 24 * 3600;
if(ta > te || te > ta + tad)
return 0;
}
} else {
tad = (dae->dae_start_window - dae->dae_start) * 60;
if(ta > te || te > ta + tad)
return 0;
}
if(abs(mktime(&a_time) - mktime(&ev_time)) > 900)
return 0;
}
duration = difftime(e->stop,e->start);
@ -210,7 +176,6 @@ dvr_autorec_create(const char *uuid, htsmsg_t *conf)
dae->dae_weekdays = 0x7f;
dae->dae_pri = DVR_PRIO_NORMAL;
dae->dae_start = -1;
dae->dae_start_window = -1;
dae->dae_config = dvr_config_find_by_name_default(NULL);
LIST_INSERT_HEAD(&dae->dae_config->dvr_autorec_entries, dae, dae_config_link);
@ -225,13 +190,12 @@ dvr_autorec_create(const char *uuid, htsmsg_t *conf)
dvr_autorec_entry_t*
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int fulltext,
channel_t *ch, uint32_t enabled, int32_t start, int32_t start_window,
uint32_t weekdays, time_t start_extra, time_t stop_extra,
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
channel_t *ch, uint32_t aroundTime, uint32_t weekdays,
time_t start_extra, time_t stop_extra,
dvr_prio_t pri, int retention,
int min_duration, int max_duration, dvr_autorec_dedup_t dup_detect,
const char *owner, const char *creator, const char *comment,
const char *name, const char *directory)
int min_duration, int max_duration,
const char *creator, const char *comment)
{
dvr_autorec_entry_t *dae;
htsmsg_t *conf, *days;
@ -239,7 +203,7 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int full
conf = htsmsg_create_map();
days = htsmsg_create_list();
htsmsg_add_u32(conf, "enabled", enabled > 0 ? 1 : 0);
htsmsg_add_u32(conf, "enabled", 1);
htsmsg_add_u32(conf, "retention", retention);
htsmsg_add_u32(conf, "pri", pri);
htsmsg_add_u32(conf, "minduration", min_duration);
@ -247,19 +211,12 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int full
htsmsg_add_s64(conf, "start_extra", start_extra);
htsmsg_add_s64(conf, "stop_extra", stop_extra);
htsmsg_add_str(conf, "title", title);
htsmsg_add_u32(conf, "fulltext", fulltext);
htsmsg_add_u32(conf, "record", dup_detect);
htsmsg_add_str(conf, "config_name", dvr_config_name ?: "");
htsmsg_add_str(conf, "owner", owner ?: "");
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
htsmsg_add_str(conf, "name", name ?: "");
htsmsg_add_str(conf, "directory", directory ?: "");
if (start >= 0)
htsmsg_add_s32(conf, "start", start);
if (start_window >= 0)
htsmsg_add_s32(conf, "start_window", start_window);
if (aroundTime)
htsmsg_add_u32(conf, "start", (aroundTime-1));
if (ch)
htsmsg_add_str(conf, "channel", idnode_uuid_as_str(&ch->ch_id));
@ -287,8 +244,7 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int full
dvr_autorec_entry_t *
dvr_autorec_add_series_link(const char *dvr_config_name,
epg_broadcast_t *event,
const char *owner, const char *creator,
const char *comment)
const char *creator, const char *comment)
{
dvr_autorec_entry_t *dae;
htsmsg_t *conf;
@ -304,7 +260,6 @@ dvr_autorec_add_series_link(const char *dvr_config_name,
htsmsg_add_str(conf, "channel", channel_get_name(event->channel));
if (event->serieslink)
htsmsg_add_str(conf, "serieslink", event->serieslink->uri);
htsmsg_add_str(conf, "owner", owner ?: "");
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
dae = dvr_autorec_create(NULL, conf);
@ -332,8 +287,6 @@ autorec_entry_destroy(dvr_autorec_entry_t *dae, int delconf)
LIST_REMOVE(dae, dae_config_link);
free(dae->dae_name);
free(dae->dae_directory);
free(dae->dae_owner);
free(dae->dae_creator);
free(dae->dae_comment);
@ -391,20 +344,6 @@ dvr_autorec_entry_class_delete(idnode_t *self)
autorec_entry_destroy((dvr_autorec_entry_t *)self, 1);
}
static int
dvr_autorec_entry_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
{
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)self;
if (access_verify2(a, ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
if (dvr_autorec_entry_verify(dae, a))
return -1;
return 0;
}
static const char *
dvr_autorec_entry_class_get_title (idnode_t *self)
{
@ -551,13 +490,6 @@ dvr_autorec_entry_class_start_set(void *o, const void *v)
return dvr_autorec_entry_class_time_set(o, v, &dae->dae_start);
}
static int
dvr_autorec_entry_class_start_window_set(void *o, const void *v)
{
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o;
return dvr_autorec_entry_class_time_set(o, v, &dae->dae_start_window);
}
static const void *
dvr_autorec_entry_class_time_get(void *o, int tm)
{
@ -578,13 +510,6 @@ dvr_autorec_entry_class_start_get(void *o)
return dvr_autorec_entry_class_time_get(o, dae->dae_start);
}
static const void *
dvr_autorec_entry_class_start_window_get(void *o)
{
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o;
return dvr_autorec_entry_class_time_get(o, dae->dae_start_window);
}
htsmsg_t *
dvr_autorec_entry_class_time_list(void *o, const char *null)
{
@ -868,20 +793,6 @@ dvr_autorec_entry_class_content_type_list(void *o)
return m;
}
static htsmsg_t *
dvr_autorec_entry_class_dedup_list ( void *o )
{
static const struct strtab tab[] = {
{ "Record all", DVR_AUTOREC_RECORD_ALL },
{ "Record if different episode number", DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER },
{ "Record if different subtitle", DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE },
{ "Record if different description", DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION },
{ "Record once per week", DVR_AUTOREC_RECORD_ONCE_PER_WEEK },
{ "Record once per day", DVR_AUTOREC_RECORD_ONCE_PER_DAY },
};
return strtab2htsmsg(tab);
}
const idclass_t dvr_autorec_entry_class = {
.ic_class = "dvrautorec",
.ic_caption = "DVR Auto-Record Entry",
@ -889,7 +800,6 @@ const idclass_t dvr_autorec_entry_class = {
.ic_save = dvr_autorec_entry_class_save,
.ic_get_title = dvr_autorec_entry_class_get_title,
.ic_delete = dvr_autorec_entry_class_delete,
.ic_perm = dvr_autorec_entry_class_perm,
.ic_properties = (const property_t[]) {
{
.type = PT_BOOL,
@ -902,12 +812,6 @@ const idclass_t dvr_autorec_entry_class = {
.id = "name",
.name = "Name",
.off = offsetof(dvr_autorec_entry_t, dae_name),
},
{
.type = PT_STR,
.id = "directory",
.name = "Directory",
.off = offsetof(dvr_autorec_entry_t, dae_directory),
},
{
.type = PT_STR,
@ -916,12 +820,6 @@ const idclass_t dvr_autorec_entry_class = {
.set = dvr_autorec_entry_class_title_set,
.off = offsetof(dvr_autorec_entry_t, dae_title),
},
{
.type = PT_BOOL,
.id = "fulltext",
.name = "Fulltext",
.off = offsetof(dvr_autorec_entry_t, dae_fulltext),
},
{
.type = PT_STR,
.id = "channel",
@ -943,21 +841,12 @@ const idclass_t dvr_autorec_entry_class = {
{
.type = PT_STR,
.id = "start",
.name = "Start After",
.name = "Starting Around",
.set = dvr_autorec_entry_class_start_set,
.get = dvr_autorec_entry_class_start_get,
.list = dvr_autorec_entry_class_time_list_,
.opts = PO_SORTKEY
},
{
.type = PT_STR,
.id = "start_window",
.name = "Start Before",
.set = dvr_autorec_entry_class_start_window_set,
.get = dvr_autorec_entry_class_start_window_get,
.list = dvr_autorec_entry_class_time_list_,
.opts = PO_SORTKEY,
},
{
.type = PT_TIME,
.id = "start_extra",
@ -1014,14 +903,6 @@ const idclass_t dvr_autorec_entry_class = {
.def.i = DVR_PRIO_NORMAL,
.off = offsetof(dvr_autorec_entry_t, dae_pri),
},
{
.type = PT_U32,
.id = "record",
.name = "Duplicate Handling",
.def.i = DVR_AUTOREC_RECORD_ALL,
.off = offsetof(dvr_autorec_entry_t, dae_record),
.list = dvr_autorec_entry_class_dedup_list,
},
{
.type = PT_INT,
.id = "retention",
@ -1061,13 +942,6 @@ const idclass_t dvr_autorec_entry_class = {
.get = dvr_autorec_entry_class_series_link_get,
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "owner",
.name = "Owner",
.off = offsetof(dvr_autorec_entry_t, dae_owner),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "creator",
@ -1173,7 +1047,6 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge)
dvr_autorec_purge_spawns(dae, 1);
CHANNEL_FOREACH(ch) {
if (!ch->ch_enabled) continue;
RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
if(autorec_cmp(dae, e))
dvr_entry_create_by_autorec(e, dae);

View file

@ -316,7 +316,7 @@ dvr_config_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
htsmsg_field_t *f;
const char *uuid, *my_uuid;
if (access_verify2(a, ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER))
if (access_verify2(a, ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
@ -571,6 +571,13 @@ const idclass_t dvr_config_class = {
.list = dvr_config_class_extra_list,
.group = 1,
},
{
.type = PT_BOOL,
.id = "episode-duplicate-detection",
.name = "Episode Duplicate Detect",
.off = offsetof(dvr_config_t, dvr_episode_duplicate),
.group = 1,
},
{
.type = PT_U32,
.id = "epg-update-window",
@ -720,13 +727,6 @@ const idclass_t dvr_config_class = {
.off = offsetof(dvr_config_t, dvr_whitespace_in_title),
.group = 5,
},
{
.type = PT_BOOL,
.id = "windows-compatible-filenames",
.name = "Use Windows-compatible filenames",
.off = offsetof(dvr_config_t, dvr_windows_compatible_filenames),
.group = 5,
},
{}
},
};

View file

@ -145,7 +145,7 @@ dvr_parse_file
dvr_cutpoint_t *cp = NULL;
float frate = 0.0;
char line[DVR_MAX_CUTPOINT_LINE];
FILE *file = tvh_fopen(path, "r");
FILE *file = fopen(path, "r");
if (file == NULL)
return -1;

View file

@ -44,7 +44,6 @@ static void dvr_timer_expire(void *aux);
static void dvr_timer_start_recording(void *aux);
static void dvr_timer_stop_recording(void *aux);
static int dvr_entry_class_disp_title_set(void *o, const void *v);
static int dvr_entry_class_disp_subtitle_set(void *o, const void *v);
/*
* Start / stop time calculators
@ -254,32 +253,35 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de)
snprintf(output + strlen(output), outlen - strlen(output),
"%s", lang_str_get(de->de_title, NULL));
if (cfg->dvr_episode_before_date) {
if (cfg->dvr_episode_in_title && de->de_bcast && de->de_bcast->episode)
epg_episode_number_format(de->de_bcast->episode,
output + strlen(output),
outlen - strlen(output),
".", "S%02d", NULL, "E%02d", NULL);
if(cfg->dvr_episode_before_date) {
if(cfg->dvr_episode_in_title) {
if(de->de_bcast && de->de_bcast->episode)
epg_episode_number_format(de->de_bcast->episode,
output + strlen(output),
outlen - strlen(output),
".", "S%02d", NULL, "E%02d", NULL);
}
}
if (cfg->dvr_subtitle_in_title && de->de_subtitle) {
if(cfg->dvr_subtitle_in_title) {
if(de->de_bcast && de->de_bcast->episode && de->de_bcast->episode->subtitle)
snprintf(output + strlen(output), outlen - strlen(output),
".%s", lang_str_get(de->de_subtitle, NULL));
".%s", lang_str_get(de->de_bcast->episode->subtitle, NULL));
}
localtime_r(&de->de_start, &tm);
if (cfg->dvr_date_in_title) {
if(cfg->dvr_date_in_title) {
strftime(buf, sizeof(buf), "%F", &tm);
snprintf(output + strlen(output), outlen - strlen(output), ".%s", buf);
}
if (cfg->dvr_time_in_title) {
if(cfg->dvr_time_in_title) {
strftime(buf, sizeof(buf), "%H-%M", &tm);
snprintf(output + strlen(output), outlen - strlen(output), ".%s", buf);
}
if (!cfg->dvr_episode_before_date) {
if(!cfg->dvr_episode_before_date) {
if(cfg->dvr_episode_in_title) {
if(de->de_bcast && de->de_bcast->episode)
epg_episode_number_format(de->de_bcast->episode,
@ -306,14 +308,14 @@ dvr_entry_set_timer(dvr_entry_t *de)
de->de_sched_state = DVR_MISSED_TIME;
else
_dvr_entry_completed(de);
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
de->de_stop + dvr_entry_get_retention(de) * 86400);
} else if (de->de_sched_state == DVR_RECORDING) {
gtimer_arm_abs(&de->de_timer, dvr_timer_stop_recording, de, stop);
} else if (de->de_channel && de->de_channel->ch_enabled) {
} else if (de->de_channel) {
de->de_sched_state = DVR_SCHEDULED;
@ -331,21 +333,6 @@ dvr_entry_set_timer(dvr_entry_t *de)
}
/**
* Get episode name
*/
static char *
dvr_entry_get_episode(epg_broadcast_t *bcast, char *buf, int len)
{
if (!bcast || !bcast->episode)
return NULL;
if (epg_episode_number_format(bcast->episode,
buf, len, NULL,
"Season %d", ".", "Episode %d", "/%d"))
return buf;
return NULL;
}
/**
* Find dvr entry using 'fuzzy' search
*/
@ -354,7 +341,6 @@ dvr_entry_fuzzy_match(dvr_entry_t *de, epg_broadcast_t *e)
{
time_t t1, t2;
const char *title1, *title2;
char buf[64];
/* Matching ID */
if (de->de_dvb_eid && de->de_dvb_eid == e->dvb_eid)
@ -373,19 +359,11 @@ dvr_entry_fuzzy_match(dvr_entry_t *de, epg_broadcast_t *e)
return 0;
/* Outside of window */
if (abs(e->start - de->de_start) > de->de_config->dvr_update_window)
if ( abs(e->start - de->de_start) > de->de_config->dvr_update_window )
return 0;
/* Title match (or contains?) */
if (strcmp(title1, title2))
return 0;
/* episode check */
if (dvr_entry_get_episode(e, buf, sizeof(buf)) && de->de_episode)
if (strcmp(buf, de->de_episode))
return 0;
return 1;
return strcmp(title1, title2) == 0;
}
/**
@ -429,11 +407,6 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf)
(s = htsmsg_get_str(conf, "disp_title")) != NULL)
dvr_entry_class_disp_title_set(de, s);
/* special case, becaous PO_NOSAVE, load ignores it */
if (de->de_subtitle == NULL &&
(s = htsmsg_get_str(conf, "disp_subtitle")) != NULL)
dvr_entry_class_disp_subtitle_set(de, s);
de->de_refcnt = 1;
LIST_INSERT_HEAD(&dvrentries, de, de_global_link);
@ -461,16 +434,14 @@ dvr_entry_t *
dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
channel_t *ch, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
const char *title, const char* subtitle, const char *description,
const char *title, const char *description,
const char *lang, epg_genre_t *content_type,
const char *owner,
const char *creator, dvr_autorec_entry_t *dae,
dvr_timerec_entry_t *dte,
dvr_prio_t pri, int retention,
const char *comment)
dvr_prio_t pri, int retention)
{
dvr_entry_t *de;
char tbuf[64], *s;
char tbuf[64];
struct tm tm;
time_t t;
lang_str_t *l;
@ -485,15 +456,11 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
htsmsg_add_str(conf, "config_name", config_uuid ?: "");
htsmsg_add_s64(conf, "start_extra", start_extra);
htsmsg_add_s64(conf, "stop_extra", stop_extra);
htsmsg_add_str(conf, "owner", owner ?: "");
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
if (e) {
htsmsg_add_u32(conf, "dvb_eid", e->dvb_eid);
if (e->episode && e->episode->title)
lang_str_serialize(e->episode->title, conf, "title");
if (e->episode && e->episode->subtitle)
lang_str_serialize(e->episode->subtitle, conf, "subtitle");
if (e->description)
lang_str_serialize(e->description, conf, "description");
else if (e->episode && e->episode->description)
@ -502,8 +469,6 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
lang_str_serialize(e->summary, conf, "description");
else if (e->episode && e->episode->summary)
lang_str_serialize(e->episode->summary, conf, "description");
if (e->episode && (s = dvr_entry_get_episode(e, tbuf, sizeof(tbuf))))
htsmsg_add_str(conf, "episode", s);
} else if (title) {
l = lang_str_create();
lang_str_add(l, title, lang, 0);
@ -515,27 +480,15 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
lang_str_serialize(l, conf, "description");
lang_str_destroy(l);
}
if (subtitle) {
l = lang_str_create();
lang_str_add(l, subtitle, lang, 0);
lang_str_serialize(l, conf, "subtitle");
lang_str_destroy(l);
}
}
if (content_type)
htsmsg_add_u32(conf, "content_type", content_type->code / 16);
if (e)
htsmsg_add_u32(conf, "broadcast", e->id);
if (dae)
{
htsmsg_add_str(conf, "autorec", idnode_uuid_as_str(&dae->dae_id));
htsmsg_add_str(conf, "directory", dae->dae_directory ?: "");
}
if (dte)
{
htsmsg_add_str(conf, "timerec", idnode_uuid_as_str(&dte->dte_id));
htsmsg_add_str(conf, "directory", dte->dte_directory ?: "");
}
de = dvr_entry_create(NULL, conf);
@ -565,13 +518,11 @@ dvr_entry_t *
dvr_entry_create_htsp(const char *config_uuid,
channel_t *ch, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
const char *title, const char* subtitle,
const char *title,
const char *description, const char *lang,
epg_genre_t *content_type,
const char *owner,
const char *creator, dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention,
const char *comment)
dvr_prio_t pri, int retention)
{
dvr_config_t *cfg = dvr_config_find_by_uuid(config_uuid);
if (!cfg)
@ -579,9 +530,8 @@ dvr_entry_create_htsp(const char *config_uuid,
return dvr_entry_create_(cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL,
NULL,
ch, start, stop, start_extra, stop_extra,
title, subtitle, description, lang, content_type,
owner, creator, dae, NULL, pri, retention,
comment);
title, description, lang, content_type,
creator, dae, NULL, pri, retention);
}
/**
@ -591,10 +541,8 @@ dvr_entry_t *
dvr_entry_create_by_event(const char *config_uuid,
epg_broadcast_t *e,
time_t start_extra, time_t stop_extra,
const char *owner,
const char *creator, dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention,
const char *comment)
dvr_prio_t pri, int retention)
{
if(!e->channel || !e->episode || !e->episode->title)
return NULL;
@ -602,100 +550,46 @@ dvr_entry_create_by_event(const char *config_uuid,
return dvr_entry_create_(config_uuid, e,
e->channel, e->start, e->stop,
start_extra, stop_extra,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
LIST_FIRST(&e->episode->genre),
owner, creator, dae, NULL, pri, retention,
comment);
creator, dae, NULL, pri, retention);
}
/**
*
*/
static dvr_entry_t* _dvr_duplicate_event(dvr_entry_t* de)
static int _dvr_duplicate_event ( epg_broadcast_t *e )
{
if (!de->de_autorec)
return NULL;
dvr_entry_t *de;
epg_episode_num_t empty_epnum;
int has_epnum = 1;
int record = de->de_autorec->dae_record;
/* skip episode duplicate check below if no episode number */
memset(&empty_epnum, 0, sizeof(empty_epnum));
if (epg_episode_number_cmp(&empty_epnum, &e->episode->epnum) == 0)
has_epnum = 0;
struct tm de_start;
localtime_r(&de->de_start, &de_start);
LIST_FOREACH(de, &dvrentries, de_global_link) {
if (de->de_bcast) {
if (de->de_bcast->episode == e->episode) return 1;
switch (record) {
case DVR_AUTOREC_RECORD_ALL:
return NULL;
case DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER:
if (strempty(de->de_episode))
return NULL;
break;
case DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE:
if (lang_str_empty(de->de_subtitle))
return NULL;
break;
case DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION:
if (lang_str_empty(de->de_desc))
return NULL;
break;
case DVR_AUTOREC_RECORD_ONCE_PER_WEEK:
de_start.tm_mday -= (de_start.tm_wday + 6) % 7; // week = mon-sun
mktime(&de_start); // adjusts de_start
break;
}
if (has_epnum) {
int ep_dup_det = de->de_config->dvr_episode_duplicate;
// title not defined, can't be deduped
if (lang_str_empty(de->de_title))
return NULL;
if (ep_dup_det) {
const char* de_title = lang_str_get(de->de_bcast->episode->title, NULL);
const char* e_title = lang_str_get(e->episode->title, NULL);
dvr_entry_t *de2;
LIST_FOREACH(de2, &dvrentries, de_global_link) {
if (de == de2)
continue;
// only earlier recordings qualify as master
if (de2->de_start > de->de_start)
continue;
// only successful earlier recordings qualify as master
if (de2->de_sched_state == DVR_MISSED_TIME || (de2->de_sched_state == DVR_COMPLETED && de2->de_last_error != SM_CODE_OK))
continue;
// if titles are not defined or do not match, don't dedup
if (lang_str_compare(de->de_title, de2->de_title))
continue;
switch (record) {
case DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER:
if (!strcmp(de->de_episode, de2->de_episode))
return de2;
break;
case DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE:
if (!lang_str_compare(de->de_subtitle, de2->de_subtitle))
return de2;
break;
case DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION:
if (!lang_str_compare(de->de_desc, de2->de_desc))
return de2;
break;
case DVR_AUTOREC_RECORD_ONCE_PER_WEEK: {
struct tm de2_start;
localtime_r(&de2->de_start, &de2_start);
de2_start.tm_mday -= (de2_start.tm_wday + 6) % 7; // week = mon-sun
mktime(&de2_start); // adjusts de2_start
if (de_start.tm_year == de2_start.tm_year && de_start.tm_yday == de2_start.tm_yday)
return de2;
break;
}
case DVR_AUTOREC_RECORD_ONCE_PER_DAY: {
struct tm de2_start;
localtime_r(&de2->de_start, &de2_start);
if (de_start.tm_year == de2_start.tm_year && de_start.tm_yday == de2_start.tm_yday)
return de2;
break;
/* duplicate if title and episode match */
if (de_title && e_title && strcmp(de_title, e_title) == 0
&& epg_episode_number_cmp(&de->de_bcast->episode->epnum, &e->episode->epnum) == 0) {
return 1;
}
}
}
}
}
return NULL;
return 0;
}
/**
@ -706,22 +600,17 @@ dvr_entry_create_by_autorec(epg_broadcast_t *e, dvr_autorec_entry_t *dae)
{
char buf[200];
/* Identical duplicate detection
NOTE: Semantic duplicate detection is deferred to the start time of recording and then done using _dvr_duplicate_event by dvr_timer_start_recording. */
dvr_entry_t* de;
LIST_FOREACH(de, &dvrentries, de_global_link) {
if (de->de_bcast == e || (de->de_bcast && de->de_bcast->episode == e->episode))
return;
/* Dup detection */
if (_dvr_duplicate_event(e)) return;
if(dae->dae_creator) {
snprintf(buf, sizeof(buf), "Auto recording by: %s", dae->dae_creator);
} else {
snprintf(buf, sizeof(buf), "Auto recording");
}
snprintf(buf, sizeof(buf), "Auto recording%s%s",
dae->dae_creator ? " by: " : "",
dae->dae_creator ?: "");
dvr_entry_create_by_event(idnode_uuid_as_str(&dae->dae_config->dvr_id), e,
dae->dae_start_extra, dae->dae_stop_extra,
dae->dae_owner, buf, dae, dae->dae_pri, dae->dae_retention,
dae->dae_comment);
buf, dae, dae->dae_pri, dae->dae_retention);
}
/**
@ -751,15 +640,10 @@ dvr_entry_dec_ref(dvr_entry_t *de)
LIST_REMOVE(de, de_config_link);
free(de->de_filename);
free(de->de_owner);
free(de->de_creator);
free(de->de_comment);
if (de->de_title) lang_str_destroy(de->de_title);
if (de->de_subtitle) lang_str_destroy(de->de_subtitle);
if (de->de_desc) lang_str_destroy(de->de_desc);
if (de->de_bcast) de->de_bcast->putref((epg_object_t*)de->de_bcast);
free(de->de_channel_name);
free(de->de_episode);
free(de);
}
@ -788,6 +672,8 @@ dvr_entry_destroy(dvr_entry_t *de, int delconf)
LIST_REMOVE(de, de_channel_link);
LIST_REMOVE(de, de_global_link);
de->de_channel = NULL;
free(de->de_channel_name);
de->de_channel_name = NULL;
dvr_entry_dec_ref(de);
}
@ -841,11 +727,10 @@ dvr_timer_expire(void *aux)
}
static dvr_entry_t *_dvr_entry_update
( dvr_entry_t *de, epg_broadcast_t *e, const char *title, const char* subtitle,
( dvr_entry_t *de, epg_broadcast_t *e, const char *title,
const char *desc, const char *lang, time_t start, time_t stop,
time_t start_extra, time_t stop_extra, dvr_prio_t pri, int retention )
{
char buf[40];
int save = 0;
if (!dvr_entry_is_editable(de))
@ -891,13 +776,7 @@ static dvr_entry_t *_dvr_entry_update
if (!de->de_title) de->de_title = lang_str_create();
save = lang_str_add(de->de_title, title, lang, 1);
}
/* Subtitle*/
if (subtitle) {
if (!de->de_subtitle) de->de_subtitle = lang_str_create();
save = lang_str_add(de->de_subtitle, subtitle, lang, 1);
}
/* EID */
if (e && e->dvb_eid != de->de_dvb_eid) {
de->de_dvb_eid = e->dvb_eid;
@ -922,16 +801,6 @@ static dvr_entry_t *_dvr_entry_update
de->de_bcast = e;
e->getref(e);
save = 1;
}
/* Episode */
if (dvr_entry_get_episode(de->de_bcast, buf, sizeof(buf))) {
if (strcmp(de->de_episode ?: "", buf)) {
free(de->de_episode);
de->de_episode = strdup(buf);
save = 1;
}
}
/* Save changes */
@ -951,12 +820,12 @@ static dvr_entry_t *_dvr_entry_update
dvr_entry_t *
dvr_entry_update
(dvr_entry_t *de,
const char* de_title, const char* de_subtitle, const char *de_desc, const char *lang,
const char* de_title, const char *de_desc, const char *lang,
time_t de_start, time_t de_stop,
time_t de_start_extra, time_t de_stop_extra,
dvr_prio_t pri, int retention)
{
return _dvr_entry_update(de, NULL, de_title, de_subtitle, de_desc, lang,
return _dvr_entry_update(de, NULL, de_title, de_desc, lang,
de_start, de_stop, de_start_extra, de_stop_extra,
pri, retention);
}
@ -992,7 +861,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
e->putref(e);
de->de_bcast = NULL;
/* If this was created by autorec - just remove it, it'll get recreated */
/* If this was craeted by autorec - just remove it, it'll get recreated */
if (de->de_autorec) {
dvr_entry_destroy(de, 1);
@ -1008,7 +877,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
e->start, e->stop);
e->getref(e);
de->de_bcast = e;
_dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
_dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
break;
}
}
@ -1021,7 +890,7 @@ void dvr_event_updated ( epg_broadcast_t *e )
dvr_entry_t *de;
de = dvr_entry_find_by_event(e);
if (de)
_dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
_dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
else {
LIST_FOREACH(de, &dvrentries, de_global_link) {
if (de->de_sched_state != DVR_SCHEDULED) continue;
@ -1037,7 +906,7 @@ void dvr_event_updated ( epg_broadcast_t *e )
e->start, e->stop);
e->getref(e);
de->de_bcast = e;
_dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
_dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
break;
}
}
@ -1093,20 +962,8 @@ dvr_timer_start_recording(void *aux)
{
dvr_entry_t *de = aux;
if (de->de_channel == NULL || !de->de_channel->ch_enabled) {
de->de_sched_state = DVR_NOSTATE;
return;
}
// if duplicate, then delete it now, don't record!
if (_dvr_duplicate_event(de)) {
dvr_entry_cancel_delete(de);
return;
}
de->de_sched_state = DVR_RECORDING;
de->de_rec_state = DVR_RS_PENDING;
de->de_last_error = SM_CODE_OK;
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" recorder starting",
lang_str_get(de->de_title, NULL), DVR_CH_NAME(de));
@ -1198,20 +1055,6 @@ dvr_entry_class_delete(idnode_t *self)
dvr_entry_cancel_delete((dvr_entry_t *)self);
}
static int
dvr_entry_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
{
dvr_entry_t *de = (dvr_entry_t *)self;
if (access_verify2(a, ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
if (dvr_entry_verify(de, a, msg_to_write == NULL ? 1 : 0))
return -1;
return 0;
}
static const char *
dvr_entry_class_get_title (idnode_t *self)
{
@ -1624,32 +1467,14 @@ dvr_entry_class_disp_title_get(void *o)
return &s;
}
static int
dvr_entry_class_disp_subtitle_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o;
const char *s = "";
if (v == NULL || *((char *)v) == '\0')
v = "UnknownSubtitle";
if (de->de_subtitle)
s = lang_str_get(de->de_subtitle, NULL);
if (strcmp(s, v)) {
lang_str_destroy(de->de_subtitle);
de->de_subtitle = lang_str_create();
lang_str_add(de->de_subtitle, v, NULL, 0);
return 1;
}
return 0;
}
static const void *
dvr_entry_class_disp_subtitle_get(void *o)
dvr_entry_class_disp_description_get(void *o)
{
dvr_entry_t *de = (dvr_entry_t *)o;
static const char *s;
s = "";
if (de->de_subtitle) {
s = lang_str_get(de->de_subtitle, NULL);
if (de->de_title) {
s = lang_str_get(de->de_desc, NULL);
if (s == NULL)
s = "";
}
@ -1657,16 +1482,17 @@ dvr_entry_class_disp_subtitle_get(void *o)
}
static const void *
dvr_entry_class_disp_description_get(void *o)
dvr_entry_class_episode_get(void *o)
{
dvr_entry_t *de = (dvr_entry_t *)o;
static const char *s;
static char buf[100];
s = "";
if (de->de_desc) {
s = lang_str_get(de->de_desc, NULL);
if (s == NULL)
s = "";
}
if (de->de_bcast && de->de_bcast->episode)
if (epg_episode_number_format(de->de_bcast->episode,
buf, sizeof(buf), NULL,
"Season %d", ".", "Episode %d", "/%d"))
s = buf;
return &s;
}
@ -1677,8 +1503,7 @@ dvr_entry_class_url_get(void *o)
static const char *s;
static char buf[100];
s = "";
if (de->de_sched_state == DVR_COMPLETED ||
de->de_sched_state == DVR_RECORDING) {
if (de->de_sched_state == DVR_COMPLETED) {
snprintf(buf, sizeof(buf), "dvrfile/%s", idnode_uuid_as_str(&de->de_id));
s = buf;
}
@ -1690,12 +1515,9 @@ dvr_entry_class_filesize_get(void *o)
{
static int64_t size;
dvr_entry_t *de = (dvr_entry_t *)o;
if (de->de_sched_state == DVR_COMPLETED ||
de->de_sched_state == DVR_RECORDING) {
if (de->de_sched_state == DVR_COMPLETED)
size = dvr_get_filesize(de);
if (size < 0)
size = 0;
} else
else
size = 0;
return &size;
}
@ -1771,15 +1593,6 @@ dvr_entry_class_channel_icon_url_get(void *o)
return &s;
}
static const void *
dvr_entry_class_duplicate_get(void *o)
{
static time_t null = 0;
dvr_entry_t *de = (dvr_entry_t *)o;
de = _dvr_duplicate_event(de);
return de ? &de->de_start : &null;
}
htsmsg_t *
dvr_entry_class_duration_list(void *o, const char *not_set, int max, int step)
{
@ -1829,10 +1642,10 @@ const idclass_t dvr_entry_class = {
.ic_class = "dvrentry",
.ic_caption = "DVR Entry",
.ic_event = "dvrentry",
.ic_perm_def = ACCESS_RECORDER,
.ic_save = dvr_entry_class_save,
.ic_get_title = dvr_entry_class_get_title,
.ic_delete = dvr_entry_class_delete,
.ic_perm = dvr_entry_class_perm,
.ic_properties = (const property_t[]) {
{
.type = PT_TIME,
@ -1912,7 +1725,6 @@ const idclass_t dvr_entry_class = {
.get = dvr_entry_class_channel_name_get,
.set = dvr_entry_class_channel_name_set,
.off = offsetof(dvr_entry_t, de_channel_name),
.opts = PO_RDONLY,
},
{
.type = PT_LANGSTR,
@ -1929,21 +1741,6 @@ const idclass_t dvr_entry_class = {
.set = dvr_entry_class_disp_title_set,
.opts = PO_NOSAVE,
},
{
.type = PT_LANGSTR,
.id = "subtitle",
.name = "Subtitle",
.off = offsetof(dvr_entry_t, de_subtitle),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "disp_subtitle",
.name = "Subtitle",
.get = dvr_entry_class_disp_subtitle_get,
.set = dvr_entry_class_disp_subtitle_set,
.opts = PO_NOSAVE,
},
{
.type = PT_LANGSTR,
.id = "description",
@ -1995,13 +1792,6 @@ const idclass_t dvr_entry_class = {
.rend = dvr_entry_class_config_name_rend,
.get_opts = dvr_entry_class_start_opts,
},
{
.type = PT_STR,
.id = "owner",
.name = "Owner",
.off = offsetof(dvr_entry_t, de_owner),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "creator",
@ -2016,13 +1806,6 @@ const idclass_t dvr_entry_class = {
.off = offsetof(dvr_entry_t, de_filename),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "directory",
.name = "Directory",
.off = offsetof(dvr_entry_t, de_directory),
.opts = PO_RDONLY,
},
{
.type = PT_U32,
.id = "errorcode",
@ -2037,13 +1820,6 @@ const idclass_t dvr_entry_class = {
.off = offsetof(dvr_entry_t, de_errors),
.opts = PO_RDONLY,
},
{
.type = PT_U32,
.id = "data_errors",
.name = "Data Errors",
.off = offsetof(dvr_entry_t, de_data_errors),
.opts = PO_RDONLY,
},
{
.type = PT_U16,
.id = "dvb_eid",
@ -2094,8 +1870,8 @@ const idclass_t dvr_entry_class = {
.type = PT_STR,
.id = "episode",
.name = "Episode",
.off = offsetof(dvr_entry_t, de_episode),
.opts = PO_RDONLY | PO_HIDDEN,
.get = dvr_entry_class_episode_get,
.opts = PO_RDONLY | PO_NOSAVE | PO_HIDDEN,
},
{
.type = PT_STR,
@ -2125,19 +1901,6 @@ const idclass_t dvr_entry_class = {
.get = dvr_entry_class_sched_status_get,
.opts = PO_RDONLY | PO_NOSAVE | PO_HIDDEN,
},
{
.type = PT_TIME,
.id = "duplicate",
.name = "Rerun of",
.get = dvr_entry_class_duplicate_get,
.opts = PO_RDONLY | PO_NOSAVE,
},
{
.type = PT_STR,
.id = "comment",
.name = "Comment",
.off = offsetof(dvr_entry_t, de_comment),
},
{}
}
};
@ -2186,7 +1949,7 @@ static struct strtab priotab[] = {
{ "high", DVR_PRIO_HIGH },
{ "normal", DVR_PRIO_NORMAL },
{ "low", DVR_PRIO_LOW },
{ "unimportant", DVR_PRIO_UNIMPORTANT }
{ "unimportant", DVR_PRIO_UNIMPORTANT },
};
dvr_prio_t
@ -2208,11 +1971,9 @@ dvr_val2pri(dvr_prio_t v)
void
dvr_entry_delete(dvr_entry_t *de)
{
dvr_config_t *cfg = de->de_config;
time_t t;
struct tm tm;
char tbuf[64], *rdir;
int r;
char tbuf[64];
t = dvr_entry_get_start_time(de);
localtime_r(&t, &tm);
@ -2230,14 +1991,37 @@ dvr_entry_delete(dvr_entry_t *de)
#if ENABLE_INOTIFY
dvr_inotify_del(de);
#endif
rdir = NULL;
if(cfg->dvr_title_dir || cfg->dvr_channel_dir || cfg->dvr_dir_per_day || de->de_directory)
rdir = cfg->dvr_storage;
r = deferred_unlink(de->de_filename, rdir);
if(r && r != -ENOENT)
if(unlink(de->de_filename) && errno != ENOENT)
tvhlog(LOG_WARNING, "dvr", "Unable to remove file '%s' from disk -- %s",
de->de_filename, strerror(-errno));
de->de_filename, strerror(errno));
/* Also delete directories, if they were created for the recording and if they are empty */
dvr_config_t *cfg = de->de_config;
char path[500];
snprintf(path, sizeof(path), "%s", cfg->dvr_storage);
if(cfg->dvr_title_dir || cfg->dvr_channel_dir || cfg->dvr_dir_per_day) {
char *p;
int l;
l = strlen(de->de_filename);
p = alloca(l + 1);
memcpy(p, de->de_filename, l);
p[l--] = 0;
for(; l >= 0; l--) {
if(p[l] == '/') {
p[l] = 0;
if(strncmp(p, cfg->dvr_storage, strlen(p)) == 0)
break;
if(rmdir(p) == -1)
break;
}
}
}
}
dvr_entry_destroy(de, 1);
}

View file

@ -63,7 +63,7 @@ pthread_t dvr_inotify_tid;
void dvr_inotify_init ( void )
{
_inot_fd = inotify_init1(IN_CLOEXEC);
_inot_fd = inotify_init();
if (_inot_fd < 0) {
tvhlog(LOG_ERR, "dvr", "failed to initialise inotify (err=%s)",
strerror(errno));
@ -93,11 +93,12 @@ void dvr_inotify_add ( dvr_entry_t *de )
{
dvr_inotify_entry_t *e;
char *path;
struct stat st;
if (_inot_fd < 0)
return;
if (!de->de_filename)
if (!de->de_filename || stat(de->de_filename, &st))
return;
path = strdup(de->de_filename);
@ -105,6 +106,11 @@ void dvr_inotify_add ( dvr_entry_t *de )
SKEL_ALLOC(dvr_inotify_entry_skel);
dvr_inotify_entry_skel->path = dirname(path);
if (stat(dvr_inotify_entry_skel->path, &st)) {
free(path);
return;
}
e = RB_INSERT_SORTED(&_inot_tree, dvr_inotify_entry_skel, link, _str_cmp);
if (!e) {
e = dvr_inotify_entry_skel;

View file

@ -69,7 +69,7 @@ dvr_rec_subscribe(dvr_entry_t *de)
assert(de->de_s == NULL);
assert(de->de_chain == NULL);
if(de->de_pri > 0 && de->de_pri < ARRAY_SIZE(prio2weight))
if(de->de_pri < ARRAY_SIZE(prio2weight))
weight = prio2weight[de->de_pri];
else
weight = 300;
@ -85,9 +85,9 @@ dvr_rec_subscribe(dvr_entry_t *de)
return;
}
de->de_s = subscription_create_from_channel(prch, NULL, weight,
de->de_s = subscription_create_from_channel(prch, weight,
buf, prch->prch_flags,
NULL, NULL, NULL, NULL);
NULL, NULL, NULL);
if (de->de_s == NULL) {
tvherror("dvr", "unable to create new channel subcription for '%s'",
channel_get_name(de->de_channel));
@ -117,7 +117,7 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
pthread_join(de->de_thread, NULL);
subscription_unsubscribe(de->de_s, 0);
subscription_unsubscribe(de->de_s);
de->de_s = NULL;
de->de_chain = NULL;
@ -134,7 +134,7 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
static char *
cleanup_filename(char *s, dvr_config_t *cfg)
{
int i, len = strlen(s), len2;
int i, len = strlen(s);
char *s1;
s1 = intlconv_utf8safestr(cfg->dvr_charset_id, s, len * 2);
@ -151,8 +151,7 @@ cleanup_filename(char *s, dvr_config_t *cfg)
if (s[0] == '.')
s[0] = '_';
len2 = strlen(s);
for (i = 0; i < len2; i++) {
for (i = 0, len = strlen(s); i < len; i++) {
if(s[i] == '/')
s[i] = '-';
@ -165,18 +164,6 @@ cleanup_filename(char *s, dvr_config_t *cfg)
((s[i] < 32) || (s[i] > 122) ||
(strchr("/:\\<>|*?'\"", s[i]) != NULL)))
s[i] = '_';
else if(cfg->dvr_windows_compatible_filenames &&
(strchr("/:\\<>|*?'\"", s[i]) != NULL))
s[i] = '_';
}
if(cfg->dvr_windows_compatible_filenames) {
// trim trailing spaces and dots
for (i = len2 - 1; i >= 0; i--) {
if((s[i] != ' ') && (s[i] != '.'))
break;
s[i] = '\0';
}
}
return s;
@ -211,53 +198,42 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
if (path[strlen(path)-1] == '/')
path[strlen(path)-1] = '\0';
/* Use the specified directory if set, otherwise construct it from the DVR
configuration */
if (de->de_directory) {
char *directory = strdup(de->de_directory);
s = cleanup_filename(directory, cfg);
/* Append per-day directory */
if (cfg->dvr_dir_per_day) {
localtime_r(&de->de_start, &tm);
strftime(fullname, sizeof(fullname), "%F", &tm);
s = cleanup_filename(fullname, cfg);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
} else {
/* Append per-day directory */
if (cfg->dvr_dir_per_day) {
localtime_r(&de->de_start, &tm);
strftime(fullname, sizeof(fullname), "%F", &tm);
s = cleanup_filename(fullname, cfg);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
/* Append per-channel directory */
if (cfg->dvr_channel_dir) {
char *chname = strdup(DVR_CH_NAME(de));
s = cleanup_filename(chname, cfg);
free(chname);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
// TODO: per-brand, per-season
/* Append per-title directory */
if (cfg->dvr_title_dir) {
char *title = strdup(lang_str_get(de->de_title, NULL));
s = cleanup_filename(title, cfg);
free(title);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
}
if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions, -1, -1) != 0)
/* Append per-channel directory */
if (cfg->dvr_channel_dir) {
char *chname = strdup(DVR_CH_NAME(de));
s = cleanup_filename(chname, cfg);
free(chname);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
// TODO: per-brand, per-season
/* Append per-title directory */
if (cfg->dvr_title_dir) {
char *title = strdup(lang_str_get(de->de_title, NULL));
s = cleanup_filename(title, cfg);
free(title);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0)
return -1;
/* Construct final name */
@ -309,18 +285,6 @@ dvr_rec_fatal_error(dvr_entry_t *de, const char *fmt, ...)
de->de_filename ?: lang_str_get(de->de_title, NULL), msgbuf);
}
/**
*
*/
static void
dvr_notify(dvr_entry_t *de, int now)
{
if (now || de->de_last_notify + 5 < dispatch_clock) {
idnode_notify_simple(&de->de_id);
de->de_last_notify = dispatch_clock;
htsp_dvr_entry_update(de);
}
}
/**
*
@ -340,7 +304,7 @@ dvr_rec_set_state(dvr_entry_t *de, dvr_rs_state_t newstate, int error)
de->de_errors++;
}
if (notify)
dvr_notify(de, 1);
idnode_notify_simple(&de->de_id);
}
/**
@ -394,8 +358,8 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
return -1;
}
if(cfg->dvr_tag_files) {
if(muxer_write_meta(muxer, de->de_bcast, de->de_comment)) {
if(cfg->dvr_tag_files && de->de_bcast) {
if(muxer_write_meta(muxer, de->de_bcast)) {
dvr_rec_fatal_error(de, "Unable to write meta data");
return -1;
}
@ -495,7 +459,6 @@ dvr_thread(void *aux)
profile_chain_t *prch = de->de_chain;
streaming_queue_t *sq = &prch->prch_sq;
streaming_message_t *sm;
th_subscription_t *ts;
th_pkt_t *pkt;
int run = 1;
int started = 0;
@ -511,24 +474,14 @@ dvr_thread(void *aux)
continue;
}
if ((ts = de->de_s) != NULL && started) {
if (de->de_s && started) {
pktbuf_t *pb = NULL;
if (sm->sm_type == SMT_PACKET) {
if (sm->sm_type == SMT_PACKET)
pb = ((th_pkt_t*)sm->sm_data)->pkt_payload;
if (((th_pkt_t*)sm->sm_data)->pkt_err) {
de->de_data_errors += ((th_pkt_t*)sm->sm_data)->pkt_err;
dvr_notify(de, 0);
}
}
else if (sm->sm_type == SMT_MPEGTS) {
else if (sm->sm_type == SMT_MPEGTS)
pb = sm->sm_data;
if (pb->pb_err) {
de->de_data_errors += pb->pb_err;
dvr_notify(de, 0);
}
}
if (pb)
atomic_add(&ts->ths_bytes_out, pktbuf_len(pb));
atomic_add(&de->de_s->ths_bytes_out, pktbuf_len(pb));
}
TAILQ_REMOVE(&sq->sq_queue, sm, sm_link);
@ -555,7 +508,6 @@ dvr_thread(void *aux)
if(started) {
muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
sm->sm_data = NULL;
dvr_notify(de, 0);
}
break;
@ -564,7 +516,6 @@ dvr_thread(void *aux)
dvr_rec_set_state(de, DVR_RS_RUNNING, 0);
muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
sm->sm_data = NULL;
dvr_notify(de, 0);
}
break;
@ -600,7 +551,7 @@ dvr_thread(void *aux)
} else if(sm->sm_code == 0) {
// Recording is completed
de->de_last_error = SM_CODE_OK;
de->de_last_error = 0;
tvhlog(LOG_INFO,
"dvr", "Recording completed: \"%s\"",
de->de_filename ?: lang_str_get(de->de_title, NULL));
@ -625,10 +576,11 @@ dvr_thread(void *aux)
case SMT_SERVICE_STATUS:
if(sm->sm_code & TSS_PACKETS) {
} else if(sm->sm_code & TSS_ERRORS) {
} else if(sm->sm_code & (TSS_GRACEPERIOD | TSS_ERRORS)) {
int code = SM_CODE_UNDEFINED_ERROR;
if(sm->sm_code & TSS_NO_DESCRAMBLER)
code = SM_CODE_NO_DESCRAMBLER;
@ -727,7 +679,7 @@ dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc)
args[i] = s;
}
spawnv(args[0], (void *)args, NULL, 1, 1);
spawnv(args[0], (void *)args);
htsstr_argsplit_free(args);
}
@ -743,7 +695,6 @@ dvr_thread_epilog(dvr_entry_t *de)
muxer_close(prch->prch_muxer);
muxer_destroy(prch->prch_muxer);
prch->prch_muxer = NULL;
dvr_notify(de, 1);
dvr_config_t *cfg = de->de_config;
if(cfg && cfg->dvr_postproc && de->de_filename)

View file

@ -32,7 +32,6 @@
#include "settings.h"
#include "dvr.h"
#include "epg.h"
#include "htsp_server.h"
struct dvr_timerec_entry_queue timerec_entries;
@ -133,7 +132,7 @@ dvr_timerec_check(dvr_timerec_entry_t *dte)
/* day boundary correction */
if (start > stop)
stop += 24 * 60 * 60;
assert(start <= stop);
assert(start < stop);
if(dte->dte_weekdays != 0x7f) {
localtime_r(&start, &tm_start);
@ -155,10 +154,9 @@ dvr_timerec_check(dvr_timerec_entry_t *dte)
dte->dte_creator ?: "");
de = dvr_entry_create_(idnode_uuid_as_str(&dte->dte_config->dvr_id),
NULL, dte->dte_channel,
start, stop, 0, 0, title, NULL,
NULL, NULL, NULL, dte->dte_owner, buf,
NULL, dte, dte->dte_pri, dte->dte_retention,
dte->dte_comment);
start, stop, 0, 0, title,
NULL, NULL, NULL, buf,
NULL, dte, dte->dte_pri, dte->dte_retention);
return;
@ -183,7 +181,7 @@ dvr_timerec_create(const char *uuid, htsmsg_t *conf)
return NULL;
}
dte->dte_title = strdup("Time-%F_%R");
dte->dte_title = strdup("Time-%x-%R");
dte->dte_weekdays = 0x7f;
dte->dte_pri = DVR_PRIO_NORMAL;
dte->dte_start = -1;
@ -195,56 +193,6 @@ dvr_timerec_create(const char *uuid, htsmsg_t *conf)
idnode_load(&dte->dte_id, conf);
htsp_timerec_entry_add(dte);
return dte;
}
dvr_timerec_entry_t*
dvr_timerec_create_htsp(const char *dvr_config_name, const char *title,
channel_t *ch, uint32_t enabled, uint32_t start, uint32_t stop,
uint32_t weekdays, dvr_prio_t pri, int retention,
const char *owner, const char *creator, const char *comment,
const char *name, const char *directory)
{
dvr_timerec_entry_t *dte;
htsmsg_t *conf, *days;
conf = htsmsg_create_map();
days = htsmsg_create_list();
htsmsg_add_u32(conf, "enabled", enabled > 0 ? 1 : 0);
htsmsg_add_u32(conf, "retention", retention);
htsmsg_add_u32(conf, "pri", pri);
htsmsg_add_str(conf, "title", title);
htsmsg_add_str(conf, "config_name", dvr_config_name ?: "");
htsmsg_add_str(conf, "owner", owner ?: "");
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
htsmsg_add_str(conf, "name", name ?: "");
htsmsg_add_str(conf, "directory", directory ?: "");
htsmsg_add_u32(conf, "start", start);
htsmsg_add_u32(conf, "stop", stop);
if (ch)
htsmsg_add_str(conf, "channel", idnode_uuid_as_str(&ch->ch_id));
int i;
for (i = 0; i < 7; i++)
if (weekdays & (1 << i))
htsmsg_add_u32(days, NULL, i + 1);
htsmsg_add_msg(conf, "weekdays", days);
dte = dvr_timerec_create(NULL, conf);
htsmsg_destroy(conf);
if (dte)
{
dvr_timerec_save(dte);
dvr_timerec_check(dte);
}
return dte;
}
@ -259,8 +207,6 @@ timerec_entry_destroy(dvr_timerec_entry_t *dte, int delconf)
if (delconf)
hts_settings_remove("dvr/timerec/%s", idnode_uuid_as_str(&dte->dte_id));
htsp_timerec_entry_delete(dte);
TAILQ_REMOVE(&timerec_entries, dte, dte_link);
idnode_unlink(&dte->dte_id);
@ -269,7 +215,6 @@ timerec_entry_destroy(dvr_timerec_entry_t *dte, int delconf)
free(dte->dte_name);
free(dte->dte_title);
free(dte->dte_owner);
free(dte->dte_creator);
free(dte->dte_comment);
@ -304,7 +249,6 @@ dvr_timerec_entry_class_save(idnode_t *self)
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)self;
dvr_timerec_save(dte);
dvr_timerec_check(dte);
htsp_timerec_entry_update(dte);
}
static void
@ -313,20 +257,6 @@ dvr_timerec_entry_class_delete(idnode_t *self)
timerec_entry_destroy((dvr_timerec_entry_t *)self, 1);
}
static int
dvr_timerec_entry_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
{
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)self;
if (access_verify2(a, ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
if (dvr_timerec_entry_verify(dte, a))
return -1;
return 0;
}
static const char *
dvr_timerec_entry_class_get_title (idnode_t *self)
{
@ -538,7 +468,6 @@ const idclass_t dvr_timerec_entry_class = {
.ic_save = dvr_timerec_entry_class_save,
.ic_get_title = dvr_timerec_entry_class_get_title,
.ic_delete = dvr_timerec_entry_class_delete,
.ic_perm = dvr_timerec_entry_class_perm,
.ic_properties = (const property_t[]) {
{
.type = PT_BOOL,
@ -557,13 +486,7 @@ const idclass_t dvr_timerec_entry_class = {
.id = "title",
.name = "Title",
.off = offsetof(dvr_timerec_entry_t, dte_title),
.def.s = "Time-%F_%R",
},
{
.type = PT_STR,
.id = "directory",
.name = "Directory",
.off = offsetof(dvr_timerec_entry_t, dte_directory),
.def.s = "Time-%x-%R",
},
{
.type = PT_STR,
@ -629,13 +552,6 @@ const idclass_t dvr_timerec_entry_class = {
.rend = dvr_timerec_entry_class_config_name_rend,
.list = dvr_entry_class_config_name_list,
},
{
.type = PT_STR,
.id = "owner",
.name = "Owner",
.off = offsetof(dvr_timerec_entry_t, dte_creator),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "creator",
@ -692,9 +608,8 @@ dvr_timerec_timer_cb(void *aux)
tvhtrace("dvr", "timerec update");
/* check all entries */
TAILQ_FOREACH(dte, &timerec_entries, dte_link) {
TAILQ_FOREACH(dte, &timerec_entries, dte_link)
dvr_timerec_check(dte);
}
/* load the timer */
gtimer_arm(&dvr_timerec_timer, dvr_timerec_timer_cb, NULL, 3550);

View file

@ -1101,19 +1101,19 @@ size_t epg_episode_number_format
epg_episode_num_t num;
epg_episode_get_epnum(episode, &num);
if ( num.e_num ) {
if (pre) tvh_strlcatf(buf, len, i, "%s", pre);
if (pre) i += snprintf(&buf[i], len-i, "%s", pre);
if ( sfmt && num.s_num ) {
tvh_strlcatf(buf, len, i, sfmt, num.s_num);
i += snprintf(&buf[i], len-i, sfmt, num.s_num);
if ( cfmt && num.s_cnt )
tvh_strlcatf(buf, len, i, cfmt, num.s_cnt);
if (sep) tvh_strlcatf(buf, len, i, "%s", sep);
i += snprintf(&buf[i], len-i, cfmt, num.s_cnt);
if (sep) i += snprintf(&buf[i], len-i, "%s", sep);
}
tvh_strlcatf(buf, len, i, efmt, num.e_num);
i += snprintf(&buf[i], len-i, efmt, num.e_num);
if ( cfmt && num.e_cnt )
tvh_strlcatf(buf, len, i, cfmt, num.e_cnt);
i+= snprintf(&buf[i], len-i, cfmt, num.e_cnt);
} else if ( num.text ) {
if (pre) tvh_strlcatf(buf, len, i, "%s", pre);
tvh_strlcatf(buf, len, i, "%s", num.text);
if (pre) i += snprintf(&buf[i], len-i, "%s", pre);
i += snprintf(&buf[i], len-i, "%s", num.text);
}
return i;
}
@ -1289,7 +1289,7 @@ const char *epg_episode_get_subtitle
return lang_str_get(e->subtitle, lang);
}
const char *epg_episode_get_summary
const char *epg_episode_get_summary
( const epg_episode_t *e, const char *lang )
{
if (!e || !e->summary) return NULL;
@ -2118,11 +2118,12 @@ size_t epg_genre_get_str ( const epg_genre_t *genre, int major_only,
if (!_epg_genre_names[maj][0]) return 0;
min = major_only ? 0 : (genre->code & 0xf);
if (!min || major_prefix ) {
tvh_strlcatf(buf, len, ret, "%s", _epg_genre_names[maj][0]);
if (min) tvh_strlcatf(buf, len, ret, " : ");
ret = snprintf(buf, len, "%s", _epg_genre_names[maj][0]);
if (min) ret += snprintf(buf+ret, len-ret, " : ");
}
if (min && _epg_genre_names[maj][min]) {
ret += snprintf(buf+ret, len-ret, "%s", _epg_genre_names[maj][min]);
}
if (min && _epg_genre_names[maj][min])
tvh_strlcatf(buf, len, ret, "%s", _epg_genre_names[maj][min]);
return ret;
}
@ -2262,7 +2263,6 @@ _eq_add ( epg_query_t *eq, epg_broadcast_t *e )
{
const char *s, *lang = eq->lang;
epg_episode_t *ep;
int fulltext = eq->stitle && eq->fulltext;
/* Filtering */
if (e == NULL) return;
@ -2292,36 +2292,22 @@ _eq_add ( epg_query_t *eq, epg_broadcast_t *e )
}
if (!r) return;
}
if (fulltext) {
if ((s = epg_episode_get_title(ep, lang)) == NULL ||
regexec(&eq->stitle_re, s, 0, NULL, 0)) {
if ((s = epg_episode_get_subtitle(ep, lang)) == NULL ||
regexec(&eq->stitle_re, s, 0, NULL, 0)) {
if ((s = epg_broadcast_get_summary(e, lang)) == NULL ||
regexec(&eq->stitle_re, s, 0, NULL, 0)) {
if ((s = epg_broadcast_get_description(e, lang)) == NULL ||
regexec(&eq->stitle_re, s, 0, NULL, 0)) {
return;
}
}
}
}
}
if (eq->title.comp != EC_NO || (eq->stitle && !fulltext)) {
if (eq->title.comp != EC_NO || eq->stitle) {
if ((s = epg_episode_get_title(ep, lang)) == NULL) return;
if (eq->stitle && !fulltext && regexec(&eq->stitle_re, s, 0, NULL, 0)) return;
if (eq->title.comp != EC_NO && _eq_comp_str(&eq->title, s)) return;
if (eq->stitle)
if (regexec(&eq->stitle_re, s, 0, NULL, 0)) return;
if (_eq_comp_str(&eq->title, s)) return;
}
if (eq->subtitle.comp != EC_NO) {
if ((s = epg_episode_get_subtitle(ep, lang)) == NULL) return;
if (_eq_comp_str(&eq->subtitle, s)) return;
}
if (eq->summary.comp != EC_NO) {
if ((s = epg_broadcast_get_summary(e, lang)) == NULL) return;
if ((s = epg_episode_get_summary(ep, lang)) == NULL) return;
if (_eq_comp_str(&eq->summary, s)) return;
}
if (eq->description.comp != EC_NO) {
if ((s = epg_broadcast_get_description(e, lang)) == NULL) return;
if ((s = epg_episode_get_description(ep, lang)) == NULL) return;
if (_eq_comp_str(&eq->description, s)) return;
}
@ -2531,7 +2517,7 @@ static int _epg_sort_genre_descending ( const void *a, const void *b, void *eq )
}
epg_broadcast_t **
epg_query ( epg_query_t *eq, access_t *perm )
epg_query ( epg_query_t *eq )
{
channel_t *channel;
channel_tag_t *tag;
@ -2556,25 +2542,20 @@ epg_query ( epg_query_t *eq, access_t *perm )
/* Single channel */
if (channel && tag == NULL) {
if (channel_access(channel, perm, 0))
_eq_add_channel(eq, channel);
_eq_add_channel(eq, channel);
/* Tag based */
} else if (tag) {
channel_tag_mapping_t *ctm;
channel_t *ch2;
LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
ch2 = ctm->ctm_channel;
if(ch2 == channel || channel == NULL)
if (channel_access(ch2, perm, 0))
_eq_add_channel(eq, ch2);
if(channel == NULL || ctm->ctm_channel == channel)
_eq_add_channel(eq, ctm->ctm_channel);
}
/* All channels */
} else {
CHANNEL_FOREACH(channel)
if (channel_access(channel, perm, 0))
_eq_add_channel(eq, channel);
_eq_add_channel(eq, channel);
}
switch (eq->sort_dir) {

View file

@ -22,7 +22,6 @@
#include <regex.h>
#include "settings.h"
#include "lang_str.h"
#include "access.h"
/*
* External forward decls
@ -579,7 +578,6 @@ typedef struct epg_query {
epg_filter_num_t channel_num;
char *stitle;
regex_t stitle_re;
int fulltext;
char *channel;
char *channel_tag;
uint32_t genre_count;
@ -611,7 +609,7 @@ typedef struct epg_query {
uint32_t allocated;
} epg_query_t;
epg_broadcast_t **epg_query(epg_query_t *eq, access_t *perm);
epg_broadcast_t **epg_query(epg_query_t *eq);
void epg_query_free(epg_query_t *eq);
/* ************************************************************************

View file

@ -31,7 +31,6 @@
#include "epggrab.h"
#define EPG_DB_VERSION 2
#define EPG_DB_ALLOC_STEP (1024*1024)
extern epg_object_tree_t epg_brands;
extern epg_object_tree_t epg_seasons;
@ -262,7 +261,7 @@ void epg_done ( void )
* Save
* *************************************************************************/
static int _epg_write ( sbuf_t *sb, htsmsg_t *m )
static int _epg_write ( int fd, htsmsg_t *m )
{
int ret = 1;
size_t msglen;
@ -271,11 +270,7 @@ static int _epg_write ( sbuf_t *sb, htsmsg_t *m )
int r = htsmsg_binary_serialize(m, &msgdata, &msglen, 0x10000);
htsmsg_destroy(m);
if (!r) {
ret = 0;
/* allocation helper - we fight with megabytes */
if (sb->sb_size - sb->sb_ptr < 32 * 1024)
sbuf_realloc(sb, (sb->sb_size - (sb->sb_size % EPG_DB_ALLOC_STEP)) + EPG_DB_ALLOC_STEP);
sbuf_append(sb, msgdata, msglen);
ret = tvh_write(fd, msgdata, msglen);
free(msgdata);
}
} else {
@ -284,31 +279,11 @@ static int _epg_write ( sbuf_t *sb, htsmsg_t *m )
return ret;
}
static int _epg_write_sect ( sbuf_t *sb, const char *sect )
static int _epg_write_sect ( int fd, const char *sect )
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "__section__", sect);
return _epg_write(sb, m);
}
static void epg_save_tsk_callback ( void *p, int dearmed )
{
sbuf_t *sb = p;
int fd, r;
tvhinfo("epgdb", "save start");
fd = hts_settings_open_file(1, "epgdb.v%d", EPG_DB_VERSION);
if (fd >= 0) {
r = tvh_write(fd, sb->sb_data, sb->sb_ptr);
close(fd);
if (r)
tvherror("epgdb", "write error (size %d)", sb->sb_ptr);
else
tvhinfo("epgdb", "stored (size %d)", sb->sb_ptr);
} else
tvherror("epgdb", "unable to open epgdb file");
sbuf_free(sb);
free(sb);
return _epg_write(fd, m);
}
void epg_save_callback ( void *p )
@ -318,68 +293,63 @@ void epg_save_callback ( void *p )
void epg_save ( void )
{
sbuf_t *sb = malloc(sizeof(*sb));
int fd;
epg_object_t *eo;
epg_broadcast_t *ebc;
channel_t *ch;
epggrab_stats_t stats;
extern gtimer_t epggrab_save_timer;
if (!sb)
return;
tvhinfo("epgdb", "snapshot start");
sbuf_init_fixed(sb, EPG_DB_ALLOC_STEP);
if (epggrab_epgdb_periodicsave)
gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL, epggrab_epgdb_periodicsave);
fd = hts_settings_open_file(1, "epgdb.v%d", EPG_DB_VERSION);
if (fd < 0)
return;
memset(&stats, 0, sizeof(stats));
if ( _epg_write_sect(sb, "config") ) goto error;
if (_epg_write(sb, epg_config_serialize())) goto error;
if ( _epg_write_sect(sb, "brands") ) goto error;
if ( _epg_write_sect(fd, "config") ) goto error;
if (_epg_write(fd, epg_config_serialize())) goto error;
if ( _epg_write_sect(fd, "brands") ) goto error;
RB_FOREACH(eo, &epg_brands, uri_link) {
if (_epg_write(sb, epg_brand_serialize((epg_brand_t*)eo))) goto error;
if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) goto error;
stats.brands.total++;
}
if ( _epg_write_sect(sb, "seasons") ) goto error;
if ( _epg_write_sect(fd, "seasons") ) goto error;
RB_FOREACH(eo, &epg_seasons, uri_link) {
if (_epg_write(sb, epg_season_serialize((epg_season_t*)eo))) goto error;
if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) goto error;
stats.seasons.total++;
}
if ( _epg_write_sect(sb, "episodes") ) goto error;
if ( _epg_write_sect(fd, "episodes") ) goto error;
RB_FOREACH(eo, &epg_episodes, uri_link) {
if (_epg_write(sb, epg_episode_serialize((epg_episode_t*)eo))) goto error;
if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) goto error;
stats.episodes.total++;
}
if ( _epg_write_sect(sb, "serieslinks") ) goto error;
if ( _epg_write_sect(fd, "serieslinks") ) goto error;
RB_FOREACH(eo, &epg_serieslinks, uri_link) {
if (_epg_write(sb, epg_serieslink_serialize((epg_serieslink_t*)eo))) goto error;
if (_epg_write(fd, epg_serieslink_serialize((epg_serieslink_t*)eo))) goto error;
stats.seasons.total++;
}
if ( _epg_write_sect(sb, "broadcasts") ) goto error;
if ( _epg_write_sect(fd, "broadcasts") ) goto error;
CHANNEL_FOREACH(ch) {
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
if (_epg_write(sb, epg_broadcast_serialize(ebc))) goto error;
if (_epg_write(fd, epg_broadcast_serialize(ebc))) goto error;
stats.broadcasts.total++;
}
}
tasklet_arm_alloc(epg_save_tsk_callback, sb);
close(fd);
/* Stats */
tvhinfo("epgdb", "queued to save (size %d)", sb->sb_ptr);
tvhinfo("epgdb", " brands %d", stats.brands.total);
tvhinfo("epgdb", " seasons %d", stats.seasons.total);
tvhinfo("epgdb", " episodes %d", stats.episodes.total);
tvhinfo("epgdb", " broadcasts %d", stats.broadcasts.total);
tvhlog(LOG_INFO, "epgdb", "saved");
tvhlog(LOG_INFO, "epgdb", " brands %d", stats.brands.total);
tvhlog(LOG_INFO, "epgdb", " seasons %d", stats.seasons.total);
tvhlog(LOG_INFO, "epgdb", " episodes %d", stats.episodes.total);
tvhlog(LOG_INFO, "epgdb", " broadcasts %d", stats.broadcasts.total);
return;
error:
tvhlog(LOG_ERR, "epgdb", "failed to store epg to disk");
hts_settings_remove("epgdb.v%d", EPG_DB_VERSION);
sbuf_free(sb);
free(sb);
close(fd);
}

View file

@ -148,11 +148,9 @@ static void _epggrab_load ( void )
htsmsg_get_u32(m, "channel_renumber", &epggrab_channel_renumber);
htsmsg_get_u32(m, "channel_reicon", &epggrab_channel_reicon);
htsmsg_get_u32(m, "epgdb_periodicsave", &epggrab_epgdb_periodicsave);
if (epggrab_epgdb_periodicsave) {
epggrab_epgdb_periodicsave = MAX(epggrab_epgdb_periodicsave, 3600);
if (epggrab_epgdb_periodicsave)
gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL,
epggrab_epgdb_periodicsave);
}
if ((str = htsmsg_get_str(m, "cron")) != NULL)
epggrab_set_cron(str);
htsmsg_get_u32(m, "grab-enabled", &enabled);
@ -297,16 +295,16 @@ int epggrab_set_channel_renumber ( uint32_t e )
int epggrab_set_periodicsave ( uint32_t e )
{
int save = 0;
pthread_mutex_lock(&global_lock);
if ( e != epggrab_epgdb_periodicsave ) {
epggrab_epgdb_periodicsave = e ? MAX(e, 3600) : 0;
epggrab_epgdb_periodicsave = e;
pthread_mutex_lock(&global_lock);
if (!e)
gtimer_disarm(&epggrab_save_timer);
else
epg_save(); // will arm the timer
pthread_mutex_unlock(&global_lock);
save = 1;
}
pthread_mutex_unlock(&global_lock);
return save;
}

View file

@ -83,9 +83,8 @@ typedef struct epggrab_channel
char *name; ///< Channel name
char *icon; ///< Channel icon
int major; ///< Channel major number
int minor; ///< Channel minor number
int number; ///< Channel number
LIST_HEAD(,epggrab_channel_link) channels; ///< Mapped channels
} epggrab_channel_t;
@ -108,7 +107,7 @@ htsmsg_t* epggrab_channel_list ( int ota );
*/
int epggrab_channel_set_name ( epggrab_channel_t *ch, const char *name );
int epggrab_channel_set_icon ( epggrab_channel_t *ch, const char *icon );
int epggrab_channel_set_number ( epggrab_channel_t *ch, int major, int minor );
int epggrab_channel_set_number ( epggrab_channel_t *ch, int number );
/*
* Updated/link
@ -293,7 +292,6 @@ int epggrab_enable_module_by_id ( const char *id, uint8_t e );
int epggrab_ota_set_cron ( const char *cron, int lock );
int epggrab_ota_set_timeout ( uint32_t e );
int epggrab_ota_set_initial ( uint32_t e );
void epggrab_ota_trigger ( int secs );
/*
* Load/Save

View file

@ -36,12 +36,10 @@ SKEL_DECLARE(epggrab_channel_skel, epggrab_channel_t);
/* Check if channels match */
int epggrab_channel_match ( epggrab_channel_t *ec, channel_t *ch )
{
if (!ec || !ch || !ch->ch_epgauto || !ch->ch_enabled) return 0;
if (!ec || !ch) return 0;
if (LIST_FIRST(&ec->channels)) return 0; // ignore already paired
if (ec->name && !strcmp(ec->name, channel_get_name(ch))) return 1;
int64_t number = channel_get_number(ch);
if ((ec->major || ec->minor) && ec->major == channel_get_major(number) && ec->minor == channel_get_minor(number)) return 1;
return 0;
}
@ -61,11 +59,10 @@ epggrab_channel_link_delete
int
epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
{
int save = 0;
epggrab_channel_link_t *ecl;
/* No change */
if (!ch || !ch->ch_enabled) return 0;
if (!ch) return 0;
/* Already linked */
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) {
@ -83,14 +80,14 @@ epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
ecl->ecl_epggrab = ec;
LIST_INSERT_HEAD(&ec->channels, ecl, ecl_epg_link);
LIST_INSERT_HEAD(&ch->ch_epggrab, ecl, ecl_chn_link);
#if TODO_CHAN_UPDATE
if (ec->name && epggrab_channel_rename)
save |= channel_set_name(ch, ec->name);
if ((ec->major > 0 || ec->minor > 0) && epggrab_channel_renumber)
save |= channel_set_number(ch, ec->major, ec->minor);
channel_rename(ch, ec->name);
if (ec->number>0 && epggrab_channel_renumber)
channel_set_number(ch, ec->number);
if (ec->icon && epggrab_channel_reicon)
save |= channel_set_icon(ch, ec->icon);
if (save)
channel_save(ch);
channel_set_icon(ch, ec->icon);
#endif
/* Save */
if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec);
@ -114,13 +111,13 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
if (!ec->name || strcmp(ec->name, name)) {
if (ec->name) free(ec->name);
ec->name = strdup(name);
#if TODO_CHAN_UPDATE
if (epggrab_channel_rename) {
epggrab_channel_link_t *ecl;
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) {
if (channel_set_name(ecl->ecl_channel, name))
channel_save(ecl->ecl_channel);
}
LIST_FOREACH(ecl, &ec->channels, link)
channel_rename(ecl->channel, name);
}
#endif
save = 1;
}
return save;
@ -134,33 +131,32 @@ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
if (!ec->icon || strcmp(ec->icon, icon) ) {
if (ec->icon) free(ec->icon);
ec->icon = strdup(icon);
#if TODO_CHAN_UPDATE
if (epggrab_channel_reicon) {
epggrab_channel_link_t *ecl;
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) {
if (channel_set_icon(ecl->ecl_channel, icon))
channel_save(ecl->ecl_channel);
}
LIST_FOREACH(ecl, &ec->channels, link)
channel_set_icon(ecl->channel, icon);
}
#endif
save = 1;
}
return save;
}
/* Set channel number */
int epggrab_channel_set_number ( epggrab_channel_t *ec, int major, int minor )
int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
{
int save = 0;
if (!ec || (major <= 0 && minor <= 0)) return 0;
if (ec->major != major || ec->minor != minor) {
ec->major = major;
ec->minor = minor;
if (!ec || (number <= 0)) return 0;
if (ec->number != number) {
ec->number = number;
#if TODO_CHAN_UPDATE
if (epggrab_channel_renumber) {
epggrab_channel_link_t *ecl;
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) {
if (channel_set_number(ecl->ecl_channel, major, minor))
channel_save(ecl->ecl_channel);
}
LIST_FOREACH(ecl, &ec->channels, link)
channel_set_number(ecl->channel, number);
}
#endif
save = 1;
}
return save;

View file

@ -163,10 +163,8 @@ void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch )
htsmsg_add_str(a, NULL, channel_get_uuid(ecl->ecl_channel));
}
if (a) htsmsg_add_msg(m, "channels", a);
if (ch->major)
htsmsg_add_u32(m, "major", ch->major);
if (ch->minor)
htsmsg_add_u32(m, "major", ch->minor);
if (ch->number)
htsmsg_add_u32(m, "number", ch->number);
hts_settings_save(m, "epggrab/%s/channels/%s", mod->id, ch->id);
htsmsg_destroy(m);
@ -210,10 +208,8 @@ static void _epggrab_module_channel_load
egc->name = strdup(str);
if ((str = htsmsg_get_str(m, "icon")))
egc->icon = strdup(str);
if(!htsmsg_get_u32(m, "major", &u32))
egc->major = u32;
if(!htsmsg_get_u32(m, "minor", &u32))
egc->minor = u32;
if(!htsmsg_get_u32(m, "number", &u32))
egc->number = u32;
if ((a = htsmsg_get_list(m, "channels"))) {
HTSMSG_FOREACH(f, a) {
if ((str = htsmsg_field_get_str(f))) {
@ -252,7 +248,6 @@ epggrab_module_int_done( void *m )
{
epggrab_module_int_t *mod = m;
free((char *)mod->path);
mod->path = NULL;
}
epggrab_module_int_t *epggrab_module_int_create
@ -284,40 +279,21 @@ epggrab_module_int_t *epggrab_module_int_create
char *epggrab_module_grab_spawn ( void *m )
{
int rd = -1, outlen;
int outlen;
char *outbuf;
epggrab_module_int_t *mod = m;
char **argv = NULL;
/* Debug */
tvhlog(LOG_INFO, mod->id, "grab %s", mod->path);
/* Arguments */
if (spawn_parse_args(&argv, 64, mod->path, NULL)) {
tvhlog(LOG_ERR, mod->id, "unable to parse arguments");
/* Grab */
outlen = spawn_and_store_stdout(mod->path, NULL, &outbuf);
if ( outlen < 1 ) {
tvhlog(LOG_ERR, mod->id, "no output detected");
return NULL;
}
/* Grab */
outlen = spawn_and_give_stdout(argv[0], (char **)argv, NULL, &rd, NULL, 1);
if (outlen < 0)
goto error;
outlen = file_readall(rd, &outbuf);
if (outlen < 1)
goto error;
close(rd);
return outbuf;
error:
spawn_free_args(argv);
if (rd >= 0)
close(rd);
tvhlog(LOG_ERR, mod->id, "no output detected");
return NULL;
}
@ -405,7 +381,7 @@ epggrab_module_done_socket( void *m )
shutdown(sock, SHUT_RDWR);
close(sock);
if (mod->tid) {
pthread_kill(mod->tid, SIGQUIT);
pthread_kill(mod->tid, SIGTERM);
pthread_join(mod->tid, NULL);
}
mod->tid = 0;

View file

@ -395,11 +395,10 @@ static int _eit_desc_crid
* EIT Event
* ***********************************************************************/
static int _eit_process_event_one
static int _eit_process_event
( epggrab_module_t *mod, int tableid,
mpegts_service_t *svc, channel_t *ch,
const uint8_t *ptr, int len,
int local, int *resched, int *save )
mpegts_service_t *svc, const uint8_t *ptr, int len,
int *resched, int *save )
{
int save2 = 0;
int ret, dllen;
@ -410,12 +409,13 @@ static int _eit_process_event_one
epg_episode_t *ee;
epg_serieslink_t *es;
eit_event_t ev;
channel_t *ch = LIST_FIRST(&svc->s_channels)->csm_chn;
if ( len < 12 ) return -1;
/* Core fields */
eid = ptr[0] << 8 | ptr[1];
start = dvb_convert_date(&ptr[2], local);
start = dvb_convert_date(&ptr[2]);
stop = start + bcdtoint(ptr[7] & 0xff) * 3600 +
bcdtoint(ptr[8] & 0xff) * 60 +
bcdtoint(ptr[9] & 0xff);
@ -446,14 +446,13 @@ static int _eit_process_event_one
int r;
dtag = ptr[0];
dlen = ptr[1];
tvhtrace(mod->id, " dtag %02X dlen %d", dtag, dlen);
tvhlog_hexdump(mod->id, ptr+2, dlen);
dllen -= 2;
ptr += 2;
if (dllen < dlen) break;
tvhtrace(mod->id, " dtag %02X dlen %d", dtag, dlen);
tvhlog_hexdump(mod->id, ptr, dlen);
switch (dtag) {
case DVB_DESC_SHORT_EVENT:
r = _eit_desc_short_event(mod, ptr, dlen, &ev);
@ -532,8 +531,6 @@ static int _eit_process_event_one
*save |= epg_episode_set_genre(ee, ev.genre, mod);
if ( ev.parental )
*save |= epg_episode_set_age_rating(ee, ev.parental, mod);
if ( ev.summary )
*save |= epg_episode_set_subtitle2(ee, ev.summary, mod);
#if TODO_ADD_EXTRA
if ( ev.extra )
*save |= epg_episode_set_extra(ee, extra, mod);
@ -552,23 +549,6 @@ static int _eit_process_event_one
return ret;
}
static int _eit_process_event
( epggrab_module_t *mod, int tableid,
mpegts_service_t *svc, const uint8_t *ptr, int len,
int local, int *resched, int *save )
{
channel_service_mapping_t *csm;
int ret = 0;
if ( len < 12 ) return -1;
LIST_FOREACH(csm, &svc->s_channels, csm_svc_link)
ret = _eit_process_event_one(mod, tableid, svc, csm->csm_chn,
ptr, len, local, resched, save);
return ret;
}
static int
_eit_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
@ -583,7 +563,7 @@ _eit_callback
epggrab_ota_map_t *map = mt->mt_opaque;
epggrab_module_t *mod = (epggrab_module_t *)map->om_module;
epggrab_ota_mux_t *ota = NULL;
mpegts_psi_table_state_t *st;
mpegts_table_state_t *st;
/* Validate */
if(tableid < 0x4e || tableid > 0x6f || len < 11)
@ -602,8 +582,7 @@ _eit_callback
ota = epggrab_ota_register((epggrab_module_ota_t*)mod, NULL, mm);
/* Begin */
r = dvb_table_begin((mpegts_psi_table_t *)mt, ptr, len,
tableid, extraid, 11, &st, &sect, &last, &ver);
r = dvb_table_begin(mt, ptr, len, tableid, extraid, 11, &st, &sect, &last, &ver);
if (r != 1) return r;
if (st) {
uint32_t mask;
@ -664,7 +643,6 @@ _eit_callback
while (len) {
int r;
if ((r = _eit_process_event(mod, tableid, svc, ptr, len,
mm->mm_network->mn_localtime,
&resched, &save)) < 0)
break;
len -= r;
@ -676,7 +654,7 @@ _eit_callback
if (save) epg_updated();
done:
r = dvb_table_end((mpegts_psi_table_t *)mt, st, sect);
r = dvb_table_end(mt, st, sect);
if (ota && !r)
epggrab_ota_complete((epggrab_module_ota_t*)mod, ota);
@ -705,7 +683,7 @@ static int _eit_start
/* Freesat (3002/3003) */
if (!strcmp("uk_freesat", m->id)) {
mpegts_table_add(dm, 0, 0, dvb_bat_callback, NULL, "bat", MT_CRC, 3002, MPS_WEIGHT_EIT);
mpegts_table_add(dm, 0, 0, dvb_bat_callback, NULL, "bat", MT_CRC, 3002);
pid = 3003;
/* Viasat Baltic (0x39) */
@ -714,10 +692,10 @@ static int _eit_start
/* Standard (0x12) */
} else {
pid = DVB_EIT_PID;
pid = 0x12;
opts = MT_RECORD;
}
mpegts_table_add(dm, 0, 0, _eit_callback, map, m->id, MT_CRC | opts, pid, MPS_WEIGHT_EIT);
mpegts_table_add(dm, 0, 0, _eit_callback, map, m->id, MT_CRC | opts, pid);
// TODO: might want to limit recording to EITpf only
tvhlog(LOG_DEBUG, m->id, "installed table handlers");
return 0;

View file

@ -76,8 +76,6 @@ typedef struct opentv_module_t
int onid;
int tsid;
int sid;
int bouquetid;
int bouquet_auto;
int *channel;
int *title;
int *summary;
@ -214,9 +212,7 @@ static int _opentv_parse_event_record
time_t mjd )
{
uint8_t rtag = buf[0];
int rlen = buf[1];
if (rlen+2 > len)
return -1;
uint8_t rlen = buf[1];
if (rlen+2 <= len) {
switch (rtag) {
case 0xb5: // title
@ -228,13 +224,8 @@ static int _opentv_parse_event_record
ev->cat = buf[6];
if (prov->genre)
ev->cat = prov->genre->map[ev->cat];
if (!ev->title) {
if (!ev->title)
ev->title = _opentv_parse_string(prov, buf+9, rlen-7);
if (!strcmp(prov->dict->id, "skynz")) {
if ((strlen(ev->title) >= 6) && (ev->title[0] == '[') && (ev->title[1] == '[') && (ev->title[4] == ']') && (ev->title[5] == ']'))
memmove(ev->title,ev->title+6,strlen(ev->title)-5);
}
}
}
break;
case 0xb9: // summary
@ -263,21 +254,13 @@ static int _opentv_parse_event
opentv_event_t *ev )
{
int slen = (((int)buf[2] & 0xf) << 8) | buf[3];
int i = 4, r;
if (slen+4 > len) {
tvhtrace("opentv", "event len (%d) > table len (%d)", slen+4, len);
return -1;
}
int i = 4;
ev->eid = ((uint16_t)buf[0] << 8) | buf[1];
/* Process records */
while (i < slen+4) {
r = _opentv_parse_event_record(prov, ev, buf+i, len-i, mjd);
if (r < 0)
return -1;
i += r;
i += _opentv_parse_event_record(prov, ev, buf+i, len-i, mjd);
}
return slen+4;
}
@ -307,26 +290,35 @@ static void *_opentv_apply_pattern_list(char *buf, size_t size_buf, const char *
/* Parse an event section */
static int
opentv_parse_event_section_one
opentv_parse_event_section
( opentv_status_t *sta, int cid, int mjd,
channel_t *ch, const char *lang,
const uint8_t *buf, int len )
{
int i, r, save = 0;
int i, save = 0;
opentv_module_t *mod = sta->os_mod;
epggrab_module_t *src = (epggrab_module_t*)mod;
epggrab_channel_t *ec;
epg_broadcast_t *ebc;
epg_episode_t *ee;
epg_serieslink_t *es;
opentv_event_t ev;
const char *lang = NULL;
epggrab_channel_link_t *ecl;
/* Get language (bit of a hack) */
if (!strcmp(mod->dict->id, "skyit")) lang = "it";
else if (!strcmp(mod->dict->id, "skyeng")) lang = "eng";
/* Channel */
if (!(ec = _opentv_find_epggrab_channel(mod, cid, 0, NULL))) return 0;
if (!(ecl = LIST_FIRST(&ec->channels))) return 0;
/* Loop around event entries */
i = 7;
while (i < len) {
memset(&ev, 0, sizeof(opentv_event_t));
r = _opentv_parse_event(mod, sta, buf+i, len-i, cid, mjd, &ev);
if (r < 0) break;
i += r;
i += _opentv_parse_event(mod, sta, buf+i, len-i, cid, mjd,
&ev);
/*
* Broadcast
@ -334,13 +326,13 @@ opentv_parse_event_section_one
/* Find broadcast */
if (ev.start && ev.stop) {
ebc = epg_broadcast_find_by_time(ch, ev.start, ev.stop,
ebc = epg_broadcast_find_by_time(ecl->ecl_channel, ev.start, ev.stop,
ev.eid, 1, &save);
tvhdebug("opentv", "find by time start %"PRItime_t " stop "
"%"PRItime_t " eid %d = %p",
ev.start, ev.stop, ev.eid, ebc);
} else {
ebc = epg_broadcast_find_by_eid(ch, ev.eid);
ebc = epg_broadcast_find_by_eid(ecl->ecl_channel, ev.eid);
tvhdebug("opentv", "find by eid %d = %p", ev.eid, ebc);
}
if (!ebc)
@ -363,7 +355,7 @@ opentv_parse_event_section_one
if (ev.serieslink) {
char suri[257];
snprintf(suri, 256, "opentv://channel-%s/series-%d",
channel_get_uuid(ch), ev.serieslink);
channel_get_uuid(ecl->ecl_channel), ev.serieslink);
if ((es = epg_serieslink_find_by_uri(suri, 1, &save)))
save |= epg_broadcast_set_serieslink(ebc, es, src);
}
@ -432,32 +424,6 @@ done:
if (ev.desc) free(ev.desc);
}
return save;
}
static int
opentv_parse_event_section
( opentv_status_t *sta, int cid, int mjd,
const uint8_t *buf, int len )
{
opentv_module_t *mod = sta->os_mod;
epggrab_channel_t *ec;
epggrab_channel_link_t *ecl;
const char *lang = NULL;
int save = 0;
/* Get language (bit of a hack) */
if (!strcmp(mod->dict->id, "skyit")) lang = "it";
else if (!strcmp(mod->dict->id, "skyeng")) lang = "eng";
else if (!strcmp(mod->dict->id, "skynz")) lang = "eng";
/* Channel */
if (!(ec = _opentv_find_epggrab_channel(mod, cid, 0, NULL))) return 0;
/* Iterate all channels */
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link)
save |= opentv_parse_event_section_one(sta, cid, mjd, ecl->ecl_channel, lang, buf, len);
/* Update EPG */
if (save) epg_updated();
return 0;
@ -469,7 +435,7 @@ opentv_parse_event_section
static int
opentv_desc_channels
( mpegts_table_t *mt, mpegts_mux_t *mm, uint16_t nbid,
( mpegts_table_t *mt, mpegts_mux_t *mm,
const uint8_t dtag, const uint8_t *buf, int len )
{
opentv_status_t *sta = mt->mt_opaque;
@ -478,45 +444,23 @@ opentv_desc_channels
epggrab_channel_link_t *ecl;
mpegts_service_t *svc;
channel_t *ch;
int sid, cid, cnum, unk;
#if ENABLE_TRACE
int type;
#endif
int sid, cid, cnum;
int save = 0;
int i = 2;
while (i < len) {
sid = ((int)buf[i] << 8) | buf[i+1];
#if ENABLE_TRACE
type = buf[2];
#endif
cid = ((int)buf[i+3] << 8) | buf[i+4];
cnum = ((int)buf[i+5] << 8) | buf[i+6];
unk = ((int)buf[i+7] << 8) | buf[i+8];
tvhtrace(mt->mt_name, " sid %04X type %02X cid %04X cnum %d unk %04X", sid, type, cid, cnum, unk);
tvhtrace(mt->mt_name, " sid %04X cid %04X cnum %d", sid, cid, cnum);
cnum = cnum < 65535 ? cnum : 0;
/* Find the service */
svc = mpegts_service_find(mm, sid, 0, 0, NULL);
tvhtrace(mt->mt_name, " svc %p [%s]", svc, svc ? svc->s_nicename : NULL);
if (svc && svc->s_dvb_opentv_chnum != cnum &&
(!svc->s_dvb_opentv_id || svc->s_dvb_opentv_id == unk)) {
if (mod->bouquetid != nbid) {
if (mod->bouquet_auto) {
if (nbid < mod->bouquetid) {
tvhwarn(mt->mt_name, "bouquet id set to %d, report this!", nbid);
mod->bouquetid = nbid;
} else
goto skip_chnum;
} else
goto skip_chnum;
}
tvhtrace(mt->mt_name, " cnum changed (%i != %i)", cnum, (int)svc->s_dvb_opentv_chnum);
if (svc && svc->s_dvb_opentv_chnum != cnum) {
svc->s_dvb_opentv_chnum = cnum;
svc->s_dvb_opentv_id = unk;
service_request_save((service_t *)svc, 0);
}
skip_chnum:
if (svc && LIST_FIRST(&svc->s_channels)) {
ec =_opentv_find_epggrab_channel(mod, cid, 1, &save);
ecl = LIST_FIRST(&ec->channels);
@ -530,7 +474,7 @@ skip_chnum:
if (!ecl)
epggrab_channel_link(ec, ch);
save |= epggrab_channel_set_number(ec, cnum, 0);
save |= epggrab_channel_set_number(ec, cnum);
}
i += 9;
}
@ -544,7 +488,7 @@ opentv_table_callback
{
int r = 1, cid, mjd;
int sect, last, ver;
mpegts_psi_table_state_t *st;
mpegts_table_state_t *st;
opentv_status_t *sta = mt->mt_opaque;
opentv_module_t *mod = sta->os_mod;
epggrab_ota_mux_t *ota = sta->os_ota;
@ -558,8 +502,7 @@ opentv_table_callback
mjd = (mjd - 40587) * 86400;
/* Begin */
r = dvb_table_begin((mpegts_psi_table_t *)mt, buf, len,
tableid, (uint64_t)cid << 32 | mjd, 7,
r = dvb_table_begin(mt, buf, len, tableid, (uint64_t)cid << 32 | mjd, 7,
&st, &sect, &last, &ver);
if (r != 1) goto done;
@ -567,7 +510,7 @@ opentv_table_callback
r = opentv_parse_event_section(sta, cid, mjd, buf, len);
/* End */
r = dvb_table_end((mpegts_psi_table_t *)mt, st, sect);
r = dvb_table_end(mt, st, sect);
/* Complete */
done:
@ -590,8 +533,7 @@ done:
mt2 = mpegts_table_add(mt->mt_mux,
OPENTV_SUMMARY_BASE, OPENTV_TABLE_MASK,
opentv_table_callback, sta,
mod->id, MT_CRC, *t++,
MPS_WEIGHT_EIT);
mod->id, MT_CRC, *t++);
if (mt2) {
sta->os_refcount++;
mt2->mt_destroy = opentv_status_destroy;
@ -639,8 +581,7 @@ opentv_bat_callback
mt2 = mpegts_table_add(mt->mt_mux,
OPENTV_TITLE_BASE, OPENTV_TABLE_MASK,
opentv_table_callback, mt->mt_opaque,
mod->id, MT_CRC, *t++,
MPS_WEIGHT_EIT);
mod->id, MT_CRC, *t++);
if (mt2) {
if (!mt2->mt_destroy) {
sta->os_refcount++;
@ -690,8 +631,7 @@ static int _opentv_start
}
mt = mpegts_table_add(mm, DVB_BAT_BASE, DVB_BAT_MASK,
opentv_bat_callback, sta,
m->id, MT_CRC, *t++,
MPS_WEIGHT_EIT);
m->id, MT_CRC, *t++);
if (mt) {
mt->mt_mux_cb = bat_desc;
if (!mt->mt_destroy) {
@ -737,15 +677,12 @@ static void _opentv_compile_pattern_list ( opentv_pattern_list_t *list, htsmsg_t
{
opentv_pattern_t *pattern;
htsmsg_field_t *f;
const char *s;
TAILQ_INIT(list);
if (!l) return;
HTSMSG_FOREACH(f, l) {
s = htsmsg_field_get_str(f);
if (s == NULL) continue;
pattern = calloc(1, sizeof(opentv_pattern_t));
pattern->text = strdup(s);
pattern->text = strdup(htsmsg_field_get_str(f));
if (regcomp(&pattern->compiled, pattern->text, REG_EXTENDED)) {
tvhlog(LOG_WARNING, "opentv", "error compiling pattern \"%s\"", pattern->text);
free(pattern->text);
@ -878,7 +815,7 @@ static int _opentv_prov_load_one ( const char *id, htsmsg_t *m )
{
char ibuf[100], nbuf[1000];
htsmsg_t *cl, *tl, *sl;
uint32_t tsid, sid, onid, bouquetid;
uint32_t tsid, sid, onid;
const char *str, *name;
opentv_dict_t *dict;
opentv_genre_t *genre;
@ -900,7 +837,6 @@ static int _opentv_prov_load_one ( const char *id, htsmsg_t *m )
if (htsmsg_get_u32(m, "nid", &onid)) return -1;
if (htsmsg_get_u32(m, "tsid", &tsid)) return -1;
if (htsmsg_get_u32(m, "sid", &sid)) return -1;
if (htsmsg_get_u32(m, "bouquetid", &bouquetid)) return -1;
/* Genre map (optional) */
str = htsmsg_get_str(m, "genre");
@ -925,8 +861,6 @@ static int _opentv_prov_load_one ( const char *id, htsmsg_t *m )
mod->onid = onid;
mod->tsid = tsid;
mod->sid = sid;
mod->bouquetid = bouquetid;
mod->bouquet_auto = bouquetid == 0;
mod->channel = _pid_list_to_array(cl);
mod->title = _pid_list_to_array(tl);
mod->summary = _pid_list_to_array(sl);

View file

@ -94,7 +94,7 @@ static int _pyepg_parse_channel
if ((str = htsmsg_xml_get_cdata_str(tags, "image")))
save |= epggrab_channel_set_icon(ch, str);
if ((!htsmsg_xml_get_cdata_u32(tags, "number", &u32)))
save |= epggrab_channel_set_number(ch, u32, 0);
save |= epggrab_channel_set_number(ch, u32);
/* Update */
if (save) {

26
src/epggrab/module/xmltv.c Normal file → Executable file
View file

@ -32,7 +32,6 @@
#include "tvheadend.h"
#include "channels.h"
#include "spawn.h"
#include "file.h"
#include "htsstr.h"
#include "lang_str.h"
@ -676,17 +675,14 @@ static int _xmltv_parse
static void _xmltv_load_grabbers ( void )
{
int outlen = -1, rd = -1;
int outlen;
size_t i, p, n;
char *outbuf;
char name[1000];
char *tmp, *tmp2 = NULL, *path;
/* Load data */
if (spawn_and_give_stdout(XMLTV_FIND, NULL, NULL, &rd, NULL, 1) >= 0)
outlen = file_readall(rd, &outbuf);
if (rd >= 0)
close(rd);
outlen = spawn_and_store_stdout(XMLTV_FIND, NULL, &outbuf);
/* Process */
if ( outlen > 0 ) {
@ -696,13 +692,8 @@ static void _xmltv_load_grabbers ( void )
outbuf[i] = '\0';
sprintf(name, "XMLTV: %s", &outbuf[n]);
epggrab_module_int_create(NULL, &outbuf[p], name, 3, &outbuf[p],
NULL, _xmltv_parse, NULL, NULL);
NULL, _xmltv_parse, NULL, NULL);
p = n = i + 1;
} else if ( outbuf[i] == '\\') {
memmove(outbuf, outbuf + 1, strlen(outbuf));
if (outbuf[i])
i++;
continue;
} else if ( outbuf[i] == '|' ) {
outbuf[i] = '\0';
n = i + 1;
@ -715,9 +706,10 @@ static void _xmltv_load_grabbers ( void )
} else if ((tmp = getenv("PATH"))) {
tvhdebug("epggrab", "using internal grab search");
char bin[256];
char desc[] = "--description";
char *argv[] = {
NULL,
(char *)"--description",
desc,
NULL
};
path = strdup(tmp);
@ -734,18 +726,12 @@ static void _xmltv_load_grabbers ( void )
if (stat(bin, &st)) continue;
if (!(st.st_mode & S_IEXEC)) continue;
if (!S_ISREG(st.st_mode)) continue;
rd = -1;
if (spawn_and_give_stdout(bin, argv, NULL, &rd, NULL, 1) >= 0 &&
(outlen = file_readall(rd, &outbuf)) > 0) {
close(rd);
if ((outlen = spawn_and_store_stdout(bin, argv, &outbuf)) > 0) {
if (outbuf[outlen-1] == '\n') outbuf[outlen-1] = '\0';
snprintf(name, sizeof(name), "XMLTV: %s", outbuf);
epggrab_module_int_create(NULL, bin, name, 3, bin,
NULL, _xmltv_parse, NULL, NULL);
free(outbuf);
} else {
if (rd >= 0)
close(rd);
}
}
closedir(dir);

View file

@ -195,16 +195,11 @@ epggrab_ota_done ( epggrab_ota_mux_t *om, int reason )
if (!om->om_done && om->om_requeue) {
TAILQ_INSERT_HEAD(&epggrab_ota_pending, om, om_q_link);
om->om_q_type = EPGGRAB_OTA_MUX_PENDING;
} else {
om->om_requeue = 0;
}
} else if (reason == EPGGRAB_OTA_DONE_TIMEOUT) {
om->om_requeue = 0;
LIST_FOREACH(map, &om->om_modules, om_link)
if (!map->om_complete)
tvhlog(LOG_WARNING, "epggrab", "%s - data completion timeout for %s", map->om_module->name, name);
} else {
om->om_requeue = 0;
}
/* Remove subscriber */
@ -295,18 +290,14 @@ epggrab_mux_start ( mpegts_mux_t *mm, void *p )
}
static void
epggrab_mux_stop ( mpegts_mux_t *mm, void *p, int reason )
epggrab_mux_stop ( mpegts_mux_t *mm, void *p )
{
epggrab_ota_mux_t *ota;
const char *uuid = idnode_uuid_as_str(&mm->mm_id);
int done = EPGGRAB_OTA_DONE_STOLEN;
if (reason == SM_CODE_NO_INPUT)
done = EPGGRAB_OTA_DONE_NO_DATA;
tvhtrace("epggrab", "mux %p (%s) stop", mm, uuid);
TAILQ_FOREACH(ota, &epggrab_ota_active, om_q_link)
if (!strcmp(ota->om_mux_uuid, uuid)) {
epggrab_ota_done(ota, done);
epggrab_ota_done(ota, EPGGRAB_OTA_DONE_STOLEN);
break;
}
}
@ -438,8 +429,6 @@ epggrab_ota_data_timeout_cb ( void *p )
epggrab_ota_done(om, EPGGRAB_OTA_DONE_NO_DATA);
/* Not completed, but no data - wait for a manual mux tuning */
epggrab_ota_complete_mark(om, 1);
} else {
tvhtrace("epggrab", "data timeout check succeed");
}
}
@ -507,13 +496,10 @@ next_one:
net->failed = 0;
}
epg_flag = MM_EPG_DISABLE;
if (mm->mm_is_enabled(mm)) {
epg_flag = mm->mm_is_epg(mm);
if (epg_flag > MM_EPG_LAST)
epg_flag = MM_EPG_ENABLE;
modname = epg_flag >= 0 ? modnames[epg_flag] : NULL;
}
epg_flag = mm->mm_is_epg(mm);
if (epg_flag > MM_EPG_LAST)
epg_flag = MM_EPG_ENABLE;
modname = epg_flag >= 0 ? modnames[epg_flag] : NULL;
if (epg_flag < 0 || epg_flag == MM_EPG_DISABLE) {
#if ENABLE_TRACE
@ -546,11 +532,8 @@ next_one:
/* Subscribe to the mux */
om->om_requeue = 1;
if ((r = mpegts_mux_subscribe(mm, NULL, "epggrab",
SUBSCRIPTION_PRIO_EPG,
SUBSCRIPTION_EPG |
SUBSCRIPTION_ONESHOT |
SUBSCRIPTION_TABLES))) {
if ((r = mpegts_mux_subscribe(mm, "epggrab", SUBSCRIPTION_PRIO_EPG,
SUBSCRIPTION_EPG))) {
TAILQ_INSERT_TAIL(&epggrab_ota_pending, om, om_q_link);
om->om_q_type = EPGGRAB_OTA_MUX_PENDING;
if (r == SM_CODE_NO_FREE_ADAPTER)
@ -558,7 +541,6 @@ next_one:
if (first == NULL)
first = om;
} else {
tvhtrace("epggrab", "mux %p started", mm);
kick = 0;
/* note: it is possible that the mux_start listener is not called */
/* for reshared mux subscriptions, so call it (maybe second time) here.. */
@ -755,7 +737,7 @@ epggrab_ota_load_one
free(ota);
return;
}
ota->om_complete = htsmsg_get_u32_or_default(c, "complete", 0) != 0;
htsmsg_get_u32(c, "complete", (uint32_t *)&ota->om_complete);
if (!(l = htsmsg_get_list(c, "modules"))) return;
HTSMSG_FOREACH(f, l) {
@ -818,16 +800,6 @@ epggrab_ota_init ( void )
}
}
void
epggrab_ota_trigger ( int secs )
{
/* notify another system layers, that we will do EPG OTA */
secs = MIN(1, MAX(secs, 7*24*3600));
dbus_emit_signal_s64("/epggrab/ota", "next", time(NULL) + secs);
epggrab_ota_pending_flag = 1;
epggrab_ota_kick(secs);
}
void
epggrab_ota_post ( void )
{
@ -835,7 +807,10 @@ epggrab_ota_post ( void )
/* Init timer (call after full init - wait for network tuners) */
if (epggrab_ota_initial) {
epggrab_ota_trigger(15);
/* notify another system layers, that we will do EPG OTA */
dbus_emit_signal_s64("/epggrab/ota", "next", time(NULL) + 15);
epggrab_ota_pending_flag = 1;
epggrab_ota_kick(15);
t = time(NULL);
}

View file

@ -85,7 +85,7 @@ esfilter_txt2class(const char *s)
static struct strtab esfilteractiontab[] = {
{ "NONE", ESFA_NONE },
{ "USE", ESFA_USE },
{ "ONE_TIME", ESFA_ONE_TIME },
{ "ONCE", ESFA_ONCE },
{ "EXCLUSIVE", ESFA_EXCLUSIVE },
{ "EMPTY", ESFA_EMPTY },
{ "IGNORE", ESFA_IGNORE }

View file

@ -73,7 +73,7 @@ extern struct esfilter_entry_queue esfilters[];
typedef enum {
ESFA_NONE = 0,
ESFA_USE, /* use this stream */
ESFA_ONE_TIME, /* use this stream once per language */
ESFA_ONCE, /* use this stream once per language */
ESFA_EXCLUSIVE, /* use this stream exclusively */
ESFA_EMPTY, /* use this stream when no streams were added */
ESFA_IGNORE,

View file

@ -16,46 +16,65 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tvheadend.h"
#define MAX_RDBUF_SIZE 8192
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "queue.h"
#include "file.h"
#define MAX_RDBUF_SIZE 8192
typedef struct file_read_buf {
TAILQ_ENTRY(file_read_buf) link;
int size;
char buf[MAX_RDBUF_SIZE];
} file_read_buf_t;
TAILQ_HEAD(file_read_buf_queue, file_read_buf);
size_t file_readall ( int fd, char **outp )
{
size_t outsize = 0, totalsize = 0;
char *outbuf = NULL, *n;
int r;
int r, totalsize = 0;
struct file_read_buf_queue bufs;
file_read_buf_t *b = NULL;
char *outbuf;
while (1) {
if(totalsize == outsize) {
n = realloc(outbuf, outsize += MAX_RDBUF_SIZE);
if (!n) {
free(outbuf);
return 0;
}
outbuf = n;
TAILQ_INIT(&bufs);
while(1) {
if(b == NULL) {
b = malloc(sizeof(file_read_buf_t));
b->size = 0;
TAILQ_INSERT_TAIL(&bufs, b, link);
}
r = read(fd, outbuf + totalsize, outsize - totalsize);
if(r < 1) {
if (ERRNO_AGAIN(errno))
continue;
r = read(fd, b->buf + b->size, MAX_RDBUF_SIZE - b->size);
if(r < 1)
break;
}
b->size += r;
totalsize += r;
if(b->size == MAX_RDBUF_SIZE)
b = NULL;
}
*outp = outbuf;
if (totalsize == outsize) {
n = realloc(outbuf, outsize += 1);
if (!n) {
free(outbuf);
return 0;
}
outbuf = n;
}
outbuf[totalsize] = 0;
close(fd);
if(totalsize == 0) {
free(b);
*outp = NULL;
return 0;
}
outbuf = malloc(totalsize + 1);
r = 0;
while((b = TAILQ_FIRST(&bufs)) != NULL) {
memcpy(outbuf + r, b->buf, b->size);
r+= b->size;
TAILQ_REMOVE(&bufs, b, link);
free(b);
}
assert(r == totalsize);
*outp = outbuf;
outbuf[totalsize] = 0;
return totalsize;
}

View file

@ -24,11 +24,7 @@
#include <string.h>
#include <stdio.h>
#if ENABLE_ZLIB
#define ZLIB_CONST 1
#include <zlib.h>
#ifndef z_const
#define z_const
#endif
#endif
#include <sys/types.h>
#include <sys/stat.h>
@ -82,16 +78,18 @@ static uint8_t *_fb_inflate ( const uint8_t *data, size_t size, size_t orig )
{
int err;
z_stream zstr;
uint8_t *bufout;
uint8_t *bufin, *bufout;
/* Setup buffers */
bufin = malloc(size);
bufout = malloc(orig);
memcpy(bufin, data, size);
/* Setup zlib */
memset(&zstr, 0, sizeof(zstr));
inflateInit2(&zstr, 31);
zstr.avail_in = size;
zstr.next_in = (z_const uint8_t *)data;
zstr.next_in = bufin;
zstr.avail_out = orig;
zstr.next_out = bufout;
@ -101,6 +99,7 @@ static uint8_t *_fb_inflate ( const uint8_t *data, size_t size, size_t orig )
free(bufout);
bufout = NULL;
}
free(bufin);
inflateEnd(&zstr);
return bufout;
@ -112,16 +111,18 @@ static uint8_t *_fb_deflate ( const uint8_t *data, size_t orig, size_t *size )
{
int err;
z_stream zstr;
uint8_t *bufout;
uint8_t *bufin, *bufout;
/* Setup buffers */
bufin = malloc(orig);
bufout = malloc(orig);
memcpy(bufin, data, orig);
/* Setup zlib */
memset(&zstr, 0, sizeof(zstr));
err = deflateInit2(&zstr, 9, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY);
zstr.avail_in = orig;
zstr.next_in = (z_const uint8_t *)data;
zstr.next_in = bufin;
zstr.avail_out = orig;
zstr.next_out = bufout;
@ -146,6 +147,7 @@ static uint8_t *_fb_deflate ( const uint8_t *data, size_t orig, size_t *size )
}
break;
}
free(bufin);
deflateEnd(&zstr);
return bufout;
@ -411,7 +413,7 @@ fb_file *fb_open2
} else {
char path[512];
snprintf(path, sizeof(path), "%s/%s", dir->d.root, name);
FILE *fp = tvh_fopen(path, "rb");
FILE *fp = fopen(path, "rb");
if (fp) {
struct stat st;
stat(path, &st);

View file

@ -105,7 +105,7 @@ void
fsmonitor_init ( void )
{
/* Intialise inotify */
fsmonitor_fd = inotify_init1(IN_CLOEXEC);
fsmonitor_fd = inotify_init();
tvhthread_create0(&fsmonitor_tid, NULL, fsmonitor_thread, NULL, "fsmonitor");
}

View file

@ -829,14 +829,10 @@ htsmsg_xml_deserialize(char *src, char *errbuf, size_t errbufsize)
char *src0 = src;
int i;
memset(&xp, 0, sizeof(xp));
xp.xp_errmsg[0] = 0;
xp.xp_encoding = XML_ENCODING_UTF8;
LIST_INIT(&xp.xp_namespaces);
/* check for UTF-8 BOM */
if(src[0] == 0xef && src[1] == 0xbb && src[2] == 0xbf)
memmove(src, src + 3, strlen(src) - 2);
if((src = htsmsg_parse_prolog(&xp, src)) == NULL)
goto err;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*
* tvheadend, HTSP interface
* Copyright (C) 2007 Andreas <EFBFBD>man
* 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
@ -44,10 +44,6 @@ void htsp_autorec_entry_add(dvr_autorec_entry_t *dae);
void htsp_autorec_entry_update(dvr_autorec_entry_t *dae);
void htsp_autorec_entry_delete(dvr_autorec_entry_t *dae);
void htsp_timerec_entry_add(dvr_timerec_entry_t *dte);
void htsp_timerec_entry_update(dvr_timerec_entry_t *dte);
void htsp_timerec_entry_delete(dvr_timerec_entry_t *dte);
void htsp_event_add(epg_broadcast_t *ebc);
void htsp_event_update(epg_broadcast_t *ebc);
void htsp_event_delete(epg_broadcast_t *ebc);

View file

@ -37,12 +37,11 @@
#include "notify.h"
#include "channels.h"
void *http_server;
static void *http_server;
static http_path_list_t http_paths;
static LIST_HEAD(, http_path) http_paths;
static struct strtab HTTP_cmdtab[] = {
{ "NONE", HTTP_CMD_NONE },
{ "GET", HTTP_CMD_GET },
{ "HEAD", HTTP_CMD_HEAD },
{ "POST", HTTP_CMD_POST },
@ -62,6 +61,8 @@ static struct strtab HTTP_versiontab[] = {
{ "RTSP/1.0", RTSP_VERSION_1_0 },
};
static void http_parse_get_args(http_connection_t *hc, char *args);
/**
*
*/
@ -112,7 +113,7 @@ http_resolve(http_connection_t *hc, char **remainp, char **argsp)
while (1) {
LIST_FOREACH(hp, hc->hc_paths, hp_link) {
LIST_FOREACH(hp, &http_paths, hp_link) {
if(!strncmp(path, hp->hp_path, hp->hp_len)) {
if(path[hp->hp_len] == 0 ||
path[hp->hp_len] == '/' ||
@ -184,16 +185,12 @@ http_rc2str(int code)
switch(code) {
case HTTP_STATUS_OK: return "OK";
case HTTP_STATUS_PARTIAL_CONTENT: return "Partial Content";
case HTTP_STATUS_FOUND: return "Found";
case HTTP_STATUS_BAD_REQUEST: return "Bad Request";
case HTTP_STATUS_NOT_FOUND: return "Not found";
case HTTP_STATUS_UNAUTHORIZED: return "Unauthorized";
case HTTP_STATUS_NOT_FOUND: return "Not Found";
case HTTP_STATUS_UNSUPPORTED: return "Unsupported Media Type";
case HTTP_STATUS_BANDWIDTH: return "Not Enough Bandwidth";
case HTTP_STATUS_BAD_SESSION: return "Session Not Found";
case HTTP_STATUS_HTTP_VERSION: return "HTTP/RTSP Version Not Supported";
case HTTP_STATUS_BAD_REQUEST: return "Bad request";
case HTTP_STATUS_FOUND: return "Found";
default:
return "Unknown Code";
return "Unknown returncode";
break;
}
}
@ -215,26 +212,22 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
int64_t contentlen,
const char *encoding, const char *location,
int maxage, const char *range,
const char *disposition,
http_arg_list_t *args)
const char *disposition)
{
struct tm tm0, *tm;
htsbuf_queue_t hdrs;
http_arg_t *ra;
time_t t;
int sess = 0;
htsbuf_queue_init(&hdrs, 0);
htsbuf_qprintf(&hdrs, "%s %d %s\r\n",
http_ver2str(hc->hc_version), rc, http_rc2str(rc));
val2str(hc->hc_version, HTTP_versiontab),
rc, http_rc2str(rc));
if (hc->hc_version != RTSP_VERSION_1_0)
htsbuf_qprintf(&hdrs, "Server: HTS/tvheadend\r\n");
htsbuf_qprintf(&hdrs, "Server: HTS/tvheadend\r\n");
if(maxage == 0) {
if (hc->hc_version != RTSP_VERSION_1_0)
htsbuf_qprintf(&hdrs, "Cache-Control: no-cache\r\n");
htsbuf_qprintf(&hdrs, "Cache-Control: no-cache\r\n");
} else {
time(&t);
@ -265,9 +258,8 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
htsbuf_qprintf(&hdrs, "Set-Cookie: logout=0; Path=\"/logout'\"; expires=Thu, 01 Jan 1970 00:00:00 GMT\r\n");
}
if (hc->hc_version != RTSP_VERSION_1_0)
htsbuf_qprintf(&hdrs, "Connection: %s\r\n",
hc->hc_keep_alive ? "Keep-Alive" : "Close");
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);
@ -289,22 +281,6 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
if(disposition != NULL)
htsbuf_qprintf(&hdrs, "Content-Disposition: %s\r\n", disposition);
if(hc->hc_cseq) {
htsbuf_qprintf(&hdrs, "CSeq: %"PRIu64"\r\n", hc->hc_cseq);
if (++hc->hc_cseq == 0)
hc->hc_cseq = 1;
}
if (args) {
TAILQ_FOREACH(ra, args, link) {
if (strcmp(ra->key, "Session") == 0)
sess = 1;
htsbuf_qprintf(&hdrs, "%s: %s\r\n", ra->key, ra->val);
}
}
if(hc->hc_session && !sess)
htsbuf_qprintf(&hdrs, "Session: %s\r\n", hc->hc_session);
htsbuf_qprintf(&hdrs, "\r\n");
tcp_write_queue(hc->hc_fd, &hdrs);
@ -320,7 +296,7 @@ http_send_reply(http_connection_t *hc, int rc, const char *content,
const char *encoding, const char *location, int maxage)
{
http_send_header(hc, rc, content, hc->hc_reply.hq_size,
encoding, location, maxage, 0, NULL, NULL);
encoding, location, maxage, 0, NULL);
if(hc->hc_no_output)
return;
@ -336,34 +312,32 @@ void
http_error(http_connection_t *hc, int error)
{
const char *errtxt = http_rc2str(error);
char addrstr[50];
if (!http_server) return;
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50);
if (error != HTTP_STATUS_FOUND && error != HTTP_STATUS_MOVED)
tvhlog(error < 400 ? LOG_INFO : LOG_ERR, "http", "%s: %s %s %s -- %d",
hc->hc_peer_ipstr, http_ver2str(hc->hc_version),
http_cmd2str(hc->hc_cmd), hc->hc_url, error);
tvhlog(error < 400 ? LOG_INFO : LOG_ERR, "HTTP", "%s: %s -- %d",
addrstr, hc->hc_url, error);
if (hc->hc_version != RTSP_VERSION_1_0) {
htsbuf_queue_flush(&hc->hc_reply);
htsbuf_queue_flush(&hc->hc_reply);
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",
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",
error, errtxt, error, errtxt);
if (error == HTTP_STATUS_UNAUTHORIZED)
htsbuf_qprintf(&hc->hc_reply, "<P><A HREF=\"/\">Default Login</A></P>");
if (error == HTTP_STATUS_UNAUTHORIZED)
htsbuf_qprintf(&hc->hc_reply, "<P><A HREF=\"/\">Default Login</A></P>");
htsbuf_qprintf(&hc->hc_reply, "</BODY></HTML>\r\n");
htsbuf_qprintf(&hc->hc_reply, "</BODY></HTML>\r\n");
http_send_reply(hc, error, "text/html", NULL, NULL, 0);
} else {
http_send_reply(hc, error, NULL, NULL, NULL, 0);
}
http_send_reply(hc, error, "text/html", NULL, NULL, 0);
}
@ -445,8 +419,10 @@ http_access_verify_ticket(http_connection_t *hc)
hc->hc_access = access_ticket_verify2(ticket_id, hc->hc_url);
if (hc->hc_access == NULL)
return;
tvhlog(LOG_INFO, "http", "%s: using ticket %s for %s",
hc->hc_peer_ipstr, ticket_id, hc->hc_url);
char addrstr[50];
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50);
tvhlog(LOG_INFO, "HTTP", "%s: using ticket %s for %s",
addrstr, ticket_id, hc->hc_url);
}
/**
@ -491,7 +467,7 @@ http_access_verify_channel(http_connection_t *hc, int mask,
if (access_verify2(hc->hc_access, mask))
return -1;
if (channel_access(ch, hc->hc_access, 0))
if (channel_access(ch, hc->hc_access, hc->hc_username))
res = 0;
return res;
}
@ -522,40 +498,7 @@ http_exec(http_connection_t *hc, http_path_t *hp, char *remain)
return 0;
}
/*
* Dump request
*/
#if ENABLE_TRACE
static void
dump_request(http_connection_t *hc)
{
char buf[2048] = "";
http_arg_t *ra;
int first, ptr = 0;
first = 1;
TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
tvh_strlcatf(buf, sizeof(buf), ptr, first ? "?%s=%s" : "&%s=%s", ra->key, ra->val);
first = 0;
}
first = 1;
TAILQ_FOREACH(ra, &hc->hc_args, link) {
tvh_strlcatf(buf, sizeof(buf), ptr, first ? "{{%s=%s" : ",%s=%s", ra->key, ra->val);
first = 0;
}
if (!first)
tvh_strlcatf(buf, sizeof(buf), ptr, "}}");
tvhtrace("http", "%s %s %s%s", http_ver2str(hc->hc_version),
http_cmd2str(hc->hc_cmd), hc->hc_url, buf);
}
#else
static inline void
dump_request(http_connection_t *hc)
{
}
#endif
/**
* HTTP GET
@ -567,8 +510,6 @@ http_cmd_get(http_connection_t *hc)
char *remain;
char *args;
dump_request(hc);
hp = http_resolve(hc, &remain, &args);
if(hp == NULL) {
http_error(hc, HTTP_STATUS_NOT_FOUND);
@ -619,7 +560,7 @@ http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
if(tcp_read_data(hc->hc_fd, hc->hc_post_data, hc->hc_post_len, spill) < 0)
return -1;
/* Parse content-type */
/* Parse content-type */
v = http_arg_get(&hc->hc_args, "Content-Type");
if(v != NULL) {
char *argv[2];
@ -633,8 +574,6 @@ http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
http_parse_get_args(hc, hc->hc_post_data);
}
dump_request(hc);
hp = http_resolve(hc, &remain, &args);
if(hp == NULL) {
http_error(hc, HTTP_STATUS_NOT_FOUND);
@ -664,6 +603,39 @@ http_process_request(http_connection_t *hc, htsbuf_queue_t *spill)
}
}
/*
*
*/
#if ENABLE_TRACE
static void
dump_request(http_connection_t *hc)
{
char buf[2048] = "";
http_arg_t *ra;
int first;
first = 1;
TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), first ? "?%s=%s" : "&%s=%s", ra->key, ra->val);
first = 0;
}
first = 1;
TAILQ_FOREACH(ra, &hc->hc_args, link) {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), first ? "{{%s=%s" : ",%s=%s", ra->key, ra->val);
first = 0;
}
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "}}");
tvhtrace("http", "%s%s", hc->hc_url, buf);
}
#else
static inline void
dump_request(http_connection_t *hc)
{
}
#endif
/**
* Process a request, extract info from headers, dispatch command and
* clean up
@ -673,42 +645,18 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill)
{
char *v, *argv[2];
int n, rval = -1;
char authbuf[150];
uint8_t authbuf[150];
dump_request(hc);
hc->hc_url_orig = tvh_strdupa(hc->hc_url);
v = http_arg_get(&hc->hc_args, "x-forwarded-for");
if (v)
tcp_get_sockaddr((struct sockaddr*)hc->hc_peer, v);
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, authbuf, sizeof(authbuf));
hc->hc_peer_ipstr = tvh_strdupa(authbuf);
hc->hc_representative = hc->hc_peer_ipstr;
hc->hc_username = NULL;
hc->hc_password = NULL;
hc->hc_session = NULL;
/* Set keep-alive status */
v = http_arg_get(&hc->hc_args, "connection");
switch(hc->hc_version) {
case RTSP_VERSION_1_0:
hc->hc_keep_alive = 1;
/* Extract CSeq */
if((v = http_arg_get(&hc->hc_args, "CSeq")) != NULL)
hc->hc_cseq = strtoll(v, NULL, 10);
else
hc->hc_cseq = 0;
free(hc->hc_session);
if ((v = http_arg_get(&hc->hc_args, "Session")) != NULL)
hc->hc_session = tvh_strdupa(v);
else
hc->hc_session = NULL;
if(hc->hc_cseq == 0) {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return -1;
}
break;
case HTTP_VERSION_1_0:
@ -725,38 +673,36 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill)
/* Extract authorization */
if((v = http_arg_get(&hc->hc_args, "Authorization")) != NULL) {
if((n = http_tokenize(v, argv, 2, -1)) == 2) {
n = base64_decode((uint8_t *)authbuf, argv[1], sizeof(authbuf) - 1);
n = base64_decode(authbuf, argv[1], sizeof(authbuf) - 1);
if (n < 0)
n = 0;
authbuf[n] = 0;
if((n = http_tokenize(authbuf, argv, 2, ':')) == 2) {
hc->hc_username = tvh_strdupa(argv[0]);
hc->hc_password = tvh_strdupa(argv[1]);
if((n = http_tokenize((char *)authbuf, argv, 2, ':')) == 2) {
hc->hc_username = strdup(argv[0]);
hc->hc_password = strdup(argv[1]);
// No way to actually track this
}
}
}
if (hc->hc_username)
hc->hc_representative = hc->hc_username;
if(hc->hc_username != NULL) {
hc->hc_representative = strdup(hc->hc_username);
} else {
hc->hc_representative = malloc(50);
/* Not threadsafe ? */
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, hc->hc_representative, 50);
}
switch(hc->hc_version) {
case RTSP_VERSION_1_0:
dump_request(hc);
if (hc->hc_cseq)
rval = hc->hc_process(hc, spill);
else
http_error(hc, HTTP_STATUS_HTTP_VERSION);
break;
case HTTP_VERSION_1_0:
case HTTP_VERSION_1_1:
if (!hc->hc_cseq)
rval = hc->hc_process(hc, spill);
else
http_error(hc, HTTP_STATUS_HTTP_VERSION);
rval = http_process_request(hc, spill);
break;
}
free(hc->hc_representative);
return rval;
}
@ -791,28 +737,6 @@ http_arg_get(struct http_arg_list *list, const char *name)
return NULL;
}
/**
* Find an argument associated with a connection and remove it
*/
char *
http_arg_get_remove(struct http_arg_list *list, const char *name)
{
static char __thread buf[128];
http_arg_t *ra;
TAILQ_FOREACH(ra, list, link)
if(!strcasecmp(ra->key, name)) {
TAILQ_REMOVE(list, ra, link);
strncpy(buf, ra->val, sizeof(buf)-1);
buf[sizeof(buf)-1] = '\0';
free(ra->key);
free(ra->val);
free(ra);
return buf;
}
buf[0] = '\0';
return buf;
}
/**
* Set an argument associated with a connection
@ -947,13 +871,11 @@ http_deescape(char *s)
/**
* Parse arguments of a HTTP GET url, not perfect, but works for us
*/
void
static void
http_parse_get_args(http_connection_t *hc, char *args)
{
char *k, *v;
if (args && *args == '&')
args++;
while(args) {
k = args;
if((args = strchr(args, '=')) == NULL)
@ -975,16 +897,12 @@ http_parse_get_args(http_connection_t *hc, char *args)
/**
*
*/
void
http_serve_requests(http_connection_t *hc)
static void
http_serve_requests(http_connection_t *hc, htsbuf_queue_t *spill)
{
htsbuf_queue_t spill;
char *argv[3], *c, *cmdline = NULL, *hdrline = NULL;
int n, r;
int n;
http_arg_init(&hc->hc_args);
http_arg_init(&hc->hc_req_args);
htsbuf_queue_init(&spill, 0);
htsbuf_queue_init(&hc->hc_reply, 0);
do {
@ -992,7 +910,7 @@ http_serve_requests(http_connection_t *hc)
if (cmdline) free(cmdline);
if ((cmdline = tcp_read_line(hc->hc_fd, &spill)) == NULL)
if ((cmdline = tcp_read_line(hc->hc_fd, spill)) == NULL)
goto error;
if((n = http_tokenize(cmdline, argv, 3, -1)) != 3)
@ -1009,28 +927,24 @@ http_serve_requests(http_connection_t *hc)
while(1) {
if (hdrline) free(hdrline);
if ((hdrline = tcp_read_line(hc->hc_fd, &spill)) == NULL)
if ((hdrline = tcp_read_line(hc->hc_fd, spill)) == NULL)
goto error;
if(!*hdrline)
break; /* header complete */
break; /* header complete */
if((n = http_tokenize(hdrline, argv, 2, -1)) < 2) {
if ((c = strchr(hdrline, ':')) != NULL) {
*c = '\0';
argv[0] = hdrline;
argv[1] = c + 1;
} else {
continue;
}
} else if((c = strrchr(argv[0], ':')) == NULL)
goto error;
if((n = http_tokenize(hdrline, argv, 2, -1)) < 2)
continue;
if((c = strrchr(argv[0], ':')) == NULL)
goto error;
*c = 0;
http_arg_set(&hc->hc_args, argv[0], argv[1]);
}
r = process_request(hc, &spill);
if(process_request(hc, spill))
break;
free(hc->hc_post_data);
hc->hc_post_data = NULL;
@ -1040,8 +954,11 @@ http_serve_requests(http_connection_t *hc)
htsbuf_queue_flush(&hc->hc_reply);
if (r)
break;
free(hc->hc_username);
hc->hc_username = NULL;
free(hc->hc_password);
hc->hc_password = NULL;
hc->hc_logout_cookie = 0;
@ -1060,29 +977,41 @@ static void
http_serve(int fd, void **opaque, struct sockaddr_storage *peer,
struct sockaddr_storage *self)
{
htsbuf_queue_t spill;
http_connection_t hc;
/* Note: global_lock held on entry */
// Note: global_lock held on entry */
pthread_mutex_unlock(&global_lock);
memset(&hc, 0, sizeof(http_connection_t));
*opaque = &hc;
hc.hc_fd = fd;
hc.hc_peer = peer;
hc.hc_self = self;
hc.hc_paths = &http_paths;
hc.hc_process = http_process_request;
http_arg_init(&hc.hc_args);
http_arg_init(&hc.hc_req_args);
http_serve_requests(&hc);
hc.hc_fd = fd;
hc.hc_peer = peer;
hc.hc_self = self;
htsbuf_queue_init(&spill, 0);
http_serve_requests(&hc, &spill);
http_arg_flush(&hc.hc_args);
http_arg_flush(&hc.hc_req_args);
htsbuf_queue_flush(&hc.hc_reply);
htsbuf_queue_flush(&spill);
close(fd);
// Note: leave global_lock held for parent
pthread_mutex_lock(&global_lock);
free(hc.hc_post_data);
free(hc.hc_username);
free(hc.hc_password);
*opaque = NULL;
}
void
static void
http_cancel( void *opaque )
{
http_connection_t *hc = opaque;

View file

@ -22,12 +22,9 @@
#include "htsbuf.h"
#include "url.h"
#include "tvhpoll.h"
#include "access.h"
#include "access.h"
struct channel;
struct http_path;
typedef LIST_HEAD(, http_path) http_path_list_t;
typedef TAILQ_HEAD(http_arg_list, http_arg) http_arg_list_t;
@ -74,17 +71,12 @@ typedef struct http_arg {
#define HTTP_STATUS_UNSUPPORTED 415
#define HTTP_STATUS_BAD_RANGE 417
#define HTTP_STATUS_EXPECTATION 418
#define HTTP_STATUS_BANDWIDTH 453
#define HTTP_STATUS_BAD_SESSION 454
#define HTTP_STATUS_METHOD_INVALID 455
#define HTTP_STATUS_BAD_TRANSFER 456
#define HTTP_STATUS_INTERNAL 500
#define HTTP_STATUS_NOT_IMPLEMENTED 501
#define HTTP_STATUS_BAD_GATEWAY 502
#define HTTP_STATUS_SERVICE 503
#define HTTP_STATUS_GATEWAY_TIMEOUT 504
#define HTTP_STATUS_HTTP_VERSION 505
#define HTTP_STATUS_OP_NOT_SUPPRT 551
typedef enum http_state {
HTTP_CON_WAIT_REQUEST,
@ -100,7 +92,6 @@ typedef enum http_state {
} http_state_t;
typedef enum http_cmd {
HTTP_CMD_NONE,
HTTP_CMD_GET,
HTTP_CMD_HEAD,
HTTP_CMD_POST,
@ -121,13 +112,9 @@ typedef enum http_ver {
typedef struct http_connection {
int hc_fd;
struct sockaddr_storage *hc_peer;
char *hc_peer_ipstr;
struct sockaddr_storage *hc_self;
char *hc_representative;
http_path_list_t *hc_paths;
int (*hc_process)(struct http_connection *hc, htsbuf_queue_t *spill);
char *hc_url;
char *hc_url_orig;
int hc_keep_alive;
@ -151,8 +138,6 @@ typedef struct http_connection {
int hc_no_output;
int hc_logout_cookie;
int hc_shutdown;
uint64_t hc_cseq;
char *hc_session;
/* Support for HTTP POST */
@ -161,7 +146,6 @@ typedef struct http_connection {
} http_connection_t;
extern void *http_server;
const char *http_cmd2str(int val);
int http_str2cmd(const char *str);
@ -176,7 +160,6 @@ static inline void http_arg_init(struct http_arg_list *list)
void http_arg_flush(struct http_arg_list *list);
char *http_arg_get(struct http_arg_list *list, const char *name);
char *http_arg_get_remove(struct http_arg_list *list, const char *name);
void http_arg_set(struct http_arg_list *list, const char *key, const char *val);
@ -194,11 +177,7 @@ void http_redirect(http_connection_t *hc, const char *location,
void http_send_header(http_connection_t *hc, int rc, const char *content,
int64_t contentlen, const char *encoding,
const char *location, int maxage, const char *range,
const char *disposition, http_arg_list_t *args);
void http_serve_requests(http_connection_t *hc);
void http_cancel(void *opaque);
const char *disposition);
typedef int (http_callback_t)(http_connection_t *hc,
const char *remain, void *opaque);
@ -234,8 +213,6 @@ int http_access_verify_channel(http_connection_t *hc, int mask,
void http_deescape(char *s);
void http_parse_get_args(http_connection_t *hc, char *args);
/*
* HTTP/RTSP Client
*/
@ -258,7 +235,6 @@ struct http_client {
TAILQ_ENTRY(http_client) hc_link;
int hc_id;
int hc_fd;
char *hc_scheme;
char *hc_host;

View file

@ -89,20 +89,11 @@ static pthread_cond_t http_cond;
static th_pipe_t http_pipe;
static char *http_user_agent;
/*
*
*/
static inline int
shortid( http_client_t *hc )
{
return hc->hc_id;
}
/*
*
*/
static int
http_port( http_client_t *hc, const char *scheme, int port )
http_port( const char *scheme, int port )
{
if (port <= 0 || port > 65535) {
if (scheme && strcmp(scheme, "http") == 0)
@ -112,7 +103,7 @@ http_port( http_client_t *hc, const char *scheme, int port )
else if (scheme && strcmp(scheme, "rtsp") == 0)
port = 554;
else {
tvhlog(LOG_ERR, "httpc", "%04X: Unknown scheme '%s'", shortid(hc), scheme ? scheme : "");
tvhlog(LOG_ERR, "httpc", "Unknown scheme '%s'", scheme ? scheme : "");
return -EINVAL;
}
}
@ -205,7 +196,6 @@ static int
http_client_flush( http_client_t *hc, int result )
{
hc->hc_result = result;
tvhtrace("httpc", "%04X: client flush %i", shortid(hc), result);
if (result < 0)
http_client_shutdown(hc, 0, 0);
hc->hc_in_data = 0;
@ -399,8 +389,8 @@ http_client_ssl_send( http_client_t *hc, const void *buf, size_t len )
if (hc->hc_verify_peer > 0) {
if (SSL_get_peer_certificate(ssl->ssl) == NULL ||
SSL_get_verify_result(ssl->ssl) != X509_V_OK) {
tvhlog(LOG_ERR, "httpc", "%04X: SSL peer verification failed (%s:%i)%s %li",
shortid(hc), hc->hc_host, hc->hc_port,
tvhlog(LOG_ERR, "httpc", "SSL peer verification failed (%s:%i)%s %li",
hc->hc_host, hc->hc_port,
SSL_get_peer_certificate(ssl->ssl) ? " X509" : "",
SSL_get_verify_result(ssl->ssl));
errno = EPERM;
@ -615,7 +605,7 @@ error:
htsbuf_read(&q, body, body_size);
#if ENABLE_TRACE
tvhtrace("httpc", "%04X: sending %s cmd", shortid(hc), http_ver2str(hc->hc_version));
tvhtrace("httpc", "sending %s cmd", http_ver2str(hc->hc_version));
tvhlog_hexdump("httpc", body, body_size);
#endif
@ -640,7 +630,7 @@ http_client_finish( http_client_t *hc )
#if ENABLE_TRACE
if (hc->hc_data) {
tvhtrace("httpc", "%04X: received %s data", shortid(hc), http_ver2str(hc->hc_version));
tvhtrace("httpc", "received %s data", http_ver2str(hc->hc_version));
tvhlog_hexdump("httpc", hc->hc_data, hc->hc_csize);
}
#endif
@ -750,7 +740,7 @@ http_client_data_chunked( http_client_t *hc, char *buf, size_t len, int *end )
}
l = 0;
if (hc->hc_chunk_csize) {
s = hc->hc_chunk;
s = d = hc->hc_chunk;
if (buf[0] == '\n' && s[hc->hc_chunk_csize-1] == '\r')
l = 1;
else if (len > 1 && buf[0] == '\r' && buf[1] == '\n')
@ -776,10 +766,7 @@ http_client_data_chunked( http_client_t *hc, char *buf, size_t len, int *end )
return res;
continue;
}
d = s + 1;
while (*d == '0' && *d)
d++;
if (s[0] == '0' && *d == '\0')
if (s[0] == '0' && s[1] == '\0')
hc->hc_chunk_trails = 1;
else {
hc->hc_chunk_size = strtoll(s, NULL, 16);
@ -863,9 +850,10 @@ int
http_client_run( http_client_t *hc )
{
char *buf, *saveptr, *argv[3], *d, *p;
int ver, res, delimsize = 4;
int ver;
ssize_t r;
size_t len;
int res;
if (hc == NULL)
return 0;
@ -894,21 +882,15 @@ http_client_run( http_client_t *hc )
buf = alloca(hc->hc_io_size);
if (!hc->hc_in_data && hc->hc_rpos > 3) {
if ((d = strstr(hc->hc_rbuf, "\r\n\r\n")) != NULL)
goto header;
if ((d = strstr(hc->hc_rbuf, "\n\n")) != NULL) {
delimsize = 2;
goto header;
}
}
if (!hc->hc_in_data && hc->hc_rpos > 3 &&
(d = strstr(hc->hc_rbuf, "\r\n\r\n")) != NULL)
goto header;
retry:
if (hc->hc_ssl)
r = http_client_ssl_recv(hc, buf, hc->hc_io_size);
else
r = recv(hc->hc_fd, buf, hc->hc_io_size, MSG_DONTWAIT);
tvhtrace("httpc", "%04X: recv %zi", shortid(hc), r);
if (r == 0) {
if (hc->hc_in_data && !hc->hc_keepalive)
return http_client_finish(hc);
@ -923,7 +905,7 @@ retry:
}
#if ENABLE_TRACE
if (r > 0) {
tvhtrace("httpc", "%04X: received %s answer", shortid(hc), http_ver2str(hc->hc_version));
tvhtrace("httpc", "received %s answer", http_ver2str(hc->hc_version));
tvhlog_hexdump("httpc", buf, r);
}
#endif
@ -952,11 +934,8 @@ retry:
next_header:
if (hc->hc_rpos < 3)
return HTTP_CON_RECEIVING;
if ((d = strstr(hc->hc_rbuf, "\r\n\r\n")) == NULL) {
delimsize = 2;
if ((d = strstr(hc->hc_rbuf, "\n\n")) == NULL)
return HTTP_CON_RECEIVING;
}
if ((d = strstr(hc->hc_rbuf, "\r\n\r\n")) == NULL)
return HTTP_CON_RECEIVING;
header:
*d = '\0';
@ -964,11 +943,11 @@ header:
hc->hc_reconnected = 0;
http_client_clear_state(hc);
hc->hc_rpos = len;
hc->hc_hsize = d - hc->hc_rbuf + delimsize;
hc->hc_hsize = d - hc->hc_rbuf + 4;
p = strtok_r(hc->hc_rbuf, "\r\n", &saveptr);
if (p == NULL)
return http_client_flush(hc, -EINVAL);
tvhtrace("httpc", "%04X: %s answer '%s'", shortid(hc), http_ver2str(hc->hc_version), p);
tvhtrace("httpc", "%s answer '%s'", http_ver2str(hc->hc_version), p);
if (http_tokenize(p, argv, 3, -1) != 3)
return http_client_flush(hc, -EINVAL);
if ((ver = http_str2ver(argv[0])) < 0)
@ -1040,7 +1019,7 @@ header:
* Redirected
*/
static void
http_client_basic_args ( http_client_t *hc, http_arg_list_t *h, const url_t *url, int keepalive )
http_client_basic_args ( http_arg_list_t *h, const url_t *url, int keepalive )
{
char buf[256];
@ -1049,7 +1028,7 @@ http_client_basic_args ( http_client_t *hc, http_arg_list_t *h, const url_t *url
http_arg_set(h, "Host", url->host);
} else {
snprintf(buf, sizeof(buf), "%s:%u", url->host,
http_port(hc, url->scheme, url->port));
http_port(url->scheme, url->port));
http_arg_set(h, "Host", buf);
}
if (http_user_agent) {
@ -1103,8 +1082,8 @@ http_client_redirected ( http_client_t *hc )
memset(&u, 0, sizeof(u));
if (urlparse(location2 ? location2 : location, &u)) {
tvherror("httpc", "%04X: redirection - cannot parse url '%s'",
shortid(hc), location2 ? location2 : location);
tvherror("httpc", "redirection - cannot parse url '%s'",
location2 ? location2 : location);
free(location);
return -EIO;
}
@ -1112,7 +1091,7 @@ http_client_redirected ( http_client_t *hc )
if (strcmp(u.scheme, hc->hc_scheme) ||
strcmp(u.host, hc->hc_host) ||
http_port(hc, u.scheme, u.port) != hc->hc_port ||
http_port(u.scheme, u.port) != hc->hc_port ||
!hc->hc_keepalive) {
efd = hc->hc_efd;
http_client_shutdown(hc, 1, 1);
@ -1130,7 +1109,7 @@ http_client_redirected ( http_client_t *hc )
http_client_flush(hc, 0);
http_client_basic_args(hc, &h, &u, hc->hc_keepalive);
http_client_basic_args(&h, &u, hc->hc_keepalive);
hc->hc_reconnected = 1;
hc->hc_shutdown = 0;
hc->hc_pevents = 0;
@ -1151,7 +1130,7 @@ http_client_simple( http_client_t *hc, const url_t *url )
{
http_arg_list_t h;
http_client_basic_args(hc, &h, url, 0);
http_client_basic_args(&h, url, 0);
return http_client_send(hc, HTTP_CMD_GET, url->path, url->query,
&h, NULL, 0);
}
@ -1165,14 +1144,14 @@ http_client_ssl_peer_verify( http_client_t *hc, int verify )
hc->hc_verify_peer = verify ? 1 : 0;
if ((ssl = hc->hc_ssl) != NULL) {
if (!SSL_CTX_set_default_verify_paths(ssl->ctx))
tvherror("httpc", "%04X: SSL - unable to load CA certificates for verification", shortid(hc));
tvherror("httpc", "SSL - unable to load CA certificates for verification");
SSL_CTX_set_verify_depth(ssl->ctx, 1);
SSL_CTX_set_verify(ssl->ctx,
hc->hc_verify_peer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
NULL);
}
} else {
tvherror("httpc", "%04X: SSL peer verification method must be set only once", shortid(hc));
tvherror("httpc", "SSL peer verification method must be set only once");
}
}
@ -1261,7 +1240,7 @@ http_client_reconnect
if (scheme == NULL || host == NULL)
return -EINVAL;
port = http_port(hc, scheme, port);
port = http_port(scheme, port);
hc->hc_pevents = 0;
hc->hc_version = ver;
hc->hc_scheme = strdup(scheme);
@ -1269,37 +1248,37 @@ http_client_reconnect
hc->hc_port = port;
hc->hc_fd = tcp_connect(host, port, hc->hc_bindaddr, errbuf, sizeof(errbuf), -1);
if (hc->hc_fd < 0) {
tvhlog(LOG_ERR, "httpc", "%04X: Unable to connect to %s:%i - %s", shortid(hc), host, port, errbuf);
tvhlog(LOG_ERR, "httpc", "Unable to connect to %s:%i - %s", host, port, errbuf);
return -EINVAL;
}
hc->hc_einprogress = 1;
tvhtrace("httpc", "%04X: Connected to %s:%i", shortid(hc), host, port);
tvhtrace("httpc", "Connected to %s:%i", host, port);
http_client_ssl_free(hc);
if (strcasecmp(scheme, "https") == 0 || strcasecmp(scheme, "rtsps") == 0) {
ssl = calloc(1, sizeof(*ssl));
hc->hc_ssl = ssl;
ssl->ctx = SSL_CTX_new(SSLv23_client_method());
if (ssl->ctx == NULL) {
tvhlog(LOG_ERR, "httpc", "%04X: Unable to get SSL_CTX", shortid(hc));
tvhlog(LOG_ERR, "httpc", "Unable to get SSL_CTX");
goto err1;
}
/* do not use SSLv2 */
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION);
/* adjust cipher list */
if (SSL_CTX_set_cipher_list(ssl->ctx, "HIGH:MEDIUM") != 1) {
tvhlog(LOG_ERR, "httpc", "%04X: Unable to adjust SSL cipher list", shortid(hc));
tvhlog(LOG_ERR, "httpc", "Unable to adjust SSL cipher list");
goto err2;
}
ssl->rbio = BIO_new(BIO_s_mem());
ssl->wbio = BIO_new(BIO_s_mem());
ssl->ssl = SSL_new(ssl->ctx);
if (ssl->ssl == NULL || ssl->rbio == NULL || ssl->wbio == NULL) {
tvhlog(LOG_ERR, "httpc", "%04X: Unable to get SSL handle", shortid(hc));
tvhlog(LOG_ERR, "httpc", "Unable to get SSL handle");
goto err3;
}
SSL_set_bio(ssl->ssl, ssl->rbio, ssl->wbio);
if (!SSL_set_tlsext_host_name(ssl->ssl, host)) {
tvhlog(LOG_ERR, "httpc", "%04X: Unable to set SSL hostname", shortid(hc));
tvhlog(LOG_ERR, "httpc", "Unable to set SSL hostname");
goto err4;
}
}
@ -1325,10 +1304,8 @@ http_client_connect
const char *host, int port, const char *bindaddr )
{
http_client_t *hc;
static int tally;
hc = calloc(1, sizeof(http_client_t));
hc->hc_id = ++tally;
hc->hc_aux = aux;
hc->hc_io_size = 1024;
hc->hc_rtsp_stream_id = -1;
@ -1387,7 +1364,6 @@ http_client_close ( http_client_t *hc )
}
http_client_shutdown(hc, 1, 0);
http_client_flush(hc, 0);
tvhtrace("httpc", "%04X: Closed", shortid(hc));
while ((wcmd = TAILQ_FIRST(&hc->hc_wqueue)) != NULL)
http_client_cmd_destroy(hc, wcmd);
http_client_ssl_free(hc);
@ -1666,7 +1642,7 @@ http_client_testsuite_run( void )
path = getenv("TVHEADEND_HTTPC_TEST");
if (path == NULL)
path = TVHEADEND_DATADIR "/support/httpc-test.txt";
fp = tvh_fopen(path, "r");
fp = fopen(path, "r");
if (fp == NULL) {
tvhlog(LOG_NOTICE, "httpc", "Test: unable to open '%s': %s", path, strerror(errno));
return;

View file

@ -342,28 +342,10 @@ idnode_get_display
if (p) {
if (p->rend)
return p->rend(self);
else if (p->islist) {
if (p->islist) {
htsmsg_t *l = (htsmsg_t*)p->get(self);
if (l)
return htsmsg_list_2_csv(l);
} else if (p->list) {
htsmsg_t *l = p->list(self), *m;
htsmsg_field_t *f;
uint32_t k, v;
char *r = NULL;
const char *s;
if (l && !idnode_get_u32(self, p->id, &v))
HTSMSG_FOREACH(f, l) {
m = htsmsg_field_get_map(f);
if (!htsmsg_get_u32(m, "key", &k) &&
(s = htsmsg_get_str(m, "val")) != NULL &&
v == k) {
r = strdup(s);
break;
}
}
htsmsg_destroy(l);
return r;
}
}
return NULL;
@ -1011,7 +993,7 @@ idnode_set_find_index
return -1;
}
int
void
idnode_set_remove
( idnode_set_t *is, idnode_t *in )
{
@ -1020,9 +1002,7 @@ idnode_set_remove
memmove(&is->is_array[i], &is->is_array[i+1],
(is->is_count - i - 1) * sizeof(idnode_t *));
is->is_count--;
return 1;
}
return 0;
}
void
@ -1442,13 +1422,11 @@ idnode_thread ( void *p )
HTSMSG_FOREACH(f, q) {
node = idnode_find(f->hmf_name, NULL, NULL);
event = htsmsg_field_get_str(f);
if (event) {
m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", f->hmf_name);
if (!node)
htsmsg_add_u32(m, "removed", 1);
notify_by_msg(event, m);
}
m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", f->hmf_name);
if (!node)
htsmsg_add_u32(m, "removed", 1);
notify_by_msg(event, m);
}
/* Finished */

View file

@ -203,7 +203,7 @@ static inline idnode_set_t * idnode_set_create(int sorted)
is->is_sorted = sorted; return is; }
void idnode_set_add
( idnode_set_t *is, idnode_t *in, idnode_filter_t *filt );
int idnode_set_remove ( idnode_set_t *is, idnode_t *in );
void idnode_set_remove ( idnode_set_t *is, idnode_t *in );
ssize_t idnode_set_find_index( idnode_set_t *is, idnode_t *in );
static inline int idnode_set_exists ( idnode_set_t *is, idnode_t *in )
{ return idnode_set_find_index(is, in) >= 0; }

View file

@ -144,8 +144,6 @@ imagecache_image_fetch ( imagecache_image_t *img )
tvhpoll_t *efd = NULL;
http_client_t *hc;
lock_assert(&global_lock);
if (img->url == NULL || img->url[0] == '\0')
return res;
@ -158,7 +156,7 @@ imagecache_image_fetch ( imagecache_image_t *img )
if (hts_settings_makedirs(path))
goto error;
snprintf(tmp, sizeof(tmp), "%s.tmp", path);
if (!(fp = tvh_fopen(tmp, "wb")))
if (!(fp = fopen(tmp, "wb")))
goto error;
/* Fetch (release lock, incase of delays) */
@ -577,7 +575,9 @@ imagecache_open ( uint32_t id )
} else if (i->state == QUEUED) {
i->state = FETCHING;
TAILQ_REMOVE(&imagecache_queue, i, q_link);
pthread_mutex_unlock(&global_lock);
e = imagecache_image_fetch(i);
pthread_mutex_lock(&global_lock);
if (e)
return -1;
}

View file

@ -18,18 +18,10 @@
#include "input.h"
#include "notify.h"
#include "access.h"
tvh_input_list_t tvh_inputs;
tvh_hardware_list_t tvh_hardware;
const idclass_t tvh_input_instance_class =
{
.ic_class = "tvh_input_instance",
.ic_caption = "Input Instance",
.ic_perm_def = ACCESS_ADMIN
};
/*
* Create entry
*/
@ -67,23 +59,6 @@ tvh_hardware_delete ( tvh_hardware_t *th )
idnode_unlink(&th->th_id);
}
/*
*
*/
void
tvh_input_instance_clear_stats ( tvh_input_instance_t *tii )
{
tvh_input_stream_stats_t *s = &tii->tii_stats;
atomic_exchange(&s->ber, 0);
atomic_exchange(&s->unc, 0);
atomic_exchange(&s->cc, 0);
atomic_exchange(&s->te, 0);
atomic_exchange(&s->ec_block, 0);
atomic_exchange(&s->tc_block, 0);
}
/*
* Input status handling
*/
@ -100,10 +75,10 @@ tvh_input_stream_create_msg
htsmsg_add_str(m, "stream", st->stream_name);
htsmsg_add_u32(m, "subs", st->subs_count);
htsmsg_add_u32(m, "weight", st->max_weight);
htsmsg_add_s32(m, "signal", st->stats.signal);
htsmsg_add_u32(m, "signal", st->stats.signal);
htsmsg_add_u32(m, "signal_scale", st->stats.signal_scale);
htsmsg_add_u32(m, "ber", st->stats.ber);
htsmsg_add_s32(m, "snr", st->stats.snr);
htsmsg_add_u32(m, "snr", st->stats.snr);
htsmsg_add_u32(m, "snr_scale", st->stats.snr_scale);
htsmsg_add_u32(m, "unc", st->stats.unc);
htsmsg_add_u32(m, "bps", st->stats.bps);

View file

@ -27,7 +27,6 @@
*/
typedef struct tvh_hardware tvh_hardware_t;
typedef struct tvh_input tvh_input_t;
typedef struct tvh_input_instance tvh_input_instance_t;
typedef struct tvh_input_stream tvh_input_stream_t;
typedef struct tvh_input_stream_stats tvh_input_stream_stats_t;
@ -88,20 +87,6 @@ struct tvh_input {
void (*ti_get_streams) (struct tvh_input *, tvh_input_stream_list_t*);
};
/*
* Generic input instance super-class
*/
struct tvh_input_instance {
idnode_t tii_id;
LIST_ENTRY(tvh_input_instance) tii_input_link;
tvh_input_stream_stats_t tii_stats;
void (*tii_delete) (tvh_input_instance_t *tii);
void (*tii_clear_stats) (tvh_input_instance_t *tii);
};
/*
* Generic hardware super-class
*/
@ -118,7 +103,6 @@ void tvh_hardware_delete ( tvh_hardware_t *th );
* Class and Global list defs
*/
extern const idclass_t tvh_input_class;
extern const idclass_t tvh_input_instance_class;
tvh_input_list_t tvh_inputs;
tvh_hardware_list_t tvh_hardware;
@ -136,16 +120,14 @@ htsmsg_t * tvh_input_stream_create_msg ( tvh_input_stream_t *st );
void tvh_input_stream_destroy ( tvh_input_stream_t *st );
static inline tvh_input_instance_t *
tvh_input_instance_find_by_uuid(const char *uuid)
{ return (tvh_input_instance_t*)idnode_find(uuid, &tvh_input_instance_class, NULL); }
void tvh_input_instance_clear_stats ( tvh_input_instance_t *tii );
/*
* Input subsystem includes
*/
#if ENABLE_MPEGPS
#include "input/mpegps.h"
#endif
#if ENABLE_MPEGTS
#include "input/mpegts.h"
#include "input/mpegts/mpegts_mux_sched.h"

1
src/input/mpegps/v4l.h Symbolic link
View file

@ -0,0 +1 @@
v4l/v4l.h

789
src/input/mpegps/v4l/v4l.c Normal file
View file

@ -0,0 +1,789 @@
/*
* TV Input - Linux analogue (v4lv2) interface
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <pthread.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <inttypes.h>
#include "settings.h"
#include "tvheadend.h"
#include "service.h"
#include "v4l.h"
#include "parsers.h"
#include "notify.h"
#include "psi.h"
#include "channels.h"
struct v4l_adapter_queue v4l_adapters;
static void v4l_adapter_notify(v4l_adapter_t *va);
const idclass_t v4l_class = {
.ic_super = &service_class,
.ic_class = "v4l",
};
/**
*
*/
static void
v4l_input(v4l_adapter_t *va)
{
service_t *t = va->va_current_service;
elementary_stream_t *st;
uint8_t buf[4000];
uint8_t *ptr, *pkt;
int len, l, r;
len = read(va->va_fd, buf, 4000);
if(len < 1)
return;
ptr = buf;
pthread_mutex_lock(&t->s_stream_mutex);
service_set_streaming_status_flags(t,
TSS_INPUT_HARDWARE | TSS_INPUT_SERVICE);
while(len > 0) {
switch(va->va_startcode) {
default:
va->va_startcode = va->va_startcode << 8 | *ptr;
va->va_lenlock = 0;
ptr++; len--;
continue;
case 0x000001e0:
st = t->s_video;
break;
case 0x000001c0:
st = t->s_audio;
break;
}
if(va->va_lenlock == 2) {
l = st->es_buf_ps.sb_size;
st->es_buf_ps.sb_data = pkt = realloc(st->es_buf_ps.sb_data, l);
r = l - st->es_buf_ps.sb_ptr;
if(r > len)
r = len;
memcpy(pkt + st->es_buf_ps.sb_ptr, ptr, r);
ptr += r;
len -= r;
st->es_buf_ps.sb_ptr += r;
if(st->es_buf_ps.sb_ptr == l) {
service_set_streaming_status_flags(t, TSS_MUX_PACKETS);
parse_mpeg_ps(t, st, pkt + 6, l - 6);
st->es_buf_ps.sb_size = 0;
va->va_startcode = 0;
} else {
assert(st->es_buf_ps.sb_ptr < l);
}
} else {
st->es_buf_ps.sb_size = st->es_buf_ps.sb_size << 8 | *ptr;
va->va_lenlock++;
if(va->va_lenlock == 2) {
st->es_buf_ps.sb_size += 6;
st->es_buf_ps.sb_ptr = 6;
}
ptr++; len--;
}
}
pthread_mutex_unlock(&t->s_stream_mutex);
}
/**
*
*/
static void *
v4l_thread(void *aux)
{
v4l_adapter_t *va = aux;
struct pollfd pfd[2];
int r;
pfd[0].fd = va->va_pipe[0];
pfd[0].events = POLLIN;
pfd[1].fd = va->va_fd;
pfd[1].events = POLLIN;
while(1) {
r = poll(pfd, 2, -1);
if(r < 0) {
tvhlog(LOG_ALERT, "v4l", "%s: poll() error %s, sleeping one second",
va->va_path, strerror(errno));
sleep(1);
continue;
}
if(pfd[0].revents & POLLIN) {
// Message on control pipe, used to exit thread, do so
break;
}
if(pfd[1].revents & POLLIN) {
v4l_input(va);
}
}
close(va->va_pipe[0]);
return NULL;
}
/**
*
*/
static int
v4l_service_start(service_t *t, int instance)
{
v4l_adapter_t *va = t->s_v4l_adapter;
int frequency = t->s_v4l_frequency;
struct v4l2_frequency vf;
int result;
v4l2_std_id std = 0xff;
int fd;
if(va->va_current_service != NULL)
return 1; // Adapter busy
fd = tvh_open(va->va_path, O_RDWR | O_NONBLOCK, 0);
if(fd == -1) {
tvhlog(LOG_ERR, "v4l",
"%s: Unable to open device: %s\n", va->va_path,
strerror(errno));
return -1;
}
if(!va->va_file) {
result = ioctl(fd, VIDIOC_S_STD, &std);
if(result < 0) {
tvhlog(LOG_ERR, "v4l",
"%s: Unable to set PAL -- %s", va->va_path, strerror(errno));
close(fd);
return -1;
}
memset(&vf, 0, sizeof(vf));
vf.tuner = 0;
vf.type = V4L2_TUNER_ANALOG_TV;
vf.frequency = (frequency * 16) / 1000000;
result = ioctl(fd, VIDIOC_S_FREQUENCY, &vf);
if(result < 0) {
tvhlog(LOG_ERR, "v4l",
"%s: Unable to tune to %dHz", va->va_path, frequency);
close(fd);
return -1;
}
tvhlog(LOG_INFO, "v4l",
"%s: Tuned to %dHz", va->va_path, frequency);
}
if(pipe(va->va_pipe)) {
tvhlog(LOG_ERR, "v4l",
"%s: Unable to create control pipe [%s]", va->va_path, strerror(errno));
close(fd);
return -1;
}
va->va_fd = fd;
va->va_current_service = t;
tvhthread_create(&va->va_thread, NULL, v4l_thread, va);
v4l_adapter_notify(va);
return 0;
}
/**
*
*/
static void
v4l_service_refresh(service_t *t)
{
}
/**
*
*/
static void
v4l_service_stop(service_t *t)
{
char c = 'q';
v4l_adapter_t *va = t->s_v4l_adapter;
assert(va->va_current_service != NULL);
if(tvh_write(va->va_pipe[1], &c, 1))
tvhlog(LOG_ERR, "v4l", "Unable to close video thread -- %s",
strerror(errno));
pthread_join(va->va_thread, NULL);
close(va->va_pipe[1]);
close(va->va_fd);
va->va_current_service = NULL;
v4l_adapter_notify(va);
}
/**
*
*/
static void
v4l_service_save(service_t *t)
{
v4l_adapter_t *va = t->s_v4l_adapter;
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_u32(m, "frequency", t->s_v4l_frequency);
if(t->s_ch != NULL) {
htsmsg_add_str(m, "channelname", t->s_ch->ch_name);
htsmsg_add_u32(m, "mapped", 1);
}
pthread_mutex_lock(&t->s_stream_mutex);
psi_save_service_settings(m, t);
pthread_mutex_unlock(&t->s_stream_mutex);
hts_settings_save(m, "v4lservices/%s/%s",
va->va_identifier, idnode_uuid_as_str(&t->s_id));
htsmsg_destroy(m);
}
/**
*
*/
static int
v4l_service_is_enabled(service_t *t)
{
return t->s_enabled;
}
/**
*
*/
static int
v4l_grace_period(service_t *t)
{
return 2;
}
/**
* Generate a descriptive name for the source
*/
static void
v4l_service_setsourceinfo(service_t *t, struct source_info *si)
{
char buf[64];
memset(si, 0, sizeof(struct source_info));
si->si_type = S_MPEG_PS;
si->si_adapter = strdup(t->s_v4l_adapter->va_displayname);
snprintf(buf, sizeof(buf), "%d Hz", t->s_v4l_frequency);
si->si_mux = strdup(buf);
}
/**
*
*/
service_t *
v4l_service_find(v4l_adapter_t *va, const char *id, int create)
{
service_t *t;
char buf[200];
int vaidlen = strlen(va->va_identifier);
if(id != NULL) {
if(strncmp(id, va->va_identifier, vaidlen))
return NULL;
LIST_FOREACH(t, &va->va_services, s_group_link)
if(!strcmp(idnode_uuid_as_str(&t->s_id), id))
return t;
}
if(create == 0)
return NULL;
if(id == NULL) {
va->va_tally++;
snprintf(buf, sizeof(buf), "%s_%d", va->va_identifier, va->va_tally);
id = buf;
} else {
va->va_tally = MAX(atoi(id + vaidlen + 1), va->va_tally);
}
t = service_create(id, 0, &v4l_class);
t->s_start_feed = v4l_service_start;
t->s_refresh_feed = v4l_service_refresh;
t->s_stop_feed = v4l_service_stop;
t->s_config_save = v4l_service_save;
t->s_setsourceinfo = v4l_service_setsourceinfo;
t->s_is_enabled = v4l_service_is_enabled;
t->s_grace_period = v4l_grace_period;
t->s_iptv_fd = -1;
t->s_v4l_adapter = va;
pthread_mutex_lock(&t->s_stream_mutex);
service_make_nicename(t);
t->s_video = service_stream_create(t, -1, SCT_MPEG2VIDEO);
t->s_audio = service_stream_create(t, -1, SCT_MPEG2AUDIO);
pthread_mutex_unlock(&t->s_stream_mutex);
LIST_INSERT_HEAD(&va->va_services, t, s_group_link);
return t;
}
/**
*
*/
static void
v4l_adapter_add(const char *path, const char *displayname,
const char *devicename, int file)
{
v4l_adapter_t *va;
int i, r;
va = calloc(1, sizeof(v4l_adapter_t));
va->va_identifier = strdup(path);
r = strlen(va->va_identifier);
for(i = 0; i < r; i++)
if(!isalnum((int)va->va_identifier[i]))
va->va_identifier[i] = '_';
va->va_displayname = strdup(displayname);
va->va_path = path ? strdup(path) : NULL;
va->va_devicename = devicename ? strdup(devicename) : NULL;
va->va_file = file;
TAILQ_INSERT_TAIL(&v4l_adapters, va, va_global_link);
}
/**
*
*/
static void
v4l_adapter_check(const char *path, int fd)
{
int r, i;
char devicename[100];
struct v4l2_capability caps;
r = ioctl(fd, VIDIOC_QUERYCAP, &caps);
if(r) {
tvhlog(LOG_WARNING, "v4l",
"%s: Can not query capabilities, device skipped", path);
return;
}
tvhlog(LOG_INFO, "v4l", "%s: %s %s %s capabilities: 0x%08x",
path, caps.driver, caps.card, caps.bus_info, caps.capabilities);
if(!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
tvhlog(LOG_WARNING, "v4l",
"%s: Device is not a video capture device, device skipped", path);
return;
}
/* Enum video standards */
for(i = 0;; i++) {
struct v4l2_standard standard;
memset(&standard, 0, sizeof(standard));
standard.index = i;
if(ioctl(fd, VIDIOC_ENUMSTD, &standard))
break;
tvhlog(LOG_INFO, "v4l",
"%s: Standard #%d: %016llx %s, frameperiod: %d/%d, %d lines",
path,
standard.index,
standard.id,
standard.name,
standard.frameperiod.numerator,
standard.frameperiod.denominator,
standard.framelines);
}
/* Enum video inputs */
for(i = 0;; i++) {
struct v4l2_input input;
memset(&input, 0, sizeof(input));
input.index = i;
if(ioctl(fd, VIDIOC_ENUMINPUT, &input))
break;
const char *type;
switch(input.type) {
case V4L2_INPUT_TYPE_TUNER:
type = "Tuner";
break;
case V4L2_INPUT_TYPE_CAMERA:
type = "Camera";
break;
default:
type = "Unknown";
break;
}
int f = input.status;
tvhlog(LOG_INFO, "v4l",
"%s: Input #%d: %s (%s), audio:0x%x, tuner:%d, standard:%016llx, "
"%s%s%s",
path,
input.index,
input.name,
type,
input.audioset,
input.tuner,
input.std,
f & V4L2_IN_ST_NO_POWER ? "[No power] " : "",
f & V4L2_IN_ST_NO_SIGNAL ? "[No signal] " : "",
f & V4L2_IN_ST_NO_COLOR ? "[No color] " : "");
}
int can_mpeg = 0;
/* Enum formats */
for(i = 0;; i++) {
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = i;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
break;
tvhlog(LOG_INFO, "v4l",
"%s: Format #%d: %s [%.4s] %s",
path,
fmtdesc.index,
fmtdesc.description,
(char*)&fmtdesc.pixelformat,
fmtdesc.flags & V4L2_FMT_FLAG_COMPRESSED ? "(compressed)" : "");
if(fmtdesc.pixelformat == V4L2_PIX_FMT_MPEG)
can_mpeg = 1;
}
if(!(caps.capabilities & V4L2_CAP_TUNER)) {
tvhlog(LOG_WARNING, "v4l",
"%s: Device does not have a tuner, device skipped", path);
return;
}
if(!can_mpeg) {
tvhlog(LOG_WARNING, "v4l",
"%s: Device lacks MPEG encoder, device skipped", path);
return;
}
snprintf(devicename, sizeof(devicename), "%s %s %s",
caps.card, caps.driver, caps.bus_info);
tvhlog(LOG_INFO, "v4l",
"%s: Using adapter", devicename);
v4l_adapter_add(path, devicename, devicename, 0);
}
/**
*
*/
static void
v4l_adapter_probe(const char *path)
{
int fd;
fd = tvh_open(path, O_RDWR | O_NONBLOCK, 0);
if(fd == -1) {
if(errno != ENOENT)
tvhlog(LOG_ALERT, "v4l",
"Unable to open %s -- %s", path, strerror(errno));
return;
}
v4l_adapter_check(path, fd);
close(fd);
}
/**
* Save config for the given adapter
*/
static void
v4l_adapter_save(v4l_adapter_t *va)
{
htsmsg_t *m = htsmsg_create_map();
lock_assert(&global_lock);
htsmsg_add_str(m, "displayname", va->va_displayname);
htsmsg_add_u32(m, "logging", va->va_logging);
hts_settings_save(m, "v4ladapters/%s", va->va_identifier);
htsmsg_destroy(m);
}
/**
*
*/
htsmsg_t *
v4l_adapter_build_msg(v4l_adapter_t *va)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "identifier", va->va_identifier);
htsmsg_add_str(m, "name", va->va_displayname);
htsmsg_add_str(m, "type", "v4l");
if(va->va_path)
htsmsg_add_str(m, "path", va->va_path);
if(va->va_devicename)
htsmsg_add_str(m, "devicename", va->va_devicename);
if(va->va_current_service != NULL) {
char buf[100];
snprintf(buf, sizeof(buf), "%d Hz",
va->va_current_service->s_v4l_frequency);
htsmsg_add_str(m, "currentMux", buf);
} else {
htsmsg_add_str(m, "currentMux", "- inactive -");
}
return m;
}
/**
*
*/
static void
v4l_adapter_notify(v4l_adapter_t *va)
{
notify_by_msg("tvAdapter", v4l_adapter_build_msg(va));
}
/**
*
*/
v4l_adapter_t *
v4l_adapter_find_by_identifier(const char *identifier)
{
v4l_adapter_t *va;
TAILQ_FOREACH(va, &v4l_adapters, va_global_link)
if(!strcmp(identifier, va->va_identifier))
return va;
return NULL;
}
/**
*
*/
void
v4l_adapter_set_displayname(v4l_adapter_t *va, const char *name)
{
lock_assert(&global_lock);
if(!strcmp(name, va->va_displayname))
return;
tvhlog(LOG_NOTICE, "v4l", "Adapter \"%s\" renamed to \"%s\"",
va->va_displayname, name);
tvh_str_set(&va->va_displayname, name);
v4l_adapter_save(va);
v4l_adapter_notify(va);
}
/**
*
*/
void
v4l_adapter_set_logging(v4l_adapter_t *va, int on)
{
if(va->va_logging == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "v4l", "Adapter \"%s\" detailed logging set to: %s",
va->va_displayname, on ? "On" : "Off");
va->va_logging = on;
v4l_adapter_save(va);
v4l_adapter_notify(va);
}
/**
*
*/
static void
v4l_service_create_by_msg(v4l_adapter_t *va, htsmsg_t *c, const char *name)
{
const char *s;
unsigned int u32;
service_t *t = v4l_service_find(va, name, 1);
if(t == NULL)
return;
s = htsmsg_get_str(c, "channelname");
if(htsmsg_get_u32(c, "mapped", &u32))
u32 = 0;
if(!htsmsg_get_u32(c, "frequency", &u32))
t->s_v4l_frequency = u32;
if(s && u32)
service_map_channel(t, channel_find_by_name(s, 1, 0), 0);
}
/**
*
*/
static void
v4l_service_load(v4l_adapter_t *va)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
if((l = hts_settings_load("v4lservices/%s", va->va_identifier)) == NULL)
return;
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
v4l_service_create_by_msg(va, c, f->hmf_name);
}
htsmsg_destroy(l);
}
/**
*
*/
void
v4l_init(void)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
char buf[256];
int i;
v4l_adapter_t *va;
TAILQ_INIT(&v4l_adapters);
for(i = 0; i < 8; i++) {
snprintf(buf, sizeof(buf), "/dev/video%d", i);
v4l_adapter_probe(buf);
}
l = hts_settings_load("v4ladapters");
if(l != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
if((va = v4l_adapter_find_by_identifier(f->hmf_name)) == NULL) {
/* Not discovered by hardware, create it */
va = calloc(1, sizeof(v4l_adapter_t));
va->va_identifier = strdup(f->hmf_name);
va->va_path = NULL;
va->va_devicename = NULL;
}
tvh_str_update(&va->va_displayname, htsmsg_get_str(c, "displayname"));
htsmsg_get_u32(c, "logging", &va->va_logging);
}
htsmsg_destroy(l);
}
TAILQ_FOREACH(va, &v4l_adapters, va_global_link)
v4l_service_load(va);
}

View file

@ -0,0 +1,82 @@
/*
* TV Input - Linux analogue (v4lv2) interface
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef V4L_H_
#define V4L_H_
#define __user
#include <linux/videodev2.h>
LIST_HEAD(v4l_adapter_list, v4l_adapter);
TAILQ_HEAD(v4l_adapter_queue, v4l_adapter);
extern struct v4l_adapter_queue v4l_adapters;
typedef struct v4l_adapter {
TAILQ_ENTRY(v4l_adapter) va_global_link;
char *va_path;
char *va_identifier;
char *va_displayname;
char *va_devicename;
int va_file;
uint32_t va_logging;
// struct v4l2_capability va_caps;
struct service *va_current_service;
struct service_list va_services;
int va_tally;
/** Receiver thread stuff */
int va_fd;
pthread_t va_thread;
int va_pipe[2];
/** Mpeg stream parsing */
uint32_t va_startcode;
int va_lenlock;
} v4l_adapter_t;
v4l_adapter_t *v4l_adapter_find_by_identifier(const char *identifier);
void v4l_adapter_set_displayname(v4l_adapter_t *va, const char *name);
void v4l_adapter_set_logging(v4l_adapter_t *va, int on);
htsmsg_t *v4l_adapter_build_msg(v4l_adapter_t *va);
service_t *v4l_service_find(v4l_adapter_t *va, const char *id,
int create);
void v4l_init(void);
#endif /* V4L_H */

View file

@ -32,14 +32,13 @@
#define MPEGTS_ONID_NONE 0xFFFF
#define MPEGTS_TSID_NONE 0xFFFF
#define MPEGTS_PSI_SECTION_SIZE 5000
#define MPEGTS_FULLMUX_PID 0x2000
#define MPEGTS_TABLES_PID 0x2001
#define MPEGTS_PID_NONE 0xFFFF
/* Types */
typedef struct mpegts_apid mpegts_apid_t;
typedef struct mpegts_apids mpegts_apids_t;
typedef struct mpegts_table mpegts_table_t;
typedef struct mpegts_psi_section mpegts_psi_section_t;
typedef struct mpegts_network mpegts_network_t;
typedef struct mpegts_mux mpegts_mux_t;
typedef struct mpegts_service mpegts_service_t;
@ -74,44 +73,6 @@ void mpegts_init ( int linuxdvb_mask, str_list_t *satip_client,
str_list_t *tsfiles, int tstuners );
void mpegts_done ( void );
/* **************************************************************************
* PIDs
* *************************************************************************/
struct mpegts_apid {
uint16_t pid;
uint16_t weight;
};
struct mpegts_apids {
mpegts_apid_t *pids;
int alloc;
int count;
int all;
int sorted;
};
int mpegts_pid_init ( mpegts_apids_t *pids );
void mpegts_pid_done ( mpegts_apids_t *pids );
mpegts_apids_t *mpegts_pid_alloc ( void );
void mpegts_pid_destroy ( mpegts_apids_t **pids );
void mpegts_pid_reset ( mpegts_apids_t *pids );
int mpegts_pid_add ( mpegts_apids_t *pids, uint16_t pid, uint16_t weight );
int mpegts_pid_add_group ( mpegts_apids_t *pids, mpegts_apids_t *vals );
int mpegts_pid_del ( mpegts_apids_t *pids, uint16_t pid, uint16_t weight );
int mpegts_pid_del_group ( mpegts_apids_t *pids, mpegts_apids_t *vals );
int mpegts_pid_find_windex ( mpegts_apids_t *pids, uint16_t pid, uint16_t weight );
int mpegts_pid_find_rindex ( mpegts_apids_t *pids, uint16_t pid );
static inline int mpegts_pid_wexists ( mpegts_apids_t *pids, uint16_t pid, uint16_t weight )
{ return pids->all || mpegts_pid_find_windex(pids, pid, weight) >= 0; }
static inline int mpegts_pid_rexists ( mpegts_apids_t *pids, uint16_t pid )
{ return pids->all || mpegts_pid_find_rindex(pids, pid) >= 0; }
int mpegts_pid_copy ( mpegts_apids_t *dst, mpegts_apids_t *src );
int mpegts_pid_compare ( mpegts_apids_t *dst, mpegts_apids_t *src,
mpegts_apids_t *add, mpegts_apids_t *del );
int mpegts_pid_weighted( mpegts_apids_t *dst, mpegts_apids_t *src, int limit );
int mpegts_pid_dump ( mpegts_apids_t *pids, char *buf, int len, int wflag, int raw );
/* **************************************************************************
* Data / SI processing
* *************************************************************************/
@ -127,63 +88,56 @@ struct mpegts_packet
typedef int (*mpegts_table_callback_t)
( mpegts_table_t*, const uint8_t *buf, int len, int tableid );
typedef void (*mpegts_psi_section_callback_t)
( const uint8_t *tsb, size_t len, void *opaque );
struct mpegts_table_mux_cb
{
int tag;
int (*cb) ( mpegts_table_t*, mpegts_mux_t *mm, uint16_t nbid,
int (*cb) ( mpegts_table_t*, mpegts_mux_t *mm,
const uint8_t dtag, const uint8_t *dptr, int dlen );
};
struct mpegts_psi_section
{
int ps_offset;
int ps_lock;
uint8_t ps_data[MPEGTS_PSI_SECTION_SIZE];
};
typedef struct mpegts_table_state
{
int tableid;
uint64_t extraid;
int version;
int complete;
int working;
uint32_t sections[8];
RB_ENTRY(mpegts_table_state) link;
} mpegts_table_state_t;
typedef struct mpegts_pid_sub
{
RB_ENTRY(mpegts_pid_sub) mps_link;
LIST_ENTRY(mpegts_pid_sub) mps_svcraw_link;
#define MPS_NONE 0x00
#define MPS_ALL 0x01
#define MPS_RAW 0x02
#define MPS_STREAM 0x04
#define MPS_SERVICE 0x08
#define MPS_TABLE 0x10
#define MPS_FTABLE 0x20
#define MPS_TABLES 0x40
int mps_type;
#define MPS_WEIGHT_PAT 1000
#define MPS_WEIGHT_CAT 999
#define MPS_WEIGHT_SDT 999
#define MPS_WEIGHT_NIT 999
#define MPS_WEIGHT_BAT 999
#define MPS_WEIGHT_VCT 999
#define MPS_WEIGHT_EIT 999
#define MPS_WEIGHT_PMT 998
#define MPS_WEIGHT_PCR 997
#define MPS_WEIGHT_CA 996
#define MPS_WEIGHT_VIDEO 900
#define MPS_WEIGHT_AUDIO 800
#define MPS_WEIGHT_SUBTITLE 700
#define MPS_WEIGHT_ESOTHER 500
#define MPS_WEIGHT_RAW 400
#define MPS_WEIGHT_NIT2 300
#define MPS_WEIGHT_SDT2 300
#define MPS_WEIGHT_TDT 101
#define MPS_WEIGHT_PMT_SCAN 100
int mps_weight;
void *mps_owner;
#define MPS_NONE 0x0
#define MPS_STREAM 0x1
#define MPS_TABLE 0x2
#define MPS_FTABLE 0x4
int mps_type;
void *mps_owner;
} mpegts_pid_sub_t;
typedef struct mpegts_pid
{
int mp_pid;
int mp_type; // mask for all subscribers
int mp_fd; // linuxdvb demux fd
int8_t mp_cc;
RB_HEAD(,mpegts_pid_sub) mp_subs; // subscribers to pid
LIST_HEAD(,mpegts_pid_sub) mp_svc_subs;
RB_ENTRY(mpegts_pid) mp_link;
} mpegts_pid_t;
struct mpegts_table
{
mpegts_psi_table_t;
/**
* Flags, must never be changed after creation.
* We inspect it without holding global_lock
@ -194,18 +148,12 @@ struct mpegts_table
#define MT_FULL 0x0002
#define MT_QUICKREQ 0x0004
#define MT_FASTSWITCH 0x0008
#define MT_ONESHOT 0x0010
#define MT_RECORD 0x0020
#define MT_SKIPSUBS 0x0040
#define MT_SCANSUBS 0x0080
#define MT_FAST 0x0100
#define MT_SLOW 0x0200
#define MT_DEFER 0x0400
/**
* PID subscription weight
*/
int mt_weight;
#define MT_RECORD 0x0010
#define MT_SKIPSUBS 0x0020
#define MT_SCANSUBS 0x0040
#define MT_FAST 0x0080
#define MT_SLOW 0x0100
#define MT_DEFER 0x0200
/**
* Cycle queue
@ -219,12 +167,20 @@ struct mpegts_table
* File descriptor for filter
*/
LIST_ENTRY(mpegts_table) mt_link;
TAILQ_ENTRY(mpegts_table) mt_defer_link;
mpegts_mux_t *mt_mux;
char *mt_name;
void *mt_opaque;
void *mt_bat;
mpegts_table_callback_t mt_callback;
RB_HEAD(,mpegts_table_state) mt_state;
int mt_complete;
int mt_incomplete;
uint8_t mt_finished;
uint8_t mt_subscribed;
uint8_t mt_defer_cmd;
@ -235,11 +191,22 @@ struct mpegts_table
int mt_count;
int mt_pid;
int mt_id;
int mt_table; // SI table id (base)
int mt_mask; // mask
int mt_destroyed; // Refcounting
int mt_arefcount;
int8_t mt_cc;
tvhlog_limit_t mt_err_log;
mpegts_psi_section_t mt_sect;
struct mpegts_table_mux_cb *mt_mux_cb;
mpegts_service_t *mt_service;
@ -257,11 +224,17 @@ struct mpegts_table
struct mpegts_table_feed {
TAILQ_ENTRY(mpegts_table_feed) mtf_link;
int mtf_len;
uint8_t mtf_tsb[188];
mpegts_mux_t *mtf_mux;
uint8_t mtf_tsb[0];
};
/*
* Assemble SI section
*/
void mpegts_psi_section_reassemble
( mpegts_psi_section_t *ps, const uint8_t *tsb, int crc, int ccerr,
mpegts_psi_section_callback_t cb, void *opaque );
/* **************************************************************************
* Logical network
* *************************************************************************/
@ -310,8 +283,7 @@ struct mpegts_network
void (*mn_display_name) (mpegts_network_t*, char *buf, size_t len);
void (*mn_config_save) (mpegts_network_t*);
mpegts_mux_t* (*mn_create_mux)
(mpegts_network_t*, void *origin, uint16_t onid, uint16_t tsid,
void *conf, int force);
(mpegts_mux_t*, uint16_t onid, uint16_t tsid, void *conf);
mpegts_service_t* (*mn_create_service)
(mpegts_mux_t*, uint16_t sid, uint16_t pmt_pid);
const idclass_t* (*mn_mux_class) (mpegts_network_t*);
@ -321,15 +293,13 @@ struct mpegts_network
* Configuration
*/
uint16_t mn_nid;
uint16_t mn_satip_source;
int mn_autodiscovery;
int mn_skipinitscan;
char *mn_charset;
int mn_idlescan;
int mn_ignore_chnum;
int mn_sid_chnum;
int mn_localtime;
int mn_satpos;
int mn_autoclean_svc;
int mn_autoclean_mux;
};
typedef enum mpegts_mux_scan_state
@ -361,19 +331,6 @@ enum mpegts_mux_epg_flag
};
#define MM_EPG_LAST MM_EPG_ONLY_OPENTV_SKY_AUSAT
enum mpegts_mux_ac3_flag
{
MM_AC3_STANDARD,
MM_AC3_PMT_06,
MM_AC3_PMT_N05,
};
typedef struct tsdebug_packet {
TAILQ_ENTRY(tsdebug_packet) link;
uint8_t pkt[188];
off_t pos;
} tsdebug_packet_t;
/* Multiplex */
struct mpegts_mux
{
@ -388,6 +345,12 @@ struct mpegts_mux
uint16_t mm_onid;
uint16_t mm_tsid;
/*
* Versioning
*/
int64_t mm_created;
int64_t mm_updated;
/*
* Services
*/
@ -413,31 +376,21 @@ struct mpegts_mux
MM_ORIG_AUTO ///< From NIT
} mm_dmc_origin2;
#endif
void *mm_dmc_origin;
mpegts_mux_t *mm_dmc_origin;
time_t mm_dmc_origin_expire;
char *mm_fastscan_muxes;
/*
* Physical instances
*/
LIST_HEAD(, mpegts_mux_instance) mm_instances;
mpegts_mux_instance_t *mm_active;
LIST_HEAD(,service) mm_transports;
/*
* Raw subscriptions
*/
LIST_HEAD(, th_subscription) mm_raw_subs;
mpegts_mux_instance_t *mm_active;
/*
* Data processing
*/
RB_HEAD(, mpegts_pid) mm_pids;
LIST_HEAD(, mpegts_pid_sub) mm_all_subs;
int mm_last_pid;
mpegts_pid_t *mm_last_mp;
@ -461,9 +414,9 @@ struct mpegts_mux
void (*mm_config_save) (mpegts_mux_t *mm);
void (*mm_display_name) (mpegts_mux_t*, char *buf, size_t len);
int (*mm_is_enabled) (mpegts_mux_t *mm);
void (*mm_stop) (mpegts_mux_t *mm, int force, int reason);
int (*mm_start) (mpegts_mux_t *mm, const char *r, int w, int flags);
void (*mm_stop) (mpegts_mux_t *mm, int force);
void (*mm_open_table) (mpegts_mux_t*,mpegts_table_t*,int subscribe);
void (*mm_unsubscribe_table)(mpegts_mux_t*,mpegts_table_t*);
void (*mm_close_table) (mpegts_mux_t*,mpegts_table_t*);
void (*mm_create_instances) (mpegts_mux_t*);
int (*mm_is_epg) (mpegts_mux_t*);
@ -475,48 +428,20 @@ struct mpegts_mux
int mm_enabled;
int mm_epg;
char *mm_charset;
int mm_pmt_ac3;
/*
* TSDEBUG
*/
#if ENABLE_TSDEBUG
int mm_tsdebug_fd;
int mm_tsdebug_fd2;
off_t mm_tsdebug_pos;
TAILQ_HEAD(, tsdebug_packet) mm_tsdebug_packets;
#endif
int mm_pmt_06_ac3;
};
#define PREFCAPID_OFF 0
#define PREFCAPID_ON 1
#define PREFCAPID_FORCE 2
/* Service */
struct mpegts_service
{
service_t; // Parent
int (*s_update_pids)(mpegts_service_t *t, struct mpegts_apids *pids);
int (*s_link)(mpegts_service_t *master, mpegts_service_t *slave);
int (*s_unlink)(mpegts_service_t *master, mpegts_service_t *slave);
int s_dvb_subscription_flags;
mpegts_apids_t *s_pids;
LIST_HEAD(, mpegts_service) s_masters;
LIST_ENTRY(mpegts_service) s_masters_link;
LIST_HEAD(, mpegts_service) s_slaves;
LIST_ENTRY(mpegts_service) s_slaves_link;
mpegts_apids_t *s_slaves_pids;
/*
* Fields defined by DVB standard EN 300 468
*/
uint32_t s_dvb_channel_num;
uint16_t s_dvb_channel_minor;
uint8_t s_dvb_channel_dtag;
uint16_t s_dvb_service_id;
char *s_dvb_svcname;
char *s_dvb_provider;
@ -527,9 +452,12 @@ struct mpegts_service
uint16_t s_dvb_prefcapid;
int s_dvb_prefcapid_lock;
uint16_t s_dvb_forcecaid;
time_t s_dvb_created;
time_t s_dvb_last_seen;
time_t s_dvb_check_seen;
/*
* History
*/
int64_t s_dvb_created;
int64_t s_dvb_updated;
/*
* EIT/EPG control
@ -537,7 +465,6 @@ struct mpegts_service
int s_dvb_eit_enable;
uint64_t s_dvb_opentv_chnum;
uint16_t s_dvb_opentv_id;
/*
* Link to carrying multiplex and active adapter
@ -558,7 +485,6 @@ struct mpegts_service
* in order to recude load.
*/
sbuf_t s_tsbuf;
time_t s_tsbuf_last;
/**
* Average continuity errors
@ -585,9 +511,10 @@ struct mpegts_service
/* Physical mux instance */
struct mpegts_mux_instance
{
tvh_input_instance_t;
idnode_t mmi_id;
LIST_ENTRY(mpegts_mux_instance) mmi_mux_link;
LIST_ENTRY(mpegts_mux_instance) mmi_input_link;
LIST_ENTRY(mpegts_mux_instance) mmi_active_link;
streaming_pad_t mmi_streaming_pad;
@ -595,7 +522,13 @@ struct mpegts_mux_instance
mpegts_mux_t *mmi_mux;
mpegts_input_t *mmi_input;
LIST_HEAD(,th_subscription) mmi_subs;
tvh_input_stream_stats_t mmi_stats;
int mmi_tune_failed;
void (*mmi_delete) (mpegts_mux_instance_t *mmi);
};
struct mpegts_mux_sub
@ -624,13 +557,11 @@ struct mpegts_input
int mi_initscan;
int mi_idlescan;
char *mi_linked;
LIST_ENTRY(mpegts_input) mi_global_link;
mpegts_network_link_list_t mi_networks;
LIST_HEAD(,tvh_input_instance) mi_mux_instances;
LIST_HEAD(,mpegts_mux_instance) mi_mux_instances;
/*
@ -643,6 +574,7 @@ struct mpegts_input
*/
uint8_t mi_running; /* threads running */
uint8_t mi_live; /* stream is live */
time_t mi_last_dispatch;
/* Data input */
@ -659,6 +591,7 @@ struct mpegts_input
/* Active sources */
LIST_HEAD(,mpegts_mux_instance) mi_mux_active;
LIST_HEAD(,service) mi_transports;
/* Table processing */
pthread_t mi_table_tid;
@ -676,22 +609,22 @@ struct mpegts_input
int (*mi_is_enabled) (mpegts_input_t*, mpegts_mux_t *mm, int flags);
void (*mi_enabled_updated)(mpegts_input_t*);
void (*mi_display_name) (mpegts_input_t*, char *buf, size_t len);
int (*mi_get_weight) (mpegts_input_t*, mpegts_mux_t *mm, int flags);
int (*mi_is_free) (mpegts_input_t*);
int (*mi_get_weight) (mpegts_input_t*, int flags);
int (*mi_get_priority) (mpegts_input_t*, mpegts_mux_t *mm, int flags);
int (*mi_get_grace) (mpegts_input_t*, mpegts_mux_t *mm);
int (*mi_warm_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
int (*mi_start_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
void (*mi_stop_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
void (*mi_open_service) (mpegts_input_t*,mpegts_service_t*,int flags, int first);
void (*mi_open_service) (mpegts_input_t*,mpegts_service_t*,int first);
void (*mi_close_service) (mpegts_input_t*,mpegts_service_t*);
mpegts_pid_t *(*mi_open_pid)(mpegts_input_t*,mpegts_mux_t*,int,int,int,void*);
int (*mi_close_pid) (mpegts_input_t*,mpegts_mux_t*,int,int,int,void*);
mpegts_pid_t *(*mi_open_pid)(mpegts_input_t*,mpegts_mux_t*,int,int,void*);
void (*mi_close_pid) (mpegts_input_t*,mpegts_mux_t*,int,int,void*);
void (*mi_create_mux_instance) (mpegts_input_t*,mpegts_mux_t*);
void (*mi_started_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
void (*mi_stopping_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
void (*mi_stopped_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
int (*mi_has_subscription) (mpegts_input_t*, mpegts_mux_t *mm);
void (*mi_tuning_error) (mpegts_input_t*,mpegts_mux_t *);
idnode_set_t *(*mi_network_list) (mpegts_input_t*);
};
@ -739,7 +672,7 @@ int mpegts_input_set_networks ( mpegts_input_t *mi, htsmsg_t *msg );
int mpegts_input_add_network ( mpegts_input_t *mi, mpegts_network_t *mn );
void mpegts_input_open_service ( mpegts_input_t *mi, mpegts_service_t *s, int flags, int init );
void mpegts_input_open_service ( mpegts_input_t *mi, mpegts_service_t *s, int init );
void mpegts_input_close_service ( mpegts_input_t *mi, mpegts_service_t *s );
void mpegts_input_status_timer ( void *p );
@ -754,8 +687,6 @@ int mpegts_input_class_network_set ( void *o, const void *p );
htsmsg_t *mpegts_input_class_network_enum ( void *o );
char *mpegts_input_class_network_rend ( void *o );
int mpegts_mps_cmp( mpegts_pid_sub_t *a, mpegts_pid_sub_t *b );
void mpegts_network_register_builder
( const idclass_t *idc,
mpegts_network_t *(*build)(const idclass_t *idc, htsmsg_t *conf) );
@ -787,7 +718,6 @@ void mpegts_network_delete ( mpegts_network_t *mn, int delconf );
int mpegts_network_set_nid ( mpegts_network_t *mn, uint16_t nid );
int mpegts_network_set_network_name ( mpegts_network_t *mn, const char *name );
void mpegts_network_scan ( mpegts_network_t *mn );
mpegts_mux_t *mpegts_mux_create0
( mpegts_mux_t *mm, const idclass_t *class, const char *uuid,
@ -812,8 +742,6 @@ void mpegts_mux_delete ( mpegts_mux_t *mm, int delconf );
void mpegts_mux_save ( mpegts_mux_t *mm, htsmsg_t *c );
void mpegts_mux_tuning_error( const char *mux_uuid, mpegts_mux_instance_t *mmi_match );
mpegts_mux_instance_t *mpegts_mux_instance_create0
( mpegts_mux_instance_t *mmi, const idclass_t *class, const char *uuid,
mpegts_input_t *mi, mpegts_mux_t *mm );
@ -824,9 +752,6 @@ mpegts_service_t *mpegts_mux_find_service(mpegts_mux_t *ms, uint16_t sid);
(struct type*)mpegts_mux_instance_create0(calloc(1, sizeof(struct type)),\
&type##_class, uuid,\
mi, mm);
void mpegts_mux_instance_delete ( tvh_input_instance_t *tii );
int mpegts_mux_instance_start
( mpegts_mux_instance_t **mmiptr );
@ -837,16 +762,12 @@ int mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid );
int mpegts_mux_set_crid_authority ( mpegts_mux_t *mm, const char *defauth );
void mpegts_mux_open_table ( mpegts_mux_t *mm, mpegts_table_t *mt, int subscribe );
void mpegts_mux_unsubscribe_table ( mpegts_mux_t *mm, mpegts_table_t *mt );
void mpegts_mux_close_table ( mpegts_mux_t *mm, mpegts_table_t *mt );
void mpegts_mux_remove_subscriber(mpegts_mux_t *mm, th_subscription_t *s, int reason);
int mpegts_mux_subscribe(mpegts_mux_t *mm, mpegts_input_t *mi,
const char *name, int weight, int flags);
int mpegts_mux_subscribe(mpegts_mux_t *mm, const char *name, int weight, int flags);
void mpegts_mux_unsubscribe_by_name(mpegts_mux_t *mm, const char *name);
void mpegts_mux_unsubscribe_linked(mpegts_input_t *mi);
void mpegts_mux_scan_done ( mpegts_mux_t *mm, const char *buf, int res );
void mpegts_mux_bouquet_rescan ( const char *src, const char *extra );
@ -873,7 +794,9 @@ void mpegts_input_recv_packets
(mpegts_input_t *mi, mpegts_mux_instance_t *mmi, sbuf_t *sb,
int64_t *pcr, uint16_t *pcr_pid);
int mpegts_input_get_weight ( mpegts_input_t *mi, mpegts_mux_t *mm, int flags );
int mpegts_input_is_free ( mpegts_input_t *mi );
int mpegts_input_get_weight ( mpegts_input_t *mi, int flags );
int mpegts_input_get_priority ( mpegts_input_t *mi, mpegts_mux_t *mm, int flags );
int mpegts_input_get_grace ( mpegts_input_t *mi, mpegts_mux_t *mm );
@ -882,35 +805,10 @@ void mpegts_input_save ( mpegts_input_t *mi, htsmsg_t *c );
void mpegts_input_flush_mux ( mpegts_input_t *mi, mpegts_mux_t *mm );
mpegts_pid_t * mpegts_input_open_pid
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, int weight, void *owner );
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner );
int mpegts_input_close_pid
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, int weight, void *owner );
void mpegts_input_close_pids
( mpegts_input_t *mi, mpegts_mux_t *mm, void *owner, int all );
static inline void
tsdebug_write(mpegts_mux_t *mm, uint8_t *buf, size_t len)
{
#if ENABLE_TSDEBUG
if (mm->mm_tsdebug_fd2 >= 0)
if (write(mm->mm_tsdebug_fd2, buf, len) != len)
tvherror("tsdebug", "unable to write input data (%i)", errno);
#endif
}
static inline ssize_t
sbuf_tsdebug_read(mpegts_mux_t *mm, sbuf_t *sb, int fd)
{
#if ENABLE_TSDEBUG
ssize_t r = sbuf_read(sb, fd);
tsdebug_write(mm, sb->sb_data + sb->sb_ptr - r, r);
return r;
#else
return sbuf_read(sb, fd);
#endif
}
void mpegts_input_close_pid
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner );
void mpegts_table_dispatch
(const uint8_t *sec, size_t r, void *mt);
@ -922,7 +820,7 @@ static inline void mpegts_table_grab
}
void mpegts_table_release_
(mpegts_table_t *mt);
static inline int mpegts_table_release
static inline void mpegts_table_release
(mpegts_table_t *mt)
{
int v = atomic_dec(&mt->mt_arefcount, 1);
@ -930,51 +828,20 @@ static inline int mpegts_table_release
if (v == 1) {
assert(mt->mt_destroyed == 1);
mpegts_table_release_(mt);
return 1;
}
return 0;
}
int mpegts_table_type
( mpegts_table_t *mt );
mpegts_table_t *mpegts_table_add
(mpegts_mux_t *mm, int tableid, int mask,
mpegts_table_callback_t callback, void *opaque,
const char *name, int flags, int pid, int weight);
const char *name, int flags, int pid);
void mpegts_table_flush_all
(mpegts_mux_t *mm);
void mpegts_table_destroy ( mpegts_table_t *mt );
void mpegts_table_consistency_check( mpegts_mux_t *mm );
void dvb_bat_destroy
(struct mpegts_table *mt);
int dvb_pat_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_cat_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_pmt_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tabelid);
int dvb_nit_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_bat_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_fs_sdt_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_sdt_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_tdt_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_tot_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int atsc_vct_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
void psi_tables_default ( struct mpegts_mux *mm );
void psi_tables_dvb ( struct mpegts_mux *mm );
void psi_tables_atsc_t ( struct mpegts_mux *mm );
void psi_tables_atsc_c ( struct mpegts_mux *mm );
mpegts_service_t *mpegts_service_create0
( mpegts_service_t *ms, const idclass_t *class, const char *uuid,
mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid, htsmsg_t *conf );
@ -987,20 +854,14 @@ mpegts_service_t *mpegts_service_create0
mpegts_service_create0(calloc(1, sizeof(mpegts_service_t)),\
&mpegts_service_class, u, m, s, p, c)
mpegts_service_t *mpegts_service_create_raw(mpegts_mux_t *mm);
mpegts_service_t *mpegts_service_find
( mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid, int create, int *save );
mpegts_service_t *
mpegts_service_find_by_pid ( mpegts_mux_t *mm, int pid );
static inline mpegts_service_t *mpegts_service_find_by_uuid(const char *uuid)
{ return idnode_find(uuid, &mpegts_service_class, NULL); }
void mpegts_service_delete ( service_t *s, int delconf );
/*
* MPEG-TS event handler
*/
@ -1010,7 +871,7 @@ typedef struct mpegts_listener
LIST_ENTRY(mpegts_listener) ml_link;
void *ml_opaque;
void (*ml_mux_start) (mpegts_mux_t *mm, void *p);
void (*ml_mux_stop) (mpegts_mux_t *mm, void *p, int reason);
void (*ml_mux_stop) (mpegts_mux_t *mm, void *p);
void (*ml_mux_create) (mpegts_mux_t *mm, void *p);
void (*ml_mux_delete) (mpegts_mux_t *mm, void *p);
} mpegts_listener_t;
@ -1030,13 +891,6 @@ LIST_HEAD(,mpegts_listener) mpegts_listeners;
if (ml->op) ml->op(t, ml->ml_opaque);\
} (void)0
#define mpegts_fire_event1(t, op, arg1)\
{\
mpegts_listener_t *ml;\
LIST_FOREACH(ml, &mpegts_listeners, ml_link)\
if (ml->op) ml->op(t, ml->ml_opaque, arg1);\
} (void)0
#endif /* __TVH_MPEGTS_H__ */
/******************************************************************************

Some files were not shown because too many files have changed in this diff Show more