]> git.sesse.net Git - vlc/blob - modules/access/dvb/scan.c
Cosmetics.
[vlc] / modules / access / dvb / scan.c
1 /*****************************************************************************
2  * scan.c: DVB scanner helpers
3  *****************************************************************************
4  * Copyright (C) 2008 the VideoLAN team
5  *
6  * Authors: Laurent Aimar <fenrir@videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_access.h>
33 #include <vlc_interface.h>
34
35 #ifdef HAVE_UNISTD_H
36 #   include <unistd.h>
37 #endif
38
39 #include <fcntl.h>
40 #include <sys/types.h>
41 #include <sys/poll.h>
42
43 #include <errno.h>
44
45 /* Include dvbpsi headers */
46 #ifdef HAVE_DVBPSI_DR_H
47 #   include <dvbpsi/dvbpsi.h>
48 #   include <dvbpsi/descriptor.h>
49 #   include <dvbpsi/pat.h>
50 #   include <dvbpsi/pmt.h>
51 #   include <dvbpsi/dr.h>
52 #   include <dvbpsi/psi.h>
53 #   include <dvbpsi/demux.h>
54 #   include <dvbpsi/sdt.h>
55 #   include <dvbpsi/nit.h>
56 #else
57 #   include "dvbpsi.h"
58 #   include "descriptor.h"
59 #   include "tables/pat.h"
60 #   include "tables/pmt.h"
61 #   include "descriptors/dr.h"
62 #   include "psi.h"
63 #   include "demux.h"
64 #   include "sdt.h"
65 #   include "nit.h"
66 #endif
67
68 #ifdef ENABLE_HTTPD
69 #   include "vlc_httpd.h"
70 #endif
71
72 #include "dvb.h"
73
74 /* */
75 scan_service_t *scan_service_New( int i_program, const scan_configuration_t *p_cfg  )
76 {
77     scan_service_t *p_srv = malloc( sizeof(*p_srv) );
78     if( !p_srv )
79         return NULL;
80
81     p_srv->i_program = i_program;
82     p_srv->cfg = *p_cfg;
83     p_srv->i_snr = -1;
84
85     p_srv->type = SERVICE_UNKNOWN;
86     p_srv->psz_name = NULL;
87     p_srv->i_channel = -1;
88     p_srv->b_crypted = false;
89
90     p_srv->i_network_id = -1;
91     p_srv->i_nit_version = -1;
92     p_srv->i_sdt_version = -1;
93
94     return p_srv;
95 }
96
97 void scan_service_Delete( scan_service_t *p_srv )
98 {
99     free( p_srv->psz_name );
100     free( p_srv );
101 }
102
103 /* */
104 int scan_Init( vlc_object_t *p_obj, scan_t *p_scan, const scan_parameter_t *p_parameter )
105 {
106     if( p_parameter->type == SCAN_DVB_T )
107     {
108         msg_Dbg( p_obj, "DVB-T scanning:" );
109         msg_Dbg( p_obj, " - frequency [%d, %d]",
110                  p_parameter->frequency.i_min, p_parameter->frequency.i_max );
111         msg_Dbg( p_obj, " - bandwidth [%d,%d]",
112                  p_parameter->bandwidth.i_min, p_parameter->bandwidth.i_max );
113         msg_Dbg( p_obj, " - exhaustive mode %s", p_parameter->b_exhaustive ? "on" : "off" );
114     }
115     else
116     {
117         return VLC_EGENERIC;
118     }
119
120     p_scan->p_obj = VLC_OBJECT(p_obj);
121     p_scan->i_index = 0;
122     p_scan->i_dialog_id = -1;
123     TAB_INIT( p_scan->i_service, p_scan->pp_service );
124     p_scan->parameter = *p_parameter;
125     p_scan->i_time_start = mdate();
126
127     return VLC_SUCCESS;
128 }
129 void scan_Clean( scan_t *p_scan )
130 {
131     if( p_scan->i_dialog_id >= 0 )
132         intf_UserHide( p_scan->p_obj, p_scan->i_dialog_id );
133
134     for( int i = 0; i < p_scan->i_service; i++ )
135         scan_service_Delete( p_scan->pp_service[i] );
136     TAB_CLEAN( p_scan->i_service, p_scan->pp_service );
137 }
138
139
140 static int ScanDvbTNextExhaustive( scan_t *p_scan, scan_configuration_t *p_cfg, double *pf_pos )
141 {
142     if( p_scan->i_index > p_scan->parameter.frequency.i_count * p_scan->parameter.bandwidth.i_count )
143         return VLC_EGENERIC;
144
145     const int i_bi = p_scan->i_index % p_scan->parameter.bandwidth.i_count;
146     const int i_fi = p_scan->i_index / p_scan->parameter.bandwidth.i_count;
147
148     p_cfg->i_frequency = p_scan->parameter.frequency.i_min + i_fi * p_scan->parameter.frequency.i_step;
149     p_cfg->i_bandwidth = p_scan->parameter.bandwidth.i_min + i_bi * p_scan->parameter.bandwidth.i_step;
150
151     *pf_pos = (double)p_scan->i_index / p_scan->parameter.frequency.i_count;
152     return VLC_SUCCESS;
153 }
154
155 static int ScanDvbTNextFast( scan_t *p_scan, scan_configuration_t *p_cfg, double *pf_pos )
156 {
157     static const int i_band_count = 2;
158     static const struct
159     {
160         const char *psz_name;
161         int i_min;
162         int i_max;
163     }
164     band[2] =
165     {
166         { "VHF", 174, 230 },
167         { "UHF", 470, 862 },
168     };
169     const int i_offset_count = 5;
170     const int i_mhz = 1000000;
171
172     /* We will probe the whole band divided in all bandwidth possibility trying 
173      * i_offset_count offset around the position
174      */
175     for( ;; p_scan->i_index++ )
176     {
177
178         const int i_bi = p_scan->i_index % p_scan->parameter.bandwidth.i_count;
179         const int i_oi = (p_scan->i_index / p_scan->parameter.bandwidth.i_count) % i_offset_count;
180         const int i_fi = (p_scan->i_index / p_scan->parameter.bandwidth.i_count) / i_offset_count;
181
182         const int i_bandwidth = p_scan->parameter.bandwidth.i_min + i_bi * p_scan->parameter.bandwidth.i_step;
183         int i;
184
185         for( i = 0; i < i_band_count; i++ )
186         {
187             if( i_fi >= band[i].i_min && i_fi <= band[i].i_max )
188                 break;
189         }
190         if( i >=i_band_count )
191         {
192             if( i_fi > band[i_band_count-1].i_max )
193                 return VLC_EGENERIC;
194             continue;
195         }
196
197         const int i_frequency_min = band[i].i_min*i_mhz + i_bandwidth*i_mhz/2;
198         const int i_frequency_base = i_fi*i_mhz;
199
200         if( i_frequency_base >= i_frequency_min && ( i_frequency_base - i_frequency_min ) % ( i_bandwidth*i_mhz ) == 0 )
201         {
202             const int i_frequency = i_frequency_base + ( i_oi - i_offset_count/2 ) * p_scan->parameter.frequency.i_step;
203
204             if( i_frequency < p_scan->parameter.frequency.i_min ||
205                 i_frequency > p_scan->parameter.frequency.i_max )
206                 continue;
207
208             p_cfg->i_frequency = i_frequency;
209             p_cfg->i_bandwidth = i_bandwidth;
210
211             int i_current = 0, i_total = 0;
212             for( int i = 0; i < i_band_count; i++ )
213             {
214                 const int i_frag = band[i].i_max-band[i].i_min;
215
216                 if( i_fi >= band[i].i_min )
217                     i_current += __MIN( i_fi - band[i].i_min, i_frag );
218                 i_total += i_frag;
219             }
220
221             *pf_pos = (double)( i_current + (double)i_oi / i_offset_count ) / i_total;
222             return VLC_SUCCESS;
223         }
224     }
225 }
226
227 static int ScanDvbTNext( scan_t *p_scan, scan_configuration_t *p_cfg, double *pf_pos )
228 {
229     if( p_scan->parameter.b_exhaustive )
230         return ScanDvbTNextExhaustive( p_scan, p_cfg, pf_pos );
231     else
232         return ScanDvbTNextFast( p_scan, p_cfg, pf_pos );
233 }
234
235 int scan_Next( scan_t *p_scan, scan_configuration_t *p_cfg )
236 {
237     double f_position;
238     int i_ret;
239
240     if( scan_IsCancelled( p_scan ) )
241         return VLC_EGENERIC;
242
243     memset( p_cfg, 0, sizeof(*p_cfg) );
244     switch( p_scan->parameter.type )
245     {
246     case SCAN_DVB_T:
247         i_ret = ScanDvbTNext( p_scan, p_cfg, &f_position );
248         break;
249     default:
250         i_ret = VLC_EGENERIC;
251         break;
252     }
253
254     if( i_ret )
255         return i_ret;
256
257     char *psz_text;
258     int i_service = 0;
259
260     for( int i = 0; i < p_scan->i_service; i++ )
261     {
262         if( p_scan->pp_service[i]->type != SERVICE_UNKNOWN )
263             i_service++;
264     }
265
266     if( asprintf( &psz_text, _("%.1f MHz (%d services)"), 
267                   (double)p_cfg->i_frequency / 1000000, i_service ) >= 0 )
268     {
269         const mtime_t i_eta = f_position > 0.005 ? (mdate() - p_scan->i_time_start) * ( 1.0 / f_position - 1.0 ) : -1;
270         char psz_eta[MSTRTIME_MAX_SIZE];
271
272         if( i_eta >= 0 )
273             msg_Info( p_scan->p_obj, "Scan ETA %s | %f", secstotimestr( psz_eta, i_eta/1000000 ), f_position * 100 );
274
275         if( p_scan->i_dialog_id < 0 )
276             p_scan->i_dialog_id = intf_UserProgress( p_scan->p_obj, _("Scanning DVB-T"), psz_text, 100.0 * f_position, -1 );
277         else
278             intf_ProgressUpdate( p_scan->p_obj, p_scan->i_dialog_id, psz_text, 100 * f_position, -1 );
279         free( psz_text );
280     }
281
282     p_scan->i_index++;
283     return VLC_SUCCESS;
284 }
285
286 bool scan_IsCancelled( scan_t *p_scan )
287 {
288     return p_scan->i_dialog_id >= 0 && intf_ProgressIsCancelled( p_scan->p_obj, p_scan->i_dialog_id );
289 }
290
291 static scan_service_t *ScanFindService( scan_t *p_scan, int i_service_start, int i_program )
292 {
293     for( int i = i_service_start; i < p_scan->i_service; i++ )
294     {
295         if( p_scan->pp_service[i]->i_program == i_program )
296             return p_scan->pp_service[i];
297     }
298     return NULL;
299 }
300
301 /* FIXME handle properly string (convert to utf8) */
302 static void PATCallBack( scan_session_t *p_session, dvbpsi_pat_t *p_pat )
303 {
304     vlc_object_t *p_obj = p_session->p_obj;
305
306     msg_Dbg( p_obj, "PATCallBack" );
307
308     /* */
309     if( p_session->p_pat && p_session->p_pat->b_current_next )
310     {
311         dvbpsi_DeletePAT( p_session->p_pat );
312         p_session->p_pat = NULL;
313     }
314     if( p_session->p_pat )
315     {
316         dvbpsi_DeletePAT( p_pat );
317         return;
318     }
319
320     dvbpsi_pat_program_t *p_program;
321
322     /* */
323     p_session->p_pat = p_pat;
324
325     /* */
326     msg_Dbg( p_obj, "new PAT ts_id=%d version=%d current_next=%d",
327              p_pat->i_ts_id, p_pat->i_version, p_pat->b_current_next );
328     for( p_program = p_pat->p_first_program; p_program != NULL; p_program = p_program->p_next )
329     {
330         msg_Dbg( p_obj, "  * number=%d pid=%d", p_program->i_number, p_program->i_pid );
331         if( p_program->i_number == 0 )
332             p_session->i_nit_pid = p_program->i_pid;
333     }
334 }
335 static void SDTCallBack( scan_session_t *p_session, dvbpsi_sdt_t *p_sdt )
336 {
337     vlc_object_t *p_obj = p_session->p_obj;
338
339     msg_Dbg( p_obj, "SDTCallBack" );
340
341     if( p_session->p_sdt && p_session->p_sdt->b_current_next )
342     {
343         dvbpsi_DeleteSDT( p_session->p_sdt );
344         p_session->p_sdt = NULL;
345     }
346     if( p_session->p_sdt )
347     {
348         dvbpsi_DeleteSDT( p_sdt );
349         return;
350     }
351
352     /* */
353     p_session->p_sdt = p_sdt;
354
355     /* */
356     msg_Dbg( p_obj, "new SDT ts_id=%d version=%d current_next=%d network_id=%d",
357              p_sdt->i_ts_id, p_sdt->i_version, p_sdt->b_current_next,
358              p_sdt->i_network_id );
359
360
361     dvbpsi_sdt_service_t *p_srv;
362     for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next )
363     {
364         dvbpsi_descriptor_t *p_dr;
365
366         msg_Dbg( p_obj, "  * service id=%d eit schedule=%d present=%d running=%d free_ca=%d",
367                  p_srv->i_service_id, p_srv->b_eit_schedule,
368                  p_srv->b_eit_present, p_srv->i_running_status,
369                  p_srv->b_free_ca );
370         for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
371         {
372             if( p_dr->i_tag == 0x48 )
373             {
374                 dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
375                 char str2[257];
376
377                 memcpy( str2, pD->i_service_name, pD->i_service_name_length );
378                 str2[pD->i_service_name_length] = '\0';
379
380                 msg_Dbg( p_obj, "    - type=%d name=%s",
381                          pD->i_service_type, str2 );
382             }
383             else
384             {
385                 msg_Dbg( p_obj, "    * dsc 0x%x", p_dr->i_tag );
386             }
387         }
388     }
389 }
390
391 static void NITCallBack( scan_session_t *p_session, dvbpsi_nit_t *p_nit )
392 {
393     vlc_object_t *p_obj = p_session->p_obj;
394
395     msg_Dbg( p_obj, "NITCallBack" );
396     msg_Dbg( p_obj, "new NIT network_id=%d version=%d current_next=%d",
397              p_nit->i_network_id, p_nit->i_version, p_nit->b_current_next );
398
399     /* */
400     if( p_session->p_nit && p_session->p_nit->b_current_next )
401     {
402         dvbpsi_DeleteNIT( p_session->p_nit );
403         p_session->p_nit = NULL;
404     }
405     if( p_session->p_nit )
406     {
407         dvbpsi_DeleteNIT( p_nit );
408         return;
409     }
410
411     /* */
412     p_session->p_nit = p_nit;
413
414     dvbpsi_descriptor_t *p_dsc;
415     for( p_dsc = p_nit->p_first_descriptor; p_dsc != NULL; p_dsc = p_dsc->p_next )
416     {
417         if( p_dsc->i_tag == 0x40 )
418         {
419             msg_Dbg( p_obj, "   * network name descriptor" );
420             char str1[257];
421
422             memcpy( str1, p_dsc->p_data, p_dsc->i_length );
423             str1[p_dsc->i_length] = '\0';
424             msg_Dbg( p_obj, "       * name %s", str1 );
425         }
426         else if( p_dsc->i_tag == 0x4a )
427         {
428             msg_Dbg( p_obj, "   * linkage descriptor" );
429             uint16_t i_ts_id = GetWBE( &p_dsc->p_data[0] );
430             uint16_t i_on_id = GetWBE( &p_dsc->p_data[2] );
431             uint16_t i_service_id = GetWBE( &p_dsc->p_data[4] );
432             int i_linkage_type = p_dsc->p_data[6];
433
434             msg_Dbg( p_obj, "       * ts_id %d", i_ts_id );
435             msg_Dbg( p_obj, "       * on_id %d", i_on_id );
436             msg_Dbg( p_obj, "       * service_id %d", i_service_id );
437             msg_Dbg( p_obj, "       * linkage_type %d", i_linkage_type );
438         }
439         else 
440         {
441             msg_Dbg( p_obj, "   * dsc 0x%x", p_dsc->i_tag );
442         }
443     }
444
445     dvbpsi_nit_ts_t *p_ts;
446     for( p_ts = p_nit->p_first_ts; p_ts != NULL; p_ts = p_ts->p_next )
447     {
448         msg_Dbg( p_obj, "   * ts ts_id=0x%x original_id=0x%x", p_ts->i_ts_id, p_ts->i_orig_network_id );
449
450         uint32_t i_private_data_id = 0;
451         dvbpsi_descriptor_t *p_dsc;
452         for( p_dsc = p_ts->p_first_descriptor; p_dsc != NULL; p_dsc = p_dsc->p_next )
453         {
454             if( p_dsc->i_tag == 0x41 )
455             {
456                 msg_Dbg( p_obj, "       * service list descriptor" );
457                 for( int i = 0; i < p_dsc->i_length/3; i++ )
458                 {
459                     uint16_t i_service_id = GetWBE( &p_dsc->p_data[3*i+0] );
460                     uint8_t  i_service_type = p_dsc->p_data[3*i+2];
461                     msg_Dbg( p_obj, "           * service_id=%d type=%d", i_service_id, i_service_type );
462                 }
463             }
464             else if( p_dsc->i_tag == 0x5a )
465             {
466                 dvbpsi_terr_deliv_sys_dr_t *p_t = dvbpsi_DecodeTerrDelivSysDr( p_dsc );
467                 msg_Dbg( p_obj, "       * terrestrial delivery system" );
468                 msg_Dbg( p_obj, "           * centre_frequency 0x%x", p_t->i_centre_frequency  );
469                 msg_Dbg( p_obj, "           * bandwidth %d", 8 - p_t->i_bandwidth );
470                 msg_Dbg( p_obj, "           * constellation %d", p_t->i_constellation );
471                 msg_Dbg( p_obj, "           * hierarchy %d", p_t->i_hierarchy_information );
472                 msg_Dbg( p_obj, "           * code_rate hp %d lp %d", p_t->i_code_rate_hp_stream, p_t->i_code_rate_lp_stream );
473                 msg_Dbg( p_obj, "           * guard_interval %d", p_t->i_guard_interval );
474                 msg_Dbg( p_obj, "           * transmission_mode %d", p_t->i_transmission_mode );
475                 msg_Dbg( p_obj, "           * other_frequency_flag %d", p_t->i_other_frequency_flag );
476             }
477             else if( p_dsc->i_tag == 0x5f )
478             {
479                 msg_Dbg( p_obj, "       * private data specifier descriptor" );
480                 i_private_data_id = GetDWBE( &p_dsc->p_data[0] );
481                 msg_Dbg( p_obj, "           * value 0x%8.8x", i_private_data_id );
482             }
483             else if( i_private_data_id == 0x28 && p_dsc->i_tag == 0x83 )
484             {
485                 msg_Dbg( p_obj, "       * logical channel descriptor (EICTA)" );
486                 for( int i = 0; i < p_dsc->i_length/4; i++ )
487                 {
488                     uint16_t i_service_id = GetWBE( &p_dsc->p_data[4*i+0] );
489                     int i_channel_number = GetWBE( &p_dsc->p_data[4*i+2] ) & 0x3ff;
490                     msg_Dbg( p_obj, "           * service_id=%d channel_number=%d", i_service_id, i_channel_number );
491                 }
492
493             }
494             else
495             {
496                 msg_Warn( p_obj, "       * dsc 0x%x", p_dsc->i_tag );
497             }
498         }
499     }
500 }
501
502 static void PSINewTableCallBack( scan_session_t *p_session, dvbpsi_handle h, uint8_t  i_table_id, uint16_t i_extension )
503 {
504     if( i_table_id == 0x42 )
505         dvbpsi_AttachSDT( h, i_table_id, i_extension, (dvbpsi_sdt_callback)SDTCallBack, p_session );
506     else if( i_table_id == 0x40 )
507         dvbpsi_AttachNIT( h, i_table_id, i_extension, (dvbpsi_nit_callback)NITCallBack, p_session );
508 }
509
510
511 int scan_session_Init( vlc_object_t *p_obj, scan_session_t *p_session, const scan_configuration_t *p_cfg )
512 {
513     /* */
514     memset( p_session, 0, sizeof(*p_session) );
515     p_session->p_obj = p_obj;
516     p_session->cfg = *p_cfg;
517     p_session->i_snr = -1;
518     p_session->pat = NULL;
519     p_session->p_pat = NULL;
520     p_session->i_nit_pid = -1;
521     p_session->sdt = NULL;
522     p_session->p_sdt = NULL;
523     p_session->nit = NULL;
524     p_session->p_nit = NULL;
525
526     return VLC_SUCCESS;
527 }
528
529 void scan_session_Clean( scan_t *p_scan, scan_session_t *p_session )
530 {
531     const int i_service_start = p_scan->i_service;
532
533     dvbpsi_pat_t *p_pat = p_session->p_pat;
534     dvbpsi_sdt_t *p_sdt = p_session->p_sdt;
535     dvbpsi_nit_t *p_nit = p_session->p_nit;
536
537     if( p_pat )
538     {
539         /* Parse PAT */
540         dvbpsi_pat_program_t *p_program;
541         for( p_program = p_pat->p_first_program; p_program != NULL; p_program = p_program->p_next )
542         {
543             if( p_program->i_number == 0 )  /* NIT */
544                 continue;
545
546             scan_service_t *s = scan_service_New( p_program->i_number, &p_session->cfg );
547             TAB_APPEND( p_scan->i_service, p_scan->pp_service, s );
548         }
549     }
550     /* Parse SDT */
551     if( p_pat && p_sdt )
552     {
553         dvbpsi_sdt_service_t *p_srv;
554         for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next )
555         {
556             scan_service_t *s = ScanFindService( p_scan, i_service_start, p_srv->i_service_id );
557             dvbpsi_descriptor_t *p_dr;
558
559             if( s )
560                 s->b_crypted = p_srv->b_free_ca;
561
562             for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
563             {
564                 if( p_dr->i_tag == 0x48 )
565                 {
566                     dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
567
568                     if( s )
569                     {
570                         if( !s->psz_name )
571                             s->psz_name = dvbsi_to_utf8( pD->i_service_name, pD->i_service_name_length );
572
573                         if( s->type == SERVICE_UNKNOWN )
574                         {
575                             switch( pD->i_service_type )
576                             {
577                             case 0x01: s->type = SERVICE_DIGITAL_TELEVISION; break;
578                             case 0x02: s->type = SERVICE_DIGITAL_RADIO; break;
579                             case 0x16: s->type = SERVICE_DIGITAL_TELEVISION_AC_SD; break;
580                             case 0x19: s->type = SERVICE_DIGITAL_TELEVISION_AC_HD; break;
581                             }
582                         }
583                     }
584                 }
585             }
586         }
587     }
588
589     /* Parse NIT */
590     if( p_pat && p_nit )
591     {
592         dvbpsi_nit_ts_t *p_ts;
593         for( p_ts = p_nit->p_first_ts; p_ts != NULL; p_ts = p_ts->p_next )
594         {
595             uint32_t i_private_data_id = 0;
596             dvbpsi_descriptor_t *p_dsc;
597
598             if( p_ts->i_orig_network_id != p_nit->i_network_id || p_ts->i_ts_id != p_pat->i_ts_id )
599                 continue;
600
601             for( p_dsc = p_ts->p_first_descriptor; p_dsc != NULL; p_dsc = p_dsc->p_next )
602             {
603                 if( p_dsc->i_tag == 0x5f )
604                 {
605                     i_private_data_id = GetDWBE( &p_dsc->p_data[0] );
606                 }
607                 else if( i_private_data_id == 0x28 && p_dsc->i_tag == 0x83 )
608                 {
609                     for( int i = 0; i < p_dsc->i_length/4; i++ )
610                     {
611                         uint16_t i_service_id = GetWBE( &p_dsc->p_data[4*i+0] );
612                         int i_channel_number = GetWBE( &p_dsc->p_data[4*i+2] ) & 0x3ff;
613
614                         scan_service_t *s = ScanFindService( p_scan, i_service_start, i_service_id );
615                         if( s && s->i_channel < 0 )
616                             s->i_channel = i_channel_number;
617                     }
618                 }
619             }
620         }
621     }
622
623     /* */
624     for( int i = i_service_start; i < p_scan->i_service; i++ )
625     {
626         scan_service_t *p_srv = p_scan->pp_service[i];
627
628         p_srv->i_snr = p_session->i_snr;
629         if( p_nit )
630         {
631             p_srv->i_network_id = p_nit->i_network_id;
632             p_srv->i_nit_version = p_nit->i_version;
633         }
634         if( p_sdt )
635             p_srv->i_sdt_version = p_sdt->i_version;
636     }
637
638
639     /* */
640     if( p_session->pat )
641         dvbpsi_DetachPAT( p_session->pat );
642     if( p_session->p_pat )
643         dvbpsi_DeletePAT( p_session->p_pat );
644
645     if( p_session->sdt )
646         dvbpsi_DetachDemux( p_session->sdt );
647     if( p_session->p_sdt )
648         dvbpsi_DeleteSDT( p_session->p_sdt );
649     if( p_session->nit )
650         dvbpsi_DetachDemux( p_session->nit );
651     if( p_session->p_nit )
652         dvbpsi_DeleteNIT( p_session->p_nit );
653 }
654
655 static int ScanServiceCmp( const void *a, const void *b )
656 {
657     scan_service_t *sa = *(scan_service_t**)a;
658     scan_service_t *sb = *(scan_service_t**)b;
659
660     if( sa->i_channel == sb->i_channel )
661     {
662         if( sa->psz_name && sb->psz_name )
663             return strcmp( sa->psz_name, sb->psz_name );
664         return 0;
665     }
666     if( sa->i_channel == -1 )
667         return 1;
668     else if( sb->i_channel == -1 )
669         return -1;
670
671     if( sa->i_channel < sb->i_channel )
672         return -1;
673     else if( sa->i_channel > sb->i_channel )
674         return 1;
675     return 0;
676 }
677
678 static block_t *BlockString( const char *psz )
679 {
680     block_t *p = block_Alloc( strlen(psz) );
681     if( p )
682         memcpy( p->p_buffer, psz, p->i_buffer );
683     return p;
684 }
685
686 block_t *scan_GetM3U( scan_t *p_scan )
687 {
688     vlc_object_t *p_obj = p_scan->p_obj;
689     block_t *p_playlist = NULL;
690
691     if( p_scan->i_service <= 0 )
692         return NULL;
693
694     /* */
695     qsort( p_scan->pp_service, p_scan->i_service, sizeof(scan_service_t*), ScanServiceCmp );
696
697     /* */
698     p_playlist = BlockString( "#EXTM3U\n\n" );/* */
699
700     for( int i = 0; i < p_scan->i_service; i++ )
701     {
702         scan_service_t *s = p_scan->pp_service[i];
703
704         if( s->type == SERVICE_UNKNOWN )
705         {
706             /* We should only select service that have been described by SDT */
707             msg_Dbg( p_obj, "scan_GetM3U: ignoring service number %d", s->i_program );
708             continue;
709         }
710
711         const char *psz_type;
712         switch( s->type )
713         {
714         case SERVICE_DIGITAL_TELEVISION:       psz_type = "Digital television"; break;
715         case SERVICE_DIGITAL_TELEVISION_AC_SD: psz_type = "Digital television advanced codec SD"; break;
716         case SERVICE_DIGITAL_TELEVISION_AC_HD: psz_type = "Digital television advanced codec HD"; break;
717         case SERVICE_DIGITAL_RADIO:            psz_type = "Digital radio"; break;
718         default:
719             psz_type = "Unknown";
720             break;
721         }
722         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",
723                   s->i_program, psz_type, s->psz_name, s->i_channel, s->b_crypted,
724                   s->i_network_id, s->i_nit_version, s->i_sdt_version,
725                   s->cfg.i_frequency, s->cfg.i_bandwidth, s->i_snr );
726
727         char *psz;
728         if( asprintf( &psz, "#EXTINF:,,%s\n"
729                         "#EXTVLCOPT:program=%d\n"
730                         "dvb://frequency=%d:bandwidth=%d\n"
731                         "\n",
732                       s->psz_name && * s->psz_name ? s->psz_name : "Unknown",
733                       s->i_program,
734                       s->cfg.i_frequency, s->cfg.i_bandwidth ) < 0 )
735             psz = NULL;
736         if( psz )
737         {
738             block_t *p_block = BlockString( psz );
739             if( p_block )
740                 block_ChainAppend( &p_playlist, p_block );
741         }
742     }
743
744     return p_playlist ? block_ChainGather( p_playlist ) : NULL;
745 }
746
747 bool scan_session_Push( scan_session_t *p_scan, block_t *p_block )
748 {
749     if( p_block->i_buffer < 188 || p_block->p_buffer[0] != 0x47 )
750     {
751         block_Release( p_block );
752         return false;
753     }
754
755     /* */
756     const int i_pid = ( (p_block->p_buffer[1]&0x1f)<<8) | p_block->p_buffer[2];
757     if( i_pid == 0x00 )
758     {
759         if( !p_scan->pat )
760             p_scan->pat = dvbpsi_AttachPAT( (dvbpsi_pat_callback)PATCallBack, p_scan );
761
762         if( p_scan->pat )
763             dvbpsi_PushPacket( p_scan->pat, p_block->p_buffer );
764     }
765     else if( i_pid == 0x11 )
766     {
767         if( !p_scan->sdt )
768             p_scan->sdt = dvbpsi_AttachDemux( (dvbpsi_demux_new_cb_t)PSINewTableCallBack, p_scan );
769
770         if( p_scan->sdt )
771             dvbpsi_PushPacket( p_scan->sdt, p_block->p_buffer );
772     }
773     else if( i_pid == p_scan->i_nit_pid )
774     {
775         if( !p_scan->nit )
776             p_scan->nit = dvbpsi_AttachDemux( (dvbpsi_demux_new_cb_t)PSINewTableCallBack, p_scan );
777
778         if( p_scan->nit )
779             dvbpsi_PushPacket( p_scan->nit, p_block->p_buffer );
780     }
781
782     block_Release( p_block );
783
784     return p_scan->p_pat && p_scan->p_sdt && p_scan->p_nit;
785 }
786
787 void scan_service_SetSNR( scan_session_t *p_session, int i_snr )
788 {
789     p_session->i_snr = i_snr;
790 }
791