7763dc3fbbdff68feb92ab8c8ee2d1a8400903d0
[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_input.h>
39 #include <vlc_interrupt.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 /*****************************************************************************
50  * Module descriptor
51  *****************************************************************************/
52 static int  Open( vlc_object_t *p_this );
53 static void Close( vlc_object_t *p_this );
54
55 #define PROBE_TEXT N_("Probe DVB card for capabilities")
56 #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.")
57
58 /* Satellite */
59 #define SATELLITE_TEXT N_("Satellite scanning config")
60 #define SATELLITE_LONGTEXT N_("filename of config file in share/dvb/dvb-s")
61
62 #define SCANLIST_TEXT N_("Scan tuning list")
63 #define SCANLIST_LONGTEXT N_("filename containing initial scan tuning data")
64
65 #define SCAN_NIT_TEXT N_("Use NIT for scanning services")
66
67 vlc_module_begin ()
68     set_shortname( N_("DVB") )
69     set_description( N_("DVB input with v4l2 support") )
70     set_category( CAT_INPUT )
71     set_subcategory( SUBCAT_INPUT_ACCESS )
72
73     add_bool( "dvb-probe", true, PROBE_TEXT, PROBE_LONGTEXT, true )
74     /* DVB-S (satellite) */
75     add_string( "dvb-satellite", NULL, SATELLITE_TEXT, SATELLITE_LONGTEXT,
76                 true )
77     add_string( "dvb-scanlist", NULL, SCANLIST_TEXT, SCANLIST_LONGTEXT,
78                 true )
79     add_bool( "dvb-scan-nit", true, SCAN_NIT_TEXT, NULL, true )
80
81     set_capability( "access", 0 )
82     add_shortcut( "dvb",                        /* Generic name */
83                   "dvb-s", "qpsk", "satellite", /* Satellite */
84                   "dvb-c", "cable",             /* Cable */
85                   "dvb-t", "terrestrial" )      /* Terrestrial */
86
87     set_callbacks( Open, Close )
88
89 vlc_module_end ()
90
91
92 /*****************************************************************************
93  * Local prototypes
94  *****************************************************************************/
95 static int Control( access_t *, int, va_list );
96
97 static block_t *BlockScan( access_t * );
98
99 #define DVB_SCAN_MAX_SIGNAL_TIME (1000*1000)
100 #define DVB_SCAN_MAX_LOCK_TIME (5000*1000)
101
102 static void FilterUnset( access_t *, int i_max );
103 static void FilterSet( access_t *, int i_pid, int i_type );
104
105 static void VarInit( access_t * );
106 static int  ParseMRL( access_t * );
107
108 /*****************************************************************************
109  * Open: open the frontend device
110  *****************************************************************************/
111 static int Open( vlc_object_t *p_this )
112 {
113     access_t     *p_access = (access_t*)p_this;
114     access_sys_t *p_sys;
115
116     /* Only if selected */
117     if( *p_access->psz_access == '\0' )
118         return VLC_EGENERIC;
119
120     p_access->p_sys = p_sys = calloc( 1, sizeof( access_sys_t ) );
121     if( !p_sys )
122         return VLC_ENOMEM;
123
124     /* Create all variables */
125     VarInit( p_access );
126
127     /* Parse the command line */
128     if( ParseMRL( p_access ) )
129     {
130         free( p_sys );
131         return VLC_EGENERIC;
132     }
133
134     bool b_scan_mode = var_GetInteger( p_access, "dvb-frequency" ) == 0;
135     if( b_scan_mode )
136     {
137         msg_Dbg( p_access, "DVB scan mode selected" );
138         p_access->pf_block = BlockScan;
139     }
140     else
141         return VLC_EGENERIC; /* let the DTV plugin do the work */
142
143     /* Getting frontend info */
144     if( FrontendOpen( p_access) )
145     {
146         free( p_sys );
147         return VLC_EGENERIC;
148     }
149
150     /* Opening DVR device */
151     if( DVROpen( p_access ) < 0 )
152     {
153         FrontendClose( p_access );
154         free( p_sys );
155         return VLC_EGENERIC;
156     }
157
158     scan_parameter_t parameter;
159     scan_t *p_scan;
160
161     scan_parameter_Init( &parameter );
162
163     parameter.b_use_nit = var_InheritBool( p_access, "dvb-scan-nit" );
164
165     msg_Dbg( p_access, "setting filter on PAT/NIT/SDT (DVB only)" );
166     FilterSet( p_access, 0x00, OTHER_TYPE );    // PAT
167     FilterSet( p_access, 0x11, OTHER_TYPE );    // SDT
168     if( parameter.b_use_nit )
169         FilterSet( p_access, 0x10, OTHER_TYPE );    // NIT
170
171     if( FrontendFillScanParameter( p_access, &parameter ) ||
172             (p_scan = scan_New( VLC_OBJECT(p_access), &parameter )) == NULL )
173     {
174         scan_parameter_Clean( &parameter );
175         Close( VLC_OBJECT(p_access) );
176         return VLC_EGENERIC;
177     }
178     scan_parameter_Clean( &parameter );
179     p_sys->scan = p_scan;
180
181
182     /* Set up access */
183     p_access->pf_control = Control;
184     access_InitFields( p_access );
185
186     return VLC_SUCCESS;
187 }
188
189 /*****************************************************************************
190  * Close : Close the device
191  *****************************************************************************/
192 static void Close( vlc_object_t *p_this )
193 {
194     access_t     *p_access = (access_t*)p_this;
195     access_sys_t *p_sys = p_access->p_sys;
196
197     FilterUnset( p_access, MAX_DEMUX );
198
199     DVRClose( p_access );
200     FrontendClose( p_access );
201     scan_Destroy( p_sys->scan );
202
203     free( p_sys );
204 }
205
206 /*****************************************************************************
207  * BlockScan:
208  *****************************************************************************/
209 static block_t *BlockScan( access_t *p_access )
210 {
211     access_sys_t *p_sys = p_access->p_sys;
212     scan_t *p_scan = p_sys->scan;
213     scan_tuner_config_t cfg;
214
215     /* */
216     if( scan_Next( p_scan, &cfg ) )
217     {
218         const bool b_first_eof = !p_access->info.b_eof;
219
220         if( b_first_eof )
221             msg_Warn( p_access, "Scanning finished" );
222
223         /* */
224         p_access->info.b_eof = true;
225         return b_first_eof ? scan_GetM3U( p_scan ) : NULL;
226     }
227
228     /* */
229     scan_session_t *session = scan_session_New( p_scan, &cfg );
230     if( session == NULL )
231         return NULL;
232
233     /* */
234     msg_Dbg( p_access, "Scanning frequency %d", cfg.i_frequency );
235     var_SetInteger( p_access, "dvb-frequency", cfg.i_frequency );
236     msg_Dbg( p_access, " bandwidth %d", cfg.i_bandwidth );
237     var_SetInteger( p_access, "dvb-bandwidth", cfg.i_bandwidth );
238     if ( cfg.c_polarization )
239         var_SetInteger( p_access, "dvb-voltage", cfg.c_polarization == 'H' ? 18 : 13 );
240
241     if ( cfg.i_symbolrate )
242         var_SetInteger( p_access, "dvb-srate", cfg.i_symbolrate );
243
244     /* Setting frontend parameters for tuning the hardware */
245     if( FrontendSet( p_access ) < 0 )
246     {
247         msg_Err( p_access, "Failed to tune the frontend" );
248         //p_access->info.b_eof = true;
249         scan_session_Destroy( p_scan, session );
250         return NULL;
251     }
252
253     /* */
254     int64_t i_scan_start = mdate();
255     int64_t i_probe_start = 0;
256
257     bool b_has_dvb_signal = false;
258     bool b_has_lock = false;
259     int i_best_snr = -1;
260
261     /* Initialize file descriptor sets */
262     struct pollfd ufds[2];
263
264     ufds[0].fd = p_sys->i_handle;
265     ufds[0].events = POLLIN;
266     ufds[1].fd = p_sys->i_frontend_handle;
267     ufds[1].events = POLLPRI;
268
269     for ( ; ; )
270     {
271         frontend_status_t status;
272         FrontendGetStatus( p_access, &status );
273         b_has_dvb_signal |= status.b_has_carrier;
274         b_has_lock |= status.b_has_lock;
275
276         int64_t i_scan_end;
277         if( i_probe_start )
278         {
279             i_scan_end = i_probe_start + scan_session_GetTablesTimeout( session );
280         }
281         else
282         {
283             i_scan_end = i_scan_start;
284             if( !b_has_dvb_signal )
285                 i_scan_end += DVB_SCAN_MAX_SIGNAL_TIME;
286             else if( !b_has_lock )
287                 i_scan_end += DVB_SCAN_MAX_LOCK_TIME;
288         }
289
290         /* Find if some data is available */
291         int i_ret;
292
293         do
294         {
295             int64_t timeout = i_scan_end - mdate();
296
297             i_ret = 0;
298
299             if( vlc_killed() || scan_IsCancelled( p_scan ) )
300                 break;
301
302             if( timeout >= 0 )
303                 i_ret = vlc_poll_i11e( ufds, 2, timeout / 1000 );
304         }
305         while( i_ret < 0 && errno == EINTR );
306
307         if( i_ret < 0 )
308         {
309             msg_Err( p_access, "poll error: %s", vlc_strerror_c(errno) );
310             p_access->info.b_eof = true;
311             break;
312         }
313
314         if( i_ret == 0 )
315         {
316             msg_Dbg( p_access,
317                      "timed out scanning current frequency (s=%d l=%d)",
318                      b_has_dvb_signal, b_has_lock );
319             break;
320         }
321
322         if( ufds[1].revents )
323         {
324             frontend_statistic_t stat;
325
326             FrontendPoll( p_access );
327
328             if( !FrontendGetStatistic( p_access, &stat ) )
329             {
330                 if( stat.i_snr > i_best_snr )
331                 {
332                     i_best_snr = stat.i_snr;
333                     scan_session_SetSNR( session, i_best_snr );
334                 }
335             }
336         }
337
338         if ( p_sys->i_frontend_timeout && mdate() > p_sys->i_frontend_timeout )
339         {
340             msg_Warn( p_access, "no lock, tuning again" );
341             FrontendSet( p_access );
342         }
343
344         if ( ufds[0].revents )
345         {
346             if( i_probe_start == 0 )
347                 i_probe_start = mdate();
348
349             ssize_t i_read = read( p_sys->i_handle, &p_sys->packet, TS_PACKET_SIZE );
350             if( i_read <= 0 )
351             {
352                 msg_Warn( p_access, "read failed: %s", vlc_strerror_c(errno) );
353                 continue;
354             }
355
356             /* */
357             if( i_read == TS_PACKET_SIZE &&
358                 scan_session_Push( session, &p_sys->packet ) )
359             {
360                 msg_Dbg( p_access, "finished scanning current frequency" );
361                 break;
362             }
363         }
364     }
365
366     scan_session_Destroy( p_scan, session );
367     return NULL;
368 }
369
370 /*****************************************************************************
371  * Control:
372  *****************************************************************************/
373 static int Control( access_t *p_access, int i_query, va_list args )
374 {
375     bool         *pb_bool;
376     int64_t      *pi_64;
377     double       *pf1, *pf2;
378     frontend_statistic_t stat;
379
380     switch( i_query )
381     {
382         case ACCESS_CAN_SEEK:
383         case ACCESS_CAN_FASTSEEK:
384         case ACCESS_CAN_PAUSE:
385         case ACCESS_CAN_CONTROL_PACE:
386             pb_bool = (bool*)va_arg( args, bool* );
387             *pb_bool = false;
388             break;
389
390         case ACCESS_GET_CONTENT_TYPE:
391             *va_arg( args, char** ) = strdup("application/vnd.apple.mpegurl"); // m3u8
392             return VLC_SUCCESS;
393
394         case ACCESS_GET_PTS_DELAY:
395             pi_64 = (int64_t*)va_arg( args, int64_t * );
396             *pi_64 = DEFAULT_PTS_DELAY;
397             break;
398
399         case ACCESS_GET_SIGNAL:
400             pf1 = (double*)va_arg( args, double * );
401             pf2 = (double*)va_arg( args, double * );
402
403             *pf1 = *pf2 = 0;
404             if( !FrontendGetStatistic( p_access, &stat ) )
405             {
406                 *pf1 = (double)stat.i_snr / 65535.0;
407                 *pf2 = (double)stat.i_signal_strenth / 65535.0;
408             }
409             break;
410
411         default:
412             return VLC_EGENERIC;
413
414     }
415     return VLC_SUCCESS;
416 }
417
418 /*****************************************************************************
419  * FilterSet/FilterUnset:
420  *****************************************************************************/
421 static void FilterSet( access_t *p_access, int i_pid, int i_type )
422 {
423     access_sys_t *p_sys = p_access->p_sys;
424     int i;
425
426     /* Find first free slot */
427     for( i = 0; i < MAX_DEMUX; i++ )
428     {
429         if( !p_sys->p_demux_handles[i].i_type )
430             break;
431
432         if( p_sys->p_demux_handles[i].i_pid == i_pid )
433             return; /* Already set */
434     }
435
436     if( i >= MAX_DEMUX )
437     {
438         msg_Err( p_access, "no free p_demux_handles !" );
439         return;
440     }
441
442     if( DMXSetFilter( p_access, i_pid,
443                            &p_sys->p_demux_handles[i].i_handle, i_type ) )
444     {
445         msg_Err( p_access, "DMXSetFilter failed" );
446         return;
447     }
448     p_sys->p_demux_handles[i].i_type = i_type;
449     p_sys->p_demux_handles[i].i_pid = i_pid;
450 }
451
452 static void FilterUnset( access_t *p_access, int i_max )
453 {
454     access_sys_t *p_sys = p_access->p_sys;
455     int i;
456
457     for( i = 0; i < i_max; i++ )
458     {
459         if( p_sys->p_demux_handles[i].i_type )
460         {
461             DMXUnsetFilter( p_access, p_sys->p_demux_handles[i].i_handle );
462             p_sys->p_demux_handles[i].i_type = 0;
463         }
464     }
465 }
466
467 /*****************************************************************************
468  * VarInit/ParseMRL:
469  *****************************************************************************/
470 static void VarInit( access_t *p_access )
471 {
472     var_Create( p_access, "dvb-adapter", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
473     var_Create( p_access, "dvb-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
474     var_Create( p_access, "dvb-frequency", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
475     var_Create( p_access, "dvb-inversion", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
476     var_Create( p_access, "dvb-probe", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
477
478     /* */
479     var_Create( p_access, "dvb-satellite", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
480     var_Create( p_access, "dvb-satno", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
481     var_Create( p_access, "dvb-voltage", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
482     var_Create( p_access, "dvb-high-voltage", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
483     var_Create( p_access, "dvb-tone", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
484     var_Create( p_access, "dvb-srate", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
485     var_Create( p_access, "dvb-lnb-lof1", VLC_VAR_INTEGER );
486     var_Create( p_access, "dvb-lnb-lof2", VLC_VAR_INTEGER );
487     var_Create( p_access, "dvb-lnb-slof", VLC_VAR_INTEGER );
488
489     /* */
490     var_Create( p_access, "dvb-bandwidth", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
491     var_Create( p_access, "dvb-transmission", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
492     var_Create( p_access, "dvb-hierarchy", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
493 }
494
495 /* */
496 static int ParseMRL( access_t *p_access )
497 {
498     char *psz_dup = strdup( p_access->psz_location );
499     char *psz_parser = psz_dup;
500     vlc_value_t         val;
501
502 #define GET_OPTION_INT( option )                                            \
503     if ( !strncmp( psz_parser, option "=", strlen(option "=") ) )           \
504     {                                                                       \
505         val.i_int = strtol( psz_parser + strlen(option "="), &psz_parser,   \
506                             0 );                                            \
507         var_Set( p_access, "dvb-" option, val );                            \
508     }
509
510 #define GET_OPTION_BOOL( option )                                           \
511     if ( !strncmp( psz_parser, option "=", strlen(option "=") ) )           \
512     {                                                                       \
513         val.b_bool = strtol( psz_parser + strlen(option "="), &psz_parser,  \
514                              0 );                                           \
515         var_Set( p_access, "dvb-" option, val );                            \
516     }
517
518 #define GET_OPTION_STRING( option )                                         \
519     if ( !strncmp( psz_parser, option "=", strlen( option "=" ) ) )         \
520     {                                                                       \
521         psz_parser += strlen( option "=" );                                 \
522         val.psz_string = psz_parser;                                        \
523         char *p_save;                                                       \
524         char *tok = strtok_r(val.psz_string, ":", &p_save);                 \
525         val.psz_string[tok - val.psz_string - 1] = 0;                       \
526         var_Set( p_access, "dvb-" option, val );                            \
527         psz_parser += strlen( val.psz_string );                             \
528     }
529
530     while( *psz_parser )
531     {
532         GET_OPTION_INT("adapter")
533         else GET_OPTION_INT("device")
534         else GET_OPTION_INT("frequency")
535         else GET_OPTION_INT("inversion")
536         else GET_OPTION_BOOL("probe")
537         else GET_OPTION_BOOL("budget-mode")
538
539         else GET_OPTION_STRING("satellite")
540         else GET_OPTION_INT("voltage")
541         else GET_OPTION_BOOL("high-voltage")
542         else GET_OPTION_INT("tone")
543         else GET_OPTION_INT("satno")
544         else GET_OPTION_INT("srate")
545         else GET_OPTION_INT("lnb-lof1")
546         else GET_OPTION_INT("lnb-lof2")
547         else GET_OPTION_INT("lnb-slof")
548
549         else GET_OPTION_INT("bandwidth")
550         else GET_OPTION_INT("transmission")
551         else GET_OPTION_INT("hierarchy")
552
553         /* Redundant with voltage but much easier to use */
554         else if( !strncmp( psz_parser, "polarization=",
555                            strlen( "polarization=" ) ) )
556         {
557             psz_parser += strlen( "polarization=" );
558             if ( *psz_parser == 'V' || *psz_parser == 'v' )
559                 val.i_int = 13;
560             else if ( *psz_parser == 'H' || *psz_parser == 'h' )
561                 val.i_int = 18;
562             else
563             {
564                 msg_Err( p_access, "illegal polarization %c", *psz_parser );
565                 free( psz_dup );
566                 return VLC_EGENERIC;
567             }
568             var_Set( p_access, "dvb-voltage", val );
569         }
570         else
571         {
572             msg_Err( p_access, "unknown option (%s)", psz_parser );
573             free( psz_dup );
574             return VLC_EGENERIC;
575         }
576
577         if ( *psz_parser )
578             psz_parser++;
579     }
580 #undef GET_OPTION_INT
581 #undef GET_OPTION_BOOL
582 #undef GET_OPTION_STRING
583
584     free( psz_dup );
585     return VLC_SUCCESS;
586 }
587