Commit 7e34273a authored by Laurent Aimar's avatar Laurent Aimar

Added DVB scan capability.

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.
parent 66715267
SOURCES_dvb = \ SOURCES_dvb = \
access.c \ access.c \
scan.c \
linux_dvb.c \ linux_dvb.c \
en50221.c \ en50221.c \
http.c \ http.c \
......
...@@ -54,6 +54,9 @@ ...@@ -54,6 +54,9 @@
# include <dvbpsi/pmt.h> # include <dvbpsi/pmt.h>
# include <dvbpsi/dr.h> # include <dvbpsi/dr.h>
# include <dvbpsi/psi.h> # include <dvbpsi/psi.h>
# include <dvbpsi/demux.h>
# include <dvbpsi/sdt.h>
# include <dvbpsi/nit.h>
#else #else
# include "dvbpsi.h" # include "dvbpsi.h"
# include "descriptor.h" # include "descriptor.h"
...@@ -61,6 +64,9 @@ ...@@ -61,6 +64,9 @@
# include "tables/pmt.h" # include "tables/pmt.h"
# include "descriptors/dr.h" # include "descriptors/dr.h"
# include "psi.h" # include "psi.h"
# include "demux.h"
# include "sdt.h"
# include "nit.h"
#endif #endif
#ifdef ENABLE_HTTPD #ifdef ENABLE_HTTPD
...@@ -196,7 +202,7 @@ vlc_module_begin(); ...@@ -196,7 +202,7 @@ vlc_module_begin();
false ); false );
add_integer( "dvb-device", 0, NULL, DEVICE_TEXT, DEVICE_LONGTEXT, add_integer( "dvb-device", 0, NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
true ); true );
add_integer( "dvb-frequency", 11954000, NULL, FREQ_TEXT, FREQ_LONGTEXT, add_integer( "dvb-frequency", 0, NULL, FREQ_TEXT, FREQ_LONGTEXT,
false ); false );
add_integer( "dvb-inversion", 2, NULL, INVERSION_TEXT, INVERSION_LONGTEXT, add_integer( "dvb-inversion", 2, NULL, INVERSION_TEXT, INVERSION_LONGTEXT,
true ); true );
...@@ -274,6 +280,7 @@ vlc_module_begin(); ...@@ -274,6 +280,7 @@ vlc_module_begin();
add_shortcut( "usdigital" ); add_shortcut( "usdigital" );
set_callbacks( Open, Close ); set_callbacks( Open, Close );
vlc_module_end(); vlc_module_end();
...@@ -283,10 +290,17 @@ vlc_module_end(); ...@@ -283,10 +290,17 @@ vlc_module_end();
static block_t *Block( access_t * ); static block_t *Block( access_t * );
static int Control( access_t *, int, va_list ); static int Control( access_t *, int, va_list );
static block_t *BlockScan( access_t * );
#define DVB_READ_ONCE 20 #define DVB_READ_ONCE 20
#define DVB_READ_ONCE_START 2 #define DVB_READ_ONCE_START 2
#define DVB_READ_ONCE_SCAN 1
#define TS_PACKET_SIZE 188 #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 FilterUnset( access_t *, int i_max );
static void FilterUnsetPID( access_t *, int i_pid ); static void FilterUnsetPID( access_t *, int i_pid );
static void FilterSet( access_t *, int i_pid, int i_type ); 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 ); ...@@ -294,7 +308,6 @@ static void FilterSet( access_t *, int i_pid, int i_type );
static void VarInit( access_t * ); static void VarInit( access_t * );
static int ParseMRL( access_t * ); static int ParseMRL( access_t * );
/***************************************************************************** /*****************************************************************************
* Open: open the frontend device * Open: open the frontend device
*****************************************************************************/ *****************************************************************************/
...@@ -342,6 +355,15 @@ static int Open( vlc_object_t *p_this ) ...@@ -342,6 +355,15 @@ static int Open( vlc_object_t *p_this )
return VLC_EGENERIC; return VLC_EGENERIC;
} }
/* */
p_sys->b_scan_mode = var_GetInteger( p_access, "dvb-frequency" ) == 0;
if( p_sys->b_scan_mode )
{
msg_Dbg( p_access, "DVB scan mode selected" );
p_access->pf_block = BlockScan;
}
else
{
/* Setting frontend parameters for tuning the hardware */ /* Setting frontend parameters for tuning the hardware */
msg_Dbg( p_access, "trying to tune the frontend..."); msg_Dbg( p_access, "trying to tune the frontend...");
if( FrontendSet( p_access ) < 0 ) if( FrontendSet( p_access ) < 0 )
...@@ -350,6 +372,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -350,6 +372,7 @@ static int Open( vlc_object_t *p_this )
free( p_sys ); free( p_sys );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
}
/* Opening DVR device */ /* Opening DVR device */
if( DVROpen( p_access ) < 0 ) if( DVROpen( p_access ) < 0 )
...@@ -359,6 +382,24 @@ static int Open( vlc_object_t *p_this ) ...@@ -359,6 +382,24 @@ static int Open( vlc_object_t *p_this )
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if( p_sys->b_scan_mode )
{
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
{
p_sys->b_budget_mode = var_GetBool( p_access, "dvb-budget-mode" ); p_sys->b_budget_mode = var_GetBool( p_access, "dvb-budget-mode" );
if( p_sys->b_budget_mode ) if( p_sys->b_budget_mode )
{ {
...@@ -373,14 +414,17 @@ static int Open( vlc_object_t *p_this ) ...@@ -373,14 +414,17 @@ static int Open( vlc_object_t *p_this )
CAMOpen( p_access ); CAMOpen( p_access );
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 #ifdef ENABLE_HTTPD
HTTPOpen( p_access ); HTTPOpen( p_access );
#endif #endif
}
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;
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -393,13 +437,17 @@ static void Close( vlc_object_t *p_this ) ...@@ -393,13 +437,17 @@ static void Close( vlc_object_t *p_this )
access_t *p_access = (access_t*)p_this; access_t *p_access = (access_t*)p_this;
access_sys_t *p_sys = p_access->p_sys; 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 ); DVRClose( p_access );
FrontendClose( p_access ); FrontendClose( p_access );
if( p_sys->b_scan_mode )
scan_Clean( &p_sys->scan );
else
CAMClose( p_access ); CAMClose( p_access );
#ifdef ENABLE_HTTPD #ifdef ENABLE_HTTPD
if( !p_sys->b_scan_mode )
HTTPClose( p_access ); HTTPClose( p_access );
#endif #endif
...@@ -512,6 +560,154 @@ static block_t *Block( access_t *p_access ) ...@@ -512,6 +560,154 @@ static block_t *Block( access_t *p_access )
return p_block; 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: * Control:
*****************************************************************************/ *****************************************************************************/
...@@ -521,6 +717,7 @@ static int Control( access_t *p_access, int i_query, va_list args ) ...@@ -521,6 +717,7 @@ static int Control( access_t *p_access, int i_query, va_list args )
bool *pb_bool, b_bool; bool *pb_bool, b_bool;
int *pi_int, i_int; int *pi_int, i_int;
int64_t *pi_64; int64_t *pi_64;
dvbpsi_pmt_t *p_pmt;
switch( i_query ) switch( i_query )
{ {
...@@ -535,6 +732,9 @@ static int Control( access_t *p_access, int i_query, va_list args ) ...@@ -535,6 +732,9 @@ static int Control( access_t *p_access, int i_query, va_list args )
/* */ /* */
case ACCESS_GET_MTU: case ACCESS_GET_MTU:
pi_int = (int*)va_arg( args, int * ); pi_int = (int*)va_arg( args, int * );
if( p_sys->b_scan_mode )
*pi_int = 0;
else
*pi_int = DVB_READ_ONCE * TS_PACKET_SIZE; *pi_int = DVB_READ_ONCE * TS_PACKET_SIZE;
break; break;
...@@ -552,6 +752,9 @@ static int Control( access_t *p_access, int i_query, va_list args ) ...@@ -552,6 +752,9 @@ static int Control( access_t *p_access, int i_query, va_list args )
return VLC_EGENERIC; return VLC_EGENERIC;
case ACCESS_SET_PRIVATE_ID_STATE: 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)*/ i_int = (int)va_arg( args, int ); /* Private data (pid for now)*/
b_bool = (bool)va_arg( args, int ); /* b_selected */ b_bool = (bool)va_arg( args, int ); /* b_selected */
if( !p_sys->b_budget_mode ) if( !p_sys->b_budget_mode )
...@@ -565,14 +768,13 @@ static int Control( access_t *p_access, int i_query, va_list args ) ...@@ -565,14 +768,13 @@ static int Control( access_t *p_access, int i_query, va_list args )
break; break;
case ACCESS_SET_PRIVATE_ID_CA: case ACCESS_SET_PRIVATE_ID_CA:
{ if( p_sys->b_scan_mode )
dvbpsi_pmt_t *p_pmt; return VLC_EGENERIC;
p_pmt = (dvbpsi_pmt_t *)va_arg( args, dvbpsi_pmt_t * ); p_pmt = (dvbpsi_pmt_t *)va_arg( args, dvbpsi_pmt_t * );
CAMSet( p_access, p_pmt ); CAMSet( p_access, p_pmt );
break; break;
}
default: default:
msg_Warn( p_access, "unimplemented query in control" ); msg_Warn( p_access, "unimplemented query in control" );
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
*****************************************************************************/ *****************************************************************************/
#include "scan.h"
/***************************************************************************** /*****************************************************************************
* Devices location * Devices location
*****************************************************************************/ *****************************************************************************/
...@@ -43,6 +45,19 @@ typedef struct demux_handle_t ...@@ -43,6 +45,19 @@ typedef struct demux_handle_t
} demux_handle_t; } demux_handle_t;
typedef struct frontend_t frontend_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 typedef struct en50221_session_t
{ {
...@@ -138,6 +153,7 @@ struct access_sys_t ...@@ -138,6 +153,7 @@ struct access_sys_t
demux_handle_t p_demux_handles[MAX_DEMUX]; demux_handle_t p_demux_handles[MAX_DEMUX];
frontend_t *p_frontend; frontend_t *p_frontend;
bool b_budget_mode; bool b_budget_mode;
bool b_scan_mode;
/* CA management */ /* CA management */
int i_ca_handle; int i_ca_handle;
...@@ -168,6 +184,9 @@ struct access_sys_t ...@@ -168,6 +184,9 @@ struct access_sys_t
char *psz_frontend_info, *psz_mmi_info; char *psz_frontend_info, *psz_mmi_info;
char *psz_request; char *psz_request;
#endif #endif
/* Scan */
scan_t scan;
}; };
#define VIDEO0_TYPE 1 #define VIDEO0_TYPE 1
...@@ -181,6 +200,7 @@ struct access_sys_t ...@@ -181,6 +200,7 @@ struct access_sys_t
/***************************************************************************** /*****************************************************************************
* Prototypes * Prototypes
*****************************************************************************/ *****************************************************************************/
int FrontendOpen( access_t * ); int FrontendOpen( access_t * );
void FrontendPoll( access_t *p_access ); void FrontendPoll( access_t *p_access );
int FrontendSet( access_t * ); int FrontendSet( access_t * );
...@@ -189,6 +209,10 @@ void FrontendClose( access_t * ); ...@@ -189,6 +209,10 @@ void FrontendClose( access_t * );
void FrontendStatus( access_t * ); void FrontendStatus( access_t * );
#endif #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 DMXSetFilter( access_t *, int i_pid, int * pi_fd, int i_type );
int DMXUnsetFilter( access_t *, int i_fd ); int DMXUnsetFilter( access_t *, int i_fd );
...@@ -214,6 +238,8 @@ void en50221_SendMMIObject( access_t * p_access, int i_slot, ...@@ -214,6 +238,8 @@ void en50221_SendMMIObject( access_t * p_access, int i_slot,
en50221_mmi_object_t *p_object ); en50221_mmi_object_t *p_object );
void en50221_End( access_t * ); void en50221_End( access_t * );
char *dvbsi_to_utf8( char *psz_instring, size_t i_length );
#ifdef ENABLE_HTTPD #ifdef ENABLE_HTTPD
int HTTPOpen( access_t *p_access ); int HTTPOpen( access_t *p_access );
void HTTPClose( access_t *p_access ); void HTTPClose( access_t *p_access );
...@@ -225,3 +251,4 @@ char *HTTPExtractValue( char *psz_uri, const char *psz_name, ...@@ -225,3 +251,4 @@ char *HTTPExtractValue( char *psz_uri, const char *psz_name,
*****************************************************************************/ *****************************************************************************/
#define STRINGIFY( z ) UGLY_KLUDGE( z ) #define STRINGIFY( z ) UGLY_KLUDGE( z )
#define UGLY_KLUDGE( z ) #z #define UGLY_KLUDGE( z ) #z
...@@ -55,6 +55,9 @@ ...@@ -55,6 +55,9 @@
# include <dvbpsi/pmt.h> # include <dvbpsi/pmt.h>
# include <dvbpsi/dr.h> # include <dvbpsi/dr.h>
# include <dvbpsi/psi.h> # include <dvbpsi/psi.h>
# include <dvbpsi/demux.h>
# include <dvbpsi/sdt.h>
# include <dvbpsi/nit.h>
#else #else
# include "dvbpsi.h" # include "dvbpsi.h"
# include "descriptor.h" # include "descriptor.h"
...@@ -62,6 +65,9 @@ ...@@ -62,6 +65,9 @@
# include "tables/pmt.h" # include "tables/pmt.h"
# include "descriptors/dr.h" # include "descriptors/dr.h"
# include "psi.h" # include "psi.h"
# include "demux.h"
# include "sdt.h"
# include "nit.h"
#endif #endif
#ifdef ENABLE_HTTPD #ifdef ENABLE_HTTPD
...@@ -81,7 +87,6 @@ static void ApplicationInformationOpen( access_t * p_access, int i_session_id ); ...@@ -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 ConditionalAccessOpen( access_t * p_access, int i_session_id );
static void DateTimeOpen( 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 void MMIOpen( access_t * p_access, int i_session_id );
static char *dvbsi_to_utf8( char *psz_instring, size_t i_length );
/***************************************************************************** /*****************************************************************************
* Utility functions * Utility functions
...@@ -174,6 +179,8 @@ static void Dump( bool b_outgoing, uint8_t *p_data, int i_size ) ...@@ -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++) for ( i = 0; i < i_size && i < MAX_DUMP; i++)
fprintf(stderr, "%02X ", p_data[i]); fprintf(stderr, "%02X ", p_data[i]);
fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : ""); fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
#else
VLC_UNUSED(b_outgoing); VLC_UNUSED(p_data); VLC_UNUSED(i_size);
#endif #endif
} }
...@@ -2318,7 +2325,7 @@ static inline void *FixUTF8( char *p ) ...@@ -2318,7 +2325,7 @@ static inline void *FixUTF8( char *p )
return 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; const char *psz_encoding, *psz_stringstart;
char *psz_outstring, *psz_tmp; char *psz_outstring, *psz_tmp;
......
...@@ -48,6 +48,9 @@ ...@@ -48,6 +48,9 @@
# include <dvbpsi/pmt.h> # include <dvbpsi/pmt.h>
# include <dvbpsi/dr.h> # include <dvbpsi/dr.h>
# include <dvbpsi/psi.h> # include <dvbpsi/psi.h>
# include <dvbpsi/demux.h>
# include <dvbpsi/sdt.h>
# include <dvbpsi/nit.h>
#else #else
# include "dvbpsi.h" # include "dvbpsi.h"
# include "descriptor.h" # include "descriptor.h"
...@@ -55,6 +58,9 @@ ...@@ -55,6 +58,9 @@
# include "tables/pmt.h" # include "tables/pmt.h"
# include "descriptors/dr.h" # include "descriptors/dr.h"
# include "psi.h" # include "psi.h"
# include "demux.h"
# include "sdt.h"
# include "nit.h"
#endif #endif
#ifdef ENABLE_HTTPD #ifdef ENABLE_HTTPD
......
...@@ -55,6 +55,9 @@ ...@@ -55,6 +55,9 @@
# include <dvbpsi/pmt.h> # include <dvbpsi/pmt.h>
# include <dvbpsi/dr.h> # include <dvbpsi/dr.h>
# include <dvbpsi/psi.h> # include <dvbpsi/psi.h>
# include <dvbpsi/demux.h>
# include <dvbpsi/sdt.h>
# include <dvbpsi/nit.h>
#else #else
# include "dvbpsi.h" # include "dvbpsi.h"
# include "descriptor.h" # include "descriptor.h"
...@@ -62,6 +65,9 @@ ...@@ -62,6 +65,9 @@
# include "tables/pmt.h" # include "tables/pmt.h"
# include "descriptors/dr.h" # include "descriptors/dr.h"
# include "psi.h" # include "psi.h"
# include "demux.h"
# include "sdt.h"
# include "nit.h"
#endif #endif
#ifdef ENABLE_HTTPD #ifdef ENABLE_HTTPD
...@@ -339,17 +345,21 @@ void FrontendPoll( access_t *p_access ) ...@@ -339,17 +345,21 @@ void FrontendPoll( access_t *p_access )
IF_UP( FE_HAS_LOCK ) IF_UP( FE_HAS_LOCK )
{ {
int32_t i_value = 0; frontend_statistic_t stat;
msg_Dbg( p_access, "frontend has acquired lock" ); msg_Dbg( p_access, "frontend has acquired lock" );
p_sys->i_frontend_timeout = 0; p_sys->i_frontend_timeout = 0;
/* Read some statistics */ /* Read some statistics */
if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &i_value ) >= 0 ) if( !FrontendGetStatistic( p_access, &stat ) )
msg_Dbg( p_access, "- Bit error rate: %d", i_value ); {
if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH, &i_value ) >= 0 ) if( stat.i_ber >= 0 )
msg_Dbg( p_access, "- Signal strength: %d", i_value ); msg_Dbg( p_access, "- Bit error rate: %d", stat.i_ber );
if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &i_value ) >= 0 ) if( stat.i_signal_strenth >= 0 )
msg_Dbg( p_access, "- SNR: %d", i_value ); 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 else
{ {
...@@ -367,6 +377,66 @@ void FrontendPoll( access_t *p_access ) ...@@ -367,6 +377,66 @@ void FrontendPoll( access_t *p_access )
#undef IF_UP #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 #ifdef ENABLE_HTTPD
/***************************************************************************** /*****************************************************************************
......
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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 );
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment