diff --git a/Autobuild.sh b/Autobuild.sh new file mode 100755 index 00000000..3593a02c --- /dev/null +++ b/Autobuild.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# +# Entry point for the Doozer autobuild system +# +# (c) Andreas Öman 2011. All rights reserved. +# +# + +set -eu + +BUILD_API_VERSION=2 +EXTRA_BUILD_NAME="" +JARGS="" +JOBSARGS="" +TARGET="" +RELEASE="--release" +WORKINGDIR="/var/tmp/showtime-autobuild" +UPLOAD_BUILD_ARTIFACTS=1 +while getopts "vht:e:j:w:R" OPTION +do + case $OPTION in + v) + echo $BUILD_API_VERSION + exit 0 + ;; + R) + UPLOAD_BUILD_ARTIFACTS=0 + ;; + h) + echo "This script is intended to be used by the autobuild system only" + exit 0 + ;; + t) + TARGET="$OPTARG" + ;; + e) + EXTRA_BUILD_NAME="$OPTARG" + ;; + j) + JOBSARGS="--jobs=$OPTARG" + JARGS="-j$OPTARG" + ;; + w) + WORKINGDIR="$OPTARG" + ;; + esac +done + + +if [[ -z $TARGET ]]; then + echo "target (-t) not specified" + exit 1 +fi + +# +# $1 = local file path +# $2 = type +# $3 = content-type +# $4 = filename +# +artifact() { + if [ $UPLOAD_BUILD_ARTIFACTS -eq 1 ]; then + echo "doozer-artifact:$PWD/$1:$2:$3:$4" + else + echo "Ignoring: $1:$2:$3:$4" + fi +} + +if [ -f Autobuild/${TARGET}.sh ]; then + source Autobuild/${TARGET}.sh +else + echo "target $TARGET not supported" + exit 1 +fi diff --git a/Autobuild/debian.sh b/Autobuild/debian.sh new file mode 100755 index 00000000..587eac69 --- /dev/null +++ b/Autobuild/debian.sh @@ -0,0 +1,26 @@ +CHANGELOG=debian/changelog +NOW=`date -R` +VER=`git describe | sed "s/\([0-9]*\)\.\([0-9]*\)-\([0-9]*\)-.*/\1.\2.\3/"` +echo >${CHANGELOG} "tvheadend (${VER}) unstable; urgency=low" +echo >>${CHANGELOG} +echo >>${CHANGELOG} " * The full changelog can be found at " +echo >>${CHANGELOG} " http://www.lonelycoder.com/tvheadend/download" +echo >>${CHANGELOG} +echo >>${CHANGELOG} " -- Andreas Öman ${NOW}" +cat ${CHANGELOG} +export JOBSARGS +export JARGS +dpkg-buildpackage -b -us -uc + +for a in ../tvheadend*${VER}*.deb; do + artifact $a deb application/x-deb `basename $a` + rm -f $a +done + +for a in ../tvheadend*${VER}*.changes; do + artifact $a changes text/plain `basename $a` + rm -f $a +done + +rm -f ${CHANGELOG} +dh_clean diff --git a/Autobuild/ubuntu-lucid-amd64.sh b/Autobuild/ubuntu-lucid-amd64.sh new file mode 100755 index 00000000..606932b3 --- /dev/null +++ b/Autobuild/ubuntu-lucid-amd64.sh @@ -0,0 +1 @@ +source Autobuild/debian.sh diff --git a/Autobuild/ubuntu-lucid-i386.sh b/Autobuild/ubuntu-lucid-i386.sh new file mode 100755 index 00000000..606932b3 --- /dev/null +++ b/Autobuild/ubuntu-lucid-i386.sh @@ -0,0 +1 @@ +source Autobuild/debian.sh diff --git a/ChangeLog b/ChangeLog deleted file mode 120000 index d526672c..00000000 --- a/ChangeLog +++ /dev/null @@ -1 +0,0 @@ -debian/changelog \ No newline at end of file diff --git a/Makefile b/Makefile index 7152da6f..8a01bf40 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,8 @@ CFLAGS += -Wall -Werror -Wwrite-strings -Wno-deprecated-declarations CFLAGS += -Wmissing-prototypes LDFLAGS += -lrt -ldl +BUNDLES += docs/html docs/docresources src/webui/static + # # Core # @@ -147,23 +149,18 @@ SLIBS += $(SLIBS-yes) OBJS= $(SRCS:%.c=$(BUILDDIR)/%.o) OBJS_EXTRA = $(SRCS_EXTRA:%.c=$(BUILDDIR)/%.so) DEPS= ${OBJS:%.o=%.d} -OBJDIRS= $(sort $(dir $(OBJS))) $(sort $(dir $(OBJS_EXTRA))) # File bundles BUNDLE_SRCS=$(BUNDLES:%=$(BUILDDIR)/bundles/%.c) BUNDLE_DEPS=$(BUNDLE_SRCS:%.c=%.d) BUNDLE_OBJS=$(BUNDLE_SRCS:%.c=%.o) -OBJDIRS+= $(sort $(dir $(BUNDLE_OBJS))) .PRECIOUS: ${BUNDLE_SRCS} -VERSION=$(shell support/version.sh) -CURVERSION=$(shell cat ${BUILDDIR}/ver || echo "0") # Common CFLAGS for all files CFLAGS_com = -g -funsigned-char -O2 CFLAGS_com += -D_FILE_OFFSET_BITS=64 CFLAGS_com += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR} -CFLAGS_com += -DHTS_VERSION=\"$(VERSION)\" MKBUNDLE = $(CURDIR)/support/mkbundle @@ -180,31 +177,41 @@ all: ${PROG} .PHONY: clean distclean -${PROG}: $(OBJDIRS) $(OBJS) $(BUNDLE_OBJS) ${OBJS_EXTRA} Makefile - $(CC) -o $@ $(OBJS) $(BUNDLE_OBJS) $(LDFLAGS) ${LDFLAGS_cfg} +# +# +# +${PROG}: $(OBJS) $(ALLDEPS) support/dataroot/wd.c + $(CC) -o $@ $(OBJS) support/dataroot/wd.c $(LDFLAGS) ${LDFLAGS_cfg} -$(OBJDIRS): - @mkdir -p $@ +${PROG}.bundle: $(OBJS) $(BUNDLE_OBJS) $(ALLDEPS) support/dataroot/bundle.c + $(CC) -o $@ $(OBJS) support/dataroot/bundle.c $(BUNDLE_OBJS) $(LDFLAGS) ${LDFLAGS_cfg} +${PROG}.datadir: $(OBJS) $(ALLDEPS) support/dataroot/datadir.c + $(CC) -o $@ $(OBJS) -iquote${BUILDDIR} support/dataroot/datadir.c $(LDFLAGS) ${LDFLAGS_cfg} + +# +# +# ${BUILDDIR}/%.o: %.c + @mkdir -p $(dir $@) $(CC) -MD -MP $(CFLAGS_com) $(CFLAGS) $(CFLAGS_cfg) -c -o $@ $(CURDIR)/$< ${BUILDDIR}/%.so: ${SRCS_EXTRA} + @mkdir -p $(dir $@) ${CC} -O -fbuiltin -fomit-frame-pointer -fPIC -shared -o $@ $< -ldl clean: - rm -rf ${BUILDDIR}/src ${BUILDDIR}/bundles ${BUILDDIR}/ver + rm -rf ${BUILDDIR}/src ${BUILDDIR}/bundles find . -name "*~" | xargs rm -f distclean: clean rm -rf build.* -ifneq ($(VERSION), $(CURVERSION)) -.PHONY: src/version.c -$(info Version changed) -src/version.c: - @echo $(VERSION) >${BUILDDIR}/ver -endif +# Create buildversion.h +src/version.c: $(BUILDDIR)/buildversion.h +$(BUILDDIR)/buildversion.h: FORCE + @$(CURDIR)/support/version.sh $(CURDIR) $@ +FORCE: # Include dependency files if they exist. @@ -215,7 +222,9 @@ include support/${OSENV}.mk # Bundle files $(BUILDDIR)/bundles/%.o: $(BUILDDIR)/bundles/%.c + @mkdir -p $(dir $@) $(CC) -I${CURDIR}/src -c -o $@ $< $(BUILDDIR)/bundles/%.c: % + @mkdir -p $(dir $@) $(MKBUNDLE) -o $@ -s $< -d ${BUILDDIR}/bundles/$<.d -p $< -z diff --git a/README b/README index 2893338c..d10ca7b3 100644 --- a/README +++ b/README @@ -1,4 +1,3 @@ - Tvheadend TV streaming server ============================= @@ -21,7 +20,7 @@ $ make Build the binary, after build the binary resides in 'build.Linux/'. Thus, to start it, just type: -$ build.Linux/tvheadend +$ build.linux/tvheadend Settings are stored in $HOME/.hts/tvheadend diff --git a/configure b/configure index 7c01c60c..651b9c37 100755 --- a/configure +++ b/configure @@ -11,14 +11,16 @@ # Copyright (c) 2005-2008 Mans Rullgard # -PLATFORM=`uname` - +PLATFORM=linux source support/configure.inc CPU=generic ARCH=`uname -m` OSENV="posix" -PREFIX=/usr/local +prefix=/usr/local +bindir="\${prefix}/bin" +mandir="\${prefix}/share/man" +datadir="\${prefix}/share/tvheadend" show_help(){ echo "Usage: configure [options]" @@ -26,7 +28,11 @@ show_help(){ echo echo "Standard options:" echo " --help print this message" - echo " --prefix=PREFIX install in PREFIX [$PREFIX]" + echo " --prefix=PREFIX Install in PREFIX [$prefix]" + echo " --bindir=DIR Install binaries in DIR [$bindir]" + echo " --mandir=DIR Install man pages in DIR [$mandir]" + echo " --datadir=DIR Install data files in DIR [$datadir]" + echo echo " --arch=arch Build for this architecture [$ARCH]" echo " --cpu=cpu Build and optimize for specific CPU" echo " --cc=CC Build using the given compiler" @@ -42,8 +48,6 @@ enable v4l for opt do optval="${opt#*=}" case "$opt" in - --prefix=*) PREFIX="$optval" - ;; --cpu=*) CPU="$optval" ;; --help) show_help @@ -53,6 +57,33 @@ for opt do ;; --cc=*) CC="$optval" ;; + --prefix=*) + prefix="$optval" + ;; + --bindir=*) + bindir="$optval" + ;; + --mandir=*) + mandir="$optval" + ;; + --datadir=*) + datadir="$optval" + ;; + --includedir=*) + ;; + --infodir=*) + ;; + --sysconfdir=*) + ;; + --localstatedir=*) + ;; + --libexecdir=*) + ;; + --disable-maintainer-mode) + ;; + --disable-dependency-tracking) + ;; + --enable-?*|--disable-?*) eval $(echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g') $action $option @@ -154,38 +185,32 @@ else die fi -# -# Configure paths, etc -# -if [ ${RELEASE} != yes ]; then - echo NOTE: - echo NOTE: Development build. - echo NOTE: The generated binary will contained compild-in paths to - echo NOTE: the current build tree. If you plan to install or move - echo NOTE: the binary, please reconfigure with '--release'. - echo NOTE: - - cat >> ${CONFIG_H} << EOF -#define TVHEADEND_CONTENT_PATH "${TOPDIR}" -EOF - -else - - echo >>${CONFIG_MAK} "BUNDLES += docs/html docs/docresources src/webui/static" - - cat >> ${CONFIG_H} << EOF -#define TVHEADEND_CONTENT_PATH NULL -EOF -fi - # # Finalize # cat >> ${CONFIG_MAK} << EOF ARCH=$ARCH -INSTALLPREFIX=$PREFIX LDFLAGS_cfg += -lpthread EOF +# +# Finalize +# +echo +echo "Installation paths:" +echo " Prefix: ${prefix}" +echo " Binaries: ${bindir}" +echo " Man pages: ${mandir}" +echo " Data files: ${datadir}" +echo +echo >>${CONFIG_MAK} "prefix=$prefix" +echo >>${CONFIG_MAK} "bindir=\${DESTDIR}$bindir" +echo >>${CONFIG_MAK} "mandir=\${DESTDIR}$mandir" +echo >>${CONFIG_MAK} "datadir=\${DESTDIR}$datadir" +echo >>${CONFIG_MAK} LDFLAGS_cfg += -lpthread -lrt -ldl +eval datadirpath=${datadir} +echo >>${CONFIG_H} "#define TVHEADEND_DATADIR \"${datadirpath}\"" + + finalize diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 86bc0ec1..00000000 --- a/debian/changelog +++ /dev/null @@ -1,472 +0,0 @@ -tvheadend (2.12.99) hts; urgency=low - - * Debian package has been renamed from hts-tvheadend to tvheadend - - * Add functionality to delete recordings - - * Better support for playing in web clients. - Temporary tickets are used instead of username/password authentication - - * Remove lock contention in CWC updates - - * Fix bug in IPTV PAT parser (Ticket #318) - - * Store EPG on disk so it can be reloaded on restart - - * Add support for Viaccess EMM - - * Add support for DRECrypt EMM - - * Depend on OpenSSL for cryptographic features - - * RTSP has been dropped. It was too buggy and noone wanted to maintain it - - * Fix bug in JSON encoder. Ticket #163 - - * Fix crash in HTTP service. Ticket #334 - - * Added http-streaming of services. Tested and working with mplayer. - - * Add support for building RPM packages - - -- Andreas Öman Sat, 19 Feb 2011 12:57:09 +0100 - -hts-tvheadend (2.12) hts; urgency=low - - * Add support for IPTV over IPv6 - - * Use a native matroska mutiplixer to get better support for metadata, etc - - * Add support for multiple DVR configurations (can be set per recording) - - * Add suport for teletext subtitles - - * Add support for E-AC3 - - * Workaround for ITV HD (which has incorrect PMT) - Ticket #215 - - * Keep track of video width and height and send it over HTSP in - subscriptionStart. - Ticket #208 - - * Add support for Logical Channel Number descriptor (0x83) - Ticket #138 - - * Maintain the order of PIDs as they appear in the PMT. - Ticket #218 - - * When there is no video pid (service type is Radio), - start the audio stream right away. - Ticket #59 - - * Add subscriptionChangeWeight HTSP method. - - * psi: Assume that elementary stream type 0x81 is AC3 without - relying on any further descriptors - Ticket #217 - - * Tvheadend no longer depends on FFmpeg so it's not externally updated - in svn, nor linked. - - * Added a native matroska muxer. - Correctly writes global headers. Ticket #61 - Add support for recording AAC audio. Ticket #160 - Improves metadata written to files. - - * Tvheadend no longer tries to fixup lack of PTS/DTS for packets - before sending them over HTSP. Rather, those fields will be left - out in the message. Currently, all HTSP clients known to mankind - handles this. - - * Make error detection in timestamp parsing much more robust. Avoid - extracting persistent data (such as video width*height) from - received data if we suspect that an error has occured (MPEG-TS TEI - or continuity counter errors). - - -- Andreas Öman Mon, 29 Nov 2010 20:06:52 +0100 - -hts-tvheadend (2.11) hts; urgency=low - - * Add support for filtering autorecordings based on weekdays. - - * Add circular 10.75 GHz LNB - - * Add a new DVB adapter option that allows the entire DVB Mux to be - written to disk as Tvheadend tunes to it. This should aid a lot when - it comes to debugging - - * Inject entries in DVR schedule as soon as we know about an EPG - entry that matches an autorecording rule. Previously Tvheadend - would scan the EPG continously and just grab shows as they neared - air time. The drawbacks of this approach was that it's a bit hard - to understand what is happening. It also makes (more) correct - wakeup from suspend hard to do. - - * Add parsing of episode information from XMLTV and display it in the - WebUI EPG - - * A bug caused the 'Automatic Recorder' tab grid not to reload - entries upon external change (Such as when a channel got deleted - or when a new auto recording was created from the EPG view). This - is now fixed. - - * Add support for storing recorded events in a directory named after - the event. Ticket #150 - - * Add support for appending season and episode numbers to filenames - during recording. - - * Prior to this release the color (i.e. connection status) of the - CWC entries were not correctly updated when a connection was established - or lost. This is now fixed. Ticket #144 - - * Add support for prioritized recordings. The user can chose among five - different priorities. - - * Fix various issues realted to the Recording Schedule user interface. - The UI now includes better visual feedback on what's going on and if - there are errors. Also recordings that do not complete successfully - are correctly reported as failed entries. Ticket #131 - - * Add support for Irdeto and Seca EMM - - -- Andreas Öman Sat, 27 Mar 2010 10:03:40 +0100 - -hts-tvheadend (2.10) hts; urgency=high - - * Fix a crash in HTSP server. - - -- Andreas Öman Thu, 04 Feb 2010 22:21:22 +0100 - -hts-tvheadend (2.9) hts; urgency=high - - * A last minute bug slipped in the 2.8 release, causing Tvheadend to read - settings and configuration from incorrect path when also switching user - during startup. This has now been fixed. - - -- Andreas Öman Mon, 25 Jan 2010 21:23:47 +0100 - -hts-tvheadend (2.8) hts; urgency=low - - * Added support for AAC audio - - * Added support for manual creation of DVR entries. Prior to this it - was only possible to schedule a recording based on EPG entries. - - * It's now possible to enable/disable the signal quality monitor per - adapter. This is now default off as it seem to mostly cause problems - for people and it's only useful if you have complex setups with - multiple adapters, etc. - - * Improve logging when subscriptions fail to start - - * More intelligent arbitration amongst all sources when a subscription - is about to start for a channel. If we cannot descramble or if no - input is detected, skip to next source. - Fixes ticket #89 - - * Add option for editing channel icon URL in the channel config tab. - - * Fix support for Irdeto ECM - - * Automatically detect if a connected USB DVB adapter is only Full speed - (ie 12Mbit/s), and if so, limit so Tvheadend only can stream one service - at the time from the tuned frequency. - - * Add support for extracting provider in the PMT for SECA/Mediaguard - - * Add support for NIT-other tables. - In particular useful for Ziggo DVB-C networks in the Netherlands. - - * Fix various bugs related to RTP encapsulated IPTV - - * Tvheadend now support placement of configurations and settings at any path. - Use the '-c' command line option for this. By default Tvheadend puts - configuration at $HOME/.hts/tvheadend - - * Make it possible to enable debug level log in the web interface. - Press the small top-right gear icon in the log console to enable/disable - debug log. - - -- Andreas Öman Mon, 25 Jan 2010 21:23:47 +0100 - -hts-tvheadend (2.7) hts; urgency=low - - * Added support for DVB subtitles. Currently only forwarded over HTSP - - * Add support for reception of RTP encapsulated IPTV - - * Fixed a crash caused by a race condition when starting a subscription - - * Increase maximum packet size for IPTV to 65536 bytes - - -- Andreas Öman Sun, 06 Dec 2009 20:40:41 +0100 - -hts-tvheadend (2.6) hts; urgency=low - - * Tvheadend's DVB service probe will now make the channel join tags based - on the service type and DVB provider name. - - * Add support for tuning on ATSC adapters - - * Add support for sattelite beams with circular polarisation - - * If a user changed the XMLTV source for a channel to 'None', tvheadend - would crash. This is now fixed. - - * Add support for copying a selection of muxes from one DVB adapter to - another in the web UI. - - * Support for DVB-S2 has been added - - * Default character encoding in DVB is ISO6937, not Latin-1. Ticket #96 - - * Add support for DiSEqC 1.1 / 2.1, configured on per-adapter basis. - Ticket #99 - - * Increase logging and include various stats to XMLTV grab - - * Add support for configuring a per-channel pre/post extra time for - scheduled recordings. Ticket #104 - - * Autodetect CPU capabilities and choose best mode for CSA descrambler. - Ticket #122 - - -- Andreas Öman Sun, 29 Nov 2009 14:02:36 +0100 - -hts-tvheadend (2.5) hts; urgency=low - - * If a previosly detected DVB adapter was not present during startup, - Tvheadend would crash. This is now fixed. - - * If XMLTV grabbing was disabled tvheadend would spin in a 100% CPU loop. - This is now fixed. - - * The HTSP service is now announced via AVAHI (mDNS service discovery) - - * Support for IPTV has been added. Only RAW TS in UDP is supported - at the moment. - - * Add support for setting default extra times to add before and after - a recording. Useful if your TV shows never start on time. - - * Use FFmpeg's av_interleaved_write_frame() or some players can't - play the recorded content (vlc is one) - - -- Andreas Öman Wed, 19 Aug 2009 22:28:02 +0200 - -hts-tvheadend (2.4) hts; urgency=low - - * Due to a bug, the polarisation of DVB-S muxes was not correctly - stored internally. This has now been fixed. - - * Fix compilation on 64bit systems - - * Make sure channel <> service mappings are saved when edited from - DVB service grid - - * Add support for merging one channel into another. - Useful if you have multiple providers offering the same DVB service - but with different names. - - * The embedded HTTP server now logs failed requests - - * Use absolute paths when serving static content (the web app itself) - This was only a problem when fork()ing a development build - - * Rework the CWC reconnection strategy. - If there are active subscription, Tvheadend will attempt to reconnect - immediately and then retry every three seconds. - If no subscription is active a reconnection attempt is performed - every minute. - Also, if any CWC configuration changes are made from the UI, Tvheadend - will try to reconnect directly. - - * If the EPG receives an updated description for an even that is shorter - than the current description it will be ignored. - This typically happens if the XMLTV and DVB EIT (Event Information Table) - differs. In other words, we assume that a longer description of an - event is better than a short. - - -- Andreas Öman Sun, 26 Jul 2009 13:27:32 +0200 - -hts-tvheadend (2.3) hts; urgency=low - - * A simple web interface has been added. To access it, visit - - http://host:9981/simple.html - - This web interface is designed to be really tiny with only - a simple search field and options to record found shows. - Preferably used from handheld devices. - - * All sensitive information (passwords, etc) are hidden from display - in the web ui. The text will still be displayed in cleartext when - editing. - - * Redesigned the DVB configuration tab in the web userinterface: - - Each adapter have three (or four) tabs - o General setup and information - o Grid of multiplexes - o Grid of services - o For sattelite adapters, a sattelite configuration tab. - - * Add support for disabling / enabling an entire DVB multiplex - - * Add support for multiple DiSEqC switchports on a single adapter - - * Add support for different sattelite LNBs - - * Graceful handling of DVB adapters that does not support many - table filters in hardware. Tvheadend will rotate among the available - ones. - - * Add support for enabling / disabling transports from the DVB configuration - - * Make it possible to remove DVB multiplexes from the web ui - - * Add 'Revert changes' button to all editable grids in the web ui - - * Make it possible to disable the idle scan on per-DVB adapter basis. - The idle scan is a process to cycles through all multiplex to check - the quality for each mux continously. - - * Hopefully fix some rounding errors in the EPG display. Ticket #69 - - * Use a ''livegrid'' (http://www.ext-livegrid.com/) for displaying EPG. - - * Remove configuration and settings (/home/hts/.hts/tvheadend) on a - deb package purge operation. Ticket #73 - - * If the Program Stream Information changes during a subscription, - react and send a subscriptionStop + subscriptionStart. - This happens on SVT (in sweden) when the transmission switches - from local to nationwide broadcast (AC3 audio is only present - in nationwide broadcast) - Ticket #78 - - * Channel editor has been reworked a bit. It uses an editorGrid, similar - to how other grids work in Tvheadend. Tags are mapped inline using - a list-of-values combobox (http://lovcombo.extjs.eu/) - - * Added a search IMDB direct link in the EPG window popup. - Ticket #79 - - * Add support for configuring DVB multiplexes by entering - all tuning parameters manually - Ticket #37 - - * Fix a bug causing channel <> tags mapping not to be restored on load. - - * Add exponential backoff for reconnect attempt in code word client. - Ticket #80 - - * Try to detect duplicate EPG entries from the DVB feed and adjust - EPG accordingly. The EPG code will search for events with the same - DVB event ID +- 2 events from the current one. If the event id is - equal, the prvious (old) entry will be removed in favor of the new one. - Reason for not blindingly trusting the event id is that some networks - seem to (incorrectly) reuse IDs. - Ticket #65 - - -- Andreas Öman Sat, 18 Jul 2009 23:19:18 +0200 - -hts-tvheadend (2.2) hts; urgency=low - - * Set $HOME so forked processes (XMLTV) will have correct environment - and to make the default storage of recorded videos to end up in the - correct place. - - * Update built-in DVB mux presets to dvb-apps:1275:9655c8cfeed8 - - * Add -h option to display usage. - - * Rewrite the internal streaming pipeline. - This is not externally visible, but needed for a future timeshifter. - - * Make it compile on gcc 4.4 - - * Send 'source' and 'network' information in HTSP.subscriptionStart message. - - * When deleting a channel any channel-bound autorecording would stay in - memory and, sooner or later, cause a crash. This has now been fixed. - - * Add extra scan for XMLTV grabbers in $PATH. Apart from $PATH we also - scan in /bin, /usr/bin, /usr/local/bin - - * Search for XMLTV grabbers in /usr/bin/perlbin/vendor (archlinux) - - * Fix a bug causing DVB card which does not implement the - FE_READ_UNCORRECTED_BLOCKS operation to report insane amount of - forward correction errors. - - -- Andreas Öman Sun, 07 Jun 2009 19:22:49 +0200 - -hts-tvheadend (2.1) hts; urgency=low - - * Add debconf setup scripts to create a superuser account. - - * HTSP server slightly modified. Now also supports the XBMC HTSP client. - - * tvheadend and showtime went separate ways. - - * tvheadend: Record post-processing added again, run command when a - recording is done. - - * tvheadend: Fix characterset translation bug introduced in r1902. - - -- Andreas Öman Thu, 28 May 2009 21:07:44 +0200 - -hts-tvheadend (2.0) unstable; urgency=low - - * tvheadend: All setup and configuration is done from the web user interface. - - * tvheadend: New web user interface based on ExtJS - - -- Andreas Öman Thu, 16 Oct 2008 19:47:23 +0200 - -hts-tvheadend (1.4) unstable; urgency=low - - * tvheadend: Add support for DVB-S (still in experimenal state) - - -- Andreas Öman Sat, 30 Mar 2008 18:00:00 +0100 - -hts-tvheadend (1.3) unstable; urgency=low - - * tvheadend: Add support for executing a post processing script after - recording is completed. - - * tvheadend: Mark I-frames as key-frames in recorder (should make seeking - work better). - - * tvheadend: Correctly set duration of packets in recorder. - - -- Andreas Öman Thu, 28 Mar 2008 18:00:00 +0100 - -hts-tvheadend (1.2) unstable; urgency=low - - * Tvheadend has been rewritten from scratch - - -- Andreas Öman Thu, 28 Feb 2008 17:30:00 +0100 - -hts-tvheadend (1.1) unstable; urgency=low - - * Bugfix: Tvheadend: Do not crash if event name is NULL - - * Rewrite tvheadend internals to fix many serious issues - - * tvheadend now correctly supports recording of h264 content - - * RTSP interface added to tvheadend that supports time shifting of live tv - - * Add web interface to tvheadend - - -- Andreas Öman Sat, 24 Nov 2007 08:11:00 +0100 - -hts-tvheadend (1.0) unstable; urgency=low - - * Initial release - - -- Andreas Öman Tue, 19 Sep 2007 19:20:00 +0200 - diff --git a/debian/rules b/debian/rules index 2bae8172..9b6d1783 100755 --- a/debian/rules +++ b/debian/rules @@ -1,38 +1,18 @@ #!/usr/bin/make -f - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) +export DH_VERBOSE=1 %: dh $@ -override_dh_clean: - dh_testdir - dh_testroot - rm -rf build.* - dh_clean - -override_dh_auto_clean: - dh_clean - override_dh_auto_configure: - dh_testdir - ./configure --release --prefix=/usr + dh_auto_configure -- ${JOBSARGS} override_dh_auto_build: - $(MAKE) - -override_dh_install: - $(MAKE) DESTDIR=$(CURDIR)/debian/tvheadend install - -override_dh_installinit: - dh_installinit --name tvheadend + make ${JARGS} override_dh_strip: dh_strip --dbg-package=tvheadend-dbg -override_dh_autotest: - echo "skipped autotest" +override_dh_auto_install: + dh_auto_install --destdir=debian/tvheadend diff --git a/debian/tvheadend.tvheadend.init b/debian/tvheadend.tvheadend.init index 2e424b97..8f781893 100644 --- a/debian/tvheadend.tvheadend.init +++ b/debian/tvheadend.tvheadend.init @@ -13,7 +13,7 @@ # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/usr/sbin:/usr/bin:/sbin:/bin -DESC="HTS Tvheadend" +DESC="Tvheadend" NAME=tvheadend DAEMON=/usr/bin/$NAME DAEMON_ARGS="-f -u hts -g video" diff --git a/docs/html/config_cwc.html b/docs/html/config_cwc.html index 3877d541..24a5a251 100644 --- a/docs/html/config_cwc.html +++ b/docs/html/config_cwc.html @@ -51,6 +51,9 @@
Update Card
Forward Entitlement Management Messages (EMMs) to the server. +
Update One +
Forward EMMs only from one channel at a time. +
Comment
Allows the administrator to set a comment only visible in this editor. It does not serve any active purpose. diff --git a/src/access.c b/src/access.c index 080d6d84..ee7c467c 100644 --- a/src/access.c +++ b/src/access.c @@ -174,7 +174,7 @@ access_verify(const char *username, const char *password, if(ae->ae_username[0] != '*') { /* acl entry requires username to match */ - if(username == NULL) + if(username == NULL || password == NULL) continue; /* Didn't get one */ if(strcmp(ae->ae_username, username) || @@ -391,6 +391,7 @@ access_record_build(access_entry_t *ae) htsmsg_add_u32(e, "streaming", ae->ae_rights & ACCESS_STREAMING ? 1 : 0); htsmsg_add_u32(e, "dvr" , ae->ae_rights & ACCESS_RECORDER ? 1 : 0); + htsmsg_add_u32(e, "dvrallcfg", ae->ae_rights & ACCESS_RECORDER_ALL ? 1 : 0); htsmsg_add_u32(e, "webui" , ae->ae_rights & ACCESS_WEB_INTERFACE ? 1 : 0); htsmsg_add_u32(e, "admin" , ae->ae_rights & ACCESS_ADMIN ? 1 : 0); @@ -480,6 +481,9 @@ access_record_update(void *opaque, const char *id, htsmsg_t *values, if(!htsmsg_get_u32(values, "dvr", &u32)) access_update_flag(ae, ACCESS_RECORDER, u32); + if(!htsmsg_get_u32(values, "dvrallcfg", &u32)) + access_update_flag(ae, ACCESS_RECORDER_ALL, u32); + if(!htsmsg_get_u32(values, "admin", &u32)) access_update_flag(ae, ACCESS_ADMIN, u32); diff --git a/src/access.h b/src/access.h index 7af118e2..37f42a08 100644 --- a/src/access.h +++ b/src/access.h @@ -58,7 +58,8 @@ typedef struct access_ticket { #define ACCESS_STREAMING 0x1 #define ACCESS_WEB_INTERFACE 0x2 #define ACCESS_RECORDER 0x4 -#define ACCESS_ADMIN 0x8 +#define ACCESS_RECORDER_ALL 0x8 +#define ACCESS_ADMIN 0x10 #define ACCESS_FULL 0x3f /** diff --git a/src/cwc.c b/src/cwc.c index 9e6c24ef..a45008bb 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -47,7 +47,7 @@ #define TVHEADEND_PROTOCOL_ID 0x6502 #define CWC_KEEPALIVE_INTERVAL 30 -#define CWS_NETMSGSIZE 272 +#define CWS_NETMSGSIZE 362 #define CWS_FIRSTCMDNO 0xe0 /** @@ -61,6 +61,8 @@ typedef enum { CARD_VIACCESS, CARD_NAGRA, CARD_NDS, + CARD_CRYPTOWORKS, + CARD_BULCRYPT, CARD_UNKNOWN } card_type_t; @@ -150,7 +152,8 @@ typedef struct cwc_service { enum { CS_UNKNOWN, CS_RESOLVED, - CS_FORBIDDEN + CS_FORBIDDEN, + CS_IDLE } cs_keystate; void *cs_keys; @@ -242,7 +245,13 @@ typedef struct cwc { int shared_toggle; int shared_len; uint8_t * shared_emm; + void *ca_update_id; } cwc_viaccess_emm; +#define cwc_cryptoworks_emm cwc_viaccess_emm + + /* one update id */ + int64_t cwc_update_time; + void *cwc_update_id; /* Card type */ card_type_t cwc_card_type; @@ -258,6 +267,7 @@ typedef struct cwc { int cwc_port; char *cwc_id; int cwc_emm; + int cwc_emmex; const char *cwc_errtxt; @@ -280,6 +290,8 @@ void cwc_emm_seca(cwc_t *cwc, uint8_t *data, int len); void cwc_emm_viaccess(cwc_t *cwc, uint8_t *data, int len); void cwc_emm_nagra(cwc_t *cwc, uint8_t *data, int len); void cwc_emm_nds(cwc_t *cwc, uint8_t *data, int len); +void cwc_emm_cryptoworks(cwc_t *cwc, uint8_t *data, int len); +void cwc_emm_bulcrypt(cwc_t *cwc, uint8_t *data, int len); /** @@ -469,7 +481,7 @@ cwc_send_msg(cwc_t *cwc, const uint8_t *msg, size_t len, int sid, int enq) buf[4] = sid >> 8; buf[5] = sid; - if((len = des_encrypt(buf, len, cwc)) < 0) { + if((len = des_encrypt(buf, len, cwc)) <= 0) { free(buf); free(cm); return -1; @@ -581,10 +593,10 @@ cwc_decode_card_data_reply(cwc_t *cwc, uint8_t *msg, int len) memcpy(cwc->cwc_ua, &msg[6], 8); - tvhlog(LOG_INFO, "cwc", "%s: Connected as user 0x%02x " + tvhlog(LOG_INFO, "cwc", "%s:%i: Connected as user 0x%02x " "to a %s-card [0x%04x : %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x] " "with %d providers", - cwc->cwc_hostname, + cwc->cwc_hostname, cwc->cwc_port, msg[3], n, cwc->cwc_caid, cwc->cwc_ua[0], cwc->cwc_ua[1], cwc->cwc_ua[2], cwc->cwc_ua[3], cwc->cwc_ua[4], cwc->cwc_ua[5], cwc->cwc_ua[6], cwc->cwc_ua[7], nprov); @@ -607,8 +619,8 @@ cwc_decode_card_data_reply(cwc_t *cwc, uint8_t *msg, int len) cwc->cwc_providers[i].sa[6] = msg[9]; cwc->cwc_providers[i].sa[7] = msg[10]; - tvhlog(LOG_INFO, "cwc", "%s: Provider ID #%d: 0x%06x %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x", - cwc->cwc_hostname, i + 1, + tvhlog(LOG_INFO, "cwc", "%s:%i: Provider ID #%d: 0x%06x %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x", + cwc->cwc_hostname, cwc->cwc_port, i + 1, cwc->cwc_providers[i].id, cwc->cwc_providers[i].sa[0], cwc->cwc_providers[i].sa[1], @@ -631,16 +643,16 @@ cwc_decode_card_data_reply(cwc_t *cwc, uint8_t *msg, int len) if (!emm_allowed) { tvhlog(LOG_INFO, "cwc", - "%s: Will not forward EMMs (not allowed by server)", - cwc->cwc_hostname); + "%s:%i: Will not forward EMMs (not allowed by server)", + cwc->cwc_hostname, cwc->cwc_port); } else if (cwc->cwc_card_type != CARD_UNKNOWN) { - tvhlog(LOG_INFO, "cwc", "%s: Will forward EMMs", - cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: Will forward EMMs", + cwc->cwc_hostname, cwc->cwc_port); cwc->cwc_forward_emm = 1; } else { tvhlog(LOG_INFO, "cwc", - "%s: Will not forward EMMs (unsupported CA system)", - cwc->cwc_hostname); + "%s:%i: Will not forward EMMs (unsupported CA system)", + cwc->cwc_hostname, cwc->cwc_port); } } @@ -658,43 +670,57 @@ static void cwc_detect_card_type(cwc_t *cwc) { uint8_t c_sys = cwc->cwc_caid >> 8; + + switch(cwc->cwc_caid) { + case 0x5581: + case 0x4aee: + cwc->cwc_card_type = CARD_BULCRYPT; + tvhlog(LOG_INFO, "cwc", "%s:%i: bulcrypt card", + cwc->cwc_hostname, cwc->cwc_port); + return; + } switch(c_sys) { case 0x17: case 0x06: cwc->cwc_card_type = CARD_IRDETO; - tvhlog(LOG_INFO, "cwc", "%s: irdeto card", - cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: irdeto card", + cwc->cwc_hostname, cwc->cwc_port); break; case 0x05: cwc->cwc_card_type = CARD_VIACCESS; - tvhlog(LOG_INFO, "cwc", "%s: viaccess card", - cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: viaccess card", + cwc->cwc_hostname, cwc->cwc_port); break; case 0x0b: cwc->cwc_card_type = CARD_CONAX; - tvhlog(LOG_INFO, "cwc", "%s: conax card", - cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: conax card", + cwc->cwc_hostname, cwc->cwc_port); break; case 0x01: cwc->cwc_card_type = CARD_SECA; - tvhlog(LOG_INFO, "cwc", "%s: seca card", - cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: seca card", + cwc->cwc_hostname, cwc->cwc_port); break; case 0x4a: cwc->cwc_card_type = CARD_DRE; - tvhlog(LOG_INFO, "cwc", "%s: dre card", - cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: dre card", + cwc->cwc_hostname, cwc->cwc_port); break; case 0x18: cwc->cwc_card_type = CARD_NAGRA; - tvhlog(LOG_INFO, "cwc", "%s: nagra card", - cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: nagra card", + cwc->cwc_hostname, cwc->cwc_port); break; case 0x09: cwc->cwc_card_type = CARD_NDS; - tvhlog(LOG_INFO, "cwc", "%s: nds card", - cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: nds card", + cwc->cwc_hostname, cwc->cwc_port); + break; + case 0x0d: + cwc->cwc_card_type = CARD_CRYPTOWORKS; + tvhlog(LOG_INFO, "cwc", "%s:%i: cryptoworks card", + cwc->cwc_hostname, cwc->cwc_port); break; default: cwc->cwc_card_type = CARD_UNKNOWN; @@ -728,6 +754,8 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg, int len, int seq) { service_t *t = ct->cs_service; + cwc_service_t *ct2; + cwc_t *cwc2; ecm_pid_t *ep; char chaninfo[32]; int i; @@ -748,20 +776,48 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg, if(ct->cs_okchannel == es->es_channel) ct->cs_okchannel = -1; + if (es->es_nok < 3) + es->es_nok++; + if(ct->cs_keystate == CS_FORBIDDEN) return; // We already know it's bad - es->es_nok = 1; + if (es->es_nok > 2) { + tvhlog(LOG_DEBUG, "cwc", + "Too many NOKs for service \"%s\"%s from %s:%i", + t->s_svcname, chaninfo, ct->cs_cwc->cwc_hostname, + ct->cs_cwc->cwc_port); + goto forbid; + } + + TAILQ_FOREACH(cwc2, &cwcs, cwc_link) { + LIST_FOREACH(ct2, &cwc2->cwc_services, cs_link) { + if (ct != ct2 && ct2->cs_service == t && + ct2->cs_keystate == CS_RESOLVED) { + tvhlog(LOG_DEBUG, "cwc", + "NOK from %s:%i: Already has a key for service \"%s\", from %s:%i", + ct->cs_cwc->cwc_hostname, ct->cs_cwc->cwc_port, + t->s_svcname, ct2->cs_cwc->cwc_hostname, ct2->cs_cwc->cwc_port); + es->es_nok = 3; /* do not send more ECM requests */ + goto forbid; + } + } + } tvhlog(LOG_DEBUG, "cwc", "Received NOK for service \"%s\"%s (seqno: %d " "Req delay: %lld ms)", t->s_svcname, chaninfo, seq, delay); +forbid: LIST_FOREACH(ep, &ct->cs_pids, ep_link) { for(i = 0; i <= ep->ep_last_section; i++) - if(ep->ep_sections[i] == NULL || - ep->ep_sections[i]->es_pending || - ep->ep_sections[i]->es_nok == 0) - return; + if(ep->ep_sections[i] == NULL) { + if(es->es_nok < 2) /* only first hit is allowed */ + return; + } else { + if(ep->ep_sections[i]->es_pending || + ep->ep_sections[i]->es_nok == 0) + return; + } } tvhlog(LOG_ERR, "cwc", "Can not descramble service \"%s\", access denied (seqno: %d " @@ -786,12 +842,26 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg, msg[3 + 5], msg[3 + 6], msg[3 + 7], msg[3 + 8], msg[3 + 9], msg[3 + 10],msg[3 + 11],msg[3 + 12],msg[3 + 13],msg[3 + 14], msg[3 + 15], seq, delay); + + TAILQ_FOREACH(cwc2, &cwcs, cwc_link) { + LIST_FOREACH(ct2, &cwc2->cwc_services, cs_link) { + if (ct != ct2 && ct2->cs_service == t && + ct2->cs_keystate == CS_RESOLVED) { + ct->cs_keystate = CS_IDLE; + tvhlog(LOG_DEBUG, "cwc", + "Already has a key for service \"%s\", from %s:%i", + t->s_svcname, ct2->cs_cwc->cwc_hostname, ct2->cs_cwc->cwc_port); + return; + } + } + } if(ct->cs_keystate != CS_RESOLVED) tvhlog(LOG_INFO, "cwc", - "Obtained key for for service \"%s\" in %lld ms, from %s", - t->s_svcname, delay, ct->cs_cwc->cwc_hostname); - + "Obtained key for service \"%s\" in %lld ms, from %s:%i", + t->s_svcname, delay, ct->cs_cwc->cwc_hostname, + ct->cs_cwc->cwc_port); + ct->cs_keystate = CS_RESOLVED; memcpy(ct->cs_cw, msg + 3, 16); ct->cs_pending_cw_update = 1; @@ -877,15 +947,15 @@ cwc_read_message(cwc_t *cwc, const char *state, int timeout) int msglen, r; if((r = cwc_read(cwc, buf, 2, timeout))) { - tvhlog(LOG_INFO, "cwc", "%s: %s: Read error (header): %s", - cwc->cwc_hostname, state, strerror(r)); + tvhlog(LOG_INFO, "cwc", "%s:%i: %s: Read error (header): %s", + cwc->cwc_hostname, cwc->cwc_port, state, strerror(r)); return -1; } msglen = (buf[0] << 8) | buf[1]; if(msglen >= CWS_NETMSGSIZE) { - tvhlog(LOG_INFO, "cwc", "%s: %s: Invalid message size: %d", - cwc->cwc_hostname, state, msglen); + tvhlog(LOG_INFO, "cwc", "%s:%i: %s: Invalid message size: %d", + cwc->cwc_hostname, cwc->cwc_port, state, msglen); return -1; } @@ -893,14 +963,14 @@ cwc_read_message(cwc_t *cwc, const char *state, int timeout) so just wait 1 second here */ if((r = cwc_read(cwc, cwc->cwc_buf + 2, msglen, 1000))) { - tvhlog(LOG_INFO, "cwc", "%s: %s: Read error: %s", - cwc->cwc_hostname, state, strerror(r)); + tvhlog(LOG_INFO, "cwc", "%s:%i: %s: Read error: %s", + cwc->cwc_hostname, cwc->cwc_port, state, strerror(r)); return -1; } if((msglen = des_decrypt(cwc->cwc_buf, msglen + 2, cwc)) < 15) { - tvhlog(LOG_INFO, "cwc", "%s: %s: Decrypt failed", - state, cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: %s: Decrypt failed", + cwc->cwc_hostname, cwc->cwc_port, state); return -1; } return msglen; @@ -962,8 +1032,8 @@ cwc_session(cwc_t *cwc) * Get login key */ if((r = cwc_read(cwc, cwc->cwc_buf, 14, 5000))) { - tvhlog(LOG_INFO, "cwc", "%s: No login key received: %s", - cwc->cwc_hostname, strerror(r)); + tvhlog(LOG_INFO, "cwc", "%s:%i: No login key received: %s", + cwc->cwc_hostname, cwc->cwc_port, strerror(r)); return; } @@ -978,7 +1048,8 @@ cwc_session(cwc_t *cwc) return; if(cwc->cwc_buf[12] != MSG_CLIENT_2_SERVER_LOGIN_ACK) { - tvhlog(LOG_INFO, "cwc", "%s: Login failed", cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: Login failed", + cwc->cwc_hostname, cwc->cwc_port); return; } @@ -992,7 +1063,8 @@ cwc_session(cwc_t *cwc) return; if(cwc->cwc_buf[12] != MSG_CARD_DATA) { - tvhlog(LOG_INFO, "cwc", "%s: Card data request failed", cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i: Card data request failed", + cwc->cwc_hostname, cwc->cwc_port); return; } @@ -1094,7 +1166,8 @@ cwc_thread(void *aux) cwc->cwc_caid = 0; cwc->cwc_connected = 0; cwc_comet_status_update(cwc); - tvhlog(LOG_INFO, "cwc", "Disconnected from %s", cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "Disconnected from %s:%i", + cwc->cwc_hostname, cwc->cwc_port); } if(subscriptions_active()) { @@ -1109,13 +1182,14 @@ cwc_thread(void *aux) ts.tv_nsec = 0; tvhlog(LOG_INFO, "cwc", - "%s: Automatic connection attempt in in %d seconds", - cwc->cwc_hostname, d); + "%s:%i: Automatic connection attempt in in %d seconds", + cwc->cwc_hostname, cwc->cwc_port, d); pthread_cond_timedwait(&cwc_config_changed, &cwc_mutex, &ts); } - tvhlog(LOG_INFO, "cwc", "%s destroyed", cwc->cwc_hostname); + tvhlog(LOG_INFO, "cwc", "%s:%i destroyed", + cwc->cwc_hostname, cwc->cwc_port); while((ct = LIST_FIRST(&cwc->cwc_services)) != NULL) { t = ct->cs_service; @@ -1180,14 +1254,24 @@ cwc_emm_cache_lookup(cwc_t *cwc, uint32_t crc) * */ void -cwc_emm(uint8_t *data, int len) +cwc_emm(uint8_t *data, int len, uint16_t caid, void *ca_update_id) { cwc_t *cwc; pthread_mutex_lock(&cwc_mutex); TAILQ_FOREACH(cwc, &cwcs, cwc_link) { - if(cwc->cwc_forward_emm && cwc->cwc_writer_running) { + if(cwc->cwc_caid == caid && + cwc->cwc_forward_emm && cwc->cwc_writer_running) { + if (cwc->cwc_emmex) { + if (cwc->cwc_update_id != ca_update_id) { + int64_t delta = getmonoclock() - cwc->cwc_update_time; + if (delta < 25000000UL) /* 25 seconds */ + continue; + } + cwc->cwc_update_time = getmonoclock(); + } + cwc->cwc_update_id = ca_update_id; switch (cwc->cwc_card_type) { case CARD_CONAX: cwc_emm_conax(cwc, data, len); @@ -1210,6 +1294,12 @@ cwc_emm(uint8_t *data, int len) case CARD_NDS: cwc_emm_nds(cwc, data, len); break; + case CARD_CRYPTOWORKS: + cwc_emm_cryptoworks(cwc, data, len); + break; + case CARD_BULCRYPT: + cwc_emm_bulcrypt(cwc, data, len); + break; case CARD_UNKNOWN: break; } @@ -1402,13 +1492,15 @@ cwc_emm_viaccess(cwc_t *cwc, uint8_t *data, int mlen) if (cwc->cwc_viaccess_emm.shared_emm) { cwc->cwc_viaccess_emm.shared_len = len; memcpy(cwc->cwc_viaccess_emm.shared_emm, data, len); + cwc->cwc_viaccess_emm.ca_update_id = cwc->cwc_update_id; } cwc->cwc_viaccess_emm.shared_toggle = data[0]; } } break; case 0x8e: - if (cwc->cwc_viaccess_emm.shared_emm) { + if (cwc->cwc_viaccess_emm.shared_emm && + cwc->cwc_viaccess_emm.ca_update_id == cwc->cwc_update_id) { int match = 0; int i; /* Match SA and provider in shared */ @@ -1490,6 +1582,9 @@ cwc_table_input(struct th_descrambler *td, struct service *t, char chaninfo[32]; caid_t *c; + if (ct->cs_keystate == CS_IDLE) + return; + if(len > 4096) return; @@ -1541,6 +1636,9 @@ cwc_table_input(struct th_descrambler *td, struct service *t, es = ep->ep_sections[section]; + if (es->es_nok > 2) + break; /* too many NOK responses in a row */ + if(es->es_ecmsize == len && !memcmp(es->es_ecm, data, len)) break; /* key already sent */ @@ -1654,6 +1752,90 @@ cwc_emm_nds(cwc_t *cwc, uint8_t *data, int len) cwc_send_msg(cwc, data, len, 0, 1); } +void +cwc_emm_cryptoworks(cwc_t *cwc, uint8_t *data, int len) +{ + int match = 0; + + switch (data[0]) { + case 0x82: /* unique */ + match = len >= 10 && memcmp(data + 5, cwc->cwc_ua + 3, 5) == 0; + break; + case 0x84: /* emm-sh */ + if (len >= 9 && memcmp(data + 5, cwc->cwc_ua + 3, 4) == 0) { + if (cwc->cwc_cryptoworks_emm.shared_emm) { + free(cwc->cwc_cryptoworks_emm.shared_emm); + cwc->cwc_cryptoworks_emm.shared_len = 0; + cwc->cwc_cryptoworks_emm.shared_emm = (uint8_t *)malloc(len); + } + cwc->cwc_cryptoworks_emm.shared_emm = malloc(len); + if (cwc->cwc_cryptoworks_emm.shared_emm) { + cwc->cwc_cryptoworks_emm.shared_len = len; + memcpy(cwc->cwc_cryptoworks_emm.shared_emm, data, len); + cwc->cwc_cryptoworks_emm.ca_update_id = cwc->cwc_update_id; + } + } + break; + case 0x86: /* emm-sb */ + if (cwc->cwc_cryptoworks_emm.shared_emm && + cwc->cwc_cryptoworks_emm.ca_update_id == cwc->cwc_update_id) { + /* python: EMM_SH[0:12] + EMM_SB[5:-1] + EMM_SH[12:-1] */ + uint32_t elen = len - 5 + cwc->cwc_cryptoworks_emm.shared_len - 12; + uint8_t *tmp = malloc(elen); + uint8_t *composed = tmp ? malloc(elen + 12) : NULL; + if (composed) { + memcpy(tmp, data + 5, len - 5); + memcpy(tmp + len - 5, cwc->cwc_cryptoworks_emm.shared_emm + 12, + cwc->cwc_cryptoworks_emm.shared_len - 12); + memcpy(composed, cwc->cwc_cryptoworks_emm.shared_emm, 12); + sort_nanos(composed + 12, tmp, elen); + composed[1] = ((elen + 9) >> 8) | 0x70; + composed[2] = (elen + 9) & 0xff; + cwc_send_msg(cwc, composed, elen + 12, 0, 1); + free(composed); + free(tmp); + } + cwc->cwc_cryptoworks_emm.shared_emm = NULL; + cwc->cwc_cryptoworks_emm.shared_len = 0; + } + break; + case 0x88: /* global */ + case 0x89: /* global */ + match = 1; + break; + default: + break; + } + + if (match) + cwc_send_msg(cwc, data, len, 0, 1); +} + +void +cwc_emm_bulcrypt(cwc_t *cwc, uint8_t *data, int len) +{ + int match = 0; + + switch (data[0]) { + case 0x82: /* unique */ + case 0x85: /* unique */ + match = len >= 10 && memcmp(data + 3, cwc->cwc_ua + 2, 3) == 0; + break; + case 0x84: /* shared */ + match = len >= 10 && memcmp(data + 3, cwc->cwc_ua + 2, 2) == 0; + break; + case 0x8b: /* shared-unknown */ + match = len >= 10 && memcmp(data + 4, cwc->cwc_ua + 2, 2) == 0; + break; + case 0x8a: /* global */ + match = len >= 10 && memcmp(data + 4, cwc->cwc_ua + 2, 1) == 0; + break; + } + + if (match) + cwc_send_msg(cwc, data, len, 0, 1); +} + /** * */ @@ -1924,6 +2106,7 @@ cwc_record_build(cwc_t *cwc) htsmsg_add_str(e, "deskey", buf); htsmsg_add_u32(e, "emm", cwc->cwc_emm); + htsmsg_add_u32(e, "emmex", cwc->cwc_emmex); htsmsg_add_str(e, "comment", cwc->cwc_comment ?: ""); return e; @@ -2010,6 +2193,9 @@ cwc_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate) if(!htsmsg_get_u32(values, "emm", &u32)) cwc->cwc_emm = u32; + if(!htsmsg_get_u32(values, "emmex", &u32)) + cwc->cwc_emmex = u32; + cwc->cwc_reconfigure = 1; if(cwc->cwc_fd != -1) diff --git a/src/cwc.h b/src/cwc.h index fa3cede9..3c28710a 100644 --- a/src/cwc.h +++ b/src/cwc.h @@ -23,6 +23,6 @@ void cwc_init(void); void cwc_service_start(struct service *t); -void cwc_emm(uint8_t *data, int len); +void cwc_emm(uint8_t *data, int len, uint16_t caid, void *ca_update_id); #endif /* CWC_H_ */ diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 9c14e23f..ad9c30fb 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -265,7 +265,8 @@ th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, uint16_t tsid, const char *network, const char *logprefix, int enabled, - int initialscan, const char *identifier); + int initialscan, const char *identifier, + dvb_satconf_t *satconf); void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *name); @@ -295,7 +296,8 @@ const char *dvb_mux_add_by_params(th_dvb_adapter_t *tda, int polarisation, const char *satconf); -int dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src); +int dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, + dvb_satconf_t *satconf); /** * DVB Transport (aka DVB service) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 9f6afd56..433107ae 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -458,7 +458,7 @@ dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src) dvb_mux_destroy(tdmi_dst); LIST_FOREACH(tdmi_src, &src->tda_muxes, tdmi_adapter_link) - dvb_mux_copy(dst, tdmi_src); + dvb_mux_copy(dst, tdmi_src, NULL); tda_save(dst); } diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 9756fd6e..770e03a2 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -150,7 +150,8 @@ tdmi_compare_conf(int adapter_type, th_dvb_mux_instance_t * dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, uint16_t tsid, const char *network, const char *source, - int enabled, int initialscan, const char *identifier) + int enabled, int initialscan, const char *identifier, + dvb_satconf_t *satconf) { th_dvb_mux_instance_t *tdmi, *c; unsigned int hash; @@ -225,8 +226,9 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, snprintf(buf, sizeof(buf), "%s%d%s%s%s", tda->tda_identifier, dmc->dmc_fe_params.frequency, qpsktxt, - dmc->dmc_satconf ? "_satconf_" : "", - dmc->dmc_satconf ? dmc->dmc_satconf->sc_id : ""); + (satconf || dmc->dmc_satconf) ? "_satconf_" : "", + (satconf ? satconf->sc_id : + (dmc->dmc_satconf ? dmc->dmc_satconf->sc_id : ""))); tdmi->tdmi_identifier = strdup(buf); } else { @@ -259,6 +261,8 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, memcpy(&tdmi->tdmi_conf, dmc, sizeof(struct dvb_mux_conf)); + if(satconf) + tdmi->tdmi_conf.dmc_satconf = satconf; if(tdmi->tdmi_conf.dmc_satconf != NULL) { LIST_INSERT_HEAD(&tdmi->tdmi_conf.dmc_satconf->sc_tdmis, tdmi, tdmi_satconf_link); @@ -708,7 +712,7 @@ tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) tdmi = dvb_mux_create(tda, &dmc, tsid, htsmsg_get_str(m, "network"), NULL, enabled, 1, - identifier); + identifier, NULL); if(tdmi != NULL) { if((s = htsmsg_get_str(m, "status")) != NULL) @@ -1058,7 +1062,7 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, } dmc.dmc_polarisation = polarisation; - tdmi = dvb_mux_create(tda, &dmc, 0xffff, NULL, NULL, 1, 1, NULL); + tdmi = dvb_mux_create(tda, &dmc, 0xffff, NULL, NULL, 1, 1, NULL, NULL); if(tdmi == NULL) return "Mux already exist"; @@ -1071,7 +1075,8 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, * */ int -dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src) +dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, + dvb_satconf_t *satconf) { th_dvb_mux_instance_t *tdmi_dst; service_t *t_src, *t_dst; @@ -1083,7 +1088,7 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src) tdmi_src->tdmi_transport_stream_id, tdmi_src->tdmi_network, "copy operation", tdmi_src->tdmi_enabled, - 1, NULL); + 1, NULL, satconf); if(tdmi_dst == NULL) return -1; // Already exist diff --git a/src/dvb/dvb_preconf.c b/src/dvb/dvb_preconf.c index ee6098ff..ef1cf6d9 100644 --- a/src/dvb/dvb_preconf.c +++ b/src/dvb/dvb_preconf.c @@ -64,9 +64,7 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const struct mux *m, int num, break; case FE_QPSK: - #ifdef SYS_DVBS - dmc.dmc_fe_delsys = SYS_DVBS; - #endif + dmc.dmc_fe_delsys = SYS_DVBS; dmc.dmc_fe_params.u.qpsk.symbol_rate = m->symrate; dmc.dmc_fe_params.u.qpsk.fec_inner = m->fec; @@ -102,7 +100,7 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const struct mux *m, int num, dmc.dmc_satconf = dvb_satconf_entry_find(tda, satconf, 0); - dvb_mux_create(tda, &dmc, 0xffff, NULL, source, 1, 1, NULL); + dvb_mux_create(tda, &dmc, 0xffff, NULL, source, 1, 1, NULL, NULL); m++; } } diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 29099cee..db2aa0d1 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -44,7 +44,7 @@ #define TDT_CRC 0x1 #define TDT_QUICKREQ 0x2 -#define TDT_INC_TABLE_HDR 0x4 +#define TDT_CA 0x4 static void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); @@ -214,8 +214,9 @@ dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec, ptr = &sec[3]; if(chkcrc) len -= 4; /* Strip trailing CRC */ - if(tdt->tdt_flags & TDT_INC_TABLE_HDR) - ret = tdt->tdt_callback(tdmi, sec, len + 3, tableid, tdt->tdt_opaque); + if(tdt->tdt_flags & TDT_CA) + ret = tdt->tdt_callback((th_dvb_mux_instance_t *)tdt, + sec, len + 3, tableid, tdt->tdt_opaque); else ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque); @@ -412,9 +413,13 @@ dvb_desc_extended_event(uint8_t *ptr, int len, if ((desclen - strlen(desc)) > 2) { /* get description -> append to desc if space left */ - strncat(desc, "\n", 1); - strncat(desc, (char*)(items+1), - items[0] > (desclen - strlen(desc)) ? (desclen - strlen(desc)) : items[0]); + if (desc[0] != '\0') + strncat(desc, "\n", 1); + if((r = dvb_get_string_with_len(desc + strlen(desc), + desclen - strlen(desc), + items, (localptr + count) - items, + dvb_default_charset)) < 0) + return -1; } items += 1 + items[0]; @@ -423,9 +428,13 @@ dvb_desc_extended_event(uint8_t *ptr, int len, if ((itemlen - strlen(item)) > 2) { /* get item -> append to item if space left */ - strncat(item, "\n", 1); - strncat(item, (char*)(items+1), - items[0] > (itemlen - strlen(item)) ? (itemlen - strlen(item)) : items[0]); + if (item[0] != '\0') + strncat(item, "\n", 1); + if((r = dvb_get_string_with_len(item + strlen(item), + itemlen - strlen(item), + items, (localptr + count) - items, + dvb_default_charset)) < 0) + return -1; } /* go to next item */ @@ -824,7 +833,7 @@ static int dvb_ca_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { - cwc_emm(ptr, len); + cwc_emm(ptr, len, (uintptr_t)opaque, (void *)tdmi); return 0; } @@ -837,6 +846,7 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, { int tag, tlen; uint16_t pid; + uintptr_t caid; if((ptr[2] & 1) == 0) { /* current_next_indicator == next, skip this */ @@ -852,14 +862,14 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, len -= 2; switch(tag) { case DVB_DESC_CA: - // caid = ( ptr[0] << 8) | ptr[1]; + caid = ( ptr[0] << 8) | ptr[1]; pid = ((ptr[2] & 0x1f) << 8) | ptr[3]; if(pid == 0) break; - tdt_add(tdmi, NULL, dvb_ca_callback, NULL, "CA", - TDT_INC_TABLE_HDR, pid, NULL); + tdt_add(tdmi, NULL, dvb_ca_callback, (void *)caid, "CA", + TDT_CA, pid, NULL); break; default: @@ -942,7 +952,7 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dmc.dmc_fe_params.u.qam.fec_inner = fec_tab[ptr[10] & 0x07]; dvb_mux_create(tdmi->tdmi_adapter, &dmc, tsid, NULL, - "automatic mux discovery", 1, 1, NULL); + "automatic mux discovery", 1, 1, NULL, NULL); return 0; } @@ -1027,7 +1037,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, #endif dvb_mux_create(tdmi->tdmi_adapter, &dmc, tsid, NULL, - "automatic mux discovery", 1, 1, NULL); + "automatic mux discovery", 1, 1, NULL, NULL); return 0; } diff --git a/src/dvr/mkmux.c b/src/dvr/mkmux.c index 8e5cf8a5..446eff3d 100644 --- a/src/dvr/mkmux.c +++ b/src/dvr/mkmux.c @@ -143,14 +143,13 @@ static htsbuf_queue_t * mk_build_segment_info(mk_mux_t *mkm) { htsbuf_queue_t *q = htsbuf_queue_alloc(0); - extern char *htsversion_full; char app[128]; - snprintf(app, sizeof(app), "HTS Tvheadend %s", htsversion_full); + snprintf(app, sizeof(app), "Tvheadend %s", tvheadend_version); ebml_append_bin(q, 0x73a4, mkm->uuid, sizeof(mkm->uuid)); ebml_append_string(q, 0x7ba9, mkm->title); - ebml_append_string(q, 0x4d80, "HTS Tvheadend Matroska muxer"); + ebml_append_string(q, 0x4d80, "Tvheadend Matroska muxer"); ebml_append_string(q, 0x5741, app); ebml_append_uint(q, 0x2ad7b1, MATROSKA_TIMESCALE); @@ -822,10 +821,13 @@ mk_write_frame_i(mk_mux_t *mkm, mk_track *t, th_pkt_t *pkt) int skippable = pkt->pkt_frametype == PKT_B_FRAME; int vkeyframe = SCT_ISVIDEO(t->type) && keyframe; - uint8_t *data; - size_t len; + uint8_t *data = pktbuf_ptr(pkt->pkt_payload); + size_t len = pktbuf_len(pkt->pkt_payload); const int clusersizemax = 2000000; + if(!data || len <= 0) + return; + if(pts == PTS_UNSET) // This is our best guess, it might be wrong but... oh well pts = t->nextpts; @@ -875,10 +877,6 @@ mk_write_frame_i(mk_mux_t *mkm, mk_track *t, th_pkt_t *pkt) addcue(mkm, pts, t->tracknum); } - - data = pktbuf_ptr(pkt->pkt_payload); - len = pktbuf_len(pkt->pkt_payload); - if(t->type == SCT_AAC || t->type == SCT_MP4A) { // Skip ADTS header if(len < 7) diff --git a/src/htsp.c b/src/htsp.c index 1e739c92..fbfc83f5 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -50,7 +50,6 @@ static void *htsp_server; #define HTSP_PRIV_MASK (ACCESS_STREAMING) -extern const char *htsversion; extern char *dvr_storage; LIST_HEAD(htsp_connection_list, htsp_connection); @@ -1047,7 +1046,7 @@ htsp_method_hello(htsp_connection_t *htsp, htsmsg_t *in) htsmsg_add_u32(r, "htspversion", HTSP_PROTO_VERSION); htsmsg_add_str(r, "servername", "HTS Tvheadend"); - htsmsg_add_str(r, "serverversion", htsversion); + htsmsg_add_str(r, "serverversion", tvheadend_version); htsmsg_add_bin(r, "challenge", htsp->htsp_challenge, 32); htsp_update_logname(htsp); @@ -1541,7 +1540,7 @@ const static char frametypearray[PKT_NTYPES] = { static void htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) { - htsmsg_t *m = htsmsg_create_map(), *n; + htsmsg_t *m, *n; htsp_msg_t *hm; htsp_connection_t *htsp = hs->hs_htsp; int64_t ts; @@ -1557,6 +1556,8 @@ htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) pkt_ref_dec(pkt); return; } + + m = htsmsg_create_map(); htsmsg_add_str(m, "method", "muxpkt"); htsmsg_add_u32(m, "subscriptionId", hs->hs_sid); diff --git a/src/main.c b/src/main.c index 365246c8..8bf287bd 100644 --- a/src/main.c +++ b/src/main.c @@ -60,8 +60,6 @@ #include "ffdecsa/FFdecsa.h" int running; -extern const char *htsversion; -extern const char *htsversion_full; time_t dispatch_clock; static LIST_HEAD(, gtimer) gtimers; pthread_mutex_t global_lock; @@ -150,7 +148,7 @@ gtimer_disarm(gtimer_t *gti) static void usage(const char *argv0) { - printf("HTS Tvheadend %s\n", htsversion_full); + printf("HTS Tvheadend %s\n", tvheadend_version); printf("usage: %s [options]\n", argv0); printf("\n"); printf(" -a Use only DVB adapters specified (csv)\n"); @@ -386,7 +384,7 @@ main(int argc, char **argv) #endif http_server_init(); - webui_init(TVHEADEND_CONTENT_PATH); + webui_init(tvheadend_dataroot()); serviceprobe_init(); @@ -433,9 +431,11 @@ main(int argc, char **argv) pthread_sigmask(SIG_UNBLOCK, &set, NULL); tvhlog(LOG_NOTICE, "START", "HTS Tvheadend version %s started, " - "running as PID:%d UID:%d GID:%d, settings located in '%s'", - htsversion_full, - getpid(), getuid(), getgid(), hts_settings_get_root()); + "running as PID:%d UID:%d GID:%d, settings located in '%s', " + "dataroot: %s", + tvheadend_version, + getpid(), getuid(), getgid(), hts_settings_get_root(), + tvheadend_dataroot() ?: ""); if(crash) abort(); diff --git a/src/parser_latm.c b/src/parser_latm.c index 9d7369a9..eca91b80 100644 --- a/src/parser_latm.c +++ b/src/parser_latm.c @@ -71,6 +71,9 @@ read_audio_specific_config(elementary_stream_t *st, latm_private_t *latm, else sr = sri_to_rate(latm->sample_rate_index); + if(sr == 0) + return; + st->es_frame_duration = 1024 * 90000 / sr; latm->channel_config = read_bits(bs, 4); diff --git a/src/psi.c b/src/psi.c index 130678d5..d27a5a9e 100644 --- a/src/psi.c +++ b/src/psi.c @@ -307,8 +307,10 @@ psi_desc_ca(service_t *t, const uint8_t *buffer, int size) } break; case 0x4a00://DRECrypt - provid = size < 4 ? 0 : buffer[4]; - break; + if (caid != 0x4aee) { // Bulcrypt + provid = size < 4 ? 0 : buffer[4]; + break; + } default: provid = 0; break; @@ -834,13 +836,23 @@ static struct strtab caidnametab[] = { { "Irdeto", 0x0600 }, { "Irdeto", 0x0602 }, { "Irdeto", 0x0604 }, + { "Irdeto", 0x0624 }, + { "Irdeto", 0x0666 }, { "Jerroldgi", 0x0700 }, { "Matra", 0x0800 }, { "NDS", 0x0900 }, { "Nokia", 0x0A00 }, { "Conax", 0x0B00 }, { "NTL", 0x0C00 }, - { "CryptoWorks", 0x0D00 }, + { "CryptoWorks", 0x0D00 }, + { "CryptoWorks", 0x0D01 }, + { "CryptoWorks", 0x0D02 }, + { "CryptoWorks", 0x0D03 }, + { "CryptoWorks", 0x0D05 }, + { "CryptoWorks", 0x0D0F }, + { "CryptoWorks", 0x0D70 }, + { "CryptoWorks ICE", 0x0D96 }, + { "CryptoWorks ICE", 0x0D97 }, { "PowerVu", 0x0E00 }, { "Sony", 0x0F00 }, { "Tandberg", 0x1000 }, @@ -866,7 +878,9 @@ static struct strtab caidnametab[] = { { "GI", 0x4700 }, { "Telemann", 0x4800 }, { "DRECrypt", 0x4ae0 }, - { "DRECrypt2", 0x4ae1 } + { "DRECrypt2", 0x4ae1 }, + { "Bulcrypt", 0x4aee }, + { "Bulcrypt", 0x5581 }, }; const char * diff --git a/src/rawtsinput.c b/src/rawtsinput.c index b1db456f..24a71d34 100644 --- a/src/rawtsinput.c +++ b/src/rawtsinput.c @@ -184,7 +184,7 @@ got_pat(const uint8_t *ptr, size_t len, void *opaque) len -= 8; ptr += 8; - if(len < 0) + if(len <= 0) return; pthread_mutex_lock(&global_lock); diff --git a/src/teletext.c b/src/teletext.c index ff7a72db..c559cac1 100644 --- a/src/teletext.c +++ b/src/teletext.c @@ -40,7 +40,6 @@ */ typedef struct tt_mag { int ttm_curpage; - int ttm_inactive; int64_t ttm_current_pts; uint8_t ttm_lang; uint8_t ttm_page[23*40 + 1]; @@ -397,8 +396,7 @@ tt_decode_line(service_t *t, elementary_stream_t *st, uint8_t *buf) case 0: if(ttm->ttm_curpage != 0) { - if(ttm->ttm_inactive == 0) - tt_subtitle_deliver(t, st, ttm); + tt_subtitle_deliver(t, st, ttm); if(ttm->ttm_curpage == 192) teletext_rundown_copy(ttp, ttm); @@ -429,11 +427,9 @@ tt_decode_line(service_t *t, elementary_stream_t *st, uint8_t *buf) teletext_rundown_scan(t, ttp); ttm->ttm_current_pts = t->s_current_pts; - ttm->ttm_inactive = 0; break; case 1 ... 23: - ttm->ttm_inactive = 0; for(i = 0; i < 40; i++) { c = buf[i + 2] & 0x7f; ttm->ttm_page[i + 40 * (line - 1)] = c; @@ -446,30 +442,6 @@ tt_decode_line(service_t *t, elementary_stream_t *st, uint8_t *buf) } - - -/** - * - */ -static void -teletext_scan_stream(service_t *t, elementary_stream_t *st) -{ - tt_private_t *ttp = st->es_priv; - tt_mag_t *ttm; - int i; - - if(ttp == NULL) - return; - for(i = 0; i < 8; i++) { - ttm = &ttp->ttp_mags[i]; - ttm->ttm_inactive++; - if(ttm->ttm_inactive == 2) { - tt_subtitle_deliver(t, st, ttm); - } - } -} - - /** * */ @@ -489,7 +461,6 @@ teletext_input(service_t *t, elementary_stream_t *st, const uint8_t *tsb) } x += 46; } - teletext_scan_stream(t, st); } diff --git a/src/trap.c b/src/trap.c index 1e92497d..bd761186 100644 --- a/src/trap.c +++ b/src/trap.c @@ -220,9 +220,6 @@ callback(struct dl_phdr_info *info, size_t size, void *data) } -extern const char *htsversion_full; - - void trap_init(const char *ver) { @@ -259,7 +256,7 @@ trap_init(const char *ver) "PRG: %s (%s) " "[%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x] " - "CWD: %s ", ver, htsversion_full, + "CWD: %s ", ver, tvheadend_version, digest[0], digest[1], digest[2], diff --git a/src/tsdemux.c b/src/tsdemux.c index 897fe1d6..57d6836e 100644 --- a/src/tsdemux.c +++ b/src/tsdemux.c @@ -142,8 +142,6 @@ ts_extract_pcr(service_t *t, elementary_stream_t *st, const uint8_t *tsb, pcr |= (uint64_t)tsb[9] << 1; pcr |= ((uint64_t)tsb[10] >> 7) & 0x01; - pcr = pcr; - if(pcrp != NULL) *pcrp = pcr; diff --git a/src/tvheadend.h b/src/tvheadend.h index c4c1d777..a82feaee 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -15,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #ifndef TVHEADEND_H #define TVHEADEND_H @@ -34,6 +33,9 @@ #include "redblack.h" +extern const char *tvheadend_version; +extern const char *tvheadend_dataroot(); + #define PTS_UNSET INT64_C(0x8000000000000000) extern pthread_mutex_t global_lock; @@ -400,7 +402,7 @@ extern void scopedunlock(pthread_mutex_t **mtxp); #define scopedlock(mtx) \ pthread_mutex_t *scopedlock ## __LINE__ \ __attribute__((cleanup(scopedunlock))) = mtx; \ - pthread_mutex_lock(mtx); + pthread_mutex_lock(scopedlock ## __LINE__); #define scopedgloballock() scopedlock(&global_lock) diff --git a/src/version.c b/src/version.c index 118eb0c4..9aebe049 100644 --- a/src/version.c +++ b/src/version.c @@ -1,9 +1,4 @@ +#include "buildversion.h" #include "config.h" -#ifdef HTS_RELEASE_TAG -const char *htsversion=HTS_RELEASE_TAG; -const char *htsversion_full=HTS_RELEASE_TAG " (" HTS_VERSION ")"; -#else -const char *htsversion=HTS_VERSION; -const char *htsversion_full=HTS_VERSION; -#endif +const char *tvheadend_version = BUILD_VERSION; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 0e444c09..f1b07f03 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -43,9 +43,6 @@ #include "epg.h" #include "iptv_input.h" -extern const char *htsversion; -extern const char *htsversion_full; - static void extjs_load(htsbuf_queue_t *hq, const char *script) { @@ -166,7 +163,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) "\n" "
\n" "\n", - htsversion); + tvheadend_version); http_output_html(hc); return 0; } @@ -185,7 +182,7 @@ page_about(http_connection_t *hc, const char *remain, void *opaque) "
" "HTS Tvheadend %s" "

" - "© 2006 - 2010 Andreas \303\226man, et al.

" + "© 2006 - 2012 Andreas \303\226man, et al.

" "
" "" "http://www.lonelycoder.com/hts

" @@ -197,8 +194,8 @@ page_about(http_connection_t *hc, const char *remain, void *opaque) "

" "Build: %s" "", - htsversion, - htsversion_full); + tvheadend_version, + tvheadend_version); http_output_html(hc); return 0; @@ -634,6 +631,9 @@ extjs_confignames(http_connection_t *hc, const char *remain, void *opaque) out = htsmsg_create_map(); array = htsmsg_create_list(); + if (http_access_verify(hc, ACCESS_RECORDER_ALL)) + goto skip; + LIST_FOREACH(cfg, &dvrconfigs, config_link) { e = htsmsg_create_map(); htsmsg_add_str(e, "identifier", cfg->dvr_config_name); @@ -644,6 +644,7 @@ extjs_confignames(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_msg(array, NULL, e); } +skip: htsmsg_add_msg(out, "entries", array); } else { @@ -890,6 +891,19 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) return HTTP_STATUS_BAD_REQUEST; } + if (http_access_verify(hc, ACCESS_RECORDER_ALL)) { + config_name = NULL; + LIST_FOREACH(cfg, &dvrconfigs, config_link) { + if (cfg->dvr_config_name && hc->hc_username && + strcmp(cfg->dvr_config_name, hc->hc_username) == 0) { + config_name = cfg->dvr_config_name; + break; + } + } + if (config_name == NULL && hc->hc_username) + tvhlog(LOG_INFO,"dvr","User '%s' has no dvr config with identical name, using default...", hc->hc_username); + } + dvr_entry_create_by_event(config_name, e, hc->hc_representative, NULL, DVR_PRIO_NORMAL); #endif @@ -961,6 +975,19 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) if(stop < start) stop += 86400; + if (http_access_verify(hc, ACCESS_RECORDER_ALL)) { + config_name = NULL; + LIST_FOREACH(cfg, &dvrconfigs, config_link) { + if (cfg->dvr_config_name && hc->hc_username && + strcmp(cfg->dvr_config_name, hc->hc_username) == 0) { + config_name = cfg->dvr_config_name; + break; + } + } + if (config_name == NULL && hc->hc_username) + tvhlog(LOG_INFO,"dvr","User '%s' has no dvr config with identical name, using default...", hc->hc_username); + } + dvr_entry_create(config_name, ch, start, stop, title, NULL, hc->hc_representative, NULL, NULL, 0, dvr_pri2val(pri)); diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index c0d9c7f0..4080868e 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -454,11 +454,14 @@ extjs_dvbsatconf(http_connection_t *hc, const char *remain, void *opaque) htsbuf_queue_t *hq = &hc->hc_reply; th_dvb_adapter_t *tda; htsmsg_t *out; + const char *adapter = http_arg_get(&hc->hc_req_args, "adapter"); pthread_mutex_lock(&global_lock); - if(remain == NULL || - (tda = dvb_adapter_find_by_identifier(remain)) == NULL) { + if((remain == NULL || + (tda = dvb_adapter_find_by_identifier(remain)) == NULL) && + (adapter == NULL || + (tda = dvb_adapter_find_by_identifier(adapter)) == NULL)) { pthread_mutex_unlock(&global_lock); return 404; } @@ -582,9 +585,11 @@ extjs_dvb_copymux(http_connection_t *hc, const char *remain, void *opaque) th_dvb_adapter_t *tda; htsmsg_t *in; const char *entries = http_arg_get(&hc->hc_req_args, "entries"); + const char *satconf = http_arg_get(&hc->hc_req_args, "satconf"); const char *id; htsmsg_field_t *f; th_dvb_mux_instance_t *tdmi; + dvb_satconf_t *sc = NULL; in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL; @@ -599,13 +604,20 @@ extjs_dvb_copymux(http_connection_t *hc, const char *remain, void *opaque) return 404; } + if (satconf) { + sc = dvb_satconf_entry_find(tda, satconf, 0); + if (sc == NULL) { + pthread_mutex_unlock(&global_lock); + return 404; + } + } TAILQ_FOREACH(f, &in->hm_fields, hmf_link) { if((id = htsmsg_field_get_string(f)) != NULL && (tdmi = dvb_mux_find_by_identifier(id)) != NULL && tda != tdmi->tdmi_adapter) { - if(dvb_mux_copy(tda, tdmi)) { + if(dvb_mux_copy(tda, tdmi, sc)) { char buf[100]; dvb_mux_nicename(buf, sizeof(buf), tdmi); diff --git a/src/webui/statedump.c b/src/webui/statedump.c index e4e73dd8..a9758e6f 100644 --- a/src/webui/statedump.c +++ b/src/webui/statedump.c @@ -36,7 +36,6 @@ #endif extern char tvh_binshasum[20]; -extern char *htsversion_full; int page_statedump(http_connection_t *hc, const char *remain, void *opaque); @@ -159,7 +158,7 @@ page_statedump(http_connection_t *hc, const char *remain, void *opaque) htsbuf_qprintf(hq, "Tvheadend %s Binary SHA1: " "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", - htsversion_full, + tvheadend_version, tvh_binshasum[0], tvh_binshasum[1], tvh_binshasum[2], diff --git a/src/webui/static/app/acleditor.js b/src/webui/static/app/acleditor.js index d9864d83..eaeddbf4 100644 --- a/src/webui/static/app/acleditor.js +++ b/src/webui/static/app/acleditor.js @@ -20,6 +20,12 @@ tvheadend.acleditor = function() { width: 100 }); + var dvrallcfgColumn = new Ext.grid.CheckColumn({ + header: "All Configs (VR)", + dataIndex: 'dvrallcfg', + width: 100 + }); + var webuiColumn = new Ext.grid.CheckColumn({ header: "Web Interface", dataIndex: 'webui', @@ -52,6 +58,7 @@ tvheadend.acleditor = function() { }, streamingColumn, dvrColumn, + dvrallcfgColumn, webuiColumn, adminColumn, { @@ -63,14 +70,14 @@ tvheadend.acleditor = function() { ]); var UserRecord = Ext.data.Record.create([ - 'enabled','streaming','dvr','admin','webui','username', + 'enabled','streaming','dvr','dvrallcfg','admin','webui','username', 'prefix','password','comment' ]); return new tvheadend.tableEditor('Access control', 'accesscontrol', cm, UserRecord, [enabledColumn, streamingColumn, - dvrColumn, webuiColumn, + dvrColumn, dvrallcfgColumn, webuiColumn, adminColumn], null, 'config_access.html', 'group'); diff --git a/src/webui/static/app/cwceditor.js b/src/webui/static/app/cwceditor.js index 99daadf1..ac5f610a 100644 --- a/src/webui/static/app/cwceditor.js +++ b/src/webui/static/app/cwceditor.js @@ -14,6 +14,12 @@ tvheadend.cwceditor = function() { width: 100 }); + var emmexColumn = new Ext.grid.CheckColumn({ + header: "Update One", + dataIndex: 'emmex', + width: 100 + }); + function setMetaAttr(meta, record){ var enabled = record.get('enabled'); if(!enabled) return; @@ -72,6 +78,7 @@ tvheadend.cwceditor = function() { editor: new fm.TextField({allowBlank: false}) }, emmColumn, + emmexColumn, { header: "Comment", dataIndex: 'comment', @@ -86,7 +93,7 @@ tvheadend.cwceditor = function() { var rec = Ext.data.Record.create([ 'enabled', 'connected', 'hostname', 'port', 'username', - 'password', 'deskey', 'emm', 'comment' + 'password', 'deskey', 'emm', 'emmex', 'comment' ]); var store = new Ext.data.JsonStore({ @@ -99,7 +106,7 @@ tvheadend.cwceditor = function() { }); var grid = new tvheadend.tableEditor('Code Word Client', 'cwc', cm, rec, - [enabledColumn, emmColumn], store, + [enabledColumn, emmColumn, emmexColumn], store, 'config_cwc.html', 'key'); tvheadend.comet.on('cwcStatus', function(msg) { diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 7fae890a..5b7d0576 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -169,14 +169,24 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) { var selectedKeys = grid.selModel.selections.keys; var target = panel.getForm().getValues('targetID').targetID; + if(adapterData.satConf) { + var satconf = panel.getForm().getValues('targetSatConfID').targetSatConfID; + var mparams = { + entries:Ext.encode(selectedKeys), + satconf:satconf + }; + } else { + var mparams = { + entries:Ext.encode(selectedKeys) + }; + } + Ext.Ajax.request({ url: "dvb/copymux/" + target, - params: { - entries:Ext.encode(selectedKeys) - }, - failure:function(response,options) { + params: mparams, + failure:function(response,options) { Ext.MessageBox.alert('Server Error','Unable to copy'); - }, + }, success: function() { win.close(); } @@ -191,30 +201,71 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) { 'name'], baseParams: {sibling: adapterId} }); + if(adapterData.satConf) { + targetSatConfStore = new Ext.data.JsonStore({ + root:'entries', + id: 'identifier', + url:'dvb/satconf', + fields: ['identifier', + 'name'], + baseParams: {adapter: adapterId} + }); + satConf = new Ext.form.ComboBox({ + store: targetSatConfStore, + fieldLabel: 'Target Satellite config', + name: 'targetsatconf', + hiddenName: 'targetSatConfID', + editable: false, + allowBlank: false, + triggerAction: 'all', + mode: 'remote', + displayField:'name', + valueField:'identifier', + emptyText: 'Select target adapter first...' + }); + } else { + satConf = null; + } + + var mitems = [ + new Ext.form.ComboBox({ + store: targetStore, + fieldLabel: 'Target adapter', + name: 'targetadapter', + hiddenName: 'targetID', + editable: false, + allowBlank: false, + triggerAction: 'all', + mode: 'remote', + displayField:'name', + valueField:'identifier', + emptyText: 'Select target adapter...', + listeners: { + 'select': function(combo, value) { + if (satConf) { + satConf.emptyText = 'Select satellite configuration...'; + satConf.clearValue(); + targetSatConfStore.baseParams = {adapter: combo.value}; + targetSatConfStore.load(); + satConf.focus(); + satConf.expand(); + } + } + } + }), + ]; + + if (satConf) + mitems.push(satConf); var panel = new Ext.FormPanel({ frame:true, border:true, bodyStyle:'padding:5px', labelAlign: 'right', - labelWidth: 110, + labelWidth: 150, defaultType: 'textfield', - items: [ - - new Ext.form.ComboBox({ - store: targetStore, - fieldLabel: 'Target adapter', - name: 'targetadapter', - hiddenName: 'targetID', - editable: false, - allowBlank: false, - triggerAction: 'all', - mode: 'remote', - displayField:'name', - valueField:'identifier', - emptyText: 'Select target adapter...' - }) - ], + items: mitems, buttons: [{ text: 'Copy', handler: doCopy @@ -225,7 +276,7 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) { title: 'Copy multiplex configuration', layout: 'fit', width: 500, - height: 120, + height: 150, modal: true, plain: true, items: panel diff --git a/src/webui/webui.c b/src/webui/webui.c index 023c9eab..0d9db992 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -156,10 +156,12 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, th_subscription_t //Check socket status getsockopt(hc->hc_fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); - - //Abort upon socket error, or after 20 seconds of silence - if(err || timeouts >= 20){ - run = 0; + if(err) { + tvhlog(LOG_DEBUG, "webui", "Client hung up, exit streaming"); + run = 0; + }else if(timeouts >= 20) { + tvhlog(LOG_WARNING, "webui", "Timeout waiting for packets"); + run = 0; } } pthread_mutex_unlock(&sq->sq_mutex); @@ -168,13 +170,13 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, th_subscription_t timeouts = 0; //Reset timeout counter TAILQ_REMOVE(&sq->sq_queue, sm, sm_link); + pthread_mutex_unlock(&sq->sq_mutex); switch(sm->sm_type) { case SMT_PACKET: { if(!mkm) break; - pkt_ref_inc(sm->sm_data); run = !mk_mux_write_pkt(mkm, sm->sm_data); sm->sm_data = NULL; @@ -221,7 +223,6 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, th_subscription_t break; } streaming_msg_free(sm); - pthread_mutex_unlock(&sq->sq_mutex); } if(mkm) @@ -378,11 +379,12 @@ http_stream_service(http_connection_t *hc, service_t *service) pthread_mutex_unlock(&global_lock); - http_stream_run(hc, &sq, s); - - pthread_mutex_lock(&global_lock); - subscription_unsubscribe(s); - pthread_mutex_unlock(&global_lock); + if(s) { + http_stream_run(hc, &sq, s); + pthread_mutex_lock(&global_lock); + subscription_unsubscribe(s); + pthread_mutex_unlock(&global_lock); + } globalheaders_destroy(gh); tsfix_destroy(tsfix); @@ -413,11 +415,12 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) 0); pthread_mutex_unlock(&global_lock); - http_stream_run(hc, &sq, s); - - pthread_mutex_lock(&global_lock); - subscription_unsubscribe(s); - pthread_mutex_unlock(&global_lock); + if(s) { + http_stream_run(hc, &sq, s); + pthread_mutex_lock(&global_lock); + subscription_unsubscribe(s); + pthread_mutex_unlock(&global_lock); + } globalheaders_destroy(gh); tsfix_destroy(tsfix); @@ -656,10 +659,9 @@ webui_static_content(const char *content_path, const char *http_path, } tvhlog(LOG_ERR, "webui", - "No source path providing HTTP content: \"%s\"." + "No source path providing HTTP content: \"%s\". " "Checked in \"%s\" and in the binary's embedded file system. " - "If you need to move or install the binary, " - "reconfigure with --release", http_path, content_path); + , http_path, content_path); } diff --git a/support/configure.inc b/support/configure.inc index bf821f7b..048e74bd 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -17,8 +17,6 @@ BUILDDIR=build.${PLATFORM} DEFAULT_THEME="new" RELEASE=no -RELEASENAME=`head -n1 ${TOPDIR}/ChangeLog | awk '{print $2}' | sed s/\(// | sed s/\)//` - CONFIG_LIST=" cwc avahi @@ -125,12 +123,6 @@ EOF finalize() { -if [ ${RELEASE} = yes ]; then - echo Release build: ${RELEASENAME} - echo >>${CONFIG_H} "#define TVHEADEND_RELEASE_TAG \"${RELEASENAME}\"" -fi - - cat >> ${CONFIG_MAK} << EOF OSENV=${OSENV} LDFLAGS_cfg += -lm diff --git a/support/dataroot/bundle.c b/support/dataroot/bundle.c new file mode 100644 index 00000000..72abeff0 --- /dev/null +++ b/support/dataroot/bundle.c @@ -0,0 +1,4 @@ +const char *tvheadend_dataroot(void) +{ + return (void *)0; +} diff --git a/support/dataroot/datadir.c b/support/dataroot/datadir.c new file mode 100644 index 00000000..af380ac3 --- /dev/null +++ b/support/dataroot/datadir.c @@ -0,0 +1,7 @@ +#include "config.h" + +const char *tvheadend_dataroot(void) +{ + return TVHEADEND_DATADIR; +} + diff --git a/support/dataroot/wd.c b/support/dataroot/wd.c new file mode 100644 index 00000000..9b653eb2 --- /dev/null +++ b/support/dataroot/wd.c @@ -0,0 +1,4 @@ +const char *tvheadend_dataroot(void) +{ + return "./"; +} diff --git a/support/getver.sh b/support/getver.sh new file mode 100755 index 00000000..938b5a5f --- /dev/null +++ b/support/getver.sh @@ -0,0 +1,8 @@ +#!/bin/sh +revision=`cd "$1" && git describe --dirty --abbrev=5 2>/dev/null | sed -e 's/-/./g'` + +if ! test $revision; then + test $revision || revision=`cd "$1" && git describe --abbrev=5 2>/dev/null | sed -e 's/-/./g'` +fi + +echo $revision diff --git a/support/posix.mk b/support/posix.mk index baad6936..7f7bfc35 100644 --- a/support/posix.mk +++ b/support/posix.mk @@ -1,22 +1,19 @@ +MAN = man/tvheadend.1 +ICON = support/gnome/tvheadend.svg -INSTBIN= ${DESTDIR}${INSTALLPREFIX}/bin -INSTMAN= ${DESTDIR}${INSTALLPREFIX}/share/man/man1 -INSTDBG= ${DESTDIR}${INSTALLPREFIX}/lib/debug/bin -MAN=man/tvheadend.1 +INSTICON= ${DESTDIR}$(prefix)/share/icons/hicolor/scalable/apps -install: ${PROG} ${MAN} - mkdir -p ${INSTBIN} - install -T ${PROG} ${INSTBIN}/tvheadend - mkdir -p ${INSTMAN} - install ${MAN} ${INSTMAN} -install-debug: ${PROG} - mkdir -p ${INSTDBG} - objcopy --only-keep-debug ${INSTBIN}/tvheadend ${INSTDBG}/tvheadend.debug - strip -g ${INSTBIN}/tvheadend - objcopy --add-gnu-debuglink=${INSTDBG}/tvheadend.debug ${INSTBIN}/tvheadend +install: ${PROG}.datadir ${MAN} + install -D ${PROG}.datadir ${bindir}/tvheadend + install -D ${MAN} ${mandir}/tvheadend.1 + + for bundle in ${BUNDLES}; do \ + mkdir -p ${datadir}/$$bundle ;\ + cp -r $$bundle/* ${datadir}/$$bundle ;\ + done + uninstall: - rm -f ${INSTBIN}/tvheadend - rm -f ${INSTDBG}/tvheadend.debug - rm -f ${INSTMAN}/tvheadend.1 + rm -f ${bindir)/tvheadend + rm -f ${mandir)/tvheadend.1 diff --git a/support/version.sh b/support/version.sh index 98035c68..cee64950 100755 --- a/support/version.sh +++ b/support/version.sh @@ -1,23 +1,11 @@ #!/bin/sh -# check for SVN revision number -revision=`cd "$1" && LC_ALL=C svn info 2> /dev/null | grep Revision | cut -d' ' -f2` -test $revision || revision=`cd "$1" && grep revision .svn/entries 2>/dev/null | cut -d '"' -f2` -test $revision || revision=`cd "$1" && sed -n -e '/^dir$/{n;p;q}' .svn/entries 2>/dev/null` -test $revision && revision=SVN-r$revision +revision=`$1/support/getver.sh` -# check for git short hash -if ! test $revision; then - revision=`cd "$1" && git log -1 --pretty=format:%h` - test $revision && revision=git-$revision +NEW_REVISION="#define BUILD_VERSION \"$revision\"" +OLD_REVISION=`cat $2 2> /dev/null` + +# Update version.h only on revision changes to avoid spurious rebuilds +if test "$NEW_REVISION" != "$OLD_REVISION"; then + echo "$NEW_REVISION" > "$2" fi - -# check for debian changelog -if ! test $revision; then - revision=`cd "$1" && cat debian/changelog |head -1|cut -f2 -d' '|sed s/\(//|sed s/\)//` -fi - -# no version number found -test $revision || revision=UNKNOWN - -echo $revision