You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1924 lines
66 KiB
1924 lines
66 KiB
/*****************************************************************************
|
|
* scan.c: DVB scanner helpers
|
|
*****************************************************************************
|
|
* Copyright (C) 2008,2010 VLC authors and VideoLAN
|
|
*
|
|
* Authors: Laurent Aimar <fenrir@videolan.org>
|
|
* David Kaplan <david@2of1.org>
|
|
* Ilkka Ollakka <ileoo@videolan.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser 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_block.h>
|
|
#include <vlc_dialog.h>
|
|
#include <vlc_charset.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
/* Include dvbpsi headers */
|
|
#include <dvbpsi/dvbpsi.h>
|
|
#include <dvbpsi/descriptor.h>
|
|
#include <dvbpsi/pat.h>
|
|
#include <dvbpsi/dr.h>
|
|
#include <dvbpsi/demux.h>
|
|
#include <dvbpsi/sdt.h>
|
|
#include <dvbpsi/nit.h>
|
|
|
|
#include "dvb.h"
|
|
#include "scan.h"
|
|
#include "scan_list.h"
|
|
#include "../../demux/dvb-text.h"
|
|
#include "../../mux/mpeg/dvbpsi_compat.h"
|
|
|
|
#define PSI_PAT_PID 0x00
|
|
#define SI_NIT_PID 0x10
|
|
#define SI_SDT_PID 0x11
|
|
|
|
#define NIT_CURRENT_NETWORK_TABLE_ID 0x40
|
|
#define NIT_OTHER_NETWORK_TABLE_ID 0x41
|
|
#define SDT_CURRENT_TS_TABLE_ID 0x42
|
|
#define SDT_OTHER_TS_TABLE_ID 0x46
|
|
|
|
#define NETWORK_ID_RESERVED 0x0000
|
|
|
|
typedef enum
|
|
{
|
|
SERVICE_TYPE_RESERVED = 0x00,
|
|
SERVICE_TYPE_DIGITAL_TELEVISION = 0x01,
|
|
SERVICE_TYPE_DIGITAL_RADIO = 0x02,
|
|
SERVICE_TYPE_DIGITAL_MPEG2_HD = 0x11,
|
|
SERVICE_TYPE_DIGITAL_TELEVISION_AC_SD = 0x16,
|
|
SERVICE_TYPE_DIGITAL_TELEVISION_AC_HD = 0x19,
|
|
SERVICE_TYPE_DIGITAL_RADIO_AC = 0x0A,
|
|
} scan_service_type_t;
|
|
|
|
typedef struct scan_multiplex_t scan_multiplex_t;
|
|
|
|
struct scan_service_t
|
|
{
|
|
const scan_multiplex_t *p_mplex; /* multiplex reference */
|
|
const void * stickyref; /* Callee private storage across updates */
|
|
|
|
uint16_t i_original_network_id;
|
|
uint16_t i_program; /* program number (service id) */
|
|
|
|
scan_service_type_t type;
|
|
|
|
char *psz_name; /* channel name in utf8 */
|
|
char *psz_provider; /* service provider */
|
|
uint16_t i_channel; /* logical channel number */
|
|
bool b_crypted; /* True if potentially encrypted */
|
|
|
|
char *psz_original_network_name;
|
|
|
|
};
|
|
|
|
struct scan_multiplex_t
|
|
{
|
|
scan_tuner_config_t cfg;
|
|
uint16_t i_network_id;
|
|
uint16_t i_ts_id;
|
|
char *psz_network_name;
|
|
size_t i_services;
|
|
scan_service_t **pp_services;
|
|
int i_snr;
|
|
bool b_scanned;
|
|
|
|
uint8_t i_nit_version;
|
|
uint8_t i_sdt_version;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
scan_modulation_t modulation;
|
|
unsigned i_symbolrate_index;
|
|
unsigned i_index;
|
|
} scan_enumeration_t;
|
|
|
|
struct scan_t
|
|
{
|
|
vlc_object_t *p_obj;
|
|
scan_frontend_tune_cb pf_tune;
|
|
scan_demux_filter_cb pf_filter;
|
|
scan_frontend_stats_cb pf_stats;
|
|
scan_demux_read_cb pf_read;
|
|
scan_service_notify_cb pf_notify_service;
|
|
void *p_cbdata;
|
|
|
|
vlc_dialog_id *p_dialog_id;
|
|
|
|
scan_parameter_t parameter;
|
|
int64_t i_time_start;
|
|
|
|
size_t i_multiplex_toscan;
|
|
|
|
size_t i_multiplex;
|
|
scan_multiplex_t **pp_multiplex;
|
|
bool b_multiplexes_from_nit;
|
|
|
|
scan_list_entry_t *p_scanlist;
|
|
size_t i_scanlist;
|
|
const scan_list_entry_t *p_current;
|
|
|
|
scan_enumeration_t spectrum;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
vlc_object_t *p_obj;
|
|
|
|
scan_tuner_config_t cfg;
|
|
int i_snr;
|
|
|
|
struct
|
|
{
|
|
dvbpsi_pat_t *p_pat;
|
|
dvbpsi_sdt_t *p_sdt;
|
|
dvbpsi_nit_t *p_nit;
|
|
} local;
|
|
|
|
struct
|
|
{
|
|
dvbpsi_sdt_t **pp_sdt;
|
|
size_t i_sdt;
|
|
dvbpsi_nit_t **pp_nit;
|
|
size_t i_nit;
|
|
} others;
|
|
|
|
scan_type_t type;
|
|
bool b_use_nit;
|
|
uint16_t i_nit_pid;
|
|
|
|
dvbpsi_t *p_pathandle;
|
|
dvbpsi_t *p_sdthandle;
|
|
dvbpsi_t *p_nithandle;
|
|
} scan_session_t;
|
|
|
|
static scan_session_t * scan_session_New( scan_t *p_scan, const scan_tuner_config_t *p_cfg );
|
|
static void scan_session_Destroy( scan_t *p_scan, scan_session_t *p_session );
|
|
static bool scan_session_Push( scan_session_t *p_scan, const uint8_t *p_packet );
|
|
static unsigned scan_session_GetTablesTimeout( const scan_session_t *p_session );
|
|
|
|
/* */
|
|
static void scan_tuner_config_Init( scan_tuner_config_t *p_cfg, const scan_parameter_t *p_params )
|
|
{
|
|
memset( p_cfg, 0, sizeof(*p_cfg) );
|
|
p_cfg->coderate_lp = SCAN_CODERATE_AUTO;
|
|
p_cfg->coderate_hp = SCAN_CODERATE_AUTO;
|
|
p_cfg->inner_fec = SCAN_CODERATE_AUTO;
|
|
switch(p_params->type)
|
|
{
|
|
case SCAN_DVB_T: p_cfg->delivery = SCAN_DELIVERY_DVB_T; break;
|
|
case SCAN_DVB_S: p_cfg->delivery = SCAN_DELIVERY_DVB_S; break;
|
|
case SCAN_DVB_C: p_cfg->delivery = SCAN_DELIVERY_DVB_C; break;
|
|
default: p_cfg->delivery = SCAN_DELIVERY_UNKNOWN; break;
|
|
}
|
|
p_cfg->type = p_params->type;
|
|
}
|
|
|
|
static bool scan_tuner_config_StandardValidate( const scan_tuner_config_t *p_cfg )
|
|
{
|
|
if( p_cfg->i_frequency == 0 ||
|
|
p_cfg->i_frequency == UINT32_MAX / 10 ) /* Invalid / broken transponder info on French TNT */
|
|
return false;
|
|
|
|
if( p_cfg->type == SCAN_DVB_T && p_cfg->i_bandwidth == 0 )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static scan_service_t *scan_service_New( uint16_t i_program )
|
|
{
|
|
scan_service_t *p_srv = malloc( sizeof(*p_srv) );
|
|
if( !p_srv )
|
|
return NULL;
|
|
|
|
p_srv->p_mplex = NULL;
|
|
p_srv->stickyref = NULL;
|
|
p_srv->i_program = i_program;
|
|
p_srv->i_original_network_id = NETWORK_ID_RESERVED;
|
|
|
|
p_srv->type = SERVICE_TYPE_RESERVED;
|
|
p_srv->psz_name = NULL;
|
|
p_srv->psz_provider = NULL;
|
|
p_srv->psz_original_network_name = NULL;
|
|
p_srv->i_channel = -1;
|
|
p_srv->b_crypted = false;
|
|
|
|
return p_srv;
|
|
}
|
|
|
|
static void scan_service_Delete( scan_service_t *p_srv )
|
|
{
|
|
free( p_srv->psz_original_network_name );
|
|
free( p_srv->psz_name );
|
|
free( p_srv->psz_provider );
|
|
free( p_srv );
|
|
}
|
|
|
|
static uint32_t decode_BCD( uint32_t input )
|
|
{
|
|
uint32_t output = 0;
|
|
for( short index=28; index >= 0 ; index -= 4 )
|
|
{
|
|
output *= 10;
|
|
output += ((input >> index) & 0x0f);
|
|
};
|
|
return output;
|
|
}
|
|
|
|
static int scan_service_type_Supported( scan_service_type_t service_type )
|
|
{
|
|
switch( service_type )
|
|
{
|
|
case SERVICE_TYPE_DIGITAL_TELEVISION:
|
|
case SERVICE_TYPE_DIGITAL_RADIO:
|
|
case SERVICE_TYPE_DIGITAL_MPEG2_HD:
|
|
case SERVICE_TYPE_DIGITAL_TELEVISION_AC_SD:
|
|
case SERVICE_TYPE_DIGITAL_TELEVISION_AC_HD:
|
|
case SERVICE_TYPE_DIGITAL_RADIO_AC:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static scan_multiplex_t *scan_multiplex_New( const scan_tuner_config_t *p_cfg, uint16_t i_ts_id )
|
|
{
|
|
scan_multiplex_t *p_mplex = malloc( sizeof(*p_mplex) );
|
|
if( likely(p_mplex) )
|
|
{
|
|
p_mplex->cfg = *p_cfg;
|
|
p_mplex->i_ts_id = i_ts_id;
|
|
p_mplex->i_network_id = NETWORK_ID_RESERVED;
|
|
p_mplex->psz_network_name = NULL;
|
|
p_mplex->i_services = 0;
|
|
p_mplex->pp_services = NULL;
|
|
p_mplex->i_nit_version = UINT8_MAX;
|
|
p_mplex->i_sdt_version = UINT8_MAX;
|
|
p_mplex->i_snr = -1;
|
|
p_mplex->b_scanned = false;
|
|
}
|
|
return p_mplex;
|
|
}
|
|
|
|
static void scan_multiplex_Clean( scan_multiplex_t *p_mplex )
|
|
{
|
|
for( size_t i=0; i<p_mplex->i_services; i++ )
|
|
scan_service_Delete( p_mplex->pp_services[i] );
|
|
free( p_mplex->pp_services );
|
|
free( p_mplex->psz_network_name );
|
|
}
|
|
|
|
static void scan_multiplex_Delete( scan_multiplex_t *p_mplex )
|
|
{
|
|
scan_multiplex_Clean( p_mplex );
|
|
free( p_mplex );
|
|
}
|
|
|
|
static bool scan_multiplex_AddService( scan_multiplex_t *p_mplex, scan_service_t *p_service )
|
|
{
|
|
if( unlikely(p_service->p_mplex) ) /* Already belongs to another multiplex, should never happen */
|
|
return false;
|
|
|
|
scan_service_t **pp_realloc = realloc( p_mplex->pp_services,
|
|
sizeof(scan_service_t *) * (p_mplex->i_services + 1) );
|
|
if( unlikely(!pp_realloc) )
|
|
return false;
|
|
pp_realloc[p_mplex->i_services] = p_service;
|
|
p_mplex->pp_services = pp_realloc;
|
|
p_mplex->i_services++;
|
|
p_service->p_mplex = p_mplex;
|
|
return true;
|
|
}
|
|
|
|
static scan_service_t * scan_multiplex_FindService( const scan_multiplex_t *p_mplex, uint16_t i_program )
|
|
{
|
|
for( size_t i = 0; i < p_mplex->i_services; i++ )
|
|
{
|
|
if( p_mplex->pp_services[i]->i_program == i_program )
|
|
return p_mplex->pp_services[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void scan_parameter_Init( scan_parameter_t *p_dst )
|
|
{
|
|
memset( p_dst, 0, sizeof(*p_dst) );
|
|
}
|
|
|
|
void scan_parameter_Clean( scan_parameter_t *p_dst )
|
|
{
|
|
free( p_dst->psz_scanlist_file );
|
|
}
|
|
|
|
static void scan_parameter_Copy( const scan_parameter_t *p_src, scan_parameter_t *p_dst )
|
|
{
|
|
scan_parameter_Clean( p_dst );
|
|
*p_dst = *p_src;
|
|
if( p_src->psz_scanlist_file )
|
|
p_dst->psz_scanlist_file = strdup( p_src->psz_scanlist_file );
|
|
}
|
|
|
|
static void scan_Prepare( vlc_object_t *p_obj, const scan_parameter_t *p_parameter, scan_t *p_scan )
|
|
{
|
|
if( p_parameter->type == SCAN_DVB_S &&
|
|
p_parameter->psz_scanlist_file && p_parameter->scanlist_format == FORMAT_DVBv3 )
|
|
{
|
|
p_scan->p_scanlist =
|
|
scan_list_dvbv3_load( p_obj, p_parameter->psz_scanlist_file, &p_scan->i_scanlist );
|
|
if( p_scan->p_scanlist )
|
|
msg_Dbg( p_scan->p_obj, "using satellite config file (%s)", p_parameter->psz_scanlist_file );
|
|
}
|
|
else if( p_parameter->psz_scanlist_file &&
|
|
p_parameter->scanlist_format == FORMAT_DVBv5 )
|
|
{
|
|
if( p_parameter->type == SCAN_DVB_T )
|
|
{
|
|
p_scan->p_scanlist = scan_list_dvbv5_load( p_obj,
|
|
p_parameter->psz_scanlist_file,
|
|
&p_scan->i_scanlist );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void scan_Debug_Parameters( vlc_object_t *p_obj, const scan_parameter_t *p_parameter )
|
|
{
|
|
const char rgc_types[3] = {'T', 'S', 'C' };
|
|
if( !p_parameter->type )
|
|
return;
|
|
|
|
msg_Dbg( p_obj, "DVB-%c scanning:", rgc_types[ p_parameter->type - 1 ] );
|
|
|
|
if( p_parameter->type != SCAN_DVB_S )
|
|
{
|
|
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" );
|
|
}
|
|
|
|
if( p_parameter->type == SCAN_DVB_C )
|
|
msg_Dbg( p_obj, " - scannin modulations %s", p_parameter->b_modulation_set ? "off" : "on" );
|
|
|
|
if( p_parameter->type == SCAN_DVB_S && p_parameter->psz_scanlist_file )
|
|
msg_Dbg( p_obj, " - satellite [%s]", p_parameter->psz_scanlist_file );
|
|
|
|
msg_Dbg( p_obj, " - use NIT %s", p_parameter->b_use_nit ? "on" : "off" );
|
|
msg_Dbg( p_obj, " - FTA only %s", p_parameter->b_free_only ? "on" : "off" );
|
|
}
|
|
|
|
/* */
|
|
scan_t *scan_New( vlc_object_t *p_obj, const scan_parameter_t *p_parameter,
|
|
scan_frontend_tune_cb pf_frontend,
|
|
scan_frontend_stats_cb pf_status,
|
|
scan_demux_filter_cb pf_filter,
|
|
scan_demux_read_cb pf_read,
|
|
void *p_cbdata )
|
|
{
|
|
if( p_parameter->type == SCAN_NONE )
|
|
return NULL;
|
|
|
|
scan_t *p_scan = malloc( sizeof( *p_scan ) );
|
|
if( unlikely(p_scan == NULL) )
|
|
return NULL;
|
|
|
|
p_scan->p_obj = VLC_OBJECT(p_obj);
|
|
p_scan->pf_tune = pf_frontend;
|
|
p_scan->pf_stats = pf_status;
|
|
p_scan->pf_read = pf_read;
|
|
p_scan->pf_filter = pf_filter;
|
|
p_scan->pf_notify_service = NULL;
|
|
p_scan->p_cbdata = p_cbdata;
|
|
p_scan->p_dialog_id = NULL;
|
|
p_scan->i_multiplex = 0;
|
|
p_scan->pp_multiplex = NULL;
|
|
p_scan->i_multiplex_toscan = 0;
|
|
p_scan->b_multiplexes_from_nit = false;
|
|
scan_parameter_Init( &p_scan->parameter );
|
|
scan_parameter_Copy( p_parameter, &p_scan->parameter );
|
|
p_scan->i_time_start = vlc_tick_now();
|
|
p_scan->p_scanlist = NULL;
|
|
p_scan->i_scanlist = 0;
|
|
|
|
scan_Prepare( p_obj, p_parameter, p_scan );
|
|
p_scan->p_current = p_scan->p_scanlist;
|
|
|
|
p_scan->spectrum.i_index = 0;
|
|
p_scan->spectrum.i_symbolrate_index = 0;
|
|
p_scan->spectrum.modulation = 0;
|
|
|
|
scan_Debug_Parameters( p_obj, p_parameter );
|
|
|
|
return p_scan;
|
|
}
|
|
|
|
void scan_Destroy( scan_t *p_scan )
|
|
{
|
|
if( !p_scan )
|
|
return;
|
|
if( p_scan->p_dialog_id != NULL )
|
|
vlc_dialog_release( p_scan->p_obj, p_scan->p_dialog_id );
|
|
|
|
scan_parameter_Clean( &p_scan->parameter );
|
|
|
|
for( size_t i = 0; i < p_scan->i_multiplex; i++ )
|
|
scan_multiplex_Delete( p_scan->pp_multiplex[i] );
|
|
free( p_scan->pp_multiplex );
|
|
|
|
scan_list_entries_release( p_scan->p_scanlist );
|
|
|
|
free( p_scan );
|
|
}
|
|
|
|
static void scan_SetMultiplexScanStatus( scan_t *p_scan, scan_multiplex_t *p_mplex, bool b_scanned )
|
|
{
|
|
if( p_mplex->b_scanned != b_scanned )
|
|
{
|
|
p_mplex->b_scanned = b_scanned;
|
|
p_scan->i_multiplex_toscan += ( b_scanned ) ? -1 : 1;
|
|
}
|
|
}
|
|
|
|
static bool scan_AddMultiplex( scan_t *p_scan, scan_multiplex_t *p_mplex )
|
|
{
|
|
scan_multiplex_t **pp_realloc = realloc( p_scan->pp_multiplex,
|
|
sizeof(scan_multiplex_t *) * (p_scan->i_multiplex + 1) );
|
|
if( unlikely(!pp_realloc) )
|
|
return false;
|
|
pp_realloc[p_scan->i_multiplex] = p_mplex;
|
|
p_scan->pp_multiplex = pp_realloc;
|
|
p_scan->i_multiplex++;
|
|
if( !p_mplex->b_scanned )
|
|
p_scan->i_multiplex_toscan++;
|
|
return true;
|
|
}
|
|
|
|
static scan_multiplex_t * scan_FindMultiplex( const scan_t *p_scan, uint16_t i_ts_id )
|
|
{
|
|
for( size_t i = 0; i < p_scan->i_multiplex; i++ )
|
|
{
|
|
if( p_scan->pp_multiplex[i]->i_ts_id == i_ts_id )
|
|
return p_scan->pp_multiplex[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static scan_multiplex_t *scan_FindOrCreateMultiplex( scan_t *p_scan, uint16_t i_ts_id,
|
|
const scan_tuner_config_t *p_cfg )
|
|
{
|
|
scan_multiplex_t *p_mplex = scan_FindMultiplex( p_scan, i_ts_id );
|
|
if( p_mplex == NULL )
|
|
{
|
|
p_mplex = scan_multiplex_New( p_cfg, i_ts_id );
|
|
if( likely(p_mplex) )
|
|
{
|
|
if ( unlikely(!scan_AddMultiplex( p_scan, p_mplex )) ) /* OOM */
|
|
{
|
|
scan_multiplex_Delete( p_mplex );
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
return p_mplex;
|
|
}
|
|
|
|
static size_t scan_CountServices( const scan_t *p_scan )
|
|
{
|
|
size_t i_total_services = 0;
|
|
for( size_t j = 0; j < p_scan->i_multiplex; j++ )
|
|
i_total_services += p_scan->pp_multiplex[j]->i_services;
|
|
return i_total_services;
|
|
}
|
|
|
|
static int Scan_Next_DVB_SpectrumExhaustive( const scan_parameter_t *p_params, scan_enumeration_t *p_spectrum,
|
|
scan_tuner_config_t *p_cfg, double *pf_pos )
|
|
{
|
|
unsigned i_bandwidth_count = p_params->bandwidth.i_max - p_params->bandwidth.i_min + 1;
|
|
unsigned i_frequency_step = p_params->frequency.i_step ? p_params->frequency.i_step : 166667;
|
|
unsigned i_frequency_count = (p_params->frequency.i_max - p_params->frequency.i_min) / p_params->frequency.i_step;
|
|
|
|
if( p_spectrum->i_index > i_frequency_count * i_bandwidth_count )
|
|
return VLC_EGENERIC;
|
|
|
|
const int i_bi = p_spectrum->i_index % i_bandwidth_count;
|
|
const int i_fi = p_spectrum->i_index / i_bandwidth_count;
|
|
|
|
p_cfg->i_frequency = p_params->frequency.i_min + i_fi * i_frequency_step;
|
|
p_cfg->i_bandwidth = p_params->bandwidth.i_min + i_bi;
|
|
|
|
*pf_pos = (double)p_spectrum->i_index / i_frequency_count;
|
|
|
|
p_spectrum->i_index++;
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static int Scan_Next_DVBC( const scan_parameter_t *p_params, scan_enumeration_t *p_spectrum,
|
|
scan_tuner_config_t *p_cfg, double *pf_pos )
|
|
{
|
|
bool b_rotate=true;
|
|
if( !p_params->b_modulation_set )
|
|
{
|
|
p_spectrum->modulation = (p_spectrum->modulation >> 1 );
|
|
/* if we iterated all modulations, move on */
|
|
/* dvb utils dvb-c channels files seems to have only
|
|
QAM64...QAM256, so lets just iterate over those */
|
|
if( p_spectrum->modulation < SCAN_MODULATION_QAM_64)
|
|
{
|
|
p_spectrum->modulation = SCAN_MODULATION_QAM_256;
|
|
} else {
|
|
b_rotate=false;
|
|
}
|
|
}
|
|
p_cfg->modulation = p_spectrum->modulation;
|
|
|
|
if( p_params->i_symbolrate == 0 )
|
|
{
|
|
/* symbol rates from dvb-tools dvb-c files */
|
|
static const unsigned short symbolrates[] = {
|
|
6900, 6875, 6950
|
|
/* With DR_44 we can cover other symbolrates from NIT-info
|
|
as all channel-seed files have at least one channel that
|
|
has one of these symbolrate
|
|
*/
|
|
};
|
|
|
|
enum { num_symbols = (sizeof(symbolrates)/sizeof(*symbolrates)) };
|
|
|
|
/* if we rotated modulations, rotate symbolrate */
|
|
if( b_rotate )
|
|
{
|
|
p_spectrum->i_symbolrate_index++;
|
|
p_spectrum->i_symbolrate_index %= num_symbols;
|
|
}
|
|
p_cfg->i_symbolrate = 1000 * (symbolrates[ p_spectrum->i_symbolrate_index ] );
|
|
|
|
if( p_spectrum->i_symbolrate_index )
|
|
b_rotate=false;
|
|
}
|
|
else
|
|
{
|
|
p_cfg->i_symbolrate = p_params->i_symbolrate;
|
|
}
|
|
|
|
if( p_params->b_exhaustive )
|
|
return Scan_Next_DVB_SpectrumExhaustive( p_params, p_spectrum, p_cfg, pf_pos );
|
|
|
|
/* Values taken from dvb-scan utils frequency-files, sorted by how
|
|
* often they appear. This hopefully speeds up finding services. */
|
|
static const unsigned int frequencies[] = { 41000, 39400, 40200,
|
|
38600, 41800, 36200, 44200, 43400, 37000, 35400, 42600, 37800,
|
|
34600, 45800, 45000, 46600, 32200, 51400, 49000, 33800, 31400,
|
|
30600, 47400, 71400, 69000, 68200, 58600, 56200, 54600, 49800,
|
|
48200, 33000, 79400, 72200, 69800, 67400, 66600, 65000, 64200,
|
|
61000, 55400, 53000, 52200, 50600, 29800, 16200, 15400, 11300,
|
|
78600, 77000, 76200, 75400, 74600, 73800, 73000, 70600, 57800,
|
|
57000, 53800, 12100, 81000, 77800, 65800, 63400, 61800, 29000,
|
|
17000, 85000, 84200, 83400, 81800, 80200, 59400, 36900, 28300,
|
|
26600, 25800, 25000, 24200, 23400, 85800, 74800, 73200, 72800,
|
|
72400, 72000, 66000, 65600, 60200, 42500, 41700, 40900, 40100,
|
|
39300, 38500, 37775, 37700, 37200, 36100, 35600, 35300, 34700,
|
|
34500, 33900, 33700, 32900, 32300, 32100, 31500, 31300, 30500,
|
|
29900, 29700, 29100, 28950, 28200, 28000, 27500, 27400, 27200,
|
|
26700, 25900, 25500, 25100, 24300, 24100, 23500, 23200, 22700,
|
|
22600, 21900, 21800, 21100, 20300, 19500, 18700, 17900, 17100,
|
|
16300, 15500, 14700, 14600, 14500, 14300, 13900, 13700, 13100,
|
|
12900, 12500, 12300
|
|
};
|
|
enum { num_frequencies = (sizeof(frequencies)/sizeof(*frequencies)) };
|
|
|
|
if( p_spectrum->i_index >= num_frequencies )
|
|
return VLC_EGENERIC; /* End */
|
|
|
|
p_cfg->i_frequency = 10000 * ( frequencies[ p_spectrum->i_index ] );
|
|
*pf_pos = (double)(p_spectrum->i_index * 1000 +
|
|
p_spectrum->i_symbolrate_index * 100 +
|
|
(256 - (p_spectrum->modulation >> 4)) )
|
|
/ (num_frequencies * 1000 + 900 + 16);
|
|
|
|
if( b_rotate )
|
|
p_spectrum->i_index++;
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static int Scan_Next_DVBT( const scan_parameter_t *p_params, scan_enumeration_t *p_spectrum,
|
|
scan_tuner_config_t *p_cfg, double *pf_pos )
|
|
{
|
|
if( p_params->b_exhaustive )
|
|
return Scan_Next_DVB_SpectrumExhaustive( p_params, p_spectrum, p_cfg, pf_pos );
|
|
|
|
unsigned i_frequency_step = p_params->frequency.i_step ? p_params->frequency.i_step : 166667;
|
|
|
|
unsigned i_bandwidth_min = p_params->bandwidth.i_min ? p_params->bandwidth.i_min : 6;
|
|
unsigned i_bandwidth_max = p_params->bandwidth.i_max ? p_params->bandwidth.i_max : 8;
|
|
unsigned i_bandwidth_count = i_bandwidth_max - i_bandwidth_min + 1;
|
|
|
|
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_spectrum->i_index++ )
|
|
{
|
|
|
|
const int i_bi = p_spectrum->i_index % i_bandwidth_count;
|
|
const int i_oi = (p_spectrum->i_index / i_bandwidth_count) % i_offset_count;
|
|
const int i_fi = (p_spectrum->i_index / i_bandwidth_count) / i_offset_count;
|
|
|
|
const int i_bandwidth = i_bandwidth_min + i_bi;
|
|
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 )
|
|
{
|
|
p_spectrum->i_index++;
|
|
return VLC_EGENERIC;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const unsigned i_frequency_min = band[i].i_min*i_mhz + i_bandwidth*i_mhz/2;
|
|
const unsigned 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 unsigned i_frequency = i_frequency_base + ( i_oi - i_offset_count/2 ) * i_frequency_step;
|
|
|
|
p_cfg->i_frequency = i_frequency;
|
|
p_cfg->i_bandwidth = i_bandwidth;
|
|
|
|
int i_current = 0, i_total = 0;
|
|
for( 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;
|
|
p_spectrum->i_index++;
|
|
return VLC_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int Scan_GetNextSpectrumTunerConfig( scan_t *p_scan, scan_tuner_config_t *p_cfg, double *pf_pos )
|
|
{
|
|
int i_ret = VLC_EGENERIC;
|
|
switch( p_scan->parameter.type )
|
|
{
|
|
case SCAN_DVB_T:
|
|
i_ret = Scan_Next_DVBT( &p_scan->parameter, &p_scan->spectrum, p_cfg, pf_pos );
|
|
break;
|
|
case SCAN_DVB_C:
|
|
i_ret = Scan_Next_DVBC( &p_scan->parameter, &p_scan->spectrum, p_cfg, pf_pos );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return i_ret;
|
|
}
|
|
|
|
static int Scan_GetNextTunerConfig( scan_t *p_scan, scan_tuner_config_t *p_cfg, double *pf_pos )
|
|
{
|
|
/* Note: Do not forget to advance current scan (avoid frontend tuning errors loops ) */
|
|
if( p_scan->p_scanlist && p_scan->p_current )
|
|
{
|
|
const scan_list_entry_t *p_entry = p_scan->p_current;
|
|
p_cfg->i_frequency = p_entry->i_freq;
|
|
p_cfg->i_bandwidth = p_entry->i_bw / 1000000;
|
|
p_cfg->modulation = p_entry->modulation;
|
|
|
|
switch( p_entry->delivery )
|
|
{
|
|
case SCAN_DELIVERY_UNKNOWN:
|
|
break;
|
|
case SCAN_DELIVERY_DVB_T:
|
|
p_cfg->coderate_hp = p_entry->coderate_hp;
|
|
p_cfg->coderate_lp = p_entry->coderate_lp;
|
|
p_cfg->type = SCAN_DVB_T;
|
|
break;
|
|
case SCAN_DELIVERY_DVB_S:
|
|
p_cfg->type = SCAN_DVB_S;
|
|
p_cfg->polarization = p_entry->polarization;
|
|
p_cfg->i_symbolrate = p_entry->i_rate / 1000;
|
|
p_cfg->inner_fec = p_entry->inner_fec;
|
|
break;
|
|
case SCAN_DELIVERY_DVB_C:
|
|
p_cfg->type = SCAN_DVB_C;
|
|
p_cfg->i_symbolrate = p_entry->i_rate / 1000;
|
|
p_cfg->inner_fec = p_entry->inner_fec;
|
|
break;
|
|
default:
|
|
p_cfg->type = SCAN_NONE;
|
|
break;
|
|
}
|
|
|
|
p_scan->p_current = p_scan->p_current->p_next;
|
|
*pf_pos = (double) p_scan->spectrum.i_index++ / p_scan->i_scanlist;
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
if( p_scan->p_scanlist == NULL &&
|
|
( p_scan->i_multiplex == 0 || /* Stop frequency scanning if we've found a valid NIT */
|
|
(p_scan->parameter.b_use_nit && !p_scan->b_multiplexes_from_nit) ) )
|
|
{
|
|
int i_ret = Scan_GetNextSpectrumTunerConfig( p_scan, p_cfg, pf_pos );
|
|
if( i_ret == VLC_SUCCESS )
|
|
return i_ret;
|
|
}
|
|
|
|
if( p_scan->i_multiplex_toscan )
|
|
{
|
|
for( size_t i=0; i<p_scan->i_multiplex_toscan; i++ )
|
|
{
|
|
if( !p_scan->pp_multiplex[i]->b_scanned )
|
|
{
|
|
scan_SetMultiplexScanStatus( p_scan, p_scan->pp_multiplex[i], true );
|
|
*p_cfg = p_scan->pp_multiplex[i]->cfg;
|
|
*pf_pos = (double) 1.0 - (p_scan->i_multiplex / p_scan->i_multiplex_toscan);
|
|
return VLC_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return VLC_ENOENT;
|
|
}
|
|
|
|
static int scan_Next( scan_t *p_scan, scan_tuner_config_t *p_cfg )
|
|
{
|
|
double f_position;
|
|
int i_ret;
|
|
|
|
if( scan_IsCancelled( p_scan ) )
|
|
return VLC_EGENERIC;
|
|
|
|
//do
|
|
{
|
|
scan_tuner_config_Init( p_cfg, &p_scan->parameter );
|
|
|
|
i_ret = Scan_GetNextTunerConfig( p_scan, p_cfg, &f_position );
|
|
if( i_ret )
|
|
return i_ret;
|
|
}
|
|
//while( !scan_tuner_config_ParametersValidate( &p_scan->parameter, p_cfg ) );
|
|
|
|
const size_t i_total_services = scan_CountServices( p_scan );
|
|
const vlc_tick_t i_eta = f_position > 0.005 ? (vlc_tick_now() - p_scan->i_time_start) * ( 1.0 / f_position - 1.0 ) : -1;
|
|
char psz_eta[MSTRTIME_MAX_SIZE];
|
|
const char *psz_fmt = _("%.1f MHz (%d services)\n~%s remaining");
|
|
vlc_tick_to_str( psz_eta, i_eta );
|
|
|
|
if( i_eta >= 0 )
|
|
msg_Info( p_scan->p_obj, "Scan ETA %s | %f", psz_eta, f_position * 100 );
|
|
|
|
if( p_scan->p_dialog_id == NULL )
|
|
{
|
|
p_scan->p_dialog_id =
|
|
vlc_dialog_display_progress( p_scan->p_obj, false,
|
|
f_position, _("Cancel"),
|
|
_("Scanning DVB"), psz_fmt,
|
|
(double)p_cfg->i_frequency / 1000000,
|
|
i_total_services,
|
|
psz_eta);
|
|
}
|
|
else
|
|
{
|
|
vlc_dialog_update_progress_text( p_scan->p_obj, p_scan->p_dialog_id,
|
|
f_position, psz_fmt,
|
|
(double)p_cfg->i_frequency / 1000000,
|
|
i_total_services,
|
|
psz_eta );
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
bool scan_IsCancelled( scan_t *p_scan )
|
|
{
|
|
if( p_scan->p_dialog_id == NULL )
|
|
return false;
|
|
return vlc_dialog_is_cancelled( p_scan->p_obj, p_scan->p_dialog_id );
|
|
}
|
|
|
|
int scan_Run( scan_t *p_scan )
|
|
{
|
|
scan_tuner_config_t cfg;
|
|
if( scan_Next( p_scan, &cfg ) )
|
|
return VLC_ENOENT;
|
|
|
|
scan_session_t *session = scan_session_New( p_scan, &cfg );
|
|
if( unlikely(session == NULL) )
|
|
return VLC_EGENERIC;
|
|
|
|
if( p_scan->pf_tune( p_scan, p_scan->p_cbdata, &cfg ) != VLC_SUCCESS )
|
|
{
|
|
scan_session_Destroy( p_scan, session );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
p_scan->pf_filter( p_scan, p_scan->p_cbdata, PSI_PAT_PID, true );
|
|
p_scan->pf_filter( p_scan, p_scan->p_cbdata, SI_SDT_PID, true );
|
|
if( p_scan->parameter.b_use_nit )
|
|
p_scan->pf_filter( p_scan, p_scan->p_cbdata, SI_NIT_PID, true );
|
|
|
|
/* */
|
|
uint8_t packet[TS_PACKET_SIZE * SCAN_READ_BUFFER_COUNT];
|
|
int64_t i_scan_start = vlc_tick_now();
|
|
|
|
for( ;; )
|
|
{
|
|
unsigned i_timeout = scan_session_GetTablesTimeout( session );
|
|
vlc_tick_t i_remaining = vlc_tick_now() - i_scan_start;
|
|
if( i_remaining > i_timeout )
|
|
break;
|
|
|
|
size_t i_packet_count = 0;
|
|
int i_ret = p_scan->pf_read( p_scan, p_scan->p_cbdata,
|
|
i_timeout - i_remaining,
|
|
SCAN_READ_BUFFER_COUNT,
|
|
(uint8_t *) &packet, &i_packet_count );
|
|
|
|
if( p_scan->pf_stats )
|
|
p_scan->pf_stats( p_scan, p_scan->p_cbdata, &session->i_snr );
|
|
|
|
if ( i_ret != VLC_SUCCESS )
|
|
break;
|
|
|
|
for( size_t i=0; i< i_packet_count; i++ )
|
|
{
|
|
if( scan_session_Push( session,
|
|
&packet[i * TS_PACKET_SIZE] ) )
|
|
break;
|
|
}
|
|
}
|
|
|
|
scan_session_Destroy( p_scan, session );
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static void scan_NotifyService( scan_t *p_scan, scan_service_t *p_service, bool b_updated )
|
|
{
|
|
if( !p_scan->pf_notify_service || !scan_service_type_Supported( p_service->type ) )
|
|
return;
|
|
p_service->stickyref = p_scan->pf_notify_service( p_scan, p_scan->p_cbdata,
|
|
p_service, p_service->stickyref,
|
|
b_updated );
|
|
}
|
|
|
|
#define scan_NotifyNewService( a, b ) scan_NotifyService( a, b, false )
|
|
#define scan_NotifyUpdatedService( a, b ) scan_NotifyService( a, b, true )
|
|
|
|
static bool GetOtherNetworkNIT( scan_session_t *p_session, uint16_t i_network_id,
|
|
dvbpsi_nit_t ***ppp_nit )
|
|
{
|
|
for( size_t i=0; i<p_session->others.i_nit; i++ )
|
|
{
|
|
if( p_session->others.pp_nit[i]->i_network_id == i_network_id )
|
|
{
|
|
*ppp_nit = &p_session->others.pp_nit[i];
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool GetOtherTsSDT( scan_session_t *p_session, uint16_t i_ts_id,
|
|
dvbpsi_sdt_t ***ppp_sdt )
|
|
{
|
|
for( size_t i=0; i<p_session->others.i_sdt; i++ )
|
|
{
|
|
if( p_session->others.pp_sdt[i]->i_extension == i_ts_id )
|
|
{
|
|
*ppp_sdt = &p_session->others.pp_sdt[i];
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void ParsePAT( vlc_object_t *p_obj, scan_t *p_scan,
|
|
const dvbpsi_pat_t *p_pat, const scan_tuner_config_t *p_cfg,
|
|
int i_snr )
|
|
{
|
|
/* PAT must not create new service without proper config ( local ) */
|
|
if( !p_cfg )
|
|
return;
|
|
|
|
scan_multiplex_t *p_mplex = scan_FindOrCreateMultiplex( p_scan, p_pat->i_ts_id, p_cfg );
|
|
if( unlikely(p_mplex == NULL) )
|
|
return;
|
|
|
|
if( p_mplex->i_snr > 0 && i_snr > p_mplex->i_snr )
|
|
{
|
|
msg_Info( p_obj, "multiplex ts_id %" PRIu16 " freq %u snr %d replaced by freq %u snr %d",
|
|
p_mplex->i_ts_id, p_mplex->cfg.i_frequency, p_mplex->i_snr,
|
|
p_cfg->i_frequency, i_snr );
|
|
p_mplex->cfg = *p_cfg;
|
|
}
|
|
p_mplex->i_snr = i_snr;
|
|
|
|
const 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_multiplex_FindService( p_mplex, p_program->i_number );
|
|
if( s == NULL )
|
|
{
|
|
s = scan_service_New( p_program->i_number );
|
|
if( likely(s) )
|
|
{
|
|
if( !scan_multiplex_AddService( p_mplex, s ) ) /* OOM */
|
|
scan_service_Delete( s );
|
|
else
|
|
scan_NotifyNewService( p_scan, s );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* */
|
|
if( p_session->local.p_pat && p_session->local.p_pat->b_current_next )
|
|
{
|
|
dvbpsi_pat_delete( p_session->local.p_pat );
|
|
p_session->local.p_pat = NULL;
|
|
}
|
|
if( p_session->local.p_pat )
|
|
{
|
|
dvbpsi_pat_delete( p_pat );
|
|
return;
|
|
}
|
|
|
|
dvbpsi_pat_program_t *p_program;
|
|
|
|
/* */
|
|
p_session->local.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 ParseSDT( vlc_object_t *p_obj, scan_t *p_scan, const dvbpsi_sdt_t *p_sdt )
|
|
{
|
|
VLC_UNUSED(p_obj);
|
|
/* SDT must not create new service without proper config ( local )
|
|
or it must has been created by another network NIT (providing freq).
|
|
Guaranteed by parsing order( PAT, current ts SDT ) or ( NIT, SDT ) */
|
|
scan_multiplex_t *p_mplex = scan_FindMultiplex( p_scan, p_sdt->i_extension );
|
|
if( unlikely(p_mplex == NULL) )
|
|
return ;
|
|
|
|
scan_SetMultiplexScanStatus( p_scan, p_mplex, true );
|
|
|
|
if( p_mplex->i_sdt_version == UINT8_MAX )
|
|
p_mplex->i_sdt_version = p_sdt->i_version;
|
|
|
|
for( const dvbpsi_sdt_service_t *p_srv = p_sdt->p_first_service;
|
|
p_srv; p_srv = p_srv->p_next )
|
|
{
|
|
bool b_newservice = false;
|
|
scan_service_t *s = scan_multiplex_FindService( p_mplex, p_srv->i_service_id );
|
|
if( s == NULL )
|
|
{
|
|
b_newservice = true;
|
|
s = scan_service_New( p_srv->i_service_id );
|
|
if( unlikely(s == NULL) )
|
|
continue;
|
|
if( !scan_multiplex_AddService( p_mplex, s ) )
|
|
{
|
|
scan_service_Delete( s );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
s->b_crypted = p_srv->b_free_ca;
|
|
|
|
for( dvbpsi_descriptor_t *p_dr = p_srv->p_first_descriptor;
|
|
p_dr; p_dr = p_dr->p_next )
|
|
{
|
|
if( p_dr->i_tag != 0x48 )
|
|
continue;
|
|
|
|
dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
|
|
if( pD )
|
|
{
|
|
if( !s->psz_name )
|
|
s->psz_name = vlc_from_EIT( pD->i_service_name,
|
|
pD->i_service_name_length );
|
|
free( s->psz_provider );
|
|
s->psz_provider = vlc_from_EIT( pD->i_service_provider_name,
|
|
pD->i_service_provider_name_length );
|
|
|
|
s->type = pD->i_service_type;
|
|
}
|
|
}
|
|
|
|
scan_NotifyService( p_scan, s, !b_newservice );
|
|
}
|
|
}
|
|
|
|
static void SDTCallBack( scan_session_t *p_session, dvbpsi_sdt_t *p_sdt )
|
|
{
|
|
vlc_object_t *p_obj = p_session->p_obj;
|
|
dvbpsi_sdt_t **pp_stored_sdt = NULL;
|
|
if( p_sdt->i_table_id == SDT_OTHER_TS_TABLE_ID )
|
|
{
|
|
if( !GetOtherTsSDT( p_session, p_sdt->i_extension, &pp_stored_sdt ) )
|
|
{
|
|
dvbpsi_sdt_t **pp_realloc = realloc( p_session->others.pp_sdt,
|
|
(p_session->others.i_sdt + 1) * sizeof( *pp_realloc ) );
|
|
if( !pp_realloc ) /* oom */
|
|
{
|
|
dvbpsi_sdt_delete( p_sdt );
|
|
return;
|
|
}
|
|
pp_stored_sdt = &pp_realloc[p_session->others.i_sdt];
|
|
p_session->others.pp_sdt = pp_realloc;
|
|
p_session->others.i_sdt++;
|
|
}
|
|
}
|
|
else /* SDT_CURRENT_TS_TABLE_ID */
|
|
{
|
|
pp_stored_sdt = &p_session->local.p_sdt;
|
|
}
|
|
|
|
/* Store, replace, or discard */
|
|
if( *pp_stored_sdt )
|
|
{
|
|
if( (*pp_stored_sdt)->i_version == p_sdt->i_version ||
|
|
(*pp_stored_sdt)->b_current_next > p_sdt->b_current_next )
|
|
{
|
|
/* Duplicate or stored one isn't current */
|
|
dvbpsi_sdt_delete( p_sdt );
|
|
return;
|
|
}
|
|
dvbpsi_sdt_delete( *pp_stored_sdt );
|
|
}
|
|
*pp_stored_sdt = p_sdt;
|
|
|
|
/* */
|
|
msg_Dbg( p_obj, "new SDT %s ts_id=%d version=%d current_next=%d network_id=%d",
|
|
( p_sdt->i_table_id == SDT_CURRENT_TS_TABLE_ID ) ? "local" : "other",
|
|
p_sdt->i_extension, 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 );
|
|
if( pD )
|
|
{
|
|
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 scan_coderate_t ConvertDelDrInnerFec( uint8_t v )
|
|
{
|
|
switch(v)
|
|
{
|
|
default:
|
|
case 0x0: return SCAN_CODERATE_AUTO;
|
|
case 0x1: return SCAN_CODERATE_1_2;
|
|
case 0x2: return SCAN_CODERATE_2_3;
|
|
case 0x3: return SCAN_CODERATE_3_4;
|
|
case 0x4: return SCAN_CODERATE_5_6;
|
|
case 0x5: return SCAN_CODERATE_7_8;
|
|
case 0x6: return SCAN_CODERATE_8_9;
|
|
case 0x7: return SCAN_CODERATE_3_5;
|
|
case 0x8: return SCAN_CODERATE_4_5;
|
|
case 0x9: return SCAN_CODERATE_9_10;
|
|
case 0xF: return SCAN_CODERATE_NONE;
|
|
}
|
|
}
|
|
|
|
static scan_coderate_t ConvertDelDrCodeRate( uint8_t v )
|
|
{
|
|
if( v > 0x04 )
|
|
return SCAN_CODERATE_AUTO;
|
|
else
|
|
return ConvertDelDrInnerFec( v + 1 );
|
|
}
|
|
|
|
static void ParseNIT( vlc_object_t *p_obj, scan_t *p_scan,
|
|
const dvbpsi_nit_t *p_nit, const scan_tuner_config_t *p_cfg )
|
|
{
|
|
for( const dvbpsi_nit_ts_t *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;
|
|
scan_tuner_config_t tscfg;
|
|
scan_tuner_config_Init( &tscfg, &p_scan->parameter );
|
|
if( p_cfg != NULL ) // p_nit->i_table_id != NIT_CURRENT_NETWORK_TABLE_ID
|
|
tscfg = *p_cfg;
|
|
|
|
dvbpsi_service_list_dr_t *p_sl = NULL;
|
|
dvbpsi_lcn_dr_t *p_lc = NULL;
|
|
dvbpsi_descriptor_t *p_nn = NULL;
|
|
|
|
for( p_dsc = p_ts->p_first_descriptor; p_dsc != NULL; p_dsc = p_dsc->p_next )
|
|
{
|
|
if( p_dsc->i_tag == 0x41 )
|
|
{
|
|
/* Store it and process it after signal config
|
|
* (required for NIT describing other networks) */
|
|
p_sl = dvbpsi_DecodeServiceListDr( p_dsc );
|
|
}
|
|
else if( p_dsc->i_tag == 0x5a )
|
|
{
|
|
dvbpsi_terr_deliv_sys_dr_t *p_t = dvbpsi_DecodeTerrDelivSysDr( p_dsc );
|
|
if( p_t )
|
|
{
|
|
tscfg.i_frequency = p_t->i_centre_frequency / 10;
|
|
tscfg.i_bandwidth = 8 - p_t->i_bandwidth;
|
|
switch(p_t->i_constellation)
|
|
{
|
|
case 0x00:
|
|
tscfg.modulation = SCAN_MODULATION_QPSK;
|
|
break;
|
|
case 0x01:
|
|
tscfg.modulation = SCAN_MODULATION_QAM_16;
|
|
break;
|
|
case 0x02:
|
|
tscfg.modulation = SCAN_MODULATION_QAM_64;
|
|
break;
|
|
default:
|
|
tscfg.modulation = SCAN_MODULATION_AUTO;
|
|
break;
|
|
}
|
|
|
|
tscfg.coderate_hp = ConvertDelDrCodeRate( p_t->i_code_rate_hp_stream );
|
|
if( p_t->i_hierarchy_information == 0x0 || p_t->i_hierarchy_information == 0x4 )
|
|
tscfg.coderate_lp = SCAN_CODERATE_NONE;
|
|
else
|
|
tscfg.coderate_lp = ConvertDelDrCodeRate( p_t->i_code_rate_lp_stream );
|
|
|
|
msg_Dbg( p_obj, " * terrestrial delivery system" );
|
|
msg_Dbg( p_obj, " * centre_frequency %u", tscfg.i_frequency );
|
|
msg_Dbg( p_obj, " * bandwidth %u", tscfg.i_bandwidth );
|
|
msg_Dbg( p_obj, " * modulation %s", scan_value_modulation( tscfg.modulation ) );
|
|
msg_Dbg( p_obj, " * hierarchy %d", p_t->i_hierarchy_information );
|
|
msg_Dbg( p_obj, " * code_rate hp %s lp %s", scan_value_coderate( tscfg.coderate_hp ),
|
|
scan_value_coderate( tscfg.coderate_hp ) );
|
|
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 == 0x43 )
|
|
{
|
|
dvbpsi_sat_deliv_sys_dr_t *p_s = dvbpsi_DecodeSatDelivSysDr( p_dsc );
|
|
if( p_s )
|
|
{
|
|
tscfg.i_frequency = decode_BCD( p_s->i_frequency ) * 1000;
|
|
tscfg.i_symbolrate = decode_BCD( p_s->i_symbol_rate ) * 100;
|
|
if( unlikely(p_s->i_polarization > 0x03) )
|
|
p_s->i_polarization = 0;
|
|
|
|
const scan_polarization_t polarizations[] = {
|
|
SCAN_POLARIZATION_HORIZONTAL,
|
|
SCAN_POLARIZATION_VERTICAL,
|
|
SCAN_POLARIZATION_CIRC_LEFT,
|
|
SCAN_POLARIZATION_CIRC_RIGHT };
|
|
tscfg.polarization = polarizations[p_s->i_polarization];
|
|
|
|
switch(p_s->i_modulation_type)
|
|
{
|
|
default:
|
|
case 0x00:
|
|
tscfg.modulation = SCAN_MODULATION_AUTO;
|
|
break;
|
|
case 0x01:
|
|
tscfg.modulation = SCAN_MODULATION_QPSK;
|
|
break;
|
|
case 0x02:
|
|
tscfg.modulation = SCAN_MODULATION_PSK_8;
|
|
break;
|
|
case 0x03:
|
|
tscfg.modulation = SCAN_MODULATION_QAM_16;
|
|
break;
|
|
}
|
|
|
|
tscfg.delivery = (p_s->i_modulation_system == 0x01) ? SCAN_DELIVERY_DVB_S2
|
|
: SCAN_DELIVERY_DVB_S;
|
|
tscfg.inner_fec = ConvertDelDrInnerFec( p_s->i_fec_inner );
|
|
|
|
msg_Dbg( p_obj, " * satellite delivery system" );
|
|
msg_Dbg( p_obj, " * frequency %u", tscfg.i_frequency );
|
|
msg_Dbg( p_obj, " * symbolrate %u", tscfg.i_symbolrate );
|
|
msg_Dbg( p_obj, " * polarization %c", (char) tscfg.polarization );
|
|
msg_Dbg( p_obj, " * modulation %s", scan_value_modulation( tscfg.modulation ) );
|
|
msg_Dbg( p_obj, " * fec inner %s", scan_value_coderate( tscfg.inner_fec ) );
|
|
if( tscfg.delivery == SCAN_DELIVERY_DVB_S2 )
|
|
{
|
|
msg_Dbg( p_obj, " * system DVB-S2" );
|
|
}
|
|
}
|
|
}
|
|
else if( p_dsc->i_tag == 0x44 )
|
|
{
|
|
dvbpsi_cable_deliv_sys_dr_t *p_t = dvbpsi_DecodeCableDelivSysDr( p_dsc );
|
|
if( p_t )
|
|
{
|
|
tscfg.i_frequency = decode_BCD( p_t->i_frequency ) * 100;
|
|
tscfg.i_symbolrate = decode_BCD( p_t->i_symbol_rate ) * 100;
|
|
if( p_t->i_modulation <= 0x05 )
|
|
tscfg.modulation = p_t->i_modulation;
|
|
else
|
|
tscfg.modulation = SCAN_MODULATION_AUTO;
|
|
tscfg.inner_fec = ConvertDelDrInnerFec( p_t->i_fec_inner );
|
|
|
|
msg_Dbg( p_obj, " * Cable delivery system");
|
|
msg_Dbg( p_obj, " * frequency %d", tscfg.i_frequency );
|
|
msg_Dbg( p_obj, " * symbolrate %u", tscfg.i_symbolrate );
|
|
msg_Dbg( p_obj, " * modulation %s", scan_value_modulation( tscfg.modulation ) );
|
|
msg_Dbg( p_obj, " * fec inner %s", scan_value_coderate( tscfg.inner_fec ) );
|
|
}
|
|
}
|
|
else if( p_dsc->i_tag == 0x5f && p_dsc->i_length > 3 )
|
|
{
|
|
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)" );
|
|
p_lc = dvbpsi_DecodeLCNDr( p_dsc );
|
|
}
|
|
else if( p_dsc->i_tag == 0x40 && p_dsc->i_length > 0 ) /* Network Name */
|
|
{
|
|
p_nn = p_dsc;
|
|
}
|
|
else
|
|
{
|
|
msg_Warn( p_obj, " * dsc 0x%x", p_dsc->i_tag );
|
|
}
|
|
}
|
|
|
|
bool b_valid = scan_tuner_config_StandardValidate( &tscfg );
|
|
if( b_valid && p_nit->i_table_id == NIT_CURRENT_NETWORK_TABLE_ID )
|
|
{
|
|
p_scan->b_multiplexes_from_nit |= b_valid;
|
|
}
|
|
|
|
scan_multiplex_t *p_mplex = scan_FindMultiplex( p_scan, p_ts->i_ts_id );
|
|
if( p_mplex == NULL && b_valid )
|
|
{
|
|
p_mplex = scan_multiplex_New( &tscfg, p_ts->i_ts_id );
|
|
if( likely(p_mplex) )
|
|
{
|
|
if ( unlikely(!scan_AddMultiplex( p_scan, p_mplex )) )
|
|
{
|
|
scan_multiplex_Delete( p_mplex );
|
|
p_mplex = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( unlikely(!p_mplex) )
|
|
continue;
|
|
|
|
if( p_mplex->i_network_id == NETWORK_ID_RESERVED )
|
|
{
|
|
p_mplex->i_network_id = p_nit->i_network_id;
|
|
p_mplex->i_nit_version = p_nit->i_version;
|
|
}
|
|
|
|
/* Now process service list, and create them if tuner config is known */
|
|
if( p_sl )
|
|
{
|
|
msg_Dbg( p_obj, " * service list descriptor" );
|
|
for( uint8_t i = 0; p_sl && i < p_sl->i_service_count; i++ )
|
|
{
|
|
const uint16_t i_service_id = p_sl->i_service[i].i_service_id;
|
|
const uint8_t i_service_type = p_sl->i_service[i].i_service_type;
|
|
msg_Dbg( p_obj, " * service_id=%" PRIu16 " type=%" PRIu8,
|
|
i_service_id, i_service_type );
|
|
|
|
if( !p_cfg || p_cfg->i_frequency == 0 )
|
|
{
|
|
msg_Warn( p_obj, "cannot create service_id=%" PRIu16 " ts_id=%" PRIu16 " (no config)",
|
|
i_service_id, p_ts->i_ts_id );
|
|
continue;
|
|
}
|
|
|
|
bool b_newservice = false;
|
|
scan_service_t *s = scan_multiplex_FindService( p_mplex, i_service_id );
|
|
if( s == NULL )
|
|
{
|
|
s = scan_service_New( i_service_id );
|
|
if( unlikely(s == NULL) )
|
|
continue;
|
|
|
|
b_newservice = true;
|
|
s->type = i_service_type;
|
|
s->i_original_network_id = p_ts->i_orig_network_id;
|
|
if( !scan_multiplex_AddService( p_mplex, s ) )
|
|
{
|
|
scan_service_Delete( s );
|
|
s = NULL;
|
|
}
|
|
}
|
|
|
|
if ( s->psz_original_network_name == NULL && p_nn )
|
|
s->psz_original_network_name = strndup( (const char*) p_nn->p_data, p_nn->i_length );
|
|
|
|
scan_NotifyService( p_scan, s, !b_newservice );
|
|
}
|
|
}
|
|
|
|
/* Set virtual channel numbers */
|
|
if( p_lc )
|
|
{
|
|
for( int i = 0; i < p_lc->i_number_of_entries; i++ )
|
|
{
|
|
const uint16_t i_service_id = p_lc->p_entries[i].i_service_id;
|
|
const uint16_t i_channel_number = p_lc->p_entries[i].i_logical_channel_number;
|
|
msg_Dbg( p_obj, " * service_id=%" PRIu16 " channel_number=%" PRIu16,
|
|
i_service_id, i_channel_number );
|
|
scan_service_t *s = scan_multiplex_FindService( p_mplex, i_service_id );
|
|
if( s )
|
|
s->i_channel = i_channel_number;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void NITCallBack( scan_session_t *p_session, dvbpsi_nit_t *p_nit )
|
|
{
|
|
vlc_object_t *p_obj = p_session->p_obj;
|
|
dvbpsi_nit_t **pp_stored_nit = NULL;
|
|
|
|
if( p_nit->i_table_id == NIT_OTHER_NETWORK_TABLE_ID )
|
|
{
|
|
if( !GetOtherNetworkNIT( p_session, p_nit->i_network_id, &pp_stored_nit ) )
|
|
{
|
|
dvbpsi_nit_t **pp_realloc = realloc( p_session->others.pp_nit,
|
|
(p_session->others.i_nit + 1) * sizeof( *pp_realloc ) );
|
|
if( !pp_realloc ) /* oom */
|
|
{
|
|
dvbpsi_nit_delete( p_nit );
|
|
return;
|
|
}
|
|
pp_stored_nit = &pp_realloc[p_session->others.i_nit];
|
|
p_session->others.pp_nit = pp_realloc;
|
|
p_session->others.i_nit++;
|
|
}
|
|
}
|
|
else /* NIT_CURRENT_NETWORK_TABLE_ID */
|
|
{
|
|
pp_stored_nit = &p_session->local.p_nit;
|
|
}
|
|
|
|
/* Store, replace, or discard */
|
|
if( *pp_stored_nit )
|
|
{
|
|
if( (*pp_stored_nit)->i_version == p_nit->i_version ||
|
|
(*pp_stored_nit)->b_current_next > p_nit->b_current_next )
|
|
{
|
|
/* Duplicate or stored one isn't current */
|
|
dvbpsi_nit_delete( p_nit );
|
|
return;
|
|
}
|
|
dvbpsi_nit_delete( *pp_stored_nit );
|
|
}
|
|
*pp_stored_nit = p_nit;
|
|
|
|
msg_Dbg( p_obj, "new NIT %s network_id=%d version=%d current_next=%d",
|
|
( p_nit->i_table_id == NIT_CURRENT_NETWORK_TABLE_ID ) ? "local" : "other",
|
|
p_nit->i_network_id, p_nit->i_version, p_nit->b_current_next );
|
|
|
|
/* */
|
|
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 && p_dsc->i_length > 0 )
|
|
{
|
|
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 );
|
|
}
|
|
#if 0
|
|
else if( p_dsc->i_tag == 0x4a )
|
|
{
|
|
dvbpsi_linkage_dr_t *p_l = dvbpsi_DecodeLinkageDr( p_dsc );
|
|
if( p_l )
|
|
{
|
|
msg_Dbg( p_obj, " * linkage descriptor" );
|
|
msg_Dbg( p_obj, " * ts_id %" PRIu16, p_l->i_transport_stream_id );
|
|
msg_Dbg( p_obj, " * on_id %" PRIu16, p_l->i_original_network_id );
|
|
msg_Dbg( p_obj, " * service_id %" PRIu16, p_l->i_service_id );
|
|
msg_Dbg( p_obj, " * linkage_type %" PRIu8, p_l->i_linkage_type );
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
msg_Dbg( p_obj, " * dsc 0x%x", p_dsc->i_tag );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void PSINewTableCallBack( dvbpsi_t *h, uint8_t i_table_id, uint16_t i_extension, void *p_data )
|
|
{
|
|
scan_session_t *p_session = (scan_session_t *)p_data;
|
|
|
|
if( i_table_id == SDT_CURRENT_TS_TABLE_ID || i_table_id == SDT_OTHER_TS_TABLE_ID )
|
|
{
|
|
if( !dvbpsi_sdt_attach( h, i_table_id, i_extension, (dvbpsi_sdt_callback)SDTCallBack, p_session ) )
|
|
msg_Err( p_session->p_obj, "PSINewTableCallback: failed attaching SDTCallback" );
|
|
}
|
|
else if( i_table_id == NIT_CURRENT_NETWORK_TABLE_ID || i_table_id == NIT_OTHER_NETWORK_TABLE_ID )
|
|
{
|
|
if( !dvbpsi_nit_attach( h, i_table_id, i_extension, (dvbpsi_nit_callback)NITCallBack, p_session ) )
|
|
msg_Err( p_session->p_obj, "PSINewTableCallback: failed attaching NITCallback" );
|
|
}
|
|
}
|
|
|
|
static scan_session_t *scan_session_New( scan_t *p_scan, const scan_tuner_config_t *p_cfg )
|
|
{
|
|
scan_session_t *p_session = malloc( sizeof( *p_session ) );
|
|
if( unlikely(p_session == NULL) )
|
|
return NULL;
|
|
p_session->p_obj = p_scan->p_obj;
|
|
p_session->cfg = *p_cfg;
|
|
p_session->i_snr = -1;
|
|
p_session->local.p_pat = NULL;
|
|
p_session->local.p_sdt = NULL;
|
|
p_session->local.p_nit = NULL;
|
|
p_session->i_nit_pid = -1;
|
|
p_session->b_use_nit = p_scan->parameter.b_use_nit;
|
|
p_session->type = p_scan->parameter.type;
|
|
p_session->others.i_nit = 0;
|
|
p_session->others.i_sdt = 0;
|
|
p_session->others.pp_nit = NULL;
|
|
p_session->others.pp_sdt = NULL;
|
|
p_session->p_pathandle = NULL;
|
|
p_session->p_sdthandle = NULL;
|
|
p_session->p_nithandle = NULL;
|
|
return p_session;
|
|
}
|
|
|
|
static void scan_session_Delete( scan_session_t *p_session )
|
|
{
|
|
for( size_t i=0; i< p_session->others.i_sdt; i++ )
|
|
dvbpsi_sdt_delete( p_session->others.pp_sdt[i] );
|
|
free( p_session->others.pp_sdt );
|
|
|
|
for( size_t i=0; i< p_session->others.i_nit; i++ )
|
|
dvbpsi_nit_delete( p_session->others.pp_nit[i] );
|
|
free( p_session->others.pp_nit );
|
|
|
|
if( p_session->p_pathandle )
|
|
{
|
|
dvbpsi_pat_detach( p_session->p_pathandle );
|
|
if( p_session->local.p_pat )
|
|
dvbpsi_pat_delete( p_session->local.p_pat );
|
|
}
|
|
|
|
if( p_session->p_sdthandle )
|
|
{
|
|
dvbpsi_DetachDemux( p_session->p_sdthandle );
|
|
if( p_session->local.p_sdt )
|
|
dvbpsi_sdt_delete( p_session->local.p_sdt );
|
|
}
|
|
|
|
if( p_session->p_nithandle )
|
|
{
|
|
dvbpsi_DetachDemux( p_session->p_nithandle );
|
|
if( p_session->local.p_nit )
|
|
dvbpsi_nit_delete( p_session->local.p_nit );
|
|
}
|
|
|
|
free( p_session );
|
|
}
|
|
|
|
static void scan_session_Destroy( scan_t *p_scan, scan_session_t *p_session )
|
|
{
|
|
dvbpsi_pat_t *p_pat = p_session->local.p_pat;
|
|
dvbpsi_sdt_t *p_sdt = p_session->local.p_sdt;
|
|
dvbpsi_nit_t *p_nit = p_session->local.p_nit;
|
|
|
|
/* Parse PAT (Declares only local services/programs) */
|
|
if( p_pat )
|
|
ParsePAT( p_scan->p_obj, p_scan, p_pat, &p_session->cfg, p_session->i_snr );
|
|
|
|
/* Parse NIT (Declares local services/programs) */
|
|
if( p_nit )
|
|
ParseNIT( p_scan->p_obj, p_scan, p_nit, &p_session->cfg );
|
|
|
|
/* Parse SDT (Maps names to programs) */
|
|
if( p_sdt )
|
|
ParseSDT( p_scan->p_obj, p_scan, p_sdt );
|
|
|
|
/* Do the same for all other networks */
|
|
for( size_t i=0; i<p_session->others.i_nit; i++ )
|
|
ParseNIT( p_scan->p_obj, p_scan, p_session->others.pp_nit[i], NULL );
|
|
|
|
/* Map service name for all other ts/networks */
|
|
for( size_t i=0; i<p_session->others.i_sdt; i++ )
|
|
ParseSDT( p_scan->p_obj, p_scan, p_session->others.pp_sdt[i] );
|
|
|
|
/* */
|
|
scan_session_Delete( p_session );
|
|
}
|
|
|
|
static int ScanServiceCmp( const void *a, const void *b )
|
|
{
|
|
const scan_service_t *sa = *((const scan_service_t**)a);
|
|
const scan_service_t *sb = *((const 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 < 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;
|
|
}
|
|
|
|
void scan_set_NotifyCB( scan_t *p_scan, scan_service_notify_cb pf )
|
|
{
|
|
p_scan->pf_notify_service = pf;
|
|
}
|
|
|
|
const char * scan_service_GetName( const scan_service_t *s )
|
|
{
|
|
return s->psz_name;
|
|
}
|
|
|
|
const char * scan_service_GetProvider( const scan_service_t *s )
|
|
{
|
|
return s->psz_provider;
|
|
}
|
|
|
|
uint16_t scan_service_GetProgram( const scan_service_t *s )
|
|
{
|
|
return s->i_program;
|
|
}
|
|
|
|
const char * scan_service_GetNetworkName( const scan_service_t *s )
|
|
{
|
|
if( s->p_mplex )
|
|
return s->p_mplex->psz_network_name;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
char * scan_service_GetUri( const scan_service_t *s )
|
|
{
|
|
char *psz_mrl = NULL;
|
|
int i_ret = -1;
|
|
switch( s->p_mplex->cfg.type )
|
|
{
|
|
case SCAN_DVB_T:
|
|
i_ret = asprintf( &psz_mrl, "dvb://frequency=%d:bandwidth=%d:modulation=%s",
|
|
s->p_mplex->cfg.i_frequency,
|
|
s->p_mplex->cfg.i_bandwidth,
|
|
scan_value_modulation( s->p_mplex->cfg.modulation ) );
|
|
break;
|
|
case SCAN_DVB_S:
|
|
i_ret = asprintf( &psz_mrl, "dvb://frequency=%d:srate=%d:polarization=%c:fec=%s",
|
|
s->p_mplex->cfg.i_frequency,
|
|
s->p_mplex->cfg.i_symbolrate,
|
|
(char) s->p_mplex->cfg.polarization,
|
|
scan_value_coderate( s->p_mplex->cfg.inner_fec ) );
|
|
break;
|
|
case SCAN_DVB_C:
|
|
i_ret = asprintf( &psz_mrl, "dvb://frequency=%d:srate=%d:modulation=%s:fec=%s",
|
|
s->p_mplex->cfg.i_frequency,
|
|
s->p_mplex->cfg.i_symbolrate,
|
|
scan_value_modulation( s->p_mplex->cfg.modulation ),
|
|
scan_value_coderate( s->p_mplex->cfg.inner_fec ) );
|
|
default:
|
|
break;
|
|
}
|
|
return (i_ret >=0) ? psz_mrl : NULL;
|
|
}
|
|
|
|
block_t *scan_GetM3U( scan_t *p_scan )
|
|
{
|
|
vlc_object_t *p_obj = p_scan->p_obj;
|
|
block_t *p_playlist = BlockString( "#EXTM3U\n\n" );
|
|
if( !p_playlist )
|
|
return NULL;
|
|
|
|
const size_t i_total_services = scan_CountServices( p_scan );
|
|
size_t i_filtered_count = 0;
|
|
const scan_service_t **pp_filtered_list = vlc_alloc( i_total_services, sizeof(scan_service_t *) );
|
|
if( !pp_filtered_list )
|
|
{
|
|
block_Release( p_playlist );
|
|
return NULL;
|
|
}
|
|
|
|
for( size_t j = 0; j < p_scan->i_multiplex; j++ )
|
|
{
|
|
const scan_multiplex_t *p_mplex = p_scan->pp_multiplex[j];
|
|
for( size_t i = 0; i < p_mplex->i_services; i++ )
|
|
{
|
|
const scan_service_t *s = p_mplex->pp_services[i];
|
|
if( !scan_service_type_Supported( s->type ) )
|
|
{
|
|
/* 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;
|
|
}
|
|
pp_filtered_list[i_filtered_count++] = s;
|
|
}
|
|
}
|
|
|
|
/* */
|
|
qsort( pp_filtered_list, i_filtered_count, sizeof(scan_service_t *), ScanServiceCmp );
|
|
|
|
for( size_t i = 0; i < i_filtered_count; i++ )
|
|
{
|
|
const scan_service_t *s = pp_filtered_list[i];
|
|
|
|
const char *psz_type;
|
|
switch( s->type )
|
|
{
|
|
case SERVICE_TYPE_DIGITAL_TELEVISION: psz_type = "Digital television"; break;
|
|
case SERVICE_TYPE_DIGITAL_TELEVISION_AC_SD: psz_type = "Digital television advanced codec SD"; break;
|
|
case SERVICE_TYPE_DIGITAL_TELEVISION_AC_HD: psz_type = "Digital television advanced codec HD"; break;
|
|
case SERVICE_TYPE_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 modulation=%s",
|
|
s->i_program, psz_type, s->psz_name, s->i_channel, s->b_crypted,
|
|
s->p_mplex->i_network_id, s->p_mplex->i_nit_version, s->p_mplex->i_sdt_version,
|
|
s->p_mplex->cfg.i_frequency, s->p_mplex->cfg.i_bandwidth, s->p_mplex->i_snr,
|
|
scan_value_modulation( s->p_mplex->cfg.modulation ) );
|
|
|
|
char *psz_mrl = scan_service_GetUri( s );
|
|
if( psz_mrl == NULL )
|
|
continue;
|
|
|
|
char *psz;
|
|
int i_ret = asprintf( &psz, "#EXTINF:,,%s\n"
|
|
"#EXTVLCOPT:program=%d\n"
|
|
"%s\n\n",
|
|
s->psz_name && * s->psz_name ? s->psz_name : "Unknown",
|
|
s->i_program,
|
|
psz_mrl );
|
|
free( psz_mrl );
|
|
if( i_ret != -1 )
|
|
{
|
|
block_t *p_block = BlockString( psz );
|
|
if( p_block )
|
|
block_ChainAppend( &p_playlist, p_block );
|
|
free( psz );
|
|
}
|
|
}
|
|
|
|
free( pp_filtered_list );
|
|
|
|
return p_playlist ? block_ChainGather( p_playlist ) : NULL;
|
|
}
|
|
|
|
#define dvbpsi_packet_push(a,b) dvbpsi_packet_push(a, (uint8_t *)b)
|
|
|
|
static bool scan_session_Push( scan_session_t *p_scan, const uint8_t *p_packet )
|
|
{
|
|
if( p_packet[0] != 0x47 )
|
|
return false;
|
|
|
|
/* */
|
|
const int i_pid = ( (p_packet[1]&0x1f)<<8) | p_packet[2];
|
|
if( i_pid == PSI_PAT_PID )
|
|
{
|
|
if( !p_scan->p_pathandle )
|
|
{
|
|
p_scan->p_pathandle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
|
|
if( !p_scan->p_pathandle )
|
|
return false;
|
|
|
|
p_scan->p_pathandle->p_sys = (void *) p_scan->p_obj;
|
|
if( !dvbpsi_pat_attach( p_scan->p_pathandle, (dvbpsi_pat_callback)PATCallBack, p_scan ) )
|
|
{
|
|
dvbpsi_delete( p_scan->p_pathandle );
|
|
p_scan->p_pathandle = NULL;
|
|
return false;
|
|
}
|
|
}
|
|
if( p_scan->p_pathandle )
|
|
dvbpsi_packet_push( p_scan->p_pathandle, p_packet );
|
|
}
|
|
else if( i_pid == SI_SDT_PID )
|
|
{
|
|
if( !p_scan->p_sdthandle )
|
|
{
|
|
p_scan->p_sdthandle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
|
|
if( !p_scan->p_sdthandle )
|
|
return false;
|
|
|
|
p_scan->p_sdthandle->p_sys = (void *) p_scan->p_obj;
|
|
if( !dvbpsi_AttachDemux( p_scan->p_sdthandle, (dvbpsi_demux_new_cb_t)PSINewTableCallBack, p_scan ) )
|
|
{
|
|
dvbpsi_delete( p_scan->p_sdthandle );
|
|
p_scan->p_sdthandle = NULL;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( p_scan->p_sdthandle )
|
|
dvbpsi_packet_push( p_scan->p_sdthandle, p_packet );
|
|
}
|
|
else if( p_scan->b_use_nit && i_pid == SI_NIT_PID ) /*if( i_pid == p_scan->i_nit_pid )*/
|
|
{
|
|
if( !p_scan->p_nithandle )
|
|
{
|
|
p_scan->p_nithandle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
|
|
if( !p_scan->p_nithandle )
|
|
return false;
|
|
|
|
p_scan->p_nithandle->p_sys = (void *) p_scan->p_obj;
|
|
if( !dvbpsi_AttachDemux( p_scan->p_nithandle, (dvbpsi_demux_new_cb_t)PSINewTableCallBack, p_scan ) )
|
|
{
|
|
dvbpsi_delete( p_scan->p_nithandle );
|
|
p_scan->p_nithandle = NULL;
|
|
return false;
|
|
}
|
|
}
|
|
if( p_scan->p_nithandle )
|
|
dvbpsi_packet_push( p_scan->p_nithandle, p_packet );
|
|
}
|
|
|
|
return p_scan->local.p_pat && p_scan->local.p_sdt &&
|
|
(!p_scan->b_use_nit || p_scan->local.p_nit);
|
|
}
|
|
|
|
static unsigned scan_session_GetTablesTimeout( const scan_session_t *p_session )
|
|
{
|
|
unsigned i_time = 0;
|
|
if( !p_session->local.p_pat )
|
|
{
|
|
i_time = 500;
|
|
}
|
|
else if( !p_session->local.p_sdt )
|
|
{
|
|
i_time = 2*1000;
|
|
}
|
|
else if( !p_session->local.p_nit && p_session->b_use_nit )
|
|
{
|
|
if( p_session->type == SCAN_DVB_T )
|
|
i_time = 6000;
|
|
else
|
|
i_time = 5000;
|
|
}
|
|
|
|
return i_time * 2 * 1000;
|
|
}
|
|
|
|
const char *scan_value_modulation( scan_modulation_t m )
|
|
{
|
|
switch(m)
|
|
{
|
|
case SCAN_MODULATION_QAM_16: return "16QAM";
|
|
case SCAN_MODULATION_QAM_32: return "32QAM";
|
|
case SCAN_MODULATION_QAM_64: return "64QAM";
|
|
case SCAN_MODULATION_QAM_128: return "128QAM";
|
|
case SCAN_MODULATION_QAM_256: return "256QAM";
|
|
case SCAN_MODULATION_QAM_AUTO: return "QAM";
|
|
case SCAN_MODULATION_PSK_8: return "8PSK";
|
|
case SCAN_MODULATION_QPSK: return "QPSK";
|
|
case SCAN_MODULATION_DQPSK: return "DQPSK";
|
|
case SCAN_MODULATION_APSK_16: return "16APSK";
|
|
case SCAN_MODULATION_APSK_32: return "32APSK";
|
|
case SCAN_MODULATION_VSB_8: return "8VSB";
|
|
case SCAN_MODULATION_VSB_16: return "16VSB";
|
|
case SCAN_MODULATION_QAM_4NR:
|
|
case SCAN_MODULATION_AUTO:
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
const char *scan_value_coderate( scan_coderate_t c )
|
|
{
|
|
switch( c )
|
|
{
|
|
case SCAN_CODERATE_NONE: return "0";
|
|
case SCAN_CODERATE_1_2: return "1/2";
|
|
case SCAN_CODERATE_2_3: return "2/3";
|
|
case SCAN_CODERATE_3_4: return "3/4";
|
|
case SCAN_CODERATE_3_5: return "3/5";
|
|
case SCAN_CODERATE_4_5: return "4/5";
|
|
case SCAN_CODERATE_5_6: return "5/6";
|
|
case SCAN_CODERATE_7_8: return "7/8";
|
|
case SCAN_CODERATE_8_9: return "8/9";
|
|
case SCAN_CODERATE_9_10: return "9/10";
|
|
case SCAN_CODERATE_AUTO:
|
|
default: return "";
|
|
}
|
|
}
|
|
|