d017686fb803c2272a6a57e184a811b5b019ab8c
[vlc.git] / modules / access / dvb / access.c
1 /*****************************************************************************
2  * access.c: DVB card input v4l2 only
3  *****************************************************************************
4  * Copyright (C) 1998-2010 VLC authors and VideoLAN
5  *
6  * Authors: Johan Bilien <jobi@via.ecp.fr>
7  *          Jean-Paul Saman <jpsaman _at_ videolan _dot_ org>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Laurent Aimar <fenrir@via.ecp.fr>
10  *          David Kaplan <david@2of1.org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_access.h>
38 #include <vlc_interrupt.h>
39 #include <vlc_dialog.h>
40
41 #include <sys/types.h>
42 #include <poll.h>
43
44 #include <errno.h>
45
46 #include "dvb.h"
47 #include "scan.h"
48
49 typedef struct
50 {
51     demux_handle_t p_demux_handles[MAX_DEMUX];
52     dvb_sys_t dvb;
53
54     /* Scan */
55     struct scan_t *scan;
56     bool done;
57 } access_sys_t;
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 static int  Open( vlc_object_t *p_this );
63 static void Close( vlc_object_t *p_this );
64
65 #define PROBE_TEXT N_("Probe DVB card for capabilities")
66 #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.")
67
68 /* Satellite */
69 #define SATELLITE_TEXT N_("Satellite scanning config")
70 #define SATELLITE_LONGTEXT N_("Filename of config file in share/dvb/dvb-s.")
71
72 #define SCANLIST_TEXT N_("Scan tuning list")
73 #define SCANLIST_LONGTEXT N_("Filename containing initial scan tuning data.")
74
75 #define SCAN_NIT_TEXT N_("Use NIT for scanning services")
76
77 vlc_module_begin ()
78     set_shortname( N_("DVB") )
79     set_description( N_("DVB input with v4l2 support") )
80     set_category( CAT_INPUT )
81     set_subcategory( SUBCAT_INPUT_ACCESS )
82
83     add_bool( "dvb-probe", true, PROBE_TEXT, PROBE_LONGTEXT, true )
84     /* DVB-S (satellite) */
85     add_string( "dvb-satellite", NULL, SATELLITE_TEXT, SATELLITE_LONGTEXT,
86                 true )
87     add_string( "dvb-scanlist", NULL, SCANLIST_TEXT, SCANLIST_LONGTEXT,
88                 true )
89     add_bool( "dvb-scan-nit", true, SCAN_NIT_TEXT, NULL, true )
90
91     set_capability( "access", 0 )
92     add_shortcut( "dvb",                        /* Generic name */
93                   "dvb-s", "qpsk", "satellite", /* Satellite */
94                   "dvb-c", "cable",             /* Cable */
95                   "dvb-t", "terrestrial" )      /* Terrestrial */
96
97     set_callbacks( Open, Close )
98
99 vlc_module_end ()
100
101
102 /*****************************************************************************
103  * Local prototypes
104  *****************************************************************************/
105 static int Control( stream_t *, int, va_list );
106
107 static block_t *BlockScan( stream_t *, bool * );
108
109 #define DVB_SCAN_MAX_LOCK_TIME VLC_TICK_FROM_SEC(2)
110
111 static void FilterUnset( stream_t *, int i_max );
112 static void FilterSet( stream_t *, int i_pid, int i_type );
113
114 static void VarInit( stream_t * );
115 static int  ParseMRL( stream_t * );
116
117 static int ScanFrontendTuningHandler( scan_t *, void *, const scan_tuner_config_t * );
118 static int ScanFilterHandler( scan_t *, void *, uint16_t, bool );
119 static int ScanStatsCallback( scan_t *p_scan, void *p_privdata, int *pi_snr );
120 static int ScanReadCallback( scan_t *, void *,  unsigned, size_t, uint8_t *, size_t *);
121
122 /*****************************************************************************
123  * Open: open the frontend device
124  *****************************************************************************/
125 static int Open( vlc_object_t *p_this )
126 {
127     stream_t     *p_access = (stream_t*)p_this;
128     access_sys_t *p_sys;
129
130     if( p_access->b_preparsing )
131         return VLC_EGENERIC;
132
133     p_access->p_sys = p_sys = calloc( 1, sizeof( access_sys_t ) );
134     if( !p_sys )
135         return VLC_ENOMEM;
136
137     /* Create all variables */
138     VarInit( p_access );
139
140     /* Parse the command line */
141     if( ParseMRL( p_access ) )
142     {
143         free( p_sys );
144         return VLC_EGENERIC;
145     }
146
147     bool b_scan_mode = var_GetInteger( p_access, "dvb-frequency" ) == 0;
148     if( b_scan_mode )
149     {
150         msg_Dbg( p_access, "DVB scan mode selected" );
151         p_access->pf_block = BlockScan;
152     }
153     else
154     {
155         free( p_sys );
156         return VLC_EGENERIC; /* let the DTV plugin do the work */
157     }
158
159     /* Getting frontend info */
160     if( FrontendOpen( p_this, &p_sys->dvb, p_access->psz_name ) )
161     {
162         free( p_sys );
163         return VLC_EGENERIC;
164     }
165
166     /* Opening DVR device */
167     if( DVROpen( p_this, &p_sys->dvb ) < 0 )
168     {
169         FrontendClose( p_this, &p_sys->dvb );
170         free( p_sys );
171         return VLC_EGENERIC;
172     }
173
174     scan_parameter_t parameter;
175     scan_t *p_scan;
176
177     scan_parameter_Init( &parameter );
178
179     parameter.b_use_nit = var_InheritBool( p_access, "dvb-scan-nit" );
180
181     if( FrontendFillScanParameter( p_this, &p_sys->dvb, &parameter ) ||
182             (p_scan = scan_New( VLC_OBJECT(p_access), &parameter,
183                                 ScanFrontendTuningHandler,
184                                 ScanStatsCallback,
185                                 ScanFilterHandler,
186                                 ScanReadCallback,
187                                 p_access )) == NULL )
188     {
189         scan_parameter_Clean( &parameter );
190         Close( VLC_OBJECT(p_access) );
191         return VLC_EGENERIC;
192     }
193
194     scan_parameter_Clean( &parameter );
195     p_sys->scan = p_scan;
196
197
198     /* Set up access */
199     p_access->pf_control = Control;
200
201     return VLC_SUCCESS;
202 }
203
204 /*****************************************************************************
205  * Close : Close the device
206  *****************************************************************************/
207 static void Close( vlc_object_t *p_this )
208 {
209     stream_t     *p_access = (stream_t*)p_this;
210     access_sys_t *p_sys = p_access->p_sys;
211
212     FilterUnset( p_access, MAX_DEMUX );
213
214     DVRClose( p_this, &p_sys->dvb );
215     FrontendClose( p_this, &p_sys->dvb );
216     scan_Destroy( p_sys->scan );
217
218     free( p_sys );
219 }
220
221 static int ScanFrontendTuningHandler( scan_t *p_scan, void *p_privdata,
222                                       const scan_tuner_config_t *p_cfg )
223 {
224     stream_t *p_access = (stream_t *) p_privdata;
225     access_sys_t *sys = p_access->p_sys;
226     VLC_UNUSED(p_scan);
227
228     var_SetInteger( p_access, "dvb-frequency", p_cfg->i_frequency );
229     var_SetInteger( p_access, "dvb-bandwidth", p_cfg->i_bandwidth );
230
231     if ( p_cfg->polarization != SCAN_POLARIZATION_NONE )
232         var_SetInteger( p_access, "dvb-voltage", p_cfg->polarization == SCAN_POLARIZATION_HORIZONTAL ? 18 : 13 );
233
234     if ( p_cfg->i_symbolrate )
235         var_SetInteger( p_access, "dvb-srate", p_cfg->i_symbolrate );
236
237     msg_Dbg( p_access, "Scanning frequency %d", p_cfg->i_frequency );
238     msg_Dbg( p_access, " bandwidth %d", p_cfg->i_bandwidth );
239
240     /* Setting frontend parameters for tuning the hardware */
241     if( FrontendSet( VLC_OBJECT(p_access), &sys->dvb ) < 0 )
242     {
243         msg_Err( p_access, "Failed to tune the frontend" );
244         return VLC_EGENERIC;
245     }
246
247     return VLC_SUCCESS;
248 }
249
250 static int ScanStatsCallback( scan_t *p_scan, void *p_privdata, int *pi_snr )
251 {
252     stream_t *p_access = (stream_t *) p_privdata;
253     access_sys_t *sys = p_access->p_sys;
254     VLC_UNUSED(p_scan);
255
256     frontend_statistic_t stat;
257     if( !FrontendGetStatistic( &sys->dvb, &stat ) )
258     {
259         *pi_snr = stat.i_snr;
260         return VLC_SUCCESS;
261     }
262
263     return VLC_EGENERIC;
264 }
265
266 static int ScanFilterHandler( scan_t *p_scan, void *p_privdata, uint16_t i_pid, bool b_set )
267 {
268     stream_t *p_access = (stream_t *) p_privdata;
269     VLC_UNUSED(p_scan);
270
271     if( b_set )
272         FilterSet( p_access, i_pid, OTHER_TYPE );
273
274     return VLC_SUCCESS;
275 }
276
277 static int ScanReadCallback( scan_t *p_scan, void *p_privdata,
278                              unsigned i_probe_timeout, size_t i_packets_max,
279                              uint8_t *p_packet, size_t *pi_count )
280 {
281     stream_t *p_access = (stream_t *) p_privdata;
282     access_sys_t *p_sys = p_access->p_sys;
283     *pi_count = 0;
284
285     /* Initialize file descriptor sets */
286     struct pollfd ufds[2];
287
288     ufds[0].fd = p_sys->dvb.i_handle;
289     ufds[0].events = POLLIN;
290     ufds[1].fd = p_sys->dvb.i_frontend_handle;
291     ufds[1].events = POLLPRI;
292
293     frontend_status_t status;
294     FrontendGetStatus( &p_sys->dvb, &status );
295     bool b_has_lock = status.b_has_lock;
296
297     vlc_tick_t i_scan_start = vlc_tick_now();
298
299     for( ; *pi_count == 0; )
300     {
301         /* Find if some data is available */
302         int i_ret;
303
304         vlc_tick_t i_timeout = b_has_lock ? i_probe_timeout:
305                                          DVB_SCAN_MAX_LOCK_TIME;
306
307         do
308         {
309             vlc_tick_t i_poll_timeout = i_scan_start - vlc_tick_now() + i_timeout;
310
311             i_ret = 0;
312
313             if( vlc_killed() || scan_IsCancelled( p_scan ) )
314                 break;
315
316             if( i_poll_timeout >= 0 )
317                 i_ret = vlc_poll_i11e( ufds, 2, i_poll_timeout / 1000 );
318         }
319         while( i_ret < 0 && errno == EINTR );
320
321         if( i_ret < 0 )
322         {
323             return VLC_EGENERIC;
324         }
325         else if( i_ret == 0 )
326         {
327             return VLC_ENOITEM;
328         }
329
330         if( ufds[1].revents )
331         {
332             FrontendPoll( VLC_OBJECT(p_access), &p_sys->dvb );
333
334             FrontendGetStatus( &p_sys->dvb, &status );
335             if( status.b_has_lock && !b_has_lock )
336             {
337                 i_scan_start = vlc_tick_now();
338                 b_has_lock = true;
339             }
340         }
341
342         if ( ufds[0].revents )
343         {
344             ssize_t i_read = read( p_sys->dvb.i_handle, p_packet, TS_PACKET_SIZE * i_packets_max );
345             if( i_read < 0 )
346             {
347                 msg_Warn( p_access, "read failed: %s", vlc_strerror_c(errno) );
348                 break;
349             }
350             else
351             {
352                 *pi_count = i_read / TS_PACKET_SIZE;
353             }
354         }
355     }
356
357     return VLC_SUCCESS;
358 }
359
360 /*****************************************************************************
361  * BlockScan:
362  *****************************************************************************/
363 static block_t *BlockScan( stream_t *p_access, bool *restrict eof )
364 {
365     access_sys_t *p_sys = p_access->p_sys;
366     scan_t *p_scan = p_sys->scan;
367     block_t *p_block = NULL;
368
369     if( scan_Run( p_scan ) != VLC_SUCCESS )
370     {
371         if( !p_sys->done )
372         {
373              msg_Info( p_access, "Scanning finished" );
374              p_sys->done = true;
375              p_block = scan_GetM3U( p_scan );
376         }
377         *eof = true;
378     }
379
380     return p_block;
381 }
382
383 /*****************************************************************************
384  * Control:
385  *****************************************************************************/
386 static int Control( stream_t *p_access, int i_query, va_list args )
387 {
388     access_sys_t *sys = p_access->p_sys;
389     bool         *pb_bool;
390     double       *pf1, *pf2;
391     frontend_statistic_t stat;
392
393     switch( i_query )
394     {
395         case STREAM_CAN_SEEK:
396         case STREAM_CAN_FASTSEEK:
397         case STREAM_CAN_PAUSE:
398         case STREAM_CAN_CONTROL_PACE:
399             pb_bool = va_arg( args, bool * );
400             *pb_bool = false;
401             break;
402
403         case STREAM_GET_CONTENT_TYPE:
404             *va_arg( args, char** ) = strdup("application/vnd.apple.mpegurl"); // m3u8
405             return VLC_SUCCESS;
406
407         case STREAM_GET_PTS_DELAY:
408             *va_arg( args, vlc_tick_t * ) = DEFAULT_PTS_DELAY;
409             break;
410
411         case STREAM_GET_SIGNAL:
412             pf1 = va_arg( args, double * );
413             pf2 = va_arg( args, double * );
414
415             *pf1 = *pf2 = 0;
416             if( !FrontendGetStatistic( &sys->dvb, &stat ) )
417             {
418                 *pf1 = (double)stat.i_snr / 65535.0;
419                 *pf2 = (double)stat.i_signal_strenth / 65535.0;
420             }
421             break;
422
423         default:
424             return VLC_EGENERIC;
425
426     }
427     return VLC_SUCCESS;
428 }
429
430 /*****************************************************************************
431  * FilterSet/FilterUnset:
432  *****************************************************************************/
433 static void FilterSet( stream_t *p_access, int i_pid, int i_type )
434 {
435     access_sys_t *p_sys = p_access->p_sys;
436     int i;
437
438     /* Find first free slot */
439     for( i = 0; i < MAX_DEMUX; i++ )
440     {
441         if( !p_sys->p_demux_handles[i].i_type )
442             break;
443
444         if( p_sys->p_demux_handles[i].i_pid == i_pid )
445             return; /* Already set */
446     }
447
448     if( i >= MAX_DEMUX )
449     {
450         msg_Err( p_access, "no free p_demux_handles !" );
451         return;
452     }
453
454     if( DMXSetFilter( VLC_OBJECT(p_access), i_pid,
455                            &p_sys->p_demux_handles[i].i_handle, i_type ) )
456     {
457         msg_Err( p_access, "DMXSetFilter failed" );
458         return;
459     }
460     p_sys->p_demux_handles[i].i_type = i_type;
461     p_sys->p_demux_handles[i].i_pid = i_pid;
462 }
463
464 static void FilterUnset( stream_t *p_access, int i_max )
465 {
466     access_sys_t *p_sys = p_access->p_sys;
467
468     for( int i = 0; i < i_max; i++ )
469     {
470         if( p_sys->p_demux_handles[i].i_type )
471         {
472             DMXUnsetFilter( VLC_OBJECT(p_access), p_sys->p_demux_handles[i].i_handle );
473             p_sys->p_demux_handles[i].i_type = 0;
474         }
475     }
476 }
477
478 /*****************************************************************************
479  * VarInit/ParseMRL:
480  *****************************************************************************/
481 static void VarInit( stream_t *p_access )
482 {
483     var_Create( p_access, "dvb-adapter", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
484     var_Create( p_access, "dvb-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
485     var_Create( p_access, "dvb-frequency", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
486     var_Create( p_access, "dvb-inversion", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
487     var_Create( p_access, "dvb-probe", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
488
489     /* */
490     var_Create( p_access, "dvb-satellite", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
491     var_Create( p_access, "dvb-satno", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
492     var_Create( p_access, "dvb-voltage", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
493     var_Create( p_access, "dvb-high-voltage", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
494     var_Create( p_access, "dvb-tone", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
495     var_Create( p_access, "dvb-srate", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
496     var_Create( p_access, "dvb-lnb-lof1", VLC_VAR_INTEGER );
497     var_Create( p_access, "dvb-lnb-lof2", VLC_VAR_INTEGER );
498     var_Create( p_access, "dvb-lnb-slof", VLC_VAR_INTEGER );
499
500     /* */
501     var_Create( p_access, "dvb-bandwidth", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
502     var_Create( p_access, "dvb-transmission", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
503     var_Create( p_access, "dvb-hierarchy", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
504 }
505
506 /* */
507 static int ParseMRL( stream_t *p_access )
508 {
509     char *psz_dup = strdup( p_access->psz_location );
510     char *psz_parser = psz_dup;
511     vlc_value_t         val;
512
513 #define GET_OPTION_INT( option )                                            \
514     if ( !strncmp( psz_parser, option "=", strlen(option "=") ) )           \
515     {                                                                       \
516         val.i_int = strtol( psz_parser + strlen(option "="), &psz_parser,   \
517                             0 );                                            \
518         var_Set( p_access, "dvb-" option, val );                            \
519     }
520
521 #define GET_OPTION_BOOL( option )                                           \
522     if ( !strncmp( psz_parser, option "=", strlen(option "=") ) )           \
523     {                                                                       \
524         val.b_bool = strtol( psz_parser + strlen(option "="), &psz_parser,  \
525                              0 );                                           \
526         var_Set( p_access, "dvb-" option, val );                            \
527     }
528
529 #define GET_OPTION_STRING( option )                                         \
530     if ( !strncmp( psz_parser, option "=", strlen( option "=" ) ) )         \
531     {                                                                       \
532         psz_parser += strlen( option "=" );                                 \
533         val.psz_string = psz_parser;                                        \
534         char *p_save;                                                       \
535         char *tok = strtok_r(val.psz_string, ":", &p_save);                 \
536         val.psz_string[tok - val.psz_string - 1] = 0;                       \
537         var_Set( p_access, "dvb-" option, val );                            \
538         psz_parser += strlen( val.psz_string );                             \
539     }
540
541     while( *psz_parser )
542     {
543         GET_OPTION_INT("adapter")
544         else GET_OPTION_INT("device")
545         else GET_OPTION_INT("frequency")
546         else GET_OPTION_INT("inversion")
547         else GET_OPTION_BOOL("probe")
548         else GET_OPTION_BOOL("budget-mode")
549
550         else GET_OPTION_STRING("satellite")
551         else GET_OPTION_INT("voltage")
552         else GET_OPTION_BOOL("high-voltage")
553         else GET_OPTION_INT("tone")
554         else GET_OPTION_INT("satno")
555         else GET_OPTION_INT("srate")
556         else GET_OPTION_INT("lnb-lof1")
557         else GET_OPTION_INT("lnb-lof2")
558         else GET_OPTION_INT("lnb-slof")
559
560         else GET_OPTION_INT("bandwidth")
561         else GET_OPTION_INT("transmission")
562         else GET_OPTION_INT("hierarchy")
563
564         /* Redundant with voltage but much easier to use */
565         else if( !strncmp( psz_parser, "polarization=",
566                            strlen( "polarization=" ) ) )
567         {
568             psz_parser += strlen( "polarization=" );
569             if ( *psz_parser == 'V' || *psz_parser == 'v' )
570                 val.i_int = 13;
571             else if ( *psz_parser == 'H' || *psz_parser == 'h' )
572                 val.i_int = 18;
573             else
574             {
575                 msg_Warn( p_access, "illegal polarization %c", *psz_parser );
576                 free( psz_dup );
577                 return VLC_EGENERIC;
578             }
579             var_Set( p_access, "dvb-voltage", val );
580         }
581         else
582         {
583             msg_Warn( p_access, "unknown option (%s)", psz_parser );
584             free( psz_dup );
585             return VLC_EGENERIC;
586         }
587
588         if ( *psz_parser )
589             psz_parser++;
590     }
591 #undef GET_OPTION_INT
592 #undef GET_OPTION_BOOL
593 #undef GET_OPTION_STRING
594
595     free( psz_dup );
596     return VLC_SUCCESS;
597 }
598