Commit ff211f10 authored by Jean-Paul Saman's avatar Jean-Paul Saman

Merge branch 'master' of git://git.videolan.org/dvblast into for-videolan

The following conflicts occured because this tree originates from SVN directly, while the
tree which was merged was converted to git by others.

Conflicts:
	AUTHORS
	INSTALL
	Makefile
	NEWS
	README
	TODO
	asi.c
	comm.c
	comm.h
	demux.c
	dvb.c
	dvblast.1
	dvblast.c
	dvblast.h
	dvblast_mmi.sh
	dvblastctl.c
	en50221.c
	en50221.h
	output.c
	udp.c
	util.c
	version.h
parents 8f716be0 9d461a55
.gitattributes export-ignore
.gitignore export-ignore
*.o
dvblast
dvblastctl
# Contributors to DVBlast # Contributors to DVBlast
# $Id$
# #
# The format of this file was inspired by the Linux kernel CREDITS file. # The format of this file was inspired by the Linux kernel CREDITS file.
# Authors are listed alphabetically. # Authors are listed alphabetically.
...@@ -7,6 +6,10 @@ ...@@ -7,6 +6,10 @@
# The fields are: name (N), email (E), web-address (W), CVS account login (C), # The fields are: name (N), email (E), web-address (W), CVS account login (C),
# PGP key ID and fingerprint (P), description (D), and snail-mail address (S). # PGP key ID and fingerprint (P), description (D), and snail-mail address (S).
N: Georgi Chorbadzhiyski
E: gf AT unixsol.org
D: numerous bug fixes and enhancements
N: Marian Ďurkovič N: Marian Ďurkovič
E: md AT bts DOT sk E: md AT bts DOT sk
C: md C: md
...@@ -17,6 +20,10 @@ E: a DOT j DOT gatward AT reading DOT ac DOT uk ...@@ -17,6 +20,10 @@ E: a DOT j DOT gatward AT reading DOT ac DOT uk
C: gatty C: gatty
D: EIT pass-through, IPv6 support, various bug fixes D: EIT pass-through, IPv6 support, various bug fixes
N: Peter Partin
E: peter DOT martin AT tripleplay DASH servies DOT com
D: ATSC, MRTG, PID remap
N: Christophe Massiot N: Christophe Massiot
E: massiot AT via DOT ecp DOT fr E: massiot AT via DOT ecp DOT fr
C: massiot C: massiot
......
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
$Id$
Installing DVBlast Installing DVBlast
================== ==================
No autotools yet... You have to tweak the Makefile by hand, especially Compile the program with `make` and install with `make install`. Your
if your kernel is S2API-enabled but not your distribution (indicate the kernel must support DVB S2API which was merged in Linux 2.6.28 and released
path of your kernel where appropriate). Compile the program with `make` on 24 Dec 2008.
and install with `make install`. It requires the installation of libdvbpsi
0.1.6 or later and libdvbpsi-devel (depending on your distribution). DVBlast 2.X no longer requires libdvbpsi runtime and libdvbpsi-devel for
compilation. Instead you must install biTStream on your build machine (no
runtime library is needed).
To install biTStream, clone git repository and run `make install`.
git clone git://git.videolan.org/bitstream.git
cd bitstream
make install
# DVBlast Makefile # DVBlast Makefile
# Customise the path of your kernel # Customise the path of your kernel
VERSION = 1.2.0 VERSION = 2.1.0
TOPDIR = `basename ${PWD}` TOPDIR = `basename ${PWD}`
GIT_VER = $(shell git describe --tags --dirty --always 2>/dev/null)
CFLAGS += -Wall -O3 -fomit-frame-pointer CFLAGS += -Wall -Wformat-security -O3 -fomit-frame-pointer
CFLAGS += -g CFLAGS += -g
CFLAGS += -I/usr/src/kernel/linux-2.6.29.1/include ifneq "$(GIT_VER)" ""
CFLAGS += -DVERSION_EXTRA=\"git-$(GIT_VER)\"
else
CFLAGS += -DVERSION_EXTRA=\"release\"
endif
LDLIBS += -lrt LDLIBS += -lrt
LDLIBS_DVBLAST += -ldvbpsi -lpthread LDLIBS_DVBLAST += -lpthread
OBJ_DVBLAST = dvblast.o util.o dvb.o udp.o asi.o demux.o output.o en50221.o comm.o OBJ_DVBLAST = dvblast.o util.o dvb.o udp.o asi.o demux.o output.o en50221.o comm.o mrtg-cnt.o
OBJ_DVBLASTCTL = util.o dvblastctl.o OBJ_DVBLASTCTL = util.o dvblastctl.o
ifndef V
Q = @
endif
CLEAN_OBJS = dvblast dvblastctl $(OBJ_DVBLAST) $(OBJ_DVBLASTCTL)
INSTALL_BIN = dvblast dvblastctl dvblast_mmi.sh
INSTALL_MAN = dvblast.1
PREFIX ?= /usr/local PREFIX ?= /usr/local
BIN = $(DESTDIR)/$(PREFIX)/bin BIN = $(subst //,/,$(DESTDIR)/$(PREFIX)/bin)
MAN = $(DESTDIR)/$(PREFIX)/share/man/man1 MAN = $(subst //,/,$(DESTDIR)/$(PREFIX)/share/man/man1)
all: dvblast dvblastctl all: dvblast dvblastctl
$(OBJ_DVBLAST) $(OBJ_DVBLASTCTL): Makefile dvblast.h en50221.h comm.h version.h asi.h .PHONY: clean install uninstall dist
%.o: %.c Makefile dvblast.h en50221.h comm.h version.h asi.h mrtg-cnt.h
@echo "CC $<"
$(Q)$(CC) $(CFLAGS) -c $<
dvblast: $(OBJ_DVBLAST) dvblast: $(OBJ_DVBLAST)
$(CC) -o $@ $(OBJ_DVBLAST) $(LDLIBS_DVBLAST) $(LDLIBS) @echo "LINK $@"
$(Q)$(CC) -o $@ $(OBJ_DVBLAST) $(LDLIBS_DVBLAST) $(LDLIBS)
dvblastctl: $(OBJ_DVBLASTCTL) dvblastctl: $(OBJ_DVBLASTCTL)
@echo "LINK $@"
$(Q)$(CC) -o $@ $(OBJ_DVBLASTCTL) $(LDLIBS_DVBLAST) $(LDLIBS)
clean: clean:
@rm -f dvblast dvblastctl $(OBJ_DVBLAST) $(OBJ_DVBLASTCTL) @echo "CLEAN $(CLEAN_OBJS)"
$(Q)rm -f $(CLEAN_OBJS)
install: all install: all
@install -d $(BIN) @install -d "$(BIN)"
@install -d $(MAN) @install -d "$(MAN)"
@install dvblast dvblastctl dvblast_mmi.sh $(BIN) @echo "INSTALL $(INSTALL_MAN) -> $(MAN)"
@install -m 644 dvblast.1 $(MAN) $(Q)install -m 644 dvblast.1 "$(MAN)"
@echo "INSTALL $(INSTALL_BIN) -> $(BIN)"
$(Q)install dvblast dvblastctl dvblast_mmi.sh "$(BIN)"
uninstall: uninstall:
@rm $(BIN)/dvblast $(BIN)/dvblastctl $(BIN)/dvblast_mmi.sh $(MAN)/dvblast.1 @-for FILE in $(INSTALL_BIN); do \
echo "RM $(BIN)/$$FILE"; \
rm "$(BIN)/$$FILE"; \
done
@-for FILE in $(INSTALL_MAN); do \
echo "RM $(MAN)/$$FILE"; \
rm "$(MAN)/$$FILE"; \
done
dist: dist: clean
( cd ../ && \ @echo "ARCHIVE dvblast-$(VERSION).tar.bz2"
tar -cj --exclude-vcs --exclude $(TOPDIR)/*.tar.bz2 $(TOPDIR)/ > $(TOPDIR)/dvblast-$(VERSION).tar.bz2 ) $(Q)git archive --format=tar --prefix=dvblast-$(VERSION)/ master | bzip2 -9 > dvblast-$(VERSION).tar.bz2
$(Q)ls -l dvblast-$(VERSION).tar.bz2
$Id$ Changes between 2.1 and 2.2:
----------------------------
* Fixed a regression that prevented ECM pass-through (-Y) from working.
* Handle ECM pids that are described in PMT ES descriptor loop.
Changes between 2.0 and 2.1:
----------------------------
* Fix MMI menus which were accidentally broken in 2.0
* Remove ecm and emm output options because they weren't working
* Better handling of changed PSI tables with same version number
Changes between 1.2 and 1.3: Changes between 1.2 and 2.0:
---------------------------- ----------------------------
* Fix latency and potential packet loss during CAM communication
* Add optional kernel patches for lower latency
* Smooth packet output with an extra buffer
* libdvbpsi runtime is no longer needed; biTStream development library
is used instead
* Add the ability to bind to a specific network interface
* Add a configurable MTU
* Add support for NIT in DVB compliance mode
* Syslog support with -l option * Syslog support with -l option
* Override FEC Inner with -F option * Override tuning parameters with new options
* Override Rolloff with -R option * Output all PMTs with -w, all ESs (even unknown) for a program with -z
* Add support for ECM and EMM packets pass-through
* Fix diseqc command with high-band tranponders
* Add basic support for ATSC
* Add support for MRTG statistics
* Add detailed information for TS errors
* Add support for getting PAT/CAT/NIT/SDT tables in dvblastctl
* Add support for getting PMT table for chosen service in dvblastctl
* Add support for getting PID information (bps, error counters and more)
* Add support for setting service name and provider name per output
* Command socket is usable with any input (ASI, DVB, UDP), previously
only DVB input worked
Changes between 1.1 and 1.2: Changes between 1.1 and 1.2:
---------------------------- ----------------------------
......
$Id$
Welcome to DVBlast! Welcome to DVBlast!
=================== ===================
...@@ -13,7 +11,7 @@ It outputs one or several RTP streams carrying transport streams with: ...@@ -13,7 +11,7 @@ It outputs one or several RTP streams carrying transport streams with:
- hardware or software PID filtering - hardware or software PID filtering
- PID-based or service-based demultiplexing - PID-based or service-based demultiplexing
- optional descrambling via CAM device - optional descrambling via CAM device
- EIT, SDT and TDT pass-through for EPG information< - optional DVB tables
DVBlast is written to be the core of a custom IRD, CID,or ASI gateway, DVBlast is written to be the core of a custom IRD, CID,or ASI gateway,
based on a PC with a Linux-supported card. It is very lightweight and based on a PC with a Linux-supported card. It is very lightweight and
...@@ -24,9 +22,10 @@ Current features ...@@ -24,9 +22,10 @@ Current features
================ ================
- Lightweight program designed for extreme memory and CPU conditions - Lightweight program designed for extreme memory and CPU conditions
- Only one dependancy: libdvbpsi - No runtime dependancy; one build dependancy (biTStream)
- CAM menus (MMI) support via an external application - CAM menus (MMI) support via an external application
- The configuration file describing outputs can be reloaded without losing a single packet - The configuration file describing outputs can be reloaded without losing
a single packet
- Support for the new S2API of linux-dvb - Support for the new S2API of linux-dvb
- IPv6 network support - IPv6 network support
- UDP rather than RTP output for IPTV STBs which don't support RTP - UDP rather than RTP output for IPTV STBs which don't support RTP
...@@ -48,6 +47,15 @@ Please note that frequencies are in kHz for DVB-S/S2/C, but Hz for DVB-T. ...@@ -48,6 +47,15 @@ Please note that frequencies are in kHz for DVB-S/S2/C, but Hz for DVB-T.
Symbol rates are in symbols/s, and bandwidths in MHz. If you have several Symbol rates are in symbols/s, and bandwidths in MHz. If you have several
linux-dvb cards in the machine, specify which one to use with -a. linux-dvb cards in the machine, specify which one to use with -a.
You generally want to run DVBlast in DVB compliance mode with option -C.
This option will pass through or generate mandatory DVB tables (NIT, SDT,
EITp/f, TOT, TDT). If you also want to pass-through the EIT schedule tables,
use the -e switch. It is considered a good practice to configure the name
of the network (for the NIT) with the -M option.
If you don't want to set these options on a general basis, you can set them
per output - see below.
Other rarely used options are available - run dvblast -h for more Other rarely used options are available - run dvblast -h for more
information. information.
...@@ -55,6 +63,11 @@ information. ...@@ -55,6 +63,11 @@ information.
Alternative inputs Alternative inputs
================== ==================
DVBlast may handle several DVB adapters in the same machine with the -a switch:
-a 3 will use /dev/dvb/adapter3. Additionally, selecting between frontends on
a single card is supported with the -n switch. This is useful for hybrid
DVB/S + DVB/T cards.
If you own a Computer Modules DVB-ASI input card, you can have DVBlast If you own a Computer Modules DVB-ASI input card, you can have DVBlast
filter and demultiplex the inputs. You just need to specify the slot number filter and demultiplex the inputs. You just need to specify the slot number
with -A. with -A.
...@@ -65,13 +78,25 @@ instance a multi-program transport stream. The address is specified with ...@@ -65,13 +78,25 @@ instance a multi-program transport stream. The address is specified with
such a stream for instance to cross network boundaries between the such a stream for instance to cross network boundaries between the
receivers and the target network. receivers and the target network.
The syntax of the -D option is:
[<src host>[:<src port>]@]<src mcast>[:<port>][/<opts>]*
where <src mcast> is the multicast address carrying the stream, and <src
host> is optionally the address of the source, for source-specific multicast.
Options include:
/udp (for streams without an RTP header)
/mtu=XXXX (sets the maximum UDP packet size)
/ifindex=X (binds to a specific network interface, by link number)
/ifaddr=XXX.XXX.XXX.XXX (binds to a specific network interface, by address)
For example:
-D 239.255.0.2:1234/udp/ifindex=1
Configuring outputs Configuring outputs
=================== ===================
DVBlast reads a configuration file containing one or several lines in the DVBlast reads a configuration file containing one or several lines in the
format : format :
<IP>[:<port>][/udp] <always on> <SID> [<PID>,]* <IP>[:<port>][@<IP>[:<port>]][/<option>]* <always on> <SID> [<PID>,]*
For instance : For instance :
239.255.0.1:1234 1 10750 1234,1235,1236 239.255.0.1:1234 1 10750 1234,1235,1236
...@@ -96,6 +121,28 @@ regularly reset the CAM module if it fails to descramble the service, ...@@ -96,6 +121,28 @@ regularly reset the CAM module if it fails to descramble the service,
assuming the module is dead. Every time it is reset a few TS packets assuming the module is dead. Every time it is reset a few TS packets
will be lost, that is why this feature is optional. will be lost, that is why this feature is optional.
Other options can be set by appending / to the multicast address definition.
Available options include :
/udp (turns on -U for a specific output)
/dvb (turns on -C for a specific output)
/epg (turns on -C -e for a specific output)
/tsid=XXX (sets the transport stream ID)
/ssrc=XXX.XXX.XXX.XXX (sets the RTP synchronization source IPv4)
/retention=XXX (see -E)
/latency=XXX (see -L)
/ttl=XX (see -t)
/tos=XX (sets the IPv4 Type Of Service option)
/mtu=XXXX (sets the maximum UDP packet size)
/srvname=Some_Channel (set service name in SDT)
/srvprovider=Some_Provider (set provider name in SDT)
When setting text options like /srvname or /srvprovider, remember
that the underscore character (_) will be replaced by space ( ).
Several options can be appended, for instance:
239.255.0.1:1234/udp/epg/tsid=42/ssrc=192.168.0.1
The optional "/udp" parameter can be used to force DVBlast to output The optional "/udp" parameter can be used to force DVBlast to output
raw UDP stream. This functionality is provided for backwards compatibility raw UDP stream. This functionality is provided for backwards compatibility
with IPTV set top boxes that don't support RTP and should only be used with IPTV set top boxes that don't support RTP and should only be used
...@@ -140,6 +187,52 @@ from the command-line : ...@@ -140,6 +187,52 @@ from the command-line :
dvblast -c /tmp/dvblast.conf dvblast -c /tmp/dvblast.conf
Buffering
=========
DVB cards usually output packets in big chunks. This can be problematic
with low bitrate multiplexes. By default, DVBlast bufferizes 200 ms
and tries to smooth the output. It may be desired to change this value
with the -L option. The appropriate value for the output latency should be :
chunk_size / multiplex_bitrate
The chunk size for saa7146-based cards is 512000 bits ; for
cx23885-based cards is 192512 bits. The chunk size can be modified with
an appropriate kernel patch (see extra/). The multiplex_bitrate depends
on the symbol rate and many other factors such as the modulation.
The current default value allows for multiplex_rates as low as 2.56 Mbi/s.
Smaller multiplexes are rare but exist, so in that case you may want to
increase the buffer size. A typical DVB multiplex is 30 or 40 Mbi/s, so
the default introduces a superfluous latency ; the buffer can be lowered
to 50 ms if latency is an issue.
DVBlast also has another parameter called "max retention time" (-E).
This controls how TS packets are grouped together in IP datagrams : the
difference between the theorical output times of the first and the last
TS packets cannot exceed the maximal retention time.
IP datagrams are normally output at the output time of the earliest TS
packet ; it implies that the next TS packets are sent too soon and must
be buffered at the receiver level. ISO/IEC 13818-1 makes no provision for
this, since IP wasn't in mind when designing TS, so in theory we risk a
buffer overflow.
However normal IP receivers feature a jitter buffer which can absorb the
overflow ; DVB recommends a 50 ms buffer. DVBlast's default maximal
retention time is just below, at 40 ms, which should be fine in most
situations.
If some anal set-top-box complains about buffer overflows or clock issues,
you may try to lower the value ; the drawback is that on low bitrate
streams it will introduce padding. People with low bitrate streams and
nice receivers with big buffers can raise this value to avoid superfluous
padding and lower the total bitrate.
Please bear in mind though that setting a value for max retention time
greater than the output latency has no effect.
Monitoring Monitoring
========== ==========
...@@ -163,11 +256,6 @@ dvblast_mmi.sh -r /tmp/dvblast.sock ...@@ -163,11 +256,6 @@ dvblast_mmi.sh -r /tmp/dvblast.sock
Advanced features Advanced features
================= =================
DVBlast may handle several DVB adapters in the same machine with the -a switch:
-a 3 will use /dev/dvb/adapter3. Additionally, selecting between frontends on
a single card is supported with the -n switch. This is useful for hybrid
DVB/S + DVB/T cards.
For better latency, run DVBlast in real-time priority: -i 1 (requires root For better latency, run DVBlast in real-time priority: -i 1 (requires root
privileges). privileges).
...@@ -179,7 +267,7 @@ Note that IPv6 addresses specified on command line may need to have the square ...@@ -179,7 +267,7 @@ Note that IPv6 addresses specified on command line may need to have the square
brackets escaped (\[ and \]), depending on your shell. brackets escaped (\[ and \]), depending on your shell.
The -u switch disables the PID filters, so that all PIDs, even the The -u switch disables the PID filters, so that all PIDs, even the
unused ones, can be output. With -e, dvblast also streams EIT and SDT packets unused ones, can be output.
for the related services.
Other options are self-understandable, and are listed in dvblast -h. Other options are self-understandable, and are listed in dvblast -h.
- Test and enhance the API for DVB-C, H, and ATSC support - Test and enhance the API for DVB-C, H, and ATSC support
- Win32 support - Win32 support
- Improve build system (autostuff) - Improve build system (autostuff)
- Rework the EN 50 221 machinery to avoid blocking TPDU_Recv and Send
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
* asi.c: support for Computer Modules ASI cards * asi.c: support for Computer Modules ASI cards
***************************************************************************** *****************************************************************************
* Copyright (C) 2004, 2009 VideoLAN * Copyright (C) 2004, 2009 VideoLAN
* $Id: asi.c 9 2007-03-15 16:58:05Z cmassiot $
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
...@@ -18,13 +17,14 @@ ...@@ -18,13 +17,14 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <bitstream/common.h>
#include "asi.h" #include "asi.h"
#include "dvblast.h" #include "dvblast.h"
...@@ -55,10 +57,12 @@ ...@@ -55,10 +57,12 @@
#define ASI_DEVICE "/dev/asirx%u" #define ASI_DEVICE "/dev/asirx%u"
#define ASI_TIMESTAMPS_FILE "/sys/class/asi/asirx%u/timestamps" #define ASI_TIMESTAMPS_FILE "/sys/class/asi/asirx%u/timestamps"
#define ASI_BUFSIZE_FILE "/sys/class/asi/asirx%u/bufsize" #define ASI_BUFSIZE_FILE "/sys/class/asi/asirx%u/bufsize"
#define ASI_LOCK_TIMEOUT 5000000 /* 5 s */
static int i_handle; static int i_handle;
static int i_bufsize; static int i_bufsize;
static uint8_t p_pid_filter[8192 / 8]; static uint8_t p_pid_filter[8192 / 8];
static mtime_t i_last_packet = 0;
/***************************************************************************** /*****************************************************************************
* Local helpers * Local helpers
...@@ -167,27 +171,33 @@ void asi_Open( void ) ...@@ -167,27 +171,33 @@ void asi_Open( void )
/***************************************************************************** /*****************************************************************************
* asi_Read : read packets from the device * asi_Read : read packets from the device
*****************************************************************************/ *****************************************************************************/
block_t *asi_Read( void ) block_t *asi_Read( mtime_t i_poll_timeout )
{ {
int i, i_len; struct pollfd pfd[2];
struct iovec p_iov[i_bufsize / TS_SIZE]; int i_ret, i_nb_fd = 1;
block_t *p_ts, **pp_current = &p_ts;
for ( ; ; ) pfd[0].fd = i_handle;
pfd[0].events = POLLIN;
if ( i_comm_fd != -1 )
{ {
struct pollfd pfd; pfd[1].fd = i_comm_fd;
pfd[1].events = POLLIN;
i_nb_fd++;
}
i_ret = poll( pfd, i_nb_fd, (i_poll_timeout + 999) / 1000 );
pfd.fd = i_handle; i_wallclock = mdate();
pfd.events = POLLIN | POLLPRI;
if ( poll(&pfd, 1, -1) < 0 ) if ( i_ret < 0 )
{ {
if( errno != EINTR )
msg_Err( NULL, "couldn't poll from device " ASI_DEVICE " (%s)", msg_Err( NULL, "couldn't poll from device " ASI_DEVICE " (%s)",
i_asi_adapter, strerror(errno) ); i_asi_adapter, strerror(errno) );
continue; return NULL;
} }
if ( (pfd.revents & POLLPRI) ) if ( (pfd[0].revents & POLLPRI) )
{ {
unsigned int i_val; unsigned int i_val;
...@@ -210,9 +220,23 @@ block_t *asi_Read( void ) ...@@ -210,9 +220,23 @@ block_t *asi_Read( void )
} }
} }
if ( (pfd.revents & POLLIN) ) if ( (pfd[0].revents & POLLIN) )
{
struct iovec p_iov[i_bufsize / TS_SIZE];
block_t *p_ts, **pp_current = &p_ts;
int i, i_len;
if ( !i_last_packet )
{
switch (i_print_type) {
case PRINT_XML:
printf("<STATUS type=\"lock\" status=\"1\"/>\n");
break; break;
default:
printf("frontend has acquired lock\n" );
}
} }
i_last_packet = i_wallclock;
for ( i = 0; i < i_bufsize / TS_SIZE; i++ ) for ( i = 0; i < i_bufsize / TS_SIZE; i++ )
{ {
...@@ -243,6 +267,23 @@ block_t *asi_Read( void ) ...@@ -243,6 +267,23 @@ block_t *asi_Read( void )
*pp_current = NULL; *pp_current = NULL;
return p_ts; return p_ts;
}
else if ( i_last_packet && i_last_packet + ASI_LOCK_TIMEOUT < i_wallclock )
{
switch (i_print_type) {
case PRINT_XML:
printf("<STATUS type=\"lock\" status=\"0\"/>\n");
break;
default:
printf("frontend has lost lock\n" );
}
i_last_packet = 0;
}
if ( i_comm_fd != -1 && pfd[1].revents )
comm_Read();
return NULL;
} }
/***************************************************************************** /*****************************************************************************
...@@ -272,3 +313,12 @@ void asi_UnsetFilter( int i_fd, uint16_t i_pid ) ...@@ -272,3 +313,12 @@ void asi_UnsetFilter( int i_fd, uint16_t i_pid )
msg_Warn( NULL, "couldn't remove filter on PID %u", i_pid ); msg_Warn( NULL, "couldn't remove filter on PID %u", i_pid );
#endif #endif
} }
/*****************************************************************************
* asi_Reset
*****************************************************************************/
void asi_Reset( void )
{
msg_Warn( NULL, "asi_Reset() do nothing" );
}
...@@ -2,29 +2,21 @@ ...@@ -2,29 +2,21 @@
* comm.c: Handles the communication socket (linux-dvb only) * comm.c: Handles the communication socket (linux-dvb only)
***************************************************************************** *****************************************************************************
* Copyright (C) 2008 VideoLAN * Copyright (C) 2008 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software. It comes without any warranty, to
* it under the terms of the GNU General Public License as published by * the extent permitted by applicable law. You can redistribute it
* the Free Software Foundation; either version 2 of the License, or * and/or modify it under the terms of the Do What The Fuck You Want
* (at your option) any later version. * To Public License, Version 2, as published by Sam Hocevar. See
* * http://sam.zoy.org/wtfpl/COPYING for more details.
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -48,8 +40,7 @@ int i_comm_fd = -1; ...@@ -48,8 +40,7 @@ int i_comm_fd = -1;
*****************************************************************************/ *****************************************************************************/
void comm_Open( void ) void comm_Open( void )
{ {
int i_mask; int i_size = COMM_MAX_MSG_CHUNK;
int i_size = 65535;
struct sockaddr_un sun_server; struct sockaddr_un sun_server;
if ( (i_comm_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) == -1 ) if ( (i_comm_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) == -1 )
...@@ -64,18 +55,15 @@ void comm_Open( void ) ...@@ -64,18 +55,15 @@ void comm_Open( void )
sun_server.sun_family = AF_UNIX; sun_server.sun_family = AF_UNIX;
strncpy( sun_server.sun_path, psz_srv_socket, sizeof(sun_server.sun_path) ); strncpy( sun_server.sun_path, psz_srv_socket, sizeof(sun_server.sun_path) );
sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0'; sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0';
i_mask = umask(077);
if ( bind( i_comm_fd, (struct sockaddr *)&sun_server, if ( bind( i_comm_fd, (struct sockaddr *)&sun_server,
SUN_LEN(&sun_server) ) < 0 ) SUN_LEN(&sun_server) ) < 0 )
{ {
msg_Err( NULL, "cannot bind comm socket (%s)", strerror(errno) ); msg_Err( NULL, "cannot bind comm socket (%s)", strerror(errno) );
umask( i_mask );
close( i_comm_fd ); close( i_comm_fd );
i_comm_fd = -1; i_comm_fd = -1;
return; return;
} }
umask( i_mask );
} }
/***************************************************************************** /*****************************************************************************
...@@ -88,12 +76,16 @@ void comm_Read( void ) ...@@ -88,12 +76,16 @@ void comm_Read( void )
ssize_t i_size, i_answer_size = 0; ssize_t i_size, i_answer_size = 0;
uint8_t p_buffer[COMM_BUFFER_SIZE], p_answer[COMM_BUFFER_SIZE]; uint8_t p_buffer[COMM_BUFFER_SIZE], p_answer[COMM_BUFFER_SIZE];
uint8_t i_command, i_answer; uint8_t i_command, i_answer;
uint8_t *p_packed_section;
unsigned int i_packed_section_size;
uint8_t *p_input = p_buffer + COMM_HEADER_SIZE;
uint8_t *p_output = p_answer + COMM_HEADER_SIZE;
i_size = recvfrom( i_comm_fd, p_buffer, COMM_BUFFER_SIZE, 0, i_size = recvfrom( i_comm_fd, p_buffer, COMM_BUFFER_SIZE, 0,
(struct sockaddr *)&sun_client, &sun_length ); (struct sockaddr *)&sun_client, &sun_length );
if ( i_size < COMM_HEADER_SIZE ) if ( i_size < COMM_HEADER_SIZE )
{ {
msg_Err( NULL, "cannot read comm socket (%d:%s)\n", i_size, msg_Err( NULL, "cannot read comm socket (%zd:%s)\n", i_size,
strerror(errno) ); strerror(errno) );
return; return;
} }
...@@ -111,10 +103,28 @@ void comm_Read( void ) ...@@ -111,10 +103,28 @@ void comm_Read( void )
i_command = p_buffer[1]; i_command = p_buffer[1];
if ( i_frequency == 0 ) /* ASI or UDP, disable DVB only commands */
{
switch ( i_command )
{
case CMD_FRONTEND_STATUS:
case CMD_MMI_STATUS:
case CMD_MMI_SLOT_STATUS:
case CMD_MMI_OPEN:
case CMD_MMI_CLOSE:
case CMD_MMI_RECV:
case CMD_MMI_SEND_TEXT:
case CMD_MMI_SEND_CHOICE:
i_answer = RET_NODATA;
i_answer_size = 0;
goto return_answer;
}
}
switch ( i_command ) switch ( i_command )
{ {
case CMD_RELOAD: case CMD_RELOAD:
b_hup_received = 1; b_conf_reload = 1;
i_answer = RET_OK; i_answer = RET_OK;
i_answer_size = 0; i_answer_size = 0;
break; break;
...@@ -130,39 +140,125 @@ void comm_Read( void ) ...@@ -130,39 +140,125 @@ void comm_Read( void )
break; break;
case CMD_MMI_SLOT_STATUS: case CMD_MMI_SLOT_STATUS:
i_answer = en50221_StatusMMISlot( p_buffer + COMM_HEADER_SIZE, i_answer = en50221_StatusMMISlot( p_input, i_size - COMM_HEADER_SIZE,
i_size - COMM_HEADER_SIZE,
p_answer + COMM_HEADER_SIZE, p_answer + COMM_HEADER_SIZE,
&i_answer_size ); &i_answer_size );
break; break;
case CMD_MMI_OPEN: case CMD_MMI_OPEN:
i_answer = en50221_OpenMMI( p_buffer + COMM_HEADER_SIZE, i_answer = en50221_OpenMMI( p_input, i_size - COMM_HEADER_SIZE );
i_size - COMM_HEADER_SIZE );
break; break;
case CMD_MMI_CLOSE: case CMD_MMI_CLOSE:
i_answer = en50221_CloseMMI( p_buffer + COMM_HEADER_SIZE, i_answer = en50221_CloseMMI( p_input, i_size - COMM_HEADER_SIZE );
i_size - COMM_HEADER_SIZE );
break; break;
case CMD_MMI_RECV: case CMD_MMI_RECV:
i_answer = en50221_GetMMIObject( p_buffer + COMM_HEADER_SIZE, i_answer = en50221_GetMMIObject( p_input, i_size - COMM_HEADER_SIZE,
i_size - COMM_HEADER_SIZE,
p_answer + COMM_HEADER_SIZE, p_answer + COMM_HEADER_SIZE,
&i_answer_size ); &i_answer_size );
break; break;
case CMD_MMI_SEND: case CMD_MMI_SEND_TEXT:
i_answer = en50221_SendMMIObject( p_buffer + COMM_HEADER_SIZE, case CMD_MMI_SEND_CHOICE:
i_size - COMM_HEADER_SIZE ); i_answer = en50221_SendMMIObject( p_input, i_size - COMM_HEADER_SIZE );
break; break;
case CMD_SHUTDOWN: case CMD_SHUTDOWN:
msg_Err( NULL, "shutdown via comm" ); b_exit_now = 1;
exit(EXIT_SUCCESS); i_answer = RET_OK;
/* this is a bit violent, but hey, closing everything cleanly i_answer_size = 0;
* would do approximately the same */ break;
case CMD_GET_PAT:
case CMD_GET_CAT:
case CMD_GET_NIT:
case CMD_GET_SDT:
{
#define CASE_TABLE(x) \
case CMD_GET_##x: \
{ \
i_answer = RET_##x; \
p_packed_section = demux_get_current_packed_##x(&i_packed_section_size); \
break; \
}
switch ( i_command )
{
CASE_TABLE(PAT)
CASE_TABLE(CAT)
CASE_TABLE(NIT)
CASE_TABLE(SDT)
}
#undef CASE_TABLE
if ( p_packed_section && i_packed_section_size )
{
if ( i_packed_section_size <= COMM_BUFFER_SIZE - COMM_HEADER_SIZE )
{
i_answer_size = i_packed_section_size;
memcpy( p_answer + COMM_HEADER_SIZE, p_packed_section, i_packed_section_size );
} else {
msg_Err( NULL, "section size is too big (%u)\n", i_packed_section_size );
i_answer = RET_NODATA;
}
free( p_packed_section );
} else {
i_answer = RET_NODATA;
}
break;
}
case CMD_GET_PMT:
{
if ( i_size < COMM_HEADER_SIZE + 2 )
{
msg_Err( NULL, "command packet is too short (%zd)\n", i_size );
return;
}
uint16_t i_sid = (uint16_t)((p_input[0] << 8) | p_input[1]);
p_packed_section = demux_get_packed_PMT(i_sid, &i_packed_section_size);
if ( p_packed_section && i_packed_section_size )
{
i_answer = RET_PMT;
i_answer_size = i_packed_section_size;
memcpy( p_answer + COMM_HEADER_SIZE, p_packed_section, i_packed_section_size );
free( p_packed_section );
} else {
i_answer = RET_NODATA;
}
break;
}
case CMD_GET_PIDS:
{
i_answer = RET_PIDS;
i_answer_size = sizeof(struct cmd_pid_info);
demux_get_PIDS_info( p_output );
break;
}
case CMD_GET_PID:
{
if ( i_size < COMM_HEADER_SIZE + 2 )
{
msg_Err( NULL, "command packet is too short (%zd)\n", i_size );
return;
}
uint16_t i_pid = (uint16_t)((p_input[0] << 8) | p_input[1]);
if ( i_pid >= MAX_PIDS ) {
i_answer = RET_NODATA;
} else {
i_answer = RET_PID;
i_answer_size = sizeof(ts_pid_info_t);
demux_get_PID_info( i_pid, p_output );
}
break;
}
default: default:
msg_Err( NULL, "wrong command %u", i_command ); msg_Err( NULL, "wrong command %u", i_command );
...@@ -171,14 +267,33 @@ void comm_Read( void ) ...@@ -171,14 +267,33 @@ void comm_Read( void )
break; break;
} }
return_answer:
p_answer[0] = COMM_HEADER_MAGIC; p_answer[0] = COMM_HEADER_MAGIC;
p_answer[1] = i_answer; p_answer[1] = i_answer;
p_answer[2] = 0; p_answer[2] = 0;
p_answer[3] = 0; p_answer[3] = 0;
msg_Dbg( NULL, "answering %d to %d with size %d", i_answer, i_command, uint32_t *p_size = (uint32_t *)&p_answer[4];
*p_size = i_answer_size + COMM_HEADER_SIZE;
msg_Dbg( NULL, "answering %d to %d with size %zd", i_answer, i_command,
i_answer_size ); i_answer_size );
if ( sendto( i_comm_fd, p_answer, i_answer_size + COMM_HEADER_SIZE, 0, #define min(a, b) (a < b ? a : b)
(struct sockaddr *)&sun_client, sun_length ) < 0 ) ssize_t i_sended = 0;
ssize_t i_to_send = i_answer_size + COMM_HEADER_SIZE;
do {
ssize_t i_sent = sendto( i_comm_fd, p_answer + i_sended,
min(i_to_send, COMM_MAX_MSG_CHUNK), 0,
(struct sockaddr *)&sun_client, sun_length );
if ( i_sent < 0 ) {
msg_Err( NULL, "cannot send comm socket (%s)", strerror(errno) ); msg_Err( NULL, "cannot send comm socket (%s)", strerror(errno) );
break;
}
i_sended += i_sent;
i_to_send -= i_sent;
} while ( i_to_send > 0 );
#undef min
>>>>>>> 9d461a5542117d8ec914b2572b3d1a6a2a9636e0
} }
...@@ -2,23 +2,14 @@ ...@@ -2,23 +2,14 @@
* comm.h * comm.h
***************************************************************************** *****************************************************************************
* Copyright (C) 2008 VideoLAN * Copyright (C) 2008 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software. It comes without any warranty, to
* it under the terms of the GNU General Public License as published by * the extent permitted by applicable law. You can redistribute it
* the Free Software Foundation; either version 2 of the License, or * and/or modify it under the terms of the Do What The Fuck You Want
* (at your option) any later version. * To Public License, Version 2, as published by Sam Hocevar. See
* * http://sam.zoy.org/wtfpl/COPYING for more details.
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
/* DVB Card Drivers */ /* DVB Card Drivers */
...@@ -27,28 +18,53 @@ ...@@ -27,28 +18,53 @@
#include <linux/dvb/frontend.h> #include <linux/dvb/frontend.h>
#include <linux/dvb/ca.h> #include <linux/dvb/ca.h>
#define COMM_BUFFER_SIZE 4096 #include <bitstream/mpeg/psi.h>
#define COMM_HEADER_SIZE 4
#define COMM_HEADER_MAGIC 0x48 #define COMM_HEADER_SIZE 8
#define COMM_BUFFER_SIZE (COMM_HEADER_SIZE + ((PSI_PRIVATE_MAX_SIZE + PSI_HEADER_SIZE) * (PSI_TABLE_MAX_SECTIONS / 2)))
#define COMM_HEADER_MAGIC 0x49
#define COMM_MAX_MSG_CHUNK 65535
#define CMD_RELOAD 1 typedef enum {
#define CMD_SHUTDOWN 2 CMD_INVALID = 0,
#define CMD_FRONTEND_STATUS 3 CMD_RELOAD = 1,
#define CMD_MMI_STATUS 4 CMD_SHUTDOWN = 2,
#define CMD_MMI_SLOT_STATUS 5 /* arg: slot */ CMD_FRONTEND_STATUS = 3,
#define CMD_MMI_OPEN 6 /* arg: slot */ CMD_MMI_STATUS = 4,
#define CMD_MMI_CLOSE 7 /* arg: slot */ CMD_MMI_SLOT_STATUS = 5, /* arg: slot */
#define CMD_MMI_RECV 8 /* arg: slot */ CMD_MMI_OPEN = 6, /* arg: slot */
#define CMD_MMI_SEND 9 /* arg: slot, en50221_mmi_object_t */ CMD_MMI_CLOSE = 7, /* arg: slot */
CMD_MMI_RECV = 8, /* arg: slot */
CMD_GET_PAT = 10,
CMD_GET_CAT = 11,
CMD_GET_NIT = 12,
CMD_GET_SDT = 13,
CMD_GET_PMT = 14, /* arg: service_id (uint16_t) */
CMD_GET_PIDS = 15,
CMD_GET_PID = 16, /* arg: pid (uint16_t) */
CMD_MMI_SEND_TEXT = 17, /* arg: slot, en50221_mmi_object_t */
CMD_MMI_SEND_CHOICE = 18, /* arg: slot, en50221_mmi_object_t */
} ctl_cmd_t;
#define RET_OK 0 typedef enum {
#define RET_ERR 1 RET_OK = 0,
#define RET_FRONTEND_STATUS 2 RET_ERR = 1,
#define RET_MMI_STATUS 3 RET_FRONTEND_STATUS = 2,
#define RET_MMI_SLOT_STATUS 4 RET_MMI_STATUS = 3,
#define RET_MMI_RECV 5 RET_MMI_SLOT_STATUS = 4,
#define RET_MMI_WAIT 6 RET_MMI_RECV = 5,
#define RET_HUH 255 RET_MMI_WAIT = 6,
RET_NODATA = 7,
RET_PAT = 8,
RET_CAT = 9,
RET_NIT = 10,
RET_SDT = 11,
RET_PMT = 12,
RET_PIDS = 13,
RET_PID = 14,
RET_HUH = 255,
} ctl_cmd_answer_t;
struct ret_frontend_status struct ret_frontend_status
{ {
...@@ -77,3 +93,8 @@ struct cmd_mmi_send ...@@ -77,3 +93,8 @@ struct cmd_mmi_send
uint8_t i_slot; uint8_t i_slot;
en50221_mmi_object_t object; en50221_mmi_object_t object;
}; };
struct cmd_pid_info
{
ts_pid_info_t pids[MAX_PIDS];
};
This source diff could not be displayed because it is too large. You can view the blob instead.
/***************************************************************************** /*****************************************************************************
* dvb.c: linux-dvb input for DVBlast * dvb.c: linux-dvb input for DVBlast
***************************************************************************** *****************************************************************************
* Copyright (C) 2008-2009 VideoLAN * Copyright (C) 2008-2010 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
...@@ -23,7 +22,9 @@ ...@@ -23,7 +22,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -47,26 +48,29 @@ ...@@ -47,26 +48,29 @@
#include "en50221.h" #include "en50221.h"
#include "comm.h" #include "comm.h"
#include <bitstream/common.h>
/***************************************************************************** /*****************************************************************************
* Local declarations * Local declarations
*****************************************************************************/ *****************************************************************************/
#define FRONTEND_LOCK_TIMEOUT 30000000 /* 30 s */ #define DVR_READ_TIMEOUT 30000000 /* 30 s */
#define CA_POLL_PERIOD 100000 /* 100 ms */
#define MAX_READ_ONCE 50 #define MAX_READ_ONCE 50
#define DVR_BUFFER_SIZE 40*188*1024 /* bytes */ #define DVR_BUFFER_SIZE 40*188*1024 /* bytes */
static int i_frontend, i_dvr; static int i_frontend, i_dvr;
static fe_status_t i_last_status; static fe_status_t i_last_status;
static mtime_t i_frontend_timeout; static mtime_t i_frontend_timeout;
static mtime_t i_last_packet = 0;
static mtime_t i_ca_next_event = 0; static mtime_t i_ca_next_event = 0;
static block_t *p_freelist = NULL; static block_t *p_freelist = NULL;
mtime_t i_ca_timeout = 0;
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
static block_t *DVRRead( void ); static block_t *DVRRead( void );
static void FrontendPoll( void ); static void FrontendPoll( void );
static void FrontendSet( void ); static void FrontendSet( bool b_reset );
/***************************************************************************** /*****************************************************************************
* dvb_Open * dvb_Open
...@@ -77,6 +81,8 @@ void dvb_Open( void ) ...@@ -77,6 +81,8 @@ void dvb_Open( void )
msg_Dbg( NULL, "using linux-dvb API version %d", DVB_API_VERSION ); msg_Dbg( NULL, "using linux-dvb API version %d", DVB_API_VERSION );
i_wallclock = mdate();
sprintf( psz_tmp, "/dev/dvb/adapter%d/frontend%d", i_adapter, i_fenum ); sprintf( psz_tmp, "/dev/dvb/adapter%d/frontend%d", i_adapter, i_fenum );
if( (i_frontend = open(psz_tmp, O_RDWR | O_NONBLOCK)) < 0 ) if( (i_frontend = open(psz_tmp, O_RDWR | O_NONBLOCK)) < 0 )
{ {
...@@ -85,7 +91,7 @@ void dvb_Open( void ) ...@@ -85,7 +91,7 @@ void dvb_Open( void )
exit(1); exit(1);
} }
FrontendSet(); FrontendSet(true);
sprintf( psz_tmp, "/dev/dvb/adapter%d/dvr%d", i_adapter, i_fenum ); sprintf( psz_tmp, "/dev/dvb/adapter%d/dvr%d", i_adapter, i_fenum );
...@@ -103,16 +109,25 @@ void dvb_Open( void ) ...@@ -103,16 +109,25 @@ void dvb_Open( void )
} }
en50221_Init(); en50221_Init();
i_ca_next_event = mdate() + i_ca_timeout; i_ca_next_event = mdate() + CA_POLL_PERIOD;
}
/*****************************************************************************
* dvb_Reset
*****************************************************************************/
void dvb_Reset( void )
{
FrontendSet(true);
} }
/***************************************************************************** /*****************************************************************************
* dvb_Read * dvb_Read
*****************************************************************************/ *****************************************************************************/
block_t *dvb_Read( void ) block_t *dvb_Read( mtime_t i_poll_timeout )
{ {
struct pollfd ufds[3]; struct pollfd ufds[4];
int i_ret, i_nb_fd = 2; int i_ret, i_nb_fd = 2;
block_t *p_blocks = NULL;
memset( ufds, 0, sizeof(ufds) ); memset( ufds, 0, sizeof(ufds) );
ufds[0].fd = i_dvr; ufds[0].fd = i_dvr;
...@@ -121,12 +136,20 @@ block_t *dvb_Read( void ) ...@@ -121,12 +136,20 @@ block_t *dvb_Read( void )
ufds[1].events = POLLERR | POLLPRI; ufds[1].events = POLLERR | POLLPRI;
if ( i_comm_fd != -1 ) if ( i_comm_fd != -1 )
{ {
ufds[2].fd = i_comm_fd; ufds[i_nb_fd].fd = i_comm_fd;
ufds[2].events = POLLIN; ufds[i_nb_fd].events = POLLIN;
i_nb_fd = 3; i_nb_fd++;
}
if ( i_ca_handle && i_ca_type == CA_CI_LINK )
{
ufds[i_nb_fd].fd = i_ca_handle;
ufds[i_nb_fd].events = POLLIN;
i_nb_fd++;
} }
i_ret = poll( ufds, i_nb_fd, 100 ); i_ret = poll( ufds, i_nb_fd, (i_poll_timeout + 999) / 1000 );
i_wallclock = mdate();
if ( i_ret < 0 ) if ( i_ret < 0 )
{ {
...@@ -135,29 +158,61 @@ block_t *dvb_Read( void ) ...@@ -135,29 +158,61 @@ block_t *dvb_Read( void )
return NULL; return NULL;
} }
if ( i_ca_handle && i_ca_type == CA_CI_LINK if ( ufds[1].revents )
&& mdate() > i_ca_next_event ) FrontendPoll();
if ( ufds[0].revents )
{ {
en50221_Poll(); p_blocks = DVRRead();
i_ca_next_event = mdate() + i_ca_timeout; i_wallclock = mdate();
} }
if ( ufds[1].revents ) if ( p_blocks != NULL )
FrontendPoll(); i_last_packet = i_wallclock;
else if ( !i_frontend_timeout
&& i_wallclock > i_last_packet + DVR_READ_TIMEOUT )
{
msg_Warn( NULL, "no DVR output, resetting" );
FrontendSet(false);
en50221_Reset();
}
if ( i_ca_handle && i_ca_type == CA_CI_LINK )
{
if ( ufds[i_nb_fd - 1].revents )
{
en50221_Read();
i_ca_next_event = i_wallclock + CA_POLL_PERIOD;
}
else if ( i_wallclock > i_ca_next_event )
{
en50221_Poll();
i_ca_next_event = i_wallclock + CA_POLL_PERIOD;
}
}
if ( i_frontend_timeout && mdate() > i_frontend_timeout ) if ( i_frontend_timeout && i_wallclock > i_frontend_timeout )
{ {
if ( i_quit_timeout_duration )
{
msg_Err( NULL, "no lock" );
switch (i_print_type) {
case PRINT_XML:
printf("</TS>\n");
break;
default:
break;
}
exit(EXIT_STATUS_FRONTEND_TIMEOUT);
}
msg_Warn( NULL, "no lock, tuning again" ); msg_Warn( NULL, "no lock, tuning again" );
FrontendSet(); FrontendSet(false);
} }
if ( i_comm_fd != -1 && ufds[2].revents ) if ( i_comm_fd != -1 && ufds[2].revents )
comm_Read(); comm_Read();
if ( ufds[0].revents ) return p_blocks;
return DVRRead();
return NULL;
} }
/***************************************************************************** /*****************************************************************************
...@@ -313,8 +368,19 @@ static void FrontendPoll( void ) ...@@ -313,8 +368,19 @@ static void FrontendPoll( void )
IF_UP( FE_HAS_LOCK ) IF_UP( FE_HAS_LOCK )
{ {
int32_t i_value = 0; int32_t i_value = 0;
msg_Dbg( NULL, "frontend has acquired lock" ); msg_Info( NULL, "frontend has acquired lock" );
switch (i_print_type) {
case PRINT_XML:
printf("<STATUS type=\"lock\" status=\"1\" />\n");
break;
default:
printf("frontend has acquired lock\n" );
}
i_frontend_timeout = 0; i_frontend_timeout = 0;
i_last_packet = i_wallclock;
if ( i_quit_timeout_duration && !i_quit_timeout )
i_quit_timeout = i_wallclock + i_quit_timeout_duration;
/* Read some statistics */ /* Read some statistics */
if( ioctl( i_frontend, FE_READ_BER, &i_value ) >= 0 ) if( ioctl( i_frontend, FE_READ_BER, &i_value ) >= 0 )
...@@ -327,14 +393,21 @@ static void FrontendPoll( void ) ...@@ -327,14 +393,21 @@ static void FrontendPoll( void )
else else
{ {
msg_Dbg( NULL, "frontend has lost lock" ); msg_Dbg( NULL, "frontend has lost lock" );
i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT; switch (i_print_type) {
case PRINT_XML:
printf("<STATUS type=\"lock\" status=\"0\"/>\n");
break;
default:
printf("frontend has lost lock\n" );
}
i_frontend_timeout = i_wallclock + i_frontend_timeout_duration;
} }
IF_UP( FE_REINIT ) IF_UP( FE_REINIT )
{ {
/* The frontend was reinited. */ /* The frontend was reinited. */
msg_Warn( NULL, "reiniting frontend"); msg_Warn( NULL, "reiniting frontend");
FrontendSet(); FrontendSet(true);
} }
} }
#undef IF_UP #undef IF_UP
...@@ -452,7 +525,7 @@ static int FrontendDoDiseqc(void) ...@@ -452,7 +525,7 @@ static int FrontendDoDiseqc(void)
if( ioctl( i_frontend, FE_DISEQC_SEND_BURST, if( ioctl( i_frontend, FE_DISEQC_SEND_BURST,
i_satnum == 0xB ? SEC_MINI_B : SEC_MINI_A ) < 0 ) i_satnum == 0xB ? SEC_MINI_B : SEC_MINI_A ) < 0 )
{ {
msg_Err( NULL, "ioctl FE_SEND_BURST failed (%m)", strerror(errno) ); msg_Err( NULL, "ioctl FE_SEND_BURST failed (%s)", strerror(errno) );
exit(1); exit(1);
} }
msleep(100000); /* ... */ msleep(100000); /* ... */
...@@ -476,8 +549,51 @@ static int FrontendDoDiseqc(void) ...@@ -476,8 +549,51 @@ static int FrontendDoDiseqc(void)
#if DVB_API_VERSION >= 5 #if DVB_API_VERSION >= 5
/***************************************************************************** /*****************************************************************************
* GetModulation : helper functions for S2API * Helper functions for S2API
*****************************************************************************/ *****************************************************************************/
static fe_spectral_inversion_t GetInversion(void)
{
switch ( i_inversion )
{
case 0: return INVERSION_OFF;
case 1: return INVERSION_ON;
default:
msg_Warn( NULL, "invalid inversion %d", i_inversion );
case -1: return INVERSION_AUTO;
}
}
static fe_code_rate_t GetFEC(fe_caps_t fe_caps, int i_fec_value)
{
#define GET_FEC_INNER(fec, val) \
if ( (fe_caps & FE_CAN_##fec) && (i_fec_value == val) ) \
return fec;
GET_FEC_INNER(FEC_AUTO, 999);
GET_FEC_INNER(FEC_AUTO, -1);
if (i_fec_value == 0)
return FEC_NONE;
GET_FEC_INNER(FEC_1_2, 12);
GET_FEC_INNER(FEC_2_3, 23);
GET_FEC_INNER(FEC_3_4, 34);
if (i_fec_value == 35)
return FEC_3_5;
GET_FEC_INNER(FEC_4_5, 45);
GET_FEC_INNER(FEC_5_6, 56);
GET_FEC_INNER(FEC_6_7, 67);
GET_FEC_INNER(FEC_7_8, 78);
GET_FEC_INNER(FEC_8_9, 89);
if (i_fec_value == 910)
return FEC_9_10;
#undef GET_FEC_INNER
msg_Warn(NULL, "invalid FEC %d", i_fec_value );
return FEC_AUTO;
}
#define GetFECInner(caps) GetFEC(caps, i_fec)
#define GetFECLP(caps) GetFEC(caps, i_fec_lp)
static fe_modulation_t GetModulation(void) static fe_modulation_t GetModulation(void)
{ {
#define GET_MODULATION( mod ) \ #define GET_MODULATION( mod ) \
...@@ -503,45 +619,77 @@ static fe_modulation_t GetModulation(void) ...@@ -503,45 +619,77 @@ static fe_modulation_t GetModulation(void)
exit(1); exit(1);
} }
static fe_code_rate_t GetFECInner(fe_caps_t fe_caps) static fe_pilot_t GetPilot(void)
{ {
#define GET_FEC_INNER(fec,val) \ switch ( i_pilot )
if ( (fe_caps & FE_CAN_##fec) && (i_fec == val) ) \ {
return fec; case 0: return PILOT_OFF;
case 1: return PILOT_ON;
GET_FEC_INNER(FEC_AUTO,999); default:
if (i_fec == 0) msg_Warn( NULL, "invalid pilot %d", i_pilot );
return FEC_NONE; case -1: return PILOT_AUTO;
GET_FEC_INNER(FEC_1_2, 12); }
GET_FEC_INNER(FEC_2_3, 23);
GET_FEC_INNER(FEC_3_4, 34);
if (i_fec == 35)
return FEC_3_5;
GET_FEC_INNER(FEC_4_5, 45);
GET_FEC_INNER(FEC_5_6, 56);
GET_FEC_INNER(FEC_6_7, 67);
GET_FEC_INNER(FEC_7_8, 78);
GET_FEC_INNER(FEC_8_9, 89);
if (i_fec == 910)
return FEC_9_10;
#undef GET_FEC_INNER
msg_Err( NULL, "invalid fec-inner %d", i_fec );
exit(1);
} }
static fe_rolloff_t GetRollOff(int rolloff) static fe_rolloff_t GetRollOff(void)
{ {
switch( rolloff ) switch ( i_rolloff )
{ {
case -1:
case 0: return ROLLOFF_AUTO; case 0: return ROLLOFF_AUTO;
case 20: return ROLLOFF_20; case 20: return ROLLOFF_20;
case 25: return ROLLOFF_25; case 25: return ROLLOFF_25;
default: default:
msg_Warn( NULL, "invalid rolloff %d", i_rolloff );
case 35: return ROLLOFF_35; case 35: return ROLLOFF_35;
} }
} }
static fe_guard_interval_t GetGuard(void)
{
switch ( i_guard )
{
case 32: return GUARD_INTERVAL_1_32;
case 16: return GUARD_INTERVAL_1_16;
case 8: return GUARD_INTERVAL_1_8;
case 4: return GUARD_INTERVAL_1_4;
default:
msg_Warn( NULL, "invalid guard interval %d", i_guard );
case -1:
case 0: return GUARD_INTERVAL_AUTO;
}
}
static fe_transmit_mode_t GetTransmission(void)
{
switch ( i_transmission )
{
case 2: return TRANSMISSION_MODE_2K;
case 8: return TRANSMISSION_MODE_8K;
#ifdef TRANSMISSION_MODE_4K
case 4: return TRANSMISSION_MODE_4K;
#endif
default:
msg_Warn( NULL, "invalid tranmission mode %d", i_transmission );
case -1:
case 0: return TRANSMISSION_MODE_AUTO;
}
}
static fe_hierarchy_t GetHierarchy(void)
{
switch ( i_hierarchy )
{
case 0: return HIERARCHY_NONE;
case 1: return HIERARCHY_1;
case 2: return HIERARCHY_2;
case 4: return HIERARCHY_4;
default:
msg_Warn( NULL, "invalid intramission mode %d", i_transmission );
case -1: return HIERARCHY_AUTO;
}
}
/***************************************************************************** /*****************************************************************************
* FrontendInfo : Print frontend info * FrontendInfo : Print frontend info
*****************************************************************************/ *****************************************************************************/
...@@ -561,16 +709,16 @@ static void FrontendInfo( struct dvb_frontend_info info ) ...@@ -561,16 +709,16 @@ static void FrontendInfo( struct dvb_frontend_info info )
{ {
msg_Dbg( NULL, "Frontend \"%s\" type \"%s\" supports:", msg_Dbg( NULL, "Frontend \"%s\" type \"%s\" supports:",
info.name, GetFrontendTypeName(info.type) ); info.name, GetFrontendTypeName(info.type) );
msg_Dbg( NULL, "\tfrequency min: %d, max: %d, stepsize: %d, tolerance: %d", msg_Dbg( NULL, " frequency min: %d, max: %d, stepsize: %d, tolerance: %d",
info.frequency_min, info.frequency_max, info.frequency_min, info.frequency_max,
info.frequency_stepsize, info.frequency_tolerance ); info.frequency_stepsize, info.frequency_tolerance );
msg_Dbg( NULL, "\tsymbolrate min: %d, max: %d, tolerance: %d", msg_Dbg( NULL, " symbolrate min: %d, max: %d, tolerance: %d",
info.symbol_rate_min, info.symbol_rate_max, info.symbol_rate_tolerance); info.symbol_rate_min, info.symbol_rate_max, info.symbol_rate_tolerance);
msg_Dbg( NULL, "\tcapabilities:" ); msg_Dbg( NULL, " capabilities:" );
#define FRONTEND_INFO(caps,val,msg) \ #define FRONTEND_INFO(caps,val,msg) \
if ( caps & val ) \ if ( caps & val ) \
msg_Dbg( NULL, "\t\t%s", msg ); msg_Dbg( NULL, " %s", msg );
FRONTEND_INFO( info.caps, FE_IS_STUPID, "FE_IS_STUPID" ) FRONTEND_INFO( info.caps, FE_IS_STUPID, "FE_IS_STUPID" )
FRONTEND_INFO( info.caps, FE_CAN_INVERSION_AUTO, "INVERSION_AUTO" ) FRONTEND_INFO( info.caps, FE_CAN_INVERSION_AUTO, "INVERSION_AUTO" )
...@@ -671,13 +819,30 @@ static struct dtv_properties dvbt_cmdseq = { ...@@ -671,13 +819,30 @@ static struct dtv_properties dvbt_cmdseq = {
.props = dvbt_cmdargs .props = dvbt_cmdargs
}; };
static struct dtv_property atsc_cmdargs[] = {
{ .cmd = DTV_FREQUENCY, .u.data = 0 },
{ .cmd = DTV_MODULATION, .u.data = QAM_AUTO },
{ .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO },
{ .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_ATSC },
{ .cmd = DTV_TUNE },
};
static struct dtv_properties atsc_cmdseq = {
.num = sizeof(atsc_cmdargs)/sizeof(struct dtv_property),
.props = atsc_cmdargs
};
#define FREQUENCY 0 #define FREQUENCY 0
#define MODULATION 1 #define MODULATION 1
#define INVERSION 2 #define INVERSION 2
#define SYMBOL_RATE 3 #define SYMBOL_RATE 3
#define BANDWIDTH 3 #define BANDWIDTH 3
#define FEC_INNER 4 #define FEC_INNER 4
#define FEC_LP 5
#define GUARD 6
#define PILOT 6
#define TRANSMISSION 7
#define ROLLOFF 7 #define ROLLOFF 7
#define HIERARCHY 8
struct dtv_property pclear[] = { struct dtv_property pclear[] = {
{ .cmd = DTV_CLEAR }, { .cmd = DTV_CLEAR },
...@@ -688,7 +853,7 @@ struct dtv_properties cmdclear = { ...@@ -688,7 +853,7 @@ struct dtv_properties cmdclear = {
.props = pclear .props = pclear
}; };
static void FrontendSet( void ) static void FrontendSet( bool b_init )
{ {
struct dvb_frontend_info info; struct dvb_frontend_info info;
struct dtv_properties *p; struct dtv_properties *p;
...@@ -699,6 +864,7 @@ static void FrontendSet( void ) ...@@ -699,6 +864,7 @@ static void FrontendSet( void )
exit(1); exit(1);
} }
if ( b_init )
FrontendInfo( info ); FrontendInfo( info );
/* Clear frontend commands */ /* Clear frontend commands */
...@@ -713,24 +879,33 @@ static void FrontendSet( void ) ...@@ -713,24 +879,33 @@ static void FrontendSet( void )
case FE_OFDM: case FE_OFDM:
p = &dvbt_cmdseq; p = &dvbt_cmdseq;
p->props[FREQUENCY].u.data = i_frequency; p->props[FREQUENCY].u.data = i_frequency;
p->props[INVERSION].u.data = GetInversion();
if ( psz_modulation != NULL ) if ( psz_modulation != NULL )
p->props[MODULATION].u.data = GetModulation(); p->props[MODULATION].u.data = GetModulation();
p->props[BANDWIDTH].u.data = i_bandwidth * 1000000; p->props[BANDWIDTH].u.data = i_bandwidth * 1000000;
p->props[FEC_INNER].u.data = GetFECInner(info.caps);
msg_Dbg( NULL, "tuning OFDM frontend to f=%d bandwidth=%d modulation=%s", p->props[FEC_LP].u.data = GetFECLP(info.caps);
i_frequency, i_bandwidth, p->props[GUARD].u.data = GetGuard();
psz_modulation == NULL ? "qam_auto" : psz_modulation ); p->props[TRANSMISSION].u.data = GetTransmission();
p->props[HIERARCHY].u.data = GetHierarchy();
msg_Dbg( NULL, "tuning OFDM frontend to f=%d bandwidth=%d inversion=%d fec_hp=%d fec_lp=%d hierarchy=%d modulation=%s guard=%d transmission=%d",
i_frequency, i_bandwidth, i_inversion, i_fec, i_fec_lp,
i_hierarchy,
psz_modulation == NULL ? "qam_auto" : psz_modulation,
i_guard, i_transmission );
break; break;
case FE_QAM: case FE_QAM:
p = &dvbc_cmdseq; p = &dvbc_cmdseq;
p->props[FREQUENCY].u.data = i_frequency; p->props[FREQUENCY].u.data = i_frequency;
p->props[INVERSION].u.data = GetInversion();
if ( psz_modulation != NULL ) if ( psz_modulation != NULL )
p->props[MODULATION].u.data = GetModulation(); p->props[MODULATION].u.data = GetModulation();
p->props[SYMBOL_RATE].u.data = i_srate; p->props[SYMBOL_RATE].u.data = i_srate;
msg_Dbg( NULL, "tuning QAM frontend to f=%d srate=%d modulation=%s", msg_Dbg( NULL, "tuning QAM frontend to f=%d srate=%d inversion=%d modulation=%s",
i_frequency, i_srate, i_frequency, i_srate, i_inversion,
psz_modulation == NULL ? "qam_auto" : psz_modulation ); psz_modulation == NULL ? "qam_auto" : psz_modulation );
break; break;
...@@ -739,18 +914,32 @@ static void FrontendSet( void ) ...@@ -739,18 +914,32 @@ static void FrontendSet( void )
{ {
p = &dvbs2_cmdseq; p = &dvbs2_cmdseq;
p->props[MODULATION].u.data = GetModulation(); p->props[MODULATION].u.data = GetModulation();
p->props[ROLLOFF].u.data = GetRollOff(i_rolloff); p->props[PILOT].u.data = GetPilot();
p->props[ROLLOFF].u.data = GetRollOff();
} }
else else
p = &dvbs_cmdseq; p = &dvbs_cmdseq;
p->props[INVERSION].u.data = GetInversion();
p->props[SYMBOL_RATE].u.data = i_srate; p->props[SYMBOL_RATE].u.data = i_srate;
p->props[FEC_INNER].u.data = GetFECInner(info.caps); p->props[FEC_INNER].u.data = GetFECInner(info.caps);
p->props[FREQUENCY].u.data = FrontendDoDiseqc(); p->props[FREQUENCY].u.data = FrontendDoDiseqc();
msg_Dbg( NULL, "tuning QPSK frontend to f=%d srate=%d fec=%d rolloff=%d modulation=%s", msg_Dbg( NULL, "tuning QPSK frontend to f=%d srate=%d inversion=%d fec=%d rolloff=%d modulation=%s pilot=%d",
i_frequency, i_srate, i_fec, i_rolloff, i_frequency, i_srate, i_inversion, i_fec, i_rolloff,
psz_modulation == NULL ? "legacy" : psz_modulation ); psz_modulation == NULL ? "legacy" : psz_modulation, i_pilot );
break;
case FE_ATSC:
p = &atsc_cmdseq;
p->props[FREQUENCY].u.data = i_frequency;
p->props[INVERSION].u.data = GetInversion();
if ( psz_modulation != NULL )
p->props[MODULATION].u.data = GetModulation();
msg_Dbg( NULL, "tuning ATSC frontend to f=%d inversion=%d modulation=%s",
i_frequency, i_inversion,
psz_modulation == NULL ? "qam_auto" : psz_modulation );
break; break;
default: default:
...@@ -775,12 +964,15 @@ static void FrontendSet( void ) ...@@ -775,12 +964,15 @@ static void FrontendSet( void )
} }
i_last_status = 0; i_last_status = 0;
i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT; i_frontend_timeout = i_wallclock + i_frontend_timeout_duration;
} }
#else /* !S2API */ #else /* !S2API */
static void FrontendSet( void ) #warning "You are trying to compile DVBlast with an outdated linux-dvb interface."
#warning "DVBlast will be very limited and some options will have no effect."
static void FrontendSet( bool b_init )
{ {
struct dvb_frontend_info info; struct dvb_frontend_info info;
struct dvb_frontend_parameters fep; struct dvb_frontend_parameters fep;
...@@ -869,7 +1061,7 @@ static void FrontendSet( void ) ...@@ -869,7 +1061,7 @@ static void FrontendSet( void )
} }
i_last_status = 0; i_last_status = 0;
i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT; i_frontend_timeout = i_wallclock + i_frontend_timeout_duration;
} }
#endif /* S2API */ #endif /* S2API */
......
.TH DVBLAST "1" "January 2010" "DVBlast 1.3" "User Commands" .TH DVBLAST "1" "January 2010" "DVBlast 2.1" "User Commands"
.SH NAME .SH NAME
DVBlast \- Simple and powerful dvb streaming application DVBlast \- Simple and powerful dvb streaming application
.SH SYNOPSIS .SH SYNOPSIS
.B dvblast .B dvblast
[\fI-q\fR] \fI-c <config_file>\fR [\fI-r <remote_socket>\fR] [\fI-t <ttl>\fR] [\fI-o <SSRC_IP>\fR] [\fI-i <RT_priority>\fR] [\fI-a <adapter>\fR] [\fI-n <frontend number>\fR] [\fI-S <diseqc>\fR] \fI-f <frequency>\fR [\fI-F <fec inner>\fR] [\fI-R <rolloff>\fR] ] [\fI-s <symbol_rate>\fR] [\fI-v <0|13|18>\fR] [\fI-p\fR] [\fI-b <bandwidth>\fR] [\fI-m <modulation\fR] [\fI-u\fR] [\fI-W\fR] [\fI-U\fR] [\fI-d <dest_IP:port>\fR] [\fI-e\fR] [\fI-T\fR] [\fI-l\fR] [\fI-q\fR] [\fI-c <config_file>\fR] [\fI-r <remote_socket>\fR] [\fI-t <ttl>\fR] [\fI-o <SSRC_IP>\fR]
[\fI-i <RT_priority>\fR] [\fI-a <adapter>\fR] [\fI-n <frontend_number>\fR] [\fI-S <diseqc>\fR]
[\fI-f <frequency>\fR] [\fI-D <src_host>[:<src_port>][[@<src_mcast>][:<port>]][/<opts>]\fR] [\fI-A <ASI_adapter>\fR]
[\fI-s <symbol_rate>\fR] [\fI-v <0|13|18>\fR] [\fI-p\fR] [\fI-b <bandwidth>\fR] [\fI-I <inversion>\fR]
[\fI-F <fec_inner>\fR] [\fI-m <modulation>\fR] [\fI-R <rolloff>\fR] [\fI-P <pilot>\fR] [\fI-K <fec_lp>\fR]
[\fI-G <guard_interval>\fR] [\fI-H <hierarchy>\fR] [\fI-X <transmission>\fR] [\fI-O <lock_timeout>\fR]
[\fI-u\fR] [\fI-w\fR] [\fI-U\fR] [\fI-L <latency>\fR] [\fI-E <retention>\fR] [\fI-d <dest_IP>[<:port>][/<opts>]\fR]
[\fI-z\fR] [\fI-C\fR] [\fI-e\fR] [\fI-M <network_name>\fR] [\fI-N <network_ID>\fR] [\fI-T\fR] [\fI-j <system_charset>\fR]
[\fI-W\fR] [\fI-Y\fR] [\fI-l\fR] [\fI-g <logger ident>\fR] [\fI-Z <mrtg_file>\fR] [\fI-V\fR] [\fI-h\fR]
[\fI-J <DVB_charset>\fR] [\fI-B <provider_name>\fR] [\fI-Q <quit_timeout>\fR] [\fI-x <text|xml>\fR]
.SH DESCRIPTION .SH DESCRIPTION
DVBlast is a simple and powerful streaming application based on the linux-dvb API. DVBlast is a simple and powerful streaming application based on the linux-dvb
It opens a DVB device, tunes it, places PID filters, configures a CAM module, and demultiplexes the packets to several RTP outputs. API. It opens a DVB device, tunes it, places PID filters, configures a CAM
module, and demultiplexes the packets to several RTP outputs.
DVBlast is designed to be the core of a custom IRD or CID, based on a PC with Linux-supported DVB cards. DVBlast is designed to be the core of a custom IRD or CID, based on a PC with
Linux-supported DVB cards.
DVBlast does not do any kind of processing on the elementary streams, such as transcoding, PID remapping or remultiplexing. it does not stream from plain files, only DVB devices. If you were looking for these features, switch to VLC. DVBlast does not do any kind of processing on the elementary streams, such as
transcoding, PID remapping or remultiplexing. it does not stream from plain
files, only DVB devices. If you were looking for these features, switch to VLC.
.SH OPTIONS .SH OPTIONS
.PP .PP
.TP .TP
...@@ -23,35 +36,77 @@ Read packets from an ASI adapter (0-n) ...@@ -23,35 +36,77 @@ Read packets from an ASI adapter (0-n)
\fB\-b\fR, \fB\-\-bandwidth\fR <bandwidth> \fB\-b\fR, \fB\-\-bandwidth\fR <bandwidth>
Frontend bandwidth Frontend bandwidth
.TP .TP
\fB\-B\fR, \fB\-\-provider-name\fR <name>
Service provider name to declare in the SDT. If you want to change
provider name per output use /srvprovider= output option in the config
file.
.TP
\fB\-c\fR, \fB\-\-config\-file\fR <config file> \fB\-c\fR, \fB\-\-config\-file\fR <config file>
Use the given configuration file Use the given configuration file
.TP .TP
\fB\-C\fR, \fB\-\-dvb-compliance\fR
Pass through or build the mandatory DVB tables
.TP
\fB\-d\fR, \fB\-\-duplicate\fR <dest IP:port> \fB\-d\fR, \fB\-\-duplicate\fR <dest IP:port>
Duplicate all received packets to a given destination Duplicate all received packets to a given destination
.TP .TP
\fB\-D\fR, \fB\-\-rtp\-input\fR \fB\-D\fR, \fB\-\-rtp\-input\fR
Read packets from a multicast address instead of a DVB card Read packets from a multicast address instead of a DVB card
.TP .TP
\fB\-W\fR, \fB\-\-emm\-passthrough\fR
Enable EMM pass through (CA system data)
.TP
\fB\-Y\fR, \fB\-\-ecm\-passthrough\fR
Enable ECM pass through (CA program data)
.TP
\fB\-e\fR, \fB\-\-epg\-passthrough\fR \fB\-e\fR, \fB\-\-epg\-passthrough\fR
Enable EPG pass through (EIT data) Enable EPG pass through (EIT data)
.TP .TP
\fB\-E\fR, \fB\-\-retention\fR <retention>
Maximum retention allowed between input and output (default: 40 ms)
.TP
\fB\-f\fR, \fB\-\-frequency\fR <frequency> \fB\-f\fR, \fB\-\-frequency\fR <frequency>
Frontend frequency Frontend frequency
.TP .TP
\fB\-F\fr, \fB\-\-fec\-inner\fR \fB\-F\fR, \fB\-\-fec\-inner\fR <FEC>
Forward Error Correction used by satellite (FEC Inner) Forward Error Correction used by satellite (FEC Inner)
.br .br
DVB-S2 0|12|23|34|35|56|78|89|910|999 (default auto: 999) DVB-S2 0|12|23|34|35|56|78|89|910|999 (default auto: 999)
.TP .TP
\fB\-G\fR, \fB\-\-guard\fR <interval>
DVB-T guard interval
.br
DVB-T 32 (1/32)|16 (1/16)|8 (1/8)|4 (1/4)|-1 (auto, default)
.TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
Print the help message Print the help message
.TP .TP
\fB\-l\fR, \fB\-\-logger\fR \fB\-H\fR, \fB\-\-hierarchy\fR <hierarchy>
Send messages to syslog instead of stderr DVB-T hierarchy (0, 1, 2, 4 or -1 auto, default)
.TP .TP
\fB\-i\fR, \fB\-\-priority\fR <RT priority> \fB\-i\fR, \fB\-\-priority\fR <RT priority>
Real time priority Real time priority
.TP .TP
Inversion (-1 auto, 0 off, 1 on)
.TP
\fB\-j\fR, \fB\-\-system-charset\fR <charset>
Character set used for printing messages (default UTF-8)
.TP
\fB\-J\fR, \fB\-\-dvb-charset\fR <charset>
Character set used in output DVB tables (default ISO_8859-1)
.TP
\fB\-K\fR, \fB\-\-fec-lp\fR
DVB-T low priority FEC (default auto)
.TP
\fB\-l\fR, \fB\-\-logger\fR
Send messages to syslog instead of stderr
.TP
\fB\-g\fR, \fB\-\-logger-ident\fR <logger_ident>
Set the program name that will appear in syslog (default: dvblast /the executable name/).
.TP
\fB\-L\fR, \fB\-\-latency\fR <latency>
Maximum latency allowed between input and output (default: 100 ms)
.TP
\fB\-m\fR, \fB\-\-modulation\fR \fB\-m\fR, \fB\-\-modulation\fR
Modulation Modulation
.br .br
...@@ -61,18 +116,33 @@ DVB-T qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto) ...@@ -61,18 +116,33 @@ DVB-T qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)
.br .br
DVB-S2 qpsk|psk_8 (default legacy DVB-S) DVB-S2 qpsk|psk_8 (default legacy DVB-S)
.TP .TP
\fB\-M\fR, \fB\-\-network-name\fR <name>
DVB network name to declare in the NIT
.TP
\fB\-n\fR, \fB\-\-frontend\-number\fR <frontend number> \fB\-n\fR, \fB\-\-frontend\-number\fR <frontend number>
The frontend number The frontend number
.TP .TP
\fB\-N\fR, \fB\-\-network-id\fR <ID>
DVB network ID to declare in the NIT
.TP
\fB\-o\fR, \fB\-\-rtp-output\fR <SSRC IP> \fB\-o\fR, \fB\-\-rtp-output\fR <SSRC IP>
RTP output IP RTP output IP
.TP .TP
\fB\-O\fR, \fB\-\-lock-timeout\fR <timeout>
Timeout for the lock operation (in ms)
.TP
\fB\-p\fR, \fB\-\-force\-pulse\fR \fB\-p\fR, \fB\-\-force\-pulse\fR
Force 22kHz pulses for high-band selection (DVB-S) Force 22kHz pulses for high-band selection (DVB-S)
.TP .TP
\fB\-q\fR \fB\-P\fR, \fB\-\-pilot\fR
DVB-S2 Pilot (-1 auto, 0 off, 1 on)
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Be quiet (less verbosity, repeat or use number for even quieter) Be quiet (less verbosity, repeat or use number for even quieter)
.TP .TP
\fB\-Q\fR, \fB\-\-quit-timeout\fR <delay>
When locked, quit after this delay (in ms), or after the first lock timeout
.TP
\fB\-r\fR, \fB\-\-remote\-socket\fR <remote socket> \fB\-r\fR, \fB\-\-remote\-socket\fR <remote socket>
Remote socket to use Remote socket to use
.TP .TP
...@@ -81,7 +151,7 @@ Rolloff value to use ...@@ -81,7 +151,7 @@ Rolloff value to use
.br .br
DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35) DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35)
.TP .TP
\fB\-s\fR, \fB\-\-symbole\-rate\fR <symbol rate> \fB\-s\fR, \fB\-\-symbol\-rate\fR <symbol rate>
Symbole rate Symbole rate
.TP .TP
\fB\-S\fR, \fB\-\-diseqc\fR <diseqc> \fB\-S\fR, \fB\-\-diseqc\fR <diseqc>
...@@ -105,11 +175,26 @@ Voltage to apply to the LNB (QPSK) ...@@ -105,11 +175,26 @@ Voltage to apply to the LNB (QPSK)
\fB\-V\fR, \fB\-\-version\fR \fB\-V\fR, \fB\-\-version\fR
Only display the version Only display the version
.TP .TP
\fB\-W\fR, \fB\-\-slow\-cam\fR \fB\-w\fR, \fB\-\-select-pmts\fR
Add extra delays for slow CAMs set a PID filter on all PMTs
.TP
\fB\-x\fR, \fB\-\-print\fR
Print interesting events on stdout in a given format
.TP
\fB\-X\fR, \fB\-\-transmission\fR <transmission>
DVB-T transmission (2, 4, 8 or -1 auto, default
.TP
\fB\-z\fR, \fB\-\-any\-type\fR
pass through all ESs from the PMT, of any type
.TP
\fB\-Z\fR, \fB\-\-mrtg-file\fR <mrtg_file>
Every 10 seconds log statistics in <mrtg_file>. The file has 4 numbers in it
and the format is: <passed_bytes> <error_packets> <packets_with_seq_errors> <scrambled_packets>
.SH SEE ALSO .SH SEE ALSO
Read the README file for more information about the configuration of dvblast. Read the README file for more information about the configuration of dvblast.
.SH AUTHORS .SH AUTHORS
Writen by Marian Ďurkovič, Andy Gatward and Christophe Massiot Writen by Marian Ďurkovič, Andy Gatward, Christophe Massiot and Jean-Paul Saman
.SH LICENCE .SH LICENSE
This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published by the
Free Software Foundation.
/***************************************************************************** /*****************************************************************************
* dvblast.c * dvblast.c
***************************************************************************** *****************************************************************************
* Copyright (C) 2004, 2008-2009 VideoLAN * Copyright (C) 2004, 2008-2011 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* Andy Gatward <a.j.gatward@reading.ac.uk> * Andy Gatward <a.j.gatward@reading.ac.uk>
...@@ -26,7 +25,10 @@ ...@@ -26,7 +25,10 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <pthread.h> #include <pthread.h>
#include <netdb.h> #include <netdb.h>
#include <sys/socket.h> #include <sys/socket.h>
...@@ -34,24 +36,34 @@ ...@@ -34,24 +36,34 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <signal.h> #include <signal.h>
#include <getopt.h> #include <getopt.h>
#include <errno.h>
#include "dvblast.h" #include "dvblast.h"
#include "version.h" #include "version.h"
#ifdef HAVE_ICONV
#include <iconv.h>
#endif
#include <bitstream/dvb/si.h>
#include <bitstream/ietf/rtp.h>
#include "mrtg-cnt.h"
/***************************************************************************** /*****************************************************************************
* Local declarations * Local declarations
*****************************************************************************/ *****************************************************************************/
mtime_t i_wallclock = 0;
output_t **pp_outputs = NULL; output_t **pp_outputs = NULL;
int i_nb_outputs = 0; int i_nb_outputs = 0;
output_t output_dup = { 0 }; output_t output_dup;
static char *psz_conf_file = NULL; static char *psz_conf_file = NULL;
char *psz_srv_socket = NULL; char *psz_srv_socket = NULL;
int i_ttl = 64;
in_addr_t i_ssrc = 0;
static int i_priority = -1; static int i_priority = -1;
int i_adapter = 0; int i_adapter = 0;
int i_fenum = 0; int i_fenum = 0;
int i_frequency = 0; int i_frequency = 0;
int i_inversion = -1;
int i_srate = 27500000; int i_srate = 27500000;
int i_fec = 999; int i_fec = 999;
int i_rolloff = 35; int i_rolloff = 35;
...@@ -60,150 +72,287 @@ int i_voltage = 13; ...@@ -60,150 +72,287 @@ int i_voltage = 13;
int b_tone = 0; int b_tone = 0;
int i_bandwidth = 8; int i_bandwidth = 8;
char *psz_modulation = NULL; char *psz_modulation = NULL;
int i_pilot = -1;
int i_fec_lp = 999;
int i_guard = -1;
int i_transmission = -1;
int i_hierarchy = -1;
mtime_t i_frontend_timeout_duration = DEFAULT_FRONTEND_TIMEOUT;
mtime_t i_quit_timeout = 0;
mtime_t i_quit_timeout_duration = 0;
int b_budget_mode = 0; int b_budget_mode = 0;
int b_slow_cam = 0; int b_any_type = 0;
int b_output_udp = 0; int b_select_pmts = 0;
int b_enable_epg = 0; int b_random_tsid = 0;
int b_unique_tsid = 0; uint16_t i_network_id = 0xffff;
volatile int b_hup_received = 0; uint8_t *p_network_name;
size_t i_network_name_size;
char *psz_udp_src = NULL;
int i_asi_adapter = 0;
const char *psz_native_charset = "UTF-8";
const char *psz_dvb_charset = "ISO_8859-1";
const char *psz_provider_name = NULL;
print_type_t i_print_type = -1;
volatile sig_atomic_t b_conf_reload = 0;
volatile sig_atomic_t b_exit_now = 0;
int i_verbose = DEFAULT_VERBOSITY; int i_verbose = DEFAULT_VERBOSITY;
int i_syslog = 0; int i_syslog = 0;
uint16_t i_src_port = DEFAULT_PORT; char *psz_syslog_ident = NULL;
in_addr_t i_src_addr = { 0 };
int b_src_rawudp = 0; bool b_enable_emm = false;
int i_asi_adapter = 0; bool b_enable_ecm = false;
uint8_t pi_ssrc_global[4] = { 0, 0, 0, 0 };
static bool b_udp_global = false;
static bool b_dvb_global = false;
static bool b_epg_global = false;
static mtime_t i_latency_global = DEFAULT_OUTPUT_LATENCY;
static mtime_t i_retention_global = DEFAULT_MAX_RETENTION;
static int i_ttl_global = 64;
/* TPS Input log filename */
char * psz_mrtg_file = NULL;
void (*pf_Open)( void ) = NULL; void (*pf_Open)( void ) = NULL;
block_t * (*pf_Read)( void ) = NULL; block_t * (*pf_Read)( mtime_t i_poll_timeout ) = NULL;
void (*pf_Reset)( void ) = NULL;
int (*pf_SetFilter)( uint16_t i_pid ) = NULL; int (*pf_SetFilter)( uint16_t i_pid ) = NULL;
void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ) = NULL; void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ) = NULL;
/***************************************************************************** /*****************************************************************************
* Configuration files * Configuration files
*****************************************************************************/ *****************************************************************************/
static void ReadConfiguration( char *psz_file ) void config_Init( output_config_t *p_config )
{ {
FILE *p_file; memset( p_config, 0, sizeof(output_config_t) );
char psz_line[2048];
int i;
if ( psz_file == NULL ) p_config->psz_displayname = NULL;
{ p_config->psz_service_name = NULL;
msg_Err( NULL, "no config file" ); p_config->psz_service_provider = NULL;
return;
}
if ( (p_file = fopen( psz_file, "r" )) == NULL ) p_config->i_family = AF_UNSPEC;
{ p_config->connect_addr.ss_family = AF_UNSPEC;
msg_Err( NULL, "can't fopen config file %s", psz_file ); p_config->bind_addr.ss_family = AF_UNSPEC;
return; p_config->i_if_index_v6 = -1;
p_config->pi_pids = NULL;
}
void config_Free( output_config_t *p_config )
{
free( p_config->psz_displayname );
free( p_config->psz_service_name );
free( p_config->psz_service_provider );
free( p_config->pi_pids );
}
static void config_Defaults( output_config_t *p_config )
{
config_Init( p_config );
p_config->i_config = (b_udp_global ? OUTPUT_UDP : 0) |
(b_dvb_global ? OUTPUT_DVB : 0) |
(b_epg_global ? OUTPUT_EPG : 0);
p_config->i_max_retention = i_retention_global;
p_config->i_output_latency = i_latency_global;
p_config->i_tsid = -1;
p_config->i_ttl = i_ttl_global;
memcpy( p_config->pi_ssrc, pi_ssrc_global, 4 * sizeof(uint8_t) );
}
static char *config_stropt( char *psz_string )
{
char *ret, *tmp;
if ( !psz_string || strlen( psz_string ) == 0 )
return NULL;
ret = tmp = strdup( psz_string );
while (*tmp) {
if (*tmp == '_')
*tmp = ' ';
if (*tmp == '/') {
*tmp = '\0';
break;
} }
tmp++;
}
return ret;
}
while ( fgets( psz_line, sizeof(psz_line), p_file ) != NULL ) bool config_ParseHost( output_config_t *p_config, char *psz_string )
{ {
output_t *p_output = NULL; struct addrinfo *p_ai;
char *psz_parser, *psz_token, *psz_token2, *psz_token3; int i_mtu;
struct addrinfo *p_addr;
struct addrinfo ai_hints;
char sz_port[6];
char *psz_displayname;
uint16_t i_sid = 0;
uint16_t *pi_pids = NULL;
int i_nb_pids = 0;
uint8_t i_config = 0;
snprintf( sz_port, sizeof( sz_port ), "%d", DEFAULT_PORT ); p_config->psz_displayname = strdup( psz_string );
if ( !strncmp( psz_line, "#", 1 ) ) p_ai = ParseNodeService( psz_string, &psz_string, DEFAULT_PORT );
continue; if ( p_ai == NULL ) return false;
memcpy( &p_config->connect_addr, p_ai->ai_addr, p_ai->ai_addrlen );
freeaddrinfo( p_ai );
psz_token = strtok_r( psz_line, "\t\n ", &psz_parser ); p_config->i_family = p_config->connect_addr.ss_family;
if ( psz_token == NULL ) if ( p_config->i_family == AF_UNSPEC ) return false;
continue;
if ( psz_string == NULL || !*psz_string ) goto end;
if ( (psz_token3 = strrchr( psz_token, '/' )) != NULL ) if ( *psz_string == '@' )
{ {
*psz_token3 = '\0'; psz_string++;
if( strncasecmp( psz_token3 + 1, "udp", 3 ) == 0 ) p_ai = ParseNodeService( psz_string, &psz_string, 0 );
i_config |= OUTPUT_UDP; if ( p_ai == NULL || p_ai->ai_family != p_config->i_family )
msg_Warn( NULL, "invalid bind address" );
else
memcpy( &p_config->bind_addr, p_ai->ai_addr, p_ai->ai_addrlen );
freeaddrinfo( p_ai );
} }
if ( !strncmp( psz_token, "[", 1 ) ) while ( (psz_string = strchr( psz_string, '/' )) != NULL )
{ {
if ( (psz_token2 = strchr( psz_token, ']' ) ) == NULL ) *psz_string++ = '\0';
continue;
#define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) ))
char *psz_maddr = malloc( psz_token2 - psz_token ); #define ARG_OPTION( option ) (psz_string + strlen(option))
memset( psz_maddr, '\0', ( psz_token2 - psz_token ) );
strncpy( psz_maddr, psz_token + 1, ( psz_token2 - psz_token - 1 )); if ( IS_OPTION("udp") )
p_config->i_config |= OUTPUT_UDP;
if ( (psz_token2 = strchr( psz_token2, ':' )) != NULL ) else if ( IS_OPTION("dvb") )
p_config->i_config |= OUTPUT_DVB;
else if ( IS_OPTION("epg") )
p_config->i_config |= OUTPUT_EPG;
else if ( IS_OPTION("tsid=") )
p_config->i_tsid = strtol( ARG_OPTION("tsid="), NULL, 0 );
else if ( IS_OPTION("retention=") )
p_config->i_max_retention = strtoll( ARG_OPTION("retention="),
NULL, 0 ) * 1000;
else if ( IS_OPTION("latency=") )
p_config->i_output_latency = strtoll( ARG_OPTION("latency="),
NULL, 0 ) * 1000;
else if ( IS_OPTION("ttl=") )
p_config->i_ttl = strtol( ARG_OPTION("ttl="), NULL, 0 );
else if ( IS_OPTION("tos=") )
p_config->i_tos = strtol( ARG_OPTION("tos="), NULL, 0 );
else if ( IS_OPTION("mtu=") )
p_config->i_mtu = strtol( ARG_OPTION("mtu="), NULL, 0 );
else if ( IS_OPTION("ifindex=") )
p_config->i_if_index_v6 = strtol( ARG_OPTION("ifindex="), NULL, 0 );
else if ( IS_OPTION("srvname=") )
{
if ( p_config->psz_service_name )
free( p_config->psz_service_name );
p_config->psz_service_name = config_stropt( ARG_OPTION("srvname=") );
}
else if ( IS_OPTION("srvprovider=") )
{ {
*psz_token2 = '\0'; if ( !p_config->psz_service_provider )
snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token2 + 1 ) ); free( p_config->psz_service_provider );
p_config->psz_service_provider = config_stropt( ARG_OPTION("srvprovider=") );
}
else if ( IS_OPTION("ssrc=") )
{
in_addr_t i_addr = inet_addr( ARG_OPTION("ssrc=") );
memcpy( p_config->pi_ssrc, &i_addr, 4 * sizeof(uint8_t) );
}
else
msg_Warn( NULL, "unrecognized option %s", psz_string );
#undef IS_OPTION
#undef ARG_OPTION
} }
p_addr = malloc( sizeof( p_addr ) ); if ( !p_config->psz_service_provider && psz_provider_name )
p_config->psz_service_provider = strdup( psz_provider_name );
memset( &ai_hints, 0, sizeof( ai_hints ) ); end:
ai_hints.ai_socktype = SOCK_DGRAM; i_mtu = p_config->i_family == AF_INET6 ? DEFAULT_IPV6_MTU :
ai_hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV; DEFAULT_IPV4_MTU;
ai_hints.ai_family = AF_INET6;
int i_ai = getaddrinfo( psz_maddr, sz_port, &ai_hints, &p_addr ); if ( !p_config->i_mtu )
if ( i_ai != 0 ) p_config->i_mtu = i_mtu;
else if ( p_config->i_mtu < TS_SIZE + RTP_HEADER_SIZE )
{ {
msg_Err( NULL, "Cannot configure output [%s]:%s: %s", psz_maddr, msg_Warn( NULL, "invalid MTU %d, setting %d", p_config->i_mtu, i_mtu );
sz_port, gai_strerror( i_ai ) ); p_config->i_mtu = i_mtu;
continue;
} }
psz_displayname = malloc( INET6_ADDRSTRLEN + 8 ); return true;
snprintf( psz_displayname, ( INET6_ADDRSTRLEN + 8 ), "[%s]:%s", }
psz_maddr, sz_port );
free( psz_maddr ); static void config_Print( output_config_t *p_config )
} {
else const char *psz_base = "conf: %s config=0x%"PRIx64" sid=%hu pids[%d]=";
size_t i_len = strlen(psz_base) + 6 * p_config->i_nb_pids + 1;
char psz_format[i_len];
int i, j = strlen(psz_base);
strcpy( psz_format, psz_base );
for ( i = 0; i < p_config->i_nb_pids; i++ )
j += sprintf( psz_format + j, "%u,", p_config->pi_pids[i] );
psz_format[j - 1] = '\0';
msg_Dbg( NULL, psz_format, p_config->psz_displayname, p_config->i_config,
p_config->i_sid, p_config->i_nb_pids );
}
static void config_ReadFile( char *psz_file )
{
FILE *p_file;
char psz_line[2048];
int i;
if ( psz_file == NULL )
{ {
if ( (psz_token2 = strrchr( psz_token, ':' )) != NULL ) msg_Err( NULL, "no config file" );
return;
}
if ( (p_file = fopen( psz_file, "r" )) == NULL )
{ {
*psz_token2 = '\0'; msg_Err( NULL, "can't fopen config file %s", psz_file );
snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token2 + 1 ) ); return;
} }
p_addr = malloc( sizeof( p_addr ) ); while ( fgets( psz_line, sizeof(psz_line), p_file ) != NULL )
{
output_config_t config;
output_t *p_output;
char *psz_token, *psz_parser;
psz_parser = strchr( psz_line, '#' );
if ( psz_parser != NULL )
*psz_parser-- = '\0';
while ( psz_parser >= psz_line && isblank( *psz_parser ) )
*psz_parser-- = '\0';
if ( psz_line[0] == '\0' )
continue;
memset( &ai_hints, 0, sizeof( ai_hints ) ); config_Defaults( &config );
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
ai_hints.ai_family = AF_INET;
int i_ai = getaddrinfo( psz_token, sz_port, &ai_hints, &p_addr ); psz_token = strtok_r( psz_line, "\t\n ", &psz_parser );
if ( i_ai != 0 ) if ( psz_token == NULL || !config_ParseHost( &config, psz_token ))
{ {
msg_Err( NULL, "Cannot configure output %s:%s: %s", psz_token, config_Free( &config );
sz_port, gai_strerror( i_ai ) );
continue; continue;
} }
psz_displayname = malloc( INET_ADDRSTRLEN + 6 );
snprintf( psz_displayname, ( INET_ADDRSTRLEN + 6 ), "%s:%s",
psz_token, sz_port );
}
psz_token = strtok_r( NULL, "\t\n ", &psz_parser ); psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token == NULL ) if ( psz_token == NULL )
{
config_Free( &config );
continue; continue;
if( atoi( psz_token ) ) }
i_config |= OUTPUT_WATCH; if( atoi( psz_token ) == 1 )
config.i_config |= OUTPUT_WATCH;
else else
i_config &= ~OUTPUT_WATCH; config.i_config &= ~OUTPUT_WATCH;
psz_token = strtok_r( NULL, "\t\n ", &psz_parser ); psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token == NULL ) if ( psz_token == NULL )
{
config_Free( &config );
continue; continue;
i_sid = strtol(psz_token, NULL, 0); }
config.i_sid = strtol(psz_token, NULL, 0);
psz_token = strtok_r( NULL, "\t\n ", &psz_parser ); psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token != NULL ) if ( psz_token != NULL )
...@@ -214,82 +363,52 @@ static void ReadConfiguration( char *psz_file ) ...@@ -214,82 +363,52 @@ static void ReadConfiguration( char *psz_file )
psz_token = strtok_r( psz_token, ",", &psz_parser ); psz_token = strtok_r( psz_token, ",", &psz_parser );
if ( psz_token == NULL ) if ( psz_token == NULL )
break; break;
pi_pids = realloc( pi_pids, config.pi_pids = realloc( config.pi_pids,
(i_nb_pids + 1) * sizeof(uint16_t) ); (config.i_nb_pids + 1) * sizeof(uint16_t) );
pi_pids[i_nb_pids++] = strtol(psz_token, NULL, 0); config.pi_pids[config.i_nb_pids++] = strtol(psz_token, NULL, 0);
psz_token = NULL; psz_token = NULL;
} }
} }
msg_Dbg( NULL, "conf: %s w=%d sid=%d pids[%d]=%d,%d,%d,%d,%d...", config_Print( &config );
psz_displayname,
( i_config & OUTPUT_WATCH ) ? 1 : 0, i_sid, i_nb_pids,
i_nb_pids < 1 ? -1 : pi_pids[0],
i_nb_pids < 2 ? -1 : pi_pids[1],
i_nb_pids < 3 ? -1 : pi_pids[2],
i_nb_pids < 4 ? -1 : pi_pids[3],
i_nb_pids < 5 ? -1 : pi_pids[4] );
for ( i = 0; i < i_nb_outputs; i++ ) p_output = output_Find( &config );
{
if ( pp_outputs[i]->p_addr->ss_family == AF_INET )
{
struct sockaddr_in *p_esad = (struct sockaddr_in *)pp_outputs[i]->p_addr;
struct sockaddr_in *p_nsad = (struct sockaddr_in *)p_addr->ai_addr;
if ( ( p_esad->sin_addr.s_addr == p_nsad->sin_addr.s_addr ) && if ( p_output == NULL )
( p_esad->sin_port == p_nsad->sin_port ) ) p_output = output_Create( &config );
{
p_output = pp_outputs[i];
output_Init( p_output, i_config, psz_displayname, (void *)p_addr );
break;
}
}
else if ( pp_outputs[i]->p_addr->ss_family == AF_INET6 )
{
struct sockaddr_in6 *p_esad = (struct sockaddr_in6 *)pp_outputs[i]->p_addr;
struct sockaddr_in6 *p_nsad = (struct sockaddr_in6 *)p_addr->ai_addr;
if ( ( p_esad->sin6_addr.s6_addr32[0] == p_nsad->sin6_addr.s6_addr32[0] ) &&
( p_esad->sin6_addr.s6_addr32[1] == p_nsad->sin6_addr.s6_addr32[1] ) &&
( p_esad->sin6_addr.s6_addr32[2] == p_nsad->sin6_addr.s6_addr32[2] ) &&
( p_esad->sin6_addr.s6_addr32[3] == p_nsad->sin6_addr.s6_addr32[3] ) &&
( p_esad->sin6_port == p_nsad->sin6_port ) )
{
p_output = pp_outputs[i];
output_Init( p_output, i_config, psz_displayname, (void *)p_addr );
break;
}
}
}
if ( i == i_nb_outputs )
p_output = output_Create( i_config, psz_displayname, (void *)p_addr );
if ( p_output != NULL ) if ( p_output != NULL )
{ {
demux_Change( p_output, i_sid, pi_pids, i_nb_pids ); free( p_output->config.psz_displayname );
p_output->i_config |= OUTPUT_STILL_PRESENT; p_output->config.psz_displayname = strdup( config.psz_displayname );
config.i_config |= OUTPUT_VALID | OUTPUT_STILL_PRESENT;
output_Change( p_output, &config );
demux_Change( p_output, &config );
} }
free( psz_displayname ); config_Free( &config );
free( pi_pids );
freeaddrinfo( p_addr );
} }
fclose( p_file ); fclose( p_file );
for ( i = 0; i < i_nb_outputs; i++ ) for ( i = 0; i < i_nb_outputs; i++ )
{ {
if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && output_t *p_output = pp_outputs[i];
!( pp_outputs[i]->i_config & OUTPUT_STILL_PRESENT ) ) output_config_t config;
config_Init( &config );
if ( (p_output->config.i_config & OUTPUT_VALID) &&
!(p_output->config.i_config & OUTPUT_STILL_PRESENT) )
{ {
msg_Dbg( NULL, "closing %s", pp_outputs[i]->psz_displayname ); msg_Dbg( NULL, "closing %s", p_output->config.psz_displayname );
demux_Change( pp_outputs[i], 0, NULL, 0 ); demux_Change( p_output, &config );
output_Close( pp_outputs[i] ); output_Close( p_output );
} }
pp_outputs[i]->i_config &= ~OUTPUT_STILL_PRESENT; p_output->config.i_config &= ~OUTPUT_STILL_PRESENT;
config_Free( &config );
} }
} }
...@@ -298,7 +417,10 @@ static void ReadConfiguration( char *psz_file ) ...@@ -298,7 +417,10 @@ static void ReadConfiguration( char *psz_file )
*****************************************************************************/ *****************************************************************************/
static void SigHandler( int i_signal ) static void SigHandler( int i_signal )
{ {
b_hup_received = 1; if ( i_signal == SIGHUP )
b_conf_reload = 1;
if ( i_signal == SIGINT )
b_exit_now = 1;
} }
/***************************************************************************** /*****************************************************************************
...@@ -306,7 +428,7 @@ static void SigHandler( int i_signal ) ...@@ -306,7 +428,7 @@ static void SigHandler( int i_signal )
*****************************************************************************/ *****************************************************************************/
static void DisplayVersion() static void DisplayVersion()
{ {
msg_Raw( NULL, "DVBlast %d.%d.%d%s", VERSION_MAJOR, VERSION_MINOR, msg_Raw( NULL, "DVBlast %d.%d.%d (%s)", VERSION_MAJOR, VERSION_MINOR,
VERSION_REVISION, VERSION_EXTRA ); VERSION_REVISION, VERSION_EXTRA );
} }
...@@ -315,62 +437,104 @@ static void DisplayVersion() ...@@ -315,62 +437,104 @@ static void DisplayVersion()
*****************************************************************************/ *****************************************************************************/
void usage() void usage()
{ {
msg_Raw( NULL, "Usage: dvblast [-q] [-c <config file>] [-r <remote socket>] [-t <ttl>] [-o <SSRC IP>] [-i <RT priority>] [-a <adapter>] [-n <frontend number>] [-S <diseqc>] [-f <frequency>|-D <src mcast>:<port>|-A <ASI adapter>] [-F <fec inner>] [-R <rolloff>] [-s <symbol rate>] [-v <0|13|18>] [-p] [-b <bandwidth>] [-m <modulation] [-u] [-W] [-U] [-d <dest IP:port>] [-e] [-T]" ); DisplayVersion();
msg_Raw( NULL, "Usage: dvblast [-q] [-c <config file>] [-r <remote socket>] [-t <ttl>] [-o <SSRC IP>] "
"[-i <RT priority>] [-a <adapter>] [-n <frontend number>] [-S <diseqc>] "
"[-f <frequency>|-D [<src host>[:<src port>]@]<src mcast>[:<port>][/<opts>]*|-A <ASI adapter>] "
"[-s <symbol rate>] [-v <0|13|18>] [-p] [-b <bandwidth>] [-I <inversion>] "
"[-F <fec inner>] [-m <modulation] [-R <rolloff>] [-P <pilot>] [-K <fec lp>] "
"[-G <guard interval>] [-H <hierarchy>] [-X <transmission>] [-O <lock timeout>] "
"[-u] [-w] [-U] [-L <latency>] [-E <retention>] [-d <dest IP>[<:port>][/<opts>]*] "
"[-z] [-C [-e] [-M <network name] [-N <network ID>]] [-T] [-j <system charset>] "
"[-W] [-Y] [-l] [-g <logger ident>] [-Z <mrtg file>] [-V] [-h] [-B <provider_name>]"
"[-J <DVB charset>] [-Q <quit timeout>] [-x <text|xml>]" );
msg_Raw( NULL, "Input:" ); msg_Raw( NULL, "Input:" );
msg_Raw( NULL, " -a --adapter <adapter>" ); msg_Raw( NULL, " -a --adapter read packets from a Linux-DVB adapter (typically 0-n)" );
msg_Raw( NULL, " -A --asi-adapter read packets from an ASI adapter (0-n)" ); msg_Raw( NULL, " -A --asi-adapter read packets from an ASI adapter (0-n)" );
msg_Raw( NULL, " -b --bandwidth frontend bandwith" ); msg_Raw( NULL, " -b --bandwidth frontend bandwith" );
msg_Raw( NULL, " -D --rtp-input read packets from a multicast address instead of a DVB card" ); msg_Raw( NULL, " -D --rtp-input read packets from a multicast address instead of a DVB card" );
msg_Raw( NULL, " -e --epg-passthrough enable EPG pass through (EIT data)" );
msg_Raw( NULL, " -f --frequency frontend frequency" ); msg_Raw( NULL, " -f --frequency frontend frequency" );
msg_Raw( NULL, " -F --fec-inner Forward Error Correction (FEC Inner)"); msg_Raw( NULL, " -F --fec-inner Forward Error Correction (FEC Inner)");
msg_Raw( NULL, " DVB-S2 0|12|23|34|35|56|78|89|910|999 (default auto: 999)"); msg_Raw( NULL, " DVB-S2 0|12|23|34|35|56|78|89|910|999 (default auto: 999)");
msg_Raw( NULL, " -I --inversion Inversion (-1 auto, 0 off, 1 on)" );
msg_Raw( NULL, " -m --modulation Modulation type" ); msg_Raw( NULL, " -m --modulation Modulation type" );
msg_Raw( NULL, " DVB-C qpsk|qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)" ); msg_Raw( NULL, " DVB-C qpsk|qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)" );
msg_Raw( NULL, " DVB-T qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)" ); msg_Raw( NULL, " DVB-T qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)" );
msg_Raw( NULL, " DVB-S2 qpsk|psk_8 (default legacy DVB-S)" ); msg_Raw( NULL, " DVB-S2 qpsk|psk_8 (default legacy DVB-S)" );
msg_Raw( NULL, " -n --frontend-number <frontend number>" ); msg_Raw( NULL, " -n --frontend-number <frontend number>" );
msg_Raw( NULL, " -p --force-pulse force 22kHz pulses for high-band selection (DVB-S)" ); msg_Raw( NULL, " -p --force-pulse force 22kHz pulses for high-band selection (DVB-S)" );
msg_Raw( NULL, " -P --pilot DVB-S2 Pilot (-1 auto, 0 off, 1 on)" );
msg_Raw( NULL, " -R --rolloff DVB-S2 Rolloff value" ); msg_Raw( NULL, " -R --rolloff DVB-S2 Rolloff value" );
msg_Raw( NULL, " DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35)" ); msg_Raw( NULL, " DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35)" );
msg_Raw( NULL, " -s --symbole-rate" ); msg_Raw( NULL, " -K --fec-lp DVB-T low priority FEC (default auto)" );
msg_Raw( NULL, " -G --guard DVB-T guard interval" );
msg_Raw( NULL, " DVB-T 32 (1/32)|16 (1/16)|8 (1/8)|4 (1/4)|-1 (auto, default)" );
msg_Raw( NULL, " -H --hierarchy DVB-T hierarchy (0, 1, 2, 4 or -1 auto, default)" );
msg_Raw( NULL, " -X --transmission DVB-T transmission (2, 4, 8 or -1 auto, default)" );
msg_Raw( NULL, " -s --symbol-rate" );
msg_Raw( NULL, " -S --diseqc satellite number for diseqc (0: no diseqc, 1-4, A or B)" ); msg_Raw( NULL, " -S --diseqc satellite number for diseqc (0: no diseqc, 1-4, A or B)" );
msg_Raw( NULL, " -T --unique-ts-id generate unique TS ID for each program" );
msg_Raw( NULL, " -u --budget-mode turn on budget mode (no hardware PID filtering)" ); msg_Raw( NULL, " -u --budget-mode turn on budget mode (no hardware PID filtering)" );
msg_Raw( NULL, " -v --voltage voltage to apply to the LNB (QPSK)" ); msg_Raw( NULL, " -v --voltage voltage to apply to the LNB (QPSK)" );
msg_Raw( NULL, " -W --slow-cam add extra delays for slow CAMs" ); msg_Raw( NULL, " -w --select-pmts set a PID filter on all PMTs" );
msg_Raw( NULL, " -O --lock-timeout timeout for the lock operation (in ms)" );
msg_Raw( NULL, "Output:" ); msg_Raw( NULL, "Output:" );
msg_Raw( NULL, " -c --config-file <config file>" ); msg_Raw( NULL, " -c --config-file <config file>" );
msg_Raw( NULL, " -C --dvb-compliance pass through or build the mandatory DVB tables" );
msg_Raw( NULL, " -d --duplicate duplicate all received packets to a given destination" ); msg_Raw( NULL, " -d --duplicate duplicate all received packets to a given destination" );
msg_Raw( NULL, " -W --emm-passthrough pass through EMM data (CA system data)" );
msg_Raw( NULL, " -Y --ecm-passthrough pass through ECM data (CA program data)" );
msg_Raw( NULL, " -e --epg-passthrough pass through DVB EIT schedule tables" );
msg_Raw( NULL, " -E --retention maximum retention allowed between input and output (default: 40 ms)" );
msg_Raw( NULL, " -L --latency maximum latency allowed between input and output (default: 100 ms)" );
msg_Raw( NULL, " -M --network-name DVB network name to declare in the NIT" );
msg_Raw( NULL, " -N --network-id DVB network ID to declare in the NIT" );
msg_Raw( NULL, " -B --provider-name Service provider name to declare in the SDT" );
msg_Raw( NULL, " -o --rtp-output <SSRC IP>" ); msg_Raw( NULL, " -o --rtp-output <SSRC IP>" );
msg_Raw( NULL, " -t --ttl <ttl> TTL of the output stream" ); msg_Raw( NULL, " -t --ttl <ttl> TTL of the output stream" );
msg_Raw( NULL, " -T --unique-ts-id generate random unique TS ID for each output" );
msg_Raw( NULL, " -U --udp use raw UDP rather than RTP (required by some IPTV set top boxes)" ); msg_Raw( NULL, " -U --udp use raw UDP rather than RTP (required by some IPTV set top boxes)" );
msg_Raw( NULL, " -z --any-type pass through all ESs from the PMT, of any type" );
msg_Raw( NULL, "Misc:" ); msg_Raw( NULL, "Misc:" );
msg_Raw( NULL, " -h --help display this full help" ); msg_Raw( NULL, " -h --help display this full help" );
msg_Raw( NULL, " -i --priority <RT pritority>" ); msg_Raw( NULL, " -i --priority <RT priority>" );
msg_Raw( NULL, " -q be quiet (less verbosity, repeat or use number for even quieter)" ); msg_Raw( NULL, " -j --system-charset character set used for printing messages (default UTF-8)" );
msg_Raw( NULL, " -J --dvb-charset character set used in output DVB tables (default ISO_8859-1)" );
msg_Raw( NULL, " -l --logger use syslog for logging messages instead of stderr" ); msg_Raw( NULL, " -l --logger use syslog for logging messages instead of stderr" );
msg_Raw( NULL, " -g --logger-ident program name that will be used in syslog messages" );
msg_Raw( NULL, " -x --print print interesting events on stdout in a given format" );
msg_Raw( NULL, " -q --quiet be quiet (less verbosity, repeat or use number for even quieter)" );
msg_Raw( NULL, " -Q --quit-timeout when locked, quit after this delay (in ms), or after the first lock timeout" );
msg_Raw( NULL, " -r --remote-socket <remote socket>" ); msg_Raw( NULL, " -r --remote-socket <remote socket>" );
msg_Raw( NULL, " -V --version only display the version" ); msg_Raw( NULL, " -V --version only display the version" );
msg_Raw( NULL, " -Z --mrtg-file <file> Log input packets and errors into mrtg-file" );
exit(1); exit(1);
} }
int main( int i_argc, char **pp_argv ) int main( int i_argc, char **pp_argv )
{ {
const char *psz_network_name = "DVBlast - http://www.videolan.org/projects/dvblast.html";
char *p_network_name_tmp = NULL;
size_t i_network_name_tmp_size;
char *psz_dup_config = NULL;
mtime_t i_poll_timeout = MAX_POLL_TIMEOUT;
struct sched_param param; struct sched_param param;
int i_error; int i_error;
int c; int c;
struct sigaction sa;
sigset_t set;
int b_enable_syslog = 0; int b_enable_syslog = 0;
DisplayVersion();
if ( i_argc == 1 ) if ( i_argc == 1 )
usage(); usage();
/*
* The only short options left are: ky0123456789
* Use them wisely.
*/
static const struct option long_options[] = static const struct option long_options[] =
{ {
{ "config-file", required_argument, NULL, 'c' }, { "config-file", required_argument, NULL, 'c' },
...@@ -388,22 +552,45 @@ int main( int i_argc, char **pp_argv ) ...@@ -388,22 +552,45 @@ int main( int i_argc, char **pp_argv )
{ "voltage", required_argument, NULL, 'v' }, { "voltage", required_argument, NULL, 'v' },
{ "force-pulse", no_argument, NULL, 'p' }, { "force-pulse", no_argument, NULL, 'p' },
{ "bandwidth", required_argument, NULL, 'b' }, { "bandwidth", required_argument, NULL, 'b' },
{ "inversion", required_argument, NULL, 'I' },
{ "modulation", required_argument, NULL, 'm' }, { "modulation", required_argument, NULL, 'm' },
{ "pilot", required_argument, NULL, 'P' },
{ "fec-lp", required_argument, NULL, 'K' },
{ "guard", required_argument, NULL, 'G' },
{ "hierarchy", required_argument, NULL, 'H' },
{ "transmission", required_argument, NULL, 'X' },
{ "lock-timeout", required_argument, NULL, 'O' },
{ "budget-mode", no_argument, NULL, 'u' }, { "budget-mode", no_argument, NULL, 'u' },
{ "slow-cam", no_argument, NULL, 'W' }, { "select-pmts", no_argument, NULL, 'w' },
{ "udp", no_argument, NULL, 'U' }, { "udp", no_argument, NULL, 'U' },
{ "unique-ts-id", no_argument, NULL, 'T' }, { "unique-ts-id", no_argument, NULL, 'T' },
{ "latency", required_argument, NULL, 'L' },
{ "retention", required_argument, NULL, 'E' },
{ "duplicate", required_argument, NULL, 'd' }, { "duplicate", required_argument, NULL, 'd' },
{ "rtp-input", required_argument, NULL, 'D' }, { "rtp-input", required_argument, NULL, 'D' },
{ "asi-adapter", required_argument, NULL, 'A' }, { "asi-adapter", required_argument, NULL, 'A' },
{ "any-type", no_argument, NULL, 'z' },
{ "dvb-compliance", no_argument, NULL, 'C' },
{ "emm-passthrough", no_argument, NULL, 'W' },
{ "ecm-passthrough", no_argument, NULL, 'Y' },
{ "epg-passthrough", no_argument, NULL, 'e' }, { "epg-passthrough", no_argument, NULL, 'e' },
{ "network-name", no_argument, NULL, 'M' },
{ "network-id", no_argument, NULL, 'N' },
{ "system-charset", required_argument, NULL, 'j' },
{ "dvb-charset", required_argument, NULL, 'J' },
{ "provider-name", required_argument, NULL, 'B' },
{ "logger", no_argument, NULL, 'l' }, { "logger", no_argument, NULL, 'l' },
{ "logger-ident", required_argument, NULL, 'g' },
{ "print", required_argument, NULL, 'x' },
{ "quit-timeout", required_argument, NULL, 'Q' },
{ "quiet", no_argument, NULL, 'q' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
{ 0, 0, 0, 0} { "mrtg-file", required_argument, NULL, 'Z' },
{ 0, 0, 0, 0 }
}; };
while ( ( c = getopt_long(i_argc, pp_argv, "q::c:r:t:o:i:a:n:f:F:R:s:S:v:pb:m:uWUTd:D:A:lehV", long_options, NULL)) != -1 ) while ( (c = getopt_long(i_argc, pp_argv, "q::c:r:t:o:i:a:n:f:F:R:s:S:v:pb:I:m:P:K:G:H:X:O:uwUTL:E:d:D:A:lg:zCWYeM:N:j:J:B:x:Q:hVZ:", long_options, NULL)) != -1 )
{ {
switch ( c ) switch ( c )
{ {
...@@ -435,16 +622,11 @@ int main( int i_argc, char **pp_argv ) ...@@ -435,16 +622,11 @@ int main( int i_argc, char **pp_argv )
break; break;
case 'r': case 'r':
if ( pf_Open != dvb_Open && pf_Open != NULL )
{
msg_Err( NULL, "-r is only available for linux-dvb input" );
usage();
}
psz_srv_socket = optarg; psz_srv_socket = optarg;
break; break;
case 't': case 't':
i_ttl = strtol( optarg, NULL, 0 ); i_ttl_global = strtol( optarg, NULL, 0 );
break; break;
case 'o': case 'o':
...@@ -452,7 +634,7 @@ int main( int i_argc, char **pp_argv ) ...@@ -452,7 +634,7 @@ int main( int i_argc, char **pp_argv )
struct in_addr maddr; struct in_addr maddr;
if ( !inet_aton( optarg, &maddr ) ) if ( !inet_aton( optarg, &maddr ) )
usage(); usage();
i_ssrc = maddr.s_addr; memcpy( pi_ssrc_global, &maddr.s_addr, 4 * sizeof(uint8_t) );
break; break;
} }
...@@ -474,6 +656,7 @@ int main( int i_argc, char **pp_argv ) ...@@ -474,6 +656,7 @@ int main( int i_argc, char **pp_argv )
usage(); usage();
pf_Open = dvb_Open; pf_Open = dvb_Open;
pf_Read = dvb_Read; pf_Read = dvb_Read;
pf_Reset = dvb_Reset;
pf_SetFilter = dvb_SetFilter; pf_SetFilter = dvb_SetFilter;
pf_UnsetFilter = dvb_UnsetFilter; pf_UnsetFilter = dvb_UnsetFilter;
break; break;
...@@ -506,177 +689,161 @@ int main( int i_argc, char **pp_argv ) ...@@ -506,177 +689,161 @@ int main( int i_argc, char **pp_argv )
i_bandwidth = strtol( optarg, NULL, 0 ); i_bandwidth = strtol( optarg, NULL, 0 );
break; break;
case 'I':
i_inversion = strtol( optarg, NULL, 0 );
break;
case 'm': case 'm':
psz_modulation = optarg; psz_modulation = optarg;
break; break;
case 'u': case 'P':
b_budget_mode = 1; i_pilot = strtol( optarg, NULL, 0 );
break; break;
case 'W': case 'K':
b_slow_cam = 1; i_fec_lp = strtol( optarg, NULL, 0 );
break; break;
case 'U': case 'G':
b_output_udp = 1; i_guard = strtol( optarg, NULL, 0 );
break; break;
case 'd': case 'X':
{ i_transmission = strtol( optarg, NULL, 0 );
char *psz_token, *psz_displayname;
char sz_port[6];
struct addrinfo *p_daddr;
struct addrinfo ai_hints;
int i_dup_config = 0;
snprintf( sz_port, sizeof( sz_port ), "%d", DEFAULT_PORT );
p_daddr = malloc( sizeof( p_daddr ) );
memset( p_daddr, '\0', sizeof( p_daddr ) );
if ( !strncmp( optarg, "[", 1 ) )
{
if ( (psz_token = strchr( optarg, ']' ) ) == NULL )
{
msg_Err(NULL, "Invalid target address for -d switch");
break; break;
}
char *psz_maddr = malloc( psz_token - optarg );
memset( psz_maddr, '\0', ( psz_token - optarg ) );
strncpy( psz_maddr, optarg + 1, ( psz_token - optarg - 1 ));
if ( (psz_token = strchr( psz_token, ':' )) != NULL )
{
*psz_token = '\0';
snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token + 1 ) );
}
memset( &ai_hints, 0, sizeof( ai_hints ) );
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
ai_hints.ai_family = AF_INET6;
int i_ai = getaddrinfo( psz_maddr, sz_port, &ai_hints, &p_daddr ); case 'O':
if ( i_ai != 0 ) i_frontend_timeout_duration = strtoll( optarg, NULL, 0 ) * 1000;
{
msg_Err( NULL, "Cannot duplicate to [%s]:%s: %s", psz_maddr,
sz_port, gai_strerror( i_ai ) );
break; break;
}
psz_displayname = malloc( INET6_ADDRSTRLEN + 20 );
snprintf( psz_displayname, ( INET6_ADDRSTRLEN + 20 ),
"duplicate ([%s]:%s)", psz_maddr, sz_port );
i_dup_config |= OUTPUT_VALID;
free( psz_maddr ); case 'H':
} i_hierarchy = strtol( optarg, NULL, 0 );
else break;
{
if ( (psz_token = strrchr( optarg, ':' )) != NULL )
{
*psz_token = '\0';
snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token + 1 ) );
}
memset( &ai_hints, 0, sizeof( ai_hints ) ); case 'u':
ai_hints.ai_socktype = SOCK_DGRAM; b_budget_mode = 1;
ai_hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV; break;
ai_hints.ai_family = AF_INET;
int i_ai = getaddrinfo( optarg, sz_port, &ai_hints, &p_daddr ); case 'w':
if ( i_ai != 0 ) b_select_pmts = 1;
{
msg_Err( NULL, "Cannot duplicate to %s:%s: %s", optarg,
sz_port, gai_strerror( i_ai ) );
break; break;
}
psz_displayname = malloc( INET_ADDRSTRLEN + 18 ); case 'U':
snprintf( psz_displayname, ( INET_ADDRSTRLEN + 18 ), b_udp_global = true;
"duplicate (%s:%s)", optarg, sz_port ); break;
i_dup_config |= OUTPUT_VALID; case 'L':
} i_latency_global = strtoll( optarg, NULL, 0 ) * 1000;
break;
if ( i_dup_config &= OUTPUT_VALID ) { case 'E':
output_Init( &output_dup, i_dup_config, psz_displayname, p_daddr ); i_retention_global = strtoll( optarg, NULL, 0 ) * 1000;
} break;
else
msg_Err( NULL, "Invalid configuration for -d switch: %s" , optarg);
free( psz_displayname ); case 'd':
freeaddrinfo( p_daddr ); psz_dup_config = optarg;
break; break;
}
case 'D': case 'D':
{ psz_udp_src = optarg;
char *psz_token;
struct in_addr maddr;
if ( pf_Open != NULL ) if ( pf_Open != NULL )
usage(); usage();
if ( psz_srv_socket != NULL )
{
msg_Err( NULL, "-r is only available for linux-dvb input" );
usage();
}
pf_Open = udp_Open; pf_Open = udp_Open;
pf_Read = udp_Read; pf_Read = udp_Read;
pf_Reset = udp_Reset;
pf_SetFilter = udp_SetFilter; pf_SetFilter = udp_SetFilter;
pf_UnsetFilter = udp_UnsetFilter; pf_UnsetFilter = udp_UnsetFilter;
if ( (psz_token = strrchr( optarg, '/' )) != NULL )
{
*psz_token = '\0';
b_src_rawudp = ( strncasecmp( psz_token + 1, "udp", 3 ) == 0 );
}
if ( (psz_token = strrchr( optarg, ':' )) != NULL )
{
*psz_token = '\0';
i_src_port = atoi( psz_token + 1 );
}
if ( !inet_aton( optarg, &maddr ) )
usage();
i_src_addr = maddr.s_addr;
break; break;
}
case 'A': case 'A':
i_asi_adapter = strtol( optarg, NULL, 0 ); i_asi_adapter = strtol( optarg, NULL, 0 );
if ( pf_Open != NULL ) if ( pf_Open != NULL )
usage(); usage();
if ( psz_srv_socket != NULL )
{
msg_Err( NULL, "-r is only available for linux-dvb input" );
usage();
}
pf_Open = asi_Open; pf_Open = asi_Open;
pf_Read = asi_Read; pf_Read = asi_Read;
pf_Reset = asi_Reset;
pf_SetFilter = asi_SetFilter; pf_SetFilter = asi_SetFilter;
pf_UnsetFilter = asi_UnsetFilter; pf_UnsetFilter = asi_UnsetFilter;
break; break;
case 'z':
b_any_type = 1;
break;
case 'C':
b_dvb_global = true;
break;
case 'W':
b_enable_emm = true;
break;
case 'Y':
b_enable_ecm = true;
b_dvb_global = true;
break;
case 'e': case 'e':
b_enable_epg = 1; b_epg_global = true;
break;
case 'M':
psz_network_name = optarg;
break;
case 'N':
i_network_id = strtoul( optarg, NULL, 0 );
break;
case 'T':
b_random_tsid = 1;
break;
case 'j':
psz_native_charset = optarg;
break;
case 'J':
psz_dvb_charset = optarg;
break;
case 'B':
psz_provider_name = optarg;
break; break;
case 'l': case 'l':
b_enable_syslog = 1; b_enable_syslog = 1;
break; break;
case 'T': case 'g':
b_unique_tsid = 1; psz_syslog_ident = optarg;
break;
case 'x':
if ( !strcmp(optarg, "text") )
i_print_type = PRINT_TEXT;
else if ( !strcmp(optarg, "xml") )
i_print_type = PRINT_XML;
else
msg_Warn( NULL, "unrecognized print type %s", optarg );
/* Make stdout line-buffered */
setvbuf(stdout, NULL, _IOLBF, 0);
break;
case 'Q':
i_quit_timeout_duration = strtoll( optarg, NULL, 0 ) * 1000;
break; break;
case 'V': case 'V':
DisplayVersion();
exit(0); exit(0);
break; break;
case 'Z':
psz_mrtg_file = optarg;
break;
case 'h': case 'h':
default: default:
usage(); usage();
...@@ -686,21 +853,106 @@ int main( int i_argc, char **pp_argv ) ...@@ -686,21 +853,106 @@ int main( int i_argc, char **pp_argv )
usage(); usage();
if ( b_enable_syslog ) if ( b_enable_syslog )
msg_Connect( pp_argv[0] ); msg_Connect( psz_syslog_ident ? psz_syslog_ident : pp_argv[0] );
if ( i_verbose )
DisplayVersion();
msg_Warn( NULL, "restarting" ); msg_Warn( NULL, "restarting" );
switch (i_print_type)
{
case PRINT_XML:
printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
printf("<TS>\n");
break;
default:
break;
}
if ( b_output_udp ) if ( b_udp_global )
{ {
msg_Warn( NULL, "raw UDP output is deprecated. Please consider using RTP." ); msg_Warn( NULL, "raw UDP output is deprecated. Please consider using RTP." );
msg_Warn( NULL, "for DVB-IP compliance you should use RTP." ); msg_Warn( NULL, "for DVB-IP compliance you should use RTP." );
} }
signal( SIGHUP, SigHandler ); if ( b_epg_global && !b_dvb_global )
{
msg_Dbg( NULL, "turning on DVB compliance, required by EPG information" );
b_dvb_global = true;
}
memset( &output_dup, 0, sizeof(output_dup) );
if ( psz_dup_config != NULL )
{
output_config_t config;
config_Defaults( &config );
if ( !config_ParseHost( &config, psz_dup_config ) )
msg_Err( NULL, "Invalid target address for -d switch" );
else
{
output_Init( &output_dup, &config );
output_Change( &output_dup, &config );
}
config_Free( &config );
}
if ( strcasecmp( psz_native_charset, psz_dvb_charset ) )
{
#ifdef HAVE_ICONV
iconv_t iconv_system = iconv_open( psz_dvb_charset,
psz_native_charset );
if ( iconv_system != (iconv_t)-1 )
{
size_t i = strlen( psz_network_name );
char *p, *psz_string;
i_network_name_tmp_size = i * 6;
p = psz_string = malloc(i_network_name_tmp_size);
if ( iconv( iconv_system, (char **)&psz_network_name, &i, &p,
&i_network_name_tmp_size ) == -1 )
free( psz_string );
else
{
p_network_name_tmp = psz_string;
i_network_name_tmp_size = p - psz_string;
}
iconv_close( iconv_system );
}
#else
msg_Warn( NULL,
"unable to convert from %s to %s (iconv is not available)",
psz_native_charset, psz_dvb_charset );
#endif
}
if ( p_network_name_tmp == NULL )
{
p_network_name_tmp = strdup(psz_network_name);
i_network_name_tmp_size = strlen(psz_network_name);
}
p_network_name = dvb_string_set( (uint8_t *)p_network_name_tmp,
i_network_name_tmp_size, psz_dvb_charset,
&i_network_name_size );
free( p_network_name_tmp );
/* Set signal handlers */
memset( &sa, 0, sizeof(struct sigaction) );
sa.sa_handler = SigHandler;
sigfillset( &set );
if ( sigaction( SIGHUP, &sa, NULL ) == -1 || sigaction( SIGINT, &sa, NULL ) == -1 )
{
msg_Err( NULL, "couldn't set signal handler: %s", strerror(errno) );
exit(EXIT_FAILURE);
}
srand( time(NULL) * getpid() ); srand( time(NULL) * getpid() );
demux_Open(); demux_Open();
// init the mrtg logfile
mrtgInit(psz_mrtg_file);
if ( i_priority > 0 ) if ( i_priority > 0 )
{ {
memset( &param, 0, sizeof(struct sched_param) ); memset( &param, 0, sizeof(struct sched_param) );
...@@ -713,23 +965,62 @@ int main( int i_argc, char **pp_argv ) ...@@ -713,23 +965,62 @@ int main( int i_argc, char **pp_argv )
} }
} }
ReadConfiguration( psz_conf_file ); config_ReadFile( psz_conf_file );
if ( psz_srv_socket != NULL ) if ( psz_srv_socket != NULL )
comm_Open(); comm_Open();
for ( ; ; ) for ( ; ; )
{ {
if ( b_hup_received ) block_t *p_ts;
if ( b_exit_now )
{ {
b_hup_received = 0; msg_Info( NULL, "Shutdown was requested." );
msg_Warn( NULL, "HUP received, reloading" ); break;
ReadConfiguration( psz_conf_file );
} }
demux_Run(); if ( b_conf_reload )
{
b_conf_reload = 0;
msg_Info( NULL, "Configuration reload was requested." );
config_ReadFile( psz_conf_file );
} }
if ( i_quit_timeout && i_quit_timeout <= i_wallclock )
{
switch (i_print_type)
{
case PRINT_XML:
printf("</TS>\n");
break;
default:
break;
}
exit(EXIT_SUCCESS);
}
p_ts = pf_Read( i_poll_timeout );
if ( p_ts != NULL )
{
mrtgAnalyse(p_ts);
demux_Run( p_ts );
}
i_poll_timeout = output_Send();
if ( i_poll_timeout == -1 || i_poll_timeout > MAX_POLL_TIMEOUT )
i_poll_timeout = MAX_POLL_TIMEOUT;
}
mrtgClose();
outputs_Close( i_nb_outputs );
demux_Close();
free( p_network_name );
if ( b_enable_syslog ) if ( b_enable_syslog )
msg_Disconnect(); msg_Disconnect();
if ( psz_srv_socket && i_comm_fd > -1 )
unlink( psz_srv_socket );
return EXIT_SUCCESS;
} }
/***************************************************************************** /*****************************************************************************
* dvblast.h * dvblast.h
***************************************************************************** *****************************************************************************
* Copyright (C) 2004, 2008-2009 VideoLAN * Copyright (C) 2004, 2008-2011 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* Andy Gatward <a.j.gatward@reading.ac.uk> * Andy Gatward <a.j.gatward@reading.ac.uk>
...@@ -22,23 +21,27 @@ ...@@ -22,23 +21,27 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#include <dvbpsi/dvbpsi.h> #include <netdb.h>
#include <dvbpsi/descriptor.h> #include <sys/socket.h>
#include <dvbpsi/pmt.h>
#include "netdb.h" #define HAVE_CLOCK_NANOSLEEP
#include "sys/socket.h" #define HAVE_ICONV
#define DEFAULT_PORT 3001 #define DEFAULT_PORT 3001
#define TS_SIZE 188 #define TS_SIZE 188
#define NB_BLOCKS 7 #define MAX_PIDS 8192
#define NB_BLOCKS_IPV6 6 // assume MTU of 1280 bytes for IPv6 #define DEFAULT_IPV4_MTU 1500
#define RTP_SIZE 12 #define DEFAULT_IPV6_MTU 1280
#define EMPTY_PID 8192
#define PADDING_PID 8191 #define PADDING_PID 8191
#define WATCHDOG_WAIT 10000000LL #define WATCHDOG_WAIT 10000000LL
#define MAX_ERRORS 1000 #define MAX_ERRORS 1000
#define DEFAULT_VERBOSITY 3 #define DEFAULT_VERBOSITY 4
#define MAX_POLL_TIMEOUT 100000 /* 100 ms */
#define DEFAULT_OUTPUT_LATENCY 200000 /* 200 ms */
#define DEFAULT_MAX_RETENTION 40000 /* 40 ms */
#define MAX_EIT_RETENTION 500000 /* 500 ms */
#define DEFAULT_FRONTEND_TIMEOUT 30000000 /* 30 s */
#define EXIT_STATUS_FRONTEND_TIMEOUT 100
/***************************************************************************** /*****************************************************************************
* Output configuration flags (for output_t -> i_config) - bit values * Output configuration flags (for output_t -> i_config) - bit values
...@@ -47,6 +50,8 @@ ...@@ -47,6 +50,8 @@
* Bit 2 : Set if output is valid (replaces m_addr != 0 tests) * Bit 2 : Set if output is valid (replaces m_addr != 0 tests)
* Bit 3 : Set for UDP, otherwise use RTP if a network stream * Bit 3 : Set for UDP, otherwise use RTP if a network stream
* Bit 4 : Set for file / FIFO output, unset for network (future use) * Bit 4 : Set for file / FIFO output, unset for network (future use)
* Bit 5 : Set if DVB conformance tables are inserted
* Bit 6 : Set if DVB EIT schedule tables are forwarded
*****************************************************************************/ *****************************************************************************/
#define OUTPUT_WATCH 0x01 #define OUTPUT_WATCH 0x01
...@@ -54,6 +59,8 @@ ...@@ -54,6 +59,8 @@
#define OUTPUT_VALID 0x04 #define OUTPUT_VALID 0x04
#define OUTPUT_UDP 0x08 #define OUTPUT_UDP 0x08
#define OUTPUT_FILE 0x10 #define OUTPUT_FILE 0x10
#define OUTPUT_DVB 0x20
#define OUTPUT_EPG 0x40
typedef int64_t mtime_t; typedef int64_t mtime_t;
...@@ -61,53 +68,88 @@ typedef struct block_t ...@@ -61,53 +68,88 @@ typedef struct block_t
{ {
uint8_t p_ts[TS_SIZE]; uint8_t p_ts[TS_SIZE];
int i_refcount; int i_refcount;
mtime_t i_dts;
struct block_t *p_next; struct block_t *p_next;
} block_t; } block_t;
typedef struct output_t typedef struct packet_t packet_t;
typedef struct output_config_t
{ {
/* address information, protocol agnostic */ /* identity */
struct sockaddr_storage *p_addr; int i_family;
socklen_t i_addrlen; struct sockaddr_storage connect_addr;
struct sockaddr_storage bind_addr;
int i_if_index_v6;
/* display string */ /* common config */
char *psz_displayname; char *psz_displayname;
uint64_t i_config;
/* output config */
char *psz_service_name;
char *psz_service_provider;
uint8_t pi_ssrc[4];
mtime_t i_output_latency, i_max_retention;
int i_ttl;
uint8_t i_tos;
int i_mtu;
/* demux config */
int i_tsid;
uint16_t i_sid; /* 0 if raw mode */
uint16_t *pi_pids;
int i_nb_pids;
} output_config_t;
typedef struct output_t
{
output_config_t config;
/* output */ /* output */
int i_handle; int i_handle;
block_t *pp_blocks[NB_BLOCKS]; packet_t *p_packets, *p_last_packet;
int i_depth; uint16_t i_seqnum;
uint16_t i_cc;
mtime_t i_ref_timestamp; mtime_t i_ref_timestamp;
mtime_t i_ref_wallclock; mtime_t i_ref_wallclock;
/* demux */ /* demux */
int i_nb_errors; int i_nb_errors;
mtime_t i_last_error; mtime_t i_last_error;
dvbpsi_psi_section_t *p_pat_section; uint8_t *p_pat_section;
uint8_t i_pat_version, i_pat_cc; uint8_t i_pat_version, i_pat_cc;
dvbpsi_psi_section_t *p_pmt_section; uint8_t *p_pmt_section;
uint8_t i_pmt_version, i_pmt_cc; uint8_t i_pmt_version, i_pmt_cc;
dvbpsi_psi_section_t *p_sdt_section; uint8_t *p_nit_section;
uint8_t i_nit_version, i_nit_cc;
uint8_t *p_sdt_section;
uint8_t i_sdt_version, i_sdt_cc; uint8_t i_sdt_version, i_sdt_cc;
uint8_t i_eit_cc; block_t *p_eit_ts_buffer;
uint16_t i_ts_id; uint8_t i_eit_ts_buffer_offset, i_eit_cc;
uint16_t i_tsid;
/* configuration */
uint16_t i_sid; /* 0 if raw mode */
uint16_t *pi_pids;
int i_nb_pids;
uint8_t i_config;
} output_t; } output_t;
typedef struct ts_pid_info {
mtime_t i_first_packet_ts; /* Time of the first seen packet */
mtime_t i_last_packet_ts; /* Time of the last seen packet */
unsigned long i_packets; /* How much packets have been seen */
unsigned long i_cc_errors; /* Countinuity counter errors */
unsigned long i_transport_errors; /* Transport errors */
unsigned long i_bytes_per_sec; /* How much bytes were process last second */
uint8_t i_scrambling; /* Scrambling bits from the last ts packet */
/* 0 = Not scrambled
1 = Reserved for future use
2 = Scrambled with even key
3 = Scrambled with odd key */
} ts_pid_info_t;
extern int i_syslog; extern int i_syslog;
extern int i_verbose; extern int i_verbose;
extern output_t **pp_outputs; extern output_t **pp_outputs;
extern int i_nb_outputs; extern int i_nb_outputs;
extern output_t output_dup; extern output_t output_dup;
extern char *psz_srv_socket; extern char *psz_srv_socket;
extern int i_ttl; extern int i_comm_fd;
extern in_addr_t i_ssrc;
extern int i_adapter; extern int i_adapter;
extern int i_fenum; extern int i_fenum;
extern int i_frequency; extern int i_frequency;
...@@ -118,22 +160,36 @@ extern int i_rolloff; ...@@ -118,22 +160,36 @@ extern int i_rolloff;
extern int i_voltage; extern int i_voltage;
extern int b_tone; extern int b_tone;
extern int i_bandwidth; extern int i_bandwidth;
extern int i_inversion;
extern char *psz_modulation; extern char *psz_modulation;
extern int i_pilot;
extern int i_fec_lp;
extern int i_guard;
extern int i_transmission;
extern int i_hierarchy;
extern mtime_t i_frontend_timeout_duration;
extern mtime_t i_quit_timeout;
extern mtime_t i_quit_timeout_duration;
extern int b_budget_mode; extern int b_budget_mode;
extern int b_slow_cam; extern int b_any_type;
extern int b_output_udp; extern int b_select_pmts;
extern int b_enable_epg; extern int b_random_tsid;
extern int b_unique_tsid; extern uint16_t i_network_id;
extern volatile int b_hup_received; extern uint8_t *p_network_name;
extern mtime_t i_ca_timeout; extern size_t i_network_name_size;
extern mtime_t i_wallclock;
extern volatile int b_conf_reload;
extern volatile int b_exit_now;
extern int i_comm_fd; extern int i_comm_fd;
extern uint16_t i_src_port; extern char *psz_udp_src;
extern in_addr_t i_src_addr;
extern int b_src_rawudp;
extern int i_asi_adapter; extern int i_asi_adapter;
extern const char *psz_native_charset;
extern const char *psz_dvb_charset;
extern enum print_type_t i_print_type;
extern void (*pf_Open)( void ); extern void (*pf_Open)( void );
extern block_t * (*pf_Read)( void ); extern block_t * (*pf_Read)( mtime_t i_poll_timeout );
extern void (*pf_Reset)( void );
extern int (*pf_SetFilter)( uint16_t i_pid ); extern int (*pf_SetFilter)( uint16_t i_pid );
extern void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ); extern void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid );
...@@ -141,49 +197,78 @@ extern void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ); ...@@ -141,49 +197,78 @@ extern void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid );
* Prototypes * Prototypes
*****************************************************************************/ *****************************************************************************/
void config_Init( output_config_t *p_config );
void config_Free( output_config_t *p_config );
bool config_ParseHost( output_config_t *p_config, char *psz_string );
/* Connect/Disconnect from syslogd */ /* Connect/Disconnect from syslogd */
void msg_Connect( const char *ident ); void msg_Connect( const char *ident );
void msg_Disconnect( void ); void msg_Disconnect( void );
/* */ /* */
void msg_Info( void *_unused, const char *psz_format, ... ); __attribute__ ((format(printf, 2, 3))) void msg_Info( void *_unused, const char *psz_format, ... );
void msg_Err( void *_unused, const char *psz_format, ... ); __attribute__ ((format(printf, 2, 3))) void msg_Err( void *_unused, const char *psz_format, ... );
void msg_Warn( void *_unused, const char *psz_format, ... ); __attribute__ ((format(printf, 2, 3))) void msg_Warn( void *_unused, const char *psz_format, ... );
void msg_Dbg( void *_unused, const char *psz_format, ... ); __attribute__ ((format(printf, 2, 3))) void msg_Dbg( void *_unused, const char *psz_format, ... );
void msg_Raw( void *_unused, const char *psz_format, ... ); __attribute__ ((format(printf, 2, 3))) void msg_Raw( void *_unused, const char *psz_format, ... );
/* */ /* */
bool streq(char *a, char *b);
char * xstrdup(char *str);
mtime_t mdate( void ); mtime_t mdate( void );
void msleep( mtime_t delay ); void msleep( mtime_t delay );
void hexDump( uint8_t *p_data, uint32_t i_len ); void hexDump( uint8_t *p_data, uint32_t i_len );
struct addrinfo *ParseNodeService( char *_psz_string, char **ppsz_end,
uint16_t i_default_port );
uint8_t *psi_pack_section( uint8_t *p_sections, unsigned int *pi_size );
uint8_t *psi_pack_sections( uint8_t **pp_sections, unsigned int *pi_size );
uint8_t **psi_unpack_sections( uint8_t *p_flat_sections, unsigned int i_size );
void dvb_Open( void ); void dvb_Open( void );
block_t * dvb_Read( void ); void dvb_Reset( void );
block_t * dvb_Read( mtime_t i_poll_timeout );
int dvb_SetFilter( uint16_t i_pid ); int dvb_SetFilter( uint16_t i_pid );
void dvb_UnsetFilter( int i_fd, uint16_t i_pid ); void dvb_UnsetFilter( int i_fd, uint16_t i_pid );
uint8_t dvb_FrontendStatus( uint8_t *p_answer, ssize_t *pi_size ); uint8_t dvb_FrontendStatus( uint8_t *p_answer, ssize_t *pi_size );
void udp_Open( void ); void udp_Open( void );
block_t * udp_Read( void ); block_t * udp_Read( mtime_t i_poll_timeout );
void udp_Reset( void );
int udp_SetFilter( uint16_t i_pid ); int udp_SetFilter( uint16_t i_pid );
void udp_UnsetFilter( int i_fd, uint16_t i_pid ); void udp_UnsetFilter( int i_fd, uint16_t i_pid );
void asi_Open( void ); void asi_Open( void );
block_t * asi_Read( void ); block_t * asi_Read( mtime_t i_poll_timeout );
void asi_Reset( void );
int asi_SetFilter( uint16_t i_pid ); int asi_SetFilter( uint16_t i_pid );
void asi_UnsetFilter( int i_fd, uint16_t i_pid ); void asi_UnsetFilter( int i_fd, uint16_t i_pid );
void demux_Open( void ); void demux_Open( void );
void demux_Run( void ); void demux_Run( block_t *p_ts );
void demux_Change( output_t *p_output, uint16_t i_sid, void demux_Change( output_t *p_output, const output_config_t *p_config );
uint16_t *pi_pids, int i_nb_pids );
void demux_ResendCAPMTs( void ); void demux_ResendCAPMTs( void );
int PIDIsSelected( uint16_t i_pid ); bool demux_PIDIsSelected( uint16_t i_pid );
char *demux_Iconv(void *_unused, const char *psz_encoding,
output_t *output_Create( uint8_t i_config, const char *psz_displayname, void *p_init_data ); char *p_string, size_t i_length);
int output_Init( output_t *p_output, uint8_t i_config, const char *psz_displayname, void *p_init_data ); void demux_Close( void );
uint8_t *demux_get_current_packed_PAT( unsigned int *pi_pack_size );
uint8_t *demux_get_current_packed_CAT( unsigned int *pi_pack_size );
uint8_t *demux_get_current_packed_NIT( unsigned int *pi_pack_size );
uint8_t *demux_get_current_packed_SDT( unsigned int *pi_pack_size );
uint8_t *demux_get_packed_PMT( uint16_t service_id, unsigned int *pi_pack_size );
void demux_get_PID_info( uint16_t i_pid, uint8_t *p_data );
void demux_get_PIDS_info( uint8_t *p_data );
output_t *output_Create( const output_config_t *p_config );
int output_Init( output_t *p_output, const output_config_t *p_config );
void output_Close( output_t *p_output ); void output_Close( output_t *p_output );
void output_Put( output_t *p_output, block_t *p_block ); void output_Put( output_t *p_output, block_t *p_block );
mtime_t output_Send( void );
output_t *output_Find( const output_config_t *p_config );
void output_Change( output_t *p_output, const output_config_t *p_config );
void outputs_Close( int i_num_outputs );
void comm_Open( void ); void comm_Open( void );
void comm_Read( void ); void comm_Read( void );
...@@ -219,87 +304,3 @@ static inline void block_DeleteChain( block_t *p_block ) ...@@ -219,87 +304,3 @@ static inline void block_DeleteChain( block_t *p_block )
p_block = p_next; p_block = p_next;
} }
} }
/*****************************************************************************
* block_GetSync
*****************************************************************************/
static inline uint8_t block_GetSync( block_t *p_block )
{
return p_block->p_ts[0];
}
/*****************************************************************************
* block_HasTransportError
*****************************************************************************/
static inline uint8_t block_HasTransportError( block_t *p_block )
{
return p_block->p_ts[1] & 0x80;
}
/*****************************************************************************
* block_UnitStart
*****************************************************************************/
static inline uint8_t block_UnitStart( block_t *p_block )
{
return p_block->p_ts[1] & 0x40;
}
/*****************************************************************************
* block_GetPID
*****************************************************************************/
static inline uint16_t block_GetPID( block_t *p_block )
{
return (((uint16_t)p_block->p_ts[1] & 0x1f) << 8)
| p_block->p_ts[2];
}
/*****************************************************************************
* block_GetScrambling
*****************************************************************************/
static inline uint8_t block_GetScrambling( block_t *p_block )
{
return p_block->p_ts[3] & 0xc0;
}
/*****************************************************************************
* block_GetCC
*****************************************************************************/
static inline uint8_t block_GetCC( block_t *p_block )
{
return p_block->p_ts[3] & 0xf;
}
/*****************************************************************************
* block_HasPCR
*****************************************************************************/
static inline int block_HasPCR( block_t *p_block )
{
return ( p_block->p_ts[3] & 0x20 ) && /* adaptation field present */
( p_block->p_ts[4] >= 7 ) && /* adaptation field size */
( p_block->p_ts[5] & 0x10 ); /* has PCR */
}
/*****************************************************************************
* block_GetPCR
*****************************************************************************/
static inline mtime_t block_GetPCR( block_t *p_block )
{
return ( (mtime_t)p_block->p_ts[6] << 25 ) |
( (mtime_t)p_block->p_ts[7] << 17 ) |
( (mtime_t)p_block->p_ts[8] << 9 ) |
( (mtime_t)p_block->p_ts[9] << 1 ) |
( (mtime_t)p_block->p_ts[10] >> 7 );
}
/*****************************************************************************
* block_GetPayload
*****************************************************************************/
static inline uint8_t *block_GetPayload( block_t *p_block )
{
if ( !(p_block->p_ts[3] & 0x10) )
return NULL;
if ( !(p_block->p_ts[3] & 0x20) )
return &p_block->p_ts[4];
return &p_block->p_ts[ 5 + p_block->p_ts[4] ];
}
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
# dvblast_mmi.sh # dvblast_mmi.sh
############################################################################### ###############################################################################
# Copyright (C) 1998-2008 VideoLAN # Copyright (C) 1998-2008 VideoLAN
# $Id$
# #
# Authors: Christophe Massiot <massiot@via.ecp.fr> # Authors: Christophe Massiot <massiot@via.ecp.fr>
# #
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
* dvblastctl.c * dvblastctl.c
***************************************************************************** *****************************************************************************
* Copyright (C) 2008 VideoLAN * Copyright (C) 2008 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
...@@ -22,12 +21,16 @@ ...@@ -22,12 +21,16 @@
*****************************************************************************/ *****************************************************************************/
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <inttypes.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
...@@ -35,6 +38,11 @@ ...@@ -35,6 +38,11 @@
#include <errno.h> #include <errno.h>
#include <getopt.h> #include <getopt.h>
#include <bitstream/mpeg/psi.h>
#include <bitstream/dvb/si.h>
#include <bitstream/dvb/si_print.h>
#include <bitstream/mpeg/psi_print.h>
#include "dvblast.h" #include "dvblast.h"
#include "en50221.h" #include "en50221.h"
#include "comm.h" #include "comm.h"
...@@ -43,23 +51,198 @@ ...@@ -43,23 +51,198 @@
int i_verbose = 3; int i_verbose = 3;
int i_syslog = 0; int i_syslog = 0;
print_type_t i_print_type = PRINT_TEXT;
mtime_t now;
int i_fd = -1;
char psz_client_socket[PATH_MAX] = {0};
static void clean_client_socket() {
if ( i_fd > -1 )
{
close( i_fd );
i_fd = -1;
}
if ( psz_client_socket[0] )
{
unlink( psz_client_socket );
psz_client_socket[0] = '\0';
}
}
/*****************************************************************************
* The following two functions are from biTStream's examples and are under the
* WTFPL (see LICENSE.WTFPL).
****************************************************************************/
__attribute__ ((format(printf, 2, 3)))
static void psi_print(void *_unused, const char *psz_format, ...)
{
char psz_fmt[strlen(psz_format) + 2];
va_list args;
va_start(args, psz_format);
strcpy(psz_fmt, psz_format);
strcat(psz_fmt, "\n");
vprintf(psz_fmt, args);
}
__attribute__ ((format(printf, 1, 2)))
void return_error( const char *psz_format, ... )
{
va_list args;
char psz_fmt[1024];
clean_client_socket();
va_start( args, psz_format );
if ( i_print_type == PRINT_XML )
snprintf( psz_fmt, sizeof(psz_fmt) - 1, "<ERROR msg=\"%s\"/>\n", psz_format );
else
snprintf( psz_fmt, sizeof(psz_fmt) - 1, "ERROR: %s\n", psz_format );
psz_fmt[sizeof(psz_fmt) - 1] = '\0';
vfprintf( stderr, psz_fmt, args );
exit(255);
}
static char *iconv_append_null(const char *p_string, size_t i_length)
{
char *psz_string = malloc(i_length + 1);
memcpy(psz_string, p_string, i_length);
psz_string[i_length] = '\0';
return psz_string;
}
char *psi_iconv(void *_unused, const char *psz_encoding,
char *p_string, size_t i_length)
{
return iconv_append_null(p_string, i_length);
}
void print_pids_header( void )
{
if ( i_print_type == PRINT_XML )
printf("<PIDS>\n");
}
void print_pids_footer( void )
{
if ( i_print_type == PRINT_XML )
printf("</PIDS>\n");
}
void print_pid(uint16_t i_pid, ts_pid_info_t *p_info)
{
if ( p_info->i_packets == 0 )
return;
if ( i_print_type == PRINT_TEXT )
printf("pid %d packn %lu ccerr %lu tserr %lu scramble %d Bps %lu seen %"PRId64"\n",
i_pid,
p_info->i_packets,
p_info->i_cc_errors,
p_info->i_transport_errors,
p_info->i_scrambling,
p_info->i_bytes_per_sec,
now - p_info->i_last_packet_ts
);
else
printf("<PID pid=\"%d\" packn=\"%lu\" ccerr=\"%lu\" tserr=\"%lu\" scramble=\"%d\" Bps=\"%lu\" seen=\"%"PRId64"\" />\n",
i_pid,
p_info->i_packets,
p_info->i_cc_errors,
p_info->i_transport_errors,
p_info->i_scrambling,
p_info->i_bytes_per_sec,
now - p_info->i_last_packet_ts
);
}
void print_pids( uint8_t *p_data )
{
int i_pid;
print_pids_header();
for ( i_pid = 0; i_pid < MAX_PIDS; i_pid++ ) {
ts_pid_info_t *p_info = (ts_pid_info_t *)(p_data + i_pid * sizeof(ts_pid_info_t));
print_pid( i_pid, p_info );
}
print_pids_footer();
}
struct dvblastctl_option {
char * opt;
int nparams;
ctl_cmd_t cmd;
};
static const struct dvblastctl_option options[] =
{
{ "reload", 0, CMD_RELOAD },
{ "shutdown", 0, CMD_SHUTDOWN },
{ "fe_status", 0, CMD_FRONTEND_STATUS },
{ "mmi_status", 0, CMD_MMI_STATUS },
{ "mmi_slot_status", 1, CMD_MMI_SLOT_STATUS }, /* arg: slot */
{ "mmi_open", 1, CMD_MMI_OPEN }, /* arg: slot */
{ "mmi_close", 1, CMD_MMI_CLOSE }, /* arg: slot */
{ "mmi_get", 1, CMD_MMI_RECV }, /* arg: slot */
{ "mmi_send_text", 1, CMD_MMI_SEND_TEXT }, /* arg: slot, en50221_mmi_object_t */
{ "mmi_send_choice", 2, CMD_MMI_SEND_CHOICE }, /* arg: slot, en50221_mmi_object_t */
{ "get_pat", 0, CMD_GET_PAT },
{ "get_cat", 0, CMD_GET_CAT },
{ "get_nit", 0, CMD_GET_NIT },
{ "get_sdt", 0, CMD_GET_SDT },
{ "get_pmt", 1, CMD_GET_PMT }, /* arg: service_id (uint16_t) */
{ "get_pids", 0, CMD_GET_PIDS },
{ "get_pid", 1, CMD_GET_PID }, /* arg: pid (uint16_t) */
{ NULL, 0, 0 }
};
void usage() void usage()
{ {
msg_Raw( NULL, "DVBlastctl %d.%d.%d%s", VERSION_MAJOR, VERSION_MINOR, printf("DVBlastctl %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR,
VERSION_REVISION, VERSION_EXTRA ); VERSION_REVISION, VERSION_EXTRA );
msg_Raw( NULL, "Usage: dvblastctl -r <remote socket> reload|shutdown|fe_status|mmi_status|mmi_open|mmi_close|mmi_get|mmi_send_text|mmi_send_choice [<CAM slot>] [<text/choice>]" ); printf("Usage: dvblastctl -r <remote socket> [-x <text|xml>] [cmd]\n");
printf("Options:\n");
printf(" -r --remote-socket <name> Set socket name to <name>.\n" );
printf(" -x --print <text|xml> Choose output format for info commands.\n" );
printf("Control commands:\n");
printf(" reload Reload configuration.\n");
printf(" shutdown Shutdown DVBlast.\n");
printf("Status commands:\n");
printf(" fe_status Read frontend status information.\n");
printf(" mmi_status Read CAM status.\n");
printf("MMI commands:\n");
printf(" mmi_slot_status <slot> Read MMI slot status.\n");
printf(" mmi_open <slot> Open MMI slot.\n");
printf(" mmi_close <slot> Close MMI slot.\n");
printf(" mmi_get <slot> Read MMI slot.\n");
printf(" mmi_send_text <slot> <text> Send text to MMI slot.\n");
printf(" mmi_send_choice <slot> <choice> Send choice to MMI slot.\n");
printf("Demux info commands:\n");
printf(" get_pat Return last PAT table.\n");
printf(" get_cat Return last CAT table.\n");
printf(" get_nit Return last NIT table.\n");
printf(" get_sdt Return last SDT table.\n");
printf(" get_pmt <service_id> Return last PMT table.\n");
printf(" get_pids Return info about all pids.\n");
printf(" get_pid <pid> Return info for chosen pid only.\n");
printf("\n");
exit(1); exit(1);
} }
int main( int i_argc, char **ppsz_argv ) int main( int i_argc, char **ppsz_argv )
{ {
char psz_client_socket[L_tmpnam]; char *client_socket_tmpl = "dvblastctl.clientsock.XXXXXX";
char *psz_srv_socket = NULL; char *psz_srv_socket = NULL;
int i_fd, i_mask; int i;
int i = 65535; char *p_cmd, *p_arg1 = NULL, *p_arg2 = NULL;
ssize_t i_size; ssize_t i_size;
struct sockaddr_un sun_client, sun_server; struct sockaddr_un sun_client, sun_server;
uint8_t p_buffer[COMM_BUFFER_SIZE]; uint8_t p_buffer[COMM_BUFFER_SIZE];
uint8_t *p_data = p_buffer + COMM_HEADER_SIZE;
uint16_t i_pid = 0;
struct dvblastctl_option opt = { 0, 0, 0 };
for ( ; ; ) for ( ; ; )
{ {
...@@ -68,11 +251,12 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -68,11 +251,12 @@ int main( int i_argc, char **ppsz_argv )
static const struct option long_options[] = static const struct option long_options[] =
{ {
{"remote-socket", required_argument, NULL, 'r'}, {"remote-socket", required_argument, NULL, 'r'},
{"print", required_argument, NULL, 'x'},
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
if ( (c = getopt_long(i_argc, ppsz_argv, "r:h", long_options, NULL)) == -1 ) if ( (c = getopt_long(i_argc, ppsz_argv, "r:x:h", long_options, NULL)) == -1 )
break; break;
switch ( c ) switch ( c )
...@@ -81,53 +265,76 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -81,53 +265,76 @@ int main( int i_argc, char **ppsz_argv )
psz_srv_socket = optarg; psz_srv_socket = optarg;
break; break;
case 'x':
if ( !strcmp(optarg, "text") )
i_print_type = PRINT_TEXT;
else if ( !strcmp(optarg, "xml") )
i_print_type = PRINT_XML;
else
msg_Warn( NULL, "unrecognized print type %s", optarg );
/* Make stdout line-buffered */
setvbuf(stdout, NULL, _IOLBF, 0);
break;
case 'h': case 'h':
default: default:
usage(); usage();
} }
} }
if ( ppsz_argv[optind] == NULL || psz_srv_socket == NULL ) /* Validate commands */
usage(); #define usage_error(msg, ...) \
do { \
if ( strcmp(ppsz_argv[optind], "reload") msg_Err( NULL, msg, ##__VA_ARGS__ ); \
&& strcmp(ppsz_argv[optind], "shutdown") usage(); \
&& strcmp(ppsz_argv[optind], "fe_status") } while(0)
&& strcmp(ppsz_argv[optind], "mmi_status") p_cmd = ppsz_argv[optind];
&& strcmp(ppsz_argv[optind], "mmi_slot_status") p_arg1 = ppsz_argv[optind + 1];
&& strcmp(ppsz_argv[optind], "mmi_open") p_arg2 = ppsz_argv[optind + 2];
&& strcmp(ppsz_argv[optind], "mmi_close")
&& strcmp(ppsz_argv[optind], "mmi_get") if ( !psz_srv_socket )
&& strcmp(ppsz_argv[optind], "mmi_send_text") usage_error( "Remote socket is not set.\n" );
&& strcmp(ppsz_argv[optind], "mmi_send_choice") )
usage(); if ( !p_cmd )
usage_error( "Command is not set.\n" );
if ( (!strcmp(ppsz_argv[optind], "mmi_slot_status")
|| !strcmp(ppsz_argv[optind], "mmi_open") i = 0;
|| !strcmp(ppsz_argv[optind], "mmi_close") do {
|| !strcmp(ppsz_argv[optind], "mmi_get") if ( streq(ppsz_argv[optind], options[i].opt) )
|| !strcmp(ppsz_argv[optind], "mmi_send_text")
|| !strcmp(ppsz_argv[optind], "mmi_send_choice"))
&& ppsz_argv[optind + 1] == NULL )
usage();
if ( !strcmp(ppsz_argv[optind], "mmi_send_choice")
&& ppsz_argv[optind + 2] == NULL )
usage();
#warning expect brain-dead gcc warning about tmpnam here
if ( tmpnam(psz_client_socket) == NULL )
{ {
msg_Err( NULL, "cannot build UNIX socket (%s)", strerror(errno) ); opt = options[i];
return -1; break;
}
} while ( options[++i].opt );
if ( !opt.opt )
usage_error( "Unknown command: %s\n", p_cmd );
if ( opt.nparams == 1 && !p_arg1 )
usage_error( "%s option needs parameter.\n", opt.opt );
if ( opt.nparams == 2 && (!p_arg1 || !p_arg2) )
usage_error( "%s option needs two parameters.\n", opt.opt );
#undef usage_error
/* Create client socket name */
char *tmpdir = getenv("TMPDIR");
snprintf( psz_client_socket, PATH_MAX - 1, "%s/%s",
tmpdir ? tmpdir : "/tmp", client_socket_tmpl );
psz_client_socket[PATH_MAX - 1] = '\0';
int tmp_fd = mkstemp(psz_client_socket);
if ( tmp_fd > -1 ) {
close(tmp_fd);
unlink(psz_client_socket);
} else {
return_error( "Cannot build UNIX socket %s (%s)", psz_client_socket, strerror(errno) );
} }
if ( (i_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) < 0 ) if ( (i_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) < 0 )
{ return_error( "Cannot create UNIX socket (%s)", strerror(errno) );
msg_Err( NULL, "cannot create UNIX socket (%s)", strerror(errno) );
return -1;
}
i = COMM_MAX_MSG_CHUNK;
setsockopt( i_fd, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i) ); setsockopt( i_fd, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i) );
memset( &sun_client, 0, sizeof(sun_client) ); memset( &sun_client, 0, sizeof(sun_client) );
...@@ -135,17 +342,10 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -135,17 +342,10 @@ int main( int i_argc, char **ppsz_argv )
strncpy( sun_client.sun_path, psz_client_socket, strncpy( sun_client.sun_path, psz_client_socket,
sizeof(sun_client.sun_path) ); sizeof(sun_client.sun_path) );
sun_client.sun_path[sizeof(sun_client.sun_path) - 1] = '\0'; sun_client.sun_path[sizeof(sun_client.sun_path) - 1] = '\0';
i_mask = umask(077);
if ( bind( i_fd, (struct sockaddr *)&sun_client, if ( bind( i_fd, (struct sockaddr *)&sun_client,
SUN_LEN(&sun_client) ) < 0 ) SUN_LEN(&sun_client) ) < 0 )
{ return_error( "Cannot bind (%s)", strerror(errno) );
msg_Err( NULL, "cannot bind (%s)", strerror(errno) );
umask( i_mask );
close( i_fd );
exit(255);
}
umask( i_mask );
memset( &sun_server, 0, sizeof(sun_server) ); memset( &sun_server, 0, sizeof(sun_server) );
sun_server.sun_family = AF_UNIX; sun_server.sun_family = AF_UNIX;
...@@ -153,43 +353,49 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -153,43 +353,49 @@ int main( int i_argc, char **ppsz_argv )
sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0'; sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0';
p_buffer[0] = COMM_HEADER_MAGIC; p_buffer[0] = COMM_HEADER_MAGIC;
p_buffer[2] = 0; p_buffer[1] = opt.cmd;
p_buffer[3] = 0; memset( p_buffer + 2, 0, COMM_HEADER_SIZE - 2 );
i_size = COMM_HEADER_SIZE; i_size = COMM_HEADER_SIZE;
if ( !strcmp(ppsz_argv[optind], "reload") ) /* Handle commands that send parameters */
p_buffer[1] = CMD_RELOAD; switch ( opt.cmd )
else if ( !strcmp(ppsz_argv[optind], "shutdown") )
p_buffer[1] = CMD_SHUTDOWN;
else if ( !strcmp(ppsz_argv[optind], "fe_status") )
p_buffer[1] = CMD_FRONTEND_STATUS;
else if ( !strcmp(ppsz_argv[optind], "mmi_status") )
p_buffer[1] = CMD_MMI_STATUS;
else
{ {
p_buffer[4] = atoi(ppsz_argv[optind + 1]); case CMD_INVALID:
i_size = COMM_HEADER_SIZE + 1; case CMD_RELOAD:
case CMD_SHUTDOWN:
if ( !strcmp(ppsz_argv[optind], "mmi_slot_status") ) case CMD_FRONTEND_STATUS:
p_buffer[1] = CMD_MMI_SLOT_STATUS; case CMD_MMI_STATUS:
else if ( !strcmp(ppsz_argv[optind], "mmi_open") ) case CMD_GET_PAT:
p_buffer[1] = CMD_MMI_OPEN; case CMD_GET_CAT:
else if ( !strcmp(ppsz_argv[optind], "mmi_close") ) case CMD_GET_NIT:
p_buffer[1] = CMD_MMI_CLOSE; case CMD_GET_SDT:
else if ( !strcmp(ppsz_argv[optind], "mmi_get") ) case CMD_GET_PIDS:
p_buffer[1] = CMD_MMI_RECV; /* These commands need no special handling because they have no parameters */
else break;
case CMD_GET_PMT:
{ {
struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)&p_buffer[4]; uint16_t i_sid = atoi(p_arg1);
p_buffer[1] = CMD_MMI_SEND; i_size = COMM_HEADER_SIZE + 2;
p_cmd->i_slot = atoi(ppsz_argv[optind + 1]); p_data[0] = (uint8_t)((i_sid >> 8) & 0xff);
p_data[1] = (uint8_t)(i_sid & 0xff);
if ( !strcmp(ppsz_argv[optind], "mmi_send_text") ) break;
}
case CMD_GET_PID:
{ {
i_pid = (uint16_t)atoi(p_arg1);
i_size = COMM_HEADER_SIZE + 2;
p_data[0] = (uint8_t)((i_pid >> 8) & 0xff);
p_data[1] = (uint8_t)(i_pid & 0xff);
break;
}
case CMD_MMI_SEND_TEXT:
{
struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data;
p_cmd->i_slot = atoi(p_arg1);
en50221_mmi_object_t object; en50221_mmi_object_t object;
object.i_object_type = EN50221_MMI_ANSW; object.i_object_type = EN50221_MMI_ANSW;
if ( ppsz_argv[optind + 2] == NULL if ( !p_arg2 || p_arg2[0] == '\0' )
|| ppsz_argv[optind + 2][0] == '\0' )
{ {
object.u.answ.b_ok = 0; object.u.answ.b_ok = 0;
object.u.answ.psz_answ = ""; object.u.answ.psz_answ = "";
...@@ -197,60 +403,76 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -197,60 +403,76 @@ int main( int i_argc, char **ppsz_argv )
else else
{ {
object.u.answ.b_ok = 1; object.u.answ.b_ok = 1;
object.u.answ.psz_answ = ppsz_argv[optind + 2]; object.u.answ.psz_answ = p_arg2;
} }
i_size = COMM_BUFFER_SIZE - COMM_HEADER_SIZE i_size = COMM_BUFFER_SIZE - COMM_HEADER_SIZE
- ((void *)&p_cmd->object - (void *)p_cmd); - ((void *)&p_cmd->object - (void *)p_cmd);
if ( en50221_SerializeMMIObject( (uint8_t *)&p_cmd->object, if ( en50221_SerializeMMIObject( (uint8_t *)&p_cmd->object,
&i_size, &object ) == -1 ) &i_size, &object ) == -1 )
{ return_error( "Comm buffer is too small" );
msg_Err( NULL, "buffer too small" );
close( i_fd );
unlink( psz_client_socket );
exit(255);
}
i_size += COMM_HEADER_SIZE i_size += COMM_HEADER_SIZE
+ ((void *)&p_cmd->object - (void *)p_cmd); + ((void *)&p_cmd->object - (void *)p_cmd);
break;
} }
else /* mmi_send_choice */ case CMD_MMI_SEND_CHOICE:
{ {
struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data;
p_cmd->i_slot = atoi(p_arg1);
i_size = COMM_HEADER_SIZE + sizeof(struct cmd_mmi_send); i_size = COMM_HEADER_SIZE + sizeof(struct cmd_mmi_send);
p_cmd->object.i_object_type = EN50221_MMI_MENU_ANSW; p_cmd->object.i_object_type = EN50221_MMI_MENU_ANSW;
p_cmd->object.u.menu_answ.i_choice p_cmd->object.u.menu_answ.i_choice = atoi(p_arg2);
= atoi(ppsz_argv[optind + 2]); break;
} }
case CMD_MMI_SLOT_STATUS:
case CMD_MMI_OPEN:
case CMD_MMI_CLOSE:
case CMD_MMI_RECV:
{
p_data[0] = atoi(p_arg1);
i_size = COMM_HEADER_SIZE + 1;
break;
} }
default:
/* This should not happen */
return_error( "Unhandled option (%d)", opt.cmd );
} }
/* Send command and receive answer */
if ( sendto( i_fd, p_buffer, i_size, 0, (struct sockaddr *)&sun_server, if ( sendto( i_fd, p_buffer, i_size, 0, (struct sockaddr *)&sun_server,
SUN_LEN(&sun_server) ) < 0 ) SUN_LEN(&sun_server) ) < 0 )
{ return_error( "Cannot send comm socket (%s)", strerror(errno) );
msg_Err( NULL, "cannot send comm socket (%s)", strerror(errno) );
close( i_fd ); uint32_t i_packet_size = 0, i_received = 0;
unlink( psz_client_socket ); do {
exit(255); i_size = recv( i_fd, p_buffer + i_received, COMM_MAX_MSG_CHUNK, 0 );
if ( i_size == -1 )
break;
if ( !i_packet_size ) {
i_packet_size = *((uint32_t *)&p_buffer[4]);
if ( i_packet_size > COMM_BUFFER_SIZE ) {
i_size = -1;
break;
} }
}
i_received += i_size;
} while ( i_received < i_packet_size );
i_size = recv( i_fd, p_buffer, COMM_BUFFER_SIZE, 0 ); clean_client_socket();
close( i_fd );
unlink( psz_client_socket );
if ( i_size < COMM_HEADER_SIZE ) if ( i_size < COMM_HEADER_SIZE )
{ return_error( "Cannot recv from comm socket, size:%zd (%s)", i_size, strerror(errno) );
msg_Err( NULL, "cannot recv comm socket (%d:%s)", i_size,
strerror(errno) );
exit(255);
}
/* Process answer */
if ( p_buffer[0] != COMM_HEADER_MAGIC ) if ( p_buffer[0] != COMM_HEADER_MAGIC )
{ return_error( "Wrong protocol version 0x%x", p_buffer[0] );
msg_Err( NULL, "wrong protocol version 0x%x", p_buffer[0] );
exit(255); now = mdate();
}
switch ( p_buffer[1] ) ctl_cmd_answer_t c_answer = p_buffer[1];
switch ( c_answer )
{ {
case RET_OK: case RET_OK:
exit(0);
break; break;
case RET_MMI_WAIT: case RET_MMI_WAIT:
...@@ -258,35 +480,95 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -258,35 +480,95 @@ int main( int i_argc, char **ppsz_argv )
break; break;
case RET_ERR: case RET_ERR:
msg_Err( NULL, "request failed" ); return_error( "Request failed" );
exit(255);
break; break;
case RET_HUH: case RET_HUH:
msg_Err( NULL, "internal error" ); return_error( "Internal error" );
exit(255); break;
case RET_NODATA:
return_error( "No data" );
break; break;
case RET_PAT:
case RET_CAT:
case RET_NIT:
case RET_SDT:
{
uint8_t *p_flat_data = p_buffer + COMM_HEADER_SIZE;
unsigned int i_flat_data_size = i_size - COMM_HEADER_SIZE;
uint8_t **pp_sections = psi_unpack_sections( p_flat_data, i_flat_data_size );
switch( c_answer )
{
case RET_PAT: pat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
case RET_CAT: cat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
case RET_NIT: nit_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
case RET_SDT: sdt_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
default: break; /* Can't happen */
}
psi_table_free( pp_sections );
free( pp_sections );
break;
}
case RET_PMT:
{
pmt_print( p_data, psi_print, NULL, psi_iconv, NULL, i_print_type );
break;
}
case RET_PID:
{
print_pids_header();
print_pid( i_pid, (ts_pid_info_t *)p_data );
print_pids_footer();
break;
}
case RET_PIDS:
{
print_pids( p_data );
break;
}
case RET_FRONTEND_STATUS: case RET_FRONTEND_STATUS:
{ {
int ret = 1;
struct ret_frontend_status *p_ret = struct ret_frontend_status *p_ret =
(struct ret_frontend_status *)&p_buffer[COMM_HEADER_SIZE]; (struct ret_frontend_status *)&p_buffer[COMM_HEADER_SIZE];
if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_frontend_status) ) if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_frontend_status) )
{ return_error( "Bad frontend status" );
msg_Err( NULL, "bad frontend status" );
exit(255); if ( i_print_type == PRINT_XML )
} printf("<FRONTEND>\n");
#define PRINT_TYPE( x ) \
do { \
if ( i_print_type == PRINT_XML ) \
printf( " <TYPE type=\"%s\"/>\n", STRINGIFY(x) ); \
else \
printf( "type: %s\n", STRINGIFY(x) ); \
} while(0)
switch ( p_ret->info.type ) switch ( p_ret->info.type )
{ {
case FE_QPSK: printf("type: QPSK\n"); break; case FE_QPSK: PRINT_TYPE(QPSK); break;
case FE_QAM: printf("type: QAM\n"); break; case FE_QAM : PRINT_TYPE(QAM); break;
case FE_OFDM: printf("type: OFDM\n"); break; case FE_OFDM: PRINT_TYPE(OFDM); break;
default: printf("type: UNKNOWN\n"); break; case FE_ATSC: PRINT_TYPE(ATSC); break;
default : PRINT_TYPE(UNKNOWN); break;
} }
#undef PRINT_TYPE
#define PRINT_INFO( x ) \ #define PRINT_INFO( x ) \
printf( STRINGIFY(x) ": %u\n", p_ret->info.x ); do { \
if ( i_print_type == PRINT_XML ) \
printf( " <SETTING %s=\"%u\"/>\n", STRINGIFY(x), p_ret->info.x ); \
else \
printf( "%s: %u\n", STRINGIFY(x), p_ret->info.x ); \
} while(0)
PRINT_INFO( frequency_min ); PRINT_INFO( frequency_min );
PRINT_INFO( frequency_max ); PRINT_INFO( frequency_max );
PRINT_INFO( frequency_stepsize ); PRINT_INFO( frequency_stepsize );
...@@ -297,11 +579,19 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -297,11 +579,19 @@ int main( int i_argc, char **ppsz_argv )
PRINT_INFO( notifier_delay ); PRINT_INFO( notifier_delay );
#undef PRINT_INFO #undef PRINT_INFO
if ( i_print_type == PRINT_TEXT )
printf("\ncapability list:\n"); printf("\ncapability list:\n");
#define PRINT_CAPS( x ) \ #define PRINT_CAPS( x ) \
if ( p_ret->info.caps & (FE_##x) ) \ do { \
printf( STRINGIFY(x) "\n" ); if ( p_ret->info.caps & (FE_##x) ) { \
if ( i_print_type == PRINT_XML ) { \
printf( " <CAPABILITY %s=\"1\"/>\n", STRINGIFY(x) ); \
} else { \
printf( "%s\n", STRINGIFY(x) ); \
} \
} \
} while(0)
PRINT_CAPS( IS_STUPID ); PRINT_CAPS( IS_STUPID );
PRINT_CAPS( CAN_INVERSION_AUTO ); PRINT_CAPS( CAN_INVERSION_AUTO );
PRINT_CAPS( CAN_FEC_1_2 ); PRINT_CAPS( CAN_FEC_1_2 );
...@@ -342,11 +632,19 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -342,11 +632,19 @@ int main( int i_argc, char **ppsz_argv )
#endif #endif
#undef PRINT_CAPS #undef PRINT_CAPS
if ( i_print_type == PRINT_TEXT )
printf("\nstatus:\n"); printf("\nstatus:\n");
#define PRINT_STATUS( x ) \ #define PRINT_STATUS( x ) \
if ( p_ret->i_status & (FE_##x) ) \ do { \
printf( STRINGIFY(x) "\n" ); if ( p_ret->i_status & (FE_##x) ) { \
if ( i_print_type == PRINT_XML ) { \
printf( " <STATUS status=\"%s\"/>\n", STRINGIFY(x) ); \
} else { \
printf( "%s\n", STRINGIFY(x) ); \
} \
} \
} while(0)
PRINT_STATUS( HAS_SIGNAL ); PRINT_STATUS( HAS_SIGNAL );
PRINT_STATUS( HAS_CARRIER ); PRINT_STATUS( HAS_CARRIER );
PRINT_STATUS( HAS_VITERBI ); PRINT_STATUS( HAS_VITERBI );
...@@ -357,13 +655,23 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -357,13 +655,23 @@ int main( int i_argc, char **ppsz_argv )
if ( p_ret->i_status & FE_HAS_LOCK ) if ( p_ret->i_status & FE_HAS_LOCK )
{ {
if ( i_print_type == PRINT_XML )
{
printf(" <VALUE bit_error_rate=\"%d\"/>\n", p_ret->i_ber);
printf(" <VALUE signal_strength=\"%d\"/>\n", p_ret->i_strength);
printf(" <VALUE SNR=\"%d\"/>\n", p_ret->i_snr);
} else {
printf("\nBit error rate: %d\n", p_ret->i_ber); printf("\nBit error rate: %d\n", p_ret->i_ber);
printf("Signal strength: %d\n", p_ret->i_strength); printf("Signal strength: %d\n", p_ret->i_strength);
printf("SNR: %d\n", p_ret->i_snr); printf("SNR: %d\n", p_ret->i_snr);
exit(0); }
ret = 0;
} }
exit(1); if ( i_print_type == PRINT_XML )
printf("</FRONTEND>\n" );
exit(ret);
break; break;
} }
...@@ -372,10 +680,7 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -372,10 +680,7 @@ int main( int i_argc, char **ppsz_argv )
struct ret_mmi_status *p_ret = struct ret_mmi_status *p_ret =
(struct ret_mmi_status *)&p_buffer[COMM_HEADER_SIZE]; (struct ret_mmi_status *)&p_buffer[COMM_HEADER_SIZE];
if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_mmi_status) ) if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_mmi_status) )
{ return_error( "Bad MMI status" );
msg_Err( NULL, "bad MMI status" );
exit(255);
}
printf("CA interface with %d %s, type:\n", p_ret->caps.slot_num, printf("CA interface with %d %s, type:\n", p_ret->caps.slot_num,
p_ret->caps.slot_num == 1 ? "slot" : "slots"); p_ret->caps.slot_num == 1 ? "slot" : "slots");
...@@ -409,10 +714,7 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -409,10 +714,7 @@ int main( int i_argc, char **ppsz_argv )
struct ret_mmi_slot_status *p_ret = struct ret_mmi_slot_status *p_ret =
(struct ret_mmi_slot_status *)&p_buffer[COMM_HEADER_SIZE]; (struct ret_mmi_slot_status *)&p_buffer[COMM_HEADER_SIZE];
if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_slot_status) ) if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_slot_status) )
{ return_error( "Bad MMI slot status" );
msg_Err( NULL, "bad MMI slot status" );
exit(255);
}
printf("CA slot #%u: ", p_ret->sinfo.num); printf("CA slot #%u: ", p_ret->sinfo.num);
...@@ -445,10 +747,7 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -445,10 +747,7 @@ int main( int i_argc, char **ppsz_argv )
struct ret_mmi_recv *p_ret = struct ret_mmi_recv *p_ret =
(struct ret_mmi_recv *)&p_buffer[COMM_HEADER_SIZE]; (struct ret_mmi_recv *)&p_buffer[COMM_HEADER_SIZE];
if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_recv) ) if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_recv) )
{ return_error( "Bad MMI recv" );
msg_Err( NULL, "bad MMI recv" );
exit(255);
}
en50221_UnserializeMMIObject( &p_ret->object, i_size en50221_UnserializeMMIObject( &p_ret->object, i_size
- COMM_HEADER_SIZE - ((void *)&p_ret->object - (void *)p_ret) ); - COMM_HEADER_SIZE - ((void *)&p_ret->object - (void *)p_ret) );
...@@ -483,8 +782,7 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -483,8 +782,7 @@ int main( int i_argc, char **ppsz_argv )
break; break;
default: default:
printf("unknown MMI object\n"); return_error( "Unknown MMI object" );
exit(255);
break; break;
} }
...@@ -493,7 +791,8 @@ int main( int i_argc, char **ppsz_argv ) ...@@ -493,7 +791,8 @@ int main( int i_argc, char **ppsz_argv )
} }
default: default:
msg_Err( NULL, "wrong answer %u", p_buffer[1] ); return_error( "Unknown command answer: %u", c_answer );
exit(255);
} }
return 0;
} }
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
* en50221.c : implementation of the transport, session and applications * en50221.c : implementation of the transport, session and applications
* layers of EN 50 221 * layers of EN 50 221
***************************************************************************** *****************************************************************************
* Copyright (C) 2004-2005 VideoLAN * Copyright (C) 2004-2005, 2010 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
...@@ -20,15 +19,16 @@ ...@@ -20,15 +19,16 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <inttypes.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -46,6 +46,10 @@ ...@@ -46,6 +46,10 @@
#include <linux/dvb/frontend.h> #include <linux/dvb/frontend.h>
#include <linux/dvb/ca.h> #include <linux/dvb/ca.h>
#include <bitstream/mpeg/psi.h>
#include <bitstream/dvb/ci.h>
#include <bitstream/dvb/si.h>
#include "dvblast.h" #include "dvblast.h"
#include "en50221.h" #include "en50221.h"
#include "comm.h" #include "comm.h"
...@@ -66,10 +70,17 @@ ...@@ -66,10 +70,17 @@
* Local declarations * Local declarations
*****************************************************************************/ *****************************************************************************/
#undef DEBUG_TPDU #undef DEBUG_TPDU
#define HLCI_WAIT_CAM_READY 0 #define CAM_INIT_TIMEOUT 15000000 /* 15 s */
#define CAM_PROG_MAX MAX_PROGRAMS #undef HLCI_WAIT_CAM_READY
#define CAPMT_WAIT 100 /* ms */ #define CAPMT_WAIT 100 /* ms */
typedef struct en50221_msg_t
{
uint8_t *p_data;
int i_size;
struct en50221_msg_t *p_next;
} en50221_msg_t;
typedef struct en50221_session_t typedef struct en50221_session_t
{ {
int i_slot; int i_slot;
...@@ -80,18 +91,44 @@ typedef struct en50221_session_t ...@@ -80,18 +91,44 @@ typedef struct en50221_session_t
void *p_sys; void *p_sys;
} en50221_session_t; } en50221_session_t;
typedef struct ci_slot_t
{
bool b_active;
bool b_expect_answer;
bool b_has_data;
bool b_mmi_expected;
bool b_mmi_undisplayed;
/* TPDU reception */
en50221_msg_t *p_recv;
/* TPDU emission */
en50221_msg_t *p_send;
en50221_msg_t **pp_send_last;
uint8_t *p;
/* InitSlot callback, if not 0 */
mtime_t i_init_timeout;
/* SPDUSend callback, if p_spdu is not NULL */
/* SessionOpen callback, if not 0 */
int i_pending_session_id;
} ci_slot_t;
int i_ca_handle = 0; int i_ca_handle = 0;
int i_ca_type = -1; int i_ca_type = -1;
static int i_nb_slots = 0; static int i_nb_slots = 0;
static bool pb_active_slot[MAX_CI_SLOTS]; static ci_slot_t p_slots[MAX_CI_SLOTS];
static bool pb_tc_has_data[MAX_CI_SLOTS];
static bool pb_slot_mmi_expected[MAX_CI_SLOTS];
static bool pb_slot_mmi_undisplayed[MAX_CI_SLOTS];
static en50221_session_t p_sessions[MAX_SESSIONS]; static en50221_session_t p_sessions[MAX_SESSIONS];
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
static void SessionOpenCb( access_t *p_access, uint8_t i_slot );
static void SPDUHandle( access_t * p_access, uint8_t i_slot,
uint8_t *p_spdu, int i_size );
static void ResourceManagerOpen( access_t * p_access, int i_session_id ); static void ResourceManagerOpen( access_t * p_access, int i_session_id );
static void ApplicationInformationOpen( access_t * p_access, int i_session_id ); static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
static void ConditionalAccessOpen( access_t * p_access, int i_session_id ); static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
...@@ -192,14 +229,55 @@ static void Dump( bool b_outgoing, uint8_t *p_data, int i_size ) ...@@ -192,14 +229,55 @@ static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
#endif #endif
} }
/*****************************************************************************
* TPDUWrite
*****************************************************************************/
static int TPDUWrite( access_t * p_access, uint8_t i_slot )
{
ci_slot_t *p_slot = &p_slots[i_slot];
en50221_msg_t *p_send = p_slot->p_send;
if ( p_slot->b_expect_answer )
msg_Warn( p_access,
"en50221: writing while expecting an answer on slot %u",
i_slot );
if ( p_send == NULL )
{
msg_Warn( p_access, "en50221: no data to write on slot %u !", i_slot );
return -1;
}
p_slot->p_send = p_send->p_next;
if ( p_slot->p_send == NULL )
p_slot->pp_send_last = &p_slot->p_send;
Dump( true, p_send->p_data, p_send->i_size );
if ( write( i_ca_handle, p_send->p_data, p_send->i_size )
!= p_send->i_size )
{
msg_Err( p_access, "en50221: cannot write to CAM device (%m)" );
free( p_send->p_data );
free( p_send );
return -1;
}
free( p_send->p_data );
free( p_send );
p_slot->b_expect_answer = true;
return 0;
}
/***************************************************************************** /*****************************************************************************
* TPDUSend * TPDUSend
*****************************************************************************/ *****************************************************************************/
static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag, static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
const uint8_t *p_content, int i_length ) const uint8_t *p_content, int i_length )
{ {
ci_slot_t *p_slot = &p_slots[i_slot];
uint8_t i_tcid = i_slot + 1; uint8_t i_tcid = i_slot + 1;
uint8_t p_data[MAX_TPDU_SIZE]; en50221_msg_t *p_send = malloc( sizeof(en50221_msg_t) );
uint8_t *p_data = malloc( MAX_TPDU_SIZE );
int i_size; int i_size;
i_size = 0; i_size = 0;
...@@ -245,13 +323,16 @@ static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag, ...@@ -245,13 +323,16 @@ static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
default: default:
break; break;
} }
Dump( true, p_data, i_size );
if ( write( i_ca_handle, p_data, i_size ) != i_size ) p_send->p_data = p_data;
{ p_send->i_size = i_size;
msg_Err( p_access, "cannot write to CAM device (%m)" ); p_send->p_next = NULL;
return -1;
} *p_slot->pp_send_last = p_send;
p_slot->pp_send_last = &p_send->p_next;
if ( !p_slot->b_expect_answer )
return TPDUWrite( p_access, i_slot );
return 0; return 0;
} }
...@@ -260,66 +341,105 @@ static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag, ...@@ -260,66 +341,105 @@ static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
/***************************************************************************** /*****************************************************************************
* TPDURecv * TPDURecv
*****************************************************************************/ *****************************************************************************/
#define CAM_READ_TIMEOUT 3500 // ms static int TPDURecv( access_t * p_access )
static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
uint8_t *p_data, int *pi_size )
{ {
uint8_t i_tcid = i_slot + 1; ci_slot_t *p_slot;
uint8_t i_tag, i_slot;
uint8_t p_data[MAX_TPDU_SIZE];
int i_size; int i_size;
struct pollfd pfd[1]; bool b_last = false;
do
{
i_size = read( i_ca_handle, p_data, MAX_TPDU_SIZE );
}
while ( i_size < 0 && errno == EINTR );
pfd[0].fd = i_ca_handle; if ( i_size < 5 )
pfd[0].events = POLLIN;
if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
{ {
msg_Err( p_access, "cannot poll from CAM device" ); msg_Err( p_access, "en50221: cannot read from CAM device (%d:%m)",
i_size );
return -1; return -1;
} }
if ( pi_size == NULL ) Dump( false, p_data, i_size );
i_slot = p_data[1] - 1;
i_tag = p_data[2];
if ( i_slot >= i_nb_slots )
{ {
p_data = malloc( MAX_TPDU_SIZE ); msg_Warn( p_access, "en50221: TPDU is from an unknown slot %u",
i_slot );
return -1;
} }
p_slot = &p_slots[i_slot];
for ( ; ; ) p_slot->b_has_data = !!(p_data[i_size - 4] == T_SB
&& p_data[i_size - 3] == 2
&& (p_data[i_size - 1] & DATA_INDICATOR));
p_slot->b_expect_answer = false;
switch ( i_tag )
{ {
i_size = read( i_ca_handle, p_data, MAX_TPDU_SIZE ); case T_CTC_REPLY:
p_slot->b_active = true;
p_slot->i_init_timeout = 0;
msg_Dbg( p_access, "CI slot %d is active", i_slot );
break;
if ( i_size >= 0 || errno != EINTR ) case T_SB:
break; break;
}
if ( i_size < 5 ) case T_DATA_LAST:
b_last = true;
/* intended pass-through */
case T_DATA_MORE:
{ {
msg_Err( p_access, "cannot read from CAM device (%d:%m)", i_size ); en50221_msg_t *p_recv;
if( pi_size == NULL ) int i_session_size;
free( p_data ); uint8_t *p_session = GetLength( &p_data[3], &i_session_size );
return -1;
}
if ( p_data[1] != i_tcid ) if ( i_session_size <= 1 )
break;
p_session++;
i_session_size--;
if ( p_slot->p_recv == NULL )
{ {
msg_Err( p_access, "invalid read from CAM device (%d instead of %d)", p_slot->p_recv = malloc( sizeof(en50221_msg_t) );
p_data[1], i_tcid ); p_slot->p_recv->p_data = NULL;
if( pi_size == NULL ) p_slot->p_recv->i_size = 0;
free( p_data );
return -1;
} }
*pi_tag = p_data[2]; p_recv = p_slot->p_recv;
pb_tc_has_data[i_slot] = (i_size >= 4 p_recv->p_data = realloc( p_recv->p_data,
&& p_data[i_size - 4] == T_SB p_recv->i_size + i_session_size );
&& p_data[i_size - 3] == 2 memcpy( &p_recv->p_data[ p_recv->i_size ], p_session, i_session_size );
&& (p_data[i_size - 1] & DATA_INDICATOR)) p_recv->i_size += i_session_size;
? true : false;
Dump( false, p_data, i_size ); if ( b_last )
{
SPDUHandle( p_access, i_slot, p_recv->p_data, p_recv->i_size );
free( p_recv->p_data );
free( p_recv );
p_slot->p_recv = NULL;
}
break;
}
if ( pi_size == NULL ) default:
free( p_data ); msg_Warn( p_access, "en50221: unhandled R_TPDU tag %u slot %u", i_tag,
else i_slot );
*pi_size = i_size; break;
}
if ( !p_slot->b_expect_answer && p_slot->p_send != NULL )
TPDUWrite( p_access, i_slot );
if ( !p_slot->b_expect_answer && p_slot->i_pending_session_id != 0 )
SessionOpenCb( p_access, i_slot );
if ( !p_slot->b_expect_answer && p_slot->b_has_data )
TPDUSend( p_access, i_slot, T_RCV, NULL, 0 );
return 0; return 0;
} }
...@@ -356,12 +476,11 @@ static int ResourceIdToInt( uint8_t *p_data ) ...@@ -356,12 +476,11 @@ static int ResourceIdToInt( uint8_t *p_data )
/***************************************************************************** /*****************************************************************************
* SPDUSend * SPDUSend
*****************************************************************************/ *****************************************************************************/
static int SPDUSend( access_t * p_access, int i_session_id, static int SPDUSend( access_t *p_access, int i_session_id,
uint8_t *p_data, int i_size ) uint8_t *p_data, int i_size )
{ {
uint8_t *p_spdu = malloc( i_size + 4 ); uint8_t *p_spdu = malloc( i_size + 4 );
uint8_t *p = p_spdu; uint8_t *p = p_spdu;
uint8_t i_tag;
uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; uint8_t i_slot = p_sessions[i_session_id - 1].i_slot;
*p++ = ST_SESSION_NUMBER; *p++ = ST_SESSION_NUMBER;
...@@ -401,15 +520,6 @@ static int SPDUSend( access_t * p_access, int i_session_id, ...@@ -401,15 +520,6 @@ static int SPDUSend( access_t * p_access, int i_session_id,
} }
i_size = 0; i_size = 0;
} }
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != 0
|| i_tag != T_SB )
{
msg_Err( p_access, "couldn't recv TPDU on session %d",
i_session_id );
free( p_spdu );
return -1;
}
} }
free( p_spdu ); free( p_spdu );
...@@ -419,14 +529,42 @@ static int SPDUSend( access_t * p_access, int i_session_id, ...@@ -419,14 +529,42 @@ static int SPDUSend( access_t * p_access, int i_session_id,
/***************************************************************************** /*****************************************************************************
* SessionOpen * SessionOpen
*****************************************************************************/ *****************************************************************************/
static void SessionOpenCb( access_t *p_access, uint8_t i_slot )
{
ci_slot_t *p_slot = &p_slots[i_slot];
int i_session_id = p_slot->i_pending_session_id;
int i_resource_id = p_sessions[i_session_id - 1].i_resource_id;
p_slot->i_pending_session_id = 0;
switch ( i_resource_id )
{
case RI_RESOURCE_MANAGER:
ResourceManagerOpen( p_access, i_session_id ); break;
case RI_APPLICATION_INFORMATION:
ApplicationInformationOpen( p_access, i_session_id ); break;
case RI_CONDITIONAL_ACCESS_SUPPORT:
ConditionalAccessOpen( p_access, i_session_id ); break;
case RI_DATE_TIME:
DateTimeOpen( p_access, i_session_id ); break;
case RI_MMI:
MMIOpen( p_access, i_session_id ); break;
case RI_HOST_CONTROL:
default:
msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
p_sessions[i_session_id - 1].i_resource_id = 0;
}
}
static void SessionOpen( access_t * p_access, uint8_t i_slot, static void SessionOpen( access_t * p_access, uint8_t i_slot,
uint8_t *p_spdu, int i_size ) uint8_t *p_spdu, int i_size )
{ {
ci_slot_t *p_slot = &p_slots[i_slot];
int i_session_id; int i_session_id;
int i_resource_id = ResourceIdToInt( &p_spdu[2] ); int i_resource_id = ResourceIdToInt( &p_spdu[2] );
uint8_t p_response[16]; uint8_t p_response[16];
int i_status = SS_NOT_ALLOCATED; int i_status = SS_NOT_ALLOCATED;
uint8_t i_tag;
for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ ) for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
{ {
...@@ -462,38 +600,17 @@ static void SessionOpen( access_t * p_access, uint8_t i_slot, ...@@ -462,38 +600,17 @@ static void SessionOpen( access_t * p_access, uint8_t i_slot,
p_response[7] = i_session_id >> 8; p_response[7] = i_session_id >> 8;
p_response[8] = i_session_id & 0xff; p_response[8] = i_session_id & 0xff;
if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) != if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) != 0 )
0 )
{ {
msg_Err( p_access, msg_Err( p_access,
"SessionOpen: couldn't send TPDU on slot %d", i_slot ); "SessionOpen: couldn't send TPDU on slot %d", i_slot );
return; return;
} }
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != 0 )
{
msg_Err( p_access,
"SessionOpen: couldn't recv TPDU on slot %d", i_slot );
return;
}
switch ( i_resource_id )
{
case RI_RESOURCE_MANAGER:
ResourceManagerOpen( p_access, i_session_id ); break;
case RI_APPLICATION_INFORMATION:
ApplicationInformationOpen( p_access, i_session_id ); break;
case RI_CONDITIONAL_ACCESS_SUPPORT:
ConditionalAccessOpen( p_access, i_session_id ); break;
case RI_DATE_TIME:
DateTimeOpen( p_access, i_session_id ); break;
case RI_MMI:
MMIOpen( p_access, i_session_id ); break;
case RI_HOST_CONTROL: if ( p_slot->i_pending_session_id != 0 )
default: msg_Warn( p_access, "overwriting pending session %d",
msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id ); p_slot->i_pending_session_id );
p_sessions[i_session_id - 1].i_resource_id = 0; p_slot->i_pending_session_id = i_session_id;
}
} }
#if 0 #if 0
...@@ -539,12 +656,6 @@ static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id ) ...@@ -539,12 +656,6 @@ static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
"SessionCreate: couldn't send TPDU on slot %d", i_slot ); "SessionCreate: couldn't send TPDU on slot %d", i_slot );
return; return;
} }
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != 0 )
{
msg_Err( p_access,
"SessionCreate: couldn't recv TPDU on slot %d", i_slot );
return;
}
} }
#endif #endif
...@@ -593,7 +704,6 @@ static void SessionCreateResponse( access_t * p_access, uint8_t i_slot, ...@@ -593,7 +704,6 @@ static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
static void SessionSendClose( access_t * p_access, int i_session_id ) static void SessionSendClose( access_t * p_access, int i_session_id )
{ {
uint8_t p_response[16]; uint8_t p_response[16];
uint8_t i_tag;
uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; uint8_t i_slot = p_sessions[i_session_id - 1].i_slot;
p_response[0] = ST_CLOSE_SESSION_REQUEST; p_response[0] = ST_CLOSE_SESSION_REQUEST;
...@@ -608,12 +718,6 @@ static void SessionSendClose( access_t * p_access, int i_session_id ) ...@@ -608,12 +718,6 @@ static void SessionSendClose( access_t * p_access, int i_session_id )
"SessionSendClose: couldn't send TPDU on slot %d", i_slot ); "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
return; return;
} }
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != 0 )
{
msg_Err( p_access,
"SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
return;
}
} }
/***************************************************************************** /*****************************************************************************
...@@ -622,7 +726,6 @@ static void SessionSendClose( access_t * p_access, int i_session_id ) ...@@ -622,7 +726,6 @@ static void SessionSendClose( access_t * p_access, int i_session_id )
static void SessionClose( access_t * p_access, int i_session_id ) static void SessionClose( access_t * p_access, int i_session_id )
{ {
uint8_t p_response[16]; uint8_t p_response[16];
uint8_t i_tag;
uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; uint8_t i_slot = p_sessions[i_session_id - 1].i_slot;
if ( p_sessions[i_session_id - 1].pf_close != NULL ) if ( p_sessions[i_session_id - 1].pf_close != NULL )
...@@ -642,12 +745,6 @@ static void SessionClose( access_t * p_access, int i_session_id ) ...@@ -642,12 +745,6 @@ static void SessionClose( access_t * p_access, int i_session_id )
"SessionClose: couldn't send TPDU on slot %d", i_slot ); "SessionClose: couldn't send TPDU on slot %d", i_slot );
return; return;
} }
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != 0 )
{
msg_Err( p_access,
"SessionClose: couldn't recv TPDU on slot %d", i_slot );
return;
}
} }
/***************************************************************************** /*****************************************************************************
...@@ -663,7 +760,9 @@ static void SPDUHandle( access_t * p_access, uint8_t i_slot, ...@@ -663,7 +760,9 @@ static void SPDUHandle( access_t * p_access, uint8_t i_slot,
case ST_SESSION_NUMBER: case ST_SESSION_NUMBER:
if ( i_size <= 4 ) if ( i_size <= 4 )
return; return;
i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3]; i_session_id = (p_spdu[2] << 8) | p_spdu[3];
if ( i_session_id <= MAX_SESSIONS
&& p_sessions[i_session_id - 1].pf_handle != NULL )
p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id, p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
p_spdu + 4, i_size - 4 ); p_spdu + 4, i_size - 4 );
break; break;
...@@ -727,6 +826,7 @@ static void SPDUHandle( access_t * p_access, uint8_t i_slot, ...@@ -727,6 +826,7 @@ static void SPDUHandle( access_t * p_access, uint8_t i_slot,
#define AOT_CA_INFO 0x9F8031 #define AOT_CA_INFO 0x9F8031
#define AOT_CA_PMT 0x9F8032 #define AOT_CA_PMT 0x9F8032
#define AOT_CA_PMT_REPLY 0x9F8033 #define AOT_CA_PMT_REPLY 0x9F8033
#define AOT_CA_UPDATE 0x9F8034
#define AOT_TUNE 0x9F8400 #define AOT_TUNE 0x9F8400
#define AOT_REPLACE 0x9F8401 #define AOT_REPLACE 0x9F8401
#define AOT_CLEAR_REPLACE 0x9F8402 #define AOT_CLEAR_REPLACE 0x9F8402
...@@ -897,7 +997,7 @@ static void ApplicationInformationEnterMenu( access_t * p_access, ...@@ -897,7 +997,7 @@ static void ApplicationInformationEnterMenu( access_t * p_access,
msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id ); msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 ); APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
pb_slot_mmi_expected[i_slot] = true; p_slots[i_slot].b_mmi_expected = true;
} }
/***************************************************************************** /*****************************************************************************
...@@ -917,7 +1017,6 @@ static void ApplicationInformationHandle( access_t * p_access, int i_session_id, ...@@ -917,7 +1017,6 @@ static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
uint8_t *d = APDUGetLength( p_apdu, &l ); uint8_t *d = APDUGetLength( p_apdu, &l );
if ( l < 4 ) break; if ( l < 4 ) break;
p_apdu[l + 4] = '\0';
i_type = *d++; i_type = *d++;
i_manufacturer = ((int)d[0] << 8) | d[1]; i_manufacturer = ((int)d[0] << 8) | d[1];
...@@ -925,9 +1024,26 @@ static void ApplicationInformationHandle( access_t * p_access, int i_session_id, ...@@ -925,9 +1024,26 @@ static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
i_code = ((int)d[0] << 8) | d[1]; i_code = ((int)d[0] << 8) | d[1];
d += 2; d += 2;
d = GetLength( d, &l ); d = GetLength( d, &l );
d[l] = '\0';
{
char *psz_name = malloc(l + 1);
memcpy( psz_name, d, l );
psz_name[l] = '\0';
msg_Info( p_access, "CAM: %s, %02X, %04X, %04X", msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
d, i_type, i_manufacturer, i_code ); psz_name, i_type, i_manufacturer, i_code );
switch (i_print_type)
{
case PRINT_XML:
psz_name = dvb_string_xml_escape(psz_name);
printf("<STATUS type=\"cam\" status=\"1\" cam_name=\"%s\" cam_type=\"%d\" cam_manufacturer=\"%d\" cam_product=\"%d\" />\n",
psz_name, i_type, i_manufacturer, i_code);
break;
default:
printf("CAM: name=%s type=%d manufacturer=%d product=%d\n",
psz_name, i_type, i_manufacturer, i_code);
}
free(psz_name);
}
break; break;
} }
default: default:
...@@ -954,11 +1070,10 @@ static void ApplicationInformationOpen( access_t * p_access, int i_session_id ) ...@@ -954,11 +1070,10 @@ static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
* Conditional Access * Conditional Access
*/ */
#define MAX_CASYSTEM_IDS 64
typedef struct typedef struct
{ {
uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1]; int i_nb_system_ids;
uint16_t *pi_system_ids;
int i_selected_programs; int i_selected_programs;
int b_high_level; int b_high_level;
...@@ -966,16 +1081,13 @@ typedef struct ...@@ -966,16 +1081,13 @@ typedef struct
static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id ) static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
{ {
int i = 0; int i;
if( !p_ids ) return false; if( p_ids == NULL ) return false;
if( p_ids->b_high_level ) return true; if( p_ids->b_high_level ) return true;
while ( p_ids->pi_system_ids[i] ) for ( i = 0; i < p_ids->i_nb_system_ids; i++ )
{
if ( p_ids->pi_system_ids[i] == i_id ) if ( p_ids->pi_system_ids[i] == i_id )
return true; return true;
i++;
}
return false; return false;
} }
...@@ -983,206 +1095,144 @@ static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id ) ...@@ -983,206 +1095,144 @@ static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
/***************************************************************************** /*****************************************************************************
* CAPMTBuild * CAPMTBuild
*****************************************************************************/ *****************************************************************************/
static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr ) static bool HasCADescriptors( system_ids_t *p_ids, uint8_t *p_descs )
{ {
int i_cad_size = 0; const uint8_t *p_desc;
uint16_t j = 0;
while ( p_dr != NULL ) while ( (p_desc = descs_get_desc( p_descs, j )) != NULL )
{ {
if( p_dr->i_tag == 0x9 ) uint8_t i_tag = desc_get_tag( p_desc );
{ j++;
uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
| p_dr->p_data[1]; if ( i_tag == 0x9 && desc09_validate( p_desc )
if ( CheckSystemID( p_ids, i_sysid ) ) && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) )
i_cad_size += p_dr->i_length + 2; return true;
}
p_dr = p_dr->p_next;
} }
return i_cad_size; return false;
} }
static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt, static void CopyCADescriptors( system_ids_t *p_ids, uint8_t i_cmd,
uint16_t i_program_number, uint8_t i_version, uint8_t *p_infos, uint8_t *p_descs )
int i_size, dvbpsi_descriptor_t *p_dr,
uint8_t i_cmd )
{ {
uint8_t *p_data; const uint8_t *p_desc;
uint16_t j = 0, k = 0;
if ( i_size )
p_data = malloc( 7 + i_size );
else
p_data = malloc( 6 );
p_data[0] = i_list_mgt; capmti_init( p_infos );
p_data[1] = i_program_number >> 8; capmti_set_length( p_infos, 0xfff );
p_data[2] = i_program_number & 0xff; capmti_set_cmd( p_infos, i_cmd );
p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
if ( i_size ) while ( (p_desc = descs_get_desc( p_descs, j )) != NULL )
{ {
int i; uint8_t i_tag = desc_get_tag( p_desc );
j++;
p_data[4] = (i_size + 1) >> 8; if ( i_tag == 0x9 && desc09_validate( p_desc )
p_data[5] = (i_size + 1) & 0xff; && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) )
p_data[6] = i_cmd;
i = 7;
while ( p_dr != NULL )
{
if( p_dr->i_tag == 0x9 )
{
uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
| p_dr->p_data[1];
if ( CheckSystemID( p_ids, i_sysid ) )
{ {
p_data[i] = 0x9; uint8_t *p_info = capmti_get_info( p_infos, k );
p_data[i + 1] = p_dr->i_length; k++;
memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length ); memcpy( p_info, p_desc,
// p_data[i+4] &= 0x1f; DESC_HEADER_SIZE + desc_get_length( p_desc ) );
i += p_dr->i_length + 2;
}
}
p_dr = p_dr->p_next;
} }
} }
else
{
p_data[4] = 0;
p_data[5] = 0;
}
return p_data;
}
static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt, if ( k )
int i_capmt_size, uint8_t i_type, uint16_t i_pid,
int i_size, dvbpsi_descriptor_t *p_dr,
uint8_t i_cmd )
{
uint8_t *p_data;
int i;
if ( i_size )
p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
else
p_data = realloc( p_capmt, i_capmt_size + 5 );
i = i_capmt_size;
p_data[i] = i_type;
p_data[i + 1] = i_pid >> 8;
p_data[i + 2] = i_pid & 0xff;
if ( i_size )
{ {
p_data[i + 3] = (i_size + 1) >> 8; uint8_t *p_info = capmti_get_info( p_infos, k );
p_data[i + 4] = (i_size + 1) & 0xff; capmti_set_length( p_infos, p_info - p_infos - DESCS_HEADER_SIZE );
p_data[i + 5] = i_cmd;
i += 6;
while ( p_dr != NULL )
{
if( p_dr->i_tag == 0x9 )
{
uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
| p_dr->p_data[1];
if ( CheckSystemID( p_ids, i_sysid ) )
{
p_data[i] = 0x9;
p_data[i + 1] = p_dr->i_length;
memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
i += p_dr->i_length + 2;
}
}
p_dr = p_dr->p_next;
}
} }
else else
{ capmti_set_length( p_infos, 0 );
p_data[i + 3] = 0;
p_data[i + 4] = 0;
}
return p_data;
} }
static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id, static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt, uint8_t *p_pmt, uint8_t i_list_mgt,
uint8_t i_cmd, int *pi_capmt_size ) uint8_t i_cmd, int *pi_capmt_size )
{ {
system_ids_t *p_ids = system_ids_t *p_ids =
(system_ids_t *)p_sessions[i_session_id - 1].p_sys; (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
dvbpsi_pmt_es_t *p_es; uint8_t *p_es;
int i_cad_size, i_cad_program_size; uint8_t *p_capmt, *p_capmt_n;
uint8_t *p_capmt; uint16_t j, k;
bool b_has_ca = HasCADescriptors( p_ids, pmt_get_descs( p_pmt ) );
bool b_has_es = false;
j = 0;
while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
{
uint16_t i_pid = pmtn_get_pid( p_es );
j++;
if ( demux_PIDIsSelected( i_pid ) )
{
b_has_es = true;
b_has_ca = b_has_ca
|| HasCADescriptors( p_ids, pmtn_get_descs( p_es ) );
}
}
i_cad_size = i_cad_program_size = if ( !b_has_es )
GetCADSize( p_ids, p_pmt->p_first_descriptor );
for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
{ {
i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor ); *pi_capmt_size = 0;
return NULL;
} }
if ( !i_cad_size ) if ( !b_has_ca )
{ {
msg_Warn( p_access, msg_Warn( p_access,
"no compatible scrambling system for SID %d on session %d", "no compatible scrambling system for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
*pi_capmt_size = 0; *pi_capmt_size = 0;
return NULL; return NULL;
} }
p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number, p_capmt = capmt_allocate();
p_pmt->i_version, i_cad_program_size, capmt_init( p_capmt );
p_pmt->p_first_descriptor, i_cmd ); capmt_set_listmanagement( p_capmt, i_list_mgt );
capmt_set_program( p_capmt, pmt_get_program( p_pmt ) );
capmt_set_version( p_capmt, psi_get_version( p_pmt ) );
if ( i_cad_program_size ) CopyCADescriptors( p_ids, i_cmd, capmt_get_infos( p_capmt ),
*pi_capmt_size = 7 + i_cad_program_size; pmt_get_descs( p_pmt ) );
else
*pi_capmt_size = 6;
for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next ) j = 0; k = 0;
while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
{ {
if ( !PIDIsSelected( p_es->i_pid ) ) uint16_t i_pid = pmtn_get_pid( p_es );
j++;
if ( !demux_PIDIsSelected( i_pid ) )
continue; continue;
i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor ); p_capmt_n = capmt_get_es( p_capmt, k );
k++;
if ( i_cad_size || i_cad_program_size ) capmtn_init( p_capmt_n );
{ capmtn_set_streamtype( p_capmt_n, pmtn_get_streamtype( p_es ) );
p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type, capmtn_set_pid( p_capmt_n, pmtn_get_pid( p_es ) );
p_es->i_pid, i_cad_size,
p_es->p_first_descriptor, i_cmd );
if ( i_cad_size )
*pi_capmt_size += 6 + i_cad_size;
else
*pi_capmt_size += 5;
}
}
if ( *pi_capmt_size <= 7 + i_cad_program_size ) CopyCADescriptors( p_ids, i_cmd, capmtn_get_infos( p_capmt_n ),
{ pmtn_get_descs( p_es ) );
msg_Dbg( p_access, "CAPMT not needed, no ES selected" );
free( p_capmt );
*pi_capmt_size = 0;
return NULL;
} }
p_capmt_n = capmt_get_es( p_capmt, k );
*pi_capmt_size = p_capmt_n - p_capmt;
return p_capmt; return p_capmt;
} }
/***************************************************************************** /*****************************************************************************
* CAPMTFirst * CAPMTFirst
*****************************************************************************/ *****************************************************************************/
static void CAPMTFirst( access_t * p_access, int i_session_id, static void CAPMTFirst( access_t * p_access, int i_session_id, uint8_t *p_pmt )
dvbpsi_pmt_t *p_pmt )
{ {
uint8_t *p_capmt; uint8_t *p_capmt;
int i_capmt_size; int i_capmt_size;
msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d", msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
0x3 /* only */, 0x1 /* ok_descrambling */, 0x3 /* only */, 0x1 /* ok_descrambling */,
...@@ -1198,20 +1248,13 @@ static void CAPMTFirst( access_t * p_access, int i_session_id, ...@@ -1198,20 +1248,13 @@ static void CAPMTFirst( access_t * p_access, int i_session_id,
/***************************************************************************** /*****************************************************************************
* CAPMTAdd * CAPMTAdd
*****************************************************************************/ *****************************************************************************/
static void CAPMTAdd( access_t * p_access, int i_session_id, static void CAPMTAdd( access_t * p_access, int i_session_id, uint8_t *p_pmt )
dvbpsi_pmt_t *p_pmt )
{ {
system_ids_t *p_ids = system_ids_t *p_ids =
(system_ids_t *)p_sessions[i_session_id - 1].p_sys; (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
uint8_t *p_capmt; uint8_t *p_capmt;
int i_capmt_size; int i_capmt_size;
if( p_ids->i_selected_programs >= CAM_PROG_MAX )
{
msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
p_pmt->i_program_number );
return;
}
p_ids->i_selected_programs++; p_ids->i_selected_programs++;
if( p_ids->i_selected_programs == 1 ) if( p_ids->i_selected_programs == 1 )
{ {
...@@ -1219,11 +1262,8 @@ static void CAPMTAdd( access_t * p_access, int i_session_id, ...@@ -1219,11 +1262,8 @@ static void CAPMTAdd( access_t * p_access, int i_session_id,
return; return;
} }
if( b_slow_cam )
msleep( CAPMT_WAIT * 1000 );
msg_Dbg( p_access, "adding CAPMT for SID %d on session %d", msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
0x4 /* add */, 0x1 /* ok_descrambling */, 0x4 /* add */, 0x1 /* ok_descrambling */,
...@@ -1239,14 +1279,13 @@ static void CAPMTAdd( access_t * p_access, int i_session_id, ...@@ -1239,14 +1279,13 @@ static void CAPMTAdd( access_t * p_access, int i_session_id,
/***************************************************************************** /*****************************************************************************
* CAPMTUpdate * CAPMTUpdate
*****************************************************************************/ *****************************************************************************/
static void CAPMTUpdate( access_t * p_access, int i_session_id, static void CAPMTUpdate( access_t * p_access, int i_session_id, uint8_t *p_pmt )
dvbpsi_pmt_t *p_pmt )
{ {
uint8_t *p_capmt; uint8_t *p_capmt;
int i_capmt_size; int i_capmt_size;
msg_Dbg( p_access, "updating CAPMT for SID %d on session %d", msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
0x5 /* update */, 0x1 /* ok_descrambling */, 0x5 /* update */, 0x1 /* ok_descrambling */,
...@@ -1262,8 +1301,7 @@ static void CAPMTUpdate( access_t * p_access, int i_session_id, ...@@ -1262,8 +1301,7 @@ static void CAPMTUpdate( access_t * p_access, int i_session_id,
/***************************************************************************** /*****************************************************************************
* CAPMTDelete * CAPMTDelete
*****************************************************************************/ *****************************************************************************/
static void CAPMTDelete( access_t * p_access, int i_session_id, static void CAPMTDelete( access_t * p_access, int i_session_id, uint8_t *p_pmt )
dvbpsi_pmt_t *p_pmt )
{ {
system_ids_t *p_ids = system_ids_t *p_ids =
(system_ids_t *)p_sessions[i_session_id - 1].p_sys; (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
...@@ -1272,7 +1310,11 @@ static void CAPMTDelete( access_t * p_access, int i_session_id, ...@@ -1272,7 +1310,11 @@ static void CAPMTDelete( access_t * p_access, int i_session_id,
p_ids->i_selected_programs--; p_ids->i_selected_programs--;
msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d", msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
<<<<<<< HEAD
p_pmt->i_program_number, i_session_id ); p_pmt->i_program_number, i_session_id );
=======
pmt_get_program( p_pmt ), i_session_id );
>>>>>>> 9d461a5542117d8ec914b2572b3d1a6a2a9636e0
p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
0x5 /* update */, 0x4 /* not selected */, 0x5 /* update */, 0x4 /* not selected */,
...@@ -1304,18 +1346,29 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id, ...@@ -1304,18 +1346,29 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
uint8_t *d = APDUGetLength( p_apdu, &l ); uint8_t *d = APDUGetLength( p_apdu, &l );
msg_Dbg( p_access, "CA system IDs supported by the application :" ); msg_Dbg( p_access, "CA system IDs supported by the application :" );
for ( i = 0; i < l / 2; i++ ) if ( p_ids->i_nb_system_ids )
free( p_ids->pi_system_ids );
p_ids->i_nb_system_ids = l / 2;
p_ids->pi_system_ids = malloc( p_ids->i_nb_system_ids
* sizeof(uint16_t) );
for ( i = 0; i < p_ids->i_nb_system_ids; i++ )
{ {
p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1]; p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
d += 2; d += 2;
msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] ); msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
} }
p_ids->pi_system_ids[i] = 0;
demux_ResendCAPMTs(); demux_ResendCAPMTs();
break; break;
} }
case AOT_CA_UPDATE:
/* http://www.cablelabs.com/specifications/OC-SP-HOSTPOD-IF-I08-011221.pdf */
case AOT_CA_PMT_REPLY:
/* We do not care */
break;
default: default:
msg_Err( p_access, msg_Err( p_access,
"unexpected tag in ConditionalAccessHandle (0x%x)", "unexpected tag in ConditionalAccessHandle (0x%x)",
...@@ -1395,7 +1448,7 @@ static void DateTimeSend( access_t * p_access, int i_session_id ) ...@@ -1395,7 +1448,7 @@ static void DateTimeSend( access_t * p_access, int i_session_id )
APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 ); APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
p_date->i_last = mdate(); p_date->i_last = i_wallclock;
} }
} }
...@@ -1443,7 +1496,7 @@ static void DateTimeManage( access_t * p_access, int i_session_id ) ...@@ -1443,7 +1496,7 @@ static void DateTimeManage( access_t * p_access, int i_session_id )
(date_time_t *)p_sessions[i_session_id - 1].p_sys; (date_time_t *)p_sessions[i_session_id - 1].p_sys;
if ( p_date->i_interval if ( p_date->i_interval
&& mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 ) && i_wallclock > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
{ {
DateTimeSend( p_access, i_session_id ); DateTimeSend( p_access, i_session_id );
} }
...@@ -1587,7 +1640,7 @@ static void MMISendObject( access_t *p_access, int i_session_id, ...@@ -1587,7 +1640,7 @@ static void MMISendObject( access_t *p_access, int i_session_id,
APDUSend( p_access, i_session_id, i_tag, p_data, i_size ); APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
free( p_data ); free( p_data );
pb_slot_mmi_expected[i_slot] = true; p_slots[i_slot].b_mmi_expected = true;
} }
/***************************************************************************** /*****************************************************************************
...@@ -1599,7 +1652,7 @@ static void MMISendClose( access_t *p_access, int i_session_id ) ...@@ -1599,7 +1652,7 @@ static void MMISendClose( access_t *p_access, int i_session_id )
APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 ); APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
pb_slot_mmi_expected[i_slot] = true; p_slots[i_slot].b_mmi_expected = true;
} }
/***************************************************************************** /*****************************************************************************
...@@ -1623,7 +1676,6 @@ static void MMIDisplayReply( access_t *p_access, int i_session_id ) ...@@ -1623,7 +1676,6 @@ static void MMIDisplayReply( access_t *p_access, int i_session_id )
static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size ) static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
{ {
int i_tag = APDUGetTag( *pp_apdu, *pi_size ); int i_tag = APDUGetTag( *pp_apdu, *pi_size );
char *psz_text;
int l; int l;
uint8_t *d; uint8_t *d;
...@@ -1635,14 +1687,11 @@ static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size ) ...@@ -1635,14 +1687,11 @@ static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
} }
d = APDUGetLength( *pp_apdu, &l ); d = APDUGetLength( *pp_apdu, &l );
psz_text = malloc( l + 1 );
strncpy( psz_text, (char *)d, l );
psz_text[l] = '\0';
*pp_apdu += l + 4; *pp_apdu += l + 4;
*pi_size -= l + 4; *pi_size -= l + 4;
return psz_text; return dvb_string_get( d, l, demux_Iconv, p_access );
} }
/***************************************************************************** /*****************************************************************************
...@@ -1667,8 +1716,9 @@ static void MMIHandleEnq( access_t *p_access, int i_session_id, ...@@ -1667,8 +1716,9 @@ static void MMIHandleEnq( access_t *p_access, int i_session_id,
msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text, msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" ); p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" );
pb_slot_mmi_expected[i_slot] = false;
pb_slot_mmi_undisplayed[i_slot] = true; p_slots[i_slot].b_mmi_expected = false;
p_slots[i_slot].b_mmi_undisplayed = true;
} }
/***************************************************************************** /*****************************************************************************
...@@ -1715,8 +1765,9 @@ static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag, ...@@ -1715,8 +1765,9 @@ static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
msg_Dbg( p_access, "MMI choice: %s", psz_text ); msg_Dbg( p_access, "MMI choice: %s", psz_text );
} }
} }
pb_slot_mmi_expected[i_slot] = false;
pb_slot_mmi_undisplayed[i_slot] = true; p_slots[i_slot].b_mmi_expected = false;
p_slots[i_slot].b_mmi_undisplayed = true;
} }
/***************************************************************************** /*****************************************************************************
...@@ -1784,8 +1835,9 @@ static void MMIClose( access_t *p_access, int i_session_id ) ...@@ -1784,8 +1835,9 @@ static void MMIClose( access_t *p_access, int i_session_id )
free( p_sessions[i_session_id - 1].p_sys ); free( p_sessions[i_session_id - 1].p_sys );
msg_Dbg( p_access, "closing MMI session (%d)", i_session_id ); msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
pb_slot_mmi_expected[i_slot] = false;
pb_slot_mmi_undisplayed[i_slot] = true; p_slots[i_slot].b_mmi_expected = false;
p_slots[i_slot].b_mmi_undisplayed = true;
} }
/***************************************************************************** /*****************************************************************************
...@@ -1812,39 +1864,11 @@ static void MMIOpen( access_t *p_access, int i_session_id ) ...@@ -1812,39 +1864,11 @@ static void MMIOpen( access_t *p_access, int i_session_id )
/***************************************************************************** /*****************************************************************************
* InitSlot: Open the transport layer * InitSlot: Open the transport layer
*****************************************************************************/ *****************************************************************************/
#define MAX_TC_RETRIES 5 static void InitSlot( access_t * p_access, int i_slot )
static int InitSlot( access_t * p_access, int i_slot )
{ {
int i; if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 ) != 0 )
if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
!= 0 )
{
msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d", msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
i_slot ); i_slot );
return -1;
}
/* Wait for T_CTC_REPLY */
for ( i = 0; i < MAX_TC_RETRIES; i++ )
{
uint8_t i_tag;
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == 0
&& i_tag == T_CTC_REPLY )
{
pb_active_slot[i_slot] = true;
break;
}
}
if ( pb_active_slot[i_slot] )
{
i_ca_timeout = 100000;
return 0;
}
return -1;
} }
/***************************************************************************** /*****************************************************************************
...@@ -1852,12 +1876,39 @@ static int InitSlot( access_t * p_access, int i_slot ) ...@@ -1852,12 +1876,39 @@ static int InitSlot( access_t * p_access, int i_slot )
*****************************************************************************/ *****************************************************************************/
static void ResetSlot( int i_slot ) static void ResetSlot( int i_slot )
{ {
ci_slot_t *p_slot = &p_slots[i_slot];
int i_session_id; int i_session_id;
switch (i_print_type)
{
case PRINT_XML:
printf("<STATUS type=\"cam\" status=\"0\" />\n");
break;
default:
break;
}
if ( ioctl( i_ca_handle, CA_RESET, 1 << i_slot ) != 0 ) if ( ioctl( i_ca_handle, CA_RESET, 1 << i_slot ) != 0 )
msg_Err( NULL, "en50221_Poll: couldn't reset slot %d", i_slot ); msg_Err( NULL, "en50221_Poll: couldn't reset slot %d", i_slot );
pb_active_slot[i_slot] = false; p_slot->b_active = false;
pb_tc_has_data[i_slot] = false; p_slot->i_init_timeout = mdate() + CAM_INIT_TIMEOUT;
p_slot->b_expect_answer = false;
p_slot->b_mmi_expected = false;
p_slot->b_mmi_undisplayed = false;
if ( p_slot->p_recv != NULL )
{
free( p_slot->p_recv->p_data );
free( p_slot->p_recv );
}
p_slot->p_recv = NULL;
while ( p_slot->p_send != NULL )
{
en50221_msg_t *p_next = p_slot->p_send->p_next;
free( p_slot->p_send->p_data );
free( p_slot->p_send );
p_slot->p_send = p_next;
}
p_slot->pp_send_last = &p_slot->p_send;
/* Close all sessions for this slot. */ /* Close all sessions for this slot. */
for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ ) for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
...@@ -1872,8 +1923,6 @@ static void ResetSlot( int i_slot ) ...@@ -1872,8 +1923,6 @@ static void ResetSlot( int i_slot )
p_sessions[i_session_id - 1].i_resource_id = 0; p_sessions[i_session_id - 1].i_resource_id = 0;
} }
} }
i_ca_timeout = 100000;
} }
...@@ -1963,10 +2012,7 @@ void en50221_Init( void ) ...@@ -1963,10 +2012,7 @@ void en50221_Init( void )
*****************************************************************************/ *****************************************************************************/
void en50221_Reset( void ) void en50221_Reset( void )
{ {
memset( pb_active_slot, 0, sizeof(bool) * MAX_CI_SLOTS ); memset( p_slots, 0, sizeof(ci_slot_t) * MAX_CI_SLOTS );
memset( pb_tc_has_data, 0, sizeof(bool) * MAX_CI_SLOTS );
memset( pb_slot_mmi_expected, 0, sizeof(bool) * MAX_CI_SLOTS );
memset( pb_slot_mmi_undisplayed, 0, sizeof(bool) * MAX_CI_SLOTS );
if( i_ca_type & CA_CI_LINK ) if( i_ca_type & CA_CI_LINK )
{ {
...@@ -2023,7 +2069,7 @@ void en50221_Reset( void ) ...@@ -2023,7 +2069,7 @@ void en50221_Reset( void )
return; return;
} }
#if HLCI_WAIT_CAM_READY #ifdef HLCI_WAIT_CAM_READY
while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff ) while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
{ {
msleep(1); msleep(1);
...@@ -2058,16 +2104,25 @@ void en50221_Reset( void ) ...@@ -2058,16 +2104,25 @@ void en50221_Reset( void )
} }
/***************************************************************************** /*****************************************************************************
* en50221_Poll : Poll the CAM for TPDUs * en50221_Read : Read the CAM for a TPDU
*****************************************************************************/
void en50221_Read( void )
{
TPDURecv( NULL );
}
/*****************************************************************************
* en50221_Poll : Send a poll TPDU to the CAM
*****************************************************************************/ *****************************************************************************/
void en50221_Poll( void ) void en50221_Poll( void )
{ {
int i_slot; int i_slot;
int i_session_id; int i_session_id;
/* Check module status */
for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ ) for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ )
{ {
uint8_t i_tag; ci_slot_t *p_slot = &p_slots[i_slot];
ca_slot_info_t sinfo; ca_slot_info_t sinfo;
sinfo.num = i_slot; sinfo.num = i_slot;
...@@ -2080,124 +2135,51 @@ void en50221_Poll( void ) ...@@ -2080,124 +2135,51 @@ void en50221_Poll( void )
if ( !(sinfo.flags & CA_CI_MODULE_READY) ) if ( !(sinfo.flags & CA_CI_MODULE_READY) )
{ {
if ( pb_active_slot[i_slot] ) if ( p_slot->b_active )
{ {
msg_Dbg( NULL, "en50221_Poll: slot %d has been removed", msg_Dbg( NULL, "en50221_Poll: slot %d has been removed",
i_slot ); i_slot );
pb_active_slot[i_slot] = false; ResetSlot( i_slot );
pb_slot_mmi_expected[i_slot] = false;
pb_slot_mmi_undisplayed[i_slot] = false;
/* Close all sessions for this slot. */
for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
i_session_id++ )
{
if ( p_sessions[i_session_id - 1].i_resource_id
&& p_sessions[i_session_id - 1].i_slot
== i_slot )
{
if ( p_sessions[i_session_id - 1].pf_close
!= NULL )
{
p_sessions[i_session_id - 1].pf_close(
NULL, i_session_id );
}
p_sessions[i_session_id - 1].i_resource_id = 0;
}
}
} }
continue; continue;
} }
else if ( !pb_active_slot[i_slot] ) else if ( !p_slot->b_active )
{ {
if ( !p_slot->b_expect_answer )
InitSlot( NULL, i_slot ); InitSlot( NULL, i_slot );
else if ( p_slot->i_init_timeout < i_wallclock )
if ( !pb_active_slot[i_slot] )
{ {
msg_Dbg( NULL, "en50221_Poll: resetting slot %d", i_slot ); msg_Dbg( NULL, "en50221_Poll: resetting slot %d", i_slot );
ResetSlot( i_slot ); ResetSlot( i_slot );
continue; continue;
} }
msg_Dbg( NULL, "en50221_Poll: slot %d is active",
i_slot );
} }
if ( !pb_tc_has_data[i_slot] )
{
if ( TPDUSend( NULL, i_slot, T_DATA_LAST, NULL, 0 ) != 0 )
{
msg_Err( NULL,
"en50221_Poll: couldn't send TPDU on slot %d, resetting",
i_slot );
ResetSlot( i_slot );
continue;
} }
if ( TPDURecv( NULL, i_slot, &i_tag, NULL, NULL ) != 0 )
/* Check if applications have data to send */
for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
{ {
msg_Err( NULL, en50221_session_t *p_session = &p_sessions[i_session_id - 1];
"en50221_Poll: couldn't recv TPDU on slot %d, resetting", if ( p_session->i_resource_id && p_session->pf_manage != NULL
i_slot ); && !p_slots[ p_session->i_slot ].b_expect_answer )
ResetSlot( i_slot ); p_session->pf_manage( NULL, i_session_id );
continue;
}
} }
while ( pb_tc_has_data[i_slot] ) /* Now send the poll command to inactive slots */
for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ )
{ {
uint8_t p_tpdu[MAX_TPDU_SIZE]; ci_slot_t *p_slot = &p_slots[i_slot];
int i_size, i_session_size;
uint8_t *p_session;
if ( TPDUSend( NULL, i_slot, T_RCV, NULL, 0 ) != 0 ) if ( p_slot->b_active && !p_slot->b_expect_answer )
{
if ( TPDUSend( NULL, i_slot, T_DATA_LAST, NULL, 0 ) != 0 )
{ {
msg_Err( NULL, msg_Err( NULL,
"en50221_Poll: couldn't send TPDU on slot %d, resetting", "en50221_Poll: couldn't send TPDU on slot %d, resetting",
i_slot ); i_slot );
ResetSlot( i_slot ); ResetSlot( i_slot );
continue;
} }
if ( TPDURecv( NULL, i_slot, &i_tag, p_tpdu, &i_size ) != 0 )
{
msg_Err( NULL,
"en50221_Poll: couldn't recv TPDU on slot %d, resetting",
i_slot );
ResetSlot( i_slot );
continue;
}
p_session = GetLength( &p_tpdu[3], &i_session_size );
if ( i_session_size <= 1 )
continue;
p_session++;
i_session_size--;
if ( i_tag != T_DATA_LAST )
{
/* I sometimes see a CAM responding T_SB to our T_RCV.
* It said it had data to send, but does not send it after
* our T_RCV. There is probably something wrong here. I
* experienced that this case happens on start-up, and the
* CAM doesn't open any session at all, so it is quite
* useless. Reset it. */
msg_Err( NULL,
"en50221_Poll: invalid TPDU 0x%x, resetting", i_tag );
ResetSlot( i_slot );
break;
}
SPDUHandle( NULL, i_slot, p_session, i_session_size );
}
}
for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
{
if ( p_sessions[i_session_id - 1].i_resource_id
&& p_sessions[i_session_id - 1].pf_manage )
{
p_sessions[i_session_id - 1].pf_manage( NULL, i_session_id );
} }
} }
} }
...@@ -2205,7 +2187,7 @@ void en50221_Poll( void ) ...@@ -2205,7 +2187,7 @@ void en50221_Poll( void )
/***************************************************************************** /*****************************************************************************
* en50221_AddPMT : * en50221_AddPMT :
*****************************************************************************/ *****************************************************************************/
void en50221_AddPMT( dvbpsi_pmt_t *p_pmt ) void en50221_AddPMT( uint8_t *p_pmt )
{ {
int i_session_id; int i_session_id;
...@@ -2218,7 +2200,7 @@ void en50221_AddPMT( dvbpsi_pmt_t *p_pmt ) ...@@ -2218,7 +2200,7 @@ void en50221_AddPMT( dvbpsi_pmt_t *p_pmt )
/***************************************************************************** /*****************************************************************************
* en50221_UpdatePMT : * en50221_UpdatePMT :
*****************************************************************************/ *****************************************************************************/
void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt ) void en50221_UpdatePMT( uint8_t *p_pmt )
{ {
int i_session_id; int i_session_id;
...@@ -2231,7 +2213,7 @@ void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt ) ...@@ -2231,7 +2213,7 @@ void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt )
/***************************************************************************** /*****************************************************************************
* en50221_DeletePMT : * en50221_DeletePMT :
*****************************************************************************/ *****************************************************************************/
void en50221_DeletePMT( dvbpsi_pmt_t *p_pmt ) void en50221_DeletePMT( uint8_t *p_pmt )
{ {
int i_session_id; int i_session_id;
...@@ -2373,7 +2355,7 @@ uint8_t en50221_GetMMIObject( uint8_t *p_buffer, ssize_t i_size, ...@@ -2373,7 +2355,7 @@ uint8_t en50221_GetMMIObject( uint8_t *p_buffer, ssize_t i_size,
if ( i_size != 1 ) return RET_HUH; if ( i_size != 1 ) return RET_HUH;
i_slot = *p_buffer; i_slot = *p_buffer;
if ( pb_slot_mmi_expected[i_slot] == true ) if ( p_slots[i_slot].b_mmi_expected )
return RET_MMI_WAIT; /* data not yet available */ return RET_MMI_WAIT; /* data not yet available */
p_ret->object.i_object_type = EN50221_MMI_NONE; p_ret->object.i_object_type = EN50221_MMI_NONE;
...@@ -2418,6 +2400,12 @@ uint8_t en50221_SendMMIObject( uint8_t *p_buffer, ssize_t i_size ) ...@@ -2418,6 +2400,12 @@ uint8_t en50221_SendMMIObject( uint8_t *p_buffer, ssize_t i_size )
int i_session_id, i_slot; int i_session_id, i_slot;
struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_buffer; struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_buffer;
if ( i_size < sizeof(struct cmd_mmi_send))
{
msg_Err( NULL, "command packet too short (%zd)\n", i_size );
return RET_HUH;
}
if ( en50221_UnserializeMMIObject( &p_cmd->object, i_size - if ( en50221_UnserializeMMIObject( &p_cmd->object, i_size -
((void *)&p_cmd->object - (void *)p_cmd) ) == -1 ) ((void *)&p_cmd->object - (void *)p_cmd) ) == -1 )
return RET_ERR; return RET_ERR;
......
...@@ -2,23 +2,14 @@ ...@@ -2,23 +2,14 @@
* en50221.h * en50221.h
***************************************************************************** *****************************************************************************
* Copyright (C) 2008 VideoLAN * Copyright (C) 2008 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software. It comes without any warranty, to
* it under the terms of the GNU General Public License as published by * the extent permitted by applicable law. You can redistribute it
* the Free Software Foundation; either version 2 of the License, or * and/or modify it under the terms of the Do What The Fuck You Want
* (at your option) any later version. * To Public License, Version 2, as published by Sam Hocevar. See
* * http://sam.zoy.org/wtfpl/COPYING for more details.
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#include <malloc.h> #include <malloc.h>
...@@ -69,7 +60,6 @@ typedef struct en50221_mmi_object_t ...@@ -69,7 +60,6 @@ typedef struct en50221_mmi_object_t
#define MAX_CI_SLOTS 16 #define MAX_CI_SLOTS 16
#define MAX_SESSIONS 32 #define MAX_SESSIONS 32
#define MAX_PROGRAMS 24
extern int i_ca_handle; extern int i_ca_handle;
extern int i_ca_type; extern int i_ca_type;
...@@ -79,10 +69,11 @@ extern int i_ca_type; ...@@ -79,10 +69,11 @@ extern int i_ca_type;
*****************************************************************************/ *****************************************************************************/
void en50221_Init( void ); void en50221_Init( void );
void en50221_Reset( void ); void en50221_Reset( void );
void en50221_Read( void );
void en50221_Poll( void ); void en50221_Poll( void );
void en50221_AddPMT( dvbpsi_pmt_t *p_pmt ); void en50221_AddPMT( uint8_t *p_pmt );
void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt ); void en50221_UpdatePMT( uint8_t *p_pmt );
void en50221_DeletePMT( dvbpsi_pmt_t *p_pmt ); void en50221_DeletePMT( uint8_t *p_pmt );
uint8_t en50221_StatusMMI( uint8_t *p_answer, ssize_t *pi_size ); uint8_t en50221_StatusMMI( uint8_t *p_answer, ssize_t *pi_size );
uint8_t en50221_StatusMMISlot( uint8_t *p_buffer, ssize_t i_size, uint8_t en50221_StatusMMISlot( uint8_t *p_buffer, ssize_t i_size,
uint8_t *p_answer, ssize_t *pi_size ); uint8_t *p_answer, ssize_t *pi_size );
......
About DVBiscovery
=================
DVBiscovery is a shell script that tries to tune a number of frequencies
(from a config file), and stops after it has found a match. It then dumps
the output of DVBlast to the standard output, so that an external process
can parse it and find relevant information there. Typically, you would
want to get the network ID from the NIT, which unically identifies the
network you are on.
DVBiscovery is therefore a sort of scanning program, but doesn't aim at
exhaustivity (there are already programs doing that). It tries to guess
where you are without any external information.
#!/bin/sh
###############################################################################
# dvbiscovery.sh
###############################################################################
# Copyright (C) 2010 VideoLAN
#
# Authors: Christophe Massiot <massiot@via.ecp.fr>
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
###############################################################################
CONF_BASE="/usr/local/share/dvblast/dvbiscovery"
#CONF_BASE="./"
DVBLAST=dvblast
LOCK_TIMEOUT=2500
QUIT_TIMEOUT=20000
usage() {
echo "Usage: $0 [-a <adapter #>] [-n <frontend #>] [-S <diseqc sat num>] [-c <conf file>]" >&2
exit 1
}
conf_file_passed=""
adapter=""
frontend=""
diseqc=""
TEMP=`getopt -o a:n:S:c: -n "$0" -- "$@"`
if test $? -ne 0; then
usage
fi
eval set -- "$TEMP"
while :; do
case "$1" in
-a)
adapter="-a $2"
shift 2
;;
-n)
frontend="-n $2"
shift 2
;;
-c)
conf_file_passed=$2
shift 2
;;
-S)
diseqc="-S $2"
shift 2
;;
--)
shift
break
;;
*)
usage
;;
esac
done
type=`$DVBLAST $diseqc $adapter $frontend -f 0 2>&1 | grep '^debug: Frontend' | sed 's/^debug: Frontend ".*" type "\(.*\)" supports:$/\1/'`
tune=""
conf_file=""
case "$type" in
"QPSK (DVB-S/S2)")
conf_file="${CONF_BASE}_dvb-s.conf"
tune=tune_sat
;;
"QAM (DVB-C)")
conf_file="${CONF_BASE}_dvb-c.conf"
tune=tune_cable
;;
"OFDM (DVB-T)")
conf_file="${CONF_BASE}_dvb-t.conf"
tune=tune_dtt
;;
"ATSC")
conf_file="${CONF_BASE}_atsc.conf"
tune=tune_atsc
;;
*)
echo "unknown frontend type $type" >&2
exit 1
esac
if test -n "$conf_file_passed"; then
conf_file=$conf_file_passed
fi
if ! test -r "$conf_file"; then
echo "unable to open $conf_file" >&2
exit 1
fi
signal_catch() {
if test $childpid -ne 0; then
kill $childpid
wait $childpid
fi
exit 1
}
exec_dvblast() {
tmp_file=`mktemp`
$DVBLAST $diseqc $adapter $frontend -O $LOCK_TIMEOUT -Q $QUIT_TIMEOUT -q4 -x xml $opts >| $tmp_file &
childpid=$!
wait $childpid
if test $? -eq 0; then
cat $tmp_file
echo "</TS>"
rm $tmp_file
exit 0
fi
childpid=0
rm $tmp_file
}
strtofec() {
case "$1" in
"NONE") opts="$opts $2 0" ;;
"1/2") opts="$opts $2 12" ;;
"2/3") opts="$opts $2 23" ;;
"3/4") opts="$opts $2 34" ;;
"4/5") opts="$opts $2 45" ;;
"5/6") opts="$opts $2 56" ;;
"6/7") opts="$opts $2 67" ;;
"7/8") opts="$opts $2 78" ;;
"8/9") opts="$opts $2 89" ;;
"AUTO"|*) ;;
esac
}
strtomod() {
case "$1" in
"QPSK") opts="$opts -m qpsk" ;;
"QAM16") opts="$opts -m qam_16" ;;
"QAM32") opts="$opts -m qam_32" ;;
"QAM64") opts="$opts -m qam_64" ;;
"QAM128") opts="$opts -m qam_128" ;;
"8VSB") opts="$opts -m vsb_8" ;;
"16VSB") opts="$opts -m vsb_16" ;;
"AUTO"|*) ;;
esac
}
tune_sat() {
childpid=0
trap signal_catch 1 2 3 15
while read sys freq pol srate fec what mod; do
opts="-f $freq -s $srate"
case "$sys" in
"S") ;;
"S2")
case "$mod" in
"QPSK") opts="$opts -m qpsk" ;;
"8PSK") opts="$opts -m psk_8" ;;
*)
echo "invalid modulation $mod" >&2
;;
esac
;;
*)
echo "incompatible file" >&2
exit 1
;;
esac
strtofec $fec "-F"
case "$pol" in
"V") opts="$opts -v 13" ;;
"H") opts="$opts -v 18" ;;
*) ;;
esac
exec_dvblast
done
}
tune_cable() {
childpid=0
trap signal_catch 1 2 3 15
while read sys freq srate fec mod; do
opts="-f $freq -s $srate"
case "$sys" in
"C") ;;
*)
echo "incompatible file" >&2
exit 1
;;
esac
strtofec $fec "-F"
strtomod $mod
exec_dvblast
done
}
tune_dtt() {
childpid=0
trap signal_catch 1 2 3 15
while read sys freq bw fec fec2 mod mode guard hier; do
opts="-f $freq"
case "$sys" in
"T"|"T2") ;;
*)
echo "incompatible file" >&2
exit 1
;;
esac
case "$bw" in
"8MHz") opts="$opts -b 8" ;;
"7MHz") opts="$opts -b 7" ;;
"6MHz") opts="$opts -b 6" ;;
"AUTO"|*) ;;
esac
strtofec $fec "-F"
strtofec $fec2 "-K"
strtomod $mod
case "$mode" in
"2k") opts="$opts -X 2" ;;
"8k") opts="$opts -X 8" ;;
"AUTO"|*) ;;
esac
case "$guard" in
"1/32") opts="$opts -G 32" ;;
"1/16") opts="$opts -G 16" ;;
"1/8") opts="$opts -G 8" ;;
"1/4") opts="$opts -G 4" ;;
"AUTO"|*) ;;
esac
case "$hier" in
"NONE") opts="$opts -H 0" ;;
"1") opts="$opts -H 1" ;;
"2") opts="$opts -H 2" ;;
"4") opts="$opts -H 4" ;;
"AUTO"|*) ;;
esac
exec_dvblast
done
}
tune_atsc() {
childpid=0
trap signal_catch 1 2 3 15
while read sys freq mod; do
opts="-f $freq"
case "$sys" in
"A") ;;
*)
echo "incompatible file" >&2
exit 1
;;
esac
strtomod $mod
exec_dvblast
done
}
childpid=0
trap signal_catch 1 2 3 15
grep -v "^#" < "$conf_file" 2>/dev/null | $tune &
childpid=$!
wait $childpid
exit 100
# US ATSC center frequencies
A 57028615 8VSB
A 63028615 8VSB
A 69028615 8VSB
A 79028615 8VSB
A 85028615 8VSB
A 177028615 8VSB
A 183028615 8VSB
A 189028615 8VSB
A 195028615 8VSB
A 201028615 8VSB
A 207028615 8VSB
A 213028615 8VSB
A 473028615 8VSB
A 479028615 8VSB
A 485028615 8VSB
A 491028615 8VSB
A 497028615 8VSB
A 503028615 8VSB
A 509028615 8VSB
A 515028615 8VSB
A 521028615 8VSB
A 527028615 8VSB
A 533028615 8VSB
A 539028615 8VSB
A 545028615 8VSB
A 551028615 8VSB
A 557028615 8VSB
A 563028615 8VSB
A 569028615 8VSB
A 575028615 8VSB
A 581028615 8VSB
A 587028615 8VSB
A 593028615 8VSB
A 599028615 8VSB
A 605028615 8VSB
A 611028615 8VSB
A 617028615 8VSB
A 623028615 8VSB
A 629028615 8VSB
A 635028615 8VSB
A 641028615 8VSB
A 647028615 8VSB
A 653028615 8VSB
A 659028615 8VSB
A 665028615 8VSB
A 671028615 8VSB
A 677028615 8VSB
A 683028615 8VSB
A 689028615 8VSB
A 695028615 8VSB
A 701028615 8VSB
A 707028615 8VSB
A 713028615 8VSB
A 719028615 8VSB
A 725028615 8VSB
A 731028615 8VSB
A 737028615 8VSB
A 743028615 8VSB
A 749028615 8VSB
A 755028615 8VSB
A 761028615 8VSB
A 767028615 8VSB
A 773028615 8VSB
A 779028615 8VSB
A 785028615 8VSB
A 791028615 8VSB
A 797028615 8VSB
A 803028615 8VSB
# Gathered from diverse sources
C 113000000 6900000 NONE AUTO
C 121000000 6900000 NONE AUTO
C 123000000 6875000 NONE AUTO
C 146000000 6900000 NONE AUTO
C 154000000 6875000 NONE AUTO
C 154000000 6900000 NONE AUTO
C 163000000 6875000 NONE AUTO
C 218000000 6900000 NONE AUTO
C 241000000 6900000 NONE AUTO
C 283000000 5900000 NONE AUTO
C 289500000 6875000 NONE AUTO
C 306000000 6900000 NONE AUTO
C 313000000 6875000 NONE AUTO
C 314000000 6900000 NONE AUTO
C 330000000 6875000 NONE AUTO
C 346000000 6875000 NONE AUTO
C 354000000 6900000 NONE AUTO
C 354000000 6950000 NONE AUTO
C 372000000 6875000 NONE AUTO
C 377750000 6900000 NONE AUTO
C 386000000 6875000 NONE AUTO
C 386000000 6900000 NONE AUTO
C 394000000 6900000 NONE AUTO
C 410000000 6900000 NONE AUTO
C 418000000 6900000 NONE AUTO
C 434000000 6900000 NONE AUTO
C 442000000 6900000 NONE AUTO
C 450000000 6875000 NONE AUTO
C 490000000 6875000 NONE AUTO
C 514000000 6900000 NONE AUTO
C 530000000 6900000 NONE AUTO
C 634000000 6900000 NONE AUTO
C 714000000 6875000 NONE AUTO
# freq pol sr fec
# Astra 19.2E
S 12551500 V 22000000 5/6
# Eurobird 9E
S 11843000 V 27500000 AUTO
# Hotbird 13E
S 11727000 V 27500000 AUTO
# Astra 23.5E
S 11914000 H 27500000 AUTO
# Eurobird 28.5E
S 11623000 H 27500000 2/3
# Eutelsat 16.0E & Telecom2 8.0W & Amos 4.0W
S 10972000 V 27500000 AUTO
# Sirius 5.0E & Nilesat
S 11727000 H 27500000 AUTO
# Turksat 42.0E
S 10970000 V 30000000 5/6
# Atlantic Bird 1 12.5W
S 11408000 V 27500000 3/4
# Atlantic Bird 3 5.0W
S 11591000 V 20000000 2/3
# Hispasat 30.0W
S 12015000 V 27500000 3/4
# Telstar 12 15.0W
S 11060000 H 19279000 3/4
# Thor 1.0W
S 11216000 V 24500000 7/8
# Express AM1 40.0E
S 10967000 V 20000000 AUTO
# Hellas Sat 39.0E
S 12565000 V 30000000 AUTO
# Eutelsat W3A 7.0E
S 11283000 V 27500000 AUTO
# freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy
# we put all UHF frequencies, hoping to find something
T 474000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 474167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 474333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 474500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 473833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 473667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 482000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 482167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 482333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 482500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 481833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 481667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 490000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 490167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 490333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 490500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 489833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 489667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 498000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 498167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 498333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 498500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 497833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 497667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 506000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 506167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 506333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 506500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 505833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 505667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 514000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 514167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 514333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 514500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 513833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 513667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 522000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 522167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 522333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 522500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 521833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 521667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 530000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 530167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 530333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 530500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 529833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 529667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 538000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 538167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 538333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 538500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 537833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 537667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 546000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 546167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 546333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 546500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 545833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 545667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 554000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 554167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 554333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 554500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 553833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 553667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 562000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 562167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 562333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 562500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 561833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 561667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 570000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 570167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 570333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 570500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 569833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 569667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 578000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 578167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 578333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 578500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 577833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 577667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 586000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 586167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 586333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 586500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 585833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 585667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 594000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 594167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 594333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 594500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 593833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 593667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 602000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 602167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 602333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 602500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 601833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 601667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 610000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 610167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 610333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 610500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 609833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 609667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 618000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 618167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 618333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 618500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 617833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 617667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 626000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 626167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 626333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 626500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 625833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 625667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 634000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 634167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 634333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 634500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 633833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 633667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 642000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 642167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 642333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 642500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 641833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 641667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 650000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 650167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 650333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 650500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 649833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 649667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 658000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 658167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 658333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 658500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 657833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 657667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 666000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 666167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 666333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 666500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 665833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 665667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 674000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 674167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 674333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 674500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 673833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 673667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 682000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 682167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 682333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 682500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 681833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 681667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 690000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 690167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 690333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 690500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 689833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 689667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 698000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 698167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 698333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 698500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 697833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 697667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 706000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 706167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 706333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 706500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 705833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 705667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 714000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 714167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 714333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 714500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 713833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 713667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 722000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 722167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 722333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 722500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 721833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 721667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 730000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 730167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 730333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 730500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 729833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 729667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 738000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 738167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 738333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 738500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 737833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 737667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 746000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 746167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 746333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 746500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 745833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 745667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 754000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 754167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 754333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 754500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 753833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 753667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 762000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 762167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 762333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 762500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 761833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 761667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 770000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 770167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 770333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 770500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 769833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 769667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 778000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 778167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 778333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 778500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 777833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 777667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 786000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 786167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 786333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 786500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 785833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 785667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 794000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 794167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 794333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 794500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 793833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 793667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 802000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 802167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 802333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 802500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 801833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 801667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 810000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 810167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 810333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 810500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 809833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 809667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 818000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 818167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 818333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 818500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 817833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 817667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 826000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 826167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 826333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 826500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 825833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 825667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 834000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 834167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 834333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 834500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 833833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 833667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 842000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 842167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 842333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 842500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 841833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 841667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 850000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 850167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 850333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 850500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 849833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 849667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 858000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 858167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 858333000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 858500000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 857833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 857667000 8MHz AUTO NONE AUTO AUTO AUTO NONE
# and now for something completely different: VHF
T 177500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 184500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 191500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 198500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 205500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 212500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 219500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 226500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
--- linux/drivers/media/video/cx23885/cx23885-dvb.c.orig 2010-06-29 15:32:09.000000000 +0200
+++ linux/drivers/media/video/cx23885/cx23885-dvb.c 2010-06-29 15:32:16.000000000 +0200
@@ -83,7 +83,7 @@
struct cx23885_tsport *port = q->priv_data;
port->ts_packet_size = 188 * 4;
- port->ts_packet_count = 32;
+ port->ts_packet_count = 5;
*size = port->ts_packet_size * port->ts_packet_count;
*count = 32;
--- linux/drivers/media/dvb/ttpci/budget-core.c.orig 2010-06-09 08:54:16.000000000 +0200
+++ linux/drivers/media/dvb/ttpci/budget-core.c 2010-06-09 08:28:23.000000000 +0200
@@ -145,7 +145,7 @@
saa7146_write(dev, BASE_EVEN3, 0);
}
saa7146_write(dev, PROT_ADDR3, budget->buffer_size);
- saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
+ saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x50);
saa7146_write(dev, PITCH3, budget->buffer_width);
saa7146_write(dev, NUM_LINE_BYTE3,
--- linux/drivers/media/video/cx88/cx88-dvb.c.orig 2010-02-22 14:58:43.000000000 +0100
+++ linux/drivers/media/video/cx88/cx88-dvb.c 2010-07-09 11:15:12.000000000 +0200
@@ -80,7 +80,7 @@
struct cx8802_dev *dev = q->priv_data;
dev->ts_packet_size = 188 * 4;
- dev->ts_packet_count = 32;
+ dev->ts_packet_count = 5;
*size = dev->ts_packet_size * dev->ts_packet_count;
*count = 32;
About DVBlast kernel patches
============================
These kernel patches are designed to dramatically improve the latency
between the DVB card and DVBlast's output, especially on low symbol rate
transponders, at the expense of a slightly higher CPU consumption.
Basically they decrease the size of the transmission buffers from the card
to 4 kB, so that TS packets are handled more frequently.
/*****************************************************************************
* mrtg-cnt.c Handle dvb TS packets and count them for MRTG
*****************************************************************************
* Copyright Tripleplay service 2004,2005,2011
*
* Author: Andy Lindsay <a.lindsay@tripleplay-services.com>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/* vim: set shiftwidth=4 tabstop=4 expandtab autoindent : */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include "dvblast.h"
// File handle
static FILE *mrtg_fh = NULL;
// Counts
static long long l_mrtg_packets = 0; // Packets received
static long long l_mrtg_seq_err_packets = 0; // Out of sequence packets received
static long long l_mrtg_error_packets = 0; // Packets received with the error flag set
static long long l_mrtg_scram_packets = 0; // Scrambled Packets received
// Reporting timer
#if defined( WIN32 )
static LARGE_INTEGER mrtg_time;
static LARGE_INTEGER mrtg_inc;
#else
static struct timeval mrtg_time = { 0, 0 };
#endif
// Define the dump period in seconds
#define MRTG_INTERVAL 10
// Pid sequence numbers
#define PIDS 0x2000
static signed char i_pid_seq[PIDS];
// Report the mrtg counters: bytes received, error packets & sequence errors
static void dumpCounts()
{
unsigned int multiplier = 1; //MRTG_INTERVAL;
if(mrtg_fh) {
rewind(mrtg_fh);
fprintf(mrtg_fh, "%lld %lld %lld %lld\n",
l_mrtg_packets * 188 * multiplier,
l_mrtg_error_packets * multiplier,
l_mrtg_seq_err_packets * multiplier,
l_mrtg_scram_packets * multiplier);
fflush(mrtg_fh);
}
}
// analyse the input block counting packets and errors
// The input is a pointer to a block_t structure, which might be a linked list
// of blocks. Each block has one TS packet.
void mrtgAnalyse(block_t * p_ts)
{
unsigned int i_pid;
block_t *p_block = p_ts;
if (mrtg_fh == NULL) return;
while (p_block != NULL) {
uint8_t *ts_packet = p_block->p_ts;
char i_seq, i_last_seq;
l_mrtg_packets++;
if (ts_packet[0] != 0x47) {
l_mrtg_error_packets++;
p_block = p_block->p_next;
continue;
}
if (ts_packet[1] & 0x80) {
l_mrtg_error_packets++;
p_block = p_block->p_next;
continue;
}
i_pid = (ts_packet[1] & 0x1f) << 8 | ts_packet[2];
// Just count null packets - don't check the sequence numbering
if (i_pid == 0x1fff) {
p_block = p_block->p_next;
continue;
}
if (ts_packet[3] & 0xc0) {
l_mrtg_scram_packets++;
}
// Check the sequence numbering
i_seq = ts_packet[3] & 0xf;
i_last_seq = i_pid_seq[i_pid];
if (i_last_seq == -1) {
// First packet - ignore the sequence
} else if (ts_packet[3] & 0x10) {
// Packet contains payload - sequence should be up by one
if (i_seq != ((i_last_seq + 1) & 0x0f)) {
l_mrtg_seq_err_packets++;
}
} else {
// Packet contains no payload - sequence should be unchanged
if (i_seq != i_last_seq) {
l_mrtg_seq_err_packets++;
}
}
i_pid_seq[i_pid] = i_seq;
// Look at next block
p_block = p_block->p_next;
}
// All blocks processed. See if we need to dump the stats
struct timeval now;
gettimeofday(&now, NULL);
if (timercmp(&now, &mrtg_time, >)) {
// Time to report the mrtg counters
dumpCounts();
// Set the timer for next time
//
// Normally we add the interval to the previous time so that if one
// dump is a bit late, the next one still occurs at the correct time.
// However, if there is a long gap (e.g. because the channel has
// stopped for some time), then just rebase the timing to the current
// time. I've chosen MRTG_INTERVAL as the long gap - this is arbitary
if ((now.tv_sec - mrtg_time.tv_sec) > MRTG_INTERVAL) {
msg_Dbg(NULL, "Dump is %d seconds late - reset timing\n",
(int) (now.tv_sec - mrtg_time.tv_sec));
mrtg_time = now;
}
mrtg_time.tv_sec += MRTG_INTERVAL;
}
}
int mrtgInit(char *mrtg_file)
{
if ( !mrtg_file )
return -1;
/* Open MRTG file */
msg_Dbg(NULL, "Opening mrtg file %s.\n", mrtg_file);
if ((mrtg_fh = fopen(mrtg_file, "wb")) == NULL) {
msg_Err(NULL, "unable to open mrtg file");
return -1;
}
// Initialise the file
fprintf(mrtg_fh, "0 0 0 0\n");
fflush(mrtg_fh);
// Initialise the sequence numbering
memset(&i_pid_seq[0], -1, sizeof(signed char) * PIDS);
// Set the reporting timer
gettimeofday(&mrtg_time, NULL);
mrtg_time.tv_sec += MRTG_INTERVAL;
return 0;
}
void mrtgClose()
{
// This is only for testing when using filetest.
if (mrtg_fh) {
dumpCounts();
fclose(mrtg_fh);
mrtg_fh = NULL;
}
}
/*****************************************************************************
* mrtg-cnt.h
*****************************************************************************
* Copyright Tripleplay service 2004,2005,2011
*
* Author: Andy Lindsay <a.lindsay@tripleplay-services.com>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/* vim: set shiftwidth=4 tabstop=4 expandtab autoindent : */
#ifndef MRTG_CNT_H
#define MRTG_CNT_H
int mrtgInit(char *mrtg_file);
void mrtgClose();
void mrtgAnalyse(block_t * p_ts);
#endif
/***************************************************************************** /*****************************************************************************
* output.c * output.c
***************************************************************************** *****************************************************************************
* Copyright (C) 2004, 2008-2009 VideoLAN * Copyright (C) 2004, 2008-2010 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* Andy Gatward <a.j.gatward@reading.ac.uk> * Andy Gatward <a.j.gatward@reading.ac.uk>
...@@ -25,6 +24,7 @@ ...@@ -25,6 +24,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <sys/socket.h> #include <sys/socket.h>
...@@ -37,23 +37,52 @@ ...@@ -37,23 +37,52 @@
#include "dvblast.h" #include "dvblast.h"
#include <bitstream/mpeg/ts.h>
#include <bitstream/ietf/rtp.h>
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
static int net_Open( output_t *p_output ); struct packet_t
static void rtp_SetHdr( output_t *p_output, uint8_t *p_hdr ); {
struct packet_t *p_next;
mtime_t i_dts;
int i_depth;
block_t **pp_blocks;
/* PRIVATE - this MUST be at the end of the structure: */
block_t *p_blocks; /* actually an array of pointers */
};
static uint8_t p_pad_ts[TS_SIZE] = {
0x47, 0x1f, 0xff, 0x10, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/***************************************************************************** /*****************************************************************************
* output_Create : called from main thread * output_Create : create and insert the output_t structure
*****************************************************************************/ *****************************************************************************/
output_t *output_Create( uint8_t i_config, const char *psz_displayname, void *p_init_data ) output_t *output_Create( const output_config_t *p_config )
{ {
int i; int i;
output_t *p_output = NULL; output_t *p_output = NULL;
for ( i = 0; i < i_nb_outputs; i++ ) for ( i = 0; i < i_nb_outputs; i++ )
{ {
if ( !( pp_outputs[i]->i_config & OUTPUT_VALID ) ) if ( !( pp_outputs[i]->config.i_config & OUTPUT_VALID ) )
{ {
p_output = pp_outputs[i]; p_output = pp_outputs[i];
break; break;
...@@ -63,61 +92,106 @@ output_t *output_Create( uint8_t i_config, const char *psz_displayname, void *p_ ...@@ -63,61 +92,106 @@ output_t *output_Create( uint8_t i_config, const char *psz_displayname, void *p_
if ( p_output == NULL ) if ( p_output == NULL )
{ {
p_output = malloc( sizeof(output_t) ); p_output = malloc( sizeof(output_t) );
memset( p_output, 0, sizeof(output_t) );
i_nb_outputs++; i_nb_outputs++;
pp_outputs = realloc( pp_outputs, i_nb_outputs * sizeof(output_t *) ); pp_outputs = realloc( pp_outputs, i_nb_outputs * sizeof(output_t *) );
pp_outputs[i] = p_output; pp_outputs[i] = p_output;
} }
if ( output_Init( p_output, i_config, psz_displayname, p_init_data ) < 0 ) if ( output_Init( p_output, p_config ) < 0 )
return NULL; return NULL;
return p_output; return p_output;
} }
/***************************************************************************** /*****************************************************************************
* output_Init * output_Init : set up the output initial config
*****************************************************************************/ *****************************************************************************/
int output_Init( output_t *p_output, uint8_t i_config, const char *psz_displayname, void *p_init_data ) int output_Init( output_t *p_output, const output_config_t *p_config )
{ {
p_output->i_sid = 0; socklen_t i_sockaddr_len = (p_config->i_family == AF_INET) ?
p_output->i_depth = 0; sizeof(struct sockaddr_in) :
p_output->pi_pids = NULL; sizeof(struct sockaddr_in6);
p_output->i_nb_pids = 0;
p_output->i_nb_errors = 0; memset( p_output, 0, sizeof(output_t) );
p_output->i_cc = rand() & 0xffff; config_Init( &p_output->config );
/* Init run-time values */
p_output->p_packets = p_output->p_last_packet = NULL;
p_output->i_seqnum = rand() & 0xffff;
p_output->i_pat_cc = rand() & 0xf; p_output->i_pat_cc = rand() & 0xf;
p_output->i_pmt_cc = rand() & 0xf; p_output->i_pmt_cc = rand() & 0xf;
p_output->i_nit_cc = rand() & 0xf;
p_output->i_sdt_cc = rand() & 0xf; p_output->i_sdt_cc = rand() & 0xf;
p_output->i_eit_cc = rand() & 0xf; p_output->i_eit_cc = rand() & 0xf;
p_output->i_pat_version = rand() & 0xff; p_output->i_pat_version = rand() & 0xff;
p_output->i_pmt_version = rand() & 0xff; p_output->i_pmt_version = rand() & 0xff;
p_output->i_nit_version = rand() & 0xff;
p_output->i_sdt_version = rand() & 0xff; p_output->i_sdt_version = rand() & 0xff;
p_output->p_pat_section = NULL; p_output->p_pat_section = NULL;
p_output->p_pmt_section = NULL; p_output->p_pmt_section = NULL;
p_output->p_nit_section = NULL;
p_output->p_sdt_section = NULL; p_output->p_sdt_section = NULL;
if ( b_unique_tsid ) p_output->p_eit_ts_buffer = NULL;
p_output->i_ts_id = rand() & 0xffff; if ( b_random_tsid )
p_output->i_ref_timestamp = 0; p_output->i_tsid = rand() & 0xffff;
p_output->i_ref_wallclock = 0;
/* Init socket-related fields */
p_output->config.i_family = p_config->i_family;
memcpy( &p_output->config.connect_addr, &p_config->connect_addr,
sizeof(struct sockaddr_storage) );
memcpy( &p_output->config.bind_addr, &p_config->bind_addr,
sizeof(struct sockaddr_storage) );
p_output->config.i_if_index_v6 = p_config->i_if_index_v6;
p_output->i_handle = socket( p_config->i_family, SOCK_DGRAM, IPPROTO_UDP );
if ( p_output->i_handle < 0 )
{
msg_Err( NULL, "couldn't create socket (%s)", strerror(errno) );
p_output->config.i_config &= ~OUTPUT_VALID;
return -errno;
}
p_output->i_config = i_config; if ( p_config->bind_addr.ss_family != AF_UNSPEC )
p_output->psz_displayname = strdup( psz_displayname ); {
if ( bind( p_output->i_handle, (struct sockaddr *)&p_config->bind_addr,
i_sockaddr_len ) < 0 )
msg_Warn( NULL, "couldn't bind socket (%s)", strerror(errno) );
struct addrinfo *p_ai = (struct addrinfo *)p_init_data; if ( p_config->i_family == AF_INET )
p_output->i_addrlen = p_ai->ai_addrlen; {
p_output->p_addr = malloc( p_output->i_addrlen ); struct sockaddr_in *p_connect_addr =
memcpy( p_output->p_addr, p_ai->ai_addr, (struct sockaddr_in *)&p_output->config.connect_addr;
p_output->i_addrlen ); struct sockaddr_in *p_bind_addr =
(struct sockaddr_in *)&p_output->config.bind_addr;
if ( IN_MULTICAST( ntohl( p_connect_addr->sin_addr.s_addr ) ) )
setsockopt( p_output->i_handle, IPPROTO_IP, IP_MULTICAST_IF,
(void *)&p_bind_addr->sin_addr.s_addr,
sizeof(p_bind_addr->sin_addr.s_addr) );
}
}
if ( ( p_output->i_handle = net_Open( p_output ) ) < 0 ) if ( p_config->i_family == AF_INET6 && p_config->i_if_index_v6 != -1 )
{ {
p_output->i_config &= ~OUTPUT_VALID; struct sockaddr_in6 *p_addr =
return -1; (struct sockaddr_in6 *)&p_output->config.connect_addr;
if ( IN6_IS_ADDR_MULTICAST( p_addr->sin6_addr.s6_addr ) )
setsockopt( p_output->i_handle, IPPROTO_IPV6,
IPV6_MULTICAST_IF, (void *)&p_config->i_if_index_v6,
sizeof(p_config->i_if_index_v6) );
} }
p_output->i_config |= OUTPUT_VALID; if ( connect( p_output->i_handle,
(struct sockaddr *)&p_output->config.connect_addr,
i_sockaddr_len ) < 0 )
{
msg_Err( NULL, "couldn't connect socket (%s)", strerror(errno) );
close( p_output->i_handle );
p_output->config.i_config &= ~OUTPUT_VALID;
return -errno;
}
p_output->config.i_config |= OUTPUT_VALID;
return 0; return 0;
} }
...@@ -127,19 +201,44 @@ int output_Init( output_t *p_output, uint8_t i_config, const char *psz_displayna ...@@ -127,19 +201,44 @@ int output_Init( output_t *p_output, uint8_t i_config, const char *psz_displayna
*****************************************************************************/ *****************************************************************************/
void output_Close( output_t *p_output ) void output_Close( output_t *p_output )
{ {
packet_t *p_packet = p_output->p_packets;
while ( p_packet != NULL )
{
int i; int i;
for ( i = 0; i < p_output->i_depth; i++ ) for ( i = 0; i < p_packet->i_depth; i++ )
{ {
p_output->pp_blocks[i]->i_refcount--; p_packet->pp_blocks[i]->i_refcount--;
if ( !p_output->pp_blocks[i]->i_refcount ) if ( !p_packet->pp_blocks[i]->i_refcount )
block_Delete( p_output->pp_blocks[i] ); block_Delete( p_packet->pp_blocks[i] );
} }
p_output->p_packets = p_packet->p_next;
free( p_packet );
p_packet = p_output->p_packets;
}
p_output->p_packets = p_output->p_last_packet = NULL;
free( p_output->p_pat_section );
free( p_output->p_pmt_section );
free( p_output->p_nit_section );
free( p_output->p_sdt_section );
free( p_output->p_eit_ts_buffer );
p_output->config.i_config &= ~OUTPUT_VALID;
free( p_output->psz_displayname );
p_output->i_depth = 0;
p_output->i_config &= ~OUTPUT_VALID;
close( p_output->i_handle ); close( p_output->i_handle );
config_Free( &p_output->config );
}
/*****************************************************************************
* output_BlockCount
*****************************************************************************/
static int output_BlockCount( output_t *p_output )
{
int i_mtu = p_output->config.i_mtu;
if ( !(p_output->config.i_config & OUTPUT_UDP) )
i_mtu -= RTP_HEADER_SIZE;
return i_mtu / TS_SIZE;
} }
/***************************************************************************** /*****************************************************************************
...@@ -147,49 +246,63 @@ void output_Close( output_t *p_output ) ...@@ -147,49 +246,63 @@ void output_Close( output_t *p_output )
*****************************************************************************/ *****************************************************************************/
static void output_Flush( output_t *p_output ) static void output_Flush( output_t *p_output )
{ {
struct iovec p_iov[NB_BLOCKS + 1]; packet_t *p_packet = p_output->p_packets;
uint8_t p_rtp_hdr[RTP_SIZE]; int i_block_cnt = output_BlockCount( p_output );
int i; struct iovec p_iov[i_block_cnt + 1];
uint8_t p_rtp_hdr[RTP_HEADER_SIZE];
int i_block_cnt = ( p_output->p_addr->ss_family == AF_INET6 ) ? NB_BLOCKS_IPV6 : NB_BLOCKS; int i_iov, i_block;
int i_outblocks = i_block_cnt;
if ( !b_output_udp && !(p_output->i_config & OUTPUT_UDP) ) if ( !(p_output->config.i_config & OUTPUT_UDP) )
{ {
p_iov[0].iov_base = p_rtp_hdr; p_iov[0].iov_base = p_rtp_hdr;
p_iov[0].iov_len = sizeof(p_rtp_hdr); p_iov[0].iov_len = sizeof(p_rtp_hdr);
rtp_SetHdr( p_output, p_rtp_hdr );
for ( i = 1; i < i_block_cnt + 1; i++ ) rtp_set_hdr( p_rtp_hdr );
{ rtp_set_type( p_rtp_hdr, RTP_TYPE_TS );
p_iov[i].iov_base = p_output->pp_blocks[i - 1]->p_ts; rtp_set_seqnum( p_rtp_hdr, p_output->i_seqnum++ );
p_iov[i].iov_len = TS_SIZE; rtp_set_timestamp( p_rtp_hdr,
} p_output->i_ref_timestamp
+ (p_packet->i_dts - p_output->i_ref_wallclock)
* 9 / 100 );
rtp_set_ssrc( p_rtp_hdr, p_output->config.pi_ssrc );
i_outblocks += 1; i_iov = 1;
} }
else else
i_iov = 0;
for ( i_block = 0; i_block < p_packet->i_depth; i_block++ )
{ {
for ( i = 0; i < i_block_cnt; i++ ) p_iov[i_iov].iov_base = p_packet->pp_blocks[i_block]->p_ts;
{ p_iov[i_iov].iov_len = TS_SIZE;
p_iov[i].iov_base = p_output->pp_blocks[i]->p_ts; i_iov++;
p_iov[i].iov_len = TS_SIZE;
} }
for ( ; i_block < i_block_cnt; i_block++ )
{
p_iov[i_iov].iov_base = p_pad_ts;
p_iov[i_iov].iov_len = TS_SIZE;
i_iov++;
} }
if ( writev( p_output->i_handle, p_iov, i_outblocks ) < 0 ) if ( writev( p_output->i_handle, p_iov, i_iov ) < 0 )
{ {
msg_Err( NULL, "couldn't writev to %s (%s)", msg_Err( NULL, "couldn't writev to %s (%s)",
p_output->psz_displayname, strerror(errno) ); p_output->config.psz_displayname, strerror(errno) );
} }
/* Update the wallclock because writev() can take some time. */
i_wallclock = mdate();
for ( i = 0; i < i_block_cnt; i++ ) for ( i_block = 0; i_block < p_packet->i_depth; i_block++ )
{ {
p_output->pp_blocks[i]->i_refcount--; p_packet->pp_blocks[i_block]->i_refcount--;
if ( !p_output->pp_blocks[i]->i_refcount ) if ( !p_packet->pp_blocks[i_block]->i_refcount )
block_Delete( p_output->pp_blocks[i] ); block_Delete( p_packet->pp_blocks[i_block] );
} }
p_output->i_depth = 0; p_output->p_packets = p_packet->p_next;
free( p_packet );
if ( p_output->p_packets == NULL )
p_output->p_last_packet = NULL;
} }
/***************************************************************************** /*****************************************************************************
...@@ -197,110 +310,193 @@ static void output_Flush( output_t *p_output ) ...@@ -197,110 +310,193 @@ static void output_Flush( output_t *p_output )
*****************************************************************************/ *****************************************************************************/
void output_Put( output_t *p_output, block_t *p_block ) void output_Put( output_t *p_output, block_t *p_block )
{ {
int i_block_cnt = output_BlockCount( p_output );
packet_t *p_packet;
p_block->i_refcount++; p_block->i_refcount++;
p_output->pp_blocks[p_output->i_depth] = p_block; if ( p_output->p_last_packet != NULL
p_output->i_depth++; && p_output->p_last_packet->i_depth < i_block_cnt
&& p_output->p_last_packet->i_dts + p_output->config.i_max_retention
> p_block->i_dts )
{
p_packet = p_output->p_last_packet;
if ( ts_has_adaptation( p_block->p_ts )
&& ts_get_adaptation( p_block->p_ts )
&& tsaf_has_pcr( p_block->p_ts ) )
p_packet->i_dts = p_block->i_dts;
}
else
{
/* This isn't the cleanest allocation of the world. */
p_packet = malloc( sizeof(packet_t)
+ (i_block_cnt - 1) * sizeof(block_t *) );
p_packet->pp_blocks = &p_packet->p_blocks;
p_packet->i_depth = 0;
p_packet->p_next = NULL;
p_packet->i_dts = p_block->i_dts;
if ( p_output->p_last_packet != NULL )
p_output->p_last_packet->p_next = p_packet;
else
p_output->p_packets = p_packet;
p_output->p_last_packet = p_packet;
}
if ( ( ( p_output->p_addr->ss_family == AF_INET6 ) && p_packet->pp_blocks[p_packet->i_depth] = p_block;
( p_output->i_depth >= NB_BLOCKS_IPV6 ) ) || p_packet->i_depth++;
( ( p_output->p_addr->ss_family == AF_INET ) &&
( p_output->i_depth >= NB_BLOCKS ) ) )
output_Flush( p_output );
} }
/***************************************************************************** /*****************************************************************************
* net_Open * output_Send : called from main to flush the queues when needed
*****************************************************************************/ *****************************************************************************/
static int net_Open( output_t *p_output ) mtime_t output_Send( void )
{ {
int i_handle = socket( p_output->p_addr->ss_family, SOCK_DGRAM, IPPROTO_UDP ); mtime_t i_earliest_dts = -1;
int i;
if ( i_handle < 0 ) if ( output_dup.config.i_config & OUTPUT_VALID )
{ {
msg_Err( NULL, "couldn't create socket for %s (%s)", while ( output_dup.p_packets != NULL
p_output->psz_displayname, strerror(errno) ); && output_dup.p_packets->i_dts
return -errno; + output_dup.config.i_output_latency <= i_wallclock )
output_Flush( &output_dup );
if ( output_dup.p_packets != NULL )
i_earliest_dts = output_dup.p_packets->i_dts;
} }
if ( p_output->p_addr->ss_family == AF_INET6 ) for ( i = 0; i < i_nb_outputs; i++ )
{
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)p_output->p_addr;
if ( IN6_IS_ADDR_MULTICAST( addr->sin6_addr.s6_addr ) )
{ {
int i = i_ttl; output_t *p_output = pp_outputs[i];
setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, if ( !( p_output->config.i_config & OUTPUT_VALID ) )
(void *)&i, sizeof(i) ); continue;
while ( p_output->p_packets != NULL
&& p_output->p_packets->i_dts
+ p_output->config.i_output_latency <= i_wallclock )
output_Flush( p_output );
if ( p_output->p_packets != NULL
&& (p_output->p_packets->i_dts
+ p_output->config.i_output_latency < i_earliest_dts
|| i_earliest_dts == -1) )
i_earliest_dts = p_output->p_packets->i_dts
+ p_output->config.i_output_latency;
} }
return i_earliest_dts == -1 ? -1 : i_earliest_dts - i_wallclock;
}
/*****************************************************************************
* output_Find : find an existing output from a given output_config_t
*****************************************************************************/
output_t *output_Find( const output_config_t *p_config )
{
socklen_t i_sockaddr_len = (p_config->i_family == AF_INET) ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6);
int i;
for ( i = 0; i < i_nb_outputs; i++ )
{
output_t *p_output = pp_outputs[i];
if ( !(p_output->config.i_config & OUTPUT_VALID) ) continue;
if ( p_config->i_family != p_output->config.i_family ||
memcmp( &p_config->connect_addr, &p_output->config.connect_addr,
i_sockaddr_len ) ||
memcmp( &p_config->bind_addr, &p_output->config.bind_addr,
i_sockaddr_len ) )
continue;
if ( p_config->i_family == AF_INET6 &&
p_config->i_if_index_v6 != p_output->config.i_if_index_v6 )
continue;
return p_output;
} }
else if ( p_output->p_addr->ss_family == AF_INET )
return NULL;
}
/*****************************************************************************
* output_Change : get changes from a new output_config_t
*****************************************************************************/
void output_Change( output_t *p_output, const output_config_t *p_config )
{
memcpy( p_output->config.pi_ssrc, p_config->pi_ssrc, 4 * sizeof(uint8_t) );
p_output->config.i_output_latency = p_config->i_output_latency;
p_output->config.i_max_retention = p_config->i_max_retention;
if ( p_output->config.i_ttl != p_config->i_ttl )
{ {
struct sockaddr_in *addr = (struct sockaddr_in *)p_output->p_addr; if ( p_output->config.i_family == AF_INET6 )
if ( IN_MULTICAST( ntohl( addr->sin_addr.s_addr ) ) )
{ {
int i = i_ttl; struct sockaddr_in6 *p_addr =
setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_TTL, (struct sockaddr_in6 *)&p_output->config.connect_addr;
(void *)&i, sizeof(i) ); if ( IN6_IS_ADDR_MULTICAST( p_addr->sin6_addr.s6_addr ) )
setsockopt( p_output->i_handle, IPPROTO_IPV6,
IPV6_MULTICAST_HOPS, (void *)&p_config->i_ttl,
sizeof(p_config->i_ttl) );
} }
else
{
struct sockaddr_in *p_addr =
(struct sockaddr_in *)&p_output->config.connect_addr;
if ( IN_MULTICAST( ntohl( p_addr->sin_addr.s_addr ) ) )
setsockopt( p_output->i_handle, IPPROTO_IP, IP_MULTICAST_TTL,
(void *)&p_config->i_ttl, sizeof(p_config->i_ttl) );
}
p_output->config.i_ttl = p_config->i_ttl;
} }
if ( connect( i_handle, (struct sockaddr *)p_output->p_addr, p_output->i_addrlen ) < 0 ) if ( p_output->config.i_tos != p_config->i_tos )
{ {
msg_Err( NULL, "couldn't connect socket to %s (%s)", if ( p_output->config.i_family == AF_INET )
p_output->psz_displayname, strerror(errno) ); setsockopt( p_output->i_handle, IPPROTO_IP, IP_TOS,
close( i_handle ); (void *)&p_config->i_tos, sizeof(p_config->i_tos) );
return -errno; p_output->config.i_tos = p_config->i_tos;
} }
return i_handle; if ( p_output->config.i_mtu != p_config->i_mtu
|| ((p_output->config.i_config ^ p_config->i_config) & OUTPUT_UDP) )
{
int i_block_cnt;
packet_t *p_packet = p_output->p_last_packet;
p_output->config.i_config &= ~OUTPUT_UDP;
p_output->config.i_config |= p_config->i_config & OUTPUT_UDP;
p_output->config.i_mtu = p_config->i_mtu;
i_block_cnt = output_BlockCount( p_output );
if ( p_packet != NULL && p_packet->i_depth < i_block_cnt )
{
p_packet = realloc( p_packet, sizeof(packet_t *)
+ (i_block_cnt - 1) * sizeof(block_t *) );
p_packet->pp_blocks = &p_packet->p_blocks;
}
}
} }
/***************************************************************************** /*****************************************************************************
* rtp_SetHdr * outputs_Close : Close all outputs and free allocated memory
*****************************************************************************/ *****************************************************************************/
/* void outputs_Close( int i_num_outputs )
* Reminder : RTP header
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static void rtp_SetHdr( output_t *p_output, uint8_t *p_hdr )
{ {
mtime_t i_timestamp; int i;
if (!p_output->i_ref_wallclock) for ( i = 0; i < i_num_outputs; i++ )
{
i_timestamp = p_output->i_ref_timestamp;
p_output->i_ref_wallclock = mdate();
}
else
{ {
i_timestamp = p_output->i_ref_timestamp output_t *p_output = pp_outputs[i];
+ (mdate() - p_output->i_ref_wallclock) * 9 / 100;
msg_Dbg( NULL, "removing %s", p_output->config.psz_displayname );
if ( p_output->p_packets )
output_Flush( p_output );
output_Close( p_output );
free( p_output );
} }
p_hdr[0] = 0x80; free( pp_outputs );
p_hdr[1] = 33;
p_hdr[2] = p_output->i_cc >> 8;
p_hdr[3] = p_output->i_cc & 0xff;
p_hdr[4] = (i_timestamp >> 24) & 0xff;
p_hdr[5] = (i_timestamp >> 16) & 0xff;
p_hdr[6] = (i_timestamp >> 8) & 0xff;
p_hdr[7] = i_timestamp & 0xff;
p_hdr[8] = ((uint8_t *)&i_ssrc)[0];
p_hdr[9] = ((uint8_t *)&i_ssrc)[1];
p_hdr[10] = ((uint8_t *)&i_ssrc)[2];
p_hdr[11] = ((uint8_t *)&i_ssrc)[3];
p_output->i_cc++;
} }
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
* udp.c: UDP input for DVBlast * udp.c: UDP input for DVBlast
***************************************************************************** *****************************************************************************
* Copyright (C) 2009 VideoLAN * Copyright (C) 2009 VideoLAN
* $Id: udp.c 9 2007-03-15 16:58:05Z cmassiot $
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
...@@ -25,6 +24,7 @@ ...@@ -25,6 +24,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -37,111 +37,344 @@ ...@@ -37,111 +37,344 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <bitstream/common.h>
#include <bitstream/ietf/rtp.h>
#include "dvblast.h" #include "dvblast.h"
/***************************************************************************** /*****************************************************************************
* Local declarations * Local declarations
*****************************************************************************/ *****************************************************************************/
#define UDP_LOCK_TIMEOUT 5000000 /* 5 s */
static int i_handle; static int i_handle;
static bool b_udp = false;
static int i_block_cnt;
static uint8_t pi_ssrc[4] = { 0, 0, 0, 0 };
static uint16_t i_seqnum = 0;
static mtime_t i_last_packet = 0;
/***************************************************************************** /*****************************************************************************
* udp_Open * udp_Open
*****************************************************************************/ *****************************************************************************/
void udp_Open( void ) void udp_Open( void )
{ {
i_handle = socket(AF_INET, SOCK_DGRAM, 0); int i_family;
struct sockaddr_in sin; struct addrinfo *p_connect_ai = NULL, *p_bind_ai;
int i_if_index = 0;
in_addr_t i_if_addr = INADDR_ANY;
int i_mtu = 0;
char *psz_bind, *psz_string = strdup( psz_udp_src );
char *psz_save = psz_string;
int i = 1; int i = 1;
/* Parse configuration. */
if ( (psz_bind = strchr( psz_string, '@' )) != NULL )
{
*psz_bind++ = '\0';
p_connect_ai = ParseNodeService( psz_string, NULL, 0 );
}
else
psz_bind = psz_string;
p_bind_ai = ParseNodeService( psz_bind, &psz_string, DEFAULT_PORT );
if ( p_bind_ai == NULL )
{
msg_Err( NULL, "couldn't parse %s", psz_bind );
exit(EXIT_FAILURE);
}
i_family = p_bind_ai->ai_family;
if ( p_connect_ai != NULL && p_connect_ai->ai_family != i_family )
{
msg_Warn( NULL, "invalid connect address" );
freeaddrinfo( p_connect_ai );
p_connect_ai = NULL;
}
while ( (psz_string = strchr( psz_string, '/' )) != NULL )
{
*psz_string++ = '\0';
#define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) ))
#define ARG_OPTION( option ) (psz_string + strlen(option))
if ( IS_OPTION("udp") )
b_udp = true;
else if ( IS_OPTION("mtu=") )
i_mtu = strtol( ARG_OPTION("mtu="), NULL, 0 );
else if ( IS_OPTION("ifindex=") )
i_if_index = strtol( ARG_OPTION("ifindex="), NULL, 0 );
else if ( IS_OPTION("ifaddr=") )
i_if_addr = inet_addr( ARG_OPTION("ifaddr=") );
else
msg_Warn( NULL, "unrecognized option %s", psz_string );
#undef IS_OPTION
#undef ARG_OPTION
}
if ( !i_mtu )
i_mtu = i_family == AF_INET6 ? DEFAULT_IPV6_MTU : DEFAULT_IPV4_MTU;
i_block_cnt = (i_mtu - (b_udp ? 0 : RTP_HEADER_SIZE)) / TS_SIZE;
/* Do stuff. */
if ( (i_handle = socket( i_family, SOCK_DGRAM, IPPROTO_UDP )) < 0 )
{
msg_Err( NULL, "couldn't create socket (%s)", strerror(errno) );
exit(EXIT_FAILURE);
}
setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR, (void *) &i, sizeof( i ) ); setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR, (void *) &i, sizeof( i ) );
/* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
* packet loss caused by scheduling problems */ * packet loss caused by scheduling problems */
i = 0x80000; i = 0x80000;
setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof( i ) );
sin.sin_family = AF_INET; setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof( i ) );
sin.sin_port = htons(i_src_port);
sin.sin_addr.s_addr = i_src_addr;
if ( bind( i_handle, (struct sockaddr *)&sin, sizeof( sin ) ) < 0 ) if ( bind( i_handle, p_bind_ai->ai_addr, p_bind_ai->ai_addrlen ) < 0 )
{ {
msg_Err( NULL, "couldn't bind\n" ); msg_Err( NULL, "couldn't bind (%s)", strerror(errno) );
close( i_handle ); close( i_handle );
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ( p_connect_ai != NULL )
{
uint16_t i_port;
if ( i_family == AF_INET6 )
i_port = ((struct sockaddr_in6 *)p_connect_ai->ai_addr)->sin6_port;
else
i_port = ((struct sockaddr_in *)p_connect_ai->ai_addr)->sin_port;
if ( i_port != 0 && connect( i_handle, p_connect_ai->ai_addr,
p_connect_ai->ai_addrlen ) < 0 )
msg_Warn( NULL, "couldn't connect socket (%s)", strerror(errno) );
}
/* Join the multicast group if the socket is a multicast address */ /* Join the multicast group if the socket is a multicast address */
if ( IN_MULTICAST( ntohl(sin.sin_addr.s_addr)) ) if ( i_family == AF_INET6 )
{ {
struct ip_mreq imr; struct sockaddr_in6 *p_addr =
(struct sockaddr_in6 *)p_bind_ai->ai_addr;
if ( IN6_IS_ADDR_MULTICAST( p_addr->sin6_addr.s6_addr ) )
{
struct ipv6_mreq imr;
imr.ipv6mr_multiaddr = p_addr->sin6_addr;
imr.ipv6mr_interface = i_if_index;
if ( i_if_addr != INADDR_ANY )
msg_Warn( NULL, "ignoring ifaddr option in IPv6" );
if ( setsockopt( i_handle, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
(char *)&imr, sizeof(struct ipv6_mreq) ) < 0 )
msg_Warn( NULL, "couldn't join multicast group (%s)",
strerror(errno) );
}
}
else
{
struct sockaddr_in *p_addr =
(struct sockaddr_in *)p_bind_ai->ai_addr;
if ( IN_MULTICAST( ntohl(p_addr->sin_addr.s_addr)) )
{
if ( p_connect_ai != NULL )
{
/* Source-specific multicast */
struct sockaddr_in *p_src =
(struct sockaddr_in *)&p_connect_ai->ai_addr;
struct ip_mreq_source imr;
imr.imr_multiaddr = p_addr->sin_addr;
imr.imr_interface.s_addr = i_if_addr;
imr.imr_sourceaddr = p_src->sin_addr;
if ( i_if_index )
msg_Warn( NULL, "ignoring ifindex option in SSM" );
imr.imr_multiaddr.s_addr = sin.sin_addr.s_addr; if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
imr.imr_interface.s_addr = INADDR_ANY; /* FIXME could be an option */ (char *)&imr, sizeof(struct ip_mreq_source) ) < 0 )
msg_Warn( NULL, "couldn't join multicast group (%s)",
strerror(errno) );
}
else if ( i_if_index )
{
/* Linux-specific interface-bound multicast */
struct ip_mreqn imr;
imr.imr_multiaddr = p_addr->sin_addr;
imr.imr_address.s_addr = i_if_addr;
imr.imr_ifindex = i_if_index;
/* Join Multicast group without source filter */
if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&imr, sizeof(struct ip_mreq) ) == -1 ) (char *)&imr, sizeof(struct ip_mreqn) ) < 0 )
msg_Warn( NULL, "couldn't join multicast group (%s)",
strerror(errno) );
}
else
{ {
msg_Err( NULL, "couldn't join multicast group" ); /* Regular multicast */
exit(EXIT_FAILURE); struct ip_mreq imr;
imr.imr_multiaddr = p_addr->sin_addr;
imr.imr_interface.s_addr = i_if_addr;
if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&imr, sizeof(struct ip_mreq) ) == -1 )
msg_Warn( NULL, "couldn't join multicast group (%s)",
strerror(errno) );
}
} }
} }
msg_Dbg( NULL, "binding socket to %s:%u in %s mode", freeaddrinfo( p_bind_ai );
inet_ntoa( sin.sin_addr ), i_src_port, if ( p_connect_ai != NULL )
b_src_rawudp ? "UDP" : "RTP" ); freeaddrinfo( p_connect_ai );
free( psz_save );
msg_Dbg( NULL, "binding socket to %s", psz_udp_src );
} }
/***************************************************************************** /*****************************************************************************
* udp_Read * udp_Read
*****************************************************************************/ *****************************************************************************/
block_t *udp_Read( void ) block_t *udp_Read( mtime_t i_poll_timeout )
{ {
int i = 0, i_len; struct pollfd pfd[2];
struct iovec p_iov[NB_BLOCKS + !b_src_rawudp]; int i_ret, i_nb_fd = 1;
pfd[0].fd = i_handle;
pfd[0].events = POLLIN;
if ( i_comm_fd != -1 )
{
pfd[1].fd = i_comm_fd;
pfd[1].events = POLLIN;
i_nb_fd++;
}
i_ret = poll( pfd, i_nb_fd, (i_poll_timeout + 999) / 1000 );
i_wallclock = mdate();
if ( i_ret < 0 )
{
if( errno != EINTR )
msg_Err( NULL, "couldn't poll from socket (%s)",
strerror(errno) );
return NULL;
}
if ( pfd[0].revents )
{
struct iovec p_iov[i_block_cnt + 1];
block_t *p_ts, **pp_current = &p_ts; block_t *p_ts, **pp_current = &p_ts;
uint8_t p_rtp_hdr[RTP_SIZE]; int i_iov, i_block;
ssize_t i_len;
uint8_t p_rtp_hdr[RTP_HEADER_SIZE];
if ( !b_src_rawudp ) if ( !i_last_packet )
{
switch (i_print_type) {
case PRINT_XML:
printf("<STATUS type=\"lock\" status=\"1\"/>\n");
break;
default:
printf("frontend has acquired lock\n" );
}
}
i_last_packet = i_wallclock;
if ( !b_udp )
{ {
/* FIXME : this is wrong if RTP header > 12 bytes */ /* FIXME : this is wrong if RTP header > 12 bytes */
p_iov[i].iov_base = p_rtp_hdr; p_iov[0].iov_base = p_rtp_hdr;
p_iov[i].iov_len = RTP_SIZE; p_iov[0].iov_len = RTP_HEADER_SIZE;
i++; i_iov = 1;
} }
else
i_iov = 0;
for ( ; i < NB_BLOCKS + !b_src_rawudp; i++ ) for ( i_block = 0; i_block < i_block_cnt; i_block++ )
{ {
*pp_current = block_New(); *pp_current = block_New();
p_iov[i].iov_base = (*pp_current)->p_ts; p_iov[i_iov].iov_base = (*pp_current)->p_ts;
p_iov[i].iov_len = TS_SIZE; p_iov[i_iov].iov_len = TS_SIZE;
pp_current = &(*pp_current)->p_next; pp_current = &(*pp_current)->p_next;
i_iov++;
} }
pp_current = &p_ts;
if ( (i_len = readv( i_handle, p_iov, NB_BLOCKS + !b_src_rawudp )) < 0 ) if ( (i_len = readv( i_handle, p_iov, i_iov )) < 0 )
{ {
msg_Err( NULL, "couldn't read from network (%s)", msg_Err( NULL, "couldn't read from network (%s)", strerror(errno) );
strerror(errno) ); goto err;
i_len = 0;
} }
if ( !b_src_rawudp )
i_len -= RTP_SIZE; if ( !b_udp )
{
uint8_t pi_new_ssrc[4];
if ( !rtp_check_hdr(p_rtp_hdr) )
msg_Warn( NULL, "invalid RTP packet received" );
if ( rtp_get_type(p_rtp_hdr) != RTP_TYPE_TS )
msg_Warn( NULL, "non-TS RTP packet received" );
rtp_get_ssrc(p_rtp_hdr, pi_new_ssrc);
if ( !memcmp( pi_ssrc, pi_new_ssrc, 4 * sizeof(uint8_t) ) )
{
if ( rtp_get_seqnum(p_rtp_hdr) != i_seqnum )
msg_Warn( NULL, "RTP discontinuity" );
}
else
{
struct in_addr addr;
memcpy( &addr.s_addr, pi_new_ssrc, 4 * sizeof(uint8_t) );
msg_Dbg( NULL, "new RTP source: %s", inet_ntoa( addr ) );
memcpy( pi_ssrc, pi_new_ssrc, 4 * sizeof(uint8_t) );
switch (i_print_type) {
case PRINT_XML:
printf("<STATUS type=\"source\" source=\"%s\"/>\n",
inet_ntoa( addr ));
break;
default:
printf("new RTP source: %s\n", inet_ntoa( addr ) );
}
}
i_seqnum = rtp_get_seqnum(p_rtp_hdr) + 1;
i_len -= RTP_HEADER_SIZE;
}
i_len /= TS_SIZE; i_len /= TS_SIZE;
pp_current = &p_ts;
while ( i_len && *pp_current ) while ( i_len && *pp_current )
{ {
pp_current = &(*pp_current)->p_next; pp_current = &(*pp_current)->p_next;
i_len--; i_len--;
} }
if ( *pp_current ) i_wallclock = mdate();
msg_Dbg( NULL, "partial buffer received" ); err:
block_DeleteChain( *pp_current ); block_DeleteChain( *pp_current );
*pp_current = NULL; *pp_current = NULL;
return p_ts; return p_ts;
}
else if ( i_last_packet && i_last_packet + UDP_LOCK_TIMEOUT < i_wallclock )
{
switch (i_print_type) {
case PRINT_XML:
printf("<STATUS type=\"lock\" status=\"0\"/>\n");
break;
default:
printf("frontend has lost lock\n" );
}
i_last_packet = 0;
}
if ( i_comm_fd != -1 && pfd[1].revents )
comm_Read();
return NULL;
} }
/* From now on these are just stubs */ /* From now on these are just stubs */
...@@ -160,3 +393,11 @@ int udp_SetFilter( uint16_t i_pid ) ...@@ -160,3 +393,11 @@ int udp_SetFilter( uint16_t i_pid )
void udp_UnsetFilter( int i_fd, uint16_t i_pid ) void udp_UnsetFilter( int i_fd, uint16_t i_pid )
{ {
} }
/*****************************************************************************
* udp_Reset:
*****************************************************************************/
void udp_Reset( void )
{
}
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
* util.c * util.c
***************************************************************************** *****************************************************************************
* Copyright (C) 2004 VideoLAN * Copyright (C) 2004 VideoLAN
* $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
...@@ -24,27 +23,31 @@ ...@@ -24,27 +23,31 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <syslog.h> #include <syslog.h>
#include "dvblast.h" #include <bitstream/mpeg/psi.h>
#define HAVE_CLOCK_NANOSLEEP #include "dvblast.h"
/***************************************************************************** /*****************************************************************************
* Local declarations * Local declarations
*****************************************************************************/ *****************************************************************************/
#define MAX_MSG 1024 #define MAX_MSG 1024
#define VERB_DBG 3 #define VERB_DBG 4
#define VERB_INFO 2 #define VERB_INFO 3
#define VERB_WARN 1 #define VERB_WARN 2
#define VERB_ERR 1
/***************************************************************************** /*****************************************************************************
* msg_Connect * msg_Connect
...@@ -88,6 +91,8 @@ void msg_Info( void *_unused, const char *psz_format, ... ) ...@@ -88,6 +91,8 @@ void msg_Info( void *_unused, const char *psz_format, ... )
*****************************************************************************/ *****************************************************************************/
void msg_Err( void *_unused, const char *psz_format, ... ) void msg_Err( void *_unused, const char *psz_format, ... )
{ {
if ( i_verbose >= VERB_ERR )
{
va_list args; va_list args;
char psz_fmt[MAX_MSG]; char psz_fmt[MAX_MSG];
va_start( args, psz_format ); va_start( args, psz_format );
...@@ -97,6 +102,7 @@ void msg_Err( void *_unused, const char *psz_format, ... ) ...@@ -97,6 +102,7 @@ void msg_Err( void *_unused, const char *psz_format, ... )
vsyslog( LOG_ERR, psz_fmt, args ); vsyslog( LOG_ERR, psz_fmt, args );
else else
vfprintf( stderr, psz_fmt, args ); vfprintf( stderr, psz_fmt, args );
}
} }
/***************************************************************************** /*****************************************************************************
...@@ -153,6 +159,23 @@ void msg_Raw( void *_unused, const char *psz_format, ... ) ...@@ -153,6 +159,23 @@ void msg_Raw( void *_unused, const char *psz_format, ... )
vfprintf( stderr, psz_fmt, args ); vfprintf( stderr, psz_fmt, args );
} }
/*****************************************************************************
* streq
*****************************************************************************/
inline bool streq(char *a, char *b) {
if (!a && b) return false;
if (!b && a) return false;
if (a == b) return true;
return strcmp(a, b) == 0 ? true : false;
}
/*****************************************************************************
* xstrdup
*****************************************************************************/
inline char * xstrdup(char *str) {
return str ? strdup(str) : NULL;
}
/***************************************************************************** /*****************************************************************************
* mdate * mdate
*****************************************************************************/ *****************************************************************************/
...@@ -244,10 +267,172 @@ void hexDump( uint8_t *p_data, uint32_t i_len ) ...@@ -244,10 +267,172 @@ void hexDump( uint8_t *p_data, uint32_t i_len )
} }
sprintf( &p_outline[53], "%16s", p_hrdata ); sprintf( &p_outline[53], "%16s", p_hrdata );
msg_Dbg( NULL, p_outline ); msg_Dbg( NULL, "%s", p_outline );
} }
free( p_hrdata ); free( p_hrdata );
free( p_outline ); free( p_outline );
} }
/*****************************************************************************
* ParseNodeService: parse a host:port string
*****************************************************************************/
struct addrinfo *ParseNodeService( char *_psz_string, char **ppsz_end,
uint16_t i_default_port )
{
int i_family = AF_INET;
char psz_port_buffer[6];
char *psz_string = strdup( _psz_string );
char *psz_node, *psz_port = NULL, *psz_end;
struct addrinfo *p_res;
struct addrinfo hint;
int i_ret;
if ( psz_string[0] == '[' )
{
i_family = AF_INET6;
psz_node = psz_string + 1;
psz_end = strchr( psz_node, ']' );
if ( psz_end == NULL )
{
msg_Warn( NULL, "invalid IPv6 address %s", _psz_string );
free( psz_string );
return NULL;
}
*psz_end++ = '\0';
}
else
{
psz_node = psz_string;
psz_end = strpbrk( psz_string, "@:,/" );
}
if ( psz_end != NULL && psz_end[0] == ':' )
{
*psz_end++ = '\0';
psz_port = psz_end;
psz_end = strpbrk( psz_port, "@:,/" );
}
if ( psz_end != NULL )
{
*psz_end = '\0';
if ( ppsz_end != NULL )
*ppsz_end = _psz_string + (psz_end - psz_string);
}
else if ( ppsz_end != NULL )
*ppsz_end = _psz_string + strlen(_psz_string);
if ( i_default_port != 0 && (psz_port == NULL || !*psz_port) )
{
sprintf( psz_port_buffer, "%u", i_default_port );
psz_port = psz_port_buffer;
}
if ( psz_node[0] == '\0' )
{
free( psz_string );
return NULL;
}
memset( &hint, 0, sizeof(hint) );
hint.ai_family = i_family;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = 0;
hint.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG;
if ( (i_ret = getaddrinfo( psz_node, psz_port, NULL, &p_res )) != 0 )
{
msg_Warn( NULL, "getaddrinfo error: %s", gai_strerror(i_ret) );
free( psz_string );
return NULL;
}
free( psz_string );
return p_res;
}
/*****************************************************************************
* psi_pack_section: return psi section
* Note: Allocates the return value. The caller must free it.
*****************************************************************************/
uint8_t *psi_pack_section( uint8_t *p_section, unsigned int *pi_size ) {
uint8_t *p_flat_section;
uint16_t psi_length = psi_get_length( p_section ) + PSI_HEADER_SIZE;
*pi_size = 0;
p_flat_section = malloc( psi_length );
if ( !p_flat_section )
return NULL;
*pi_size = psi_length;
memcpy( p_flat_section, p_section, psi_length );
return p_flat_section;
}
/*****************************************************************************
* psi_pack_sections: return psi sections as array
* Note: Allocates the return value. The caller must free it.
*****************************************************************************/
uint8_t *psi_pack_sections( uint8_t **pp_sections, unsigned int *pi_size ) {
uint8_t i_last_section;
uint8_t *p_flat_section;
unsigned int i, i_pos = 0;
if ( !psi_table_validate( pp_sections ) )
return NULL;
i_last_section = psi_table_get_lastsection( pp_sections );
/* Calculate total size */
*pi_size = 0;
for ( i = 0; i <= i_last_section; i++ )
{
uint8_t *p_section = psi_table_get_section( pp_sections, i );
*pi_size += psi_get_length( p_section ) + PSI_HEADER_SIZE;
}
p_flat_section = malloc( *pi_size );
if ( !p_flat_section )
return NULL;
for ( i = 0; i <= i_last_section; i++ )
{
uint8_t *p_section = psi_table_get_section( pp_sections, i );
uint16_t psi_length = psi_get_length( p_section ) + PSI_HEADER_SIZE;
memcpy( p_flat_section + i_pos, p_section, psi_length );
i_pos += psi_length;
}
return p_flat_section;
}
/*****************************************************************************
* psi_unpack_sections: return psi sections
* Note: Allocates psi_table, the result must be psi_table_free()'ed
*****************************************************************************/
uint8_t **psi_unpack_sections( uint8_t *p_flat_sections, unsigned int i_size ) {
uint8_t **pp_sections;
unsigned int i, i_offset = 0;
pp_sections = psi_table_allocate();
psi_table_init( pp_sections );
for ( i = 0; i < PSI_TABLE_MAX_SECTIONS; i++ ) {
uint8_t *p_section = p_flat_sections + i_offset;
uint16_t i_section_len = psi_get_length( p_section ) + PSI_HEADER_SIZE;
/* Must use allocated section not p_flat_section + offset directly! */
uint8_t *p_section_local = psi_private_allocate();
memcpy( p_section_local, p_section, i_section_len );
psi_table_section( pp_sections, p_section_local );
i_offset += i_section_len;
if ( i_offset >= i_size - 1 )
break;
}
return pp_sections;
}
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
* version.h * version.h
***************************************************************************** *****************************************************************************
* Copyright (C) 2004, 2008-2010 VideoLAN * Copyright (C) 2004, 2008-2010 VideoLAN
* $Id$
* *
* Authors: Andy Gatward <a.j.gatward@reading.ac.uk> * Authors: Andy Gatward <a.j.gatward@reading.ac.uk>
* *
...@@ -21,7 +20,6 @@ ...@@ -21,7 +20,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#define VERSION_MAJOR 1 #define VERSION_MAJOR 2
#define VERSION_MINOR 3 #define VERSION_MINOR 0
#define VERSION_REVISION 0 #define VERSION_REVISION 0
#define VERSION_EXTRA "-svn"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment