File Coverage

blib/lib/Linux/DVB/DVBT/Config.pm
Criterion Covered Total %
statement 379 701 54.0
branch 145 364 39.8
condition 24 93 25.8
subroutine 27 43 62.7
pod 28 28 100.0
total 603 1229 49.0


line stmt bran cond sub pod time code
1             package Linux::DVB::DVBT::Config ;
2              
3             =head1 NAME
4              
5             Linux::DVB::DVBT::Config - DVBT configuration functions
6              
7             =head1 SYNOPSIS
8              
9             use Linux::DVB::DVBT::Config ;
10            
11              
12             =head1 DESCRIPTION
13              
14             Module provides a set of configuration routines used by the DVBT module. It is unlikely that you will need to access these functions directly, but
15             you can if you wish.
16              
17             =cut
18              
19              
20 10     10   66 use strict ;
  10         22  
  10         574  
21              
22 10     10   9258 use Data::Dumper ;
  10         110601  
  10         1228  
23              
24             our $VERSION = '2.08' ;
25             our $DEBUG = 0 ;
26              
27             our $DEFAULT_CONFIG_PATH = '/etc/dvb:~/.tv' ;
28              
29 10     10   83 use File::Path ;
  10         17  
  10         634  
30 10     10   53 use File::Spec ;
  10         16  
  10         39693  
31              
32             my %FILES = (
33             'ts' => { 'file' => "dvb-ts", 'required' => 1 },
34             'pr' => { 'file' => "dvb-pr", 'required' => 1 },
35             'aliases' => { 'file' => "dvb-aliases", 'required' => 0 },
36             ) ;
37              
38             my %NUMERALS = (
39             'one' => 1,
40             'two' => 2,
41             'three' => 3,
42             'four' => 4,
43             'five' => 5,
44             'six' => 6,
45             'seven' => 7,
46             'eight' => 8,
47             'nine' => 9,
48             ) ;
49              
50             our @SCAN_INFO_FIELDS = qw/pr ts lcn freqs/ ;
51              
52             #============================================================================================
53              
54             =head2 Functions
55              
56             =over 4
57              
58             =cut
59              
60              
61              
62             #----------------------------------------------------------------------
63              
64             =item B
65              
66             Given a frequency, find the matching TSID.
67              
68             $tuning_href is the HASH returned by L.
69              
70             =cut
71              
72             sub find_tsid
73             {
74 0     0 1 0 my ($frequency, $tuning_href) = @_ ;
75 0         0 my $tsid ;
76              
77             # 'ts' =>
78             # 4107 =>
79             # {
80             # tsid => 4107,
81             # frequency => 57800000,
82             # ...
83             # },
84              
85 0         0 foreach my $this_tsid (keys %{$tuning_href->{'ts'}})
  0         0  
86             {
87 0 0       0 if ($frequency == $tuning_href->{'ts'}{$this_tsid}{'frequency'})
88             {
89 0         0 $tsid = $this_tsid ;
90 0         0 last ;
91             }
92             }
93 0         0 return $tsid ;
94             }
95              
96             #----------------------------------------------------------------------
97              
98             =item B
99              
100             Given a tsid, return the frontend params (or undef). The frontend params HASH
101             contain the information used to tune the frontend i.e. this is the transponder
102             (TSID) information. It corresponds to the matching 'ts' entry in the tuning info
103             HASH.
104              
105             $tuning_href is the HASH returned by L.
106              
107             =cut
108              
109             sub tsid_params
110             {
111 0     0 1 0 my ($tsid, $tuning_href) = @_ ;
112              
113 0         0 my $params_href ;
114              
115             # 'ts' =>
116             # 4107 =>
117             # {
118             # tsid => 4107,
119             # frequency => 57800000,
120             # ...
121             # },
122              
123 0 0 0     0 if ($tsid && exists($tuning_href->{'ts'}{$tsid}))
124             {
125 0         0 $params_href = $tuning_href->{'ts'}{$tsid} ;
126             }
127              
128 0         0 return $params_href ;
129             }
130              
131             #----------------------------------------------------------------------
132              
133             =item B
134              
135             Given a tsid and pid, find the matching channel information and returns the
136             program HASH ref if found. This corresponds to the matching 'pr' entry in the tuning
137             info HASH.
138              
139             $tuning_href is the HASH returned by L.
140              
141             =cut
142              
143             sub chan_from_pid
144             {
145 0     0 1 0 my ($tsid, $pid, $tuning_href) = @_ ;
146 0         0 my $pr_href ;
147            
148             # skip PAT
149 0 0       0 return $pr_href unless $pid ;
150              
151             # 'pr' =>
152             # BBC ONE =>
153             # {
154             # pnr => 4171,
155             # tsid => 4107,
156             # tuned_freq => 57800000,
157             # ...
158             # },
159              
160 0         0 foreach my $chan (keys %{$tuning_href->{'pr'}})
  0         0  
161             {
162             # if ($tsid == $tuning_href->{'pr'}{$chan}{'tsid'})
163 0 0       0 if ($tsid eq $tuning_href->{'pr'}{$chan}{'tsid'})
164             {
165 0         0 foreach my $stream (qw/video audio teletext subtitle/)
166             {
167 0 0       0 if ($pid == $tuning_href->{'pr'}{$chan}{$stream})
168             {
169 0         0 $pr_href = $tuning_href->{'pr'}{$chan} ;
170 0         0 last ;
171             }
172             }
173 0 0       0 last if $pr_href ;
174              
175             # check other audio
176 0         0 my @audio = audio_list( $tuning_href->{'pr'}{$chan} ) ;
177 0         0 foreach (@audio)
178             {
179 0 0       0 if ($pid == $_)
180             {
181 0         0 $pr_href = $tuning_href->{'pr'}{$chan} ;
182 0         0 last ;
183             }
184             }
185             }
186            
187 0 0       0 last if $pr_href ;
188             }
189              
190 0         0 return $pr_href ;
191             }
192              
193             #----------------------------------------------------------------------
194              
195             =item B
196              
197             Given a pid, find the matching channel & TSID information
198              
199             Returns an array of HASH entries, each HASH containing the stream type (video, audio, subtitle, or
200             teletext), along with a copy of the associated program information (i.e. the matching 'pr' entry from the
201             tuning info HASH):
202              
203             @pid_info = [
204             {
205             'pidtype' => video, audio, subtitle, teletext
206             pnr => 4171,
207             tsid => 4107,
208             tuned_freq => 57800000,
209             ...
210             },
211             ...
212             ]
213              
214              
215             $tuning_href is the HASH returned by L.
216              
217             =cut
218              
219             sub pid_info
220             {
221 7     7 1 12470 my ($pid, $tuning_href) = @_ ;
222              
223 7 50       30 print "pid_info(pid=\"$pid\")\n" if $DEBUG ;
224              
225 7         14 my @pid_info ;
226            
227             # skip PAT
228 7 50       29 return @pid_info unless $pid ;
229            
230 7         10 foreach my $chan (keys %{$tuning_href->{'pr'}})
  7         82  
231             {
232 441         1150 my $tsid = $tuning_href->{'pr'}{$chan}{'tsid'} ;
233            
234             # program
235 441         2855 my @chan_pids ;
236 441         692 foreach my $stream (qw/video audio teletext subtitle/)
237             {
238 1764         6192 push @chan_pids, [$stream, $tuning_href->{'pr'}{$chan}{$stream}] ;
239             }
240            
241             # extra audio
242 441         1431 my @audio = audio_list( $tuning_href->{'pr'}{$chan} ) ;
243 441         984 foreach (@audio)
244             {
245 217         632 push @chan_pids, ['audio', $_] ;
246             }
247              
248             # extra subtitle by rainbowcrypt
249 441         1440 my @sub = sub_list( $tuning_href->{'pr'}{$chan} ) ;
250 441         681 foreach (@sub)
251             {
252 21         46 push @chan_pids, ['subtitle', $_] ;
253             }
254            
255             # SI
256 441         580 foreach my $si (qw/pmt/)
257             {
258 441         2063 push @chan_pids, [uc $si, $tuning_href->{'pr'}{$chan}{$si}] ;
259             }
260            
261              
262             # check pids
263 441         793 foreach my $aref (@chan_pids)
264             {
265 2443 100       6589 if ($pid == $aref->[1])
266             {
267 16 50       3624 print " + pidtype=$aref->[0]\n" if $DEBUG ;
268 16         323 push @pid_info, {
269 16         21 %{$tuning_href->{'pr'}{$chan}},
270             'pidtype' => $aref->[0],
271            
272             # keep ref to program HASH (used by downstream functions)
273             'demux_params' => $tuning_href->{'pr'}{$chan},
274             } ;
275             }
276             }
277             }
278              
279 7         79 return @pid_info ;
280             }
281              
282             #----------------------------------------------------------------------
283              
284             =item B
285              
286             Given a channel name, do a "fuzzy" search and return an array containing params:
287              
288             ($frontend_params_href, $demux_params_href)
289              
290             $demux_params_href HASH ref are of the form:
291              
292             {
293             pnr => 4171,
294             tsid => 4107,
295             tuned_freq => 57800000,
296             ...
297             },
298            
299             (i.e. $tuning_href->{'pr'}{$channel_name})
300              
301             $frontend_params_href HASH ref are of the form:
302              
303             {
304             tsid => 4107,
305             frequency => 57800000,
306             ...
307             },
308            
309             (i.e. $tuning_href->{'ts'}{$tsid} where $tsid is TSID for the channel)
310            
311             $tuning_href is the HASH returned by L.
312              
313             =cut
314              
315             sub find_channel
316             {
317 15     15 1 23970 my ($channel_name, $tuning_href) = @_ ;
318            
319 15         24 my ($frontend_params_href, $demux_params_href) ;
320              
321             ## Look for channel info
322 15 50       111 print STDERR "find $channel_name ...\n" if $DEBUG ;
323            
324 15         62 $channel_name = _channel_alias($channel_name, $tuning_href->{'aliases'}) ;
325 15         50 my $found_channel_name = _channel_search($channel_name, $tuning_href->{'pr'}) ;
326 15 50       47 if ($found_channel_name)
327             {
328 15         45 $demux_params_href = $tuning_href->{'pr'}{$found_channel_name} ;
329             }
330            
331             ## If we've got the channel, look up it's frontend settings
332 15 50       33 if ($demux_params_href)
333             {
334 15         26 my $tsid = $demux_params_href->{'tsid'} ;
335 15         146 $frontend_params_href = {
336 15         19 %{$tuning_href->{'ts'}{$tsid}},
337             'tsid' => $tsid,
338             } ;
339             }
340              
341 15         64 return ($frontend_params_href, $demux_params_href) ;
342             }
343              
344              
345             #----------------------------------------------------------------------
346             # Do "fuzzy" search for channel name
347             #
348             sub _channel_search
349             {
350 18     18   29 my ($channel_name, $search_href) = @_ ;
351            
352 18         21 my $found_channel_name ;
353            
354             # start by just seeing if it's the correct name...
355 18 100       45 if (exists($search_href->{$channel_name}))
356             {
357 3         7 return $channel_name ;
358             }
359             else
360             {
361             ## Otherwise, try finding variations on the channel name
362 15         19 my %search ;
363              
364 15         31 $channel_name = lc $channel_name ;
365            
366             # lower-case, no spaces
367 15         19 my $srch = $channel_name ;
368 15         46 $srch =~ s/\s+//g ;
369 15         35 $search{$srch}=1 ;
370              
371             # lower-case, replaced words with numbers, no spaces
372 15         21 $srch = $channel_name ;
373 15         63 foreach my $num (keys %NUMERALS)
374             {
375 135         1101 $srch =~ s/\b($num)\b/$NUMERALS{$num}/ge ;
  3         12  
376             }
377 15         53 $srch =~ s/\s+//g ;
378 15         26 $search{$srch}=1 ;
379              
380             # lower-case, replaced numbers with words, no spaces
381 15         22 $srch = $channel_name ;
382 15         44 foreach my $num (keys %NUMERALS)
383             {
384 135 50       228 print STDERR " -- $srch - replace $NUMERALS{$num} with $num..\n" if $DEBUG>3 ;
385 135         1030 $srch =~ s/($NUMERALS{$num})\b/$num/ge ;
  9         29  
386 135 50       350 print STDERR " -- -- $srch\n" if $DEBUG>3 ;
387             }
388 15         49 $srch =~ s/\s+//g ;
389 15         33 $search{$srch}=1 ;
390              
391 15 50       33 print STDERR " + Searching tuning info [", keys %search, "]...\n" if $DEBUG>2 ;
392            
393 15         52 foreach my $chan (keys %$search_href)
394             {
395 70         98 my $srch_chan = lc $chan ;
396 70         688 $srch_chan =~ s/\s+//g ;
397            
398 70         123 foreach my $search (keys %search)
399             {
400 126 50       284 print STDERR " + + checking $search against $srch_chan \n" if $DEBUG>2 ;
401 126 100       831 if ($srch_chan eq $search)
402             {
403 14         22 $found_channel_name = $chan ;
404 14 50       31 print STDERR " + found $channel_name\n" if $DEBUG ;
405 14         23 last ;
406             }
407             }
408            
409 70 100       173 last if $found_channel_name ;
410             }
411             }
412            
413 15         39 return $found_channel_name ;
414             }
415              
416              
417             #----------------------------------------------------------------------
418             # Lookup channel name alias (if it exists)
419             #
420             sub _channel_alias
421             {
422 15     15   33 my ($channel_name, $alias_href) = @_ ;
423              
424 15 100 100     52 if ($alias_href && scalar(keys %$alias_href))
425             {
426 3 50       20 print STDERR "Searching channel aliases for \"$channel_name\" ... \n" if $DEBUG>3 ;
427 3         6 my $alias_key = _channel_search($channel_name, $alias_href) ;
428 3 100       8 if ($alias_key)
429             {
430 2         4 my $alias = $alias_href->{$alias_key} ;
431 2 50       4 print STDERR "... using alias \"$alias\" for \"$channel_name\"\n" if $DEBUG>3 ;
432 2         4 $channel_name = $alias ;
433             }
434             }
435            
436 15         33 return $channel_name ;
437             }
438              
439             #----------------------------------------------------------------------
440              
441             =item B
442              
443             Process the demux parameters and a language specifier to return the list of audio
444             streams required.
445              
446             demux_params are of the form:
447              
448             {
449             pnr => 4171,
450             tsid => 4107,
451             tuned_freq => 57800000,
452             ...
453             },
454              
455             (i.e. $tuning_href->{'pr'}{$channel_name})
456              
457            
458             Language specifier string is in the format:
459              
460             =over 4
461              
462             =item a)
463              
464             Empty string : just return the default audio stream pid
465              
466             =item b)
467              
468             Comma/space seperated list of one or more language names : returns the audio stream pids for all that match (does not necessarily include default stream)
469              
470             =back
471            
472             If the list in (b) contains a '+' character (normally at the start) then the default audio stream is automatically included in teh list, and the
473             extra streams are added to it.
474            
475             For example, if a channel has the following audio details: eng:100 eng:101 fra:102 deu:103
476             Then the following specifications result in the lists as shown:
477              
478             =over 4
479              
480             =item *
481              
482             "" => (100)
483              
484             =item *
485              
486             "eng deu" => (100, 103)
487              
488             =item *
489              
490             "+eng fra" => (100, 101, 102)
491              
492             =back
493            
494             Note that the language names are not case sensitive
495              
496              
497             =cut
498              
499             sub audio_pids
500             {
501 18     18 1 21188 my ($demux_params_href, $language_spec, $pids_aref) = @_ ;
502 18         25 my $error = 0 ;
503            
504 18 50       60 print "audio_pids(lang=\"$language_spec\")\n" if $DEBUG ;
505              
506 18         43 my $audio_pid = $demux_params_href->{'audio'} ;
507            
508             ## simplest case is no language spec
509 18   100     61 $language_spec ||= "" ;
510 18 100       55 if (!$language_spec)
511             {
512 4 50       11 print " + simplest case - add default audio $audio_pid\n" if $DEBUG ;
513              
514 4         12 push @$pids_aref, $audio_pid ;
515 4         13 return 0 ;
516             }
517              
518             # split details
519 14         17 my @audio_details ;
520 14         31 my $details = $demux_params_href->{'audio_details'} ;
521 14 50       30 print "audio_details=\"$details\")\n" if $DEBUG ;
522 14         89 while ($details =~ m/(\S+):(\d+)/g)
523             {
524 56         122 my ($lang, $pid) = ($1, $2) ;
525 56         283 push @audio_details, {'lang'=>lc $lang, 'pid'=>$pid} ;
526              
527 56 50       379 print " + lang=$audio_details[-1]{lang} pid=$audio_details[-1]{pid}\n" if $DEBUG >= 10 ;
528             }
529              
530             # drop default audio
531 14         76 shift @audio_details ;
532              
533             # process language spec
534 14 100       457 if ($language_spec =~ s/\+//g)
535             {
536             # ensure default is in the list
537 2         7 push @$pids_aref, $audio_pid ;
538              
539 2 50       7 print " - lang spec contains '+', added default audio\n" if $DEBUG >= 10 ;
540             }
541              
542 14 50       35 print "process lang spec\n" if $DEBUG >= 10 ;
543              
544             # work through the language spec
545 14         25 my $pid ;
546             my $lang ;
547 14         155 my @lang = split /[\s,]+/, $language_spec ;
548 14         33 while (@lang)
549             {
550 20         36 $lang = shift @lang ;
551              
552 20 50       47 print " + lang=$lang\n" if $DEBUG >= 10 ;
553            
554 20         28 $pid = undef ;
555 20   100     109 while (!$pid && @audio_details)
556             {
557 32         135 my $audio_href = shift @audio_details ;
558 32 50       144 print " + + checking this audio detail: lang=$audio_href->{lang} pid=$audio_href->{pid}\n" if $DEBUG >= 10 ;
559 32 100       982 if ($audio_href->{'lang'} =~ /$lang/i)
560             {
561 12         26 $pid = $audio_href->{'pid'} ;
562 12 50       31 print " + + Found pid = $pid\n" if $DEBUG >= 10 ;
563              
564 12         25 push @$pids_aref, $pid ;
565 12 50       63 print " + Added pid = $pid\n" if $DEBUG >= 10 ;
566             }
567             }
568 20 100       65 last unless @audio_details ;
569             }
570            
571             # clean up
572 14 100 100     91 if (@lang || !$pid)
573             {
574 8 50       27 unshift @lang, $lang if $lang ;
575 8         499 $error = "Error: could not find the languages: " . join(', ', @lang) . " associated with program \"$demux_params_href->{pnr}\"" ;
576             }
577            
578 14         66 return $error ;
579             }
580             #----------------------------------------------------------------------
581              
582             =item B #copy/paste from audio_pid by rainbowcrypt
583              
584             Process the demux parameters and a language specifier to return the list of audio
585             streams required.
586              
587             demux_params are of the form:
588              
589             {
590             pnr => 4171,
591             tsid => 4107,
592             tuned_freq => 57800000,
593             ...
594             },
595              
596             (i.e. $tuning_href->{'pr'}{$channel_name})
597              
598            
599             Language specifier string is in the format:
600              
601             =over 4
602              
603             =item a)
604              
605             Empty string : just return the default audio stream pid
606              
607             =item b)
608              
609             Comma/space seperated list of one or more language names : returns the audio stream pids for all that match (does not necessarily include default stream)
610              
611             =back
612            
613             If the list in (b) contains a '+' character (normally at the start) then the default audio stream is automatically included in teh list, and the
614             extra streams are added to it.
615            
616             For example, if a channel has the following audio details: eng:100 eng:101 fra:102 deu:103
617             Then the following specifications result in the lists as shown:
618              
619             =over 4
620              
621             =item *
622              
623             "" => (100)
624              
625             =item *
626              
627             "eng deu" => (100, 103)
628              
629             =item *
630              
631             "+eng fra" => (100, 101, 102)
632              
633             =back
634            
635             Note that the language names are not case sensitive
636              
637              
638             =cut
639              
640             sub subtitle_pids
641             { #copy/paste from audio_pid by rainbowcrypt
642 2     2 1 5 my ($demux_params_href, $language_spec, $pids_aref) = @_ ;
643 2         5 my $error = 0 ;
644            
645 2 50       7 print "subtitle_pids(lang=\"$language_spec\")\n" if $DEBUG ;
646              
647 2         5 my $subtitle_pid = $demux_params_href->{'subtitle'} ;
648            
649             ## simplest case is no language spec
650 2   100     19 $language_spec ||= "" ;
651 2 100       8 if (!$language_spec)
652             {
653 1 50       4 print " + simplest case - add default subtitle $subtitle_pid\n" if $DEBUG ;
654              
655 1         3 push @$pids_aref, $subtitle_pid ;
656 1         4 return 0 ;
657             }
658              
659             # split details
660 1         3 my @subtitle_details ;
661 1   50     9 my $details = $demux_params_href->{'subtitle_details'} || "" ;
662 1 50       10 print "subtitle_details=\"$details\")\n" if $DEBUG ;
663 1         10 while ($details =~ m/(\S+):(\d+)/g)
664             {
665 4         99 my ($lang, $pid) = ($1, $2) ;
666 4         18 push @subtitle_details, {'lang'=>lc $lang, 'pid'=>$pid} ;
667              
668 4 50       27 print " + lang=$subtitle_details[-1]{lang} pid=$subtitle_details[-1]{pid}\n" if $DEBUG >= 10 ;
669             }
670              
671             # drop default audio
672 1         3 shift @subtitle_details ;
673              
674             # process language spec
675 1 50       126 if ($language_spec =~ s/\+//g)
676             {
677             # ensure default is in the list
678 0         0 push @$pids_aref, $subtitle_pid ;
679              
680 0 0       0 print " - lang spec contains '+', added default subtitle\n" if $DEBUG >= 10 ;
681             }
682              
683 1 50       7 print "process lang spec\n" if $DEBUG >= 10 ;
684              
685             # work through the language spec
686 1         2 my $pid ;
687             my $lang ;
688 1         8 my @lang = split /[\s,]+/, $language_spec ;
689 1         5 while (@lang)
690             {
691 2         5 $lang = shift @lang ;
692              
693 2 50       6 print " + lang=$lang\n" if $DEBUG >= 10 ;
694            
695 2         3 $pid = undef ;
696 2   66     12 while (!$pid && @subtitle_details)
697             {
698 3         5 my $subtitle_href = shift @subtitle_details ;
699 3 50       9 print " + + checking this subtitle detail: lang=$subtitle_href->{lang} pid=$subtitle_href->{pid}\n" if $DEBUG >= 10 ;
700 3 100       39 if ($subtitle_href->{'lang'} =~ /$lang/i)
701             {
702 2         5 $pid = $subtitle_href->{'pid'} ;
703 2 50       7 print " + + Found pid = $pid\n" if $DEBUG >= 10 ;
704              
705 2         6 push @$pids_aref, $pid ;
706 2 50       21 print " + Added pid = $pid\n" if $DEBUG >= 10 ;
707             }
708             }
709 2 100       7 last unless @subtitle_details ;
710             }
711            
712             # clean up
713 1 50 33     7 if (@lang || !$pid)
714             {
715 0 0       0 unshift @lang, $lang if $lang ;
716 0         0 $error = "Error: could not find the languages: " . join(', ', @lang) . " associated with program \"$demux_params_href->{pnr}\"" ;
717             }
718            
719 1         6 return $error ;
720             }
721              
722             #----------------------------------------------------------------------
723              
724             =item B #modified by rainbowcrypt
725              
726             Process the demux parameters and an output specifier to return the list of all
727             stream pids required.
728              
729             Output specifier string is in the format such that it just needs to contain the following characters:
730              
731             a = audio
732             v = video
733             s = subtitle
734              
735             Returns an array of HASHes of the form:
736              
737             {'pid' => $pid, 'pidtype' => $type, 'pmt' => $pmt}
738              
739              
740             =cut
741              
742             sub out_pids
743             {
744 9     9 1 17159 my ($demux_params_href, $out_spec, $language_spec, $subtitle_language_spec, $pids_aref) = @_ ;
745 9         25 my $error = 0 ;
746              
747             ## default
748 9   100     45 $out_spec ||= "av" ;
749            
750             # my $pmt = $demux_params_href->{'pmt'} ;
751              
752             ## Audio required?
753 9 50       50 if ($out_spec =~ /a/i)
754             {
755 9         14 my @audio_pids ;
756 9         36 $error = audio_pids($demux_params_href, $language_spec, \@audio_pids) ;
757 9 100       36 return $error if $error ;
758            
759 5         12 foreach my $pid (@audio_pids)
760             {
761 6 50       47 push @$pids_aref, {
762             'pid' => $pid,
763             'pidtype' => 'audio',
764            
765             # keep ref to program HASH (used by downstream functions)
766             'demux_params' => $demux_params_href,
767             } if $pid ;
768             }
769             }
770            
771             ## Video required?
772 5 100       26 if ($out_spec =~ /v/i)
773             {
774 4         11 my $pid = $demux_params_href->{'video'} ;
775 4 50       26 push @$pids_aref, {
776             'pid' => $pid,
777             'pidtype' => 'video',
778            
779             # keep ref to program HASH (used by downstream functions)
780             'demux_params' => $demux_params_href,
781             } if $pid ;
782             }
783            
784             ## Subtitle required?
785 5 100       20 if ($out_spec =~ /s/i) #modified by rainbowcrypt
786             {
787 2         4 my @subtitle_pids ;
788 2         11 $error = subtitle_pids($demux_params_href, $subtitle_language_spec, \@subtitle_pids) ;
789 2 50       7 return $error if $error ;
790            
791 2         3 foreach my $pid (@subtitle_pids)
792             {
793 3 50       22 push @$pids_aref, {
794             'pid' => $pid,
795             'pidtype' => 'subtitle',
796            
797             # keep ref to program HASH (used by downstream functions)
798             'demux_params' => $demux_params_href,
799             } if $pid ;
800             }
801             }
802            
803 5         19 return $error ;
804             }
805              
806             #----------------------------------------------------------------------
807              
808             =item B
809              
810             Process the demux parameters and return a list of additional audio
811             streams (or an empty list if none available).
812              
813             For example:
814              
815             {
816             audio => 601,
817             audio_details => eng:601 eng:602,
818             ...
819             },
820              
821             would return the list: ( 602 )
822              
823              
824             =cut
825              
826             sub audio_list
827             {
828 441     441 1 543 my ($demux_params_href) = @_ ;
829 441         440 my @pids ;
830            
831 441         643 my $audio_pid = $demux_params_href->{'audio'} ;
832 441         817 my $details = $demux_params_href->{'audio_details'} ;
833 441         1989 while ($details =~ m/(\S+):(\d+)/g)
834             {
835 658         1475 my ($lang, $pid) = ($1, $2) ;
836 658 100       3117 push @pids, $pid if ($pid != $audio_pid) ;
837             }
838            
839 441         1169 return @pids ;
840             }
841              
842             #----------------------------------------------------------------------
843              
844             =item B by rainbowcrypt
845              
846             Process the demux parameters and return a list of additional subtitle
847             streams (or an empty list if none available).
848              
849             For example:
850              
851             {
852             subtitle => 601,
853             subtitle_details => DVD_malentendant:601 DVB-francais:602,
854             ...
855             },
856              
857             would return the list: ( 602 )
858              
859              
860             =cut
861              
862             sub sub_list
863             {
864 441     441 1 542 my ($demux_params_href) = @_ ;
865 441         485 my @pids ;
866            
867 441         940 my $sub_pid = $demux_params_href->{'subtitle'} ;
868 441   100     1700 my $details = $demux_params_href->{'subtitle_details'} || "" ;
869 441         1623 while ($details =~ m/(\S+):(\d+)/g)
870             {
871 28         52 my ($lang, $pid) = ($1, $2) ;
872 28 100       125 push @pids, $pid if ($pid != $sub_pid) ;
873             }
874            
875 441         913 return @pids ;
876             }
877              
878              
879             #----------------------------------------------------------------------
880              
881             =item B
882              
883             Read tuning information from config files. Look in search path and return first
884             set of readable file information in a tuning HASH ref.
885              
886             Returns a HASH ref of tuning information - i.e. it contains the complete information on all
887             transponders (under the 'ts' field), and all programs (under the 'pr' field). [see L method for format].
888              
889              
890             =cut
891              
892             sub read
893             {
894 5     5 1 13 my ($search_path) = @_ ;
895            
896 5 50       17 $search_path = $DEFAULT_CONFIG_PATH unless defined($search_path) ;
897            
898 5         24 my $href ;
899 5         37 my $dir = read_dir($search_path) ;
900 5 50       23 if ($dir)
901             {
902 5         10 $href = {} ;
903 5         21 foreach my $region (keys %FILES)
904             {
905 10     10   265 no strict "refs" ;
  10         33  
  10         3356  
906 15         41 my $fn = "read_dvb_$region" ;
907              
908 15 50       44 print STDERR " + Running $fn() for $region ...\n" if $DEBUG ;
909              
910 15         262 $href->{$region} = &$fn("$dir/$FILES{$region}{'file'}") ;
911             }
912            
913             ## Special case - get tuning info if present
914 5         48 $href->{'freqfile'} = read_dvb_ts_freqs("$dir/$FILES{ts}{'file'}") ;
915            
916 5 50       20 print STDERR "Read config from $dir\n" if $DEBUG ;
917 5 50       21 print STDERR Data::Dumper->Dump(["Config=", $href]) if $DEBUG >= 5 ;
918            
919             }
920 5         33 return $href ;
921             }
922              
923             #----------------------------------------------------------------------
924              
925             =item B
926              
927             Write tuning information into the first writeable area in the search path.
928              
929             =cut
930              
931             sub write
932             {
933 1     1 1 7 my ($search_path, $href) = @_ ;
934              
935 1 50       10 $search_path = $DEFAULT_CONFIG_PATH unless defined($search_path) ;
936 1         21 my $dir = write_dir($search_path) ;
937 1 50 33     17 if ($dir && $href)
938             {
939 1         8 foreach my $region (keys %FILES)
940             {
941 10     10   76 no strict "refs" ;
  10         42  
  10         100201  
942 3         8 my $fn = "write_dvb_$region" ;
943 3         171 &$fn("$dir/$FILES{$region}{'file'}", $href->{$region}, $href->{'freqfile'}) ;
944             }
945              
946 1 50       7 print STDERR "Written config to $dir\n" if $DEBUG ;
947             }
948             }
949              
950              
951             #----------------------------------------------------------------------
952              
953             =item B
954              
955             Returns the readable filename for the specified file type, which can be one of: 'pr'=program, 'ts'=transponder.
956              
957             Optionally specify the search path (otherwise the default search path is used)
958              
959             Returns undef if invalid file type is specified, or unable to find a readable area.
960              
961             =cut
962              
963             sub read_filename
964             {
965 0     0 1 0 my ($filetype, $search_path) = @_ ;
966            
967 0         0 my $filename ;
968 0 0       0 return $filename if (!exists($FILES{$filetype}));
969            
970 0 0       0 $search_path = $DEFAULT_CONFIG_PATH unless defined($search_path) ;
971 0         0 my $dir = read_dir($search_path) ;
972              
973 0 0       0 if ($dir)
974             {
975 0         0 $filename = "$dir/$FILES{$filetype}{'file'}" ;
976             }
977 0         0 return $filename ;
978             }
979              
980             #----------------------------------------------------------------------
981              
982             =item B
983              
984             Returns the writeable filename for the specified file type, which can be one of: 'pr'=program, 'ts'=transponder.
985              
986             Optionally specify the search path (otherwise the default search path is used)
987              
988             Returns undef if invalid file type is specified, or unable to find a writeable area.
989              
990             =cut
991              
992             sub write_filename
993             {
994 0     0 1 0 my ($filetype, $search_path) = @_ ;
995              
996 0         0 my $filename ;
997 0 0       0 return $filename if (!exists($FILES{$filetype}));
998              
999 0 0       0 $search_path = $DEFAULT_CONFIG_PATH unless defined($search_path) ;
1000 0         0 my $dir = write_dir($search_path) ;
1001              
1002 0 0       0 if ($dir)
1003             {
1004 0         0 $filename = "$dir/$FILES{$filetype}{'file'}" ;
1005             }
1006 0         0 return $filename ;
1007             }
1008              
1009              
1010             #----------------------------------------------------------------------
1011              
1012             =item B
1013              
1014             Sorts TSIDs. As I now allow duplicate TSIDs in scans, and the duplicates
1015             are suffixed with a letter to make it obvious, numeric sorting is not possible.
1016              
1017             This function can be used to correctly sort the TSIDs into order. Returns the usual
1018             -1, 0, 1 depending on if a is <, ==, or > b
1019              
1020             =cut
1021              
1022             sub tsid_sort
1023             {
1024 0     0 1 0 my ($tsid_a, $tsid_b) = @_ ;
1025            
1026 0         0 my $a_int = int($tsid_a) ;
1027 0         0 my $b_int = int($tsid_b) ;
1028            
1029             return
1030 0   0     0 $a_int <=> $b_int
1031             ||
1032             $tsid_a cmp $tsid_b
1033             ;
1034             }
1035              
1036             #----------------------------------------------------------------------
1037              
1038             =item B
1039              
1040             Format the tsid number/name into a string. As I now allow duplicate TSIDs in
1041             scans, and the duplicates are suffixed with a letter to make it obvious which
1042             are duplicates. This routine formats the numeric part and always adds a suffix
1043             character (or space if none present).
1044              
1045             =cut
1046              
1047             sub tsid_str
1048             {
1049 0     0 1 0 my ($tsid) = @_ ;
1050            
1051 0         0 my ($tsid_int, $tsid_suffix) = ($tsid, " ") ;
1052 0 0       0 if ($tsid =~ /(\d+)([a-z])/i)
1053             {
1054 0         0 ($tsid_int, $tsid_suffix) = ($1, $2) ;
1055             }
1056              
1057 0         0 return sprintf "%5d$tsid_suffix", $tsid_int ;
1058             }
1059              
1060             #----------------------------------------------------------------------
1061              
1062             =item B
1063              
1064             Remove the specified TSID from the tuning information. Also removes any channels
1065             that are under that TSID.
1066              
1067             =cut
1068              
1069             sub tsid_delete
1070             {
1071 0     0 1 0 my ($tsid, $tuning_href) = @_ ;
1072            
1073 0         0 my $ok = 0;
1074 0 0       0 if (exists($tuning_href->{'ts'}{$tsid}))
1075             {
1076 0         0 $ok = 1 ;
1077 0         0 my $info_href = _scan_info($tuning_href) ;
1078            
1079 0         0 delete $tuning_href->{'ts'}{$tsid} ;
1080            
1081 0         0 foreach my $pnr (keys %{$info_href->{'tsid'}{$tsid}{'pr'}} )
  0         0  
1082             {
1083 0         0 my $chan = $info_href->{'tsid'}{$tsid}{'pr'}{$pnr} ;
1084 0         0 delete $tuning_href->{'pr'}{$chan} ;
1085             }
1086              
1087             }
1088            
1089 0         0 return $ok ;
1090             }
1091              
1092              
1093              
1094             #----------------------------------------------------------------------
1095              
1096             =item B
1097              
1098             Merge tuning information - overwrites previous with new - into $old_href and return
1099             the HASH ref.
1100              
1101             =cut
1102              
1103             sub merge
1104             {
1105 0     0 1 0 my ($new_href, $old_href, $scan_info_href) = @_ ;
1106              
1107 0 0       0 print STDERR Data::Dumper->Dump(["merge - Scan info [$scan_info_href]=", $scan_info_href]) if $DEBUG>=5 ;
1108              
1109 0   0     0 $scan_info_href ||= {} ;
1110              
1111             # region: 'ts' =>
1112             # section: '4107' =>
1113             # field: name = Oxford/Bexley
1114             #
1115 0 0 0     0 if ($old_href && $new_href)
1116             {
1117 0         0 foreach my $region (keys %FILES)
1118             {
1119 0   0     0 $old_href->{$region} ||= {} ;
1120 0 0       0 if (exists($new_href->{$region}))
1121             {
1122 0         0 foreach my $section (keys %{$new_href->{$region}})
  0         0  
1123             {
1124 0         0 foreach my $field (keys %{$new_href->{$region}{$section}})
  0         0  
1125             {
1126 0         0 $old_href->{$region}{$section}{$field} = $new_href->{$region}{$section}{$field} ;
1127             }
1128             }
1129             }
1130             }
1131             }
1132              
1133 0 0       0 $old_href = $new_href if (!$old_href) ;
1134            
1135 0 0       0 print STDERR Data::Dumper->Dump(["merge END - Scan info [$scan_info_href]=", $scan_info_href]) if $DEBUG>=5 ;
1136              
1137 0         0 return $old_href ;
1138             }
1139              
1140             #----------------------------------------------------------------------
1141              
1142             =item B
1143              
1144             Merge tuning information - checks to ensure new program info has the
1145             best strength, and that new program has all of it's settings
1146              
1147             'pr' => {
1148             BBC ONE =>
1149             {
1150             pnr => 4171,
1151             tsid => 4107,
1152             lcn => 1,
1153             ...
1154             },
1155             $chan => ...
1156             },
1157             'lcn' => {
1158             4107 => {
1159             4171 => {
1160             service_type => 2,
1161             visible => 1,
1162             lcn => 46,
1163             ...
1164             },
1165             },
1166            
1167             $tsid => {
1168             $pnr => ...
1169             }
1170             },
1171             'ts' => {
1172             4107 =>
1173             {
1174             tsid => 4107,
1175             frequency => 57800000,
1176             strength => 46829,
1177             ...
1178             },
1179             $tsid => ..
1180             },
1181             'freqs' => {
1182             57800000 =>
1183             {
1184             strength => 46829,
1185             snr => bbb,
1186             ber => ccc,
1187             ...
1188             },
1189             $freq => ...
1190             },
1191              
1192              
1193              
1194             =cut
1195              
1196              
1197             sub merge_scan_freqs
1198             {
1199 0     0 1 0 my ($new_href, $old_href, $options_href, $verbose, $scan_info_href) = @_ ;
1200              
1201 0 0       0 print STDERR Data::Dumper->Dump(["merge_scan_freqs - Scan info [$scan_info_href]=", $scan_info_href]) if $DEBUG>=5 ;
1202              
1203 0   0     0 $scan_info_href ||= {} ;
1204 0   0     0 $scan_info_href->{'chans'} ||= {} ;
1205 0   0     0 $scan_info_href->{'tsids'} ||= {} ;
1206            
1207 0 0       0 print STDERR "merge_scan_freqs()\n" if $DEBUG ;
1208              
1209 0 0 0     0 if ($old_href && $new_href)
1210             {
1211 0 0       0 print STDERR Data::Dumper->Dump(["New:", $new_href, "Old:", $old_href]) if $DEBUG>=2 ;
1212            
1213             ## gather information on new & existing
1214 0         0 my %old_new_info ;
1215 0         0 $old_new_info{'new'} = _scan_info($new_href) ;
1216 0         0 $old_new_info{'old'} = _scan_info($old_href) ;
1217            
1218             ## Copy special fields first
1219 0         0 my %fields = map {$_ => 1} @SCAN_INFO_FIELDS ;
  0         0  
1220            
1221             # ts
1222 0         0 delete $fields{'ts'} ;
1223 0         0 _merge_tsid($new_href, $old_href, $options_href, $verbose, $scan_info_href, \%old_new_info) ;
1224            
1225             # pr
1226 0         0 delete $fields{'pr'} ;
1227 0         0 _merge_chan($new_href, $old_href, $options_href, $verbose, $scan_info_href, \%old_new_info) ;
1228            
1229             # merge the rest
1230 0         0 foreach my $region (keys %fields)
1231             {
1232 0         0 foreach my $section (keys %{$new_href->{$region}})
  0         0  
1233             {
1234 0 0       0 print STDERR " + Overwrite existing {$region}{$section} with new ....\n" if $DEBUG ;
1235              
1236             ## Just overwrite
1237 0         0 foreach my $field (keys %{$new_href->{$region}{$section}})
  0         0  
1238             {
1239 0         0 $old_href->{$region}{$section}{$field} = $new_href->{$region}{$section}{$field} ;
1240             }
1241             }
1242             }
1243             }
1244              
1245 0 0       0 $old_href = $new_href if (!$old_href) ;
1246            
1247 0 0       0 print STDERR Data::Dumper->Dump(["merge_scan_freqs END - Scan info [$scan_info_href]=", $scan_info_href]) if $DEBUG>=5 ;
1248            
1249 0 0       0 print STDERR "merge_scan_freqs() - DONE\n" if $DEBUG ;
1250            
1251 0         0 return $old_href ;
1252             }
1253              
1254            
1255             #----------------------------------------------------------------------
1256             sub _merge_tsid
1257             {
1258 0     0   0 my ($new_href, $old_href, $options_href, $verbose, $scan_info_href, $new_old_info_href) = @_ ;
1259              
1260 0   0     0 $scan_info_href->{'chans'} ||= {} ;
1261 0   0     0 $scan_info_href->{'tsids'} ||= {} ;
1262            
1263 0 0       0 print STDERR "_merge_tsid()\n" if $DEBUG ;
1264 0 0       0 print STDERR Data::Dumper->Dump(["_merge_tsid()", $new_href->{'ts'}]) if $DEBUG>=2 ;
1265              
1266              
1267             ## Compare new with old
1268 0         0 foreach my $tsid (keys %{$new_old_info_href->{'new'}{'tsid'}})
  0         0  
1269             {
1270 0         0 my $new_chans = scalar(keys %{$new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}}) ;
  0         0  
1271 0         0 my $old_chans = 0 ;
1272              
1273 0         0 my $new_strength_href = _strength_create($new_old_info_href->{'new'}{'tsid'}{$tsid}) ;
1274 0         0 my $old_strength_href = _strength_create(0) ;
1275             # my $new_strength = $new_old_info_href->{'new'}{'tsid'}{$tsid}{'strength'} ;
1276             # my $old_strength = 0 ;
1277            
1278 0         0 my $new_freq = $new_old_info_href->{'new'}{'tsid'}{$tsid}{'freq'} ;
1279 0         0 my $old_freq ;
1280            
1281 0         0 my $overlap = 0 ;
1282 0 0       0 if ( exists($new_old_info_href->{'old'}{'tsid'}{$tsid}) )
1283             {
1284 0         0 $overlap = 1 ;
1285 0         0 $old_chans = scalar(keys %{$new_old_info_href->{'old'}{'tsid'}{$tsid}{'pr'}}) ;
  0         0  
1286             # $old_strength = $new_old_info_href->{'old'}{'tsid'}{$tsid}{'strength'} ;
1287 0         0 $old_strength_href = _strength_create($new_old_info_href->{'old'}{'tsid'}{$tsid}) ;
1288 0         0 $old_freq = $new_old_info_href->{'old'}{'tsid'}{$tsid}{'freq'} ;
1289            
1290 0 0       0 if ($old_freq == $new_freq)
1291             {
1292 0         0 $overlap = 0 ;
1293             }
1294             }
1295            
1296 0   0     0 $scan_info_href->{'tsids'}{$tsid} ||= {
1297             'comments' => [],
1298             } ;
1299            
1300 0         0 my $delete = 0 ;
1301 0         0 my $duplicate = 0 ;
1302 0         0 my $reason = "" ;
1303            
1304 0 0       0 if (!$overlap)
1305             {
1306 0         0 $reason = "[merge] TSID $tsid : creating new freq $new_freq (contains $new_chans chans)" ;
1307             }
1308             else
1309             {
1310             ## overlap - do something
1311 0 0       0 if ($options_href->{'duplicates'})
1312             {
1313 0         0 $duplicate = 1 ;
1314 0         0 $reason = "[duplicate] TSID $tsid : tsid already exists (new freq $new_freq chans $new_chans, old freq $old_freq chans $old_chans), creating duplicate" ;
1315             }
1316             else
1317             {
1318             # do we overwrite based on number of channels a multiplex contains OR on the signal strength
1319 0 0 0     0 if (!$options_href->{'num_chans'} || ($new_chans == $old_chans))
    0          
1320             {
1321              
1322             # overwrite based on signal strength
1323             ## if ($new_strength < $old_strength)
1324 0 0       0 if (_strength_cmp($new_strength_href, $old_strength_href) < 0)
1325             {
1326 0         0 my $new_strength_str = _strength_str($new_strength_href);
1327 0         0 my $old_strength_str = _strength_str($old_strength_href);
1328              
1329 0         0 $delete = 1 ;
1330             # $reason = "[overlap] TSID $tsid : new freq $new_freq strength $new_strength ($new_chans chans) < existing freq $old_freq strength $old_strength ($old_chans chans) - new freq ignored" ;
1331 0         0 $reason = "[overlap] TSID $tsid : new freq $new_freq strength $new_strength_str ($new_chans chans) < existing freq $old_freq strength $old_strength_str ($old_chans chans) - new freq ignored" ;
1332             }
1333             else
1334             {
1335 0         0 my $new_strength_str = _strength_str($new_strength_href);
1336 0         0 my $old_strength_str = _strength_str($old_strength_href);
1337              
1338             # $reason = "[overlap] TSID $tsid : new freq $new_freq strength $new_strength >= existing freq $old_freq strength $old_strength - using new freq" ;
1339 0         0 $reason = "[overlap] TSID $tsid : new freq $new_freq strength $new_strength_str ($new_chans chans) >= existing freq $old_freq strength $old_strength_str ($old_chans chans) - using new freq" ;
1340             }
1341             }
1342            
1343             # compare number of channels
1344             elsif ($new_chans < $old_chans)
1345             {
1346 0         0 $delete = 1 ;
1347 0         0 $reason = "[overlap] TSID $tsid : new freq $new_freq has only $new_chans chans (existing freq $old_freq has $old_chans chans) - new freq ignored" ;
1348             }
1349             else
1350             {
1351 0         0 $reason = "[overlap] TSID $tsid : new freq $new_freq has $new_chans chans (existing freq $old_freq has $old_chans chans) - using new freq" ;
1352             }
1353             }
1354             }
1355            
1356             ## delete if required
1357 0 0       0 if ($delete)
    0          
1358             {
1359 0         0 delete $new_href->{'ts'}{$tsid} ;
1360            
1361 0         0 foreach my $pnr (keys %{$new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}} )
  0         0  
1362             {
1363 0         0 my $chan = $new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}{$pnr} ;
1364 0   0     0 $scan_info_href->{'chans'}{$chan} ||= {
1365             'comments' => [],
1366             } ;
1367 0         0 push @{$scan_info_href->{'chans'}{$chan}{'comments'}}, $reason ;
  0         0  
1368            
1369 0         0 delete $new_href->{'pr'}{$chan} ;
1370             }
1371             }
1372             ## duplicate if required
1373             elsif ($duplicate)
1374             {
1375             ## Create a dummy name for this tsid
1376 0         0 my $suffix = 'a' ;
1377 0         0 my $tsid_dup = "$tsid$suffix" ;
1378 0         0 while (exists($new_old_info_href->{'old'}{'tsid'}{$tsid_dup}))
1379             {
1380 0         0 ++$suffix ;
1381 0         0 $tsid_dup = "$tsid$suffix" ;
1382             }
1383 0         0 $reason .= " TSID $tsid_dup" ;
1384            
1385            
1386             ## rename tsid
1387            
1388             # ts
1389 0         0 my $tsid_href = delete $new_href->{'ts'}{$tsid} ;
1390 0         0 $new_href->{'ts'}{$tsid_dup} = $tsid_href ;
1391 0         0 $new_href->{'ts'}{$tsid_dup}{'tsid'} = $tsid_dup ;
1392            
1393             # pr
1394 0         0 foreach my $pnr (keys %{$new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}} )
  0         0  
1395             {
1396 0         0 my $chan = $new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}{$pnr} ;
1397 0   0     0 $scan_info_href->{'chans'}{$chan} ||= {
1398             'comments' => [],
1399             } ;
1400 0         0 push @{$scan_info_href->{'chans'}{$chan}{'comments'}}, $reason ;
  0         0  
1401            
1402 0         0 $new_href->{'pr'}{$chan}{'tsid'} = $tsid_dup ;
1403             }
1404            
1405             # lcn
1406 0         0 my $lcn_href = delete $new_href->{'lcn'}{$tsid} ;
1407 0         0 $new_href->{'lcn'}{$tsid_dup} = $lcn_href ;
1408              
1409             ## rename chan
1410            
1411             # pr
1412 0         0 foreach my $pnr (keys %{$new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}} )
  0         0  
1413             {
1414 0         0 my $chan = $new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}{$pnr} ;
1415              
1416 0         0 my $count = 1 ;
1417 0         0 my $chan_dup = "$chan ($count)";
1418 0         0 while (exists($new_old_info_href->{'old'}{'pr'}{$chan_dup}))
1419             {
1420 0         0 ++$count ;
1421 0         0 $chan_dup = "$chan ($count)";
1422             }
1423            
1424 0         0 push @{$scan_info_href->{'chans'}{$chan}{'comments'}}, "[duplicate] Renamed $chan to $chan_dup" ;
  0         0  
1425            
1426 0         0 my $chan_href = delete $new_href->{'pr'}{$chan} ;
1427 0         0 $new_href->{'pr'}{$chan_dup} = $chan_href ;
1428 0         0 $new_href->{'pr'}{$chan_dup}{'name'} = $chan_dup ;
1429             }
1430            
1431              
1432 0 0       0 print STDERR " + duplicate TSID\n" if $DEBUG ;
1433 0 0       0 print STDERR Data::Dumper->Dump(["After tsid rename ", $new_href]) if $DEBUG>=2 ;
1434            
1435             }
1436             else
1437             {
1438             ## ok to copy
1439 0         0 foreach my $pnr (keys %{$new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}} )
  0         0  
1440             {
1441 0         0 my $chan = $new_old_info_href->{'new'}{'tsid'}{$tsid}{'pr'}{$pnr} ;
1442 0   0     0 $scan_info_href->{'chans'}{$chan} ||= {
1443             'comments' => [],
1444             } ;
1445 0         0 push @{$scan_info_href->{'chans'}{$chan}{'comments'}}, $reason ;
  0         0  
1446             }
1447             }
1448              
1449             # update TSID debug info
1450 0         0 push @{$scan_info_href->{'tsids'}{$tsid}{'comments'}}, $reason ;
  0         0  
1451             }
1452            
1453             ## Do merge
1454 0         0 foreach my $tsid (keys %{$new_href->{'ts'}})
  0         0  
1455             {
1456             ## Just overwrite
1457 0         0 foreach my $field (keys %{$new_href->{'ts'}{$tsid}})
  0         0  
1458             {
1459 0         0 $old_href->{'ts'}{$tsid}{$field} = $new_href->{'ts'}{$tsid}{$field} ;
1460             }
1461             }
1462              
1463             }
1464              
1465              
1466             #----------------------------------------------------------------------
1467             sub _merge_chan
1468             {
1469 0     0   0 my ($new_href, $old_href, $options_href, $verbose, $scan_info_href, $new_old_info_href) = @_ ;
1470              
1471 0   0     0 $scan_info_href->{'chans'} ||= {} ;
1472 0   0     0 $scan_info_href->{'tsids'} ||= {} ;
1473            
1474 0 0       0 print STDERR "_merge_chan()\n" if $DEBUG ;
1475 0 0       0 print STDERR Data::Dumper->Dump(["_merge_chan()", $new_href->{'pr'}]) if $DEBUG>=2 ;
1476            
1477             ## Do merge
1478 0         0 foreach my $chan (keys %{$new_href->{'pr'}})
  0         0  
1479             {
1480             ## Check for channel rename
1481 0         0 my $tsid = $new_href->{'pr'}{$chan}{'tsid'} ;
1482 0         0 my $pnr = $new_href->{'pr'}{$chan}{'pnr'} ;
1483              
1484 0 0       0 print STDERR " + check {$tsid-$pnr} = $chan \n" if $DEBUG ;
1485            
1486 0 0 0     0 if (exists($new_old_info_href->{'old'}{'tsid-pnr'}{"$tsid-$pnr"}) && ($new_old_info_href->{'old'}{'tsid-pnr'}{"$tsid-$pnr"} ne $chan))
1487             {
1488             # Rename
1489 0         0 my $old_chan = $new_old_info_href->{'old'}{'tsid-pnr'}{"$tsid-$pnr"} ;
1490 0         0 push @{$scan_info_href->{'chans'}{$chan}{'comments'}}, "[merge] channel renamed from \"$old_chan\" to \"$chan\" " ;
  0         0  
1491 0         0 delete $old_href->{'pr'}{$old_chan} ;
1492 0 0       0 print STDERR " + + delete $old_chan \n" if $DEBUG ;
1493             }
1494              
1495             ## Check for channel TSID change
1496 0         0 my $overlap = 0 ;
1497 0 0       0 if (exists($old_href->{'pr'}{$chan}))
1498             {
1499 0         0 $overlap = 1 ;
1500 0 0       0 if ($new_href->{'pr'}{$chan}{'tsid'} eq $old_href->{'pr'}{$chan}{'tsid'})
1501             {
1502 0         0 $overlap = 0 ;
1503             }
1504             }
1505            
1506 0   0     0 $scan_info_href->{'chans'}{$chan} ||= {
1507             'comments' => [],
1508             } ;
1509            
1510 0         0 my $reason ;
1511 0         0 my $copy_chan = $chan ;
1512 0 0       0 if (!$overlap)
1513             {
1514 0         0 $reason = "[merge] creating new channel info" ;
1515             }
1516             else
1517             {
1518             ## overlap - do something
1519 0 0       0 if ($options_href->{'duplicates'})
1520             {
1521             # duplicate
1522 0         0 $reason = "[duplicate] Channel $chan already exists (new TSID $new_href->{'pr'}{$chan}{'tsid'}, old TSID $old_href->{'pr'}{$chan}{'tsid'}), creating duplicate" ;
1523            
1524              
1525 0         0 my $count = 1 ;
1526 0         0 $copy_chan = "$chan ($count)";
1527 0         0 while (exists($old_href->{'pr'}{$copy_chan}))
1528             {
1529 0         0 ++$count ;
1530 0         0 $copy_chan = "$chan ($count)";
1531             }
1532            
1533 0         0 $reason .= " New channel name $copy_chan" ;
1534             }
1535             else
1536             {
1537             # overwrite
1538 0         0 $reason = "[overlap] overwriting existing channel info with new (old: TSID $old_href->{'pr'}{$chan}{tsid})" ;
1539             }
1540             }
1541 0         0 push @{$scan_info_href->{'chans'}{$chan}{'comments'}}, $reason ;
  0         0  
1542              
1543            
1544             ## Now overwrite
1545 0         0 foreach my $field (keys %{$new_href->{'pr'}{$chan}})
  0         0  
1546             {
1547 0         0 $old_href->{'pr'}{$copy_chan}{$field} = $new_href->{'pr'}{$chan}{$field} ;
1548             }
1549 0         0 $old_href->{'pr'}{$copy_chan}{'name'} = $copy_chan ;
1550             }
1551              
1552             }
1553              
1554              
1555              
1556              
1557             #----------------------------------------------------------------------
1558             sub _scan_info
1559             {
1560 0     0   0 my ($scan_href) = @_ ;
1561            
1562             ## Get info on existing
1563 0         0 my %tsid_map ;
1564 0         0 foreach my $chan (keys %{$scan_href->{'pr'}})
  0         0  
1565             {
1566 0         0 my $tsid = $scan_href->{'pr'}{$chan}{'tsid'} ;
1567 0         0 my $pnr = $scan_href->{'pr'}{$chan}{'pnr'} ;
1568 0         0 $tsid_map{"$tsid-$pnr"} = $chan ;
1569             }
1570            
1571             ## Various ways of looking at tsid info
1572 0         0 my %ts_info ;
1573 0         0 foreach my $tsid (keys %{$scan_href->{'ts'}})
  0         0  
1574             {
1575 0         0 my $freq = $scan_href->{'ts'}{$tsid}{'frequency'} ;
1576 0         0 $ts_info{$tsid} = {
1577             'pr' => {},
1578             'freq' => $scan_href->{'ts'}{$tsid}{'frequency'},
1579             'strength' => $scan_href->{'ts'}{$tsid}{'strength'},
1580             'snr' => $scan_href->{'ts'}{$tsid}{'snr'},
1581             'ber' => $scan_href->{'ts'}{$tsid}{'ber'},
1582             } ;
1583             }
1584 0         0 foreach my $chan (keys %{$scan_href->{'pr'}})
  0         0  
1585             {
1586 0         0 my $tsid = $scan_href->{'pr'}{$chan}{'tsid'} ;
1587 0         0 my $pnr = $scan_href->{'pr'}{$chan}{'pnr'} ;
1588 0         0 $ts_info{$tsid}{'pr'}{$pnr} = $chan ;
1589             }
1590            
1591            
1592             ## Various ways of looking at channel info
1593 0         0 my %chan_info ;
1594 0         0 foreach my $chan (keys %{$scan_href->{'pr'}})
  0         0  
1595             {
1596 0         0 my $tsid = $scan_href->{'pr'}{$chan}{'tsid'} ;
1597 0         0 $chan_info{$chan} = $tsid ;
1598             }
1599            
1600 0         0 my %info = (
1601             'tsid-pnr' => \%tsid_map,
1602             'tsid' => \%ts_info,
1603             'chan' => \%chan_info,
1604             ) ;
1605 0         0 return \%info ;
1606             }
1607              
1608              
1609              
1610              
1611             #----------------------------------------------------------------------
1612             # Split the search path & expand all the directories to absolute paths
1613             #
1614             sub _expand_search_path
1615             {
1616 6     6   15 my ($search_path) = @_ ;
1617              
1618 6         36 my @dirs = split /:/, $search_path ;
1619 6         26 foreach my $d (@dirs)
1620             {
1621             # Replace any '~' with $HOME
1622 6         17 $d =~ s/~/\$HOME/g ;
1623            
1624             # Now replace any vars with values from the environment
1625 6         23 $d =~ s/\$(\w+)/$ENV{$1}/ge ;
  0         0  
1626            
1627             # Ensure path is clean
1628 6         370 $d = File::Spec->rel2abs($d) ;
1629             }
1630            
1631 6         23 return @dirs ;
1632             }
1633              
1634             #----------------------------------------------------------------------
1635              
1636             =item B
1637              
1638             Find directory to read from - first readable directory in search path
1639              
1640             =cut
1641              
1642             sub read_dir
1643             {
1644 5     5 1 11 my ($search_path) = @_ ;
1645            
1646 5         21 my @dirs = _expand_search_path($search_path) ;
1647 5         8 my $dir ;
1648            
1649 5         16 foreach my $d (@dirs)
1650             {
1651 5         10 my $found=1 ;
1652 5         24 foreach my $region (keys %FILES)
1653             {
1654 15 100       54 if ($FILES{$region}{'required'})
1655             {
1656 10 50       410 $found=0 if (! -f "$d/$FILES{$region}{'file'}") ;
1657             }
1658             }
1659            
1660 5 50       20 if ($found)
1661             {
1662 5         10 $dir = $d ;
1663 5         19 last ;
1664             }
1665             }
1666              
1667 5 0       17 print STDERR "Searched $search_path : read dir=".($dir?$dir:"")."\n" if $DEBUG ;
    50          
1668            
1669 5         15 return $dir ;
1670             }
1671              
1672             #----------------------------------------------------------------------
1673              
1674             =item B
1675              
1676             Find directory to write to - first writeable directory in search path
1677              
1678             =cut
1679              
1680             sub write_dir
1681             {
1682 1     1 1 3 my ($search_path) = @_ ;
1683              
1684 1         6 my @dirs = _expand_search_path($search_path) ;
1685 1         3 my $dir ;
1686              
1687 1 50       7 print STDERR "Find dir to write to from $search_path ...\n" if $DEBUG ;
1688            
1689 1         5 foreach my $d (@dirs)
1690             {
1691 1         2 my $found=1 ;
1692              
1693 1 50       8 print STDERR " + processing $d\n" if $DEBUG ;
1694              
1695             # See if dir exists
1696 1 50       93 if (!-d $d)
1697             {
1698             # See if this user can create the dir
1699 1         5 eval {
1700 1         306 mkpath([$d], $DEBUG, 0755) ;
1701             };
1702 1 50       5 $found=0 if $@ ;
1703              
1704 1 50       5 print STDERR " + $d does not exist - attempt to mkdir=$found\n" if $DEBUG ;
1705             }
1706              
1707 1 50       25 if (-d $d)
1708             {
1709 1 50       4 print STDERR " + $d does exist ...\n" if $DEBUG ;
1710              
1711             # See if this user can write to the dir
1712 1         6 foreach my $region (keys %FILES)
1713             {
1714 3 50       469 if (open my $fh, ">>$d/$FILES{$region}{'file'}")
1715             {
1716 3         54 close $fh ;
1717              
1718 3 50       19 print STDERR " + + Write to $d/$FILES{$region}{'file'} succeded\n" if $DEBUG ;
1719             }
1720             else
1721             {
1722 0 0       0 print STDERR " + + Unable to write to $d/$FILES{$region}{'file'} - aborting this dir\n" if $DEBUG ;
1723              
1724 0         0 $found = 0;
1725 0         0 last ;
1726             }
1727             }
1728             }
1729            
1730 1 50       6 if ($found)
1731             {
1732 1         6 $dir = $d ;
1733 1         3 last ;
1734             }
1735             }
1736              
1737 1 0       5 print STDERR "Searched $search_path : write dir=".($dir?$dir:"")."\n" if $DEBUG ;
    50          
1738            
1739 1         4 return $dir ;
1740             }
1741              
1742              
1743             #============================================================================================
1744              
1745             =back
1746              
1747             =head3 TSID config file (dvb-ts) read/write
1748              
1749             =over 4
1750              
1751             =cut
1752              
1753              
1754             #----------------------------------------------------------------------
1755              
1756             =item B
1757              
1758             Read the transponder settings file of the form:
1759              
1760             [4107]
1761             name = Oxford/Bexley
1762             frequency = 578000000
1763             bandwidth = 8
1764             modulation = 16
1765             hierarchy = 0
1766             code_rate_high = 34
1767             code_rate_low = 34
1768             guard_interval = 32
1769             transmission = 2
1770            
1771             =cut
1772              
1773             sub read_dvb_ts
1774             {
1775 5     5 1 13 my ($fname) = @_ ;
1776              
1777 5         9 my %dvb_ts ;
1778 5 50       290 open my $fh, "<$fname" or die "Error: Unable to read $fname : $!" ;
1779            
1780 5         16 my $line ;
1781             my $tsid ;
1782 5         125 while(defined($line=<$fh>))
1783             {
1784 225         264 chomp $line ;
1785 225 100       2859 next if $line =~ /^\s*#/ ; # skip comments
1786            
1787 221 100       1268 if ($line =~ /\[([\da-z]+)\]/i)
    100          
    50          
1788             {
1789 18         101 $tsid=$1;
1790             }
1791             elsif ($line =~ /(\S+)\s*=\s*(\S+)/)
1792             {
1793 184 50       409 if ($tsid)
1794             {
1795 184         964 $dvb_ts{$tsid}{$1} = $2 ;
1796             }
1797             }
1798             elsif ($line =~ /(\S+)\s*=/)
1799             {
1800             # skip empty entries
1801             }
1802             else
1803             {
1804 19         85 $tsid = undef ;
1805             }
1806             }
1807 5         61 close $fh ;
1808            
1809 5         44 return \%dvb_ts ;
1810             }
1811              
1812             #----------------------------------------------------------------------
1813              
1814             =item B
1815              
1816             Read the transponder settings file comments section, if present, containing the
1817             frequency file information used during the scan. The values are in "VDR" format:
1818              
1819             # VDR freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy inversion
1820              
1821             For example, the frequency file format:
1822              
1823             # T 578000000 8MHz 2/3 NONE QAM64 2k 1/32 NONE
1824            
1825             will be saved as:
1826            
1827             # VDR 578000000 8 23 0 64 2 32 0 0
1828              
1829             =cut
1830              
1831             sub read_dvb_ts_freqs
1832             {
1833 5     5 1 15 my ($fname) = @_ ;
1834              
1835 5 50       23 print STDERR "read_dvb_ts_freqs($fname)\n" if $DEBUG>=5 ;
1836              
1837 5         16 my %dvb_ts_freqs = () ;
1838 5 50       284 open my $fh, "<$fname" or die "Error: Unable to read $fname : $!" ;
1839            
1840 5         11 my $line ;
1841 5         114 while(defined($line=<$fh>))
1842             {
1843 225         225 chomp $line ;
1844 225 100       854 next unless $line =~ /^\s*#/ ; # skip non-comments
1845            
1846 4 50       10 print STDERR " + line $line\n" if $DEBUG>=5 ;
1847              
1848             ## Parse line
1849 4 100       37 if ($line =~ m%^\s*#\s*VDR\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%i)
1850             {
1851 2         24 my $freq = Linux::DVB::DVBT::dvb_round_freq($1) ;
1852            
1853 2 50       9 if (exists($dvb_ts_freqs{$freq}))
1854             {
1855 0         0 print STDERR "Note: frequency $freq Hz already seen, skipping\n" ;
1856 0         0 next ;
1857             }
1858              
1859 2 50       5 print STDERR " + + add $freq\n" if $DEBUG>=5 ;
1860              
1861 2         54 $dvb_ts_freqs{$freq} = {
1862             frequency => $freq,
1863             bandwidth => $2,
1864             code_rate_high => $3,
1865             code_rate_low => $4,
1866             modulation => $5,
1867             transmission => $6,
1868             guard_interval => $7,
1869             hierarchy => $8,
1870             inversion => $9,
1871             } ;
1872             }
1873            
1874             }
1875 5         55 close $fh ;
1876              
1877 5 50       20 print STDERR Data::Dumper->Dump(["read_dvb_ts_freqs - href=", \%dvb_ts_freqs]) if $DEBUG>=5 ;
1878            
1879 5         31 return \%dvb_ts_freqs ;
1880             }
1881              
1882              
1883             #----------------------------------------------------------------------
1884              
1885             =item B
1886              
1887             Write transponder config information
1888              
1889             =cut
1890              
1891             sub write_dvb_ts
1892             {
1893 1     1 1 3 my ($fname, $href, $freqs_href) = @_ ;
1894              
1895 1 50       75 open my $fh, ">$fname" or die "Error: Unable to write $fname : $!" ;
1896              
1897 1 50       5 print STDERR Data::Dumper->Dump(["write_dvb_ts - href=", $href, "freqs=", $freqs_href]) if $DEBUG>=5 ;
1898            
1899             ## Save frequency list first (if available)
1900 1 50 33     20 if ($freqs_href && (keys %$freqs_href))
1901             {
1902             # # VDR freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy inversion
1903             #
1904             # # VDR 578000000 8 23 0 64 2 32 0 0
1905             #
1906 1         19 print $fh "## freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy inversion\n" ;
1907 1         8 foreach my $freq (sort {$a <=> $b} keys %$freqs_href)
  0         0  
1908             {
1909 1         11 my $tuning_href = $freqs_href->{$freq} ;
1910 1         3 print $fh "# VDR " ;
1911 1         4 foreach my $field (qw/
1912             frequency
1913             bandwidth
1914             code_rate_high
1915             code_rate_low
1916             modulation
1917             transmission
1918             guard_interval
1919             hierarchy
1920             inversion
1921             /)
1922             {
1923 9         39 printf $fh "%d ", $tuning_href->{$field} ;
1924             }
1925 1         5 print $fh "\n" ;
1926             }
1927             }
1928            
1929             # Write config information
1930             #
1931             # 'ts' =>
1932             # 4107 =>
1933             # { # HASH(0x83241b8)
1934             # bandwidth => 8,
1935             # code_rate_hp => 34, code_rate_high
1936             # code_rate_lp => 34, code_rate_low
1937             # constellation => 16, modulation
1938             # frequency => 578000000,
1939             # guard => 32, guard_interval
1940             # hierarchy => 0,
1941             # net => Oxford/Bexley,
1942             # transmission => 2,
1943             # tsid => 4107,
1944             # },
1945             #
1946             #[4107]
1947             #name = Oxford/Bexley
1948             #frequency = 578000000
1949             #bandwidth = 8
1950             #modulation = 16
1951             #hierarchy = 0
1952             #code_rate_high = 34
1953             #code_rate_low = 34
1954             #guard_interval = 32
1955             #transmission = 2
1956             #
1957             #
1958 1         16 foreach my $section (sort {$a <=> $b} keys %$href)
  1         10  
1959             {
1960 2         5 print $fh "[$section]\n" ;
1961 2         4 foreach my $field (sort keys %{$href->{$section}})
  2         15  
1962             {
1963 18         40 my $val = $href->{$section}{$field} ;
1964 18 50       60 if ($val =~ /\S+/)
1965             {
1966 18         39 print $fh "$field = $val\n" ;
1967             }
1968             }
1969 2         7 print $fh "\n" ;
1970             }
1971            
1972 1         51 close $fh ;
1973             }
1974              
1975              
1976             #============================================================================================
1977              
1978             =back
1979              
1980             =head3 Channels config file (dvb-pr) read/write
1981              
1982             =over 4
1983              
1984             =cut
1985              
1986              
1987             #----------------------------------------------------------------------
1988              
1989             =item B
1990              
1991             Read dvb-pr - channel information - of the form:
1992            
1993             [4107-4171]
1994             video = 600
1995             audio = 601
1996             audio_details = eng:601 eng:602
1997             type = 1
1998             net = BBC
1999             name = BBC ONE
2000              
2001             =cut
2002              
2003             sub read_dvb_pr
2004             {
2005 5     5 1 12 my ($fname) = @_ ;
2006              
2007 5         9 my %dvb_pr ;
2008 5 50       340 open my $fh, "<$fname" or die "Error: Unable to read $fname : $!" ;
2009            
2010 5         9 my $line ;
2011             my $pnr ;
2012 0         0 my $tsid ;
2013 5         259 while(defined($line=<$fh>))
2014             {
2015 2098         3168 chomp $line ;
2016 2098 100       6479 next if $line =~ /^\s*#/ ; # skip comments
2017            
2018 2094 100       11006 if ($line =~ /\[([\da-z]+)\-([\d]+)\]/i)
    100          
    50          
2019             {
2020 150         609 ($tsid, $pnr)=($1,$2);
2021             }
2022             elsif ($line =~ /(\S+)\s*=\s*(\S+.*)/)
2023             {
2024 1782 50 33     8959 if ($pnr && $tsid)
2025             {
2026 1782         7109 $dvb_pr{"$tsid-$pnr"}{$1} = $2 ;
2027            
2028             # ensure tsid & pnr are in the hash
2029 1782         3607 $dvb_pr{"$tsid-$pnr"}{'tsid'} = $tsid ;
2030 1782         8042 $dvb_pr{"$tsid-$pnr"}{'pnr'} = $pnr ;
2031             }
2032             }
2033             elsif ($line =~ /(\S+)\s*=/)
2034             {
2035             # skip empty entries
2036             }
2037             else
2038             {
2039 162         188 $pnr = undef ;
2040 162         432 $tsid = undef ;
2041             }
2042             }
2043 5         91 close $fh ;
2044            
2045             # Make channel name the first key
2046 5         18 my %chans ;
2047 5         131 foreach (keys %dvb_pr)
2048             {
2049             # handle chans with no name
2050 150   33     497 my $name = $dvb_pr{$_}{'name'} || $_ ;
2051 150         838 $chans{$name} = $dvb_pr{$_} ;
2052             }
2053            
2054 5         104 return \%chans ;
2055             }
2056              
2057             #----------------------------------------------------------------------
2058              
2059             =item B
2060              
2061             Write program config file.
2062              
2063             =cut
2064              
2065             sub write_dvb_pr
2066             {
2067 1     1 1 4 my ($fname, $href) = @_ ;
2068              
2069 1 50       64 open my $fh, ">$fname" or die "Error: Unable to write $fname : $!" ;
2070            
2071             # Write config information
2072             #
2073             # 'pr' =>
2074             # BBC ONE =>
2075             # { # HASH(0x8327848)
2076             # a_pid => 601, audio
2077             # audio => eng:601 eng:602, audio_details
2078             # ca => 0,
2079             # name => "BBC ONE",
2080             # net => BBC,
2081             # p_pid => 4171, -N/A-
2082             # pnr => 4171,
2083             # running => 4,
2084             # t_pid => 0, teletext
2085             # tsid => 4107,
2086             # type => 1,
2087             # v_pid => 600, video
2088             # version => 26, -N/A-
2089             # },
2090             #
2091             #[4107-4171]
2092             #video = 600
2093             #audio = 601
2094             #audio_details = eng:601 eng:602
2095             #type = 1
2096             #net = BBC
2097             #name = BBC ONE
2098             #
2099 1 50       8 foreach my $section (sort {
  15         57  
2100             $href->{$a}{'tsid'} <=> $href->{$b}{'tsid'}
2101             ||
2102             $href->{$a}{'pnr'} <=> $href->{$b}{'pnr'}
2103             } keys %$href)
2104             {
2105 8         24 print $fh "[$href->{$section}{tsid}-$href->{$section}{pnr}]\n" ;
2106 8         11 foreach my $field (sort keys %{$href->{$section}})
  8         40  
2107             {
2108 64         126 my $val = $href->{$section}{$field} ;
2109 64 50       195 if ($val =~ /\S+/)
2110             {
2111 64         133 print $fh "$field = $val\n" ;
2112             }
2113             }
2114 8         19 print $fh "\n" ;
2115             }
2116            
2117 1         41 close $fh ;
2118             }
2119              
2120              
2121             #============================================================================================
2122              
2123             =back
2124              
2125             =head3 Channel names aliases config file (dvb-aliases) read/write
2126              
2127             =over 4
2128              
2129             =cut
2130              
2131             #----------------------------------------------------------------------
2132              
2133             =item B
2134              
2135             Read dvb-aliases - channel names aliases - of the form:
2136            
2137             FIVE = Channel 5
2138              
2139             =cut
2140              
2141             sub read_dvb_aliases
2142             {
2143 5     5 1 14 my ($fname) = @_ ;
2144              
2145 5         11 my %dvb_aliases ;
2146              
2147             #print STDERR "read_dvb_aliases($fname)\n" ;
2148              
2149 5 100       316 if (-f $fname)
2150             {
2151 2 50       194 open my $fh, "<$fname" or die "Error: Unable to read $fname : $!" ;
2152            
2153 2         6 my $line ;
2154 2         48 while(defined($line=<$fh>))
2155             {
2156 8         11 chomp $line ;
2157 8 100       39 next if $line =~ /^\s*#/ ; # skip comments
2158 4         20 $line =~ s/\s+$// ;
2159 4         8 $line =~ s/^\s+// ;
2160             # print STDERR "!! $line !!\n" ;
2161              
2162 4 100       21 if ($line =~ /(\S+[^=]+)\s*=\s*(\S+[^=]+)\s*/)
2163             {
2164 3         10 my ($from, $to) = ($1, $2) ;
2165            
2166 3         12 $from =~ s/\s+$// ;
2167            
2168 3         25 $dvb_aliases{$from} = $to ;
2169             # print STDERR " + <$from> = <$to>\n" ;
2170             }
2171             }
2172 2         27 close $fh ;
2173            
2174             }
2175             #print STDERR "read_dvb_aliases - done\n" ;
2176            
2177 5         32 return \%dvb_aliases ;
2178             }
2179              
2180              
2181             #----------------------------------------------------------------------
2182              
2183             =item B
2184              
2185             Write channel names aliases config file.
2186              
2187             =cut
2188              
2189             sub write_dvb_aliases
2190             {
2191 1     1 1 7 my ($fname, $href) = @_ ;
2192              
2193 1 50       65 open my $fh, ">$fname" or die "Error: Unable to write $fname : $!" ;
2194            
2195             # Write config information
2196             #
2197             # 'aliases' =>
2198             # "FIVE" => "Channel 5"
2199             #
2200             # FIVE = Channel 5
2201             #
2202 1         9 foreach my $from (sort keys %$href)
2203             {
2204 0         0 my $val = $href->{$from} ;
2205 0 0       0 if ($val =~ /\S+/)
2206             {
2207 0         0 print $fh "$from = $val\n" ;
2208             }
2209             }
2210            
2211 1         12 close $fh ;
2212             }
2213              
2214              
2215             #============================================================================================
2216              
2217             # TSID strength/snr/ber
2218              
2219             #----------------------------------------------------------------------
2220             sub _strength_create
2221             {
2222 0     0     my ($href) = @_ ;
2223              
2224 0           my $strength_href = {
2225             'strength' => 0,
2226             'snr' => 0,
2227             'ber' => undef,
2228            
2229             'use' => undef,
2230             } ;
2231              
2232 0 0         print STDERR "_strength_create()\n" if $DEBUG ;
2233              
2234 0 0         if (ref($href) eq 'HASH')
2235             {
2236 0           foreach my $field (qw/strength snr ber/)
2237             {
2238 0 0         print STDERR " + $field = $href->{$field}\n" if $DEBUG ;
2239              
2240 0 0         $strength_href->{$field} = $href->{$field} if exists($href->{$field}) ;
2241              
2242             # Handle special case where value reads back as all 1's
2243 0 0         if ($strength_href->{$field} == 0xffff)
2244             {
2245 0 0         print STDERR " + + clamped dodgy value\n" if $DEBUG ;
2246              
2247             # treat it as a bad value
2248 0           $strength_href->{$field} = 0 ;
2249             }
2250             }
2251            
2252             # # Handle special case where strength reads back as all 1's
2253             # if ($strength_href->{'strength'} == 0xffff)
2254             # {
2255             # # treat it as a bad value
2256             # $strength_href->{'strength'} = 0 ;
2257             # }
2258             }
2259            
2260 0           return $strength_href ;
2261             }
2262              
2263              
2264             #----------------------------------------------------------------------
2265             sub _strength_cmp
2266             {
2267 0     0     my ($a_href, $b_href) = @_ ;
2268              
2269             ## Work through the fields in order of preference
2270 0           my $use ;
2271 0           foreach my $field (qw/snr strength ber/)
2272             {
2273 0 0 0       if (defined($a_href->{$field}) && defined($b_href->{$field}) && ($a_href->{$field} > 0) && ($a_href->{$field} > 0))
      0        
      0        
2274             {
2275 0           $use = $field ;
2276 0           last ;
2277             }
2278             }
2279              
2280 0 0         print STDERR "_strength_cmp()\n" if $DEBUG ;
2281            
2282 0   0       $use ||= 'strength' ;
2283 0           $a_href->{'use'} = $use ;
2284 0           $b_href->{'use'} = $use ;
2285              
2286 0           my $a_val = $a_href->{$use} ;
2287 0           my $b_val = $b_href->{$use} ;
2288 0 0         if ($use eq 'ber')
2289             {
2290 0           $a_val = 0xffff - $a_val ;
2291 0           $b_val = 0xffff - $b_val ;
2292             }
2293              
2294 0 0         print STDERR " + using $use - $a_val <=> $b_val\n" if $DEBUG ;
2295            
2296 0           return $a_val <=> $b_val ;
2297             }
2298              
2299             #----------------------------------------------------------------------
2300             sub _strength_str
2301             {
2302 0     0     my ($href) = @_ ;
2303              
2304 0           my $str = "unset" ;
2305 0 0         if ($href->{'use'})
2306             {
2307 0           $str = "$href->{$href->{use}} ($href->{use})" ;
2308             }
2309 0           return $str ;
2310             }
2311              
2312             # ============================================================================================
2313             # END OF PACKAGE
2314             1;
2315