diff -Nru dvblast-1.2/asi.c dvblast-2.1.0/asi.c --- dvblast-1.2/asi.c 2009-12-22 17:09:00.000000000 +0000 +++ dvblast-2.1.0/asi.c 2012-01-03 22:06:11.000000000 +0000 @@ -2,7 +2,6 @@ * asi.c: support for Computer Modules ASI cards ***************************************************************************** * Copyright (C) 2004, 2009 VideoLAN - * $Id: asi.c 9 2007-03-15 16:58:05Z cmassiot $ * * Authors: Christophe Massiot * @@ -18,13 +17,14 @@ * * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #include #include #include #include +#include #include #include #include @@ -37,6 +37,8 @@ #include #include +#include + #include "asi.h" #include "dvblast.h" @@ -55,10 +57,12 @@ #define ASI_DEVICE "/dev/asirx%u" #define ASI_TIMESTAMPS_FILE "/sys/class/asi/asirx%u/timestamps" #define ASI_BUFSIZE_FILE "/sys/class/asi/asirx%u/bufsize" +#define ASI_LOCK_TIMEOUT 5000000 /* 5 s */ static int i_handle; static int i_bufsize; static uint8_t p_pid_filter[8192 / 8]; +static mtime_t i_last_packet = 0; /***************************************************************************** * Local helpers @@ -167,82 +171,119 @@ /***************************************************************************** * 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 iovec p_iov[i_bufsize / TS_SIZE]; - block_t *p_ts, **pp_current = &p_ts; + struct pollfd pfd[2]; + int i_ret, i_nb_fd = 1; - 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++; + } - pfd.fd = i_handle; - pfd.events = POLLIN | POLLPRI; + i_ret = poll( pfd, i_nb_fd, (i_poll_timeout + 999) / 1000 ); - if ( poll(&pfd, 1, -1) < 0 ) - { + i_wallclock = mdate(); + + if ( i_ret < 0 ) + { + if( errno != EINTR ) msg_Err( NULL, "couldn't poll from device " ASI_DEVICE " (%s)", i_asi_adapter, strerror(errno) ); - continue; - } + return NULL; + } + + if ( (pfd[0].revents & POLLPRI) ) + { + unsigned int i_val; - if ( (pfd.revents & POLLPRI) ) + if ( ioctl(i_handle, ASI_IOC_RXGETEVENTS, &i_val) < 0 ) + msg_Err( NULL, "couldn't RXGETEVENTS (%s)", strerror(errno) ); + else { - unsigned int i_val; + if ( i_val & ASI_EVENT_RX_BUFFER ) + msg_Warn( NULL, "driver receive buffer queue overrun" ); + if ( i_val & ASI_EVENT_RX_FIFO ) + msg_Warn( NULL, "onboard receive FIFO overrun" ); + if ( i_val & ASI_EVENT_RX_CARRIER ) + msg_Warn( NULL, "carrier status change" ); + if ( i_val & ASI_EVENT_RX_LOS ) + msg_Warn( NULL, "loss of packet synchronization" ); + if ( i_val & ASI_EVENT_RX_AOS ) + msg_Warn( NULL, "acquisition of packet synchronization" ); + if ( i_val & ASI_EVENT_RX_DATA ) + msg_Warn( NULL, "receive data status change" ); + } + } - if ( ioctl(i_handle, ASI_IOC_RXGETEVENTS, &i_val) < 0 ) - msg_Err( NULL, "couldn't RXGETEVENTS (%s)", strerror(errno) ); - else - { - if ( i_val & ASI_EVENT_RX_BUFFER ) - msg_Warn( NULL, "driver receive buffer queue overrun" ); - if ( i_val & ASI_EVENT_RX_FIFO ) - msg_Warn( NULL, "onboard receive FIFO overrun" ); - if ( i_val & ASI_EVENT_RX_CARRIER ) - msg_Warn( NULL, "carrier status change" ); - if ( i_val & ASI_EVENT_RX_LOS ) - msg_Warn( NULL, "loss of packet synchronization" ); - if ( i_val & ASI_EVENT_RX_AOS ) - msg_Warn( NULL, "acquisition of packet synchronization" ); - if ( i_val & ASI_EVENT_RX_DATA ) - msg_Warn( NULL, "receive data status change" ); + 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("\n"); + break; + default: + printf("frontend has acquired lock\n" ); } } + i_last_packet = i_wallclock; - if ( (pfd.revents & POLLIN) ) - break; - } + for ( i = 0; i < i_bufsize / TS_SIZE; i++ ) + { + *pp_current = block_New(); + p_iov[i].iov_base = (*pp_current)->p_ts; + p_iov[i].iov_len = TS_SIZE; + pp_current = &(*pp_current)->p_next; + } - for ( i = 0; i < i_bufsize / TS_SIZE; i++ ) - { - *pp_current = block_New(); - p_iov[i].iov_base = (*pp_current)->p_ts; - p_iov[i].iov_len = TS_SIZE; - pp_current = &(*pp_current)->p_next; - } + if ( (i_len = readv(i_handle, p_iov, i_bufsize / TS_SIZE)) < 0 ) + { + msg_Err( NULL, "couldn't read from device " ASI_DEVICE " (%s)", + i_asi_adapter, strerror(errno) ); + i_len = 0; + } + i_len /= TS_SIZE; - if ( (i_len = readv(i_handle, p_iov, i_bufsize / TS_SIZE)) < 0 ) - { - msg_Err( NULL, "couldn't read from device " ASI_DEVICE " (%s)", - i_asi_adapter, strerror(errno) ); - i_len = 0; - } - i_len /= TS_SIZE; + pp_current = &p_ts; + while ( i_len && *pp_current ) + { + pp_current = &(*pp_current)->p_next; + i_len--; + } + + if ( *pp_current ) + msg_Dbg( NULL, "partial buffer received" ); + block_DeleteChain( *pp_current ); + *pp_current = NULL; - pp_current = &p_ts; - while ( i_len && *pp_current ) + return p_ts; + } + else if ( i_last_packet && i_last_packet + ASI_LOCK_TIMEOUT < i_wallclock ) { - pp_current = &(*pp_current)->p_next; - i_len--; + switch (i_print_type) { + case PRINT_XML: + printf("\n"); + break; + default: + printf("frontend has lost lock\n" ); + } + i_last_packet = 0; } - if ( *pp_current ) - msg_Dbg( NULL, "partial buffer received" ); - block_DeleteChain( *pp_current ); - *pp_current = NULL; + if ( i_comm_fd != -1 && pfd[1].revents ) + comm_Read(); - return p_ts; + return NULL; } /***************************************************************************** @@ -251,7 +292,7 @@ int asi_SetFilter( uint16_t i_pid ) { #ifdef USE_HARDWARE_FILTERING - p_pid_filter[ i_pid / 8 ] |= (0x01 << (i_pid % 8)); + p_pid_filter[ i_pid / 8 ] |= (0x01 << (i_pid % 8)); if ( ioctl( i_handle, ASI_IOC_RXSETPF, p_pid_filter ) < 0 ) msg_Warn( NULL, "couldn't add filter on PID %u", i_pid ); @@ -267,8 +308,17 @@ void asi_UnsetFilter( int i_fd, uint16_t i_pid ) { #ifdef USE_HARDWARE_FILTERING - p_pid_filter[ i_pid / 8 ] &= ~(0x01 << (i_pid % 8)); + p_pid_filter[ i_pid / 8 ] &= ~(0x01 << (i_pid % 8)); if ( ioctl( i_handle, ASI_IOC_RXSETPF, p_pid_filter ) < 0 ) msg_Warn( NULL, "couldn't remove filter on PID %u", i_pid ); #endif } + +/***************************************************************************** + * asi_Reset + *****************************************************************************/ +void asi_Reset( void ) +{ + msg_Warn( NULL, "asi_Reset() do nothing" ); +} + diff -Nru dvblast-1.2/AUTHORS dvblast-2.1.0/AUTHORS --- dvblast-1.2/AUTHORS 2010-01-04 20:15:43.000000000 +0000 +++ dvblast-2.1.0/AUTHORS 2012-01-03 22:06:11.000000000 +0000 @@ -1,5 +1,4 @@ # Contributors to DVBlast -# $Id: AUTHORS 94 2010-01-04 20:15:43Z gatty $ # # The format of this file was inspired by the Linux kernel CREDITS file. # Authors are listed alphabetically. @@ -7,6 +6,10 @@ # 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). +N: Georgi Chorbadzhiyski +E: gf AT unixsol.org +D: numerous bug fixes and enhancements + N: Marian Ďurkovič E: md AT bts DOT sk C: md @@ -17,7 +20,16 @@ C: gatty 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 E: massiot AT via DOT ecp DOT fr C: massiot D: Most of the code + +N: Jean-Paul Saman +E: jpsaman AT videolan DOT org +C: jpsaman +D: DVB-S2 bug fixes, syslog support diff -Nru dvblast-1.2/Changelog dvblast-2.1.0/Changelog --- dvblast-1.2/Changelog 2010-02-27 14:14:39.000000000 +0000 +++ dvblast-2.1.0/Changelog 1970-01-01 00:00:00.000000000 +0000 @@ -1,505 +0,0 @@ ------------------------------------------------------------------------- -r105 | md | 2010-02-20 11:53:29 +0100 (Sat, 20 Feb 2010) | 2 lines - -Implement wating for MMI data - ------------------------------------------------------------------------- -r104 | md | 2010-02-20 10:58:57 +0100 (Sat, 20 Feb 2010) | 2 lines - -Fix endless looping with PowerCAM PRO due to protocol violation. - ------------------------------------------------------------------------- -r103 | gatty | 2010-01-26 20:46:03 +0100 (Tue, 26 Jan 2010) | 1 line - -Fix typo in Makefile ------------------------------------------------------------------------- -r102 | ivoire | 2010-01-19 22:28:52 +0100 (Tue, 19 Jan 2010) | 2 lines - -Add install and uninstall target (adapted to be used also for debian packaging) - ------------------------------------------------------------------------- -r101 | ivoire | 2010-01-18 15:42:40 +0100 (Mon, 18 Jan 2010) | 2 lines - -FIx typos in the manpage. - ------------------------------------------------------------------------- -r100 | ivoire | 2010-01-17 23:34:44 +0100 (Sun, 17 Jan 2010) | 3 lines - -Add a manpage. -This manapage is only using dvblast 1.1 options (but that's a begining). - ------------------------------------------------------------------------- -r99 | ivoire | 2010-01-10 09:32:39 +0100 (Sun, 10 Jan 2010) | 2 lines - -s/bandwith/bandwidth/ - ------------------------------------------------------------------------- -r98 | ivoire | 2010-01-10 09:10:12 +0100 (Sun, 10 Jan 2010) | 2 lines - -Add long options support. - ------------------------------------------------------------------------- -r97 | massiot | 2010-01-09 00:46:48 +0100 (Sat, 09 Jan 2010) | 2 lines - - * dvblast.c: Fix segfault when no input option is passed. - ------------------------------------------------------------------------- -r96 | massiot | 2010-01-06 17:20:38 +0100 (Wed, 06 Jan 2010) | 3 lines - - * dvblast.c: Fix segfault with buffer overflow and -d option (thanks testking !) - * ALL: Fix trailing whitespaces/tabs. - ------------------------------------------------------------------------- -r95 | gatty | 2010-01-05 10:06:22 +0100 (Tue, 05 Jan 2010) | 1 line - -Fix port numbers > 4 digits. Patch courtesy of cpulinker@yandex.ru ------------------------------------------------------------------------- -r94 | gatty | 2010-01-04 21:15:43 +0100 (Mon, 04 Jan 2010) | 1 line - -Re-UTF8 Marian Ďurkovič's name in AUTHORS and comments :) ------------------------------------------------------------------------- -r93 | gatty | 2010-01-03 14:07:23 +0100 (Sun, 03 Jan 2010) | 1 line - -Added IPv6 support for output and duplicate (-d) targets. See INSTALL file for usage info. ------------------------------------------------------------------------- -r92 | md | 2009-12-29 09:25:06 +0100 (Tue, 29 Dec 2009) | 2 lines - - * dvb.c: Simplify dvb_Open() - ------------------------------------------------------------------------- -r91 | massiot | 2009-12-28 19:23:30 +0100 (Mon, 28 Dec 2009) | 2 lines - - * INSTALL: Explain units for the options. - ------------------------------------------------------------------------- -r90 | md | 2009-12-28 16:57:04 +0100 (Mon, 28 Dec 2009) | 4 lines - - * dvb.c: Read upto 50 TS packets at once. We're in non-blocking mode, - so the kernel will deliver less than 50 automatically. - Note: one DMA cycle might provide upto 350 TS packets (64 kByte) - ------------------------------------------------------------------------- -r89 | md | 2009-12-28 11:06:01 +0100 (Mon, 28 Dec 2009) | 2 lines - - * dvb.c: Reuse allocated blocks - ------------------------------------------------------------------------- -r88 | md | 2009-12-28 10:50:38 +0100 (Mon, 28 Dec 2009) | 2 lines - - * demux.c: Make sure we reset CAM if it outputs garbage (invalid PES headers) - ------------------------------------------------------------------------- -r87 | gatty | 2009-12-27 22:03:48 +0100 (Sun, 27 Dec 2009) | 1 line - -Migrate network code to use sockaddr_in instead of in_addr_t in readiness for addition of IPv6 support ------------------------------------------------------------------------- -r86 | massiot | 2009-12-22 18:09:00 +0100 (Tue, 22 Dec 2009) | 2 lines - - * asi.c, asi.h: Support for Computer Modules ASI input cards (-A). - ------------------------------------------------------------------------- -r85 | massiot | 2009-12-22 14:19:56 +0100 (Tue, 22 Dec 2009) | 4 lines - - * dvb.c: Read from DVR in amounts of NB_OUTPUTS * 7 * 188. - This attemps at lowering the latency, at the expense of a - slightly raised CPU consumption. Please yell if you're not OK. - ------------------------------------------------------------------------- -r84 | massiot | 2009-12-22 14:17:46 +0100 (Tue, 22 Dec 2009) | 2 lines - - * util.c: Use a monotonic clock for mdate() and msleep(). - ------------------------------------------------------------------------- -r83 | massiot | 2009-12-22 10:14:19 +0100 (Tue, 22 Dec 2009) | 2 lines - - * udp.c: Actually check in this file. - ------------------------------------------------------------------------- -r82 | massiot | 2009-12-21 18:30:01 +0100 (Mon, 21 Dec 2009) | 3 lines - - * udp.c: New input for DVBlast : UDP instead of linux-dvb (-D). - * ALL: Virtualize the input functions to allow multiple input modules. - ------------------------------------------------------------------------- -r81 | massiot | 2009-12-21 15:33:39 +0100 (Mon, 21 Dec 2009) | 2 lines - - * en50221.c: Fixed BSS corruption introduced in [78]. - ------------------------------------------------------------------------- -r80 | massiot | 2009-12-21 14:11:19 +0100 (Mon, 21 Dec 2009) | 2 lines - - * dvblast.c: Add -V for printing the version only; bail out if unknown args are passed. - ------------------------------------------------------------------------- -r79 | gatty | 2009-12-21 12:25:58 +0100 (Mon, 21 Dec 2009) | 1 line - -Add version.h file to repo ------------------------------------------------------------------------- -r78 | gatty | 2009-12-21 12:23:12 +0100 (Mon, 21 Dec 2009) | 1 line - -Include version info in dvblast and dvblastctl console output. Suggestion and initial patch (reworked) by Adrien Grand. ------------------------------------------------------------------------- -r77 | ivoire | 2009-12-19 18:42:27 +0100 (Sat, 19 Dec 2009) | 2 lines - -Fix a potential memory leak. - ------------------------------------------------------------------------- -r76 | ivoire | 2009-12-19 18:14:56 +0100 (Sat, 19 Dec 2009) | 2 lines - -Use booleans to resynchronise with vlc code base. - ------------------------------------------------------------------------- -r73 | massiot | 2009-11-23 23:06:00 +0100 (Mon, 23 Nov 2009) | 2 lines - - * Update for 1.1 release. - ------------------------------------------------------------------------- -r72 | massiot | 2009-11-23 22:52:43 +0100 (Mon, 23 Nov 2009) | 2 lines - - * dvb.c: Fix C-band tuning. - ------------------------------------------------------------------------- -r70 | md | 2009-11-18 08:29:03 +0100 (Wed, 18 Nov 2009) | 2 lines - - * Changelog: Update for 1.1-release - ------------------------------------------------------------------------- -r69 | md | 2009-11-16 11:20:15 +0100 (Mon, 16 Nov 2009) | 2 lines - -* demux.c: Use PAT_PID where applicable - ------------------------------------------------------------------------- -r68 | md | 2009-11-16 11:10:03 +0100 (Mon, 16 Nov 2009) | 2 lines - -* ALL: Per-output SDT versioning - ------------------------------------------------------------------------- -r67 | massiot | 2009-11-16 00:01:38 +0100 (Mon, 16 Nov 2009) | 2 lines - - * demux.c: Print SDT to debug output, adapted from VLC by Marian. - ------------------------------------------------------------------------- -r66 | massiot | 2009-11-15 23:59:58 +0100 (Sun, 15 Nov 2009) | 2 lines - - * demux.c: Fix CAPMT update when changing SID, patch by Marian. - ------------------------------------------------------------------------- -r65 | massiot | 2009-11-15 23:57:53 +0100 (Sun, 15 Nov 2009) | 2 lines - - * ALL: Cache SDT and handle it in demux_Change, patch by Marian. - ------------------------------------------------------------------------- -r64 | massiot | 2009-11-15 23:51:52 +0100 (Sun, 15 Nov 2009) | 2 lines - - * dvb.c: Raise DVR buffer to work around brain-dead code in STB0899 driver, thanks Marian. - ------------------------------------------------------------------------- -r63 | massiot | 2009-11-07 12:28:44 +0100 (Sat, 07 Nov 2009) | 2 lines - - * ChangeLog: Prepare for revision 1.1. - ------------------------------------------------------------------------- -r62 | massiot | 2009-11-07 12:22:16 +0100 (Sat, 07 Nov 2009) | 2 lines - - * demux.c: Minor optimization of EIT handling, patch by Marian. - ------------------------------------------------------------------------- -r61 | massiot | 2009-11-07 12:16:00 +0100 (Sat, 07 Nov 2009) | 2 lines - - * dvb.c, dvblastctl.c: Simplify #ifdef's for different DVB API version, patch by Marian. - ------------------------------------------------------------------------- -r60 | massiot | 2009-11-07 12:08:22 +0100 (Sat, 07 Nov 2009) | 2 lines - - * demux.c: Pad-after instead of pad-before SI tables, patch by Marian. - ------------------------------------------------------------------------- -r59 | massiot | 2009-11-07 12:06:16 +0100 (Sat, 07 Nov 2009) | 2 lines - - * INSTALL: Detail a bit the -a switch. - ------------------------------------------------------------------------- -r58 | massiot | 2009-11-07 12:03:23 +0100 (Sat, 07 Nov 2009) | 3 lines - - * demux.c: Send dummy PAT containing no program for services which are - currently inactive, patch by Marian. - ------------------------------------------------------------------------- -r57 | massiot | 2009-11-07 11:59:10 +0100 (Sat, 07 Nov 2009) | 3 lines - - * demux.c: Correctly remove SDT entries which have been withdrawn, and - always send the EIT, patch by Marian. - ------------------------------------------------------------------------- -r56 | massiot | 2009-11-07 11:54:19 +0100 (Sat, 07 Nov 2009) | 2 lines - - * demux.c: Do not make SDT depend on PMT existence, patch by Marian. - ------------------------------------------------------------------------- -r55 | massiot | 2009-11-03 23:31:53 +0100 (Tue, 03 Nov 2009) | 2 lines - - * demux.c: Don't send EIT to outputs without a valid PAT, patch by Marian. - ------------------------------------------------------------------------- -r54 | massiot | 2009-11-03 23:30:27 +0100 (Tue, 03 Nov 2009) | 2 lines - - * demux.c: Remove redundant warning on program removal. - ------------------------------------------------------------------------- -r53 | massiot | 2009-11-03 23:27:31 +0100 (Tue, 03 Nov 2009) | 2 lines - - * demux.c: Don't send TDT to outputs without a valid PAT, patch by Marian. - ------------------------------------------------------------------------- -r52 | massiot | 2009-11-03 23:26:18 +0100 (Tue, 03 Nov 2009) | 2 lines - - * demux.c: On new PMTs, delete the CAPMT before changing ES selection, patch by Marian. - ------------------------------------------------------------------------- -r51 | massiot | 2009-11-03 23:23:57 +0100 (Tue, 03 Nov 2009) | 2 lines - - * en50221.c: Do not send empty CAPMT when no PID is selected. - ------------------------------------------------------------------------- -r50 | massiot | 2009-11-03 23:21:05 +0100 (Tue, 03 Nov 2009) | 2 lines - - * en50221.c: Fix CAPMT memory leak, patch by Marian. - ------------------------------------------------------------------------- -r49 | massiot | 2009-11-02 15:44:38 +0100 (Mon, 02 Nov 2009) | 2 lines - - * Fix documentation. - ------------------------------------------------------------------------- -r48 | massiot | 2009-11-02 15:41:46 +0100 (Mon, 02 Nov 2009) | 2 lines - - * demux.c: Correctly remove CAPMT when the program goes clear, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r47 | massiot | 2009-11-02 15:31:47 +0100 (Mon, 02 Nov 2009) | 2 lines - - * demux.c, en50221.c: Compatibility with gcc 2.95, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r46 | massiot | 2009-11-02 15:30:56 +0100 (Mon, 02 Nov 2009) | 2 lines - - * dvblastctl.c, dvb.c: Compatibility with older DVB drivers, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r45 | massiot | 2009-11-02 14:31:17 +0100 (Mon, 02 Nov 2009) | 2 lines - - * demux.c: Fix program cleanup when new PAT removes a program, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r44 | massiot | 2009-11-02 14:23:47 +0100 (Mon, 02 Nov 2009) | 2 lines - - * demux.c: Fix multiple PID refcounting, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r43 | massiot | 2009-10-30 17:51:43 +0100 (Fri, 30 Oct 2009) | 2 lines - - * dvblast.c: Add support for /udp syntax in config file, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r42 | massiot | 2009-10-30 17:43:52 +0100 (Fri, 30 Oct 2009) | 2 lines - - * demux.c: Fix invalid CAPMTs when unselecting programs, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r41 | massiot | 2009-10-30 17:03:06 +0100 (Fri, 30 Oct 2009) | 2 lines - - * en50221.c: New -W switch to support sloW CAMs. - ------------------------------------------------------------------------- -r40 | massiot | 2009-10-30 16:45:25 +0100 (Fri, 30 Oct 2009) | 2 lines - - * en50221.c: Attempt at fixing high-level CI (but can't test). - ------------------------------------------------------------------------- -r39 | massiot | 2009-10-30 16:13:40 +0100 (Fri, 30 Oct 2009) | 2 lines - - * en50221.c: Increase MAX_CASYSTEM_IDS to 64 and fix inverted logic (thanks Marian !) - ------------------------------------------------------------------------- -r38 | massiot | 2009-10-30 16:10:02 +0100 (Fri, 30 Oct 2009) | 2 lines - - * demux.c: Pass through the TDT for DVB compliance, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r37 | massiot | 2009-10-30 16:08:08 +0100 (Fri, 30 Oct 2009) | 2 lines - - * demux.c: Avoid segfault caused when EPG is not used (thanks Marian !). - ------------------------------------------------------------------------- -r36 | massiot | 2009-10-30 16:06:37 +0100 (Fri, 30 Oct 2009) | 2 lines - - * dvb.c: More fixes for people who like typing invalid values. - ------------------------------------------------------------------------- -r35 | massiot | 2009-10-30 16:04:00 +0100 (Fri, 30 Oct 2009) | 2 lines - - * More getopt() related fixes. - ------------------------------------------------------------------------- -r34 | massiot | 2009-10-24 18:44:45 +0200 (Sat, 24 Oct 2009) | 2 lines - - * en50221.c: Fix memory leak (thanks ivoire). - ------------------------------------------------------------------------- -r33 | massiot | 2009-10-24 15:06:34 +0200 (Sat, 24 Oct 2009) | 2 lines - - * dvblast.c: Fix -d description. - ------------------------------------------------------------------------- -r32 | massiot | 2009-10-24 14:17:44 +0200 (Sat, 24 Oct 2009) | 2 lines - - * dvb.c: Diseqc support (based on patch #5 with extended msleep()s). - ------------------------------------------------------------------------- -r31 | massiot | 2009-10-24 14:12:33 +0200 (Sat, 24 Oct 2009) | 2 lines - - * dvblast.c: getopt() returns int rather than (possibly unsigned) char. - ------------------------------------------------------------------------- -r30 | massiot | 2009-10-24 14:03:29 +0200 (Sat, 24 Oct 2009) | 3 lines - - * dvb.c: Increase lock timeout to 30 s, because some low symbol rate - transponders are very hard to lock. - ------------------------------------------------------------------------- -r29 | massiot | 2009-10-23 12:58:25 +0200 (Fri, 23 Oct 2009) | 2 lines - - * dvblast.c: Check if a config file has been given. - ------------------------------------------------------------------------- -r28 | massiot | 2009-10-21 00:01:26 +0200 (Wed, 21 Oct 2009) | 2 lines - - * comm.c: Early termination for anonymous sockets. - ------------------------------------------------------------------------- -r27 | massiot | 2009-10-20 21:15:04 +0200 (Tue, 20 Oct 2009) | 2 lines - - * ALL: Update Copyright information. - ------------------------------------------------------------------------- -r26 | massiot | 2009-10-20 20:44:22 +0200 (Tue, 20 Oct 2009) | 2 lines - - * Documentation: Add $Id$ keyword. - ------------------------------------------------------------------------- -r25 | massiot | 2009-10-20 20:38:44 +0200 (Tue, 20 Oct 2009) | 2 lines - - * Documentation update. - ------------------------------------------------------------------------- -r24 | massiot | 2009-10-06 22:14:50 +0200 (Tue, 06 Oct 2009) | 2 lines - - * demux.c: Randomize TSID in unique TSID mode. - ------------------------------------------------------------------------- -r23 | massiot | 2009-10-06 22:00:20 +0200 (Tue, 06 Oct 2009) | 2 lines - - * demux.c: By default preserve original TSID, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r22 | massiot | 2009-10-06 21:56:57 +0200 (Tue, 06 Oct 2009) | 2 lines - - * demux.c: Correctly handle SDT on configuration changes. - ------------------------------------------------------------------------- -r21 | massiot | 2009-10-06 21:50:53 +0200 (Tue, 06 Oct 2009) | 2 lines - - * en50221.c: Allow for larger TPDUs, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r20 | massiot | 2009-09-29 00:32:28 +0200 (Tue, 29 Sep 2009) | 2 lines - - * output.c: Avoid jitter in RTP timestamps, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r19 | massiot | 2009-09-28 22:24:11 +0200 (Mon, 28 Sep 2009) | 2 lines - - * dvb.c: Fix CAM poll timing, patch by Marian Ďurkovič. - ------------------------------------------------------------------------- -r18 | massiot | 2009-09-28 22:18:51 +0200 (Mon, 28 Sep 2009) | 2 lines - - * demux.c: do not erase EIT last_table_id, patch by Andy Gatward. - ------------------------------------------------------------------------- -r17 | massiot | 2009-08-18 11:06:05 +0200 (Tue, 18 Aug 2009) | 2 lines - - * utils.c: Shorter stderr output. - ------------------------------------------------------------------------- -r16 | massiot | 2009-08-17 20:23:32 +0200 (Mon, 17 Aug 2009) | 2 lines - - * Update distribution files. - ------------------------------------------------------------------------- -r15 | massiot | 2009-08-17 20:06:59 +0200 (Mon, 17 Aug 2009) | 2 lines - - * demux.c: Typo int the EITp/f table_id, spotted by Marian Ďurkovič. Thanks ! - ------------------------------------------------------------------------- -r14 | massiot | 2009-08-13 18:18:10 +0200 (Thu, 13 Aug 2009) | 2 lines - - * ALL: EPG pass-through support, with SDT regeneration, patch courtesy of Andy Gatward. - ------------------------------------------------------------------------- -r13 | massiot | 2009-08-13 18:17:23 +0200 (Thu, 13 Aug 2009) | 2 lines - - * dvblastctl.c: Fix a gcc warning. - ------------------------------------------------------------------------- -r12 | massiot | 2009-07-02 23:55:25 +0200 (Thu, 02 Jul 2009) | 2 lines - - * dvblast.c: Fix for getopt on unicode consoles, patch courtesy of Andy Gatward. - ------------------------------------------------------------------------- -r11 | massiot | 2009-06-09 23:55:02 +0200 (Tue, 09 Jun 2009) | 2 lines - - * dvblast.c: Also move warnings outside of the getopt loop. - ------------------------------------------------------------------------- -r10 | massiot | 2009-06-09 23:45:17 +0200 (Tue, 09 Jun 2009) | 2 lines - - * dvblast.c, util.c: Quietness patch, courtesy of Andy Gatward. - ------------------------------------------------------------------------- -r9 | massiot | 2009-06-03 22:28:23 +0200 (Wed, 03 Jun 2009) | 3 lines - - * output.c: Support for MPEG-TS over raw UDP with the -U switch, patch - courtesy of Andy Gatward. - ------------------------------------------------------------------------- -r8 | massiot | 2009-05-15 20:53:57 +0200 (Fri, 15 May 2009) | 3 lines - - * dvb.c: Add support for adapters with multiple frontend, patch courtesy - of Andy Gatward. - ------------------------------------------------------------------------- -r7 | sam | 2009-05-12 14:01:27 +0200 (Tue, 12 May 2009) | 1 line - -Minor spelling fixes. ------------------------------------------------------------------------- -r5 | massiot | 2009-05-11 16:54:00 +0200 (Mon, 11 May 2009) | 2 lines - - * dvb.c: Fine-tune the S2API code. Fix issues with some DVB-T cards. - ------------------------------------------------------------------------- -r4 | massiot | 2009-05-11 15:35:16 +0200 (Mon, 11 May 2009) | 2 lines - - * README: Better wording. - ------------------------------------------------------------------------- -r3 | massiot | 2009-05-11 15:19:16 +0200 (Mon, 11 May 2009) | 2 lines - - * dvblast: Set svn property "keyword". - ------------------------------------------------------------------------- -r1 | massiot | 2009-05-11 15:05:39 +0200 (Mon, 11 May 2009) | 2 lines - - * dvblast: Initial import of DVBlast 1.0. - ------------------------------------------------------------------------- diff -Nru dvblast-1.2/comm.c dvblast-2.1.0/comm.c --- dvblast-1.2/comm.c 2009-12-21 17:30:01.000000000 +0000 +++ dvblast-2.1.0/comm.c 2012-01-03 22:06:11.000000000 +0000 @@ -2,29 +2,21 @@ * comm.c: Handles the communication socket (linux-dvb only) ***************************************************************************** * Copyright (C) 2008 VideoLAN - * $Id: comm.c 82 2009-12-21 17:30:01Z massiot $ * * Authors: Christophe Massiot * - * 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. + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. *****************************************************************************/ #include #include #include #include +#include #include #include #include @@ -48,8 +40,7 @@ *****************************************************************************/ void comm_Open( void ) { - int i_mask; - int i_size = 65535; + int i_size = COMM_MAX_MSG_CHUNK; struct sockaddr_un sun_server; if ( (i_comm_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) == -1 ) @@ -64,18 +55,15 @@ sun_server.sun_family = AF_UNIX; strncpy( sun_server.sun_path, psz_srv_socket, sizeof(sun_server.sun_path) ); sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0'; - i_mask = umask(077); if ( bind( i_comm_fd, (struct sockaddr *)&sun_server, SUN_LEN(&sun_server) ) < 0 ) { msg_Err( NULL, "cannot bind comm socket (%s)", strerror(errno) ); - umask( i_mask ); close( i_comm_fd ); i_comm_fd = -1; return; } - umask( i_mask ); } /***************************************************************************** @@ -88,12 +76,16 @@ ssize_t i_size, i_answer_size = 0; uint8_t p_buffer[COMM_BUFFER_SIZE], p_answer[COMM_BUFFER_SIZE]; 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, (struct sockaddr *)&sun_client, &sun_length ); 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) ); return; } @@ -111,10 +103,28 @@ 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 ) { case CMD_RELOAD: - b_hup_received = 1; + b_conf_reload = 1; i_answer = RET_OK; i_answer_size = 0; break; @@ -130,39 +140,125 @@ break; case CMD_MMI_SLOT_STATUS: - i_answer = en50221_StatusMMISlot( p_buffer + COMM_HEADER_SIZE, - i_size - COMM_HEADER_SIZE, + i_answer = en50221_StatusMMISlot( p_input, i_size - COMM_HEADER_SIZE, p_answer + COMM_HEADER_SIZE, &i_answer_size ); break; case CMD_MMI_OPEN: - i_answer = en50221_OpenMMI( p_buffer + COMM_HEADER_SIZE, - i_size - COMM_HEADER_SIZE ); + i_answer = en50221_OpenMMI( p_input, i_size - COMM_HEADER_SIZE ); break; case CMD_MMI_CLOSE: - i_answer = en50221_CloseMMI( p_buffer + COMM_HEADER_SIZE, - i_size - COMM_HEADER_SIZE ); + i_answer = en50221_CloseMMI( p_input, i_size - COMM_HEADER_SIZE ); break; case CMD_MMI_RECV: - i_answer = en50221_GetMMIObject( p_buffer + COMM_HEADER_SIZE, - i_size - COMM_HEADER_SIZE, + i_answer = en50221_GetMMIObject( p_input, i_size - COMM_HEADER_SIZE, p_answer + COMM_HEADER_SIZE, &i_answer_size ); break; - case CMD_MMI_SEND: - i_answer = en50221_SendMMIObject( p_buffer + COMM_HEADER_SIZE, - i_size - COMM_HEADER_SIZE ); + case CMD_MMI_SEND_TEXT: + case CMD_MMI_SEND_CHOICE: + i_answer = en50221_SendMMIObject( p_input, i_size - COMM_HEADER_SIZE ); break; case CMD_SHUTDOWN: - msg_Err( NULL, "shutdown via comm" ); - exit(EXIT_SUCCESS); - /* this is a bit violent, but hey, closing everything cleanly - * would do approximately the same */ + b_exit_now = 1; + i_answer = RET_OK; + i_answer_size = 0; + 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: msg_Err( NULL, "wrong command %u", i_command ); @@ -171,14 +267,32 @@ break; } + return_answer: p_answer[0] = COMM_HEADER_MAGIC; p_answer[1] = i_answer; p_answer[2] = 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 ); - if ( sendto( i_comm_fd, p_answer, i_answer_size + COMM_HEADER_SIZE, 0, - (struct sockaddr *)&sun_client, sun_length ) < 0 ) - msg_Err( NULL, "cannot send comm socket (%s)", strerror(errno) ); +#define min(a, b) (a < b ? a : b) + 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) ); + break; + } + + i_sended += i_sent; + i_to_send -= i_sent; + } while ( i_to_send > 0 ); +#undef min } diff -Nru dvblast-1.2/comm.h dvblast-2.1.0/comm.h --- dvblast-1.2/comm.h 2010-02-20 10:53:29.000000000 +0000 +++ dvblast-2.1.0/comm.h 2012-01-03 22:06:11.000000000 +0000 @@ -2,23 +2,14 @@ * comm.h ***************************************************************************** * Copyright (C) 2008 VideoLAN - * $Id: comm.h 105 2010-02-20 10:53:29Z md $ * * Authors: Christophe Massiot * - * 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. + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. *****************************************************************************/ /* DVB Card Drivers */ @@ -27,28 +18,53 @@ #include #include -#define COMM_BUFFER_SIZE 4096 -#define COMM_HEADER_SIZE 4 -#define COMM_HEADER_MAGIC 0x48 - -#define CMD_RELOAD 1 -#define CMD_SHUTDOWN 2 -#define CMD_FRONTEND_STATUS 3 -#define CMD_MMI_STATUS 4 -#define CMD_MMI_SLOT_STATUS 5 /* arg: slot */ -#define CMD_MMI_OPEN 6 /* arg: slot */ -#define CMD_MMI_CLOSE 7 /* arg: slot */ -#define CMD_MMI_RECV 8 /* arg: slot */ -#define CMD_MMI_SEND 9 /* arg: slot, en50221_mmi_object_t */ - -#define RET_OK 0 -#define RET_ERR 1 -#define RET_FRONTEND_STATUS 2 -#define RET_MMI_STATUS 3 -#define RET_MMI_SLOT_STATUS 4 -#define RET_MMI_RECV 5 -#define RET_MMI_WAIT 6 -#define RET_HUH 255 +#include + +#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 + +typedef enum { + CMD_INVALID = 0, + CMD_RELOAD = 1, + CMD_SHUTDOWN = 2, + CMD_FRONTEND_STATUS = 3, + CMD_MMI_STATUS = 4, + CMD_MMI_SLOT_STATUS = 5, /* arg: slot */ + CMD_MMI_OPEN = 6, /* arg: slot */ + 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; + +typedef enum { + RET_OK = 0, + RET_ERR = 1, + RET_FRONTEND_STATUS = 2, + RET_MMI_STATUS = 3, + RET_MMI_SLOT_STATUS = 4, + RET_MMI_RECV = 5, + 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 { @@ -77,3 +93,8 @@ uint8_t i_slot; en50221_mmi_object_t object; }; + +struct cmd_pid_info +{ + ts_pid_info_t pids[MAX_PIDS]; +}; diff -Nru dvblast-1.2/COPYING.WTFPL dvblast-2.1.0/COPYING.WTFPL --- dvblast-1.2/COPYING.WTFPL 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/COPYING.WTFPL 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + 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. diff -Nru dvblast-1.2/debian/changelog dvblast-2.1.0/debian/changelog --- dvblast-1.2/debian/changelog 2011-06-03 07:17:02.000000000 +0000 +++ dvblast-2.1.0/debian/changelog 2012-02-13 18:39:35.000000000 +0000 @@ -1,3 +1,13 @@ +dvblast (2.1.0-1) unstable; urgency=low + + * Team upload. + * New upstream release. + * Drop 02_fix_typo_in_manpage.diff patch, applied upstream. + * Refresh 01_remove_bad_cflags.diff patch. + * Replace libdvbpsi-dev with libbitstream-dev in the Depends field. + + -- Alessio Treglia Mon, 13 Feb 2012 19:39:28 +0100 + dvblast (1.2-2) unstable; urgency=low [ Alessio Treglia ] diff -Nru dvblast-1.2/debian/control dvblast-2.1.0/debian/control --- dvblast-1.2/debian/control 2011-05-17 10:40:54.000000000 +0000 +++ dvblast-2.1.0/debian/control 2011-12-28 09:09:33.000000000 +0000 @@ -6,7 +6,7 @@ Sam Hocevar (Debian packages) , Christophe Mutricy , Build-Depends: debhelper (>= 7), - libdvbpsi-dev, + libbitstream-dev, linux-libc-dev Standards-Version: 3.9.2 Homepage: http://www.videolan.org/projects/dvblast.html diff -Nru dvblast-1.2/debian/patches/01_remove_bad_cflags.diff dvblast-2.1.0/debian/patches/01_remove_bad_cflags.diff --- dvblast-1.2/debian/patches/01_remove_bad_cflags.diff 2011-05-17 10:34:50.000000000 +0000 +++ dvblast-2.1.0/debian/patches/01_remove_bad_cflags.diff 2011-12-25 11:06:32.000000000 +0000 @@ -1,14 +1,16 @@ Remove some unneeded CFLAGS. ---- dvblast-1.1.orig/Makefile 2009-05-11 15:05:39.000000000 +0200 -+++ dvblast-1.1/Makefile 2010-01-18 14:44:26.459058679 +0100 -@@ -1,9 +1,6 @@ - # DVBlast Makefile --# Customise the path of your kernel +--- + Makefile | 1 - + 1 file changed, 1 deletion(-) + +--- dvblast.orig/Makefile ++++ dvblast/Makefile +@@ -6,7 +6,6 @@ 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 += -I/usr/src/kernel/linux-2.6.29.1/include - LDLIBS += -lrt - LDLIBS_DVBLAST += -ldvbpsi -lpthread - + ifneq "$(GIT_VER)" "" + CFLAGS += -DVERSION_EXTRA=\"git-$(GIT_VER)\" + else diff -Nru dvblast-1.2/debian/patches/02_fix_typo_in_manpage.diff dvblast-2.1.0/debian/patches/02_fix_typo_in_manpage.diff --- dvblast-1.2/debian/patches/02_fix_typo_in_manpage.diff 2011-05-17 10:34:50.000000000 +0000 +++ dvblast-2.1.0/debian/patches/02_fix_typo_in_manpage.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -Fix a typo in the manpage. - ---- dvblasp-1.2.orig/dvblast.1 2010-03-01 23:28:08.700001103 +0100 -+++ dvblasp-1.2/dvblast.1 2010-03-01 23:28:20.727993551 +0100 -@@ -1,4 +1,4 @@ --.TH DVBLAST "1" "January 2010" "DVBlast 1.1" "User Commands" -+.TH DVBLAST "1" "January 2010" "DVBlast 1.2" "User Commands" - .SH NAME - DVBlast \- Simple and powerful dvb streaming application - .SH SYNOPSIS diff -Nru dvblast-1.2/debian/patches/series dvblast-2.1.0/debian/patches/series --- dvblast-1.2/debian/patches/series 2011-05-17 10:34:50.000000000 +0000 +++ dvblast-2.1.0/debian/patches/series 2011-12-25 11:07:04.000000000 +0000 @@ -1,2 +1 @@ 01_remove_bad_cflags.diff -02_fix_typo_in_manpage.diff diff -Nru dvblast-1.2/demux.c dvblast-2.1.0/demux.c --- dvblast-1.2/demux.c 2010-01-06 16:20:38.000000000 +0000 +++ dvblast-2.1.0/demux.c 2012-01-03 22:06:11.000000000 +0000 @@ -1,8 +1,7 @@ /***************************************************************************** * demux.c ***************************************************************************** - * Copyright (C) 2004, 2008-2009 VideoLAN - * $Id: demux.c 96 2010-01-06 16:20:38Z massiot $ + * Copyright (C) 2004, 2008-2011 VideoLAN * * Authors: Christophe Massiot * Andy Gatward @@ -24,8 +23,12 @@ *****************************************************************************/ #include +#include #include +#include #include +#include +#include #include #include #include @@ -33,28 +36,44 @@ #include "dvblast.h" #include "en50221.h" -#include -#include -#include -#include -#include -#include +#ifdef HAVE_ICONV +#include +#endif + +#include +#include +#include +#include +#include +#include + +extern bool b_enable_emm; +extern bool b_enable_ecm; /***************************************************************************** * Local declarations *****************************************************************************/ - -#define PAT_PID 0x00 -#define SDT_PID 0x11 -#define EIT_PID 0x12 -#define TDT_PID 0x14 +#define MIN_SECTION_FRAGMENT PSI_HEADER_SIZE_SYNTAX1 typedef struct ts_pid_t { int i_refcount; - int b_pes; - int i_last_cc; + int i_psi_refcount; + bool b_pes; + int8_t i_last_cc; int i_demux_fd; + /* b_emm is set to true when PID carries EMM packet + and should be outputed in all services */ + bool b_emm; + + /* PID info and stats */ + mtime_t i_bytes_ts; + unsigned long i_packets_passed; + ts_pid_info_t info; + + /* biTStream PSI section gathering */ + uint8_t *p_psi_buffer; + uint16_t i_psi_buffer_used; output_t **pp_outputs; int i_nb_outputs; @@ -63,50 +82,75 @@ typedef struct sid_t { uint16_t i_sid, i_pmt_pid; - dvbpsi_handle p_dvbpsi_handle; - dvbpsi_pmt_t *p_current_pmt; + uint8_t *p_current_pmt; } sid_t; -ts_pid_t p_pids[8192]; +ts_pid_t p_pids[MAX_PIDS]; static sid_t **pp_sids = NULL; static int i_nb_sids = 0; -static dvbpsi_handle p_pat_dvbpsi_handle; -static dvbpsi_handle p_sdt_dvbpsi_handle; -static dvbpsi_handle p_eit_dvbpsi_handle; -static dvbpsi_pat_t *p_current_pat = NULL; -static dvbpsi_sdt_t *p_current_sdt = NULL; +static PSI_TABLE_DECLARE(pp_current_pat_sections); +static PSI_TABLE_DECLARE(pp_next_pat_sections); +static PSI_TABLE_DECLARE(pp_current_cat_sections); +static PSI_TABLE_DECLARE(pp_next_cat_sections); +static PSI_TABLE_DECLARE(pp_current_nit_sections); +static PSI_TABLE_DECLARE(pp_next_nit_sections); +static PSI_TABLE_DECLARE(pp_current_sdt_sections); +static PSI_TABLE_DECLARE(pp_next_sdt_sections); +static mtime_t i_last_dts = -1; static int i_demux_fd; +static int i_nb_errors = 0; +static mtime_t i_last_error = 0; + +#ifdef HAVE_ICONV +static iconv_t iconv_handle = (iconv_t)-1; +#endif /***************************************************************************** * Local prototypes *****************************************************************************/ static void demux_Handle( block_t *p_ts ); +static void SetDTS( block_t *p_list ); static void SetPID( uint16_t i_pid ); +static void SetPID_EMM( uint16_t i_pid ); static void UnsetPID( uint16_t i_pid ); static void StartPID( output_t *p_output, uint16_t i_pid ); static void StopPID( output_t *p_output, uint16_t i_pid ); static void SelectPID( uint16_t i_sid, uint16_t i_pid ); static void UnselectPID( uint16_t i_sid, uint16_t i_pid ); -static void SelectPSI( uint16_t i_sid, uint16_t i_pid ); -static void UnselectPSI( uint16_t i_sid, uint16_t i_pid ); +static void SelectPMT( uint16_t i_sid, uint16_t i_pid ); +static void UnselectPMT( uint16_t i_sid, uint16_t i_pid ); static void GetPIDS( uint16_t **ppi_wanted_pids, int *pi_nb_wanted_pids, uint16_t i_sid, const uint16_t *pi_pids, int i_nb_pids ); -static int SIDIsSelected( uint16_t i_sid ); -int PIDWouldBeSelected( dvbpsi_pmt_es_t *p_es ); -static int PMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt ); -static void SendPAT( void ); -static void SendSDT( void ); -static void SendPMT( sid_t *p_sid ); +static bool SIDIsSelected( uint16_t i_sid ); +static bool PIDWouldBeSelected( uint8_t *p_es ); +static bool PMTNeedsDescrambling( uint8_t *p_pmt ); +static void FlushEIT( output_t *p_output, mtime_t i_dts ); +static void SendTDT( block_t *p_ts ); +static void SendEMM( block_t *p_ts ); static void NewPAT( output_t *p_output ); -static void NewSDT( output_t *p_output ); static void NewPMT( output_t *p_output ); -static void PATCallback( void *_unused, dvbpsi_pat_t *p_pat ); -static void SDTCallback( void *_unused, dvbpsi_sdt_t *p_sdt ); -static void PMTCallback( void *_unused, dvbpsi_pmt_t *p_pmt ); -static void PSITableCallback( void *_unused, dvbpsi_handle h_dvbpsi, - uint8_t i_table_id, uint16_t i_extension ); +static void NewNIT( output_t *p_output ); +static void NewSDT( output_t *p_output ); +static void HandlePSIPacket( uint8_t *p_ts, mtime_t i_dts ); +static const char *get_pid_desc(uint16_t i_pid, uint16_t *i_sid); + +/***************************************************************************** + * FindSID + *****************************************************************************/ +static inline sid_t *FindSID( uint16_t i_sid ) +{ + int i; + + for ( i = 0; i < i_nb_sids; i++ ) + { + sid_t *p_sid = pp_sids[i]; + if ( p_sid->i_sid == i_sid ) + return p_sid; + } + return NULL; +} /***************************************************************************** * demux_Open @@ -119,36 +163,93 @@ pf_Open(); - for ( i = 0; i < 8192; i++ ) + for ( i = 0; i < MAX_PIDS; i++ ) { p_pids[i].i_last_cc = -1; p_pids[i].i_demux_fd = -1; + psi_assemble_init( &p_pids[i].p_psi_buffer, + &p_pids[i].i_psi_buffer_used ); } if ( b_budget_mode ) i_demux_fd = pf_SetFilter(8192); - SetPID(PAT_PID); /* PAT */ - p_pat_dvbpsi_handle = dvbpsi_AttachPAT( PATCallback, NULL ); + psi_table_init( pp_current_pat_sections ); + psi_table_init( pp_next_pat_sections ); + SetPID(PAT_PID); + p_pids[PAT_PID].i_psi_refcount++; - if( b_enable_epg ) + if ( b_enable_emm ) { - SetPID(SDT_PID); /* SDT */ - p_sdt_dvbpsi_handle = dvbpsi_AttachDemux( PSITableCallback, NULL ); + psi_table_init( pp_current_cat_sections ); + psi_table_init( pp_next_cat_sections ); + SetPID_EMM(CAT_PID); + p_pids[CAT_PID].i_psi_refcount++; + } - SetPID(EIT_PID); /* EIT */ - p_eit_dvbpsi_handle = dvbpsi_AttachDemux( PSITableCallback, NULL ); + SetPID(NIT_PID); + p_pids[NIT_PID].i_psi_refcount++; - SetPID(TDT_PID); /* TDT */ + psi_table_init( pp_current_sdt_sections ); + psi_table_init( pp_next_sdt_sections ); + SetPID(SDT_PID); + p_pids[SDT_PID].i_psi_refcount++; + + SetPID(EIT_PID); + p_pids[EIT_PID].i_psi_refcount++; + + SetPID(RST_PID); + + SetPID(TDT_PID); +} + +/***************************************************************************** + * demux_Close + *****************************************************************************/ +void demux_Close( void ) +{ + int i, j; + + psi_table_free( pp_current_pat_sections ); + psi_table_free( pp_next_pat_sections ); + psi_table_free( pp_current_cat_sections ); + psi_table_free( pp_next_cat_sections ); + psi_table_free( pp_current_nit_sections ); + psi_table_free( pp_next_nit_sections ); + psi_table_free( pp_current_sdt_sections ); + psi_table_free( pp_next_sdt_sections ); + + for ( i = 0; i < MAX_PIDS; i++ ) + { + free( p_pids[i].p_psi_buffer ); + for ( j = 0; j < p_pids[i].i_nb_outputs; j++ ) + { + free( p_pids[i].pp_outputs ); + } + } + + for ( i = 0; i < i_nb_sids; i++ ) + { + sid_t *p_sid = pp_sids[i]; + free( p_sid->p_current_pmt ); + free( p_sid ); + } + free( pp_sids ); + +#ifdef HAVE_ICONV + if (iconv_handle != (iconv_t)-1) { + iconv_close(iconv_handle); + iconv_handle = (iconv_t)-1; } +#endif } /***************************************************************************** * demux_Run *****************************************************************************/ -void demux_Run( void ) +void demux_Run( block_t *p_ts ) { - block_t *p_ts = pf_Read(); + SetDTS( p_ts ); while ( p_ts != NULL ) { @@ -164,108 +265,169 @@ *****************************************************************************/ static void demux_Handle( block_t *p_ts ) { - mtime_t i_wallclock = mdate(); - uint16_t i_pid = block_GetPID( p_ts ); - uint8_t i_cc = block_GetCC( p_ts ); + uint16_t i_pid = ts_get_pid( p_ts->p_ts ); + uint8_t i_cc = ts_get_cc( p_ts->p_ts ); int i; - if ( block_GetSync( p_ts ) != 0x47 ) + if ( !ts_validate( p_ts->p_ts ) ) { - msg_Warn( NULL, "invalid sync (0x%x)", p_ts->p_ts[0] ); + msg_Warn( NULL, "lost TS sync" ); + switch ( i_print_type ) + { + case PRINT_XML: + printf("\n"); + break; + case PRINT_TEXT: + printf("lost TS sync"); + break; + default: + break; + } + block_Delete( p_ts ); return; } - if ( i_pid != PADDING_PID && p_pids[i_pid].i_last_cc != -1 - && p_pids[i_pid].i_last_cc != i_cc /* dup */ - && (p_pids[i_pid].i_last_cc + 17 - i_cc) % 16 ) - msg_Warn( NULL, "discontinuity for PID %d", i_pid ); - p_pids[i_pid].i_last_cc = i_cc; + if ( i_pid != PADDING_PID ) + p_pids[i_pid].info.i_scrambling = ts_get_scrambling( p_ts->p_ts ); + + p_pids[i_pid].info.i_last_packet_ts = i_wallclock; + p_pids[i_pid].info.i_packets++; + + p_pids[i_pid].i_packets_passed++; + + /* Calculate bytes_per_sec */ + if ( i_wallclock > p_pids[i_pid].i_bytes_ts + 1000000 ) { + p_pids[i_pid].info.i_bytes_per_sec = p_pids[i_pid].i_packets_passed * TS_SIZE; + p_pids[i_pid].i_packets_passed = 0; + p_pids[i_pid].i_bytes_ts = i_wallclock; + } - if ( block_HasTransportError( p_ts ) ) - msg_Warn( NULL, "transport_error_indicator" ); + if ( p_pids[i_pid].info.i_first_packet_ts == 0 ) + p_pids[i_pid].info.i_first_packet_ts = i_wallclock; - if ( p_pids[i_pid].i_refcount ) + if ( i_pid != PADDING_PID && p_pids[i_pid].i_last_cc != -1 + && !ts_check_duplicate( i_cc, p_pids[i_pid].i_last_cc ) + && ts_check_discontinuity( i_cc, p_pids[i_pid].i_last_cc ) ) { - if ( i_pid == PAT_PID ) - { - dvbpsi_PushPacket( p_pat_dvbpsi_handle, p_ts->p_ts ); - if ( block_UnitStart( p_ts ) ) - SendPAT(); - } - else if ( b_enable_epg && i_pid == EIT_PID ) - { - dvbpsi_PushPacket( p_eit_dvbpsi_handle, p_ts->p_ts ); - } - else if ( b_enable_epg && i_pid == SDT_PID ) - { - dvbpsi_PushPacket( p_sdt_dvbpsi_handle, p_ts->p_ts ); - if ( block_UnitStart( p_ts ) ) - SendSDT(); - } - else if ( b_enable_epg && i_pid == TDT_PID ) + unsigned int expected_cc = (p_pids[i_pid].i_last_cc + 1) & 0x0f; + uint16_t i_sid = 0; + const char *pid_desc = get_pid_desc(i_pid, &i_sid); + + p_pids[i_pid].info.i_cc_errors++; + + msg_Warn( NULL, "TS discontinuity on pid %4hu expected_cc %2u got %2u (%s, sid %d)", + i_pid, expected_cc, i_cc, pid_desc, i_sid ); + + switch ( i_print_type ) { - for ( i = 0; i < i_nb_outputs; i++ ) - { - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->p_sdt_section ) - output_Put( pp_outputs[i], p_ts ); - } + case PRINT_XML: + printf("\n", + i_pid, expected_cc, i_cc, pid_desc, i_sid ); + break; + case PRINT_TEXT: + printf("TS discontinuity (PID=%hu) (expected_cc=%u) (got_cc=%u) (PID_carries=%s) (sid=%d)", + i_pid, expected_cc, i_cc, pid_desc, i_sid ); + break; + default: + break; } - else + } + + if ( ts_get_transporterror( p_ts->p_ts ) ) + { + uint16_t i_sid = 0; + const char *pid_desc = get_pid_desc(i_pid, &i_sid); + + p_pids[i_pid].info.i_transport_errors++; + + msg_Warn( NULL, "transport_error_indicator on pid %hu (%s, sid %u)", + i_pid, pid_desc, i_sid ); + switch ( i_print_type ) { - for ( i = 0; i < i_nb_sids; i++ ) - { - if ( pp_sids[i]->i_sid && pp_sids[i]->i_pmt_pid == i_pid ) - { - dvbpsi_PushPacket( pp_sids[i]->p_dvbpsi_handle, - p_ts->p_ts ); - if ( block_UnitStart( p_ts ) ) - SendPMT( pp_sids[i] ); - } - } + case PRINT_XML: + printf("\n", + i_pid, pid_desc, i_sid ); + break; + case PRINT_TEXT: + printf("transport_error_indicator (PID=%hu) (PID_carries=%s) (sid=%u)", + i_pid, pid_desc, i_sid ); + break; + default: + break; } + + i_nb_errors++; + i_last_error = i_wallclock; + } + else if ( i_wallclock > i_last_error + WATCHDOG_WAIT ) + i_nb_errors = 0; + + if ( i_nb_errors > MAX_ERRORS ) + { + i_nb_errors = 0; + msg_Warn( NULL, + "too many transport errors, tuning again" ); + pf_Reset(); } - if ( block_HasPCR( p_ts ) ) + if ( !ts_get_transporterror( p_ts->p_ts ) ) { - mtime_t i_timestamp = block_GetPCR( p_ts ); + /* PSI parsing */ + if ( i_pid == TDT_PID || i_pid == RST_PID ) + SendTDT( p_ts ); + else if ( p_pids[i_pid].i_psi_refcount ) + HandlePSIPacket( p_ts->p_ts, p_ts->i_dts ); + + if ( b_enable_emm && p_pids[i_pid].b_emm ) + SendEMM( p_ts ); - for ( i = 0; i < i_nb_sids; i++ ) + /* PCR handling */ + if ( ts_has_adaptation( p_ts->p_ts ) + && ts_get_adaptation( p_ts->p_ts ) + && tsaf_has_pcr( p_ts->p_ts ) ) { - if ( pp_sids[i]->i_sid && pp_sids[i]->p_current_pmt != NULL - && pp_sids[i]->p_current_pmt->i_pcr_pid == i_pid ) - { - uint16_t i_sid = pp_sids[i]->i_sid; - int j; + mtime_t i_timestamp = tsaf_get_pcr( p_ts->p_ts ); + int j; - for ( j = 0; j < i_nb_outputs; j++ ) + for ( j = 0; j < i_nb_sids; j++ ) + { + sid_t *p_sid = pp_sids[j]; + if ( p_sid->i_sid && p_sid->p_current_pmt != NULL + && pmt_get_pcrpid( p_sid->p_current_pmt ) == i_pid ) { - if ( pp_outputs[j]->i_sid == i_sid ) + for ( i = 0; i < i_nb_outputs; i++ ) { - pp_outputs[j]->i_ref_timestamp = i_timestamp; - pp_outputs[j]->i_ref_wallclock = 0; + output_t *p_output = pp_outputs[i]; + if ( p_output->config.i_sid == p_sid->i_sid ) + { + p_output->i_ref_timestamp = i_timestamp; + p_output->i_ref_wallclock = p_ts->i_dts; + } } } } } } + p_pids[i_pid].i_last_cc = i_cc; + + /* Output */ for ( i = 0; i < p_pids[i_pid].i_nb_outputs; i++ ) { output_t *p_output = p_pids[i_pid].pp_outputs[i]; if ( p_output != NULL ) { - if ( i_ca_handle && (p_output->i_config & OUTPUT_WATCH) && - block_UnitStart( p_ts ) ) + if ( i_ca_handle && (p_output->config.i_config & OUTPUT_WATCH) && + ts_get_unitstart( p_ts->p_ts ) ) { uint8_t *p_payload; - if ( block_GetScrambling( p_ts ) || + if ( ts_get_scrambling( p_ts->p_ts ) || ( p_pids[i_pid].b_pes - && (p_payload = block_GetPayload( p_ts )) != NULL - && p_payload + 3 < p_ts->p_ts + TS_SIZE - && (p_payload[0] != 0 || p_payload[1] != 0 - || p_payload[2] != 1) ) ) + && (p_payload = ts_payload( p_ts->p_ts )) + 3 + < p_ts->p_ts + TS_SIZE + && !pes_validate(p_payload) ) ) { p_output->i_nb_errors++; p_output->i_last_error = i_wallclock; @@ -281,16 +443,21 @@ msg_Warn( NULL, "too many errors for stream %s, resetting", - p_output->psz_displayname ); + p_output->config.psz_displayname ); en50221_Reset(); } } output_Put( p_output, p_ts ); + + if ( p_output->p_eit_ts_buffer != NULL + && p_ts->i_dts > p_output->p_eit_ts_buffer->i_dts + + MAX_EIT_RETENTION ) + FlushEIT( p_output, p_ts->i_dts ); } } - if ( output_dup.i_config & OUTPUT_VALID ) + if ( output_dup.config.i_config & OUTPUT_VALID ) output_Put( &output_dup, p_ts ); p_ts->i_refcount--; @@ -309,127 +476,201 @@ return ( i != i_nb_pids ); } -void demux_Change( output_t *p_output, uint16_t i_sid, - uint16_t *pi_pids, int i_nb_pids ) +void demux_Change( output_t *p_output, const output_config_t *p_config ) { - int i; uint16_t *pi_wanted_pids, *pi_current_pids; int i_nb_wanted_pids, i_nb_current_pids; - uint16_t i_old_sid = p_output->i_sid; - int sid_change = ( i_sid != i_old_sid ); - int pid_change = 0; - - if ( i_sid == p_output->i_sid && i_nb_pids == p_output->i_nb_pids && - (!i_nb_pids || - !memcmp( p_output->pi_pids, pi_pids, i_nb_pids * sizeof(uint16_t) )) ) - return; /* No change */ + + uint16_t i_old_sid = p_output->config.i_sid; + uint16_t i_sid = p_config->i_sid; + uint16_t *pi_old_pids = p_output->config.pi_pids; + uint16_t *pi_pids = p_config->pi_pids; + int i_old_nb_pids = p_output->config.i_nb_pids; + int i_nb_pids = p_config->i_nb_pids; + + bool b_sid_change = i_sid != i_old_sid; + bool b_pid_change = false, b_tsid_change = false; + bool b_dvb_change = !!((p_output->config.i_config ^ p_config->i_config) + & OUTPUT_DVB); + bool b_service_name_change = + (!streq(p_output->config.psz_service_name, p_config->psz_service_name) || + !streq(p_output->config.psz_service_provider, p_config->psz_service_provider)); + int i; + + p_output->config.i_config = p_config->i_config; + + /* Change output settings related to service_name and service_provider . */ + free( p_output->config.psz_service_name ); + free( p_output->config.psz_service_provider ); + p_output->config.psz_service_name = xstrdup( p_config->psz_service_name ); + p_output->config.psz_service_provider = xstrdup( p_config->psz_service_provider ); + + if ( p_config->i_tsid != -1 && p_output->config.i_tsid != p_config->i_tsid ) + { + p_output->i_tsid = p_config->i_tsid; + b_tsid_change = true; + } + if ( p_config->i_tsid == -1 && p_output->config.i_tsid != -1 ) + { + if ( psi_table_validate(pp_current_pat_sections) && !b_random_tsid ) + p_output->i_tsid = + psi_table_get_tableidext(pp_current_pat_sections); + else + p_output->i_tsid = rand() & 0xffff; + b_tsid_change = true; + } + p_output->config.i_tsid = p_config->i_tsid; + + if ( !b_sid_change && p_config->i_nb_pids == p_output->config.i_nb_pids && + (!p_config->i_nb_pids || + !memcmp( p_output->config.pi_pids, p_config->pi_pids, + p_config->i_nb_pids * sizeof(uint16_t) )) ) + goto out_change; GetPIDS( &pi_wanted_pids, &i_nb_wanted_pids, i_sid, pi_pids, i_nb_pids ); - GetPIDS( &pi_current_pids, &i_nb_current_pids, p_output->i_sid, - p_output->pi_pids, p_output->i_nb_pids ); + GetPIDS( &pi_current_pids, &i_nb_current_pids, i_old_sid, pi_old_pids, + i_old_nb_pids ); - if ( sid_change && i_old_sid ) + if ( b_sid_change && i_old_sid ) { - p_output->i_sid = i_sid; - for ( i = 0; i < i_nb_sids; i++ ) + sid_t *p_old_sid = FindSID( i_old_sid ); + p_output->config.i_sid = p_config->i_sid; + + if ( p_old_sid != NULL ) { - if ( pp_sids[i]->i_sid == i_old_sid ) - { - UnsetPID( pp_sids[i]->i_pmt_pid ); + if ( i_sid != i_old_sid ) + UnselectPMT( i_old_sid, p_old_sid->i_pmt_pid ); - if ( i_ca_handle && !SIDIsSelected( i_old_sid ) - && pp_sids[i]->p_current_pmt != NULL - && PMTNeedsDescrambling( pp_sids[i]->p_current_pmt ) ) - en50221_DeletePMT( pp_sids[i]->p_current_pmt ); - break; - } + if ( i_ca_handle && !SIDIsSelected( i_old_sid ) + && p_old_sid->p_current_pmt != NULL + && PMTNeedsDescrambling( p_old_sid->p_current_pmt ) ) + en50221_DeletePMT( p_old_sid->p_current_pmt ); } } for ( i = 0; i < i_nb_current_pids; i++ ) { - if ( pi_current_pids[i] != EMPTY_PID && - !IsIn( pi_wanted_pids, i_nb_wanted_pids, pi_current_pids[i] ) ) + if ( !IsIn( pi_wanted_pids, i_nb_wanted_pids, pi_current_pids[i] ) ) { StopPID( p_output, pi_current_pids[i] ); - pid_change = 1; + b_pid_change = true; } } - if ( sid_change && - i_ca_handle && i_old_sid && SIDIsSelected( i_old_sid ) ) + if ( b_sid_change && i_ca_handle && i_old_sid && + SIDIsSelected( i_old_sid ) ) { - for ( i = 0; i < i_nb_sids; i++ ) - { - if ( pp_sids[i]->i_sid == i_old_sid ) - { - if ( pp_sids[i]->p_current_pmt != NULL - && PMTNeedsDescrambling( pp_sids[i]->p_current_pmt ) ) - en50221_UpdatePMT( pp_sids[i]->p_current_pmt ); - break; - } - } + sid_t *p_old_sid = FindSID( i_old_sid ); + if ( p_old_sid != NULL && p_old_sid->p_current_pmt != NULL + && PMTNeedsDescrambling( p_old_sid->p_current_pmt ) ) + en50221_UpdatePMT( p_old_sid->p_current_pmt ); } for ( i = 0; i < i_nb_wanted_pids; i++ ) { - if ( pi_wanted_pids[i] != EMPTY_PID && - !IsIn( pi_current_pids, i_nb_current_pids, pi_wanted_pids[i] ) ) + if ( !IsIn( pi_current_pids, i_nb_current_pids, pi_wanted_pids[i] ) ) { StartPID( p_output, pi_wanted_pids[i] ); - pid_change = 1; + b_pid_change = true; } } free( pi_wanted_pids ); free( pi_current_pids ); - if ( sid_change && i_sid ) + if ( b_sid_change && i_sid ) { - p_output->i_sid = i_old_sid; - for ( i = 0; i < i_nb_sids; i++ ) + sid_t *p_sid = FindSID( i_sid ); + p_output->config.i_sid = i_old_sid; + + if ( p_sid != NULL ) { - if ( pp_sids[i]->i_sid == i_sid ) - { - SetPID( pp_sids[i]->i_pmt_pid ); + if ( i_sid != i_old_sid ) + SelectPMT( i_sid, p_sid->i_pmt_pid ); - if ( i_ca_handle && !SIDIsSelected( i_sid ) - && pp_sids[i]->p_current_pmt != NULL - && PMTNeedsDescrambling( pp_sids[i]->p_current_pmt ) ) - en50221_AddPMT( pp_sids[i]->p_current_pmt ); - break; - } + if ( i_ca_handle && !SIDIsSelected( i_sid ) + && p_sid->p_current_pmt != NULL + && PMTNeedsDescrambling( p_sid->p_current_pmt ) ) + en50221_AddPMT( p_sid->p_current_pmt ); } } if ( i_ca_handle && i_sid && SIDIsSelected( i_sid ) ) { - for ( i = 0; i < i_nb_sids; i++ ) - { - if ( pp_sids[i]->i_sid == i_sid ) - { - if ( pp_sids[i]->p_current_pmt != NULL - && PMTNeedsDescrambling( pp_sids[i]->p_current_pmt ) ) - en50221_UpdatePMT( pp_sids[i]->p_current_pmt ); - break; - } - } + sid_t *p_sid = FindSID( i_sid ); + if ( p_sid != NULL && p_sid->p_current_pmt != NULL + && PMTNeedsDescrambling( p_sid->p_current_pmt ) ) + en50221_UpdatePMT( p_sid->p_current_pmt ); } - p_output->i_sid = i_sid; - free( p_output->pi_pids ); - p_output->pi_pids = malloc( sizeof(uint16_t) * i_nb_pids ); - memcpy( p_output->pi_pids, pi_pids, sizeof(uint16_t) * i_nb_pids ); - p_output->i_nb_pids = i_nb_pids; + p_output->config.i_sid = i_sid; + free( p_output->config.pi_pids ); + p_output->config.pi_pids = malloc( sizeof(uint16_t) * i_nb_pids ); + memcpy( p_output->config.pi_pids, pi_pids, sizeof(uint16_t) * i_nb_pids ); + p_output->config.i_nb_pids = i_nb_pids; - if ( sid_change ) +out_change: + if ( b_sid_change ) { NewSDT( p_output ); + NewNIT( p_output ); NewPAT( p_output ); NewPMT( p_output ); } - else if ( pid_change ) - NewPMT( p_output ); + else + { + if ( b_tsid_change ) + { + NewSDT( p_output ); + NewNIT( p_output ); + NewPAT( p_output ); + } + else if ( b_dvb_change ) + { + NewNIT( p_output ); + NewPAT( p_output ); + } + + if ( b_pid_change ) + NewPMT( p_output ); + + if ( !b_sid_change && b_service_name_change ) + NewSDT( p_output ); + } +} + +/***************************************************************************** + * SetDTS + *****************************************************************************/ +static void SetDTS( block_t *p_list ) +{ + int i_nb_ts = 0, i; + mtime_t i_duration; + block_t *p_ts = p_list; + + while ( p_ts != NULL ) + { + i_nb_ts++; + p_ts = p_ts->p_next; + } + + /* We suppose the stream is CBR, at least between two consecutive read(). + * This is especially true in budget mode */ + if ( i_last_dts == -1 ) + i_duration = 0; + else + i_duration = i_wallclock - i_last_dts; + + p_ts = p_list; + i = i_nb_ts - 1; + while ( p_ts != NULL ) + { + p_ts->i_dts = i_wallclock - i_duration * i / i_nb_ts; + i--; + p_ts = p_ts->p_next; + } + + i_last_dts = i_wallclock; } /***************************************************************************** @@ -444,6 +685,12 @@ p_pids[i_pid].i_demux_fd = pf_SetFilter( i_pid ); } +static void SetPID_EMM( uint16_t i_pid ) +{ + SetPID( i_pid ); + p_pids[i_pid].b_emm = true; +} + static void UnsetPID( uint16_t i_pid ) { p_pids[i_pid].i_refcount--; @@ -453,6 +700,7 @@ { pf_UnsetFilter( p_pids[i_pid].i_demux_fd, i_pid ); p_pids[i_pid].i_demux_fd = -1; + p_pids[i_pid].b_emm = false; } } @@ -514,8 +762,9 @@ int i; for ( i = 0; i < i_nb_outputs; i++ ) - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid - && !pp_outputs[i]->i_nb_pids ) + if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID) + && pp_outputs[i]->config.i_sid == i_sid + && !pp_outputs[i]->config.i_nb_pids ) StartPID( pp_outputs[i], i_pid ); } @@ -524,29 +773,44 @@ int i; for ( i = 0; i < i_nb_outputs; i++ ) - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid - && !pp_outputs[i]->i_nb_pids ) + if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID) + && pp_outputs[i]->config.i_sid == i_sid + && !pp_outputs[i]->config.i_nb_pids ) StopPID( pp_outputs[i], i_pid ); } /***************************************************************************** - * SelectPSI/UnselectPSI + * SelectPMT/UnselectPMT *****************************************************************************/ -static void SelectPSI( uint16_t i_sid, uint16_t i_pid ) +static void SelectPMT( uint16_t i_sid, uint16_t i_pid ) { int i; - for ( i = 0; i < i_nb_outputs; i++ ) - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid ) + p_pids[i_pid].i_psi_refcount++; + p_pids[i_pid].b_pes = false; + + if ( b_select_pmts ) + SetPID( i_pid ); + else for ( i = 0; i < i_nb_outputs; i++ ) + if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID) + && pp_outputs[i]->config.i_sid == i_sid ) SetPID( i_pid ); } -static void UnselectPSI( uint16_t i_sid, uint16_t i_pid ) +static void UnselectPMT( uint16_t i_sid, uint16_t i_pid ) { int i; - for ( i = 0; i < i_nb_outputs; i++ ) - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid ) + p_pids[i_pid].i_psi_refcount--; + if ( !p_pids[i_pid].i_psi_refcount ) + psi_assemble_reset( &p_pids[i_pid].p_psi_buffer, + &p_pids[i_pid].i_psi_buffer_used ); + + if ( b_select_pmts ) + UnsetPID( i_pid ); + else for ( i = 0; i < i_nb_outputs; i++ ) + if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID) + && pp_outputs[i]->config.i_sid == i_sid ) UnsetPID( i_pid ); } @@ -557,10 +821,11 @@ uint16_t i_sid, const uint16_t *pi_pids, int i_nb_pids ) { - dvbpsi_pmt_t *p_pmt = NULL; - uint16_t i_pmt_pid; - dvbpsi_pmt_es_t *p_es; - int i; + sid_t *p_sid; + uint8_t *p_pmt; + uint16_t i_pmt_pid, i_pcr_pid; + uint8_t *p_es; + uint8_t j; if ( i_nb_pids || i_sid == 0 ) { @@ -573,478 +838,727 @@ *pi_nb_wanted_pids = 0; *ppi_wanted_pids = NULL; - for ( i = 0; i < i_nb_sids; i++ ) - { - if ( pp_sids[i]->i_sid == i_sid ) - { - p_pmt = pp_sids[i]->p_current_pmt; - i_pmt_pid = pp_sids[i]->i_pmt_pid; - break; - } - } - - if ( p_pmt == NULL ) + p_sid = FindSID( i_sid ); + if ( p_sid == NULL ) return; - i = 0; - for ( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next ) + p_pmt = p_sid->p_current_pmt; + i_pmt_pid = p_sid->i_pmt_pid; + + i_pcr_pid = pmt_get_pcrpid( p_pmt ); + j = 0; + while ( (p_es = pmt_get_es( p_pmt, j )) != NULL ) + { + j++; if ( PIDWouldBeSelected( p_es ) ) { *ppi_wanted_pids = realloc( *ppi_wanted_pids, (*pi_nb_wanted_pids + 1) * sizeof(uint16_t) ); - (*ppi_wanted_pids)[(*pi_nb_wanted_pids)++] = p_es->i_pid; + (*ppi_wanted_pids)[(*pi_nb_wanted_pids)++] = pmtn_get_pid( p_es ); } + } - if ( p_pmt->i_pcr_pid != PADDING_PID && p_pmt->i_pcr_pid != i_pmt_pid - && !IsIn( *ppi_wanted_pids, *pi_nb_wanted_pids, p_pmt->i_pcr_pid ) ) + if ( i_pcr_pid != PADDING_PID && i_pcr_pid != i_pmt_pid + && !IsIn( *ppi_wanted_pids, *pi_nb_wanted_pids, i_pcr_pid ) ) { *ppi_wanted_pids = realloc( *ppi_wanted_pids, (*pi_nb_wanted_pids + 1) * sizeof(uint16_t) ); - (*ppi_wanted_pids)[(*pi_nb_wanted_pids)++] = p_pmt->i_pcr_pid; + (*ppi_wanted_pids)[(*pi_nb_wanted_pids)++] = i_pcr_pid; } } /***************************************************************************** - * WritePSISection + * OutputPSISection *****************************************************************************/ -static block_t *WritePSISection( dvbpsi_psi_section_t *p_section, - uint16_t i_pid, uint8_t *pi_cc ) +static void OutputPSISection( output_t *p_output, uint8_t *p_section, + uint16_t i_pid, uint8_t *pi_cc, mtime_t i_dts, + block_t **pp_ts_buffer, + uint8_t *pi_ts_buffer_offset ) { - block_t *p_block, **pp_last = &p_block; - uint32_t i_length; - uint8_t *p_data = p_section->p_data; - int b_first = 1; - - i_length = (uint32_t)( p_section->p_payload_end - p_section->p_data ) + - ( p_section->b_syntax_indicator ? 4 : 0 ); + uint16_t i_section_length = psi_get_length(p_section) + PSI_HEADER_SIZE; + uint16_t i_section_offset = 0; do { - uint32_t i_copy = i_length > (184 - b_first) ? (184 - b_first) : - i_length; - int i; - block_t *p_ts; - - p_ts = *pp_last = block_New(); - pp_last = &p_ts->p_next; - - /* write header - * 8b 0x47 sync byte - * 1b transport_error_indicator - * 1b payload_unit_start - * 1b transport_priority - * 13b pid - * 2b transport_scrambling_control - * 2b if adaptation_field 0x03 else 0x01 - * 4b continuity_counter - */ - - p_ts->p_ts[0] = 0x47; - p_ts->p_ts[1] = ( b_first ? 0x40 : 0x00 ) | ( ( i_pid >> 8 ) & 0x1f ); - p_ts->p_ts[2] = i_pid & 0xff; - p_ts->p_ts[3] = 0x10 | *pi_cc; - (*pi_cc)++; - *pi_cc &= 0xf; - - if ( b_first ) - p_ts->p_ts[4] = 0; /* pointer */ - - /* copy payload */ - memcpy( &p_ts->p_ts[4 + b_first], p_data, i_copy ); - - /* stuffing */ - for( i = 4 + b_first + i_copy; i < 188; i++ ) - p_ts->p_ts[i] = 0xff; - - b_first = 0; - i_length -= i_copy; - p_data += i_copy; - } - while ( i_length > 0 ); + block_t *p_block; + uint8_t *p; + uint8_t i_ts_offset; + bool b_append = (pp_ts_buffer != NULL && *pp_ts_buffer != NULL); + + if ( b_append ) + { + p_block = *pp_ts_buffer; + i_ts_offset = *pi_ts_buffer_offset; + } + else + { + p_block = block_New(); + p_block->i_dts = i_dts; + i_ts_offset = 0; + } + p = p_block->p_ts; + + psi_split_section( p, &i_ts_offset, p_section, &i_section_offset ); + + if ( !b_append ) + { + ts_set_pid( p, i_pid ); + ts_set_cc( p, *pi_cc ); + (*pi_cc)++; + *pi_cc &= 0xf; + } + + if ( i_section_offset == i_section_length ) + { + if ( i_ts_offset < TS_SIZE - MIN_SECTION_FRAGMENT + && pp_ts_buffer != NULL ) + { + *pp_ts_buffer = p_block; + *pi_ts_buffer_offset = i_ts_offset; + break; + } + else + psi_split_end( p, &i_ts_offset ); + } - return p_block; + p_block->i_dts = i_dts; + p_block->i_refcount--; + output_Put( p_output, p_block ); + if ( pp_ts_buffer != NULL ) + { + *pp_ts_buffer = NULL; + *pi_ts_buffer_offset = 0; + } + } + while ( i_section_offset < i_section_length ); } /***************************************************************************** * SendPAT *****************************************************************************/ -static void SendPAT( void ) +static void SendPAT( mtime_t i_dts ) { int i; for ( i = 0; i < i_nb_outputs; i++ ) { - if ( !( pp_outputs[i]->i_config & OUTPUT_VALID ) ) + output_t *p_output = pp_outputs[i]; + + if ( !(p_output->config.i_config & OUTPUT_VALID) ) continue; - if ( pp_outputs[i]->p_pat_section == NULL && - pp_outputs[i]->p_sdt_section != NULL && p_current_pat != NULL ) + if ( p_output->p_pat_section == NULL && + psi_table_validate(pp_current_pat_sections) ) { - dvbpsi_pat_t pat; - - if ( b_unique_tsid ) - dvbpsi_InitPAT( &pat, pp_outputs[i]->i_ts_id, - pp_outputs[i]->i_pat_version, 1 ); - else - dvbpsi_InitPAT( &pat, p_current_pat->i_ts_id, - pp_outputs[i]->i_pat_version, 1 ); + /* SID doesn't exist - build an empty PAT. */ + uint8_t *p; + p_output->i_pat_version++; - pp_outputs[i]->p_pat_section = dvbpsi_GenPATSections( &pat, 0 ); + p = p_output->p_pat_section = psi_allocate(); + pat_init( p ); + pat_set_length( p, 0 ); + pat_set_tsid( p, p_output->i_tsid ); + psi_set_version( p, p_output->i_pat_version ); + psi_set_current( p ); + psi_set_section( p, 0 ); + psi_set_lastsection( p, 0 ); + psi_set_crc( p_output->p_pat_section ); } - if ( pp_outputs[i]->p_pat_section != NULL ) - { - block_t *p_block; + if ( p_output->p_pat_section != NULL ) + OutputPSISection( p_output, p_output->p_pat_section, PAT_PID, + &p_output->i_pat_cc, i_dts, NULL, NULL ); + } +} - p_block = WritePSISection( pp_outputs[i]->p_pat_section, PAT_PID, - &pp_outputs[i]->i_pat_cc ); - while ( p_block != NULL ) - { - block_t *p_next = p_block->p_next; - p_block->i_refcount--; - output_Put( pp_outputs[i], p_block ); - p_block = p_next; - } - } +/***************************************************************************** + * SendPMT + *****************************************************************************/ +static void SendPMT( sid_t *p_sid, mtime_t i_dts ) +{ + 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) + && p_output->config.i_sid == p_sid->i_sid + && p_output->p_pmt_section != NULL ) + OutputPSISection( p_output, p_output->p_pmt_section, + p_sid->i_pmt_pid, &p_output->i_pmt_cc, i_dts, + NULL, NULL ); } } /***************************************************************************** - * SendSDT + * SendNIT *****************************************************************************/ -static void SendSDT( void ) +static void SendNIT( mtime_t i_dts ) { int i; for ( i = 0; i < i_nb_outputs; i++ ) { - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->p_sdt_section != NULL ) - { - block_t *p_block; + output_t *p_output = pp_outputs[i]; - p_block = WritePSISection( pp_outputs[i]->p_sdt_section, SDT_PID, - &pp_outputs[i]->i_sdt_cc ); - while ( p_block != NULL ) - { - block_t *p_next = p_block->p_next; - p_block->i_refcount--; - output_Put( pp_outputs[i], p_block ); - p_block = p_next; - } - } + if ( (p_output->config.i_config & OUTPUT_VALID) + && (p_output->config.i_config & OUTPUT_DVB) + && p_output->p_nit_section != NULL ) + OutputPSISection( p_output, p_output->p_nit_section, NIT_PID, + &p_output->i_nit_cc, i_dts, NULL, NULL ); } } /***************************************************************************** - * SendPMT + * SendSDT *****************************************************************************/ -static void SendPMT( sid_t *p_sid ) +static void SendSDT( mtime_t i_dts ) { int i; for ( i = 0; i < i_nb_outputs; i++ ) { - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == p_sid->i_sid ) - { - if ( pp_outputs[i]->p_pmt_section != NULL ) - { - block_t *p_block; + output_t *p_output = pp_outputs[i]; - p_block = WritePSISection( pp_outputs[i]->p_pmt_section, - p_sid->i_pmt_pid, - &pp_outputs[i]->i_pmt_cc ); - while ( p_block != NULL ) - { - block_t *p_next = p_block->p_next; - p_block->i_refcount--; - output_Put( pp_outputs[i], p_block ); - p_block = p_next; - } - } - } + if ( (p_output->config.i_config & OUTPUT_VALID) + && (p_output->config.i_config & OUTPUT_DVB) + && p_output->p_sdt_section != NULL ) + OutputPSISection( p_output, p_output->p_sdt_section, SDT_PID, + &p_output->i_sdt_cc, i_dts, NULL, NULL ); } } /***************************************************************************** * SendEIT *****************************************************************************/ -static void SendEIT( dvbpsi_psi_section_t *p_section, uint16_t i_sid, - uint8_t i_table_id ) +static void SendEIT( sid_t *p_sid, mtime_t i_dts, uint8_t *p_eit ) { + uint8_t i_table_id = psi_get_tableid( p_eit ); + bool b_epg = i_table_id >= EIT_TABLE_ID_SCHED_ACTUAL_FIRST && + i_table_id <= EIT_TABLE_ID_SCHED_ACTUAL_LAST; int i; - - for( i = 0; i < i_nb_outputs; i++ ) + for ( i = 0; i < i_nb_outputs; i++ ) { - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid ) - { - block_t *p_block; + output_t *p_output = pp_outputs[i]; - if( b_unique_tsid ) + if ( (p_output->config.i_config & OUTPUT_VALID) + && (p_output->config.i_config & OUTPUT_DVB) + && (!b_epg || (p_output->config.i_config & OUTPUT_EPG)) + && p_output->config.i_sid == p_sid->i_sid ) + { + if ( eit_get_tsid( p_eit ) != p_output->i_tsid ) { - p_section->p_data[8] = (pp_outputs[i]->i_ts_id >> 8) & 0xff; - p_section->p_data[9] = pp_outputs[i]->i_ts_id & 0xff; - dvbpsi_BuildPSISection( p_section ); + eit_set_tsid( p_eit, p_output->i_tsid ); + psi_set_crc( p_eit ); } - p_block = WritePSISection( p_section, - EIT_PID, - &pp_outputs[i]->i_eit_cc ); - while ( p_block != NULL ) - { - block_t *p_next = p_block->p_next; - p_block->i_refcount--; - output_Put( pp_outputs[i], p_block ); - p_block = p_next; - } + OutputPSISection( p_output, p_eit, EIT_PID, &p_output->i_eit_cc, + i_dts, &p_output->p_eit_ts_buffer, + &p_output->i_eit_ts_buffer_offset ); } } } /***************************************************************************** - * NewPAT + * FlushEIT *****************************************************************************/ -static void NewPAT( output_t *p_output ) +static void FlushEIT( output_t *p_output, mtime_t i_dts ) { - dvbpsi_pat_t pat; - dvbpsi_pat_program_t *p_program; - - if ( p_output->p_pat_section != NULL ) - dvbpsi_DeletePSISections( p_output->p_pat_section ); - p_output->p_pat_section = NULL; - p_output->i_pat_version++; + block_t *p_block = p_output->p_eit_ts_buffer; - if ( !p_output->i_sid ) return; - if ( p_current_pat == NULL ) return; + psi_split_end( p_block->p_ts, &p_output->i_eit_ts_buffer_offset ); + p_block->i_dts = i_dts; + p_block->i_refcount--; + output_Put( p_output, p_block ); + p_output->p_eit_ts_buffer = NULL; + p_output->i_eit_ts_buffer_offset = 0; +} - for( p_program = p_current_pat->p_first_program; p_program != NULL; - p_program = p_program->p_next ) - if ( p_program->i_number == p_output->i_sid ) - break; +/***************************************************************************** + * SendTDT + *****************************************************************************/ +static void SendTDT( block_t *p_ts ) +{ + int i; - if ( p_program == NULL ) return; + for ( i = 0; i < i_nb_outputs; i++ ) + { + output_t *p_output = pp_outputs[i]; - if ( b_unique_tsid ) - dvbpsi_InitPAT( &pat, p_output->i_ts_id, p_output->i_pat_version, 1 ); - else - dvbpsi_InitPAT( &pat, p_current_pat->i_ts_id, p_output->i_pat_version, 1 ); + if ( (p_output->config.i_config & OUTPUT_VALID) + && (p_output->config.i_config & OUTPUT_DVB) + && p_output->p_sdt_section != NULL ) + output_Put( p_output, p_ts ); + } +} +/***************************************************************************** + * SendEMM + *****************************************************************************/ +static void SendEMM( block_t *p_ts ) +{ + int i; - dvbpsi_PATAddProgram( &pat, p_output->i_sid, p_program->i_pid ); + for ( i = 0; i < i_nb_outputs; i++ ) + { + output_t *p_output = pp_outputs[i]; - p_output->p_pat_section = dvbpsi_GenPATSections( &pat, 0 ); - dvbpsi_EmptyPAT( &pat ); + if ( (p_output->config.i_config & OUTPUT_VALID) ) + output_Put( p_output, p_ts ); + } } /***************************************************************************** - * NewSDT + * NewPAT *****************************************************************************/ -static void NewSDT( output_t *p_output ) +static void NewPAT( output_t *p_output ) { - dvbpsi_sdt_t sdt; - dvbpsi_sdt_service_t *p_service, *p_new_service ; - dvbpsi_descriptor_t *p_descriptor; + const uint8_t *p_program; + uint8_t *p; + uint8_t k = 0; - if ( p_output->p_sdt_section != NULL ) - dvbpsi_DeletePSISections( p_output->p_sdt_section ); - p_output->p_sdt_section = NULL; - p_output->i_sdt_version++; + free( p_output->p_pat_section ); + p_output->p_pat_section = NULL; + p_output->i_pat_version++; - if ( !p_output->i_sid ) return; - if ( p_current_sdt == NULL ) return; + if ( !p_output->config.i_sid ) return; + if ( !psi_table_validate(pp_current_pat_sections) ) return; - for( p_service = p_current_sdt->p_first_service; p_service != NULL; - p_service = p_service->p_next ) - if ( p_service->i_service_id == p_output->i_sid ) - break; + p_program = pat_table_find_program( pp_current_pat_sections, + p_output->config.i_sid ); + if ( p_program == NULL ) return; - if ( p_service == NULL ) + p = p_output->p_pat_section = psi_allocate(); + pat_init( p ); + psi_set_length( p, PSI_MAX_SIZE ); + pat_set_tsid( p, p_output->i_tsid ); + psi_set_version( p, p_output->i_pat_version ); + psi_set_current( p ); + psi_set_section( p, 0 ); + psi_set_lastsection( p, 0 ); + + if ( p_output->config.i_config & OUTPUT_DVB ) + { + /* NIT */ + p = pat_get_program( p_output->p_pat_section, k++ ); + patn_init( p ); + patn_set_program( p, 0 ); + patn_set_pid( p, NIT_PID ); + } + + p = pat_get_program( p_output->p_pat_section, k++ ); + patn_init( p ); + patn_set_program( p, p_output->config.i_sid ); + patn_set_pid( p, patn_get_pid( p_program ) ); + + p = pat_get_program( p_output->p_pat_section, k ); + pat_set_length( p_output->p_pat_section, + p - p_output->p_pat_section - PAT_HEADER_SIZE ); + psi_set_crc( p_output->p_pat_section ); +} + +/***************************************************************************** + * NewPMT + *****************************************************************************/ +static void CopyDescriptors( uint8_t *p_descs, uint8_t *p_current_descs ) +{ + uint8_t *p_desc; + const uint8_t *p_current_desc; + uint16_t j = 0, k = 0; + + descs_set_length( p_descs, DESCS_MAX_SIZE ); + + while ( (p_current_desc = descs_get_desc( p_current_descs, j )) != NULL ) { - if ( p_output->p_pat_section != NULL && - p_output->p_pat_section->i_length == 9 ) - { - dvbpsi_DeletePSISections( p_output->p_pat_section ); - p_output->p_pat_section = NULL; - p_output->i_pat_version++; - } - return; - } + uint8_t i_tag = desc_get_tag( p_current_desc ); - if ( b_unique_tsid ) - dvbpsi_InitSDT( &sdt, p_output->i_ts_id, - p_output->i_sdt_version, 1, - p_current_sdt->i_network_id ); - else - dvbpsi_InitSDT( &sdt, p_current_sdt->i_ts_id, - p_output->i_sdt_version, 1, - p_current_sdt->i_network_id ); - - p_new_service = dvbpsi_SDTAddService( &sdt, - p_service->i_service_id, p_service->b_eit_schedule, - p_service->b_eit_present, p_service->i_running_status, 0 ); - - for( p_descriptor = p_service->p_first_descriptor; p_descriptor != NULL; - p_descriptor = p_descriptor->p_next ) - dvbpsi_SDTServiceAddDescriptor( p_new_service, - p_descriptor->i_tag, p_descriptor->i_length, - p_descriptor->p_data ); + j++; + if ( b_enable_ecm && i_tag == 0x9 ) continue; - p_output->p_sdt_section = dvbpsi_GenSDTSections( &sdt ); + p_desc = descs_get_desc( p_descs, k ); + if ( p_desc == NULL ) continue; /* This shouldn't happen */ + k++; + memcpy( p_desc, p_current_desc, + DESC_HEADER_SIZE + desc_get_length( p_current_desc ) ); + } - dvbpsi_EmptySDT( &sdt ); + p_desc = descs_get_desc( p_descs, k ); + if ( p_desc == NULL ) + /* This shouldn't happen if the incoming PMT is valid */ + descs_set_length( p_descs, 0 ); + else + descs_set_length( p_descs, p_desc - p_descs - DESCS_HEADER_SIZE ); } -/***************************************************************************** - * NewPMT - *****************************************************************************/ static void NewPMT( output_t *p_output ) { - dvbpsi_pmt_t pmt, *p_current_pmt; - dvbpsi_pmt_es_t *p_current_es; - dvbpsi_descriptor_t *p_dr; - int i; + sid_t *p_sid; + uint8_t *p_current_pmt; + uint8_t *p_es, *p_current_es; + uint8_t *p; + uint16_t j, k; - if ( p_output->p_pmt_section != NULL ) - dvbpsi_DeletePSISections( p_output->p_pmt_section ); + free( p_output->p_pmt_section ); p_output->p_pmt_section = NULL; p_output->i_pmt_version++; - if ( !p_output->i_sid ) return; + if ( !p_output->config.i_sid ) return; - for ( i = 0; i < i_nb_sids; i++ ) - if ( pp_sids[i]->i_sid == p_output->i_sid ) - break; + p_sid = FindSID( p_output->config.i_sid ); + if ( p_sid == NULL ) return; - if ( i == i_nb_sids ) - return; + if ( p_sid->p_current_pmt == NULL ) return; + p_current_pmt = p_sid->p_current_pmt; + + p = p_output->p_pmt_section = psi_allocate(); + pmt_init( p ); + psi_set_length( p, PSI_MAX_SIZE ); + pmt_set_program( p, p_output->config.i_sid ); + psi_set_version( p, p_output->i_pmt_version ); + psi_set_current( p ); + pmt_set_pcrpid( p, pmt_get_pcrpid( p_current_pmt ) ); + pmt_set_desclength( p, 0 ); + + CopyDescriptors( pmt_get_descs( p ), pmt_get_descs( p_current_pmt ) ); + + j = 0; k = 0; + while ( (p_current_es = pmt_get_es( p_current_pmt, j )) != NULL ) + { + uint16_t i_pid = pmtn_get_pid( p_current_es ); + + j++; + if ( (p_output->config.i_nb_pids || !PIDWouldBeSelected( p_current_es )) + && !IsIn( p_output->config.pi_pids, p_output->config.i_nb_pids, + i_pid ) ) + continue; - if ( pp_sids[i]->p_current_pmt == NULL ) return; - p_current_pmt = pp_sids[i]->p_current_pmt; + p_es = pmt_get_es( p, k ); + if ( p_es == NULL ) continue; /* This shouldn't happen */ + k++; + pmtn_init( p_es ); + pmtn_set_streamtype( p_es, pmtn_get_streamtype( p_current_es ) ); + pmtn_set_pid( p_es, i_pid ); + pmtn_set_desclength( p_es, 0 ); + + CopyDescriptors( pmtn_get_descs( p_es ), + pmtn_get_descs( p_current_es ) ); + } + + p_es = pmt_get_es( p, k ); + if ( p_es == NULL ) + /* This shouldn't happen if the incoming PMT is valid */ + pmt_set_length( p, 0 ); + else + pmt_set_length( p, p_es - p - PMT_HEADER_SIZE ); + psi_set_crc( p ); +} - dvbpsi_InitPMT( &pmt, p_output->i_sid, p_output->i_pmt_version, 1, - p_current_pmt->i_pcr_pid ); +/***************************************************************************** + * NewNIT + *****************************************************************************/ +static void NewNIT( output_t *p_output ) +{ + uint8_t *p_ts; + uint8_t *p_header2; + uint8_t *p; + + free( p_output->p_nit_section ); + p_output->p_nit_section = NULL; + p_output->i_nit_version++; + + p = p_output->p_nit_section = psi_allocate(); + nit_init( p, true ); + nit_set_length( p, PSI_MAX_SIZE ); + nit_set_nid( p, i_network_id ); + psi_set_version( p, p_output->i_nit_version ); + psi_set_current( p ); + psi_set_section( p, 0 ); + psi_set_lastsection( p, 0 ); + + if ( p_network_name != NULL ) + { + uint8_t *p_descs; + uint8_t *p_desc; + nit_set_desclength( p, DESCS_MAX_SIZE ); + p_descs = nit_get_descs( p ); + p_desc = descs_get_desc( p_descs, 0 ); + desc40_init( p_desc ); + desc40_set_networkname( p_desc, p_network_name, i_network_name_size ); + p_desc = descs_get_desc( p_descs, 1 ); + descs_set_length( p_descs, p_desc - p_descs - DESCS_HEADER_SIZE ); + } + else + nit_set_desclength( p, 0 ); - for ( p_dr = p_current_pmt->p_first_descriptor; p_dr != NULL; - p_dr = p_dr->p_next ) - dvbpsi_PMTAddDescriptor( &pmt, p_dr->i_tag, p_dr->i_length, - p_dr->p_data ); + p_header2 = nit_get_header2( p ); + nith_init( p_header2 ); + nith_set_tslength( p_header2, NIT_TS_SIZE ); + + p_ts = nit_get_ts( p, 0 ); + nitn_init( p_ts ); + nitn_set_tsid( p_ts, p_output->i_tsid ); + nitn_set_onid( p_ts, i_network_id ); + nitn_set_desclength( p_ts, 0 ); + + p_ts = nit_get_ts( p, 1 ); + if ( p_ts == NULL ) + /* This shouldn't happen */ + nit_set_length( p, 0 ); + else + nit_set_length( p, p_ts - p - NIT_HEADER_SIZE ); + psi_set_crc( p_output->p_nit_section ); +} - for( p_current_es = p_current_pmt->p_first_es; p_current_es != NULL; - p_current_es = p_current_es->p_next ) +/***************************************************************************** + * NewSDT + *****************************************************************************/ +static void NewSDT( output_t *p_output ) +{ + uint8_t *p_service, *p_current_service; + uint8_t *p; + + free( p_output->p_sdt_section ); + p_output->p_sdt_section = NULL; + p_output->i_sdt_version++; + + if ( !p_output->config.i_sid ) return; + if ( !psi_table_validate(pp_current_sdt_sections) ) return; + + p_current_service = sdt_table_find_service( pp_current_sdt_sections, + p_output->config.i_sid ); + + if ( p_current_service == NULL ) { - if ( (!p_output->i_nb_pids && PIDWouldBeSelected( p_current_es )) - || IsIn( p_output->pi_pids, p_output->i_nb_pids, - p_current_es->i_pid ) ) + if ( p_output->p_pat_section != NULL && + pat_get_program( p_output->p_pat_section, 0 ) == NULL ) { - dvbpsi_pmt_es_t *p_es = dvbpsi_PMTAddES( &pmt, p_current_es->i_type, - p_current_es->i_pid ); + /* Empty PAT and no SDT anymore */ + free( p_output->p_pat_section ); + p_output->p_pat_section = NULL; + p_output->i_pat_version++; + } + return; + } - for ( p_dr = p_current_es->p_first_descriptor; p_dr != NULL; - p_dr = p_dr->p_next ) - dvbpsi_PMTESAddDescriptor( p_es, p_dr->i_tag, p_dr->i_length, - p_dr->p_data ); + p = p_output->p_sdt_section = psi_allocate(); + sdt_init( p, true ); + sdt_set_length( p, PSI_MAX_SIZE ); + sdt_set_tsid( p, p_output->i_tsid ); + psi_set_version( p, p_output->i_sdt_version ); + psi_set_current( p ); + psi_set_section( p, 0 ); + psi_set_lastsection( p, 0 ); + sdt_set_onid( p, + sdt_get_onid( psi_table_get_section( pp_current_sdt_sections, 0 ) ) ); + + p_service = sdt_get_service( p, 0 ); + sdtn_init( p_service ); + sdtn_set_sid( p_service, p_output->config.i_sid ); + if ( sdtn_get_eitschedule(p_current_service) ) + sdtn_set_eitschedule(p_service); + if ( sdtn_get_eitpresent(p_current_service) ) + sdtn_set_eitpresent(p_service); + sdtn_set_running( p_service, sdtn_get_running(p_current_service) ); + /* Do not set free_ca */ + sdtn_set_desclength( p_service, sdtn_get_desclength(p_current_service) ); + + char *p_new_provider = p_output->config.psz_service_provider; + char *p_new_service = p_output->config.psz_service_name; + + if ( !p_new_provider && !p_new_service ) { + /* Copy all descriptors unchanged */ + memcpy( descs_get_desc( sdtn_get_descs(p_service), 0 ), + descs_get_desc( sdtn_get_descs(p_current_service), 0 ), + sdtn_get_desclength(p_current_service) ); + } else { + int j = 0, i_total_desc_len = 0; + uint8_t *p_desc; + uint8_t *p_new_desc = descs_get_desc( sdtn_get_descs(p_service), 0 ); + while ( (p_desc = descs_get_desc( sdtn_get_descs( p_current_service ), j++ )) != NULL ) + { + /* Regenerate descriptor 48 (service name) */ + if ( desc_get_tag( p_desc ) == 0x48 && desc48_validate( p_desc ) ) + { + uint8_t i_old_provider_len, i_old_service_len; + uint8_t i_new_desc_len = 3; /* 1 byte - type, 1 byte provider_len, 1 byte service_len */ + char *p_new_provider = p_output->config.psz_service_provider; + char *p_new_service = p_output->config.psz_service_name; + const uint8_t *p_old_provider = desc48_get_provider( p_desc, &i_old_provider_len ); + const uint8_t *p_old_service = desc48_get_service( p_desc, &i_old_service_len ); + + desc48_init( p_new_desc ); + desc48_set_type( p_new_desc, desc48_get_type( p_desc ) ); + + if ( p_new_provider ) { + desc48_set_provider( p_new_desc, (uint8_t *)p_new_provider, strlen( p_new_provider ) ); + i_new_desc_len += strlen( p_new_provider ); + } else { + desc48_set_provider( p_new_desc, p_old_provider, i_old_provider_len ); + i_new_desc_len += i_old_provider_len; + } + + if ( p_new_service ) { + desc48_set_service( p_new_desc, (uint8_t *)p_new_service, strlen( p_new_service ) ); + i_new_desc_len += strlen( p_new_service ); + } else { + desc48_set_service( p_new_desc, p_old_service, i_old_service_len ); + i_new_desc_len += i_old_service_len; + } + desc_set_length( p_new_desc, i_new_desc_len ); + i_total_desc_len += DESC_HEADER_SIZE + i_new_desc_len; + p_new_desc += DESC_HEADER_SIZE + i_new_desc_len; + } else { + /* Copy single descriptor */ + int i_desc_len = DESC_HEADER_SIZE + desc_get_length( p_desc ); + memcpy( p_new_desc, p_desc, i_desc_len ); + p_new_desc += i_desc_len; + i_total_desc_len += i_desc_len; + } } + sdtn_set_desclength( p_service, i_total_desc_len ); } - p_output->p_pmt_section = dvbpsi_GenPMTSections( &pmt ); - dvbpsi_EmptyPMT( &pmt ); + p_service = sdt_get_service( p, 1 ); + if ( p_service == NULL ) + /* This shouldn't happen if the incoming SDT is valid */ + sdt_set_length( p, 0 ); + else + sdt_set_length( p, p_service - p - SDT_HEADER_SIZE ); + psi_set_crc( p_output->p_sdt_section ); } /***************************************************************************** - * UpdatePAT + * UpdatePAT/PMT/SDT *****************************************************************************/ -static void UpdatePAT( uint16_t i_sid ) -{ - int i; - - for ( i = 0; i < i_nb_outputs; i++ ) - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid ) - NewPAT( pp_outputs[i] ); +#define DECLARE_UPDATE_FUNC( table ) \ +static void Update##table( uint16_t i_sid ) \ +{ \ + int i; \ + \ + for ( i = 0; i < i_nb_outputs; i++ ) \ + if ( ( pp_outputs[i]->config.i_config & OUTPUT_VALID ) \ + && pp_outputs[i]->config.i_sid == i_sid ) \ + New##table( pp_outputs[i] ); \ } +DECLARE_UPDATE_FUNC(PAT) +DECLARE_UPDATE_FUNC(PMT) +DECLARE_UPDATE_FUNC(SDT) + /***************************************************************************** - * UpdatePMT + * UpdateTSID *****************************************************************************/ -static void UpdatePMT( uint16_t i_sid ) +static void UpdateTSID(void) { + uint16_t i_tsid = psi_table_get_tableidext(pp_current_pat_sections); int i; for ( i = 0; i < i_nb_outputs; i++ ) - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid ) - NewPMT( pp_outputs[i] ); + { + output_t *p_output = pp_outputs[i]; + + if ( (p_output->config.i_config & OUTPUT_VALID) + && p_output->config.i_tsid == -1 && !b_random_tsid ) + { + p_output->i_tsid = i_tsid; + NewNIT( p_output ); + } + } } /***************************************************************************** * SIDIsSelected *****************************************************************************/ -static int SIDIsSelected( uint16_t i_sid ) +static bool SIDIsSelected( uint16_t i_sid ) { int i; for ( i = 0; i < i_nb_outputs; i++ ) - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid ) - return 1; + if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID) + && pp_outputs[i]->config.i_sid == i_sid ) + return true; - return 0; + return false; } /***************************************************************************** - * PIDIsSelected + * demux_PIDIsSelected *****************************************************************************/ -int PIDIsSelected( uint16_t i_pid ) +bool demux_PIDIsSelected( uint16_t i_pid ) { int i; for ( i = 0; i < p_pids[i_pid].i_nb_outputs; i++ ) if ( p_pids[i_pid].pp_outputs[i] != NULL ) - return 1; + return true; - return 0; + return false; } /***************************************************************************** * PIDWouldBeSelected *****************************************************************************/ -int PIDWouldBeSelected( dvbpsi_pmt_es_t *p_es ) +static bool PIDWouldBeSelected( uint8_t *p_es ) { - dvbpsi_descriptor_t *p_dr; + if ( b_any_type ) return true; - switch ( p_es->i_type ) + uint8_t i_type = pmtn_get_streamtype( p_es ); + + switch ( i_type ) { case 0x1: /* video MPEG-1 */ case 0x2: /* video */ case 0x3: /* audio MPEG-1 */ case 0x4: /* audio */ - case 0xf: /* audio AAC */ + case 0xf: /* audio AAC ADTS */ + case 0x10: /* video MPEG-4 */ + case 0x11: /* audio AAC LATM */ case 0x1b: /* video H264 */ - return 1; + case 0x81: /* ATSC A/52 */ + case 0x87: /* ATSC Enhanced A/52 */ + return true; break; case 0x6: - for( p_dr = p_es->p_first_descriptor; p_dr != NULL; - p_dr = p_dr->p_next ) + { + uint16_t j = 0; + const uint8_t *p_desc; + + while ( (p_desc = descs_get_desc( pmtn_get_descs( p_es ), j )) != NULL ) { - if( p_dr->i_tag == 0x56 /* ttx */ - || p_dr->i_tag == 0x59 /* dvbsub */ - || p_dr->i_tag == 0x6a /* A/52 */ ) - return 1; + uint8_t i_tag = desc_get_tag( p_desc ); + j++; + + if( i_tag == 0x46 /* VBI + teletext */ + || i_tag == 0x56 /* teletext */ + || i_tag == 0x59 /* dvbsub */ + || i_tag == 0x6a /* A/52 */ + || i_tag == 0x7a /* Enhanced A/52 */ + || i_tag == 0x7b /* DCA */ + || i_tag == 0x7c /* AAC */ ) + return true; } break; + } default: break; } /* FIXME: also parse IOD */ - return 0; + return false; } /***************************************************************************** * PIDCarriesPES *****************************************************************************/ -int PIDCarriesPES( dvbpsi_pmt_es_t *p_es ) +static bool PIDCarriesPES( const uint8_t *p_es ) { - switch ( p_es->i_type ) + uint8_t i_type = pmtn_get_streamtype( p_es ); + + switch ( i_type ) { case 0x1: /* video MPEG-1 */ case 0x2: /* video */ @@ -1052,12 +1566,16 @@ case 0x4: /* audio */ case 0x6: /* private PES data */ case 0xf: /* audio AAC */ + case 0x10: /* video MPEG-4 */ + case 0x11: /* audio AAC LATM */ case 0x1b: /* video H264 */ - return 1; + case 0x81: /* ATSC A/52 */ + case 0x87: /* ATSC Enhanced A/52 */ + return true; break; default: - return 0; + return false; break; } } @@ -1065,22 +1583,37 @@ /***************************************************************************** * PMTNeedsDescrambling *****************************************************************************/ -static int PMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt ) +static bool PMTNeedsDescrambling( uint8_t *p_pmt ) { - dvbpsi_descriptor_t *p_dr; - dvbpsi_pmt_es_t *p_es; + uint8_t i; + uint16_t j; + uint8_t *p_es; + const uint8_t *p_desc; - for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next ) - if( p_dr->i_tag == 0x9 ) - return 1; - - for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next ) - for( p_dr = p_es->p_first_descriptor; p_dr != NULL; - p_dr = p_dr->p_next ) - if( p_dr->i_tag == 0x9 ) - return 1; + j = 0; + while ( (p_desc = descs_get_desc( pmt_get_descs( p_pmt ), j )) != NULL ) + { + uint8_t i_tag = desc_get_tag( p_desc ); + j++; - return 0; + if ( i_tag == 0x9 ) return true; + } + + i = 0; + while ( (p_es = pmt_get_es( p_pmt, i )) != NULL ) + { + i++; + j = 0; + while ( (p_desc = descs_get_desc( pmtn_get_descs( p_es ), j )) != NULL ) + { + uint8_t i_tag = desc_get_tag( p_desc ); + j++; + + if ( i_tag == 0x9 ) return true; + } + } + + return false; } /***************************************************************************** @@ -1096,238 +1629,613 @@ en50221_AddPMT( pp_sids[i]->p_current_pmt ); } +/* Find CA descriptor that have PID i_ca_pid */ +static uint8_t *ca_desc_find( uint8_t *p_descl, uint16_t i_length, + uint16_t i_ca_pid ) +{ + int j = 0; + uint8_t *p_desc; + + while ( (p_desc = descl_get_desc( p_descl, i_length, j++ )) != NULL ) { + if ( desc_get_tag( p_desc ) != 0x09 || !desc09_validate( p_desc ) ) + continue; + if ( desc09_get_pid( p_desc ) == i_ca_pid ) + return p_desc; + } + + return NULL; +} + /***************************************************************************** * DeleteProgram *****************************************************************************/ -static void DeleteProgram( dvbpsi_pat_program_t *p_program ) +static void DeleteProgram( uint16_t i_sid, uint16_t i_pid ) { - int i_pmt; + sid_t *p_sid; + uint8_t *p_pmt; + + UnselectPMT( i_sid, i_pid ); + + p_sid = FindSID( i_sid ); + if ( p_sid == NULL ) return; - UnselectPSI( p_program->i_number, p_program->i_pid ); + p_pmt = p_sid->p_current_pmt; - for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ ) + if ( p_pmt != NULL ) { - if ( pp_sids[i_pmt]->i_sid == p_program->i_number ) - { - dvbpsi_pmt_t *p_pmt = pp_sids[i_pmt]->p_current_pmt; + uint16_t i_pcr_pid = pmt_get_pcrpid( p_pmt ); + uint8_t *p_es; + uint8_t j; - if ( p_pmt != NULL ) - { - dvbpsi_pmt_es_t *p_es; + if ( i_ca_handle && SIDIsSelected( i_sid ) + && PMTNeedsDescrambling( p_pmt ) ) + en50221_DeletePMT( p_pmt ); - if ( i_ca_handle - && SIDIsSelected( p_program->i_number ) - && PMTNeedsDescrambling( p_pmt ) ) - en50221_DeletePMT( p_pmt ); - - if ( p_pmt->i_pcr_pid != PADDING_PID - && p_pmt->i_pcr_pid != pp_sids[i_pmt]->i_pmt_pid ) - UnselectPID( p_program->i_number, p_pmt->i_pcr_pid ); + if ( i_pcr_pid != PADDING_PID + && i_pcr_pid != p_sid->i_pmt_pid ) + UnselectPID( i_sid, i_pcr_pid ); - for( p_es = p_pmt->p_first_es; p_es != NULL; - p_es = p_es->p_next ) - { - if ( PIDWouldBeSelected( p_es ) ) - UnselectPID( p_program->i_number, p_es->i_pid ); - } + if ( b_enable_ecm ) + { + j = 0; + uint8_t *p_desc; - dvbpsi_DeletePMT( p_pmt ); + while ((p_desc = descs_get_desc( pmt_get_descs( p_pmt ), j++ )) != NULL) + { + if ( desc_get_tag( p_desc ) != 0x09 || + !desc09_validate( p_desc ) ) + continue; + UnselectPID( i_sid, desc09_get_pid( p_desc ) ); } - pp_sids[i_pmt]->p_current_pmt = NULL; - pp_sids[i_pmt]->i_sid = 0; - pp_sids[i_pmt]->i_pmt_pid = 0; - dvbpsi_DetachPMT( pp_sids[i_pmt]->p_dvbpsi_handle ); - break; } + + j = 0; + while ( (p_es = pmt_get_es( p_pmt, j )) != NULL ) + { + uint16_t i_pid = pmtn_get_pid( p_es ); + j++; + + if ( PIDWouldBeSelected( p_es ) ) + UnselectPID( i_sid, i_pid ); + } + + free( p_pmt ); + p_sid->p_current_pmt = NULL; } + p_sid->i_sid = 0; + p_sid->i_pmt_pid = 0; +} + +/***************************************************************************** + * demux_Iconv + ***************************************************************************** + * This code is from biTStream's examples and is under the WTFPL (see + * LICENSE.WTFPL). + *****************************************************************************/ +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 *demux_Iconv(void *_unused, const char *psz_encoding, + char *p_string, size_t i_length) +{ +#ifdef HAVE_ICONV + static const char *psz_current_encoding = ""; + + char *psz_string, *p; + size_t i_out_length; + + if (!strcmp(psz_encoding, psz_native_charset)) + return iconv_append_null(p_string, i_length); + + if (iconv_handle != (iconv_t)-1 && + strcmp(psz_encoding, psz_current_encoding)) { + iconv_close(iconv_handle); + iconv_handle = (iconv_t)-1; + } + + if (iconv_handle == (iconv_t)-1) + iconv_handle = iconv_open(psz_native_charset, psz_encoding); + if (iconv_handle == (iconv_t)-1) { + msg_Warn(NULL, "couldn't convert from %s to %s (%m)", psz_encoding, + psz_native_charset); + return iconv_append_null(p_string, i_length); + } + + /* converted strings can be up to six times larger */ + i_out_length = i_length * 6; + p = psz_string = malloc(i_out_length); + if (iconv(iconv_handle, &p_string, &i_length, &p, &i_out_length) == -1) { + msg_Warn(NULL, "couldn't convert from %s to %s (%m)", psz_encoding, + psz_native_charset); + free(psz_string); + return iconv_append_null(p_string, i_length); + } + if (i_length) + msg_Warn(NULL, "partial conversion from %s to %s", psz_encoding, + psz_native_charset); + + *p = '\0'; + return psz_string; +#else + return iconv_append_null(p_string, i_length); +#endif +} + +/***************************************************************************** + * demux_Print + ***************************************************************************** + * This code is from biTStream's examples and is under the WTFPL (see + * LICENSE.WTFPL). + *****************************************************************************/ +__attribute__ ((format(printf, 2, 3))) +static void demux_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); + if ( i_print_type != PRINT_XML ) + strcat(psz_fmt, "\n"); + vprintf(psz_fmt, args); } /***************************************************************************** - * dvbpsi callbacks + * HandlePAT *****************************************************************************/ -static void PATCallback( void *_unused, dvbpsi_pat_t *p_pat ) +static void HandlePAT( mtime_t i_dts ) { - dvbpsi_pat_program_t *p_program, *p_old_program; - dvbpsi_pat_t *p_old_pat = p_current_pat; + bool b_change = false; + PSI_TABLE_DECLARE( pp_old_pat_sections ); + uint8_t i_last_section = psi_table_get_lastsection( pp_next_pat_sections ); + uint8_t i; - if( p_current_pat != NULL && - ( !p_pat->b_current_next || - p_pat->i_version == p_current_pat->i_version ) ) + if ( psi_table_validate( pp_current_pat_sections ) && + psi_table_compare( pp_current_pat_sections, pp_next_pat_sections ) ) { - dvbpsi_DeletePAT( p_pat ); - return; + /* Identical PAT. Shortcut. */ + psi_table_free( pp_next_pat_sections ); + psi_table_init( pp_next_pat_sections ); + goto out_pat; } - msg_Dbg( NULL, "new PAT ts_id=%d version=%d current_next=%d", - p_pat->i_ts_id, p_pat->i_version, p_pat->b_current_next ); - p_current_pat = p_pat; + if ( !pat_table_validate( pp_next_pat_sections ) ) + { + msg_Warn( NULL, "invalid PAT received" ); + switch (i_print_type) { + case PRINT_XML: + printf("\n"); + break; + default: + printf("invalid PAT received\n"); + } + psi_table_free( pp_next_pat_sections ); + psi_table_init( pp_next_pat_sections ); + goto out_pat; + } + + /* Switch tables. */ + psi_table_copy( pp_old_pat_sections, pp_current_pat_sections ); + psi_table_copy( pp_current_pat_sections, pp_next_pat_sections ); + psi_table_init( pp_next_pat_sections ); - for( p_program = p_pat->p_first_program; p_program != NULL; - p_program = p_program->p_next ) + if ( !psi_table_validate( pp_old_pat_sections ) + || psi_table_get_tableidext( pp_current_pat_sections ) + != psi_table_get_tableidext( pp_old_pat_sections ) ) { - int i_pmt; + b_change = true; + UpdateTSID(); + /* This will trigger a universal reset of everything. */ + } - msg_Dbg( NULL, " * number=%d pid=%d", p_program->i_number, - p_program->i_pid ); + for ( i = 0; i <= i_last_section; i++ ) + { + uint8_t *p_section = + psi_table_get_section( pp_current_pat_sections, i ); + const uint8_t *p_program; + int j = 0; - if( p_program->i_number == 0 ) - continue; + while ( (p_program = pat_get_program( p_section, j )) != NULL ) + { + const uint8_t *p_old_program = NULL; + uint16_t i_sid = patn_get_program( p_program ); + uint16_t i_pid = patn_get_pid( p_program ); + j++; + + if ( i_sid == 0 ) + { + if ( i_pid != NIT_PID ) + msg_Warn( NULL, + "NIT is carried on PID %hu which isn't DVB compliant", + i_pid ); + continue; /* NIT */ + } + + if ( !psi_table_validate( pp_old_pat_sections ) + || (p_old_program = pat_table_find_program( + pp_old_pat_sections, i_sid )) == NULL + || patn_get_pid( p_old_program ) != i_pid + || b_change ) + { + sid_t *p_sid; + + if ( p_old_program != NULL ) + DeleteProgram( i_sid, patn_get_pid( p_old_program ) ); + + SelectPMT( i_sid, i_pid ); + + p_sid = FindSID( 0 ); + if ( p_sid == NULL ) + { + p_sid = malloc( sizeof(sid_t) ); + p_sid->p_current_pmt = NULL; + i_nb_sids++; + pp_sids = realloc( pp_sids, sizeof(sid_t *) * i_nb_sids ); + pp_sids[i_nb_sids - 1] = p_sid; + } + + p_sid->i_sid = i_sid; + p_sid->i_pmt_pid = i_pid; + + UpdatePAT( i_sid ); + } + } + } - if ( p_old_pat != NULL ) + if ( psi_table_validate( pp_old_pat_sections ) ) + { + i_last_section = psi_table_get_lastsection( pp_old_pat_sections ); + for ( i = 0; i <= i_last_section; i++ ) { - for ( p_old_program = p_old_pat->p_first_program; - p_old_program != NULL; - p_old_program = p_old_program->p_next ) - if ( p_old_program->i_number == p_program->i_number ) - break; + uint8_t *p_section = + psi_table_get_section( pp_old_pat_sections, i ); + const uint8_t *p_program; + int j = 0; + + while ( (p_program = pat_get_program( p_section, j )) != NULL ) + { + uint16_t i_sid = patn_get_program( p_program ); + uint16_t i_pid = patn_get_pid( p_program ); + j++; - if ( p_old_program != NULL && - p_old_program->i_pid == p_program->i_pid ) - continue; /* No change */ + if ( i_sid == 0 ) + continue; /* NIT */ - if ( p_old_program != NULL && - p_old_program->i_pid != p_program->i_pid ) - DeleteProgram( p_old_program ); + if ( pat_table_find_program( pp_current_pat_sections, i_sid ) + == NULL ) + { + DeleteProgram( i_sid, i_pid ); + UpdatePAT( i_sid ); + } + } } - SelectPSI( p_program->i_number, p_program->i_pid ); + psi_table_free( pp_old_pat_sections ); + } - for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ ) - if ( pp_sids[i_pmt]->i_sid == 0 ) - break; + pat_table_print( pp_current_pat_sections, msg_Dbg, NULL, PRINT_TEXT ); + if ( i_print_type != -1 ) + { + pat_table_print( pp_current_pat_sections, demux_Print, NULL, + i_print_type ); + if ( i_print_type == PRINT_XML ) + printf("\n"); + } - if ( i_pmt == i_nb_sids ) - { - sid_t *p_sid = malloc( sizeof(sid_t) ); - p_sid->p_current_pmt = NULL; - i_nb_sids++; - pp_sids = realloc( pp_sids, sizeof(sid_t *) * i_nb_sids ); - pp_sids[i_pmt] = p_sid; +out_pat: + SendPAT( i_dts ); +} + +/***************************************************************************** + * HandlePATSection + *****************************************************************************/ +static void HandlePATSection( uint16_t i_pid, uint8_t *p_section, + mtime_t i_dts ) +{ + if ( i_pid != PAT_PID || !pat_validate( p_section ) ) + { + msg_Warn( NULL, "invalid PAT section received on PID %hu", i_pid ); + switch (i_print_type) { + case PRINT_XML: + printf("\n"); + break; + default: + printf("invalid PAT section received on PID %hu\n", i_pid); } + free( p_section ); + return; + } - pp_sids[i_pmt]->i_sid = p_program->i_number; - pp_sids[i_pmt]->i_pmt_pid = p_program->i_pid; - pp_sids[i_pmt]->p_dvbpsi_handle = dvbpsi_AttachPMT( p_program->i_number, - PMTCallback, NULL ); + if ( !psi_table_section( pp_next_pat_sections, p_section ) ) + return; + + HandlePAT( i_dts ); +} - UpdatePAT( p_program->i_number ); +/***************************************************************************** + * HandleCAT + *****************************************************************************/ +static void HandleCAT( mtime_t i_dts ) +{ + PSI_TABLE_DECLARE( pp_old_cat_sections ); + uint8_t i_last_section = psi_table_get_lastsection( pp_next_cat_sections ); + uint8_t i_last_section2; + uint8_t i, r; + uint8_t *p_desc; + int j, k; + + if ( psi_table_validate( pp_current_cat_sections ) && + psi_table_compare( pp_current_cat_sections, pp_next_cat_sections ) ) + { + /* Identical CAT. Shortcut. */ + psi_table_free( pp_next_cat_sections ); + psi_table_init( pp_next_cat_sections ); + goto out_cat; } - if ( p_old_pat != NULL ) + if ( !cat_table_validate( pp_next_cat_sections ) ) { - for ( p_old_program = p_old_pat->p_first_program; - p_old_program != NULL; - p_old_program = p_old_program->p_next ) + msg_Warn( NULL, "invalid CAT received" ); + switch (i_print_type) { + case PRINT_XML: + printf("\n"); + break; + default: + printf("invalid CAT received\n"); + } + psi_table_free( pp_next_cat_sections ); + psi_table_init( pp_next_cat_sections ); + goto out_cat; + } + + /* Switch tables. */ + psi_table_copy( pp_old_cat_sections, pp_current_cat_sections ); + psi_table_copy( pp_current_cat_sections, pp_next_cat_sections ); + psi_table_init( pp_next_cat_sections ); + + for ( i = 0; i <= i_last_section; i++ ) + { + uint8_t *p_section = psi_table_get_section( pp_current_cat_sections, i ); + + j = 0; + while ( (p_desc = descl_get_desc( cat_get_descl(p_section), cat_get_desclength(p_section), j++ )) != NULL ) { - if( p_old_program->i_number == 0 ) + if ( desc_get_tag( p_desc ) != 0x09 || !desc09_validate( p_desc ) ) continue; - for( p_program = p_pat->p_first_program; p_program != NULL; - p_program = p_program->p_next ) - if ( p_program->i_number == p_old_program->i_number ) - break; + SetPID_EMM( desc09_get_pid( p_desc ) ); + } + } - if ( p_program == NULL ) + if ( psi_table_validate( pp_old_cat_sections ) ) + { + i_last_section = psi_table_get_lastsection( pp_old_cat_sections ); + for ( i = 0; i <= i_last_section; i++ ) + { + uint8_t *p_old_section = psi_table_get_section( pp_old_cat_sections, i ); + j = 0; + while ( (p_desc = descl_get_desc( cat_get_descl(p_old_section), cat_get_desclength(p_old_section), j++ )) != NULL ) { - msg_Dbg( NULL, " * removed number=%d pid=%d", - p_old_program->i_number, - p_old_program->i_pid ); + uint16_t emm_pid; + int pid_found = 0; + + if ( desc_get_tag( p_desc ) != 0x09 || !desc09_validate( p_desc ) ) + continue; - DeleteProgram( p_old_program ); - UpdatePAT( p_old_program->i_number ); + emm_pid = desc09_get_pid( p_desc ); + + // Search in current sections if the pid exists + i_last_section2 = psi_table_get_lastsection( pp_current_cat_sections ); + for ( r = 0; r <= i_last_section2; r++ ) + { + uint8_t *p_section = psi_table_get_section( pp_current_cat_sections, r ); + + k = 0; + while ( (p_desc = descl_get_desc( cat_get_descl(p_section), cat_get_desclength(p_section), k++ )) != NULL ) + { + if ( desc_get_tag( p_desc ) != 0x09 || !desc09_validate( p_desc ) ) + continue; + if ( ca_desc_find( cat_get_descl(p_section), cat_get_desclength(p_section), emm_pid ) != NULL ) + { + pid_found = 1; + break; + } + } + } + + if ( !pid_found ) + UnsetPID(emm_pid); } } - dvbpsi_DeletePAT( p_old_pat ); + psi_table_free( pp_old_cat_sections ); + } + + cat_table_print( pp_current_cat_sections, msg_Dbg, NULL, PRINT_TEXT ); + if ( i_print_type != -1 ) + { + cat_table_print( pp_current_cat_sections, demux_Print, NULL, + i_print_type ); + if ( i_print_type == PRINT_XML ) + printf("\n"); } + +out_cat: + return; } -static void PMTCallback( void *_unused, dvbpsi_pmt_t *p_pmt ) +/***************************************************************************** + * HandleCATSection + *****************************************************************************/ +static void HandleCATSection( uint16_t i_pid, uint8_t *p_section, + mtime_t i_dts ) { - dvbpsi_pmt_t *p_current_pmt = NULL; - dvbpsi_pmt_es_t *p_es, *p_current_es; - int b_needs_descrambling = PMTNeedsDescrambling( p_pmt ); - int b_needed_descrambling = 0; - int b_is_selected = SIDIsSelected( p_pmt->i_program_number ); - int i_pmt; - - for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ ) + if ( i_pid != CAT_PID || !cat_validate( p_section ) ) { - if ( pp_sids[i_pmt]->i_sid == p_pmt->i_program_number ) - { - p_current_pmt = pp_sids[i_pmt]->p_current_pmt; - if ( p_current_pmt != NULL ) - b_needed_descrambling = PMTNeedsDescrambling( p_current_pmt ); + msg_Warn( NULL, "invalid CAT section received on PID %hu", i_pid ); + switch (i_print_type) { + case PRINT_XML: + printf("\n"); break; + default: + printf("invalid CAT section received on PID %hu\n", i_pid); } + free( p_section ); + return; } - if ( i_pmt == i_nb_sids ) + if ( !psi_table_section( pp_next_cat_sections, p_section ) ) + return; + + HandleCAT( i_dts ); +} + +/***************************************************************************** + * HandlePMT + *****************************************************************************/ +static void HandlePMT( uint16_t i_pid, uint8_t *p_pmt, mtime_t i_dts ) +{ + uint16_t i_sid = pmt_get_program( p_pmt ); + sid_t *p_sid; + bool b_needs_descrambling, b_needed_descrambling, b_is_selected; + uint16_t i_pcr_pid; + uint8_t *p_es; + uint8_t *p_desc; + uint16_t j; + + p_sid = FindSID( i_sid ); + if ( p_sid == NULL ) { - msg_Err( NULL, "unknown service %d", p_pmt->i_program_number ); - dvbpsi_DeletePMT( p_pmt ); + /* Unwanted SID (happens when the same PMT PID is used for several + * programs). */ + free( p_pmt ); return; } - if ( p_current_pmt != NULL && - ( !p_pmt->b_current_next || - p_pmt->i_version == p_current_pmt->i_version ) ) + if ( i_pid != p_sid->i_pmt_pid ) { - dvbpsi_DeletePMT( p_pmt ); + msg_Warn( NULL, "invalid PMT section received on PID %hu", i_pid ); + switch (i_print_type) { + case PRINT_XML: + printf("\n", + i_sid, i_pid); + break; + default: + printf("ghost PMT for service %hu carried on PID %hu\n", i_sid, + i_pid); + } + free( p_pmt ); return; } + if ( p_sid->p_current_pmt != NULL && + psi_compare( p_sid->p_current_pmt, p_pmt ) ) + { + /* Identical PMT. Shortcut. */ + free( p_pmt ); + goto out_pmt; + } + + if ( !pmt_validate( p_pmt ) ) + { + msg_Warn( NULL, "invalid PMT section received on PID %hu", i_pid ); + switch (i_print_type) { + case PRINT_XML: + printf("\n", + i_pid); + break; + default: + printf("invalid PMT section received on PID %hu\n", i_pid); + } + free( p_pmt ); + goto out_pmt; + } + + b_needs_descrambling = PMTNeedsDescrambling( p_pmt ); + b_needed_descrambling = p_sid->p_current_pmt != NULL ? + PMTNeedsDescrambling( p_sid->p_current_pmt ) : + false; + b_is_selected = SIDIsSelected( i_sid ); + i_pcr_pid = pmt_get_pcrpid( p_pmt ); + if ( i_ca_handle && b_is_selected && !b_needs_descrambling && b_needed_descrambling ) - en50221_DeletePMT( p_current_pmt ); + en50221_DeletePMT( p_sid->p_current_pmt ); - msg_Dbg( NULL, "new PMT program number=%d version=%d pid_pcr=%d", - p_pmt->i_program_number, p_pmt->i_version, p_pmt->i_pcr_pid ); + if ( b_enable_ecm ) + { + j = 0; + while ( (p_desc = descs_get_desc( pmt_get_descs( p_pmt ), j++ )) != NULL ) + { + if ( desc_get_tag( p_desc ) != 0x09 || !desc09_validate( p_desc ) ) + continue; + SelectPID( i_sid, desc09_get_pid( p_desc ) ); + } + } - if ( p_pmt->i_pcr_pid != PADDING_PID - && p_pmt->i_pcr_pid != pp_sids[i_pmt]->i_pmt_pid ) - SelectPID( p_pmt->i_program_number, p_pmt->i_pcr_pid ); + if ( p_sid->p_current_pmt == NULL + || i_pcr_pid != pmt_get_pcrpid( p_sid->p_current_pmt ) ) + { + if ( i_pcr_pid != PADDING_PID + && i_pcr_pid != p_sid->i_pmt_pid ) + SelectPID( i_sid, i_pcr_pid ); + } - for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next ) + j = 0; + while ( (p_es = pmt_get_es( p_pmt, j )) != NULL ) { - msg_Dbg( NULL, " * es pid=%d type=%d", - p_es->i_pid, p_es->i_type ); + uint16_t i_pid = pmtn_get_pid( p_es ); + j++; if ( PIDWouldBeSelected( p_es ) ) - SelectPID( p_pmt->i_program_number, p_es->i_pid ); - p_pids[p_es->i_pid].b_pes = PIDCarriesPES( p_es ); + SelectPID( i_sid, i_pid ); + p_pids[i_pid].b_pes = PIDCarriesPES( p_es ); } - if ( p_current_pmt != NULL ) + if ( p_sid->p_current_pmt != NULL ) { - if ( p_current_pmt->i_pcr_pid != p_pmt->i_pcr_pid - && p_current_pmt->i_pcr_pid != PADDING_PID - && p_current_pmt->i_pcr_pid != pp_sids[i_pmt]->i_pmt_pid ) - UnselectPID( p_pmt->i_program_number, p_current_pmt->i_pcr_pid ); + if ( b_enable_ecm ) + { + j = 0; + while ((p_desc = descs_get_desc( pmt_get_descs( p_sid->p_current_pmt ), j++ )) != NULL) + { + if ( desc_get_tag( p_desc ) != 0x09 || !desc09_validate( p_desc ) ) + continue; + if ( ca_desc_find( pmt_get_descs(p_pmt) + DESCS_HEADER_SIZE, descs_get_length(pmt_get_descs(p_pmt)), desc09_get_pid( p_desc ) ) == NULL ) + UnselectPID( i_sid, desc09_get_pid( p_desc ) ); + } + } + + uint16_t i_current_pcr_pid = pmt_get_pcrpid( p_sid->p_current_pmt ); + if ( i_current_pcr_pid != i_pcr_pid + && i_current_pcr_pid != PADDING_PID ) + { + if ( pmt_find_es( p_pmt, i_current_pcr_pid ) == NULL ) + UnselectPID( i_sid, i_current_pcr_pid ); + } - for( p_current_es = p_current_pmt->p_first_es; p_current_es != NULL; - p_current_es = p_current_es->p_next ) + j = 0; + while ( (p_es = pmt_get_es( p_sid->p_current_pmt, j )) != NULL ) { - if ( PIDWouldBeSelected( p_current_es ) ) + j++; + + if ( PIDWouldBeSelected( p_es ) ) { - for( p_es = p_pmt->p_first_es; p_es != NULL; - p_es = p_es->p_next ) - if ( p_es->i_pid == p_current_es->i_pid ) - break; + uint16_t i_current_pid = pmtn_get_pid( p_es ); - if ( p_es == NULL ) - { - msg_Dbg( NULL, " * removed es pid=%d type=%d", - p_current_es->i_pid, p_current_es->i_type ); - UnselectPID( p_pmt->i_program_number, p_current_es->i_pid ); - } + if ( pmt_find_es( p_pmt, i_current_pid ) == NULL ) + UnselectPID( i_sid, i_current_pid ); } } - dvbpsi_DeletePMT( p_current_pmt ); + free( p_sid->p_current_pmt ); } - pp_sids[i_pmt]->p_current_pmt = p_pmt; + p_sid->p_current_pmt = p_pmt; if ( i_ca_handle && b_is_selected ) { @@ -1337,84 +2245,560 @@ en50221_UpdatePMT( p_pmt ); } - UpdatePMT( p_pmt->i_program_number ); + UpdatePMT( i_sid ); + + pmt_print( p_pmt, msg_Dbg, NULL, demux_Iconv, NULL, PRINT_TEXT ); + if ( i_print_type != -1 ) + { + pmt_print( p_pmt, demux_Print, NULL, demux_Iconv, NULL, + i_print_type ); + if ( i_print_type == PRINT_XML ) + printf("\n"); + } + +out_pmt: + SendPMT( p_sid, i_dts ); } -static void PSITableCallback( void *_unused, dvbpsi_handle h_dvbpsi, - uint8_t i_table_id, uint16_t i_extension ) +/***************************************************************************** + * HandleNIT + *****************************************************************************/ +static void HandleNIT( mtime_t i_dts ) { - /* EIT tables */ + if ( psi_table_validate( pp_current_nit_sections ) && + psi_table_compare( pp_current_nit_sections, pp_next_nit_sections ) ) + { + /* Identical NIT. Shortcut. */ + psi_table_free( pp_next_nit_sections ); + psi_table_init( pp_next_nit_sections ); + goto out_nit; + } - if ( i_table_id == 0x4e || ( i_table_id >= 0x50 && i_table_id <= 0x5f ) ) + if ( !nit_table_validate( pp_next_nit_sections ) ) { - SendEIT( h_dvbpsi->p_current_section, i_extension, i_table_id ); + msg_Warn( NULL, "invalid NIT received" ); + switch (i_print_type) { + case PRINT_XML: + printf("\n"); + break; + default: + printf("invalid NIT received\n"); + } + psi_table_free( pp_next_nit_sections ); + psi_table_init( pp_next_nit_sections ); + goto out_nit; } - /* SDT tables */ + /* Switch tables. */ + psi_table_free( pp_current_nit_sections ); + psi_table_copy( pp_current_nit_sections, pp_next_nit_sections ); + psi_table_init( pp_next_nit_sections ); - if ( i_table_id == 0x42 ) + nit_table_print( pp_current_nit_sections, msg_Dbg, NULL, + demux_Iconv, NULL, PRINT_TEXT ); + if ( i_print_type != -1 ) { - dvbpsi_AttachSDT( h_dvbpsi, i_table_id, i_extension, SDTCallback, - NULL ); + nit_table_print( pp_current_nit_sections, demux_Print, NULL, + demux_Iconv, NULL, i_print_type ); + if ( i_print_type == PRINT_XML ) + printf("\n"); } + +out_nit: + ; } -static void SDTCallback( void *_unused, dvbpsi_sdt_t *p_sdt ) +/***************************************************************************** + * HandleNITSection + *****************************************************************************/ +static void HandleNITSection( uint16_t i_pid, uint8_t *p_section, + mtime_t i_dts ) { - dvbpsi_sdt_t *p_old_sdt = p_current_sdt; - dvbpsi_sdt_service_t *p_srv; - dvbpsi_descriptor_t *p_dr; - int i; + if ( i_pid != NIT_PID || !nit_validate( p_section ) ) + { + msg_Warn( NULL, "invalid NIT section received on PID %hu", i_pid ); + switch (i_print_type) { + case PRINT_XML: + printf("\n", + i_pid); + break; + default: + printf("invalid NIT section received on PID %hu\n", i_pid); + } + free( p_section ); + return; + } + + if ( psi_table_section( pp_next_nit_sections, p_section ) ) + HandleNIT( i_dts ); - if( p_current_sdt != NULL && - ( !p_sdt->b_current_next || - p_sdt->i_version == p_current_sdt->i_version ) ) + /* This case is different because DVB specifies a minimum bitrate for + * PID 0x10, even if we don't have any thing to send (for cheap + * transport over network boundaries). */ + SendNIT( i_dts ); +} + + +/***************************************************************************** + * HandleSDT + *****************************************************************************/ +static void HandleSDT( mtime_t i_dts ) +{ + PSI_TABLE_DECLARE( pp_old_sdt_sections ); + uint8_t i_last_section = psi_table_get_lastsection( pp_next_sdt_sections ); + uint8_t i; + int j; + + if ( psi_table_validate( pp_current_sdt_sections ) && + psi_table_compare( pp_current_sdt_sections, pp_next_sdt_sections ) ) + { + /* Identical SDT. Shortcut. */ + psi_table_free( pp_next_sdt_sections ); + psi_table_init( pp_next_sdt_sections ); + goto out_sdt; + } + + if ( !sdt_table_validate( pp_next_sdt_sections ) ) { - dvbpsi_DeleteSDT( p_sdt ); + msg_Warn( NULL, "invalid SDT received" ); + switch (i_print_type) { + case PRINT_XML: + printf("\n"); + break; + default: + printf("invalid SDT received\n"); + } + psi_table_free( pp_next_sdt_sections ); + psi_table_init( pp_next_sdt_sections ); + goto out_sdt; + } + + /* Switch tables. */ + psi_table_copy( pp_old_sdt_sections, pp_current_sdt_sections ); + psi_table_copy( pp_current_sdt_sections, pp_next_sdt_sections ); + psi_table_init( pp_next_sdt_sections ); + + for ( i = 0; i <= i_last_section; i++ ) + { + uint8_t *p_section = + psi_table_get_section( pp_current_sdt_sections, i ); + uint8_t *p_service; + j = 0; + + while ( (p_service = sdt_get_service( p_section, j )) != NULL ) + { + uint16_t i_sid = sdtn_get_sid( p_service ); + j++; + + UpdateSDT( i_sid ); + } + } + + if ( psi_table_validate( pp_old_sdt_sections ) ) + { + i_last_section = psi_table_get_lastsection( pp_old_sdt_sections ); + for ( i = 0; i <= i_last_section; i++ ) + { + uint8_t *p_section = + psi_table_get_section( pp_old_sdt_sections, i ); + const uint8_t *p_service; + int j = 0; + + while ( (p_service = sdt_get_service( p_section, j )) != NULL ) + { + uint16_t i_sid = sdtn_get_sid( p_service ); + j++; + + if ( sdt_table_find_service( pp_current_sdt_sections, i_sid ) + == NULL ) + UpdateSDT( i_sid ); + } + } + + psi_table_free( pp_old_sdt_sections ); + } + + sdt_table_print( pp_current_sdt_sections, msg_Dbg, NULL, + demux_Iconv, NULL, PRINT_TEXT ); + if ( i_print_type != -1 ) + { + sdt_table_print( pp_current_sdt_sections, demux_Print, NULL, + demux_Iconv, NULL, i_print_type ); + if ( i_print_type == PRINT_XML ) + printf("\n"); + } + +out_sdt: + SendSDT( i_dts ); +} + +/***************************************************************************** + * HandleSDTSection + *****************************************************************************/ +static void HandleSDTSection( uint16_t i_pid, uint8_t *p_section, + mtime_t i_dts ) +{ + if ( i_pid != SDT_PID || !sdt_validate( p_section ) ) + { + msg_Warn( NULL, "invalid SDT section received on PID %hu", i_pid ); + switch (i_print_type) { + case PRINT_XML: + printf("\n", + i_pid); + break; + default: + printf("invalid SDT section received on PID %hu\n", i_pid); + } + free( p_section ); return; } - msg_Dbg( NULL, "new SDT ts_id=%d version=%d current_next=%d " - "network_id=%d", - p_sdt->i_ts_id, p_sdt->i_version, p_sdt->b_current_next, - p_sdt->i_network_id ); - - for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next ) - { - msg_Dbg( NULL, " * service id=%d eit schedule=%d present=%d " - "running=%d free_ca=%d", - p_srv->i_service_id, p_srv->b_eit_schedule, - p_srv->b_eit_present, p_srv->i_running_status, - p_srv->b_free_ca ); + if ( !psi_table_section( pp_next_sdt_sections, p_section ) ) + return; - for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next ) + HandleSDT( i_dts ); +} + +/***************************************************************************** + * HandleEITSection + *****************************************************************************/ +static void HandleEIT( uint16_t i_pid, uint8_t *p_eit, mtime_t i_dts ) +{ + uint16_t i_sid = eit_get_sid( p_eit ); + sid_t *p_sid; + + p_sid = FindSID( i_sid ); + if ( p_sid == NULL ) + { + /* Not a selected program. */ + free( p_eit ); + return; + } + + if ( i_pid != EIT_PID || !eit_validate( p_eit ) ) + { + msg_Warn( NULL, "invalid EIT section received on PID %hu", i_pid ); + switch (i_print_type) { + case PRINT_XML: + printf("\n", + i_pid); + break; + default: + printf("invalid EIT section received on PID %hu\n", i_pid); + } + free( p_eit ); + return; + } + + SendEIT( p_sid, i_dts, p_eit ); + free( p_eit ); +} + +/***************************************************************************** + * HandleSection + *****************************************************************************/ +static void HandleSection( uint16_t i_pid, uint8_t *p_section, mtime_t i_dts ) +{ + uint8_t i_table_id = psi_get_tableid( p_section ); + + if ( !psi_validate( p_section ) ) + { + msg_Warn( NULL, "invalid section on PID %hu", i_pid ); + switch (i_print_type) { + case PRINT_XML: + printf("\n", i_pid); + break; + default: + printf("invalid section on PID %hu\n", i_pid); + } + free( p_section ); + return; + } + + if ( !psi_get_current( p_section ) ) + { + /* Ignore sections which are not in use yet. */ + free( p_section ); + return; + } + + switch ( i_table_id ) + { + case PAT_TABLE_ID: + HandlePATSection( i_pid, p_section, i_dts ); + break; + + case CAT_TABLE_ID: + if ( b_enable_emm ) + HandleCATSection( i_pid, p_section, i_dts ); + break; + + case PMT_TABLE_ID: + HandlePMT( i_pid, p_section, i_dts ); + break; + + case NIT_TABLE_ID_ACTUAL: + HandleNITSection( i_pid, p_section, i_dts ); + break; + + case SDT_TABLE_ID_ACTUAL: + HandleSDTSection( i_pid, p_section, i_dts ); + break; + + default: + if ( i_table_id == EIT_TABLE_ID_PF_ACTUAL || + (i_table_id >= EIT_TABLE_ID_SCHED_ACTUAL_FIRST && + i_table_id <= EIT_TABLE_ID_SCHED_ACTUAL_LAST) ) + { + HandleEIT( i_pid, p_section, i_dts ); + break; + } + free( p_section ); + break; + } +} + +/***************************************************************************** + * HandlePSIPacket + *****************************************************************************/ +static void HandlePSIPacket( uint8_t *p_ts, mtime_t i_dts ) +{ + uint16_t i_pid = ts_get_pid( p_ts ); + ts_pid_t *p_pid = &p_pids[i_pid]; + uint8_t i_cc = ts_get_cc( p_ts ); + const uint8_t *p_payload; + uint8_t i_length; + + if ( ts_check_duplicate( i_cc, p_pid->i_last_cc ) + || !ts_has_payload( p_ts ) ) + return; + + if ( p_pid->i_last_cc != -1 + && ts_check_discontinuity( i_cc, p_pid->i_last_cc ) ) + psi_assemble_reset( &p_pid->p_psi_buffer, &p_pid->i_psi_buffer_used ); + + p_payload = ts_section( p_ts ); + i_length = p_ts + TS_SIZE - p_payload; + + if ( !psi_assemble_empty( &p_pid->p_psi_buffer, + &p_pid->i_psi_buffer_used ) ) + { + uint8_t *p_section = psi_assemble_payload( &p_pid->p_psi_buffer, + &p_pid->i_psi_buffer_used, + &p_payload, &i_length ); + if ( p_section != NULL ) + HandleSection( i_pid, p_section, i_dts ); + } + + p_payload = ts_next_section( p_ts ); + i_length = p_ts + TS_SIZE - p_payload; + + while ( i_length ) + { + uint8_t *p_section = psi_assemble_payload( &p_pid->p_psi_buffer, + &p_pid->i_psi_buffer_used, + &p_payload, &i_length ); + if ( p_section != NULL ) + HandleSection( i_pid, p_section, i_dts ); + } +} + +/***************************************************************************** + * PID info functions + *****************************************************************************/ +static const char *h222_stream_type_desc(uint8_t i_stream_type) { + /* See ISO/IEC 13818-1 : 2000 (E) | Table 2-29 - Stream type assignments, Page 66 (48) */ + if (i_stream_type == 0 || (i_stream_type > 0x1c && i_stream_type < 0x7e)) + return "Reserved stream"; + switch (i_stream_type) { + case 0x01: return "11172-2 video (MPEG-1)"; + case 0x02: return "H.262/13818-2 video (MPEG-2) or 11172-2 constrained video"; + case 0x03: return "11172-3 audio (MPEG-1)"; + case 0x04: return "13818-3 audio (MPEG-2)"; + case 0x05: return "H.222.0/13818-1 private sections"; + case 0x06: return "H.222.0/13818-1 PES private data"; + case 0x07: return "13522 MHEG"; + case 0x08: return "H.222.0/13818-1 Annex A - DSM CC"; + case 0x09: return "H.222.1"; + case 0x0A: return "13818-6 type A"; + case 0x0B: return "13818-6 type B"; + case 0x0C: return "13818-6 type C"; + case 0x0D: return "13818-6 type D"; + case 0x0E: return "H.222.0/13818-1 auxiliary"; + case 0x0F: return "13818-7 Audio with ADTS transport syntax"; + case 0x10: return "14496-2 Visual (MPEG-4 part 2 video)"; + case 0x11: return "14496-3 Audio with LATM transport syntax (14496-3/AMD 1)"; + case 0x12: return "14496-1 SL-packetized or FlexMux stream in PES packets"; + case 0x13: return "14496-1 SL-packetized or FlexMux stream in 14496 sections"; + case 0x14: return "ISO/IEC 13818-6 Synchronized Download Protocol"; + case 0x15: return "Metadata in PES packets"; + case 0x16: return "Metadata in metadata_sections"; + case 0x17: return "Metadata in 13818-6 Data Carousel"; + case 0x18: return "Metadata in 13818-6 Object Carousel"; + case 0x19: return "Metadata in 13818-6 Synchronized Download Protocol"; + case 0x1A: return "13818-11 MPEG-2 IPMP stream"; + case 0x1B: return "H.264/14496-10 video (MPEG-4/AVC)"; + case 0x42: return "AVS Video"; + case 0x7F: return "IPMP stream"; + default : return "Unknown stream"; + } +} + +static const char *get_pid_desc(uint16_t i_pid, uint16_t *i_sid) { + int i, j, k; + uint8_t i_last_section; + uint8_t *p_desc; + uint16_t i_nit_pid = NIT_PID, i_pcr_pid = 0; + + /* Simple cases */ + switch (i_pid) + { + case 0x00: return "PAT"; + case 0x01: return "CAT"; + case 0x11: return "SDT"; + case 0x12: return "EPG"; + case 0x14: return "TDT/TOT"; + } + + /* Detect NIT pid */ + if ( psi_table_validate( pp_current_pat_sections ) ) + { + i_last_section = psi_table_get_lastsection( pp_current_pat_sections ); + for ( i = 0; i <= i_last_section; i++ ) { - if( p_dr->i_tag == 0x48 ) + uint8_t *p_section = psi_table_get_section( pp_current_pat_sections, i ); + uint8_t *p_program; + + j = 0; + while ( (p_program = pat_get_program( p_section, j++ )) != NULL ) { - dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr ); - char str1[256]; - char str2[256]; - - memcpy( str1, pD->i_service_provider_name, - pD->i_service_provider_name_length ); - str1[pD->i_service_provider_name_length] = '\0'; - memcpy( str2, pD->i_service_name, pD->i_service_name_length ); - str2[pD->i_service_name_length] = '\0'; - - msg_Dbg( NULL, " - type=%d provider=%s name=%s", - pD->i_service_type, str1, str2 ); + /* Programs with PID == 0 are actually NIT */ + if ( patn_get_program( p_program ) == 0 ) + { + i_nit_pid = patn_get_pid( p_program ); + break; + } } } } - p_current_sdt = p_sdt; + /* Detect EMM pids */ + if ( b_enable_emm && psi_table_validate( pp_current_cat_sections ) ) + { + i_last_section = psi_table_get_lastsection( pp_current_cat_sections ); + for ( i = 0; i <= i_last_section; i++ ) + { + uint8_t *p_section = psi_table_get_section( pp_current_cat_sections, i ); - for ( i = 0; i < i_nb_outputs; i++ ) + j = 0; + while ( (p_desc = descl_get_desc( cat_get_descl(p_section), cat_get_desclength(p_section), j++ )) != NULL ) + { + if ( desc_get_tag( p_desc ) != 0x09 || !desc09_validate( p_desc ) ) + continue; + + if ( desc09_get_pid( p_desc ) == i_pid ) { + return "EMM"; + } + } + } + } + + /* Detect streams in PMT */ + for ( k = 0; k < i_nb_sids; k++ ) { - if ( pp_outputs[i]->i_config & OUTPUT_VALID ) - NewSDT( pp_outputs[i] ); + sid_t *p_sid = pp_sids[k]; + if ( p_sid->i_pmt_pid == i_pid ) + { + if ( i_sid ) + *i_sid = p_sid->i_sid; + return "PMT"; + } + + if ( p_sid->i_sid && p_sid->p_current_pmt != NULL ) + { + uint8_t *p_current_pmt = p_sid->p_current_pmt; + uint8_t *p_current_es; + + /* The PCR PID can be alone or PCR can be carried in some other PIDs (mostly video) + so just remember the pid and if it is alone it will be reported as PCR, otherwise + stream type of the PID will be reported */ + if ( i_pid == pmt_get_pcrpid( p_current_pmt ) ) { + if ( i_sid ) + *i_sid = p_sid->i_sid; + i_pcr_pid = pmt_get_pcrpid( p_current_pmt ); + } + + /* Look for ECMs */ + j = 0; + while ((p_desc = descs_get_desc( pmt_get_descs( p_current_pmt ), j++ )) != NULL) + { + if ( desc_get_tag( p_desc ) != 0x09 || !desc09_validate( p_desc ) ) + continue; + + if ( desc09_get_pid( p_desc ) == i_pid ) { + if ( i_sid ) + *i_sid = p_sid->i_sid; + return "ECM"; + } + } + + /* Detect stream types */ + j = 0; + while ( (p_current_es = pmt_get_es( p_current_pmt, j++ )) != NULL ) + { + if ( pmtn_get_pid( p_current_es ) == i_pid ) + { + if ( i_sid ) + *i_sid = p_sid->i_sid; + return h222_stream_type_desc( pmtn_get_streamtype( p_current_es ) ); + } + } + } } - if ( p_old_sdt != NULL ) - dvbpsi_DeleteSDT( p_old_sdt ); + /* Are there any other PIDs? */ + if (i_pid == i_nit_pid) + return "NIT"; + + if (i_pid == i_pcr_pid) + return "PCR"; + + return "..."; +} + +/***************************************************************************** + * Functions that return packed sections + *****************************************************************************/ +uint8_t *demux_get_current_packed_PAT( unsigned int *pi_pack_size ) { + return psi_pack_sections( pp_current_pat_sections, pi_pack_size ); +} + +uint8_t *demux_get_current_packed_CAT( unsigned int *pi_pack_size ) { + return psi_pack_sections( pp_current_cat_sections, pi_pack_size ); +} + +uint8_t *demux_get_current_packed_NIT( unsigned int *pi_pack_size ) { + return psi_pack_sections( pp_current_nit_sections, pi_pack_size ); +} + +uint8_t *demux_get_current_packed_SDT( unsigned int *pi_pack_size ) { + return psi_pack_sections( pp_current_sdt_sections, pi_pack_size ); +} + +uint8_t *demux_get_packed_PMT( uint16_t i_sid, unsigned int *pi_pack_size ) { + sid_t *p_sid = FindSID( i_sid ); + if ( p_sid != NULL && pmt_validate( p_sid->p_current_pmt ) ) + return psi_pack_section( p_sid->p_current_pmt, pi_pack_size ); + return NULL; +} + +inline void demux_get_PID_info( uint16_t i_pid, uint8_t *p_data ) { + ts_pid_info_t *p_info = (ts_pid_info_t *)p_data; + *p_info = p_pids[i_pid].info; +} + +inline void demux_get_PIDS_info( uint8_t *p_data ) { + int i_pid; + for (i_pid = 0; i_pid < MAX_PIDS; i_pid++ ) + demux_get_PID_info( i_pid, p_data + ( i_pid * sizeof(ts_pid_info_t) ) ); } diff -Nru dvblast-1.2/dvb.c dvblast-2.1.0/dvb.c --- dvblast-1.2/dvb.c 2009-12-29 08:25:06.000000000 +0000 +++ dvblast-2.1.0/dvb.c 2012-01-03 22:06:11.000000000 +0000 @@ -1,8 +1,7 @@ /***************************************************************************** * dvb.c: linux-dvb input for DVBlast ***************************************************************************** - * Copyright (C) 2008-2009 VideoLAN - * $Id: dvb.c 92 2009-12-29 08:25:06Z md $ + * Copyright (C) 2008-2010 VideoLAN * * Authors: Christophe Massiot * @@ -23,7 +22,9 @@ #include #include +#include #include +#include #include #include #include @@ -47,26 +48,29 @@ #include "en50221.h" #include "comm.h" +#include + /***************************************************************************** * 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 DVR_BUFFER_SIZE 40*188*1024 /* bytes */ static int i_frontend, i_dvr; static fe_status_t i_last_status; static mtime_t i_frontend_timeout; +static mtime_t i_last_packet = 0; static mtime_t i_ca_next_event = 0; static block_t *p_freelist = NULL; -mtime_t i_ca_timeout = 0; /***************************************************************************** * Local prototypes *****************************************************************************/ static block_t *DVRRead( void ); static void FrontendPoll( void ); -static void FrontendSet( void ); +static void FrontendSet( bool b_reset ); /***************************************************************************** * dvb_Open @@ -77,6 +81,8 @@ 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 ); if( (i_frontend = open(psz_tmp, O_RDWR | O_NONBLOCK)) < 0 ) { @@ -85,7 +91,7 @@ exit(1); } - FrontendSet(); + FrontendSet(true); sprintf( psz_tmp, "/dev/dvb/adapter%d/dvr%d", i_adapter, i_fenum ); @@ -103,16 +109,25 @@ } 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 *****************************************************************************/ -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; + block_t *p_blocks = NULL; memset( ufds, 0, sizeof(ufds) ); ufds[0].fd = i_dvr; @@ -121,12 +136,20 @@ ufds[1].events = POLLERR | POLLPRI; if ( i_comm_fd != -1 ) { - ufds[2].fd = i_comm_fd; - ufds[2].events = POLLIN; - i_nb_fd = 3; + ufds[i_nb_fd].fd = i_comm_fd; + ufds[i_nb_fd].events = POLLIN; + 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 ) { @@ -135,29 +158,61 @@ return NULL; } - if ( i_ca_handle && i_ca_type == CA_CI_LINK - && mdate() > i_ca_next_event ) + if ( ufds[1].revents ) + FrontendPoll(); + + if ( ufds[0].revents ) { - en50221_Poll(); - i_ca_next_event = mdate() + i_ca_timeout; + p_blocks = DVRRead(); + i_wallclock = mdate(); } - if ( ufds[1].revents ) - FrontendPoll(); + if ( p_blocks != NULL ) + 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("\n"); + break; + default: + break; + } + exit(EXIT_STATUS_FRONTEND_TIMEOUT); + } msg_Warn( NULL, "no lock, tuning again" ); - FrontendSet(); + FrontendSet(false); } if ( i_comm_fd != -1 && ufds[2].revents ) comm_Read(); - if ( ufds[0].revents ) - return DVRRead(); - - return NULL; + return p_blocks; } /***************************************************************************** @@ -313,8 +368,19 @@ IF_UP( FE_HAS_LOCK ) { 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("\n"); + break; + default: + printf("frontend has acquired lock\n" ); + } 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 */ if( ioctl( i_frontend, FE_READ_BER, &i_value ) >= 0 ) @@ -327,14 +393,21 @@ else { msg_Dbg( NULL, "frontend has lost lock" ); - i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT; + switch (i_print_type) { + case PRINT_XML: + printf("\n"); + break; + default: + printf("frontend has lost lock\n" ); + } + i_frontend_timeout = i_wallclock + i_frontend_timeout_duration; } IF_UP( FE_REINIT ) { /* The frontend was reinited. */ msg_Warn( NULL, "reiniting frontend"); - FrontendSet(); + FrontendSet(true); } } #undef IF_UP @@ -452,13 +525,13 @@ if( ioctl( i_frontend, FE_DISEQC_SEND_BURST, 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); } msleep(100000); /* ... */ } - else if ( ioctl( i_frontend, FE_SET_TONE, fe_tone ) < 0 ) + if ( ioctl( i_frontend, FE_SET_TONE, fe_tone ) < 0 ) { msg_Err( NULL, "FE_SET_TONE failed (%s)", strerror(errno) ); exit(1); @@ -476,8 +549,51 @@ #if DVB_API_VERSION >= 5 /***************************************************************************** - * GetModulation : helper function for both APIs + * 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) { #define GET_MODULATION( mod ) \ @@ -493,18 +609,151 @@ GET_MODULATION(QAM_AUTO); GET_MODULATION(VSB_8); GET_MODULATION(VSB_16); -#if DVB_API_VERSION >= 5 GET_MODULATION(PSK_8); GET_MODULATION(APSK_16); GET_MODULATION(APSK_32); GET_MODULATION(DQPSK); -#endif #undef GET_MODULATION msg_Err( NULL, "invalid modulation %s", psz_modulation ); exit(1); } +static fe_pilot_t GetPilot(void) +{ + switch ( i_pilot ) + { + case 0: return PILOT_OFF; + case 1: return PILOT_ON; + default: + msg_Warn( NULL, "invalid pilot %d", i_pilot ); + case -1: return PILOT_AUTO; + } +} + +static fe_rolloff_t GetRollOff(void) +{ + switch ( i_rolloff ) + { + case -1: + case 0: return ROLLOFF_AUTO; + case 20: return ROLLOFF_20; + case 25: return ROLLOFF_25; + default: + msg_Warn( NULL, "invalid rolloff %d", i_rolloff ); + 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 + *****************************************************************************/ +static const char *GetFrontendTypeName( fe_type_t type ) +{ + switch(type) + { + case FE_QPSK: return "QPSK (DVB-S/S2)"; + case FE_QAM: return "QAM (DVB-C)"; + case FE_OFDM: return "OFDM (DVB-T)"; + case FE_ATSC: return "ATSC"; + default: return "unknown"; + } +} + +static void FrontendInfo( struct dvb_frontend_info info ) +{ + msg_Dbg( NULL, "Frontend \"%s\" type \"%s\" supports:", + info.name, GetFrontendTypeName(info.type) ); + msg_Dbg( NULL, " frequency min: %d, max: %d, stepsize: %d, tolerance: %d", + info.frequency_min, info.frequency_max, + info.frequency_stepsize, info.frequency_tolerance ); + msg_Dbg( NULL, " symbolrate min: %d, max: %d, tolerance: %d", + info.symbol_rate_min, info.symbol_rate_max, info.symbol_rate_tolerance); + msg_Dbg( NULL, " capabilities:" ); + +#define FRONTEND_INFO(caps,val,msg) \ + if ( caps & val ) \ + msg_Dbg( NULL, " %s", msg ); + + 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_FEC_1_2, "FEC_1_2" ) + FRONTEND_INFO( info.caps, FE_CAN_FEC_2_3, "FEC_2_3" ) + FRONTEND_INFO( info.caps, FE_CAN_FEC_3_4, "FEC_3_4" ) + FRONTEND_INFO( info.caps, FE_CAN_FEC_4_5, "FEC_4_5" ) + FRONTEND_INFO( info.caps, FE_CAN_FEC_5_6, "FEC_5_6" ) + FRONTEND_INFO( info.caps, FE_CAN_FEC_6_7, "FEC_6_7" ) + FRONTEND_INFO( info.caps, FE_CAN_FEC_7_8, "FEC_7_8" ) + FRONTEND_INFO( info.caps, FE_CAN_FEC_8_9, "FEC_8_9" ) + FRONTEND_INFO( info.caps, FE_CAN_FEC_AUTO,"FEC_AUTO") + FRONTEND_INFO( info.caps, FE_CAN_QPSK, "QPSK" ) + FRONTEND_INFO( info.caps, FE_CAN_QAM_16, "QAM_16" ) + FRONTEND_INFO( info.caps, FE_CAN_QAM_32, "QAM_32" ) + FRONTEND_INFO( info.caps, FE_CAN_QAM_64, "QAM_64" ) + FRONTEND_INFO( info.caps, FE_CAN_QAM_128,"QAM_128") + FRONTEND_INFO( info.caps, FE_CAN_QAM_256,"QAM_256") + FRONTEND_INFO( info.caps, FE_CAN_QAM_AUTO,"QAM_AUTO" ) + FRONTEND_INFO( info.caps, FE_CAN_TRANSMISSION_MODE_AUTO, "TRANSMISSION_MODE_AUTO" ) + FRONTEND_INFO( info.caps, FE_CAN_BANDWIDTH_AUTO, "BANDWIDTH_AUTO" ) + FRONTEND_INFO( info.caps, FE_CAN_GUARD_INTERVAL_AUTO, "GUARD_INTERVAL_AUTO" ) + FRONTEND_INFO( info.caps, FE_CAN_HIERARCHY_AUTO, "HIERARCHY_AUTO" ) + FRONTEND_INFO( info.caps, FE_CAN_8VSB, "8VSB" ) + FRONTEND_INFO( info.caps, FE_CAN_16VSB,"16VSB" ) + FRONTEND_INFO( info.caps, FE_HAS_EXTENDED_CAPS, "EXTENDED_CAPS" ) +#if DVBAPI_VERSION >= 501 + FRONTEND_INFO( info.caps, FE_CAN_2G_MODULATION, "2G_MODULATION" ) +#endif + FRONTEND_INFO( info.caps, FE_NEEDS_BENDING, "NEEDS_BENDING" ) + FRONTEND_INFO( info.caps, FE_CAN_RECOVER, "FE_CAN_RECOVER" ) + FRONTEND_INFO( info.caps, FE_CAN_MUTE_TS, "FE_CAN_MUTE_TS" ) +#undef FRONTEND_INFO +} + /***************************************************************************** * FrontendSet *****************************************************************************/ @@ -570,13 +819,41 @@ .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 MODULATION 1 #define INVERSION 2 #define SYMBOL_RATE 3 #define BANDWIDTH 3 +#define FEC_INNER 4 +#define FEC_LP 5 +#define GUARD 6 +#define PILOT 6 +#define TRANSMISSION 7 +#define ROLLOFF 7 +#define HIERARCHY 8 + +struct dtv_property pclear[] = { + { .cmd = DTV_CLEAR }, +}; + +struct dtv_properties cmdclear = { + .num = 1, + .props = pclear +}; -static void FrontendSet( void ) +static void FrontendSet( bool b_init ) { struct dvb_frontend_info info; struct dtv_properties *p; @@ -587,29 +864,48 @@ exit(1); } + if ( b_init ) + FrontendInfo( info ); + + /* Clear frontend commands */ + if ( ioctl( i_frontend, FE_SET_PROPERTY, &cmdclear ) < 0 ) + { + msg_Err( NULL, "Unable to clear frontend" ); + exit(1); + } + switch ( info.type ) { case FE_OFDM: p = &dvbt_cmdseq; p->props[FREQUENCY].u.data = i_frequency; + p->props[INVERSION].u.data = GetInversion(); if ( psz_modulation != NULL ) p->props[MODULATION].u.data = GetModulation(); p->props[BANDWIDTH].u.data = i_bandwidth * 1000000; - - msg_Dbg( NULL, "tuning OFDM frontend to f=%d bandwidth=%d modulation=%s", - i_frequency, i_bandwidth, - psz_modulation == NULL ? "qam_auto" : psz_modulation ); + p->props[FEC_INNER].u.data = GetFECInner(info.caps); + p->props[FEC_LP].u.data = GetFECLP(info.caps); + p->props[GUARD].u.data = GetGuard(); + 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; case FE_QAM: p = &dvbc_cmdseq; p->props[FREQUENCY].u.data = i_frequency; + p->props[INVERSION].u.data = GetInversion(); if ( psz_modulation != NULL ) p->props[MODULATION].u.data = GetModulation(); p->props[SYMBOL_RATE].u.data = i_srate; - msg_Dbg( NULL, "tuning QAM frontend to f=%d srate=%d modulation=%s", - i_frequency, i_srate, + msg_Dbg( NULL, "tuning QAM frontend to f=%d srate=%d inversion=%d modulation=%s", + i_frequency, i_srate, i_inversion, psz_modulation == NULL ? "qam_auto" : psz_modulation ); break; @@ -618,16 +914,32 @@ { p = &dvbs2_cmdseq; p->props[MODULATION].u.data = GetModulation(); + p->props[PILOT].u.data = GetPilot(); + p->props[ROLLOFF].u.data = GetRollOff(); } else p = &dvbs_cmdseq; + p->props[INVERSION].u.data = GetInversion(); p->props[SYMBOL_RATE].u.data = i_srate; + p->props[FEC_INNER].u.data = GetFECInner(info.caps); p->props[FREQUENCY].u.data = FrontendDoDiseqc(); - msg_Dbg( NULL, "tuning QPSK frontend to f=%d srate=%d modulation=%s", - i_frequency, i_srate, - psz_modulation == NULL ? "legacy" : psz_modulation ); + 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_inversion, i_fec, i_rolloff, + 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; default: @@ -652,12 +964,15 @@ } i_last_status = 0; - i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT; + i_frontend_timeout = i_wallclock + i_frontend_timeout_duration; } #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_parameters fep; @@ -700,8 +1015,8 @@ fep.u.qam.fec_inner = FEC_AUTO; fep.u.qam.modulation = QAM_AUTO; - msg_Dbg( NULL, "tuning QAM frontend to f=%d, srate=%d", i_frequency, - i_srate ); + msg_Dbg( NULL, "tuning QAM frontend to f=%d, srate=%d", + i_frequency, i_srate ); break; case FE_QPSK: @@ -746,7 +1061,7 @@ } i_last_status = 0; - i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT; + i_frontend_timeout = i_wallclock + i_frontend_timeout_duration; } #endif /* S2API */ @@ -763,6 +1078,7 @@ msg_Err( NULL, "ioctl FE_GET_INFO failed (%s)", strerror(errno) ); return RET_ERR; } + if ( ioctl( i_frontend, FE_READ_STATUS, &p_ret->i_status ) < 0 ) { msg_Err( NULL, "ioctl FE_READ_STATUS failed (%s)", strerror(errno) ); diff -Nru dvblast-1.2/dvblast.1 dvblast-2.1.0/dvblast.1 --- dvblast-1.2/dvblast.1 2010-03-01 12:46:25.000000000 +0000 +++ dvblast-2.1.0/dvblast.1 2012-01-03 22:06:11.000000000 +0000 @@ -1,16 +1,29 @@ -.TH DVBLAST "1" "January 2010" "DVBlast 1.1" "User Commands" +.TH DVBLAST "1" "January 2010" "DVBlast 2.1" "User Commands" .SH NAME DVBlast \- Simple and powerful dvb streaming application .SH SYNOPSIS .B dvblast -[\fI-q\fR] \fI-c \fR [\fI-r \fR] [\fI-t \fR] [\fI-o \fR] [\fI-i \fR] [\fI-a \fR] [\fI-n \fR] [\fI-S \fR] \fI-f \fR [\fI-s \fR] [\fI-v <0|13|18>\fR] [\fI-p\fR] [\fI-b \fR] [\fI-m \fR] [\fI-e\fR] [\fI-T\fR] +[\fI-q\fR] [\fI-c \fR] [\fI-r \fR] [\fI-t \fR] [\fI-o \fR] +[\fI-i \fR] [\fI-a \fR] [\fI-n \fR] [\fI-S \fR] +[\fI-f \fR] [\fI-D [:][[@][:]][/]\fR] [\fI-A \fR] +[\fI-s \fR] [\fI-v <0|13|18>\fR] [\fI-p\fR] [\fI-b \fR] [\fI-I \fR] +[\fI-F \fR] [\fI-m \fR] [\fI-R \fR] [\fI-P \fR] [\fI-K \fR] +[\fI-G \fR] [\fI-H \fR] [\fI-X \fR] [\fI-O \fR] +[\fI-u\fR] [\fI-w\fR] [\fI-U\fR] [\fI-L \fR] [\fI-E \fR] [\fI-d [<:port>][/]\fR] +[\fI-z\fR] [\fI-C\fR] [\fI-e\fR] [\fI-M \fR] [\fI-N \fR] [\fI-T\fR] [\fI-j \fR] +[\fI-W\fR] [\fI-Y\fR] [\fI-l\fR] [\fI-g \fR] [\fI-Z \fR] [\fI-V\fR] [\fI-h\fR] +[\fI-J \fR] [\fI-B \fR] [\fI-Q \fR] [\fI-x \fR] .SH DESCRIPTION -DVBlast is a simple and powerful streaming application based on the linux-dvb 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 a simple and powerful streaming application based on the linux-dvb +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 .PP .TP @@ -23,27 +36,78 @@ \fB\-b\fR, \fB\-\-bandwidth\fR Frontend bandwidth .TP +\fB\-B\fR, \fB\-\-provider-name\fR +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 Use the given configuration file .TP +\fB\-C\fR, \fB\-\-dvb-compliance\fR +Pass through or build the mandatory DVB tables +.TP \fB\-d\fR, \fB\-\-duplicate\fR Duplicate all received packets to a given destination .TP \fB\-D\fR, \fB\-\-rtp\-input\fR Read packets from a multicast address instead of a DVB card .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 Enable EPG pass through (EIT data) .TP +\fB\-E\fR, \fB\-\-retention\fR +Maximum retention allowed between input and output (default: 40 ms) +.TP \fB\-f\fR, \fB\-\-frequency\fR Frontend frequency .TP +\fB\-F\fR, \fB\-\-fec\-inner\fR +Forward Error Correction used by satellite (FEC Inner) +.br +DVB-S2 0|12|23|34|35|56|78|89|910|999 (default auto: 999) +.TP +\fB\-G\fR, \fB\-\-guard\fR +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 Print the help message .TP +\fB\-H\fR, \fB\-\-hierarchy\fR +DVB-T hierarchy (0, 1, 2, 4 or -1 auto, default) +.TP \fB\-i\fR, \fB\-\-priority\fR Real time priority .TP +\fB\-I\fR, \fB\-\-inversion\fR +Inversion (-1 auto, 0 off, 1 on) +.TP +\fB\-j\fR, \fB\-\-system-charset\fR +Character set used for printing messages (default UTF-8) +.TP +\fB\-J\fR, \fB\-\-dvb-charset\fR +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 +Set the program name that will appear in syslog (default: dvblast /the executable name/). +.TP +\fB\-L\fR, \fB\-\-latency\fR +Maximum latency allowed between input and output (default: 100 ms) +.TP \fB\-m\fR, \fB\-\-modulation\fR Modulation .br @@ -53,22 +117,42 @@ .br DVB-S2 qpsk|psk_8 (default legacy DVB-S) .TP +\fB\-M\fR, \fB\-\-network-name\fR +DVB network name to declare in the NIT +.TP \fB\-n\fR, \fB\-\-frontend\-number\fR -THe frontend number +The frontend number +.TP +\fB\-N\fR, \fB\-\-network-id\fR +DVB network ID to declare in the NIT .TP \fB\-o\fR, \fB\-\-rtp-output\fR RTP output IP .TP +\fB\-O\fR, \fB\-\-lock-timeout\fR +Timeout for the lock operation (in ms) +.TP \fB\-p\fR, \fB\-\-force\-pulse\fR Force 22kHz pulses for high-band selection (DVB-S) .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) .TP +\fB\-Q\fR, \fB\-\-quit-timeout\fR +When locked, quit after this delay (in ms), or after the first lock timeout +.TP \fB\-r\fR, \fB\-\-remote\-socket\fR Remote socket to use .TP -\fB\-s\fR, \fB\-\-symbole\-rate\fR +\fB\-R\fR, \fB\-\-rolloff\fR +Rolloff value to use +.br +DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35) +.TP +\fB\-s\fR, \fB\-\-symbol\-rate\fR Symbole rate .TP \fB\-S\fR, \fB\-\-diseqc\fR @@ -92,11 +176,26 @@ \fB\-V\fR, \fB\-\-version\fR Only display the version .TP -\fB\-W\fR, \fB\-\-slow\-cam\fR -Add extra delays for slow CAMs +\fB\-w\fR, \fB\-\-select-pmts\fR +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 +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 +Every 10 seconds log statistics in . The file has 4 numbers in it +and the format is: .SH SEE ALSO Read the README file for more information about the configuration of dvblast. .SH AUTHORS -Writen by Marian Ďurkovič, Andy Gatward and Christophe Massiot -.SH LICENCE -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. +Writen by Marian Ďurkovič, Andy Gatward, Christophe Massiot and Jean-Paul Saman +.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. diff -Nru dvblast-1.2/dvblast.c dvblast-2.1.0/dvblast.c --- dvblast-1.2/dvblast.c 2010-02-28 21:08:45.000000000 +0000 +++ dvblast-2.1.0/dvblast.c 2012-01-03 22:06:11.000000000 +0000 @@ -1,8 +1,7 @@ /***************************************************************************** * dvblast.c ***************************************************************************** - * Copyright (C) 2004, 2008-2009 VideoLAN - * $Id: dvblast.c 115 2010-02-28 21:08:45Z ivoire $ + * Copyright (C) 2004, 2008-2011 VideoLAN * * Authors: Christophe Massiot * Andy Gatward @@ -26,7 +25,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -34,173 +36,323 @@ #include #include #include +#include #include "dvblast.h" #include "version.h" +#ifdef HAVE_ICONV +#include +#endif + +#include +#include + +#include "mrtg-cnt.h" + /***************************************************************************** * Local declarations *****************************************************************************/ +mtime_t i_wallclock = 0; output_t **pp_outputs = NULL; int i_nb_outputs = 0; -output_t output_dup = { 0 }; +output_t output_dup; static char *psz_conf_file = NULL; char *psz_srv_socket = NULL; -int i_ttl = 64; -in_addr_t i_ssrc = 0; static int i_priority = -1; int i_adapter = 0; int i_fenum = 0; int i_frequency = 0; +int i_inversion = -1; int i_srate = 27500000; +int i_fec = 999; +int i_rolloff = 35; int i_satnum = 0; int i_voltage = 13; int b_tone = 0; int i_bandwidth = 8; 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_slow_cam = 0; -int b_output_udp = 0; -int b_enable_epg = 0; -int b_unique_tsid = 0; -volatile int b_hup_received = 0; -int i_verbose = DEFAULT_VERBOSITY; -uint16_t i_src_port = DEFAULT_PORT; -in_addr_t i_src_addr = { 0 }; -int b_src_rawudp = 0; +int b_any_type = 0; +int b_select_pmts = 0; +int b_random_tsid = 0; +uint16_t i_network_id = 0xffff; +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_syslog = 0; +char *psz_syslog_ident = NULL; + +bool b_enable_emm = false; +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; -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; void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ) = NULL; /***************************************************************************** * Configuration files *****************************************************************************/ -static void ReadConfiguration( char *psz_file ) +void config_Init( output_config_t *p_config ) { - FILE *p_file; - char psz_line[2048]; - int i; + memset( p_config, 0, sizeof(output_config_t) ); - if ( psz_file == NULL ) - { - msg_Err( NULL, "no config file" ); - return; + p_config->psz_displayname = NULL; + p_config->psz_service_name = NULL; + p_config->psz_service_provider = NULL; + + p_config->i_family = AF_UNSPEC; + p_config->connect_addr.ss_family = AF_UNSPEC; + p_config->bind_addr.ss_family = AF_UNSPEC; + 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; +} - if ( (p_file = fopen( psz_file, "r" )) == NULL ) +bool config_ParseHost( output_config_t *p_config, char *psz_string ) +{ + struct addrinfo *p_ai; + int i_mtu; + + p_config->psz_displayname = strdup( psz_string ); + + p_ai = ParseNodeService( psz_string, &psz_string, DEFAULT_PORT ); + if ( p_ai == NULL ) return false; + memcpy( &p_config->connect_addr, p_ai->ai_addr, p_ai->ai_addrlen ); + freeaddrinfo( p_ai ); + + p_config->i_family = p_config->connect_addr.ss_family; + if ( p_config->i_family == AF_UNSPEC ) return false; + + if ( psz_string == NULL || !*psz_string ) goto end; + + if ( *psz_string == '@' ) { - msg_Err( NULL, "can't fopen config file %s", psz_file ); - return; + psz_string++; + p_ai = ParseNodeService( psz_string, &psz_string, 0 ); + 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 ); } - while ( fgets( psz_line, sizeof(psz_line), p_file ) != NULL ) + while ( (psz_string = strchr( psz_string, '/' )) != NULL ) { - output_t *p_output = NULL; - char *psz_parser, *psz_token, *psz_token2, *psz_token3; - 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; + *psz_string++ = '\0'; - snprintf( sz_port, sizeof( sz_port ), "%d", DEFAULT_PORT ); +#define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) )) +#define ARG_OPTION( option ) (psz_string + strlen(option)) - if ( !strncmp( psz_line, "#", 1 ) ) - continue; - - psz_token = strtok_r( psz_line, "\t\n ", &psz_parser ); - if ( psz_token == NULL ) - continue; - - if ( (psz_token3 = strrchr( psz_token, '/' )) != NULL ) + if ( IS_OPTION("udp") ) + p_config->i_config |= OUTPUT_UDP; + 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=") ) { - *psz_token3 = '\0'; - if( strncasecmp( psz_token3 + 1, "udp", 3 ) == 0 ) - i_config |= OUTPUT_UDP; + if ( p_config->psz_service_name ) + free( p_config->psz_service_name ); + p_config->psz_service_name = config_stropt( ARG_OPTION("srvname=") ); } - - if ( !strncmp( psz_token, "[", 1 ) ) + else if ( IS_OPTION("srvprovider=") ) + { + if ( !p_config->psz_service_provider ) + free( p_config->psz_service_provider ); + p_config->psz_service_provider = config_stropt( ARG_OPTION("srvprovider=") ); + } + else if ( IS_OPTION("ssrc=") ) { - if ( (psz_token2 = strchr( psz_token, ']' ) ) == NULL ) - continue; + 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 ); - char *psz_maddr = malloc( psz_token2 - psz_token ); - memset( psz_maddr, '\0', ( psz_token2 - psz_token ) ); - strncpy( psz_maddr, psz_token + 1, ( psz_token2 - psz_token - 1 )); +#undef IS_OPTION +#undef ARG_OPTION + } - if ( (psz_token2 = strchr( psz_token2, ':' )) != NULL ) - { - *psz_token2 = '\0'; - snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token2 + 1 ) ); - } + if ( !p_config->psz_service_provider && psz_provider_name ) + p_config->psz_service_provider = strdup( psz_provider_name ); - p_addr = malloc( sizeof( p_addr ) ); +end: + i_mtu = p_config->i_family == AF_INET6 ? DEFAULT_IPV6_MTU : + DEFAULT_IPV4_MTU; + + if ( !p_config->i_mtu ) + p_config->i_mtu = i_mtu; + else if ( p_config->i_mtu < TS_SIZE + RTP_HEADER_SIZE ) + { + msg_Warn( NULL, "invalid MTU %d, setting %d", p_config->i_mtu, i_mtu ); + p_config->i_mtu = i_mtu; + } - 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; + return true; +} - int i_ai = getaddrinfo( psz_maddr, sz_port, &ai_hints, &p_addr ); - if ( i_ai != 0 ) - { - msg_Err( NULL, "Cannot configure output [%s]:%s: %s", psz_maddr, - sz_port, gai_strerror( i_ai ) ); - continue; - } +static void config_Print( output_config_t *p_config ) +{ + 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'; - psz_displayname = malloc( INET6_ADDRSTRLEN + 8 ); - snprintf( psz_displayname, ( INET6_ADDRSTRLEN + 8 ), "[%s]:%s", - psz_maddr, sz_port ); + msg_Dbg( NULL, psz_format, p_config->psz_displayname, p_config->i_config, + p_config->i_sid, p_config->i_nb_pids ); +} - free( psz_maddr ); - } - else - { - if ( (psz_token2 = strrchr( psz_token, ':' )) != NULL ) - { - *psz_token2 = '\0'; - snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token2 + 1 ) ); - } +static void config_ReadFile( char *psz_file ) +{ + FILE *p_file; + char psz_line[2048]; + int i; - p_addr = malloc( sizeof( p_addr ) ); + if ( psz_file == NULL ) + { + msg_Err( NULL, "no config file" ); + return; + } - 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_INET; + if ( (p_file = fopen( psz_file, "r" )) == NULL ) + { + msg_Err( NULL, "can't fopen config file %s", psz_file ); + return; + } - int i_ai = getaddrinfo( psz_token, sz_port, &ai_hints, &p_addr ); - if ( i_ai != 0 ) - { - msg_Err( NULL, "Cannot configure output %s:%s: %s", psz_token, - sz_port, gai_strerror( i_ai ) ); - continue; - } + 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; + + config_Defaults( &config ); - psz_displayname = malloc( INET_ADDRSTRLEN + 6 ); - snprintf( psz_displayname, ( INET_ADDRSTRLEN + 6 ), "%s:%s", - psz_token, sz_port ); + psz_token = strtok_r( psz_line, "\t\n ", &psz_parser ); + if ( psz_token == NULL || !config_ParseHost( &config, psz_token )) + { + config_Free( &config ); + continue; } psz_token = strtok_r( NULL, "\t\n ", &psz_parser ); if ( psz_token == NULL ) + { + config_Free( &config ); continue; - if( atoi( psz_token ) ) - i_config |= OUTPUT_WATCH; + } + if( atoi( psz_token ) == 1 ) + config.i_config |= OUTPUT_WATCH; else - i_config &= ~OUTPUT_WATCH; + config.i_config &= ~OUTPUT_WATCH; psz_token = strtok_r( NULL, "\t\n ", &psz_parser ); if ( psz_token == NULL ) + { + config_Free( &config ); 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 ); if ( psz_token != NULL ) @@ -211,82 +363,52 @@ psz_token = strtok_r( psz_token, ",", &psz_parser ); if ( psz_token == NULL ) break; - pi_pids = realloc( pi_pids, - (i_nb_pids + 1) * sizeof(uint16_t) ); - pi_pids[i_nb_pids++] = strtol(psz_token, NULL, 0); + config.pi_pids = realloc( config.pi_pids, + (config.i_nb_pids + 1) * sizeof(uint16_t) ); + config.pi_pids[config.i_nb_pids++] = strtol(psz_token, NULL, 0); psz_token = NULL; } } - msg_Dbg( NULL, "conf: %s w=%d sid=%d pids[%d]=%d,%d,%d,%d,%d...", - 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++ ) - { - 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 ) && - ( p_esad->sin_port == p_nsad->sin_port ) ) - { - 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; + config_Print( &config ); - 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; - } - } - } + p_output = output_Find( &config ); - if ( i == i_nb_outputs ) - p_output = output_Create( i_config, psz_displayname, (void *)p_addr ); + if ( p_output == NULL ) + p_output = output_Create( &config ); if ( p_output != NULL ) { - demux_Change( p_output, i_sid, pi_pids, i_nb_pids ); - p_output->i_config |= OUTPUT_STILL_PRESENT; + free( p_output->config.psz_displayname ); + 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 ); - free( pi_pids ); - freeaddrinfo( p_addr ); + config_Free( &config ); } fclose( p_file ); for ( i = 0; i < i_nb_outputs; i++ ) { - if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && - !( pp_outputs[i]->i_config & OUTPUT_STILL_PRESENT ) ) + output_t *p_output = pp_outputs[i]; + 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 ); - demux_Change( pp_outputs[i], 0, NULL, 0 ); - output_Close( pp_outputs[i] ); + msg_Dbg( NULL, "closing %s", p_output->config.psz_displayname ); + demux_Change( p_output, &config ); + output_Close( p_output ); } - pp_outputs[i]->i_config &= ~OUTPUT_STILL_PRESENT; + p_output->config.i_config &= ~OUTPUT_STILL_PRESENT; + config_Free( &config ); } } @@ -295,7 +417,10 @@ *****************************************************************************/ 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; } /***************************************************************************** @@ -303,7 +428,7 @@ *****************************************************************************/ 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 ); } @@ -312,55 +437,104 @@ *****************************************************************************/ void usage() { - msg_Raw( NULL, "Usage: dvblast [-q] [-c ] [-r ] [-t ] [-o ] [-i ] [-a ] [-n ] [-S ] [-f |-D :|-A ] [-s ] [-v <0|13|18>] [-p] [-b ] [-m ] [-e] [-T]" ); + DisplayVersion(); + msg_Raw( NULL, "Usage: dvblast [-q] [-c ] [-r ] [-t ] [-o ] " + "[-i ] [-a ] [-n ] [-S ] " + "[-f |-D [[:]@][:][/]*|-A ] " + "[-s ] [-v <0|13|18>] [-p] [-b ] [-I ] " + "[-F ] [-m ] [-P ] [-K ] " + "[-G ] [-H ] [-X ] [-O ] " + "[-u] [-w] [-U] [-L ] [-E ] [-d [<:port>][/]*] " + "[-z] [-C [-e] [-M ]] [-T] [-j ] " + "[-W] [-Y] [-l] [-g ] [-Z ] [-V] [-h] [-B ]" + "[-J ] [-Q ] [-x ]" ); msg_Raw( NULL, "Input:" ); - msg_Raw( NULL, " -a --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, " -b --bandwidth frontend bandwith" ); 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 --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, " -I --inversion Inversion (-1 auto, 0 off, 1 on)" ); 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-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, " -n --frontend-number " ); msg_Raw( NULL, " -p --force-pulse force 22kHz pulses for high-band selection (DVB-S)" ); - msg_Raw( NULL, " -s --symbole-rate" ); + 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, " DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35)" ); + 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, " -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, " -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, " -c --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, " -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 " ); msg_Raw( NULL, " -t --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, " -z --any-type pass through all ESs from the PMT, of any type" ); msg_Raw( NULL, "Misc:" ); msg_Raw( NULL, " -h --help display this full help" ); - msg_Raw( NULL, " -i --priority " ); - msg_Raw( NULL, " -q be quiet (less verbosity, repeat or use number for even quieter)" ); + msg_Raw( NULL, " -i --priority " ); + 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, " -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 " ); msg_Raw( NULL, " -V --version only display the version" ); + msg_Raw( NULL, " -Z --mrtg-file Log input packets and errors into mrtg-file" ); exit(1); } 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; int i_error; int c; + struct sigaction sa; + sigset_t set; - DisplayVersion(); + int b_enable_syslog = 0; if ( i_argc == 1 ) usage(); + /* + * The only short options left are: ky0123456789 + * Use them wisely. + */ static const struct option long_options[] = { { "config-file", required_argument, NULL, 'c' }, @@ -371,26 +545,52 @@ { "adapter", required_argument, NULL, 'a' }, { "frontend-number", required_argument, NULL, 'n' }, { "frequency", required_argument, NULL, 'f' }, + { "fec-inner", required_argument, NULL, 'F' }, + { "rolloff", required_argument, NULL, 'R' }, { "symbol-rate", required_argument, NULL, 's' }, { "diseqc", required_argument, NULL, 'S' }, { "voltage", required_argument, NULL, 'v' }, { "force-pulse", no_argument, NULL, 'p' }, { "bandwidth", required_argument, NULL, 'b' }, + { "inversion", required_argument, NULL, 'I' }, { "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' }, - { "slow-cam", no_argument, NULL, 'W' }, + { "select-pmts", no_argument, NULL, 'w' }, { "udp", no_argument, NULL, 'U' }, { "unique-ts-id", no_argument, NULL, 'T' }, + { "latency", required_argument, NULL, 'L' }, + { "retention", required_argument, NULL, 'E' }, { "duplicate", required_argument, NULL, 'd' }, { "rtp-input", required_argument, NULL, 'D' }, { "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' }, + { "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-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' }, { "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:s:S:v:pb:m:uWUTd:D:A:ehV", 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 ) { @@ -422,16 +622,11 @@ break; 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; break; case 't': - i_ttl = strtol( optarg, NULL, 0 ); + i_ttl_global = strtol( optarg, NULL, 0 ); break; case 'o': @@ -439,7 +634,7 @@ struct in_addr maddr; if ( !inet_aton( optarg, &maddr ) ) usage(); - i_ssrc = maddr.s_addr; + memcpy( pi_ssrc_global, &maddr.s_addr, 4 * sizeof(uint8_t) ); break; } @@ -461,10 +656,19 @@ usage(); pf_Open = dvb_Open; pf_Read = dvb_Read; + pf_Reset = dvb_Reset; pf_SetFilter = dvb_SetFilter; pf_UnsetFilter = dvb_UnsetFilter; break; + case 'F': + i_fec = strtol( optarg, NULL, 0 ); + break; + + case 'R': + i_rolloff = strtol( optarg, NULL, 0 ); + break; + case 's': i_srate = strtol( optarg, NULL, 0 ); break; @@ -485,173 +689,161 @@ i_bandwidth = strtol( optarg, NULL, 0 ); break; + case 'I': + i_inversion = strtol( optarg, NULL, 0 ); + break; + case 'm': psz_modulation = optarg; break; - case 'u': - b_budget_mode = 1; + case 'P': + i_pilot = strtol( optarg, NULL, 0 ); break; - case 'W': - b_slow_cam = 1; + case 'K': + i_fec_lp = strtol( optarg, NULL, 0 ); break; - case 'U': - b_output_udp = 1; + case 'G': + i_guard = strtol( optarg, NULL, 0 ); break; - case 'd': - { - 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; - } - - 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 ); - if ( i_ai != 0 ) - { - msg_Err( NULL, "Cannot duplicate to [%s]:%s: %s", psz_maddr, - sz_port, gai_strerror( i_ai ) ); - break; - } - - psz_displayname = malloc( INET6_ADDRSTRLEN + 20 ); - snprintf( psz_displayname, ( INET6_ADDRSTRLEN + 20 ), - "duplicate ([%s]:%s)", psz_maddr, sz_port ); + case 'X': + i_transmission = strtol( optarg, NULL, 0 ); + break; - i_dup_config |= OUTPUT_VALID; + case 'O': + i_frontend_timeout_duration = strtoll( optarg, NULL, 0 ) * 1000; + break; - free( psz_maddr ); - } - else - { - if ( (psz_token = strrchr( optarg, ':' )) != NULL ) - { - *psz_token = '\0'; - snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token + 1 ) ); - } + case 'H': + i_hierarchy = strtol( optarg, NULL, 0 ); + break; - 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_INET; + case 'u': + b_budget_mode = 1; + break; - int i_ai = getaddrinfo( optarg, sz_port, &ai_hints, &p_daddr ); - if ( i_ai != 0 ) - { - msg_Err( NULL, "Cannot duplicate to %s:%s: %s", optarg, - sz_port, gai_strerror( i_ai ) ); - break; - } + case 'w': + b_select_pmts = 1; + break; - psz_displayname = malloc( INET_ADDRSTRLEN + 18 ); - snprintf( psz_displayname, ( INET_ADDRSTRLEN + 18 ), - "duplicate (%s:%s)", optarg, sz_port ); + case 'U': + b_udp_global = true; + break; - i_dup_config |= OUTPUT_VALID; - } + case 'L': + i_latency_global = strtoll( optarg, NULL, 0 ) * 1000; + break; - if ( i_dup_config &= OUTPUT_VALID ) { - output_Init( &output_dup, i_dup_config, psz_displayname, p_daddr ); - } - else - msg_Err( NULL, "Invalid configuration for -d switch: %s" , optarg); + case 'E': + i_retention_global = strtoll( optarg, NULL, 0 ) * 1000; + break; - free( psz_displayname ); - freeaddrinfo( p_daddr ); + case 'd': + psz_dup_config = optarg; break; - } case 'D': - { - char *psz_token; - struct in_addr maddr; + psz_udp_src = optarg; if ( pf_Open != NULL ) usage(); - if ( psz_srv_socket != NULL ) - { - msg_Err( NULL, "-r is only available for linux-dvb input" ); - usage(); - } - pf_Open = udp_Open; pf_Read = udp_Read; + pf_Reset = udp_Reset; pf_SetFilter = udp_SetFilter; 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; - } case 'A': i_asi_adapter = strtol( optarg, NULL, 0 ); if ( pf_Open != NULL ) usage(); - if ( psz_srv_socket != NULL ) - { - msg_Err( NULL, "-r is only available for linux-dvb input" ); - usage(); - } pf_Open = asi_Open; pf_Read = asi_Read; + pf_Reset = asi_Reset; pf_SetFilter = asi_SetFilter; pf_UnsetFilter = asi_UnsetFilter; 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': - 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_unique_tsid = 1; + 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; + + case 'l': + b_enable_syslog = 1; + break; + + case 'g': + 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; case 'V': + DisplayVersion(); exit(0); break; + case 'Z': + psz_mrtg_file = optarg; + break; + case 'h': default: usage(); @@ -660,19 +852,107 @@ if ( optind < i_argc || pf_Open == NULL ) usage(); + if ( b_enable_syslog ) + msg_Connect( psz_syslog_ident ? psz_syslog_ident : pp_argv[0] ); + + if ( i_verbose ) + DisplayVersion(); + msg_Warn( NULL, "restarting" ); + switch (i_print_type) + { + case PRINT_XML: + printf("\n"); + printf("\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, "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() ); demux_Open(); + // init the mrtg logfile + mrtgInit(psz_mrtg_file); + if ( i_priority > 0 ) { memset( ¶m, 0, sizeof(struct sched_param) ); @@ -685,20 +965,62 @@ } } - ReadConfiguration( psz_conf_file ); + config_ReadFile( psz_conf_file ); if ( psz_srv_socket != NULL ) comm_Open(); for ( ; ; ) { - if ( b_hup_received ) + block_t *p_ts; + + if ( b_exit_now ) + { + msg_Info( NULL, "Shutdown was requested." ); + break; + } + + 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 ) { - b_hup_received = 0; - msg_Warn( NULL, "HUP received, reloading" ); - ReadConfiguration( psz_conf_file ); + switch (i_print_type) + { + case PRINT_XML: + printf("\n"); + break; + default: + break; + } + exit(EXIT_SUCCESS); } - demux_Run(); + 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 ) + msg_Disconnect(); + + if ( psz_srv_socket && i_comm_fd > -1 ) + unlink( psz_srv_socket ); + + return EXIT_SUCCESS; } diff -Nru dvblast-1.2/dvblastctl.c dvblast-2.1.0/dvblastctl.c --- dvblast-1.2/dvblastctl.c 2010-02-20 10:53:29.000000000 +0000 +++ dvblast-2.1.0/dvblastctl.c 2012-01-03 22:06:11.000000000 +0000 @@ -2,7 +2,6 @@ * dvblastctl.c ***************************************************************************** * Copyright (C) 2008 VideoLAN - * $Id: dvblastctl.c 105 2010-02-20 10:53:29Z md $ * * Authors: Christophe Massiot * @@ -22,12 +21,16 @@ *****************************************************************************/ #include +#include #include #include #include +#include #include +#include #include #include +#include #include #include #include @@ -35,30 +38,211 @@ #include #include +#include +#include +#include +#include + #include "dvblast.h" #include "en50221.h" #include "comm.h" #include "version.h" int i_verbose = 3; +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, "\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("\n"); +} + +void print_pids_footer( void ) +{ + if ( i_print_type == PRINT_XML ) + printf("\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("\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() { - msg_Raw( NULL, "DVBlastctl %d.%d.%d%s", VERSION_MAJOR, VERSION_MINOR, - VERSION_REVISION, VERSION_EXTRA ); - msg_Raw( NULL, "Usage: dvblastctl -r reload|shutdown|fe_status|mmi_status|mmi_open|mmi_close|mmi_get|mmi_send_text|mmi_send_choice [] []" ); + printf("DVBlastctl %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR, + VERSION_REVISION, VERSION_EXTRA ); + printf("Usage: dvblastctl -r [-x ] [cmd]\n"); + printf("Options:\n"); + printf(" -r --remote-socket Set socket name to .\n" ); + printf(" -x --print 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 Read MMI slot status.\n"); + printf(" mmi_open Open MMI slot.\n"); + printf(" mmi_close Close MMI slot.\n"); + printf(" mmi_get Read MMI slot.\n"); + printf(" mmi_send_text Send text to MMI slot.\n"); + printf(" mmi_send_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 Return last PMT table.\n"); + printf(" get_pids Return info about all pids.\n"); + printf(" get_pid Return info for chosen pid only.\n"); + printf("\n"); exit(1); } 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; - int i_fd, i_mask; - int i = 65535; + int i; + char *p_cmd, *p_arg1 = NULL, *p_arg2 = NULL; ssize_t i_size; struct sockaddr_un sun_client, sun_server; 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 ( ; ; ) { @@ -67,11 +251,12 @@ static const struct option long_options[] = { {"remote-socket", required_argument, NULL, 'r'}, + {"print", required_argument, NULL, 'x'}, {"help", no_argument, NULL, 'h'}, {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; switch ( c ) @@ -80,53 +265,76 @@ psz_srv_socket = 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 'h': default: usage(); } } - if ( ppsz_argv[optind] == NULL || psz_srv_socket == NULL ) - usage(); + /* Validate commands */ +#define usage_error(msg, ...) \ + do { \ + msg_Err( NULL, msg, ##__VA_ARGS__ ); \ + usage(); \ + } while(0) + p_cmd = ppsz_argv[optind]; + p_arg1 = ppsz_argv[optind + 1]; + p_arg2 = ppsz_argv[optind + 2]; + + if ( !psz_srv_socket ) + usage_error( "Remote socket is not set.\n" ); + + if ( !p_cmd ) + usage_error( "Command is not set.\n" ); + + i = 0; + do { + if ( streq(ppsz_argv[optind], options[i].opt) ) + { + opt = options[i]; + break; + } + } while ( options[++i].opt ); - if ( strcmp(ppsz_argv[optind], "reload") - && strcmp(ppsz_argv[optind], "shutdown") - && strcmp(ppsz_argv[optind], "fe_status") - && strcmp(ppsz_argv[optind], "mmi_status") - && strcmp(ppsz_argv[optind], "mmi_slot_status") - && strcmp(ppsz_argv[optind], "mmi_open") - && strcmp(ppsz_argv[optind], "mmi_close") - && strcmp(ppsz_argv[optind], "mmi_get") - && strcmp(ppsz_argv[optind], "mmi_send_text") - && strcmp(ppsz_argv[optind], "mmi_send_choice") ) - usage(); - - if ( (!strcmp(ppsz_argv[optind], "mmi_slot_status") - || !strcmp(ppsz_argv[optind], "mmi_open") - || !strcmp(ppsz_argv[optind], "mmi_close") - || !strcmp(ppsz_argv[optind], "mmi_get") - || !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(); + if ( !opt.opt ) + usage_error( "Unknown command: %s\n", p_cmd ); -#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) ); - return -1; + 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 ) - { - msg_Err( NULL, "cannot create UNIX socket (%s)", strerror(errno) ); - return -1; - } + return_error( "Cannot create UNIX socket (%s)", strerror(errno) ); + i = COMM_MAX_MSG_CHUNK; setsockopt( i_fd, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i) ); memset( &sun_client, 0, sizeof(sun_client) ); @@ -134,17 +342,10 @@ strncpy( sun_client.sun_path, psz_client_socket, sizeof(sun_client.sun_path) ); sun_client.sun_path[sizeof(sun_client.sun_path) - 1] = '\0'; - i_mask = umask(077); if ( bind( i_fd, (struct sockaddr *)&sun_client, SUN_LEN(&sun_client) ) < 0 ) - { - msg_Err( NULL, "cannot bind (%s)", strerror(errno) ); - umask( i_mask ); - close( i_fd ); - exit(255); - } - umask( i_mask ); + return_error( "Cannot bind (%s)", strerror(errno) ); memset( &sun_server, 0, sizeof(sun_server) ); sun_server.sun_family = AF_UNIX; @@ -152,104 +353,126 @@ sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0'; p_buffer[0] = COMM_HEADER_MAGIC; - p_buffer[2] = 0; - p_buffer[3] = 0; + p_buffer[1] = opt.cmd; + memset( p_buffer + 2, 0, COMM_HEADER_SIZE - 2 ); i_size = COMM_HEADER_SIZE; - if ( !strcmp(ppsz_argv[optind], "reload") ) - p_buffer[1] = CMD_RELOAD; - 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 + /* Handle commands that send parameters */ + switch ( opt.cmd ) { - p_buffer[4] = atoi(ppsz_argv[optind + 1]); - i_size = COMM_HEADER_SIZE + 1; + case CMD_INVALID: + case CMD_RELOAD: + case CMD_SHUTDOWN: + case CMD_FRONTEND_STATUS: + case CMD_MMI_STATUS: + case CMD_GET_PAT: + case CMD_GET_CAT: + case CMD_GET_NIT: + case CMD_GET_SDT: + case CMD_GET_PIDS: + /* These commands need no special handling because they have no parameters */ + break; + case CMD_GET_PMT: + { + uint16_t i_sid = atoi(p_arg1); + i_size = COMM_HEADER_SIZE + 2; + p_data[0] = (uint8_t)((i_sid >> 8) & 0xff); + p_data[1] = (uint8_t)(i_sid & 0xff); + 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); - if ( !strcmp(ppsz_argv[optind], "mmi_slot_status") ) - p_buffer[1] = CMD_MMI_SLOT_STATUS; - else if ( !strcmp(ppsz_argv[optind], "mmi_open") ) - p_buffer[1] = CMD_MMI_OPEN; - else if ( !strcmp(ppsz_argv[optind], "mmi_close") ) - p_buffer[1] = CMD_MMI_CLOSE; - else if ( !strcmp(ppsz_argv[optind], "mmi_get") ) - p_buffer[1] = CMD_MMI_RECV; + en50221_mmi_object_t object; + object.i_object_type = EN50221_MMI_ANSW; + if ( !p_arg2 || p_arg2[0] == '\0' ) + { + object.u.answ.b_ok = 0; + object.u.answ.psz_answ = ""; + } else { - struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)&p_buffer[4]; - p_buffer[1] = CMD_MMI_SEND; - p_cmd->i_slot = atoi(ppsz_argv[optind + 1]); - - if ( !strcmp(ppsz_argv[optind], "mmi_send_text") ) - { - en50221_mmi_object_t object; - object.i_object_type = EN50221_MMI_ANSW; - if ( ppsz_argv[optind + 2] == NULL - || ppsz_argv[optind + 2][0] == '\0' ) - { - object.u.answ.b_ok = 0; - object.u.answ.psz_answ = ""; - } - else - { - object.u.answ.b_ok = 1; - object.u.answ.psz_answ = ppsz_argv[optind + 2]; - } - i_size = COMM_BUFFER_SIZE - COMM_HEADER_SIZE - - ((void *)&p_cmd->object - (void *)p_cmd); - if ( en50221_SerializeMMIObject( (uint8_t *)&p_cmd->object, - &i_size, &object ) == -1 ) - { - msg_Err( NULL, "buffer too small" ); - close( i_fd ); - unlink( psz_client_socket ); - exit(255); - } - i_size += COMM_HEADER_SIZE - + ((void *)&p_cmd->object - (void *)p_cmd); - } - else /* mmi_send_choice */ - { - i_size = COMM_HEADER_SIZE + sizeof(struct cmd_mmi_send); - p_cmd->object.i_object_type = EN50221_MMI_MENU_ANSW; - p_cmd->object.u.menu_answ.i_choice - = atoi(ppsz_argv[optind + 2]); - } + object.u.answ.b_ok = 1; + object.u.answ.psz_answ = p_arg2; } + i_size = COMM_BUFFER_SIZE - COMM_HEADER_SIZE + - ((void *)&p_cmd->object - (void *)p_cmd); + if ( en50221_SerializeMMIObject( (uint8_t *)&p_cmd->object, + &i_size, &object ) == -1 ) + return_error( "Comm buffer is too small" ); + + i_size += COMM_HEADER_SIZE + + ((void *)&p_cmd->object - (void *)p_cmd); + break; } + case CMD_MMI_SEND_CHOICE: + { + struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data; + p_cmd->i_slot = atoi(p_arg1); - if ( sendto( i_fd, p_buffer, i_size, 0, (struct sockaddr *)&sun_server, - SUN_LEN(&sun_server) ) < 0 ) + i_size = COMM_HEADER_SIZE + sizeof(struct cmd_mmi_send); + p_cmd->object.i_object_type = EN50221_MMI_MENU_ANSW; + p_cmd->object.u.menu_answ.i_choice = atoi(p_arg2); + break; + } + case CMD_MMI_SLOT_STATUS: + case CMD_MMI_OPEN: + case CMD_MMI_CLOSE: + case CMD_MMI_RECV: { - msg_Err( NULL, "cannot send comm socket (%s)", strerror(errno) ); - close( i_fd ); - unlink( psz_client_socket ); - exit(255); + 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 ); } - i_size = recv( i_fd, p_buffer, COMM_BUFFER_SIZE, 0 ); - close( i_fd ); - unlink( psz_client_socket ); + /* Send command and receive answer */ + if ( sendto( i_fd, p_buffer, i_size, 0, (struct sockaddr *)&sun_server, + SUN_LEN(&sun_server) ) < 0 ) + return_error( "Cannot send comm socket (%s)", strerror(errno) ); + + uint32_t i_packet_size = 0, i_received = 0; + do { + 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 ); + + clean_client_socket(); if ( i_size < COMM_HEADER_SIZE ) - { - msg_Err( NULL, "cannot recv comm socket (%d:%s)", i_size, - strerror(errno) ); - exit(255); - } + return_error( "Cannot recv from comm socket, size:%zd (%s)", i_size, strerror(errno) ); + /* Process answer */ if ( p_buffer[0] != COMM_HEADER_MAGIC ) - { - msg_Err( NULL, "wrong protocol version 0x%x", p_buffer[0] ); - exit(255); - } + return_error( "Wrong protocol version 0x%x", p_buffer[0] ); + + now = mdate(); - switch ( p_buffer[1] ) + ctl_cmd_answer_t c_answer = p_buffer[1]; + switch ( c_answer ) { case RET_OK: - exit(0); break; case RET_MMI_WAIT: @@ -257,35 +480,95 @@ break; case RET_ERR: - msg_Err( NULL, "request failed" ); - exit(255); + return_error( "Request failed" ); break; case RET_HUH: - msg_Err( NULL, "internal error" ); - exit(255); + return_error( "Internal error" ); + break; + + case RET_NODATA: + return_error( "No data" ); 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: { + int ret = 1; struct ret_frontend_status *p_ret = (struct ret_frontend_status *)&p_buffer[COMM_HEADER_SIZE]; if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_frontend_status) ) - { - msg_Err( NULL, "bad frontend status" ); - exit(255); - } + return_error( "Bad frontend status" ); + + if ( i_print_type == PRINT_XML ) + printf("\n"); +#define PRINT_TYPE( x ) \ + do { \ + if ( i_print_type == PRINT_XML ) \ + printf( " \n", STRINGIFY(x) ); \ + else \ + printf( "type: %s\n", STRINGIFY(x) ); \ + } while(0) switch ( p_ret->info.type ) { - case FE_QPSK: printf("type: QPSK\n"); break; - case FE_QAM: printf("type: QAM\n"); break; - case FE_OFDM: printf("type: OFDM\n"); break; - default: printf("type: UNKNOWN\n"); break; + case FE_QPSK: PRINT_TYPE(QPSK); break; + case FE_QAM : PRINT_TYPE(QAM); break; + case FE_OFDM: PRINT_TYPE(OFDM); break; + case FE_ATSC: PRINT_TYPE(ATSC); break; + default : PRINT_TYPE(UNKNOWN); break; } +#undef PRINT_TYPE -#define PRINT_INFO( x ) \ - printf( STRINGIFY(x) ": %u\n", p_ret->info.x ); +#define PRINT_INFO( x ) \ + do { \ + if ( i_print_type == PRINT_XML ) \ + printf( " \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_max ); PRINT_INFO( frequency_stepsize ); @@ -296,11 +579,19 @@ PRINT_INFO( notifier_delay ); #undef PRINT_INFO - printf("\ncapability list:\n"); + if ( i_print_type == PRINT_TEXT ) + printf("\ncapability list:\n"); -#define PRINT_CAPS( x ) \ - if ( p_ret->info.caps & (FE_##x) ) \ - printf( STRINGIFY(x) "\n" ); +#define PRINT_CAPS( x ) \ + do { \ + if ( p_ret->info.caps & (FE_##x) ) { \ + if ( i_print_type == PRINT_XML ) { \ + printf( " \n", STRINGIFY(x) ); \ + } else { \ + printf( "%s\n", STRINGIFY(x) ); \ + } \ + } \ + } while(0) PRINT_CAPS( IS_STUPID ); PRINT_CAPS( CAN_INVERSION_AUTO ); PRINT_CAPS( CAN_FEC_1_2 ); @@ -341,11 +632,19 @@ #endif #undef PRINT_CAPS - printf("\nstatus:\n"); + if ( i_print_type == PRINT_TEXT ) + printf("\nstatus:\n"); -#define PRINT_STATUS( x ) \ - if ( p_ret->i_status & (FE_##x) ) \ - printf( STRINGIFY(x) "\n" ); +#define PRINT_STATUS( x ) \ + do { \ + if ( p_ret->i_status & (FE_##x) ) { \ + if ( i_print_type == PRINT_XML ) { \ + printf( " \n", STRINGIFY(x) ); \ + } else { \ + printf( "%s\n", STRINGIFY(x) ); \ + } \ + } \ + } while(0) PRINT_STATUS( HAS_SIGNAL ); PRINT_STATUS( HAS_CARRIER ); PRINT_STATUS( HAS_VITERBI ); @@ -356,13 +655,23 @@ if ( p_ret->i_status & FE_HAS_LOCK ) { - printf("\nBit error rate: %d\n", p_ret->i_ber); - printf("Signal strength: %d\n", p_ret->i_strength); - printf("SNR: %d\n", p_ret->i_snr); - exit(0); + if ( i_print_type == PRINT_XML ) + { + printf(" \n", p_ret->i_ber); + printf(" \n", p_ret->i_strength); + printf(" \n", p_ret->i_snr); + } else { + printf("\nBit error rate: %d\n", p_ret->i_ber); + printf("Signal strength: %d\n", p_ret->i_strength); + printf("SNR: %d\n", p_ret->i_snr); + } + ret = 0; } - exit(1); + if ( i_print_type == PRINT_XML ) + printf("\n" ); + + exit(ret); break; } @@ -371,10 +680,7 @@ struct ret_mmi_status *p_ret = (struct ret_mmi_status *)&p_buffer[COMM_HEADER_SIZE]; if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_mmi_status) ) - { - msg_Err( NULL, "bad MMI status" ); - exit(255); - } + return_error( "Bad MMI status" ); printf("CA interface with %d %s, type:\n", p_ret->caps.slot_num, p_ret->caps.slot_num == 1 ? "slot" : "slots"); @@ -408,10 +714,7 @@ struct ret_mmi_slot_status *p_ret = (struct ret_mmi_slot_status *)&p_buffer[COMM_HEADER_SIZE]; if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_slot_status) ) - { - msg_Err( NULL, "bad MMI slot status" ); - exit(255); - } + return_error( "Bad MMI slot status" ); printf("CA slot #%u: ", p_ret->sinfo.num); @@ -444,10 +747,7 @@ struct ret_mmi_recv *p_ret = (struct ret_mmi_recv *)&p_buffer[COMM_HEADER_SIZE]; if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_recv) ) - { - msg_Err( NULL, "bad MMI recv" ); - exit(255); - } + return_error( "Bad MMI recv" ); en50221_UnserializeMMIObject( &p_ret->object, i_size - COMM_HEADER_SIZE - ((void *)&p_ret->object - (void *)p_ret) ); @@ -482,8 +782,7 @@ break; default: - printf("unknown MMI object\n"); - exit(255); + return_error( "Unknown MMI object" ); break; } @@ -492,7 +791,8 @@ } default: - msg_Err( NULL, "wrong answer %u", p_buffer[1] ); - exit(255); + return_error( "Unknown command answer: %u", c_answer ); } + + return 0; } diff -Nru dvblast-1.2/dvblast.h dvblast-2.1.0/dvblast.h --- dvblast-1.2/dvblast.h 2010-02-28 20:39:34.000000000 +0000 +++ dvblast-2.1.0/dvblast.h 2012-01-03 22:06:11.000000000 +0000 @@ -1,8 +1,7 @@ /***************************************************************************** * dvblast.h ***************************************************************************** - * Copyright (C) 2004, 2008-2009 VideoLAN - * $Id: dvblast.h 114 2010-02-28 20:39:34Z ivoire $ + * Copyright (C) 2004, 2008-2011 VideoLAN * * Authors: Christophe Massiot * Andy Gatward @@ -22,23 +21,27 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -#include -#include -#include +#include +#include -#include "netdb.h" -#include "sys/socket.h" +#define HAVE_CLOCK_NANOSLEEP +#define HAVE_ICONV #define DEFAULT_PORT 3001 #define TS_SIZE 188 -#define NB_BLOCKS 7 -#define NB_BLOCKS_IPV6 6 // assume MTU of 1280 bytes for IPv6 -#define RTP_SIZE 12 -#define EMPTY_PID 8192 +#define MAX_PIDS 8192 +#define DEFAULT_IPV4_MTU 1500 +#define DEFAULT_IPV6_MTU 1280 #define PADDING_PID 8191 #define WATCHDOG_WAIT 10000000LL #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 @@ -47,6 +50,8 @@ * 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 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 @@ -54,6 +59,8 @@ #define OUTPUT_VALID 0x04 #define OUTPUT_UDP 0x08 #define OUTPUT_FILE 0x10 +#define OUTPUT_DVB 0x20 +#define OUTPUT_EPG 0x40 typedef int64_t mtime_t; @@ -61,118 +68,207 @@ { uint8_t p_ts[TS_SIZE]; int i_refcount; + mtime_t i_dts; struct block_t *p_next; } block_t; -typedef struct output_t +typedef struct packet_t packet_t; + +typedef struct output_config_t { - /* address information, protocol agnostic */ - struct sockaddr_storage *p_addr; - socklen_t i_addrlen; + /* identity */ + int i_family; + struct sockaddr_storage connect_addr; + struct sockaddr_storage bind_addr; + int i_if_index_v6; - /* display string */ + /* common config */ 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 */ int i_handle; - block_t *pp_blocks[NB_BLOCKS]; - int i_depth; - uint16_t i_cc; + packet_t *p_packets, *p_last_packet; + uint16_t i_seqnum; mtime_t i_ref_timestamp; mtime_t i_ref_wallclock; /* demux */ int i_nb_errors; 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; - dvbpsi_psi_section_t *p_pmt_section; + uint8_t *p_pmt_section; 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_eit_cc; - uint16_t i_ts_id; - - /* configuration */ - uint16_t i_sid; /* 0 if raw mode */ - uint16_t *pi_pids; - int i_nb_pids; - uint8_t i_config; + block_t *p_eit_ts_buffer; + uint8_t i_eit_ts_buffer_offset, i_eit_cc; + uint16_t i_tsid; } 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_verbose; extern output_t **pp_outputs; extern int i_nb_outputs; extern output_t output_dup; extern char *psz_srv_socket; -extern int i_ttl; -extern in_addr_t i_ssrc; +extern int i_comm_fd; extern int i_adapter; extern int i_fenum; extern int i_frequency; extern int i_srate; extern int i_satnum; +extern int i_fec; +extern int i_rolloff; extern int i_voltage; extern int b_tone; extern int i_bandwidth; +extern int i_inversion; 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_slow_cam; -extern int b_output_udp; -extern int b_enable_epg; -extern int b_unique_tsid; -extern volatile int b_hup_received; -extern mtime_t i_ca_timeout; +extern int b_any_type; +extern int b_select_pmts; +extern int b_random_tsid; +extern uint16_t i_network_id; +extern uint8_t *p_network_name; +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 uint16_t i_src_port; -extern in_addr_t i_src_addr; -extern int b_src_rawudp; +extern char *psz_udp_src; 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 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 void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ); /***************************************************************************** * Prototypes *****************************************************************************/ -void msg_Info( void *_unused, const char *psz_format, ... ); -void msg_Err( void *_unused, const char *psz_format, ... ); -void msg_Warn( void *_unused, const char *psz_format, ... ); -void msg_Dbg( void *_unused, const char *psz_format, ... ); -void msg_Raw( void *_unused, const char *psz_format, ... ); + +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 */ +void msg_Connect( const char *ident ); +void msg_Disconnect( void ); + +/* */ +__attribute__ ((format(printf, 2, 3))) void msg_Info( void *_unused, const char *psz_format, ... ); +__attribute__ ((format(printf, 2, 3))) void msg_Err( void *_unused, const char *psz_format, ... ); +__attribute__ ((format(printf, 2, 3))) void msg_Warn( void *_unused, const char *psz_format, ... ); +__attribute__ ((format(printf, 2, 3))) void msg_Dbg( 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 ); void msleep( mtime_t delay ); 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 ); -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 ); void dvb_UnsetFilter( int i_fd, uint16_t i_pid ); uint8_t dvb_FrontendStatus( uint8_t *p_answer, ssize_t *pi_size ); 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 ); void udp_UnsetFilter( int i_fd, uint16_t i_pid ); 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 ); void asi_UnsetFilter( int i_fd, uint16_t i_pid ); void demux_Open( void ); -void demux_Run( void ); -void demux_Change( output_t *p_output, uint16_t i_sid, - uint16_t *pi_pids, int i_nb_pids ); +void demux_Run( block_t *p_ts ); +void demux_Change( output_t *p_output, const output_config_t *p_config ); 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, + char *p_string, size_t i_length); +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( uint8_t i_config, const char *psz_displayname, void *p_init_data ); -int output_Init( output_t *p_output, uint8_t i_config, const char *psz_displayname, void *p_init_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_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_Read( void ); @@ -208,87 +304,3 @@ 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] ]; -} - diff -Nru dvblast-1.2/dvblast_mmi.sh dvblast-2.1.0/dvblast_mmi.sh --- dvblast-1.2/dvblast_mmi.sh 2010-02-20 10:53:29.000000000 +0000 +++ dvblast-2.1.0/dvblast_mmi.sh 2012-01-03 22:06:11.000000000 +0000 @@ -3,7 +3,6 @@ # dvblast_mmi.sh ############################################################################### # Copyright (C) 1998-2008 VideoLAN -# $Id: dvblast_mmi.sh 105 2010-02-20 10:53:29Z md $ # # Authors: Christophe Massiot # diff -Nru dvblast-1.2/en50221.c dvblast-2.1.0/en50221.c --- dvblast-1.2/en50221.c 2010-02-20 10:53:29.000000000 +0000 +++ dvblast-2.1.0/en50221.c 2012-01-03 22:06:11.000000000 +0000 @@ -2,8 +2,7 @@ * en50221.c : implementation of the transport, session and applications * layers of EN 50 221 ***************************************************************************** - * Copyright (C) 2004-2005 VideoLAN - * $Id: en50221.c 105 2010-02-20 10:53:29Z md $ + * Copyright (C) 2004-2005, 2010 VideoLAN * * Authors: Christophe Massiot * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger @@ -20,15 +19,16 @@ * * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #include #include +#include #include #include #include -#include +#include #include #include #include @@ -46,6 +46,10 @@ #include #include +#include +#include +#include + #include "dvblast.h" #include "en50221.h" #include "comm.h" @@ -59,17 +63,24 @@ { \ (tab) = malloc( sizeof( void ** ) ); \ } \ - (tab)[count] = (p); \ + (tab)[count] = (p); \ (count)++ /***************************************************************************** * Local declarations *****************************************************************************/ #undef DEBUG_TPDU -#define HLCI_WAIT_CAM_READY 0 -#define CAM_PROG_MAX MAX_PROGRAMS +#define CAM_INIT_TIMEOUT 15000000 /* 15 s */ +#undef HLCI_WAIT_CAM_READY #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 { int i_slot; @@ -80,18 +91,44 @@ void *p_sys; } 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_type = -1; + static int i_nb_slots = 0; -static bool pb_active_slot[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 ci_slot_t p_slots[MAX_CI_SLOTS]; static en50221_session_t p_sessions[MAX_SESSIONS]; /***************************************************************************** * 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 ApplicationInformationOpen( access_t * p_access, int i_session_id ); static void ConditionalAccessOpen( access_t * p_access, int i_session_id ); @@ -193,13 +230,54 @@ } /***************************************************************************** + * 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 *****************************************************************************/ static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag, 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 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; i_size = 0; @@ -245,13 +323,16 @@ default: break; } - Dump( true, p_data, i_size ); - if ( write( i_ca_handle, p_data, i_size ) != i_size ) - { - msg_Err( p_access, "cannot write to CAM device (%m)" ); - return -1; - } + p_send->p_data = p_data; + p_send->i_size = i_size; + p_send->p_next = NULL; + + *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; } @@ -260,66 +341,105 @@ /***************************************************************************** * TPDURecv *****************************************************************************/ -#define CAM_READ_TIMEOUT 3500 // ms - -static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag, - uint8_t *p_data, int *pi_size ) +static int TPDURecv( access_t * p_access ) { - 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; - struct pollfd pfd[1]; + bool b_last = false; - pfd[0].fd = i_ca_handle; - pfd[0].events = POLLIN; - if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) ) + do { - msg_Err( p_access, "cannot poll from CAM device" ); - return -1; + i_size = read( i_ca_handle, p_data, MAX_TPDU_SIZE ); } + while ( i_size < 0 && errno == EINTR ); - if ( pi_size == NULL ) + if ( i_size < 5 ) { - p_data = malloc( MAX_TPDU_SIZE ); + msg_Err( p_access, "en50221: cannot read from CAM device (%d:%m)", + i_size ); + return -1; } - for ( ; ; ) - { - i_size = read( i_ca_handle, p_data, MAX_TPDU_SIZE ); + Dump( false, p_data, i_size ); - if ( i_size >= 0 || errno != EINTR ) - break; - } + i_slot = p_data[1] - 1; + i_tag = p_data[2]; - if ( i_size < 5 ) + if ( i_slot >= i_nb_slots ) { - msg_Err( p_access, "cannot read from CAM device (%d:%m)", i_size ); - if( pi_size == NULL ) - free( p_data ); + msg_Warn( p_access, "en50221: TPDU is from an unknown slot %u", + i_slot ); return -1; } + p_slot = &p_slots[i_slot]; + + 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; - if ( p_data[1] != i_tcid ) + switch ( i_tag ) { - msg_Err( p_access, "invalid read from CAM device (%d instead of %d)", - p_data[1], i_tcid ); - if( pi_size == NULL ) - free( p_data ); - return -1; - } + 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; - *pi_tag = p_data[2]; - pb_tc_has_data[i_slot] = (i_size >= 4 - && p_data[i_size - 4] == T_SB - && p_data[i_size - 3] == 2 - && (p_data[i_size - 1] & DATA_INDICATOR)) - ? true : false; + case T_SB: + break; - Dump( false, p_data, i_size ); + case T_DATA_LAST: + b_last = true; + /* intended pass-through */ + case T_DATA_MORE: + { + en50221_msg_t *p_recv; + int i_session_size; + uint8_t *p_session = GetLength( &p_data[3], &i_session_size ); - if ( pi_size == NULL ) - free( p_data ); - else - *pi_size = i_size; + if ( i_session_size <= 1 ) + break; + p_session++; + i_session_size--; + + if ( p_slot->p_recv == NULL ) + { + p_slot->p_recv = malloc( sizeof(en50221_msg_t) ); + p_slot->p_recv->p_data = NULL; + p_slot->p_recv->i_size = 0; + } + + p_recv = p_slot->p_recv; + p_recv->p_data = realloc( p_recv->p_data, + p_recv->i_size + i_session_size ); + memcpy( &p_recv->p_data[ p_recv->i_size ], p_session, i_session_size ); + p_recv->i_size += i_session_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; + } + + default: + msg_Warn( p_access, "en50221: unhandled R_TPDU tag %u slot %u", i_tag, + i_slot ); + 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; } @@ -356,12 +476,11 @@ /***************************************************************************** * SPDUSend *****************************************************************************/ -static int SPDUSend( access_t * p_access, int i_session_id, - uint8_t *p_data, int i_size ) +static int SPDUSend( access_t *p_access, int i_session_id, + uint8_t *p_data, int i_size ) { uint8_t *p_spdu = malloc( i_size + 4 ); uint8_t *p = p_spdu; - uint8_t i_tag; uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; *p++ = ST_SESSION_NUMBER; @@ -401,15 +520,6 @@ } 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 ); @@ -419,14 +529,42 @@ /***************************************************************************** * 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, uint8_t *p_spdu, int i_size ) { + ci_slot_t *p_slot = &p_slots[i_slot]; int i_session_id; int i_resource_id = ResourceIdToInt( &p_spdu[2] ); uint8_t p_response[16]; int i_status = SS_NOT_ALLOCATED; - uint8_t i_tag; for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ ) { @@ -462,38 +600,17 @@ p_response[7] = i_session_id >> 8; p_response[8] = i_session_id & 0xff; - if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) != - 0 ) + if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) != 0 ) { msg_Err( p_access, "SessionOpen: couldn't send TPDU on slot %d", i_slot ); 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: - default: - msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id ); - p_sessions[i_session_id - 1].i_resource_id = 0; - } + if ( p_slot->i_pending_session_id != 0 ) + msg_Warn( p_access, "overwriting pending session %d", + p_slot->i_pending_session_id ); + p_slot->i_pending_session_id = i_session_id; } #if 0 @@ -539,12 +656,6 @@ "SessionCreate: couldn't send TPDU on slot %d", i_slot ); 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 @@ -593,7 +704,6 @@ static void SessionSendClose( access_t * p_access, int i_session_id ) { uint8_t p_response[16]; - uint8_t i_tag; uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; p_response[0] = ST_CLOSE_SESSION_REQUEST; @@ -608,12 +718,6 @@ "SessionSendClose: couldn't send TPDU on slot %d", i_slot ); 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 SessionClose( access_t * p_access, int i_session_id ) { uint8_t p_response[16]; - uint8_t i_tag; uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; if ( p_sessions[i_session_id - 1].pf_close != NULL ) @@ -642,12 +745,6 @@ "SessionClose: couldn't send TPDU on slot %d", i_slot ); 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,9 +760,11 @@ case ST_SESSION_NUMBER: if ( i_size <= 4 ) return; - i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3]; - p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id, - p_spdu + 4, i_size - 4 ); + 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_spdu + 4, i_size - 4 ); break; case ST_OPEN_SESSION_REQUEST: @@ -727,6 +826,7 @@ #define AOT_CA_INFO 0x9F8031 #define AOT_CA_PMT 0x9F8032 #define AOT_CA_PMT_REPLY 0x9F8033 +#define AOT_CA_UPDATE 0x9F8034 #define AOT_TUNE 0x9F8400 #define AOT_REPLACE 0x9F8401 #define AOT_CLEAR_REPLACE 0x9F8402 @@ -793,7 +893,7 @@ * APDUSend *****************************************************************************/ static int APDUSend( access_t * p_access, int i_session_id, int i_tag, - uint8_t *p_data, int i_size ) + uint8_t *p_data, int i_size ) { uint8_t *p_apdu = malloc( i_size + 12 ); uint8_t *p = p_apdu; @@ -897,7 +997,7 @@ msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id ); 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 @@ uint8_t *d = APDUGetLength( p_apdu, &l ); if ( l < 4 ) break; - p_apdu[l + 4] = '\0'; i_type = *d++; i_manufacturer = ((int)d[0] << 8) | d[1]; @@ -925,9 +1024,26 @@ i_code = ((int)d[0] << 8) | d[1]; d += 2; d = GetLength( d, &l ); - d[l] = '\0'; - msg_Info( p_access, "CAM: %s, %02X, %04X, %04X", - d, i_type, i_manufacturer, i_code ); + + { + char *psz_name = malloc(l + 1); + memcpy( psz_name, d, l ); + psz_name[l] = '\0'; + msg_Info( p_access, "CAM: %s, %02X, %04X, %04X", + psz_name, i_type, i_manufacturer, i_code ); + switch (i_print_type) + { + case PRINT_XML: + psz_name = dvb_string_xml_escape(psz_name); + printf("\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; } default: @@ -954,11 +1070,10 @@ * Conditional Access */ -#define MAX_CASYSTEM_IDS 64 - 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 b_high_level; @@ -966,16 +1081,13 @@ static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id ) { - int i = 0; - if( !p_ids ) return false; + int i; + if( p_ids == NULL ) return false; 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 ) return true; - i++; - } return false; } @@ -983,206 +1095,144 @@ /***************************************************************************** * 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 ) - { - uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8) - | p_dr->p_data[1]; - if ( CheckSystemID( p_ids, i_sysid ) ) - i_cad_size += p_dr->i_length + 2; - } - p_dr = p_dr->p_next; + uint8_t i_tag = desc_get_tag( p_desc ); + j++; + + if ( i_tag == 0x9 && desc09_validate( p_desc ) + && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) ) + return true; } - return i_cad_size; + return false; } -static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt, - uint16_t i_program_number, uint8_t i_version, - int i_size, dvbpsi_descriptor_t *p_dr, - uint8_t i_cmd ) +static void CopyCADescriptors( system_ids_t *p_ids, uint8_t i_cmd, + uint8_t *p_infos, uint8_t *p_descs ) { - 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 ); + capmti_init( p_infos ); + capmti_set_length( p_infos, 0xfff ); + capmti_set_cmd( p_infos, i_cmd ); - p_data[0] = i_list_mgt; - p_data[1] = i_program_number >> 8; - p_data[2] = i_program_number & 0xff; - p_data[3] = ((i_version & 0x1f) << 1) | 0x1; - - if ( i_size ) + while ( (p_desc = descs_get_desc( p_descs, j )) != NULL ) { - int i; - - p_data[4] = (i_size + 1) >> 8; - p_data[5] = (i_size + 1) & 0xff; - p_data[6] = i_cmd; - i = 7; + uint8_t i_tag = desc_get_tag( p_desc ); + j++; - while ( p_dr != NULL ) + if ( i_tag == 0x9 && desc09_validate( p_desc ) + && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) ) { - 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 ); -// p_data[i+4] &= 0x1f; - i += p_dr->i_length + 2; - } - } - p_dr = p_dr->p_next; + uint8_t *p_info = capmti_get_info( p_infos, k ); + k++; + memcpy( p_info, p_desc, + DESC_HEADER_SIZE + desc_get_length( p_desc ) ); } } - 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, - 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 ) + if ( k ) { - p_data[i + 3] = (i_size + 1) >> 8; - p_data[i + 4] = (i_size + 1) & 0xff; - 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; - } + uint8_t *p_info = capmti_get_info( p_infos, k ); + capmti_set_length( p_infos, p_info - p_infos - DESCS_HEADER_SIZE ); } else - { - p_data[i + 3] = 0; - p_data[i + 4] = 0; - } - - return p_data; + capmti_set_length( p_infos, 0 ); } 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 ) { system_ids_t *p_ids = (system_ids_t *)p_sessions[i_session_id - 1].p_sys; - dvbpsi_pmt_es_t *p_es; - int i_cad_size, i_cad_program_size; - uint8_t *p_capmt; + uint8_t *p_es; + uint8_t *p_capmt, *p_capmt_n; + 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 = - 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 ) + if ( !b_has_es ) { - 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, "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; return NULL; } - p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number, - p_pmt->i_version, i_cad_program_size, - p_pmt->p_first_descriptor, i_cmd ); + p_capmt = capmt_allocate(); + capmt_init( p_capmt ); + 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 ) ); + + CopyCADescriptors( p_ids, i_cmd, capmt_get_infos( p_capmt ), + pmt_get_descs( p_pmt ) ); - if ( i_cad_program_size ) - *pi_capmt_size = 7 + i_cad_program_size; - 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; - 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 ) - { - p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type, - 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; - } - } + capmtn_init( p_capmt_n ); + capmtn_set_streamtype( p_capmt_n, pmtn_get_streamtype( p_es ) ); + capmtn_set_pid( p_capmt_n, pmtn_get_pid( p_es ) ); - if ( *pi_capmt_size <= 7 + i_cad_program_size ) - { - msg_Dbg( p_access, "CAPMT not needed, no ES selected" ); - free( p_capmt ); - *pi_capmt_size = 0; - return NULL; + CopyCADescriptors( p_ids, i_cmd, capmtn_get_infos( p_capmt_n ), + pmtn_get_descs( p_es ) ); } + p_capmt_n = capmt_get_es( p_capmt, k ); + *pi_capmt_size = p_capmt_n - p_capmt; + return p_capmt; } /***************************************************************************** * CAPMTFirst *****************************************************************************/ -static void CAPMTFirst( access_t * p_access, int i_session_id, - dvbpsi_pmt_t *p_pmt ) +static void CAPMTFirst( access_t * p_access, int i_session_id, uint8_t *p_pmt ) { uint8_t *p_capmt; int i_capmt_size; 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, 0x3 /* only */, 0x1 /* ok_descrambling */, @@ -1198,20 +1248,13 @@ /***************************************************************************** * CAPMTAdd *****************************************************************************/ -static void CAPMTAdd( access_t * p_access, int i_session_id, - dvbpsi_pmt_t *p_pmt ) +static void CAPMTAdd( access_t * p_access, int i_session_id, uint8_t *p_pmt ) { system_ids_t *p_ids = (system_ids_t *)p_sessions[i_session_id - 1].p_sys; uint8_t *p_capmt; 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++; if( p_ids->i_selected_programs == 1 ) { @@ -1219,11 +1262,8 @@ return; } - if( b_slow_cam ) - msleep( CAPMT_WAIT * 1000 ); - 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, 0x4 /* add */, 0x1 /* ok_descrambling */, @@ -1239,14 +1279,13 @@ /***************************************************************************** * CAPMTUpdate *****************************************************************************/ -static void CAPMTUpdate( access_t * p_access, int i_session_id, - dvbpsi_pmt_t *p_pmt ) +static void CAPMTUpdate( access_t * p_access, int i_session_id, uint8_t *p_pmt ) { uint8_t *p_capmt; int i_capmt_size; 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, 0x5 /* update */, 0x1 /* ok_descrambling */, @@ -1262,8 +1301,7 @@ /***************************************************************************** * CAPMTDelete *****************************************************************************/ -static void CAPMTDelete( access_t * p_access, int i_session_id, - dvbpsi_pmt_t *p_pmt ) +static void CAPMTDelete( access_t * p_access, int i_session_id, uint8_t *p_pmt ) { system_ids_t *p_ids = (system_ids_t *)p_sessions[i_session_id - 1].p_sys; @@ -1272,7 +1310,7 @@ p_ids->i_selected_programs--; msg_Dbg( p_access, "deleting 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, 0x5 /* update */, 0x4 /* not selected */, @@ -1304,18 +1342,29 @@ uint8_t *d = APDUGetLength( p_apdu, &l ); 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]; d += 2; msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] ); } - p_ids->pi_system_ids[i] = 0; demux_ResendCAPMTs(); 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: msg_Err( p_access, "unexpected tag in ConditionalAccessHandle (0x%x)", @@ -1395,7 +1444,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 +1492,7 @@ (date_time_t *)p_sessions[i_session_id - 1].p_sys; 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 ); } @@ -1587,7 +1636,7 @@ APDUSend( p_access, i_session_id, i_tag, p_data, i_size ); free( p_data ); - pb_slot_mmi_expected[i_slot] = true; + p_slots[i_slot].b_mmi_expected = true; } /***************************************************************************** @@ -1599,7 +1648,7 @@ 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 +1672,6 @@ static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size ) { int i_tag = APDUGetTag( *pp_apdu, *pi_size ); - char *psz_text; int l; uint8_t *d; @@ -1635,14 +1683,11 @@ } d = APDUGetLength( *pp_apdu, &l ); - psz_text = malloc( l + 1 ); - strncpy( psz_text, (char *)d, l ); - psz_text[l] = '\0'; *pp_apdu += l + 4; *pi_size -= l + 4; - return psz_text; + return dvb_string_get( d, l, demux_Iconv, p_access ); } /***************************************************************************** @@ -1667,8 +1712,9 @@ 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)" : "" ); - 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 +1761,9 @@ 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 +1831,9 @@ free( p_sessions[i_session_id - 1].p_sys ); 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 +1860,11 @@ /***************************************************************************** * InitSlot: Open the transport layer *****************************************************************************/ -#define MAX_TC_RETRIES 5 - -static int InitSlot( access_t * p_access, int i_slot ) +static void 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", 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 +1872,39 @@ *****************************************************************************/ static void ResetSlot( int i_slot ) { + ci_slot_t *p_slot = &p_slots[i_slot]; int i_session_id; + switch (i_print_type) + { + case PRINT_XML: + printf("\n"); + break; + default: + break; + } + if ( ioctl( i_ca_handle, CA_RESET, 1 << i_slot ) != 0 ) msg_Err( NULL, "en50221_Poll: couldn't reset slot %d", i_slot ); - pb_active_slot[i_slot] = false; - pb_tc_has_data[i_slot] = false; + p_slot->b_active = 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. */ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ ) @@ -1872,8 +1919,6 @@ p_sessions[i_session_id - 1].i_resource_id = 0; } } - - i_ca_timeout = 100000; } @@ -1963,10 +2008,7 @@ *****************************************************************************/ void en50221_Reset( void ) { - memset( pb_active_slot, 0, sizeof(bool) * 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 ); + memset( p_slots, 0, sizeof(ci_slot_t) * MAX_CI_SLOTS ); if( i_ca_type & CA_CI_LINK ) { @@ -2023,7 +2065,7 @@ return; } -#if HLCI_WAIT_CAM_READY +#ifdef HLCI_WAIT_CAM_READY while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff ) { msleep(1); @@ -2058,16 +2100,25 @@ } /***************************************************************************** - * 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 ) { int i_slot; int i_session_id; + /* Check module status */ 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; sinfo.num = i_slot; @@ -2080,124 +2131,51 @@ 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", i_slot ); - pb_active_slot[i_slot] = false; - 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; - } - } + ResetSlot( i_slot ); } continue; } - else if ( !pb_active_slot[i_slot] ) + else if ( !p_slot->b_active ) { - InitSlot( NULL, i_slot ); - - if ( !pb_active_slot[i_slot] ) + if ( !p_slot->b_expect_answer ) + InitSlot( NULL, i_slot ); + else if ( p_slot->i_init_timeout < i_wallclock ) { msg_Dbg( NULL, "en50221_Poll: resetting slot %d", i_slot ); ResetSlot( i_slot ); 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 ) - { - msg_Err( NULL, - "en50221_Poll: couldn't recv TPDU on slot %d, resetting", - i_slot ); - ResetSlot( i_slot ); - continue; - } - } + /* Check if applications have data to send */ + for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ ) + { + en50221_session_t *p_session = &p_sessions[i_session_id - 1]; + if ( p_session->i_resource_id && p_session->pf_manage != NULL + && !p_slots[ p_session->i_slot ].b_expect_answer ) + p_session->pf_manage( NULL, i_session_id ); + } - while ( pb_tc_has_data[i_slot] ) - { - uint8_t p_tpdu[MAX_TPDU_SIZE]; - int i_size, i_session_size; - uint8_t *p_session; + /* Now send the poll command to inactive slots */ + for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ ) + { + ci_slot_t *p_slot = &p_slots[i_slot]; - 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, "en50221_Poll: couldn't send TPDU on slot %d, resetting", 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 +2183,7 @@ /***************************************************************************** * en50221_AddPMT : *****************************************************************************/ -void en50221_AddPMT( dvbpsi_pmt_t *p_pmt ) +void en50221_AddPMT( uint8_t *p_pmt ) { int i_session_id; @@ -2218,7 +2196,7 @@ /***************************************************************************** * en50221_UpdatePMT : *****************************************************************************/ -void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt ) +void en50221_UpdatePMT( uint8_t *p_pmt ) { int i_session_id; @@ -2231,7 +2209,7 @@ /***************************************************************************** * en50221_DeletePMT : *****************************************************************************/ -void en50221_DeletePMT( dvbpsi_pmt_t *p_pmt ) +void en50221_DeletePMT( uint8_t *p_pmt ) { int i_session_id; @@ -2373,7 +2351,7 @@ if ( i_size != 1 ) return RET_HUH; 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 */ p_ret->object.i_object_type = EN50221_MMI_NONE; @@ -2418,6 +2396,12 @@ int i_session_id, i_slot; 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 - ((void *)&p_cmd->object - (void *)p_cmd) ) == -1 ) return RET_ERR; diff -Nru dvblast-1.2/en50221.h dvblast-2.1.0/en50221.h --- dvblast-1.2/en50221.h 2009-10-20 19:15:04.000000000 +0000 +++ dvblast-2.1.0/en50221.h 2012-01-03 22:06:11.000000000 +0000 @@ -2,23 +2,14 @@ * en50221.h ***************************************************************************** * Copyright (C) 2008 VideoLAN - * $Id: en50221.h 27 2009-10-20 19:15:04Z massiot $ * * Authors: Christophe Massiot * - * 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. + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. *****************************************************************************/ #include @@ -69,7 +60,6 @@ #define MAX_CI_SLOTS 16 #define MAX_SESSIONS 32 -#define MAX_PROGRAMS 24 extern int i_ca_handle; extern int i_ca_type; @@ -79,10 +69,11 @@ *****************************************************************************/ void en50221_Init( void ); void en50221_Reset( void ); +void en50221_Read( void ); void en50221_Poll( void ); -void en50221_AddPMT( dvbpsi_pmt_t *p_pmt ); -void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt ); -void en50221_DeletePMT( dvbpsi_pmt_t *p_pmt ); +void en50221_AddPMT( uint8_t *p_pmt ); +void en50221_UpdatePMT( uint8_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_StatusMMISlot( uint8_t *p_buffer, ssize_t i_size, uint8_t *p_answer, ssize_t *pi_size ); diff -Nru dvblast-1.2/extra/dvbiscovery/dvbiscovery_atsc.conf dvblast-2.1.0/extra/dvbiscovery/dvbiscovery_atsc.conf --- dvblast-1.2/extra/dvbiscovery/dvbiscovery_atsc.conf 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/dvbiscovery/dvbiscovery_atsc.conf 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,69 @@ +# 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 diff -Nru dvblast-1.2/extra/dvbiscovery/dvbiscovery_dvb-c.conf dvblast-2.1.0/extra/dvbiscovery/dvbiscovery_dvb-c.conf --- dvblast-1.2/extra/dvbiscovery/dvbiscovery_dvb-c.conf 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/dvbiscovery/dvbiscovery_dvb-c.conf 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,34 @@ +# 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 diff -Nru dvblast-1.2/extra/dvbiscovery/dvbiscovery_dvb-s.conf dvblast-2.1.0/extra/dvbiscovery/dvbiscovery_dvb-s.conf --- dvblast-1.2/extra/dvbiscovery/dvbiscovery_dvb-s.conf 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/dvbiscovery/dvbiscovery_dvb-s.conf 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,33 @@ +# 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 diff -Nru dvblast-1.2/extra/dvbiscovery/dvbiscovery_dvb-t.conf dvblast-2.1.0/extra/dvbiscovery/dvbiscovery_dvb-t.conf --- dvblast-1.2/extra/dvbiscovery/dvbiscovery_dvb-t.conf 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/dvbiscovery/dvbiscovery_dvb-t.conf 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,305 @@ +# 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 diff -Nru dvblast-1.2/extra/dvbiscovery/dvbiscovery.sh dvblast-2.1.0/extra/dvbiscovery/dvbiscovery.sh --- dvblast-1.2/extra/dvbiscovery/dvbiscovery.sh 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/dvbiscovery/dvbiscovery.sh 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,302 @@ +#!/bin/sh +############################################################################### +# dvbiscovery.sh +############################################################################### +# Copyright (C) 2010 VideoLAN +# +# Authors: Christophe Massiot +# +# 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 ] [-n ] [-S ] [-c ]" >&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 "" + 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 diff -Nru dvblast-1.2/extra/dvbiscovery/README dvblast-2.1.0/extra/dvbiscovery/README --- dvblast-1.2/extra/dvbiscovery/README 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/dvbiscovery/README 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,13 @@ +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. diff -Nru dvblast-1.2/extra/kernel-patches/01-cx23885.patch dvblast-2.1.0/extra/kernel-patches/01-cx23885.patch --- dvblast-1.2/extra/kernel-patches/01-cx23885.patch 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/kernel-patches/01-cx23885.patch 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,11 @@ +--- 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; diff -Nru dvblast-1.2/extra/kernel-patches/02-saa7146.patch dvblast-2.1.0/extra/kernel-patches/02-saa7146.patch --- dvblast-1.2/extra/kernel-patches/02-saa7146.patch 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/kernel-patches/02-saa7146.patch 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,11 @@ +--- 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, diff -Nru dvblast-1.2/extra/kernel-patches/03-cx88.patch dvblast-2.1.0/extra/kernel-patches/03-cx88.patch --- dvblast-1.2/extra/kernel-patches/03-cx88.patch 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/kernel-patches/03-cx88.patch 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,11 @@ +--- 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; diff -Nru dvblast-1.2/extra/kernel-patches/README dvblast-2.1.0/extra/kernel-patches/README --- dvblast-1.2/extra/kernel-patches/README 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/extra/kernel-patches/README 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,9 @@ +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. diff -Nru dvblast-1.2/INSTALL dvblast-2.1.0/INSTALL --- dvblast-1.2/INSTALL 2010-02-28 23:52:48.000000000 +0000 +++ dvblast-2.1.0/INSTALL 2012-01-03 22:06:11.000000000 +0000 @@ -1,10 +1,29 @@ -$Id: INSTALL 118 2010-02-28 23:52:48Z massiot $ + +Getting DVBlast +=============== + +Offically released DVBlast versions can be downloaded at: +ftp://ftp.videolan.org/pub/videolan/dvblast/ + +DVBlast is developed using git. To get the latest version clone +the repository at: git://git.videolan.org/dvblast.git + +To see the latest developments visit the following url: +http://git.videolan.org/?p=dvblast.git Installing DVBlast ================== -No autotools yet... You have to tweak the Makefile by hand, especially -if your kernel is S2API-enabled but not your distribution (indicate the -path of your kernel where appropriate). Compile the program with `make` -and install with `make install`. It requires the installation of libdvbpsi -0.1.6 or later and libdvbpsi-devel (depending on your distribution). +Compile the program with `make` and install with `make install`. Your +kernel must support DVB S2API which was merged in Linux 2.6.28 and released +on 24 Dec 2008. + +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 diff -Nru dvblast-1.2/Makefile dvblast-2.1.0/Makefile --- dvblast-1.2/Makefile 2010-03-01 13:33:31.000000000 +0000 +++ dvblast-2.1.0/Makefile 2012-01-03 22:06:11.000000000 +0000 @@ -1,36 +1,75 @@ # DVBlast Makefile # Customise the path of your kernel -CFLAGS += -Wall -O3 -fomit-frame-pointer +VERSION = 2.1.0 +TOPDIR = `basename ${PWD}` +GIT_VER = $(shell git describe --tags --dirty --always 2>/dev/null) + +CFLAGS += -Wall -Wformat-security -O3 -fomit-frame-pointer 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_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 +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 -BIN = $(DESTDIR)/$(PREFIX)/bin -MAN = $(DESTDIR)/$(PREFIX)/share/man/man1 +BIN = $(subst //,/,$(DESTDIR)/$(PREFIX)/bin) +MAN = $(subst //,/,$(DESTDIR)/$(PREFIX)/share/man/man1) 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) - $(CC) -o $@ $(OBJ_DVBLAST) $(LDLIBS_DVBLAST) $(LDLIBS) + @echo "LINK $@" + $(Q)$(CC) -o $@ $(OBJ_DVBLAST) $(LDLIBS_DVBLAST) $(LDLIBS) dvblastctl: $(OBJ_DVBLASTCTL) + @echo "LINK $@" + $(Q)$(CC) -o $@ $(OBJ_DVBLASTCTL) $(LDLIBS_DVBLAST) $(LDLIBS) clean: - @rm -f dvblast dvblastctl $(OBJ_DVBLAST) $(OBJ_DVBLASTCTL) + @echo "CLEAN $(CLEAN_OBJS)" + $(Q)rm -f $(CLEAN_OBJS) install: all - @install -d $(BIN) - @install -d $(MAN) - @install dvblast dvblastctl dvblast_mmi.sh $(BIN) - @install -m 644 dvblast.1 $(MAN) + @install -d "$(BIN)" + @install -d "$(MAN)" + @echo "INSTALL $(INSTALL_MAN) -> $(MAN)" + $(Q)install -m 644 dvblast.1 "$(MAN)" + @echo "INSTALL $(INSTALL_BIN) -> $(BIN)" + $(Q)install dvblast dvblastctl dvblast_mmi.sh "$(BIN)" 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: clean + @echo "ARCHIVE 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 + diff -Nru dvblast-1.2/mrtg-cnt.c dvblast-2.1.0/mrtg-cnt.c --- dvblast-1.2/mrtg-cnt.c 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/mrtg-cnt.c 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,195 @@ +/***************************************************************************** + * mrtg-cnt.c Handle dvb TS packets and count them for MRTG + ***************************************************************************** + * Copyright Tripleplay service 2004,2005,2011 + * + * Author: Andy Lindsay + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; + } +} diff -Nru dvblast-1.2/mrtg-cnt.h dvblast-2.1.0/mrtg-cnt.h --- dvblast-1.2/mrtg-cnt.h 1970-01-01 00:00:00.000000000 +0000 +++ dvblast-2.1.0/mrtg-cnt.h 2012-01-03 22:06:11.000000000 +0000 @@ -0,0 +1,32 @@ +/***************************************************************************** + * mrtg-cnt.h + ***************************************************************************** + * Copyright Tripleplay service 2004,2005,2011 + * + * Author: Andy Lindsay + * + * 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 diff -Nru dvblast-1.2/NEWS dvblast-2.1.0/NEWS --- dvblast-1.2/NEWS 2010-02-27 14:14:39.000000000 +0000 +++ dvblast-2.1.0/NEWS 2012-01-03 22:06:11.000000000 +0000 @@ -1,4 +1,34 @@ -$Id: NEWS 106 2010-02-27 14:14:39Z massiot $ + +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 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 + * Override tuning parameters with new options + * 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: ---------------------------- @@ -6,6 +36,7 @@ * Support input from Computer Modules ASI card * Support input from (IPv4) RTP / UDP stream * Miscellaneous CAM and demux fixes + * Miscellaneous DVB-S2 fixes Changes between 1.0 and 1.1: ---------------------------- diff -Nru dvblast-1.2/output.c dvblast-2.1.0/output.c --- dvblast-1.2/output.c 2010-02-28 20:39:34.000000000 +0000 +++ dvblast-2.1.0/output.c 2012-01-03 22:06:11.000000000 +0000 @@ -1,8 +1,7 @@ /***************************************************************************** * output.c ***************************************************************************** - * Copyright (C) 2004, 2008-2009 VideoLAN - * $Id: output.c 114 2010-02-28 20:39:34Z ivoire $ + * Copyright (C) 2004, 2008-2010 VideoLAN * * Authors: Christophe Massiot * Andy Gatward @@ -25,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -37,23 +37,52 @@ #include "dvblast.h" +#include +#include + /***************************************************************************** * Local prototypes *****************************************************************************/ -static int net_Open( output_t *p_output ); -static void rtp_SetHdr( output_t *p_output, uint8_t *p_hdr ); +struct packet_t +{ + 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; output_t *p_output = NULL; 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]; break; @@ -63,61 +92,106 @@ if ( p_output == NULL ) { p_output = malloc( sizeof(output_t) ); - memset( p_output, 0, sizeof(output_t) ); i_nb_outputs++; pp_outputs = realloc( pp_outputs, i_nb_outputs * sizeof(output_t *) ); 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 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; - p_output->i_depth = 0; - p_output->pi_pids = NULL; - p_output->i_nb_pids = 0; - - p_output->i_nb_errors = 0; - p_output->i_cc = rand() & 0xffff; + socklen_t i_sockaddr_len = (p_config->i_family == AF_INET) ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6); + + memset( p_output, 0, sizeof(output_t) ); + 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_pmt_cc = rand() & 0xf; + p_output->i_nit_cc = rand() & 0xf; p_output->i_sdt_cc = rand() & 0xf; p_output->i_eit_cc = rand() & 0xf; p_output->i_pat_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->p_pat_section = NULL; p_output->p_pmt_section = NULL; + p_output->p_nit_section = NULL; p_output->p_sdt_section = NULL; - if ( b_unique_tsid ) - p_output->i_ts_id = rand() & 0xffff; - p_output->i_ref_timestamp = 0; - p_output->i_ref_wallclock = 0; + p_output->p_eit_ts_buffer = NULL; + if ( b_random_tsid ) + p_output->i_tsid = rand() & 0xffff; + + /* 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_config = i_config; - p_output->psz_displayname = strdup( psz_displayname ); + 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; + } - struct addrinfo *p_ai = (struct addrinfo *)p_init_data; - p_output->i_addrlen = p_ai->ai_addrlen; - p_output->p_addr = malloc( p_output->i_addrlen ); - memcpy( p_output->p_addr, p_ai->ai_addr, - p_output->i_addrlen ); + if ( p_config->bind_addr.ss_family != AF_UNSPEC ) + { + 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) ); + + if ( p_config->i_family == AF_INET ) + { + struct sockaddr_in *p_connect_addr = + (struct sockaddr_in *)&p_output->config.connect_addr; + 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; - return -1; + struct sockaddr_in6 *p_addr = + (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) ); + } + + 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->i_config |= OUTPUT_VALID; + p_output->config.i_config |= OUTPUT_VALID; return 0; } @@ -127,19 +201,44 @@ *****************************************************************************/ void output_Close( output_t *p_output ) { - int i; - - for ( i = 0; i < p_output->i_depth; i++ ) + packet_t *p_packet = p_output->p_packets; + while ( p_packet != NULL ) { - p_output->pp_blocks[i]->i_refcount--; - if ( !p_output->pp_blocks[i]->i_refcount ) - block_Delete( p_output->pp_blocks[i] ); + int i; + + for ( i = 0; i < p_packet->i_depth; i++ ) + { + p_packet->pp_blocks[i]->i_refcount--; + if ( !p_packet->pp_blocks[i]->i_refcount ) + block_Delete( p_packet->pp_blocks[i] ); + } + p_output->p_packets = p_packet->p_next; + free( p_packet ); + p_packet = p_output->p_packets; } - free( p_output->psz_displayname ); - p_output->i_depth = 0; - p_output->i_config &= ~OUTPUT_VALID; + 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; + 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 @@ *****************************************************************************/ static void output_Flush( output_t *p_output ) { - struct iovec p_iov[NB_BLOCKS + 1]; - uint8_t p_rtp_hdr[RTP_SIZE]; - int i; - - int i_block_cnt = ( p_output->p_addr->ss_family == AF_INET6 ) ? NB_BLOCKS_IPV6 : NB_BLOCKS; - int i_outblocks = i_block_cnt; + packet_t *p_packet = p_output->p_packets; + int i_block_cnt = output_BlockCount( p_output ); + struct iovec p_iov[i_block_cnt + 1]; + uint8_t p_rtp_hdr[RTP_HEADER_SIZE]; + int i_iov, i_block; - 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_len = sizeof(p_rtp_hdr); - rtp_SetHdr( p_output, p_rtp_hdr ); - for ( i = 1; i < i_block_cnt + 1; i++ ) - { - p_iov[i].iov_base = p_output->pp_blocks[i - 1]->p_ts; - p_iov[i].iov_len = TS_SIZE; - } + rtp_set_hdr( p_rtp_hdr ); + rtp_set_type( p_rtp_hdr, RTP_TYPE_TS ); + rtp_set_seqnum( p_rtp_hdr, p_output->i_seqnum++ ); + 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 + 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_base = p_output->pp_blocks[i]->p_ts; - p_iov[i].iov_len = TS_SIZE; - } + p_iov[i_iov].iov_base = p_packet->pp_blocks[i_block]->p_ts; + p_iov[i_iov].iov_len = TS_SIZE; + i_iov++; } - if ( writev( p_output->i_handle, p_iov, i_outblocks ) < 0 ) + 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_iov ) < 0 ) { 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--; - if ( !p_output->pp_blocks[i]->i_refcount ) - block_Delete( p_output->pp_blocks[i] ); + p_packet->pp_blocks[i_block]->i_refcount--; + if ( !p_packet->pp_blocks[i_block]->i_refcount ) + 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 @@ *****************************************************************************/ 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_output->pp_blocks[p_output->i_depth] = p_block; - p_output->i_depth++; + if ( p_output->p_last_packet != NULL + && 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_output->i_depth >= NB_BLOCKS_IPV6 ) ) || - ( ( p_output->p_addr->ss_family == AF_INET ) && - ( p_output->i_depth >= NB_BLOCKS ) ) ) - output_Flush( p_output ); + p_packet->pp_blocks[p_packet->i_depth] = p_block; + p_packet->i_depth++; } /***************************************************************************** - * 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)", - p_output->psz_displayname, strerror(errno) ); - return -errno; + while ( output_dup.p_packets != NULL + && output_dup.p_packets->i_dts + + 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; - setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - (void *)&i, sizeof(i) ); - } + output_t *p_output = pp_outputs[i]; + if ( !( p_output->config.i_config & OUTPUT_VALID ) ) + 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; } - else if ( p_output->p_addr->ss_family == AF_INET ) + + 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; + } + + 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 ( IN_MULTICAST( ntohl( addr->sin_addr.s_addr ) ) ) + if ( p_output->config.i_family == AF_INET6 ) + { + struct sockaddr_in6 *p_addr = + (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_HOPS, (void *)&p_config->i_ttl, + sizeof(p_config->i_ttl) ); + } + else { - int i = i_ttl; - setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_TTL, - (void *)&i, sizeof(i) ); + 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)", - p_output->psz_displayname, strerror(errno) ); - close( i_handle ); - return -errno; + if ( p_output->config.i_family == AF_INET ) + setsockopt( p_output->i_handle, IPPROTO_IP, IP_TOS, + (void *)&p_config->i_tos, sizeof(p_config->i_tos) ); + 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 *****************************************************************************/ -/* - * 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 ) +void outputs_Close( int i_num_outputs ) { - mtime_t i_timestamp; + int i; - if (!p_output->i_ref_wallclock) - { - i_timestamp = p_output->i_ref_timestamp; - p_output->i_ref_wallclock = mdate(); - } - else + for ( i = 0; i < i_num_outputs; i++ ) { - i_timestamp = p_output->i_ref_timestamp - + (mdate() - p_output->i_ref_wallclock) * 9 / 100; + output_t *p_output = pp_outputs[i]; + + 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; - 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++; + free( pp_outputs ); } - diff -Nru dvblast-1.2/README dvblast-2.1.0/README --- dvblast-1.2/README 2010-02-28 22:21:33.000000000 +0000 +++ dvblast-2.1.0/README 2012-01-03 22:06:11.000000000 +0000 @@ -1,4 +1,3 @@ -$Id: README 116 2010-02-28 22:21:33Z massiot $ Welcome to DVBlast! =================== @@ -13,7 +12,7 @@ - hardware or software PID filtering - PID-based or service-based demultiplexing - 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, based on a PC with a Linux-supported card. It is very lightweight and @@ -24,9 +23,10 @@ ================ - 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 - - 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 - IPv6 network support - UDP rather than RTP output for IPTV STBs which don't support RTP @@ -48,6 +48,15 @@ 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. +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 information. @@ -55,6 +64,11 @@ 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 filter and demultiplex the inputs. You just need to specify the slot number with -A. @@ -65,13 +79,26 @@ such a stream for instance to cross network boundaries between the receivers and the target network. +The syntax of the -D option is: +[[:]@][:][/]* +where is the multicast address carrying the stream, and 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 =================== DVBlast reads a configuration file containing one or several lines in the format : -[:][/udp] [,]* +[:][@[:]][/