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.
 
 
 
 
 
 

598 lines
19 KiB

/*****************************************************************************
* access.c: DVB card input v4l2 only
*****************************************************************************
* Copyright (C) 1998-2010 VLC authors and VideoLAN
*
* Authors: Johan Bilien <jobi@via.ecp.fr>
* Jean-Paul Saman <jpsaman _at_ videolan _dot_ org>
* Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir@via.ecp.fr>
* David Kaplan <david@2of1.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_plugin.h>
#include <vlc_access.h>
#include <vlc_input.h>
#include <vlc_interrupt.h>
#include <vlc_dialog.h>
#include <sys/types.h>
#include <poll.h>
#include <errno.h>
#include "dvb.h"
#include "scan.h"
typedef struct
{
demux_handle_t p_demux_handles[MAX_DEMUX];
dvb_sys_t dvb;
/* Scan */
struct scan_t *scan;
bool done;
} access_sys_t;
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open( vlc_object_t *p_this );
static void Close( vlc_object_t *p_this );
#define PROBE_TEXT N_("Probe DVB card for capabilities")
#define PROBE_LONGTEXT N_("Some DVB cards do not like to be probed for their capabilities, you can disable this feature if you experience some trouble.")
/* Satellite */
#define SATELLITE_TEXT N_("Satellite scanning config")
#define SATELLITE_LONGTEXT N_("Filename of config file in share/dvb/dvb-s.")
#define SCANLIST_TEXT N_("Scan tuning list")
#define SCANLIST_LONGTEXT N_("Filename containing initial scan tuning data.")
#define SCAN_NIT_TEXT N_("Use NIT for scanning services")
vlc_module_begin ()
set_shortname( N_("DVB") )
set_description( N_("DVB input with v4l2 support") )
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_ACCESS )
add_bool( "dvb-probe", true, PROBE_TEXT, PROBE_LONGTEXT, true )
/* DVB-S (satellite) */
add_string( "dvb-satellite", NULL, SATELLITE_TEXT, SATELLITE_LONGTEXT,
true )
add_string( "dvb-scanlist", NULL, SCANLIST_TEXT, SCANLIST_LONGTEXT,
true )
add_bool( "dvb-scan-nit", true, SCAN_NIT_TEXT, NULL, true )
set_capability( "access", 0 )
add_shortcut( "dvb", /* Generic name */
"dvb-s", "qpsk", "satellite", /* Satellite */
"dvb-c", "cable", /* Cable */
"dvb-t", "terrestrial" ) /* Terrestrial */
set_callbacks( Open, Close )
vlc_module_end ()
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Control( stream_t *, int, va_list );
static block_t *BlockScan( stream_t *, bool * );
#define DVB_SCAN_MAX_LOCK_TIME (2*CLOCK_FREQ)
static void FilterUnset( stream_t *, int i_max );
static void FilterSet( stream_t *, int i_pid, int i_type );
static void VarInit( stream_t * );
static int ParseMRL( stream_t * );
static int ScanFrontendTuningHandler( scan_t *, void *, const scan_tuner_config_t * );
static int ScanFilterHandler( scan_t *, void *, uint16_t, bool );
static int ScanStatsCallback( scan_t *p_scan, void *p_privdata, int *pi_snr );
static int ScanReadCallback( scan_t *, void *, unsigned, size_t, uint8_t *, size_t *);
/*****************************************************************************
* Open: open the frontend device
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
stream_t *p_access = (stream_t*)p_this;
access_sys_t *p_sys;
if( p_access->b_preparsing )
return VLC_EGENERIC;
p_access->p_sys = p_sys = calloc( 1, sizeof( access_sys_t ) );
if( !p_sys )
return VLC_ENOMEM;
/* Create all variables */
VarInit( p_access );
/* Parse the command line */
if( ParseMRL( p_access ) )
{
free( p_sys );
return VLC_EGENERIC;
}
bool b_scan_mode = var_GetInteger( p_access, "dvb-frequency" ) == 0;
if( b_scan_mode )
{
msg_Dbg( p_access, "DVB scan mode selected" );
p_access->pf_block = BlockScan;
}
else
return VLC_EGENERIC; /* let the DTV plugin do the work */
/* Getting frontend info */
if( FrontendOpen( p_this, &p_sys->dvb, p_access->psz_name ) )
{
free( p_sys );
return VLC_EGENERIC;
}
/* Opening DVR device */
if( DVROpen( p_this, &p_sys->dvb ) < 0 )
{
FrontendClose( p_this, &p_sys->dvb );
free( p_sys );
return VLC_EGENERIC;
}
scan_parameter_t parameter;
scan_t *p_scan;
scan_parameter_Init( &parameter );
parameter.b_use_nit = var_InheritBool( p_access, "dvb-scan-nit" );
if( FrontendFillScanParameter( p_this, &p_sys->dvb, &parameter ) ||
(p_scan = scan_New( VLC_OBJECT(p_access), &parameter,
ScanFrontendTuningHandler,
ScanStatsCallback,
ScanFilterHandler,
ScanReadCallback,
p_access )) == NULL )
{
scan_parameter_Clean( &parameter );
Close( VLC_OBJECT(p_access) );
return VLC_EGENERIC;
}
scan_parameter_Clean( &parameter );
p_sys->scan = p_scan;
/* Set up access */
p_access->pf_control = Control;
return VLC_SUCCESS;
}
/*****************************************************************************
* Close : Close the device
*****************************************************************************/
static void Close( vlc_object_t *p_this )
{
stream_t *p_access = (stream_t*)p_this;
access_sys_t *p_sys = p_access->p_sys;
FilterUnset( p_access, MAX_DEMUX );
DVRClose( p_this, &p_sys->dvb );
FrontendClose( p_this, &p_sys->dvb );
scan_Destroy( p_sys->scan );
free( p_sys );
}
static int ScanFrontendTuningHandler( scan_t *p_scan, void *p_privdata,
const scan_tuner_config_t *p_cfg )
{
stream_t *p_access = (stream_t *) p_privdata;
access_sys_t *sys = p_access->p_sys;
VLC_UNUSED(p_scan);
var_SetInteger( p_access, "dvb-frequency", p_cfg->i_frequency );
var_SetInteger( p_access, "dvb-bandwidth", p_cfg->i_bandwidth );
if ( p_cfg->polarization != SCAN_POLARIZATION_NONE )
var_SetInteger( p_access, "dvb-voltage", p_cfg->polarization == SCAN_POLARIZATION_HORIZONTAL ? 18 : 13 );
if ( p_cfg->i_symbolrate )
var_SetInteger( p_access, "dvb-srate", p_cfg->i_symbolrate );
msg_Dbg( p_access, "Scanning frequency %d", p_cfg->i_frequency );
msg_Dbg( p_access, " bandwidth %d", p_cfg->i_bandwidth );
/* Setting frontend parameters for tuning the hardware */
if( FrontendSet( VLC_OBJECT(p_access), &sys->dvb ) < 0 )
{
msg_Err( p_access, "Failed to tune the frontend" );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
static int ScanStatsCallback( scan_t *p_scan, void *p_privdata, int *pi_snr )
{
stream_t *p_access = (stream_t *) p_privdata;
access_sys_t *sys = p_access->p_sys;
VLC_UNUSED(p_scan);
frontend_statistic_t stat;
if( !FrontendGetStatistic( &sys->dvb, &stat ) )
{
*pi_snr = stat.i_snr;
return VLC_SUCCESS;
}
return VLC_EGENERIC;
}
static int ScanFilterHandler( scan_t *p_scan, void *p_privdata, uint16_t i_pid, bool b_set )
{
stream_t *p_access = (stream_t *) p_privdata;
VLC_UNUSED(p_scan);
if( b_set )
FilterSet( p_access, i_pid, OTHER_TYPE );
return VLC_SUCCESS;
}
static int ScanReadCallback( scan_t *p_scan, void *p_privdata,
unsigned i_probe_timeout, size_t i_packets_max,
uint8_t *p_packet, size_t *pi_count )
{
stream_t *p_access = (stream_t *) p_privdata;
access_sys_t *p_sys = p_access->p_sys;
*pi_count = 0;
/* Initialize file descriptor sets */
struct pollfd ufds[2];
ufds[0].fd = p_sys->dvb.i_handle;
ufds[0].events = POLLIN;
ufds[1].fd = p_sys->dvb.i_frontend_handle;
ufds[1].events = POLLPRI;
frontend_status_t status;
FrontendGetStatus( &p_sys->dvb, &status );
bool b_has_lock = status.b_has_lock;
vlc_tick_t i_scan_start = mdate();
for( ; *pi_count == 0; )
{
/* Find if some data is available */
int i_ret;
vlc_tick_t i_timeout = b_has_lock ? i_probe_timeout:
DVB_SCAN_MAX_LOCK_TIME;
do
{
vlc_tick_t i_poll_timeout = i_scan_start - mdate() + i_timeout;
i_ret = 0;
if( vlc_killed() || scan_IsCancelled( p_scan ) )
break;
if( i_poll_timeout >= 0 )
i_ret = vlc_poll_i11e( ufds, 2, i_poll_timeout / 1000 );
}
while( i_ret < 0 && errno == EINTR );
if( i_ret < 0 )
{
return VLC_EGENERIC;
}
else if( i_ret == 0 )
{
return VLC_ENOITEM;
}
if( ufds[1].revents )
{
FrontendPoll( VLC_OBJECT(p_access), &p_sys->dvb );
FrontendGetStatus( &p_sys->dvb, &status );
if( status.b_has_lock && !b_has_lock )
{
i_scan_start = mdate();
b_has_lock = true;
}
}
if ( ufds[0].revents )
{
ssize_t i_read = read( p_sys->dvb.i_handle, p_packet, TS_PACKET_SIZE * i_packets_max );
if( i_read < 0 )
{
msg_Warn( p_access, "read failed: %s", vlc_strerror_c(errno) );
break;
}
else
{
*pi_count = i_read / TS_PACKET_SIZE;
}
}
}
return VLC_SUCCESS;
}
/*****************************************************************************
* BlockScan:
*****************************************************************************/
static block_t *BlockScan( stream_t *p_access, bool *restrict eof )
{
access_sys_t *p_sys = p_access->p_sys;
scan_t *p_scan = p_sys->scan;
block_t *p_block = NULL;
if( scan_Run( p_scan ) != VLC_SUCCESS )
{
if( !p_sys->done )
{
msg_Info( p_access, "Scanning finished" );
p_sys->done = true;
p_block = scan_GetM3U( p_scan );
}
*eof = true;
}
return p_block;
}
/*****************************************************************************
* Control:
*****************************************************************************/
static int Control( stream_t *p_access, int i_query, va_list args )
{
access_sys_t *sys = p_access->p_sys;
bool *pb_bool;
int64_t *pi_64;
double *pf1, *pf2;
frontend_statistic_t stat;
switch( i_query )
{
case STREAM_CAN_SEEK:
case STREAM_CAN_FASTSEEK:
case STREAM_CAN_PAUSE:
case STREAM_CAN_CONTROL_PACE:
pb_bool = va_arg( args, bool * );
*pb_bool = false;
break;
case STREAM_GET_CONTENT_TYPE:
*va_arg( args, char** ) = strdup("application/vnd.apple.mpegurl"); // m3u8
return VLC_SUCCESS;
case STREAM_GET_PTS_DELAY:
pi_64 = va_arg( args, int64_t * );
*pi_64 = DEFAULT_PTS_DELAY;
break;
case STREAM_GET_SIGNAL:
pf1 = va_arg( args, double * );
pf2 = va_arg( args, double * );
*pf1 = *pf2 = 0;
if( !FrontendGetStatistic( &sys->dvb, &stat ) )
{
*pf1 = (double)stat.i_snr / 65535.0;
*pf2 = (double)stat.i_signal_strenth / 65535.0;
}
break;
default:
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* FilterSet/FilterUnset:
*****************************************************************************/
static void FilterSet( stream_t *p_access, int i_pid, int i_type )
{
access_sys_t *p_sys = p_access->p_sys;
int i;
/* Find first free slot */
for( i = 0; i < MAX_DEMUX; i++ )
{
if( !p_sys->p_demux_handles[i].i_type )
break;
if( p_sys->p_demux_handles[i].i_pid == i_pid )
return; /* Already set */
}
if( i >= MAX_DEMUX )
{
msg_Err( p_access, "no free p_demux_handles !" );
return;
}
if( DMXSetFilter( VLC_OBJECT(p_access), i_pid,
&p_sys->p_demux_handles[i].i_handle, i_type ) )
{
msg_Err( p_access, "DMXSetFilter failed" );
return;
}
p_sys->p_demux_handles[i].i_type = i_type;
p_sys->p_demux_handles[i].i_pid = i_pid;
}
static void FilterUnset( stream_t *p_access, int i_max )
{
access_sys_t *p_sys = p_access->p_sys;
for( int i = 0; i < i_max; i++ )
{
if( p_sys->p_demux_handles[i].i_type )
{
DMXUnsetFilter( VLC_OBJECT(p_access), p_sys->p_demux_handles[i].i_handle );
p_sys->p_demux_handles[i].i_type = 0;
}
}
}
/*****************************************************************************
* VarInit/ParseMRL:
*****************************************************************************/
static void VarInit( stream_t *p_access )
{
var_Create( p_access, "dvb-adapter", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-frequency", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-inversion", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-probe", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
/* */
var_Create( p_access, "dvb-satellite", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-satno", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-voltage", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-high-voltage", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-tone", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-srate", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-lnb-lof1", VLC_VAR_INTEGER );
var_Create( p_access, "dvb-lnb-lof2", VLC_VAR_INTEGER );
var_Create( p_access, "dvb-lnb-slof", VLC_VAR_INTEGER );
/* */
var_Create( p_access, "dvb-bandwidth", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-transmission", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-hierarchy", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
}
/* */
static int ParseMRL( stream_t *p_access )
{
char *psz_dup = strdup( p_access->psz_location );
char *psz_parser = psz_dup;
vlc_value_t val;
#define GET_OPTION_INT( option ) \
if ( !strncmp( psz_parser, option "=", strlen(option "=") ) ) \
{ \
val.i_int = strtol( psz_parser + strlen(option "="), &psz_parser, \
0 ); \
var_Set( p_access, "dvb-" option, val ); \
}
#define GET_OPTION_BOOL( option ) \
if ( !strncmp( psz_parser, option "=", strlen(option "=") ) ) \
{ \
val.b_bool = strtol( psz_parser + strlen(option "="), &psz_parser, \
0 ); \
var_Set( p_access, "dvb-" option, val ); \
}
#define GET_OPTION_STRING( option ) \
if ( !strncmp( psz_parser, option "=", strlen( option "=" ) ) ) \
{ \
psz_parser += strlen( option "=" ); \
val.psz_string = psz_parser; \
char *p_save; \
char *tok = strtok_r(val.psz_string, ":", &p_save); \
val.psz_string[tok - val.psz_string - 1] = 0; \
var_Set( p_access, "dvb-" option, val ); \
psz_parser += strlen( val.psz_string ); \
}
while( *psz_parser )
{
GET_OPTION_INT("adapter")
else GET_OPTION_INT("device")
else GET_OPTION_INT("frequency")
else GET_OPTION_INT("inversion")
else GET_OPTION_BOOL("probe")
else GET_OPTION_BOOL("budget-mode")
else GET_OPTION_STRING("satellite")
else GET_OPTION_INT("voltage")
else GET_OPTION_BOOL("high-voltage")
else GET_OPTION_INT("tone")
else GET_OPTION_INT("satno")
else GET_OPTION_INT("srate")
else GET_OPTION_INT("lnb-lof1")
else GET_OPTION_INT("lnb-lof2")
else GET_OPTION_INT("lnb-slof")
else GET_OPTION_INT("bandwidth")
else GET_OPTION_INT("transmission")
else GET_OPTION_INT("hierarchy")
/* Redundant with voltage but much easier to use */
else if( !strncmp( psz_parser, "polarization=",
strlen( "polarization=" ) ) )
{
psz_parser += strlen( "polarization=" );
if ( *psz_parser == 'V' || *psz_parser == 'v' )
val.i_int = 13;
else if ( *psz_parser == 'H' || *psz_parser == 'h' )
val.i_int = 18;
else
{
msg_Warn( p_access, "illegal polarization %c", *psz_parser );
free( psz_dup );
return VLC_EGENERIC;
}
var_Set( p_access, "dvb-voltage", val );
}
else
{
msg_Warn( p_access, "unknown option (%s)", psz_parser );
free( psz_dup );
return VLC_EGENERIC;
}
if ( *psz_parser )
psz_parser++;
}
#undef GET_OPTION_INT
#undef GET_OPTION_BOOL
#undef GET_OPTION_STRING
free( psz_dup );
return VLC_SUCCESS;
}