]> git.sesse.net Git - vlc/commitdiff
Added DVB scan capability.
authorLaurent Aimar <fenrir@videolan.org>
Mon, 25 Aug 2008 10:54:43 +0000 (12:54 +0200)
committerLaurent Aimar <fenrir@videolan.org>
Mon, 25 Aug 2008 20:32:45 +0000 (22:32 +0200)
Only DVB-T tuner is supported (but it would be easy to add DVB-S/C).
The files scan.c/h are strictly independant of the DVB access and could
be reused for the DBA access if wanted.

modules/access/dvb/Modules.am
modules/access/dvb/access.c
modules/access/dvb/dvb.h
modules/access/dvb/en50221.c
modules/access/dvb/http.c
modules/access/dvb/linux_dvb.c
modules/access/dvb/scan.c [new file with mode: 0644]
modules/access/dvb/scan.h [new file with mode: 0644]

index 1217318a99b060e973993b0f8a3aca3edf52b71c..22887e5fd35621fc4a46035f7bcd00d25c105c1d 100644 (file)
@@ -1,5 +1,6 @@
 SOURCES_dvb = \
        access.c \
+       scan.c \
        linux_dvb.c \
        en50221.c \
        http.c \
index 6dc9bed70ed8e5d0844fe54d55da1431faa9733f..b3b1be616113f809985ea37efb44807fe9de589f 100644 (file)
@@ -54,6 +54,9 @@
 #   include <dvbpsi/pmt.h>
 #   include <dvbpsi/dr.h>
 #   include <dvbpsi/psi.h>
+#   include <dvbpsi/demux.h>
+#   include <dvbpsi/sdt.h>
+#   include <dvbpsi/nit.h>
 #else
 #   include "dvbpsi.h"
 #   include "descriptor.h"
@@ -61,6 +64,9 @@
 #   include "tables/pmt.h"
 #   include "descriptors/dr.h"
 #   include "psi.h"
+#   include "demux.h"
+#   include "sdt.h"
+#   include "nit.h"
 #endif
 
 #ifdef ENABLE_HTTPD
@@ -196,7 +202,7 @@ vlc_module_begin();
                  false );
     add_integer( "dvb-device", 0, NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
                  true );
-    add_integer( "dvb-frequency", 11954000, NULL, FREQ_TEXT, FREQ_LONGTEXT,
+    add_integer( "dvb-frequency", 0, NULL, FREQ_TEXT, FREQ_LONGTEXT,
                  false );
     add_integer( "dvb-inversion", 2, NULL, INVERSION_TEXT, INVERSION_LONGTEXT,
                  true );
@@ -274,6 +280,7 @@ vlc_module_begin();
     add_shortcut( "usdigital" );
 
     set_callbacks( Open, Close );
+
 vlc_module_end();
 
 
@@ -283,10 +290,17 @@ vlc_module_end();
 static block_t *Block( access_t * );
 static int Control( access_t *, int, va_list );
 
+static block_t *BlockScan( access_t * );
+
 #define DVB_READ_ONCE 20
 #define DVB_READ_ONCE_START 2
+#define DVB_READ_ONCE_SCAN 1
 #define TS_PACKET_SIZE 188
 
+#define DVB_SCAN_MAX_SIGNAL_TIME (300*1000)
+#define DVB_SCAN_MAX_LOCK_TIME (5000*1000)
+#define DVB_SCAN_MAX_PROBE_TIME (30000*1000)
+
 static void FilterUnset( access_t *, int i_max );
 static void FilterUnsetPID( access_t *, int i_pid );
 static void FilterSet( access_t *, int i_pid, int i_type );
@@ -294,7 +308,6 @@ static void FilterSet( access_t *, int i_pid, int i_type );
 static void VarInit( access_t * );
 static int  ParseMRL( access_t * );
 
-
 /*****************************************************************************
  * Open: open the frontend device
  *****************************************************************************/
@@ -342,13 +355,23 @@ static int Open( vlc_object_t *p_this )
         return VLC_EGENERIC;
     }
 
-    /* Setting frontend parameters for tuning the hardware */
-    msg_Dbg( p_access, "trying to tune the frontend...");
-    if( FrontendSet( p_access ) < 0 )
+    /* */
+    p_sys->b_scan_mode = var_GetInteger( p_access, "dvb-frequency" ) == 0;
+    if( p_sys->b_scan_mode )
     {
-        FrontendClose( p_access );
-        free( p_sys );
-        return VLC_EGENERIC;
+        msg_Dbg( p_access, "DVB scan mode selected" );
+        p_access->pf_block = BlockScan;
+    }
+    else
+    {
+        /* Setting frontend parameters for tuning the hardware */
+        msg_Dbg( p_access, "trying to tune the frontend...");
+        if( FrontendSet( p_access ) < 0 )
+        {
+            FrontendClose( p_access );
+            free( p_sys );
+            return VLC_EGENERIC;
+        }
     }
 
     /* Opening DVR device */
@@ -359,29 +382,50 @@ static int Open( vlc_object_t *p_this )
         return VLC_EGENERIC;
     }
 
-    p_sys->b_budget_mode = var_GetBool( p_access, "dvb-budget-mode" );
-    if( p_sys->b_budget_mode )
+    if( p_sys->b_scan_mode )
     {
-        msg_Dbg( p_access, "setting filter on all PIDs" );
-        FilterSet( p_access, 0x2000, OTHER_TYPE );
+        scan_parameter_t parameter;
+
+        msg_Dbg( p_access, "setting filter on PAT/NIT/SDT (DVB only)" );
+        FilterSet( p_access, 0x00, OTHER_TYPE );    // PAT
+        FilterSet( p_access, 0x10, OTHER_TYPE );    // NIT
+        FilterSet( p_access, 0x11, OTHER_TYPE );    // SDT
+
+        if( FrontendGetScanParameter( p_access, &parameter ) ||
+            scan_Init( VLC_OBJECT(p_access), &p_sys->scan, &parameter ) )
+        {
+            Close( VLC_OBJECT(p_access) );
+            return VLC_EGENERIC;
+        }
     }
     else
     {
-        msg_Dbg( p_access, "setting filter on PAT" );
-        FilterSet( p_access, 0x0, OTHER_TYPE );
-    }
+        p_sys->b_budget_mode = var_GetBool( p_access, "dvb-budget-mode" );
+        if( p_sys->b_budget_mode )
+        {
+            msg_Dbg( p_access, "setting filter on all PIDs" );
+            FilterSet( p_access, 0x2000, OTHER_TYPE );
+        }
+        else
+        {
+            msg_Dbg( p_access, "setting filter on PAT" );
+            FilterSet( p_access, 0x0, OTHER_TYPE );
+        }
+
+        CAMOpen( p_access );
 
-    CAMOpen( p_access );
+#ifdef ENABLE_HTTPD
+        HTTPOpen( p_access );
+#endif
+    }
 
-    if( p_sys->b_budget_mode )
+    if( p_sys->b_scan_mode )
+        p_sys->i_read_once = DVB_READ_ONCE_SCAN;
+    else if( p_sys->b_budget_mode )
         p_sys->i_read_once = DVB_READ_ONCE;
     else
         p_sys->i_read_once = DVB_READ_ONCE_START;
 
-#ifdef ENABLE_HTTPD
-    HTTPOpen( p_access );
-#endif
-
     return VLC_SUCCESS;
 }
 
@@ -393,14 +437,18 @@ static void Close( vlc_object_t *p_this )
     access_t     *p_access = (access_t*)p_this;
     access_sys_t *p_sys = p_access->p_sys;
 
-    FilterUnset( p_access, p_sys->b_budget_mode ? 1 : MAX_DEMUX );
+    FilterUnset( p_access, p_sys->b_budget_mode && !p_sys->b_scan_mode ? 1 : MAX_DEMUX );
 
     DVRClose( p_access );
     FrontendClose( p_access );
-    CAMClose( p_access );
+    if( p_sys->b_scan_mode )
+        scan_Clean( &p_sys->scan );
+    else
+        CAMClose( p_access );
 
 #ifdef ENABLE_HTTPD
-    HTTPClose( p_access );
+    if( !p_sys->b_scan_mode )
+        HTTPClose( p_access );
 #endif
 
     free( p_sys );
@@ -512,6 +560,154 @@ static block_t *Block( access_t *p_access )
     return p_block;
 }
 
+/*****************************************************************************
+ * BlockScan:
+ *****************************************************************************/
+static block_t *BlockScan( access_t *p_access )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    scan_t *p_scan = &p_sys->scan;
+    scan_configuration_t cfg;
+    scan_session_t session;
+
+    /* */
+    if( scan_Next( p_scan, &cfg ) )
+    {
+        const bool b_first_eof = !p_access->info.b_eof;
+
+        if( b_first_eof )
+            msg_Warn( p_access, "Scanning finished" );
+
+        /* */
+        p_access->info.b_eof = true;
+        return b_first_eof ? scan_GetM3U( p_scan ) : NULL;
+    }
+
+    /* */
+
+    if( scan_session_Init( VLC_OBJECT(p_access), &session, &cfg ) )
+        return NULL;
+
+    /* */
+    msg_Dbg( p_access, "Scanning frequency %d", cfg.i_frequency );
+    var_SetInteger( p_access, "dvb-frequency", cfg.i_frequency );
+    var_SetInteger( p_access, "dvb-bandwidth", cfg.i_bandwidth );
+
+    /* Setting frontend parameters for tuning the hardware */
+    if( FrontendSet( p_access ) < 0 )
+    {
+        msg_Err( p_access, "Failed to tune the frontend" );
+        p_access->info.b_eof = true;
+        return NULL;
+    }
+
+    /* */
+    int64_t i_scan_start = mdate();
+
+    bool b_has_dvb_signal = false;
+    bool b_has_lock = false;
+    int i_best_snr = -1;
+
+    for ( ; ; )
+    {
+        struct pollfd ufds[2];
+        int i_ret;
+
+        /* Initialize file descriptor sets */
+        memset (ufds, 0, sizeof (ufds));
+        ufds[0].fd = p_sys->i_handle;
+        ufds[0].events = POLLIN;
+        ufds[1].fd = p_sys->i_frontend_handle;
+        ufds[1].events = POLLPRI;
+
+        /* We'll wait 0.1 second if nothing happens */
+        /* Find if some data is available */
+        i_ret = poll( ufds, 2, 100 );
+
+        if( !vlc_object_alive (p_access) || scan_IsCancelled( p_scan ) )
+            break;
+
+        if( i_ret <= 0 )
+        {
+            const mtime_t i_scan_time = mdate() - i_scan_start;
+            frontend_status_t status;
+
+            FrontendGetStatus( p_access, &status );
+
+            b_has_dvb_signal |= status.b_has_carrier;
+            b_has_lock |= status.b_has_lock;
+
+            if( ( !b_has_dvb_signal && i_scan_time > DVB_SCAN_MAX_SIGNAL_TIME ) ||
+                ( !b_has_lock && i_scan_time > DVB_SCAN_MAX_LOCK_TIME ) ||
+                ( i_scan_time > DVB_SCAN_MAX_PROBE_TIME ) )
+            {
+                msg_Dbg( p_access, "timed out scanning current frequency (s=%d l=%d)", b_has_dvb_signal, b_has_lock );
+                break;
+            }
+        }
+
+        if( i_ret < 0 )
+        {
+            if( errno == EINTR )
+                continue;
+
+            msg_Err( p_access, "poll error: %m" );
+            scan_session_Clean( p_scan, &session );
+
+            p_access->info.b_eof = true;
+            return NULL;
+        }
+
+        if( ufds[1].revents )
+        {
+            frontend_statistic_t stat;
+
+            FrontendPoll( p_access );
+
+            if( !FrontendGetStatistic( p_access, &stat ) )
+            {
+                if( stat.i_snr > i_best_snr )
+                    i_best_snr = stat.i_snr;
+            }
+        }
+
+        if ( p_sys->i_frontend_timeout && mdate() > p_sys->i_frontend_timeout )
+        {
+            msg_Warn( p_access, "no lock, tuning again" );
+            FrontendSet( p_access );
+        }
+
+        if ( ufds[0].revents )
+        {
+            const int i_read_once = 1;
+            block_t *p_block = block_New( p_access, i_read_once * TS_PACKET_SIZE );
+
+            if( ( i_ret = read( p_sys->i_handle, p_block->p_buffer,
+                                i_read_once * TS_PACKET_SIZE ) ) <= 0 )
+            {
+                msg_Warn( p_access, "read failed (%m)" );
+                block_Release( p_block );
+                continue;
+            }
+            p_block->i_buffer = i_ret;
+
+            /* */
+            if( scan_session_Push( &session, p_block ) )
+            {
+                msg_Dbg( p_access, "finished scanning current frequency" );
+                break;
+            }
+        }
+    }
+
+    /* */
+    if( i_best_snr > 0 )
+        scan_service_SetSNR( &session, i_best_snr );
+
+    scan_session_Clean( p_scan, &session );
+    return NULL;
+}
+
 /*****************************************************************************
  * Control:
  *****************************************************************************/
@@ -521,6 +717,7 @@ static int Control( access_t *p_access, int i_query, va_list args )
     bool   *pb_bool, b_bool;
     int          *pi_int, i_int;
     int64_t      *pi_64;
+    dvbpsi_pmt_t *p_pmt;
 
     switch( i_query )
     {
@@ -535,7 +732,10 @@ static int Control( access_t *p_access, int i_query, va_list args )
         /* */
         case ACCESS_GET_MTU:
             pi_int = (int*)va_arg( args, int * );
-            *pi_int = DVB_READ_ONCE * TS_PACKET_SIZE;
+            if( p_sys->b_scan_mode )
+                *pi_int = 0;
+            else
+                *pi_int = DVB_READ_ONCE * TS_PACKET_SIZE;
             break;
 
         case ACCESS_GET_PTS_DELAY:
@@ -552,6 +752,9 @@ static int Control( access_t *p_access, int i_query, va_list args )
             return VLC_EGENERIC;
 
         case ACCESS_SET_PRIVATE_ID_STATE:
+            if( p_sys->b_scan_mode )
+                return VLC_EGENERIC;
+
             i_int  = (int)va_arg( args, int );               /* Private data (pid for now)*/
             b_bool = (bool)va_arg( args, int ); /* b_selected */
             if( !p_sys->b_budget_mode )
@@ -565,14 +768,13 @@ static int Control( access_t *p_access, int i_query, va_list args )
             break;
 
         case ACCESS_SET_PRIVATE_ID_CA:
-        {
-            dvbpsi_pmt_t *p_pmt;
+            if( p_sys->b_scan_mode )
+                return VLC_EGENERIC;
 
             p_pmt = (dvbpsi_pmt_t *)va_arg( args, dvbpsi_pmt_t * );
 
             CAMSet( p_access, p_pmt );
             break;
-        }
 
         default:
             msg_Warn( p_access, "unimplemented query in control" );
index 49be127b0285fe2a652261c2c6a4d90ccfbe420f..20b8d1017abcaf151727b877fdfebb3464831aac 100644 (file)
@@ -24,6 +24,8 @@
  *****************************************************************************/
 
 
+#include "scan.h"
+
 /*****************************************************************************
  * Devices location
  *****************************************************************************/
@@ -43,6 +45,19 @@ typedef struct demux_handle_t
 } demux_handle_t;
 
 typedef struct frontend_t frontend_t;
+typedef struct
+{
+    int i_snr;              /**< Signal Noise ratio */
+    int i_ber;              /**< Bitrate error ratio */
+    int i_signal_strenth;   /**< Signal strength */
+} frontend_statistic_t;
+
+typedef struct
+{
+    bool b_has_signal;
+    bool b_has_carrier;
+    bool b_has_lock;
+} frontend_status_t;
 
 typedef struct en50221_session_t
 {
@@ -138,6 +153,7 @@ struct access_sys_t
     demux_handle_t p_demux_handles[MAX_DEMUX];
     frontend_t *p_frontend;
     bool b_budget_mode;
+    bool b_scan_mode;
 
     /* CA management */
     int i_ca_handle;
@@ -168,6 +184,9 @@ struct access_sys_t
     char                *psz_frontend_info, *psz_mmi_info;
     char                *psz_request;
 #endif
+
+    /* Scan */
+    scan_t scan;
 };
 
 #define VIDEO0_TYPE     1
@@ -181,6 +200,7 @@ struct access_sys_t
 /*****************************************************************************
  * Prototypes
  *****************************************************************************/
+
 int  FrontendOpen( access_t * );
 void FrontendPoll( access_t *p_access );
 int  FrontendSet( access_t * );
@@ -189,6 +209,10 @@ void FrontendClose( access_t * );
 void FrontendStatus( access_t * );
 #endif
 
+int  FrontendGetStatistic( access_t *, frontend_statistic_t * );
+void FrontendGetStatus( access_t *, frontend_status_t * );
+int  FrontendGetScanParameter( access_t *, scan_parameter_t * );
+
 int DMXSetFilter( access_t *, int i_pid, int * pi_fd, int i_type );
 int DMXUnsetFilter( access_t *, int i_fd );
 
@@ -214,6 +238,8 @@ void en50221_SendMMIObject( access_t * p_access, int i_slot,
                                 en50221_mmi_object_t *p_object );
 void en50221_End( access_t * );
 
+char *dvbsi_to_utf8( char *psz_instring, size_t i_length );
+
 #ifdef ENABLE_HTTPD
 int HTTPOpen( access_t *p_access );
 void HTTPClose( access_t *p_access );
@@ -225,3 +251,4 @@ char *HTTPExtractValue( char *psz_uri, const char *psz_name,
  *****************************************************************************/
 #define STRINGIFY( z )   UGLY_KLUDGE( z )
 #define UGLY_KLUDGE( z ) #z
+
index 9a78e97f0f7202fb36c5195a2482f43da96d3765..965ac4102bf7e3c7c27f17a316362cc6ef10dfb3 100644 (file)
@@ -55,6 +55,9 @@
 #   include <dvbpsi/pmt.h>
 #   include <dvbpsi/dr.h>
 #   include <dvbpsi/psi.h>
+#   include <dvbpsi/demux.h>
+#   include <dvbpsi/sdt.h>
+#   include <dvbpsi/nit.h>
 #else
 #   include "dvbpsi.h"
 #   include "descriptor.h"
@@ -62,6 +65,9 @@
 #   include "tables/pmt.h"
 #   include "descriptors/dr.h"
 #   include "psi.h"
+#   include "demux.h"
+#   include "sdt.h"
+#   include "nit.h"
 #endif
 
 #ifdef ENABLE_HTTPD
@@ -81,7 +87,6 @@ static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
 static void DateTimeOpen( access_t * p_access, int i_session_id );
 static void MMIOpen( access_t * p_access, int i_session_id );
-static char *dvbsi_to_utf8( char *psz_instring, size_t i_length );
 
 /*****************************************************************************
  * Utility functions
@@ -174,6 +179,8 @@ static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
     for ( i = 0; i < i_size && i < MAX_DUMP; i++)
         fprintf(stderr, "%02X ", p_data[i]);
     fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
+#else
+    VLC_UNUSED(b_outgoing); VLC_UNUSED(p_data); VLC_UNUSED(i_size);
 #endif
 }
 
@@ -2318,7 +2325,7 @@ static inline void *FixUTF8( char *p )
     return p;
 }
 
-static char *dvbsi_to_utf8( char *psz_instring, size_t i_length )
+char *dvbsi_to_utf8( char *psz_instring, size_t i_length )
 {
     const char *psz_encoding, *psz_stringstart;
     char *psz_outstring, *psz_tmp;
index 8074273465e20fc79eda1f2b6e5c220a1d8390da..21365ff361e3b15de582c7b08c8d99dd170f5a75 100644 (file)
@@ -48,6 +48,9 @@
 #   include <dvbpsi/pmt.h>
 #   include <dvbpsi/dr.h>
 #   include <dvbpsi/psi.h>
+#   include <dvbpsi/demux.h>
+#   include <dvbpsi/sdt.h>
+#   include <dvbpsi/nit.h>
 #else
 #   include "dvbpsi.h"
 #   include "descriptor.h"
@@ -55,6 +58,9 @@
 #   include "tables/pmt.h"
 #   include "descriptors/dr.h"
 #   include "psi.h"
+#   include "demux.h"
+#   include "sdt.h"
+#   include "nit.h"
 #endif
 
 #ifdef ENABLE_HTTPD
index c282b5e893d2f859d160ef7d5ddf3aa90d2bd10d..01acde204d6ac76d1941ee240e05b4ca8042fade 100644 (file)
@@ -55,6 +55,9 @@
 #   include <dvbpsi/pmt.h>
 #   include <dvbpsi/dr.h>
 #   include <dvbpsi/psi.h>
+#   include <dvbpsi/demux.h>
+#   include <dvbpsi/sdt.h>
+#   include <dvbpsi/nit.h>
 #else
 #   include "dvbpsi.h"
 #   include "descriptor.h"
@@ -62,6 +65,9 @@
 #   include "tables/pmt.h"
 #   include "descriptors/dr.h"
 #   include "psi.h"
+#   include "demux.h"
+#   include "sdt.h"
+#   include "nit.h"
 #endif
 
 #ifdef ENABLE_HTTPD
@@ -339,17 +345,21 @@ void FrontendPoll( access_t *p_access )
 
             IF_UP( FE_HAS_LOCK )
             {
-                int32_t i_value = 0;
+                frontend_statistic_t stat;
+
                 msg_Dbg( p_access, "frontend has acquired lock" );
                 p_sys->i_frontend_timeout = 0;
 
                 /* Read some statistics */
-                if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &i_value ) >= 0 )
-                    msg_Dbg( p_access, "- Bit error rate: %d", i_value );
-                if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH, &i_value ) >= 0 )
-                    msg_Dbg( p_access, "- Signal strength: %d", i_value );
-                if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &i_value ) >= 0 )
-                    msg_Dbg( p_access, "- SNR: %d", i_value );
+                if( !FrontendGetStatistic( p_access, &stat ) )
+                {
+                    if( stat.i_ber >= 0 )
+                        msg_Dbg( p_access, "- Bit error rate: %d", stat.i_ber );
+                    if( stat.i_signal_strenth >= 0 )
+                        msg_Dbg( p_access, "- Signal strength: %d", stat.i_signal_strenth );
+                    if( stat.i_snr >= 0 )
+                        msg_Dbg( p_access, "- SNR: %d", stat.i_snr );
+                }
             }
             else
             {
@@ -367,6 +377,66 @@ void FrontendPoll( access_t *p_access )
 #undef IF_UP
     }
 }
+int FrontendGetStatistic( access_t *p_access, frontend_statistic_t *p_stat )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    frontend_t * p_frontend = p_sys->p_frontend;
+
+    if( (p_frontend->i_last_status & FE_HAS_LOCK) == 0 )
+        return VLC_EGENERIC;
+
+    memset( p_stat, 0, sizeof(*p_stat) );
+    if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &p_stat->i_ber ) < 0 )
+        p_stat->i_ber = -1;
+    if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH, &p_stat->i_signal_strenth ) < 0 )
+        p_stat->i_signal_strenth = -1;
+    if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &p_stat->i_snr ) < 0 )
+        p_stat->i_snr = -1;
+
+    return VLC_SUCCESS;
+}
+void FrontendGetStatus( access_t *p_access, frontend_status_t *p_status )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    frontend_t * p_frontend = p_sys->p_frontend;
+
+    p_status->b_has_signal = (p_frontend->i_last_status & FE_HAS_SIGNAL) != 0;
+    p_status->b_has_carrier = (p_frontend->i_last_status & FE_HAS_CARRIER) != 0;
+    p_status->b_has_lock = (p_frontend->i_last_status & FE_HAS_LOCK) != 0;
+}
+static int ScanParametersDvbT( access_t *p_access, scan_parameter_t *p_scan )
+{
+    const frontend_t *p_frontend = p_access->p_sys->p_frontend;
+
+
+    memset( p_scan, 0, sizeof(*p_scan) );
+    p_scan->type = SCAN_DVB_T;
+    p_scan->b_exhaustive = false;
+
+    /* */
+    p_scan->frequency.i_min = p_frontend->info.frequency_min;
+    p_scan->frequency.i_max = p_frontend->info.frequency_max;
+    p_scan->frequency.i_step = p_frontend->info.frequency_stepsize;
+    p_scan->frequency.i_count = (p_scan->frequency.i_max-p_scan->frequency.i_min)/p_scan->frequency.i_step;
+
+    /* */
+    p_scan->bandwidth.i_min  = 6;
+    p_scan->bandwidth.i_max  = 8;
+    p_scan->bandwidth.i_step = 1;
+    p_scan->bandwidth.i_count = 3;
+    return VLC_SUCCESS;
+}
+int  FrontendGetScanParameter( access_t *p_access, scan_parameter_t *p_scan )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    const frontend_t *p_frontend = p_sys->p_frontend;
+
+    if( p_frontend->info.type == FE_OFDM )  // DVB-T
+        return ScanParametersDvbT( p_access, p_scan );
+
+    msg_Err( p_access, "Frontend type not supported for scanning" );
+    return VLC_EGENERIC;
+}
 
 #ifdef ENABLE_HTTPD
 /*****************************************************************************
diff --git a/modules/access/dvb/scan.c b/modules/access/dvb/scan.c
new file mode 100644 (file)
index 0000000..5182603
--- /dev/null
@@ -0,0 +1,793 @@
+/*****************************************************************************
+ * scan.c: DVB scanner helpers
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ *
+ * Authors: Laurent Aimar <fenrir@videolan.org>
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_access.h>
+#include <vlc_interface.h>
+
+#ifdef HAVE_UNISTD_H
+#   include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+
+#include <errno.h>
+
+/* Include dvbpsi headers */
+#ifdef HAVE_DVBPSI_DR_H
+#   include <dvbpsi/dvbpsi.h>
+#   include <dvbpsi/descriptor.h>
+#   include <dvbpsi/pat.h>
+#   include <dvbpsi/pmt.h>
+#   include <dvbpsi/dr.h>
+#   include <dvbpsi/psi.h>
+#   include <dvbpsi/demux.h>
+#   include <dvbpsi/sdt.h>
+#   include <dvbpsi/nit.h>
+#else
+#   include "dvbpsi.h"
+#   include "descriptor.h"
+#   include "tables/pat.h"
+#   include "tables/pmt.h"
+#   include "descriptors/dr.h"
+#   include "psi.h"
+#   include "demux.h"
+#   include "sdt.h"
+#   include "nit.h"
+#endif
+
+#ifdef ENABLE_HTTPD
+#   include "vlc_httpd.h"
+#endif
+
+#include "dvb.h"
+
+/* */
+scan_service_t *scan_service_New( int i_program, const scan_configuration_t *p_cfg  )
+{
+    scan_service_t *p_srv = malloc( sizeof(*p_srv) );
+    if( !p_srv )
+        return NULL;
+
+    p_srv->i_program = i_program;
+    p_srv->cfg = *p_cfg;
+    p_srv->i_snr = -1;
+
+    p_srv->type = SERVICE_UNKNOWN;
+    p_srv->psz_name = NULL;
+    p_srv->i_channel = -1;
+    p_srv->b_crypted = false;
+
+    p_srv->i_network_id = -1;
+    p_srv->i_nit_version = -1;
+    p_srv->i_sdt_version = -1;
+
+    return p_srv;
+}
+
+void scan_service_Delete( scan_service_t *p_srv )
+{
+    free( p_srv->psz_name );
+    free( p_srv );
+}
+
+/* */
+int scan_Init( vlc_object_t *p_obj, scan_t *p_scan, const scan_parameter_t *p_parameter )
+{
+    if( p_parameter->type == SCAN_DVB_T )
+    {
+        msg_Dbg( p_obj, "DVB-T scanning:" );
+        msg_Dbg( p_obj, " - frequency [%d, %d]",
+                 p_parameter->frequency.i_min, p_parameter->frequency.i_max );
+        msg_Dbg( p_obj, " - bandwidth [%d,%d]",
+                 p_parameter->bandwidth.i_min, p_parameter->bandwidth.i_max );
+        msg_Dbg( p_obj, " - exhaustive mode %s", p_parameter->b_exhaustive ? "on" : "off" );
+    }
+    else
+    {
+        return VLC_EGENERIC;
+    }
+
+    p_scan->p_obj = VLC_OBJECT(p_obj);
+    p_scan->i_index = 0;
+    p_scan->i_dialog_id = -1;
+    TAB_INIT( p_scan->i_service, p_scan->pp_service );
+    p_scan->parameter = *p_parameter;
+    p_scan->i_time_start = mdate();
+
+    return VLC_SUCCESS;
+}
+void scan_Clean( scan_t *p_scan )
+{
+    if( p_scan->i_dialog_id >= 0 )
+        intf_UserHide( p_scan->p_obj, p_scan->i_dialog_id );
+
+    for( int i = 0; i < p_scan->i_service; i++ )
+        scan_service_Delete( p_scan->pp_service[i] );
+    TAB_CLEAN( p_scan->i_service, p_scan->pp_service );
+}
+
+
+static int ScanDvbTNextExhaustive( scan_t *p_scan, scan_configuration_t *p_cfg, double *pf_pos )
+{
+    if( p_scan->i_index > p_scan->parameter.frequency.i_count * p_scan->parameter.bandwidth.i_count )
+        return VLC_EGENERIC;
+
+    const int i_bi = p_scan->i_index % p_scan->parameter.bandwidth.i_count;
+    const int i_fi = p_scan->i_index / p_scan->parameter.bandwidth.i_count;
+
+    p_cfg->i_frequency = p_scan->parameter.frequency.i_min + i_fi * p_scan->parameter.frequency.i_step;
+    p_cfg->i_bandwidth = p_scan->parameter.bandwidth.i_min + i_bi * p_scan->parameter.bandwidth.i_step;
+
+    *pf_pos = (double)p_scan->i_index / p_scan->parameter.frequency.i_count;
+    return VLC_SUCCESS;
+}
+
+static int ScanDvbTNextFast( scan_t *p_scan, scan_configuration_t *p_cfg, double *pf_pos )
+{
+    static const int i_band_count = 2;
+    static const struct
+    {
+        const char *psz_name;
+        int i_min;
+        int i_max;
+    }
+    band[2] =
+    {
+        { "VHF", 174, 230 },
+        { "UHF", 470, 862 },
+    };
+    const int i_offset_count = 5;
+    const int i_mhz = 1000000;
+
+    /* We will probe the whole band divided in all bandwidth possibility trying 
+     * i_offset_count offset around the position
+     */
+    for( ;; p_scan->i_index++ )
+    {
+
+        const int i_bi = p_scan->i_index % p_scan->parameter.bandwidth.i_count;
+        const int i_oi = (p_scan->i_index / p_scan->parameter.bandwidth.i_count) % i_offset_count;
+        const int i_fi = (p_scan->i_index / p_scan->parameter.bandwidth.i_count) / i_offset_count;
+
+        const int i_bandwidth = p_scan->parameter.bandwidth.i_min + i_bi * p_scan->parameter.bandwidth.i_step;
+        int i;
+
+        for( i = 0; i < i_band_count; i++ )
+        {
+            if( i_fi >= band[i].i_min && i_fi <= band[i].i_max )
+                break;
+        }
+        if( i >=i_band_count )
+        {
+            if( i_fi > band[i_band_count-1].i_max )
+                return VLC_EGENERIC;
+            continue;
+        }
+
+        const int i_frequency_min = band[i].i_min*i_mhz + i_bandwidth*i_mhz/2;
+        const int i_frequency_base = i_fi*i_mhz;
+
+        if( i_frequency_base >= i_frequency_min && ( i_frequency_base - i_frequency_min ) % ( i_bandwidth*i_mhz ) == 0 )
+        {
+            const int i_frequency = i_frequency_base + ( i_oi - i_offset_count/2 ) * p_scan->parameter.frequency.i_step;
+
+            if( i_frequency < p_scan->parameter.frequency.i_min ||
+                i_frequency > p_scan->parameter.frequency.i_max )
+                continue;
+
+            p_cfg->i_frequency = i_frequency;
+            p_cfg->i_bandwidth = i_bandwidth;
+
+            int i_current = 0, i_total = 0;
+            for( int i = 0; i < i_band_count; i++ )
+            {
+                const int i_frag = band[i].i_max-band[i].i_min;
+
+                if( i_fi >= band[i].i_min )
+                    i_current += __MIN( i_fi - band[i].i_min, i_frag );
+                i_total += i_frag;
+            }
+
+            *pf_pos = (double)( i_current + (double)i_oi / i_offset_count ) / i_total;
+            return VLC_SUCCESS;
+        }
+    }
+}
+
+static int ScanDvbTNext( scan_t *p_scan, scan_configuration_t *p_cfg, double *pf_pos )
+{
+    if( p_scan->parameter.b_exhaustive )
+        return ScanDvbTNextExhaustive( p_scan, p_cfg, pf_pos );
+    else
+        return ScanDvbTNextFast( p_scan, p_cfg, pf_pos );
+}
+
+int scan_Next( scan_t *p_scan, scan_configuration_t *p_cfg )
+{
+    double f_position;
+    int i_ret;
+
+    if( scan_IsCancelled( p_scan ) )
+        return VLC_EGENERIC;
+
+    memset( p_cfg, 0, sizeof(*p_cfg) );
+    switch( p_scan->parameter.type )
+    {
+    case SCAN_DVB_T:
+        i_ret = ScanDvbTNext( p_scan, p_cfg, &f_position );
+        break;
+    default:
+        i_ret = VLC_EGENERIC;
+        break;
+    }
+
+    if( i_ret )
+        return i_ret;
+
+    char *psz_text;
+    int i_service = 0;
+
+    for( int i = 0; i < p_scan->i_service; i++ )
+    {
+        if( p_scan->pp_service[i]->type != SERVICE_UNKNOWN )
+            i_service++;
+    }
+
+    if( asprintf( &psz_text, _("%.1f MHz (%d services)"), 
+                  (double)p_cfg->i_frequency / 1000000, i_service ) >= 0 )
+    {
+        const mtime_t i_eta = f_position > 0.005 ? (mdate() - p_scan->i_time_start) * ( 1.0 / f_position - 1.0 ) : -1;
+        char psz_eta[MSTRTIME_MAX_SIZE];
+
+        if( i_eta >= 0 )
+            msg_Info( p_scan->p_obj, "Scan ETA %s | %f", secstotimestr( psz_eta, i_eta/1000000 ), f_position * 100 );
+
+        if( p_scan->i_dialog_id < 0 )
+            p_scan->i_dialog_id = intf_UserProgress( p_scan->p_obj, _("Scanning DVB-T"), psz_text, 100.0 * f_position, -1 );
+        else
+            intf_ProgressUpdate( p_scan->p_obj, p_scan->i_dialog_id, psz_text, 100 * f_position, -1 );
+        free( psz_text );
+    }
+
+    p_scan->i_index++;
+    return VLC_SUCCESS;
+}
+
+bool scan_IsCancelled( scan_t *p_scan )
+{
+    return p_scan->i_dialog_id >= 0 && intf_ProgressIsCancelled( p_scan->p_obj, p_scan->i_dialog_id );
+}
+
+static scan_service_t *ScanFindService( scan_t *p_scan, int i_service_start, int i_program )
+{
+    /* FIXME add network id */
+    for( int i = i_service_start; i < p_scan->i_service; i++ )
+    {
+        if( p_scan->pp_service[i]->i_program == i_program )
+            return p_scan->pp_service[i];
+    }
+    return NULL;
+}
+
+/* FIXME handle properly string (convert to utf8) */
+static void PATCallBack( scan_session_t *p_session, dvbpsi_pat_t *p_pat )
+{
+    vlc_object_t *p_obj = p_session->p_obj;
+
+    msg_Err( p_obj, "PATCallBack" );
+
+    /* */
+    if( p_session->p_pat && p_session->p_pat->b_current_next )
+    {
+        dvbpsi_DeletePAT( p_session->p_pat );
+        p_session->p_pat = NULL;
+    }
+    if( p_session->p_pat )
+    {
+        dvbpsi_DeletePAT( p_pat );
+        return;
+    }
+
+    dvbpsi_pat_program_t *p_program;
+
+    /* */
+    p_session->p_pat = p_pat;
+
+    /* */
+    msg_Dbg( p_obj, "new PAT ts_id=%d version=%d current_next=%d",
+             p_pat->i_ts_id, p_pat->i_version, p_pat->b_current_next );
+    for( p_program = p_pat->p_first_program; p_program != NULL; p_program = p_program->p_next )
+    {
+        msg_Dbg( p_obj, "  * number=%d pid=%d", p_program->i_number, p_program->i_pid );
+        if( p_program->i_number == 0 )
+            p_session->i_nit_pid = p_program->i_pid;
+    }
+}
+static void SDTCallBack( scan_session_t *p_session, dvbpsi_sdt_t *p_sdt )
+{
+    vlc_object_t *p_obj = p_session->p_obj;
+
+    msg_Err( p_obj, "SDTCallBack" );
+
+    if( p_session->p_sdt && p_session->p_sdt->b_current_next )
+    {
+        dvbpsi_DeleteSDT( p_session->p_sdt );
+        p_session->p_sdt = NULL;
+    }
+    if( p_session->p_sdt )
+    {
+        dvbpsi_DeleteSDT( p_sdt );
+        return;
+    }
+
+    /* */
+    p_session->p_sdt = p_sdt;
+
+    /* */
+    msg_Dbg( p_obj, "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 );
+
+
+    dvbpsi_sdt_service_t *p_srv;
+    for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next )
+    {
+        dvbpsi_descriptor_t *p_dr;
+
+        msg_Dbg( p_obj, "  * 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 );
+        for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
+        {
+            if( p_dr->i_tag == 0x48 )
+            {
+                dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
+                char str2[257];
+
+                memcpy( str2, pD->i_service_name, pD->i_service_name_length );
+                str2[pD->i_service_name_length] = '\0';
+
+                msg_Dbg( p_obj, "    - type=%d name=%s",
+                         pD->i_service_type, str2 );
+            }
+            else
+            {
+                msg_Dbg( p_obj, "    * dsc 0x%x", p_dr->i_tag );
+            }
+        }
+    }
+}
+
+static void NITCallBack( scan_session_t *p_session, dvbpsi_nit_t *p_nit )
+{
+    vlc_object_t *p_obj = p_session->p_obj;
+
+    msg_Err( p_obj, "NITCallBack" );
+    msg_Dbg( p_obj, "new NIT network_id=%d version=%d current_next=%d",
+             p_nit->i_network_id, p_nit->i_version, p_nit->b_current_next );
+
+    /* */
+    if( p_session->p_nit && p_session->p_nit->b_current_next )
+    {
+        dvbpsi_DeleteNIT( p_session->p_nit );
+        p_session->p_nit = NULL;
+    }
+    if( p_session->p_nit )
+    {
+        dvbpsi_DeleteNIT( p_nit );
+        return;
+    }
+
+    /* */
+    p_session->p_nit = p_nit;
+
+    dvbpsi_descriptor_t *p_dsc;
+    for( p_dsc = p_nit->p_first_descriptor; p_dsc != NULL; p_dsc = p_dsc->p_next )
+    {
+        if( p_dsc->i_tag == 0x40 )
+        {
+            msg_Dbg( p_obj, "   * network name descriptor" );
+            char str1[257];
+
+            memcpy( str1, p_dsc->p_data, p_dsc->i_length );
+            str1[p_dsc->i_length] = '\0';
+            msg_Dbg( p_obj, "       * name %s", str1 );
+        }
+        else if( p_dsc->i_tag == 0x4a )
+        {
+            msg_Dbg( p_obj, "   * linkage descriptor" );
+            uint16_t i_ts_id = GetWBE( &p_dsc->p_data[0] );
+            uint16_t i_on_id = GetWBE( &p_dsc->p_data[2] );
+            uint16_t i_service_id = GetWBE( &p_dsc->p_data[4] );
+            int i_linkage_type = p_dsc->p_data[6];
+
+            msg_Dbg( p_obj, "       * ts_id %d", i_ts_id );
+            msg_Dbg( p_obj, "       * on_id %d", i_on_id );
+            msg_Dbg( p_obj, "       * service_id %d", i_service_id );
+            msg_Dbg( p_obj, "       * linkage_type %d", i_linkage_type );
+        }
+        else 
+        {
+            msg_Dbg( p_obj, "   * dsc 0x%x", p_dsc->i_tag );
+        }
+    }
+
+    dvbpsi_nit_ts_t *p_ts;
+    for( p_ts = p_nit->p_first_ts; p_ts != NULL; p_ts = p_ts->p_next )
+    {
+        msg_Dbg( p_obj, "   * ts ts_id=0x%x original_id=0x%x", p_ts->i_ts_id, p_ts->i_orig_network_id );
+
+        uint32_t i_private_data_id = 0;
+        dvbpsi_descriptor_t *p_dsc;
+        for( p_dsc = p_ts->p_first_descriptor; p_dsc != NULL; p_dsc = p_dsc->p_next )
+        {
+            if( p_dsc->i_tag == 0x41 )
+            {
+                msg_Dbg( p_obj, "       * service list descriptor" );
+                for( int i = 0; i < p_dsc->i_length/3; i++ )
+                {
+                    uint16_t i_service_id = GetWBE( &p_dsc->p_data[3*i+0] );
+                    uint8_t  i_service_type = p_dsc->p_data[3*i+2];
+                    msg_Dbg( p_obj, "           * service_id=%d type=%d", i_service_id, i_service_type );
+                }
+            }
+            else if( p_dsc->i_tag == 0x5a )
+            {
+                dvbpsi_terr_deliv_sys_dr_t *p_t = dvbpsi_DecodeTerrDelivSysDr( p_dsc );
+                msg_Dbg( p_obj, "       * terrestrial delivery system" );
+                msg_Dbg( p_obj, "           * centre_frequency 0x%x", p_t->i_centre_frequency  );
+                msg_Dbg( p_obj, "           * bandwidth %d", 8 - p_t->i_bandwidth );
+                msg_Dbg( p_obj, "           * constellation %d", p_t->i_constellation );
+                msg_Dbg( p_obj, "           * hierarchy %d", p_t->i_hierarchy_information );
+                msg_Dbg( p_obj, "           * code_rate hp %d lp %d", p_t->i_code_rate_hp_stream, p_t->i_code_rate_lp_stream );
+                msg_Dbg( p_obj, "           * guard_interval %d", p_t->i_guard_interval );
+                msg_Dbg( p_obj, "           * transmission_mode %d", p_t->i_transmission_mode );
+                msg_Dbg( p_obj, "           * other_frequency_flag %d", p_t->i_other_frequency_flag );
+            }
+            else if( p_dsc->i_tag == 0x5f )
+            {
+                msg_Dbg( p_obj, "       * private data specifier descriptor" );
+                i_private_data_id = GetDWBE( &p_dsc->p_data[0] );
+                msg_Dbg( p_obj, "           * value 0x%8.8x", i_private_data_id );
+            }
+            else if( i_private_data_id == 0x28 && p_dsc->i_tag == 0x83 )
+            {
+                msg_Dbg( p_obj, "       * logical channel descriptor (EICTA)" );
+                for( int i = 0; i < p_dsc->i_length/4; i++ )
+                {
+                    uint16_t i_service_id = GetWBE( &p_dsc->p_data[4*i+0] );
+                    int i_channel_number = GetWBE( &p_dsc->p_data[4*i+2] ) & 0x3ff;
+                    msg_Dbg( p_obj, "           * service_id=%d channel_number=%d", i_service_id, i_channel_number );
+                }
+
+            }
+            else
+            {
+                msg_Warn( p_obj, "       * dsc 0x%x", p_dsc->i_tag );
+            }
+        }
+    }
+    //dvbpsi_DeleteNIT( p_nit );
+}
+
+static void PSINewTableCallBack( scan_session_t *p_session, dvbpsi_handle h, uint8_t  i_table_id, uint16_t i_extension )
+{
+    if( i_table_id == 0x42 )
+        dvbpsi_AttachSDT( h, i_table_id, i_extension, (dvbpsi_sdt_callback)SDTCallBack, p_session );
+    else if( i_table_id == 0x40 )
+        dvbpsi_AttachNIT( h, i_table_id, i_extension, (dvbpsi_nit_callback)NITCallBack, p_session );
+}
+
+
+int scan_session_Init( vlc_object_t *p_obj, scan_session_t *p_session, const scan_configuration_t *p_cfg )
+{
+    /* */
+    memset( p_session, 0, sizeof(*p_session) );
+    p_session->p_obj = p_obj;
+    p_session->cfg = *p_cfg;
+    p_session->i_snr = -1;
+    p_session->pat = NULL;
+    p_session->p_pat = NULL;
+    p_session->i_nit_pid = -1;
+    p_session->sdt = NULL;
+    p_session->p_sdt = NULL;
+    p_session->nit = NULL;
+    p_session->p_nit = NULL;
+
+    return VLC_SUCCESS;
+}
+
+void scan_session_Clean( scan_t *p_scan, scan_session_t *p_session )
+{
+    const int i_service_start = p_scan->i_service;
+
+    dvbpsi_pat_t *p_pat = p_session->p_pat;
+    dvbpsi_sdt_t *p_sdt = p_session->p_sdt;
+    dvbpsi_nit_t *p_nit = p_session->p_nit;
+
+    if( p_pat )
+    {
+        /* Parse PAT */
+        dvbpsi_pat_program_t *p_program;
+        for( p_program = p_pat->p_first_program; p_program != NULL; p_program = p_program->p_next )
+        {
+            if( p_program->i_number == 0 )  /* NIT */
+                continue;
+
+            scan_service_t *s = scan_service_New( p_program->i_number, &p_session->cfg );
+            TAB_APPEND( p_scan->i_service, p_scan->pp_service, s );
+        }
+    }
+    /* Parse SDT */
+    if( p_pat && p_sdt )
+    {
+        dvbpsi_sdt_service_t *p_srv;
+        for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next )
+        {
+            scan_service_t *s = ScanFindService( p_scan, i_service_start, p_srv->i_service_id );
+            dvbpsi_descriptor_t *p_dr;
+
+            if( s )
+                s->b_crypted = p_srv->b_free_ca;
+
+            for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
+            {
+                if( p_dr->i_tag == 0x48 )
+                {
+                    dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
+
+                    if( s )
+                    {
+                        if( !s->psz_name )
+                            s->psz_name = dvbsi_to_utf8( pD->i_service_name, pD->i_service_name_length );
+
+                        if( s->type == SERVICE_UNKNOWN )
+                        {
+                            switch( pD->i_service_type )
+                            {
+                            case 0x01: s->type = SERVICE_DIGITAL_TELEVISION; break;
+                            case 0x02: s->type = SERVICE_DIGITAL_RADIO; break;
+                            case 0x16: s->type = SERVICE_DIGITAL_TELEVISION_AC_SD; break;
+                            case 0x19: s->type = SERVICE_DIGITAL_TELEVISION_AC_HD; break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* Parse NIT */
+    if( p_pat && p_nit )
+    {
+        dvbpsi_nit_ts_t *p_ts;
+        for( p_ts = p_nit->p_first_ts; p_ts != NULL; p_ts = p_ts->p_next )
+        {
+            uint32_t i_private_data_id = 0;
+            dvbpsi_descriptor_t *p_dsc;
+
+            if( p_ts->i_orig_network_id != p_nit->i_network_id || p_ts->i_ts_id != p_pat->i_ts_id )
+                continue;
+
+            for( p_dsc = p_ts->p_first_descriptor; p_dsc != NULL; p_dsc = p_dsc->p_next )
+            {
+                if( p_dsc->i_tag == 0x5f )
+                {
+                    i_private_data_id = GetDWBE( &p_dsc->p_data[0] );
+                }
+                else if( i_private_data_id == 0x28 && p_dsc->i_tag == 0x83 )
+                {
+                    for( int i = 0; i < p_dsc->i_length/4; i++ )
+                    {
+                        uint16_t i_service_id = GetWBE( &p_dsc->p_data[4*i+0] );
+                        int i_channel_number = GetWBE( &p_dsc->p_data[4*i+2] ) & 0x3ff;
+
+                        scan_service_t *s = ScanFindService( p_scan, i_service_start, i_service_id );
+                        if( s && s->i_channel < 0 )
+                            s->i_channel = i_channel_number;
+                    }
+                }
+            }
+        }
+    }
+
+    /* */
+    for( int i = i_service_start; i < p_scan->i_service; i++ )
+    {
+        scan_service_t *p_srv = p_scan->pp_service[i];
+
+        p_srv->i_snr = p_session->i_snr;
+        if( p_nit )
+        {
+            p_srv->i_network_id = p_nit->i_network_id;
+            p_srv->i_nit_version = p_nit->i_version;
+        }
+        if( p_sdt )
+            p_srv->i_sdt_version = p_sdt->i_version;
+    }
+
+
+    /* */
+    if( p_session->pat )
+        dvbpsi_DetachPAT( p_session->pat );
+    if( p_session->p_pat )
+        dvbpsi_DeletePAT( p_session->p_pat );
+
+    if( p_session->sdt )
+        dvbpsi_DetachDemux( p_session->sdt );
+    if( p_session->p_sdt )
+        dvbpsi_DeleteSDT( p_session->p_sdt );
+    if( p_session->nit )
+        dvbpsi_DetachDemux( p_session->nit );
+    if( p_session->p_nit )
+        dvbpsi_DeleteNIT( p_session->p_nit );
+}
+
+static int ScanServiceCmp( const void *a, const void *b )
+{
+    scan_service_t *sa = *(scan_service_t**)a;
+    scan_service_t *sb = *(scan_service_t**)b;
+
+    if( sa->i_channel == sb->i_channel )
+    {
+        if( sa->psz_name && sb->psz_name )
+            return strcmp( sa->psz_name, sb->psz_name );
+        return 0;
+    }
+    if( sa->i_channel == -1 )
+        return 1;
+    else if( sb->i_channel == -1 )
+        return -1;
+
+    if( sa->i_channel < sb->i_channel )
+        return -1;
+    else if( sa->i_channel > sb->i_channel )
+        return 1;
+    return 0;
+}
+
+static block_t *BlockString( const char *psz )
+{
+    block_t *p = block_Alloc( strlen(psz) );
+    if( p )
+        memcpy( p->p_buffer, psz, p->i_buffer );
+    return p;
+}
+
+block_t *scan_GetM3U( scan_t *p_scan )
+{
+    vlc_object_t *p_obj = p_scan->p_obj;
+    block_t *p_playlist = NULL;
+
+    if( p_scan->i_service <= 0 )
+        return NULL;
+
+    /* */
+    qsort( p_scan->pp_service, p_scan->i_service, sizeof(scan_service_t*), ScanServiceCmp );
+
+    /* */
+    p_playlist = BlockString( "#EXTM3U\n\n" );/* */
+
+    for( int i = 0; i < p_scan->i_service; i++ )
+    {
+        scan_service_t *s = p_scan->pp_service[i];
+
+        if( s->type == SERVICE_UNKNOWN )
+        {
+            /* We should only select service that have been described by SDT */
+            msg_Dbg( p_obj, "scan_GetM3U: ignoring service number %d", s->i_program );
+            continue;
+        }
+
+        const char *psz_type;
+        switch( s->type )
+        {
+        case SERVICE_DIGITAL_TELEVISION:       psz_type = "Digital television"; break;
+        case SERVICE_DIGITAL_TELEVISION_AC_SD: psz_type = "Digital television advanced codec SD"; break;
+        case SERVICE_DIGITAL_TELEVISION_AC_HD: psz_type = "Digital television advanced codec HD"; break;
+        case SERVICE_DIGITAL_RADIO:            psz_type = "Digital radio"; break;
+        default:
+            psz_type = "Unknown";
+            break;
+        }
+        msg_Warn( p_obj, "scan_GetM3U: service number %d type '%s' name '%s' channel %d cypted=%d| network_id %d (nit:%d sdt:%d)| f=%d bw=%d snr=%d",
+                  s->i_program, psz_type, s->psz_name, s->i_channel, s->b_crypted,
+                  s->i_network_id, s->i_nit_version, s->i_sdt_version,
+                  s->cfg.i_frequency, s->cfg.i_bandwidth, s->i_snr );
+
+        char *psz;
+        if( asprintf( &psz, "#EXTINF:,,%s\n"
+                        "#EXTVLCOPT:program=%d\n"
+                        "dvb://frequency=%d:bandwidth=%d\n"
+                        "\n",
+                      s->psz_name && * s->psz_name ? s->psz_name : "Unknown",
+                      s->i_program,
+                      s->cfg.i_frequency, s->cfg.i_bandwidth ) < 0 )
+            psz = NULL;
+        if( psz )
+        {
+            block_t *p_block = BlockString( psz );
+            if( p_block )
+                block_ChainAppend( &p_playlist, p_block );
+        }
+    }
+
+    return p_playlist ? block_ChainGather( p_playlist ) : NULL;
+}
+
+bool scan_session_Push( scan_session_t *p_scan, block_t *p_block )
+{
+    if( p_block->i_buffer < 188 || p_block->p_buffer[0] != 0x47 )
+    {
+        block_Release( p_block );
+        return false;
+    }
+
+    /* */
+    const int i_pid = ( (p_block->p_buffer[1]&0x1f)<<8) | p_block->p_buffer[2];
+    if( i_pid == 0x00 )
+    {
+        if( !p_scan->pat )
+            p_scan->pat = dvbpsi_AttachPAT( (dvbpsi_pat_callback)PATCallBack, p_scan );
+
+        if( p_scan->pat )
+            dvbpsi_PushPacket( p_scan->pat, p_block->p_buffer );
+    }
+    else if( i_pid == 0x11 )
+    {
+        if( !p_scan->sdt )
+            p_scan->sdt = dvbpsi_AttachDemux( (dvbpsi_demux_new_cb_t)PSINewTableCallBack, p_scan );
+
+        if( p_scan->sdt )
+            dvbpsi_PushPacket( p_scan->sdt, p_block->p_buffer );
+    }
+    else if( i_pid == p_scan->i_nit_pid )
+    {
+        if( !p_scan->nit )
+            p_scan->nit = dvbpsi_AttachDemux( (dvbpsi_demux_new_cb_t)PSINewTableCallBack, p_scan );
+
+        if( p_scan->nit )
+            dvbpsi_PushPacket( p_scan->nit, p_block->p_buffer );
+    }
+
+    block_Release( p_block );
+
+    return p_scan->p_pat && p_scan->p_sdt && p_scan->p_nit;
+}
+
+void scan_service_SetSNR( scan_session_t *p_session, int i_snr )
+{
+    p_session->i_snr = i_snr;
+}
+
diff --git a/modules/access/dvb/scan.h b/modules/access/dvb/scan.h
new file mode 100644 (file)
index 0000000..c3d0ea0
--- /dev/null
@@ -0,0 +1,137 @@
+/*****************************************************************************
+ * scan.h : functions to ease DVB scanning
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ *
+ * Authors: Laurent Aimar <fenrir@videolan.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA    02111, USA.
+ *****************************************************************************/
+
+typedef enum
+{
+    SCAN_NONE,
+    SCAN_DVB_T,
+    SCAN_DVB_S,
+    SCAN_DVB_C,
+} scan_type_t;
+
+typedef struct
+{
+    scan_type_t type;
+    bool b_exhaustive;
+    struct
+    {
+        int i_min;
+        int i_max;
+        int i_step;
+
+        int i_count;    /* Number of frequency test to do */
+    } frequency;
+
+    struct
+    {
+        /* Bandwidth should be 6, 7 or 8 */
+        int i_min;
+        int i_max;
+        int i_step;
+
+        int i_count;
+    } bandwidth;
+
+} scan_parameter_t;
+
+typedef struct
+{
+    int i_frequency;
+    int i_bandwidth;
+} scan_configuration_t;
+
+typedef enum
+{
+    SERVICE_UNKNOWN = 0,
+    SERVICE_DIGITAL_RADIO,
+    SERVICE_DIGITAL_TELEVISION,
+    SERVICE_DIGITAL_TELEVISION_AC_SD,
+    SERVICE_DIGITAL_TELEVISION_AC_HD,
+} scan_service_type_t;
+
+typedef struct
+{
+    int  i_program;     /* program number (service id) */
+    scan_configuration_t cfg;
+    int i_snr;
+
+    scan_service_type_t type;
+    char *psz_name;     /* channel name in utf8 or NULL */
+    int  i_channel;     /* -1 if unknown */
+    bool b_crypted;     /* True if potentially crypted */
+
+
+    int i_network_id;
+
+    int i_nit_version;
+    int i_sdt_version;
+
+} scan_service_t;
+
+typedef struct
+{
+    vlc_object_t *p_obj;
+
+    scan_configuration_t cfg;
+    int i_snr;
+
+    dvbpsi_handle pat;
+    dvbpsi_pat_t *p_pat;
+    int i_nit_pid;
+
+    dvbpsi_handle sdt;
+    dvbpsi_sdt_t *p_sdt;
+
+    dvbpsi_handle nit;
+    dvbpsi_nit_t *p_nit;
+
+} scan_session_t;
+
+typedef struct
+{
+    vlc_object_t *p_obj;
+    int64_t i_index;
+    int i_dialog_id;
+    scan_parameter_t parameter;
+    int64_t i_time_start;
+
+    int            i_service;
+    scan_service_t **pp_service;
+} scan_t;
+
+
+scan_service_t *scan_service_New( int i_program, const scan_configuration_t *p_cfg  );
+void scan_service_Delete( scan_service_t *p_srv );
+
+int  scan_Init( vlc_object_t *p_obj, scan_t *p_scan, const scan_parameter_t *p_parameter );
+void scan_Clean( scan_t *p_scan );
+
+int scan_Next( scan_t *p_scan, scan_configuration_t *p_cfg );
+
+block_t *scan_GetM3U( scan_t *p_scan );
+bool scan_IsCancelled( scan_t *p_scan );
+
+int  scan_session_Init( vlc_object_t *p_obj, scan_session_t *p_session, const scan_configuration_t *p_cfg );
+void scan_session_Clean( scan_t *p_scan, scan_session_t *p_session );
+bool scan_session_Push( scan_session_t *p_scan, block_t *p_block );
+void scan_service_SetSNR( scan_session_t *p_scan, int i_snr );
+