The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Linux::DVB::DVBT - Perl extension for DVB terrestrial recording, epg, and scanning

SYNOPSIS

        use Linux::DVB::DVBT;
  
        # get list of installed adapters
        my @devices = Linux::DVB::DVBT->device_list() ;
        foreach (@devices)
        {
                printf "%s : adapter number: %d, frontend number: %d\n", 
                        $_->{name}, $_->{adapter_num}, $_->{frontend_num} ;
        }
  
        # Create a dvb object using the first dvb adapter in the list
        my $dvb = Linux::DVB::DVBT->new() ;
        
        # .. or specify the device numbers
        my $dvb = Linux::DVB::DVBT->new(
                'adapter_num' => 2,
                'frontend_num' => 1,
        ) ;


        # Scan for channels - using frequency file
        $dvb->scan_from_file('/usr/share/dvb/dvb-t/uk-Oxford') ;
        
        # Scan for channels - using country code
        $dvb->scan_from_file('GB') ;
        
        # Scan for channels - if scanned before, use previous frequencies
        $dvb->scan_from_previous() ;
        
        # Set channel
        $dvb->select_channel("BBC ONE") ;
        
        # Get EPG data
        my ($epg_href, $dates_href) = $dvb->epg() ;

        # Record 30 minute program (after setting channel using select_channel method)
        $dvb->record('test.ts', 30*60) ;

        ## Record multiple programs in parallel (in the same multiplex)
        
                # parse arguments
                my @args = qw/file=itv2.mpeg ch=itv2 len=0:30 event=41140
                               file=five.mpeg ch=five len=0:30 off=0:15 event=11134 max_timeslip=2:00
                               file=itv1.mpeg ch=itv1 len=0:30 off=0:30
                               file=more4.mpeg ch=more4 len=0:05 off=0:15
                               file=e4.mpeg ch=e4 len=0:30 off=0:05
                               file=ch4+1.mpeg ch='channel4+1' len=1:30 off=0:05/ ;
                
                my @chan_spec ;
                $dvb->multiplex_parse(\@chan_spec, @ARGV);
        
                # Select the channel(s)
                $dvb->multiplex_select(\@chan_spec) ;
        
                # Get multiplex info
                my %multiplex_info = $dvb->multiplex_info() ;

                # Record
                $dvb->multiplex_record(%multiplex_info) ;


        ## Release the hardware (to allow a new recording to start)
        $dvb->dvb_close() ;
        

        # show the logical channel numbers
        my $tuning_href = $dvb->get_tuning_info() ;
        my $channels_aref = $dvb->get_channel_list() ;
        
        print "Chans\n" ;
        foreach my $ch_href (@$channels_aref)
        {
                my $chan = $ch_href->{'channel'} ;
                printf "%3d : %-40s %5d-%5d $ch_href->{type}\n", 
                        $ch_href->{'channel_num'},
                        $chan,
                        $tuning_href->{'pr'}{$chan}{'tsid'},
                        $tuning_href->{'pr'}{$chan}{'pnr'} ;
        }

DESCRIPTION

Linux::DVB::DVBT is a package that provides an object interface to any installed Freeview tuner cards fitted to a Linux PC. The package supports initial set up (i.e. frequency scanning), searching for the latest electronic program guide (EPG), and selectign a channel for recording the video to disk.

Additional Modules

Along with this module, the following extra modules are provided:

Linux::DVB::DVBT::Config

Configuration files and data utilities

Linux::DVB::DVBT::Utils

Miscellaneous utilities

Linux::DVB::DVBT::Ffmpeg

Helper module that wraps up useful ffmpeg calls to post-process recorded files.

Logical Channel Numbers (LCNs)

Where broadcast, the scan function will gather the logical channel number information for all of the channels. The scan() method now stores the LCN information into the config files, and makes the list of channels available through the "get_channel_list()" method. So you can now get the channel number you see (and enter) on any standard freeview TV or PVR.

This is of most interest if you want to use the "epg()" method to gather data to create a TV guide. Generally, you'd like the channel listings to be sorted in the order to which we've all become used to through TV viewing (i.e. it helps to have BBC1 appear before channel 4!).

TVAnytime

New in this version is the gathering of TV Anytime series and program information by the epg function. Where available, you now have a 'tva_series' and 'tva_program' field in the epg HASH that contains the unique TV Anytime number for the series and program respectfully. This is meant to ensure that you can determine the program and series uniquely and allow you to not re-record programs. In reality, I've found that some broadcasters use different series identifiers even when the same series is shown at a different time!

At present, I use the series identifier to group recordings within a series (I then rename the series directory something more meaningful!). Within a series, the program identifier seems to be useable to determine if the program has been recorded before.

Multiplex Recording

Another new feature in this version is support for multiplex recording (i.e. being able to record multiple streams/programs at the same time, as long as they are all in the same multiplex). As you can imagine, specifying the recording of multiple programs (many of which will be different lengths and start at diffent times) can get quite involved.

To simplify these tasks in your scripts, I've written various "helpers" that handle parsing command line arguments, through to optionally running ffmpeg to transcode the recorded files. These are all in addition to the base function that adds a demux filter to the list that will be recorded (see "add_demux_filter($pid, $pid_type [, $tsid])"). Feel free to use as much (or as little) of the helper functions as you like - you can always write your own scripts using add_demux_filter().

For details of the ffmpeg helper functions, please see Linux::DVB::DVBT::Ffmpeg. Obviously, you need to have ffmpeg installed on your system for any of the functions to work!

To record multiple channels (in the same multiplex) at once, you need something like:

        use Linux::DVB::DVBT;

        ## Parse command line
        my @chan_spec ;
        my $error = $dvb->multiplex_parse(\@chan_spec, @ARGV);
        
        ## Select the channel(s)
        my %options = (
                'lang'          => $lang,
                'out'           => $out,
                'tsid'          => $tsid,
        ) ;
        $error = $dvb->multiplex_select(\@chan_spec, %options) ;
        
        ## Get multiplex info
        my %multiplex_info = $dvb->multiplex_info() ;

        ## Record
        $dvb->multiplex_record(%multiplex_info) ;

        ## Release the hardware (to allow a new recording to start)
        $dvb->dvb_close() ;
        
        ## [OPTIONAL] Transcode the recordings (uses ffmpeg helper module)
        $error = $dvb->multiplex_transcode(%multiplex_info) ;

Note, the old record() function has been re-written to use the same underlying multiplex functions. This means that, even though you are only recording a single program, you can still use the ffmpeg helper transcode functions after the recording has finished. For example:

        ## Record
        $dvb->record("$dir$name$ext", $duration) ;
        
        ## Release DVB (for next recording)
        $dvb->dvb_close() ;
        
        ## Get multiplex info
        my %multiplex_info = $dvb->multiplex_info() ;
        
        ## Transcode the recordings (uses ffmpeg helper module)
        $dvb->multiplex_transcode(%multiplex_info) ;
        
        ## Display ffmpeg output / warnings / errors
        foreach my $line (@{$multiplex_info{'lines'}})
        {
                info("[ffmpeg] $line") ;
        }
        
        foreach my $line (@{$multiplex_info{'warnings'}})
        {
                info("[ffmpeg] WARN: $line") ;
        }
        
        foreach my $line (@{$multiplex_info{'errors'}})
        {
                info("[ffmpeg] ERROR: $line") ;
        }

Since this is a new feature, I've left access to the original recording method but renamed it record_v1(). If, for any reason, you wish to use the original recording method, then you need to change your scripts to call the renamed function. But please contact me if you are having problems, and I will do my best to fix them. Future releases will eventually drop the old recording method.

Using UDEV

If, like me, you have more than one adapter fitted and find the order in which the adapters are numbered changes with reboots, then you may like to use udev to define rules to fix your adapters to known numbers (see http://www.mythtv.org/wiki/Device_Filenames_and_udev for further details).

To create rules you make a file in /etc/udev/rules.d and call it something like 100-dvb.rules. The rules file then needs to create a rule for each adapter that creates a link for all of the low-level devices (i.e. the frontend0, dvr0 etc). Each line matches information about the device (using rules with "=="), then applies some setting rules (signified by using "=") to create the symlink.

For example, the following:

         SUBSYSTEM=="dvb", ATTRS{manufacturer}=="Hauppauge", ATTRS{product}=="Nova-T Stick", ATTRS{serial}=="4030521975"

matches a Hauppage Nova-T adapter with serial number "4030521975". Note that this will match all of the devices (dvr0, frontend0 etc) for this adapter. The "set" rule needs to use some variables to create a link for each device.

The set rule we use actually calls a "program" to edit some variables and output the final string followed by a rule that creates the symlink:

        PROGRAM="/bin/sh -c 'K=%k; K=$${K#dvb}; printf dvb/adapter101/%%s $${K#*.}'", SYMLINK+="%c"
        

The PROGRAM rule runs the sh shell and manipulates the kernel name string (which will be something like dvb/adapter0/dvr0) and creates a string with a new adapter number (101 in this case). The SYMLINK rule uses this output (via the %c variable).

Putting this together in a file:

        # /etc/udev/rules.d/100-dvb.rules
        # 
        # To Ientify serial nos etc for a Device call
        # udevadm info -a -p $(udevadm info -q path -n /dev/dvb/adapter0/frontend0)
        #
        
        # Locate 290e at 100
        SUBSYSTEM=="dvb", ATTRS{manufacturer}=="PCTV Systems", ATTRS{product}=="PCTV 290e", PROGRAM="/bin/sh -c 'K=%k; K=$${K#dvb}; printf dvb/adapter100/%%s $${K#*.}'", SYMLINK+="%c"
        
        # Locate Nova-T at 101
        SUBSYSTEM=="dvb", ATTRS{manufacturer}=="Hauppauge", ATTRS{product}=="Nova-T Stick", PROGRAM="/bin/sh -c 'K=%k; K=$${K#dvb}; printf dvb/adapter101/%%s $${K#*.}'", SYMLINK+="%c"

On my system this locates my PCTV DVB-T2 stick at /dev/dvb/adapter100 and my Nova-T stick at /dev/dvb/adapter101.

You can then refer to these devices using the 'adapter_num' field as 100 and 101 (or via the 'adapter' field as '100:0' and '101:0').

Example Scripts

Example scripts have been provided in the package which illustrate the expected use of the package (and are useable programs in themeselves). To see the full man page of each script, simply run it with the '-man' option.

dvbt-devices

Shows information about fited DVB-T tuners

dvbt-scan

Run this by providing the frequency file (usually stored in /usr/share/dvb/dvb-t). If run as root, this will set up the configuration files for all users. For example:

   $ dvbt-scan /usr/share/dvb/dvb-t/uk-Oxford

NOTE: Frequency files are provided by the 'dvb' rpm package available for most distros

dvbt-chans

Use to display the current list of tuned channels. Shows them in logical channel number order. The latest version shows information on the PID numbers for the video, audio, teletext, and subtitle streams that make up each channel.

It also now has the option (-multi) to display the channels grouped into their multiplexes (i.e. their transponder or TSIDs). This becomes really useful if you want to schedule a multiplex recording and need to check which channels you can record at the same time.

dvbt-epg

When run, this grabs the latest EPG information and prints out the program guide:

   $ dvbt-epg

NOTE: This process can take quite a while (it takes around 30 minutes on my system), so please be patient.

dvbt-record

Specify the channel, the duration, and the output filename to record a channel:

   $ dvbt-record "bbc1" spooks.ts 1:00 
   

Note that the duration can be specified as an integer (number of minutes), or in HH:MM format (for hours and minutes)

dvbt-ffrec

Similar to dvbt-record, but pipes the transport stream into ffmpeg and uses that to transcode the data directly into an MPEG file (without saving the transport stream file).

Specify the channel, the duration, and the output filename to record a channel:

   $ dvbt-ffrec "bbc1" spooks.mpeg 1:00 
   

Note that the duration can be specified as an integer (number of minutes), or in HH:MM format (for hours and minutes)

It's worth mentioning that this relies on ffmpeg operating correctly. Some versions of ffmpeg are fine; others have failed reporting:

  "error, non monotone timestamps"

which appear to be related to piping the in via stdin (running ffmpeg on a saved transport stream file always seems to work)

dvbt-multirec

Record multiple channels at the same time (as long as they are all in the same multiplex).

Specify each recording with a filename, duration, and optional offset start time. Then specify the channel name, or a list of the pids you want to record. Repeat this for every file you want to record.

For example, you want to record some programs starting at 13:00. The list of programs are:

  • ITV2 start 13:00, duration 0:30

  • FIVE start 13:15, duration 0:30

  • ITV1 start 13:30, duration 0:30

  • More 4 start 13:15, duration 0:05

  • E4 start 13:05, duration 0:30

  • Channel 4+1 start 13:05, duration 1:30

To record these (running the script at 13:00) use:

   $ dvbt-multirec file=itv2.mpeg ch=itv2 len=0:30  \
                       file=five.mpeg ch=five len=0:30 off=0:15 \
                       file=itv1.mpeg ch=itv1 len=0:30 off=0:30 \
                       file=more4.mpeg ch=more4 len=0:05 off=0:15 \
                       file=e4.mpeg ch=e4 len=0:30 off=0:05 \
                       file=ch4+1.mpeg ch='channel4+1' len=1:30 off=0:05 
   

HISTORY

I started this package after being lent a Hauppauge WinTV-Nova-T usb tuner (thanks Tim!) and trying to do some command line recording. After I'd failed to get most applications to even talk to the tuner I discovered xawtv (http://linux.bytesex.org/xawtv/), started looking at it's source code and started reading the DVB-T standards.

This package is the result of various expermients and is being used for my web TV listing and program record scheduling software.

FIELDS

All of the object fields are accessed via an accessor method of the same name as the field, or by using the set method where the field name and value are passed as key/value pairs in a HASH

adapter_num - DVB adapter number

Number of the DVBT adapter. When multiple DVBT adapters are fitted to a machine, they will be numbered from 0 onwards. Use this field to select the adapter.

frontend_num - DVB frontend number

A single adapter may have multiple frontends. If so then use this field to select the frontend within the selected adapter.

adapter - DVB adapter

Instead of supplying an individual adapter number and frontend number, you can use this field to supply both using the syntax:

        <adapter number>:<frontend number>

If no frontend number is specified then the firast valid frontend number for that adapter is used.

frontend_name - Device path for frontend (set multiplex)

Once the DVBT adapter has been selected, read this field to get the device path for the frontend. It will be of the form: /dev/dvb/adapter0/frontend0

demux_name - Device path for demux (select channel within multiplex)

Once the DVBT adapter has been selected, read this field to get the device path for the demux. It will be of the form: /dev/dvb/adapter0/demux0

dvr_name - Device path for dvr (video record access)

Once the DVBT adapter has been selected, read this field to get the device path for the dvr. It will be of the form: /dev/dvb/adapter0/dvr0

debug - Set debug level

Set this to the required debug level. Higher values give more verbose information.

devices - Fitted DVBT adapter list

Read this ARRAY ref to get the list of fitted DVBT adapters. This is equivalent to running the "device_list()" class method (see "device_list()" for array format)

merge - Merge scan results

Set this flag before running the scan() method. When set, the scan will merge the new results with any previous scan results (read from the config files)

By default this flag is set (so each scan merge with prvious results). Clear this flag to re-start from fresh - useful when broadcasters change the frequencies.

frontend_params - Last used frontend settings

This is a HASH ref containing the parameters used in the last call to "set_frontend(%params)" (either externally or internally by this module).

config_path - Search path for configuration files

Set to ':' separated list of directories. When the module wants to either read or write configuration settings (for channel frequencies etc) then it uses this field to determine where to read/write those files from.

By default this is set to:

    /etc/dvb:~/.tv

Which means that the files are read from /etc/dvb if it has been created (by root); or alternatively it uses ~/.tv (which also happens to be where xawtv stores it's files). Similarly, when writing files these directories are searched until a writeable area is found (so a user won't be able to write into /etc/dvb).

tuning - Channel tuning information

Use this field to read back the tuning parameters HASH ref as scanned or read from the configuration files (see "scan()" method for format)

This field is only used internally by the object but can be used for debug/information.

errmode - Set error handling mode

Set this field to one of 'die' (the default), 'return', or 'message' and when an error occurs that error mode action will be taken.

If the mode is set to 'die' then the application will terminate after printing all of the errors stored in the errors list (see "errors" field). When the mode is set to 'return' then the object method returns control back to the calling application with a non-zero status (which is actually the current count of errors logged so far). Similalrly, if the mode is set to 'message' then the object method simply returns the error message. It is the application's responsibility to handle the errors (stored in "errors") when setting the mode to 'return' or 'message'.

timeout - Timeout

Set hardware timeout time in milliseconds. Most hardware will be ok using the default (900ms), but you can use this field to increase the timeout time.

add_si - Automatically add SI tables

By default, recorded files automatically have the SI tables (the PAT & PMT for the program) recorded along with the usual audio/video streams. This is the new default since the latest version of ffmpeg refuses to understand the encoding of any video streams unless this information is added.

If you really want to, you can change this flag to 0 to prevent SI tables being added in all cases.

NOTE: You still get the tables whenever you add subtitles.

errors - List of errors

This is an ARRAY ref containing a list of any errors that have occurred. Each error is stored as a text string.

CONSTRUCTOR

new([%args])

Create a new object.

The %args are specified as they would be in the set method, for example:

        'adapter_num' => 0

The full list of possible arguments are as described in the "FIELDS" section

dvb_close()

Close the hardware down (for example, to allow another script access), without destroying the object.

CLASS METHODS

Use as Linux::DVB::DVBT->method()

debug([$level])

Set new debug level. Returns setting.

dvb_debug([$level])

Set new debug level for dvb XS code

verbose([$level])

Set new verbosity level. Returns setting.

device_list()

Return list of available hardware as an array of hashes. Each hash entry is of the form:

    {
        'device'        => device name (e.g. '/dev/dvb/adapter0')
        'name'          => Manufacturer name
        'adpater_num'   => Adapter number
        'frontend_num'  => Frontend number
        'flags'         => Adapter capability flags

        'capabilities'  => HASH (see below)
        'fe_type'               => Frontend type (e.g. 'FE_OFDM')
        'type'                  => adapter type (e.g. 'DVB-T')

        'frequency_max' => Maximum supported frequency
        'frequency_min' => Minimum supported frequency
        'frequency_stepsize' => Frequency stepping
    }

          
  The 'flags' field is split into a HASH under the 'capabilities' field, each capability a flag that is set or cleared:
          
        'capabilities' => {
                              'FE_CAN_QAM_16' => 1,
                              'FE_CAN_TRANSMISSION_MODE_AUTO' => 1,
                              'FE_IS_STUPID' => 0,
                              'FE_CAN_QAM_AUTO' => 1,
                              'FE_CAN_FEC_1_2' => 1,
                              'FE_CAN_QAM_32' => 0,
                              'FE_CAN_FEC_5_6' => 1,
                              'FE_CAN_FEC_6_7' => 0,
                              'FE_CAN_HIERARCHY_AUTO' => 1,
                              'FE_CAN_RECOVER' => 1,
                              'FE_CAN_FEC_3_4' => 1,
                              'FE_CAN_FEC_7_8' => 1,
                              'FE_CAN_FEC_2_3' => 1,
                              'FE_CAN_QAM_128' => 0,
                              'FE_CAN_FEC_4_5' => 0,
                              'FE_CAN_FEC_AUTO' => 1,
                              'FE_CAN_QPSK' => 1,
                              'FE_CAN_QAM_64' => 1,
                              'FE_CAN_QAM_256' => 0,
                              'FE_CAN_8VSB' => 0,
                              'FE_CAN_GUARD_INTERVAL_AUTO' => 1,
                              'FE_CAN_BANDWIDTH_AUTO' => 0,
                              'FE_CAN_INVERSION_AUTO' => 1,
                              'FE_CAN_MUTE_TS' => 0,
                              'FE_CAN_16VSB' => 0
                            }
                            

Where a device is actually a link to a real device, there is the additonal field:

        'symlink'       => {
                
        'adpater_num'   => Adapter number
        'frontend_num'  => Frontend number
                
        }

which details the real device the link points to.

By default, this routine will only return details of DVB-T/T2 adapters. To return the list of all adapters discovered (including DVB-C etc) add the optional arguments:

        'show' => 'all'

for example:

        my @devices = $dvb->device_list('show' => 'all') ;

Note that this information is also available via the object instance using the 'devices' method, but this returns an ARRAY REF (rather than an ARRAY)

is_error()

If there was an error during one of the function calls, returns the error string; otherwise returns "".

OBJECT METHODS

set(%args)

Set one or more settable parameter.

The %args are specified as a hash, for example

        set('frequency' => 578000)

The full list of possible arguments are as described in the "FIELDS" section

handle_error($error_message)

Add the error message to the error log and then handle the error depending on the setting of the 'errmode' field.

Get the log as an ARRAY ref via the 'errors()' method.

SCANNING

scan()

Starts a channel scan using previously set tuning. On successful completion of a scan, saves the results into the configuration files.

Returns the discovered channel information as a HASH:

    'pr' => 
    { 
        $channel_name => 
        { 
          'audio' => "407",
          'audio_details' => "eng:407 und:408",
          'ca' => "0",
          'name' => "301",
          'net' => "BBC",
          'pnr' => "19456",
          'running' => "4",
          'teletext' => "0",
          'tsid' => "16384",
          'type' => "1",
          'video' => "203",
          'lcn' => 301
        },
                ....
    },
    
    'ts' =>
    {
      $tsid => 
        { 
          'bandwidth' => "8",
          'code_rate_high' => "23",
          'code_rate_low' => "12",
          'frequency' => "713833330",
          'guard_interval' => "32",
          'hierarchy' => "0",
          'modulation' => "64",
          'net' => "Oxford/Bexley",
          'transmission' => "2",
        },
        ...
    }

Normally this information is only used internally.

scan_from_file($freq_file)

Reads the DVBT frequency file (usually stored in /usr/share/dvb/dvb-t) and uses the contents to set the frontend to the initial frequency. Then starts a channel scan using that tuning.

$freq_file must be the full path to the file. The file contents should be something like:

   # Oxford
   # T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy
   T 578000000 8MHz 2/3 NONE QAM64 2k 1/32 NONE

NOTE: Frequency files are provided by the 'dvb' rpm package available for most distros

Returns the discovered channel information as a HASH (see "scan()")

scan_from_country($iso3166)

Given a 2 letter country code (as defined by ISO 3166-1) attempts to scan those frequencies to produce a scan list.

Note that this routine relies on the adapter supporting auto settings for most of the parameters. Older adapters may not work properly.

Returns the discovered channel information as a HASH (see "scan()")

scan_from_previous()

Uses the last scan frequencies to re-scan. This assumes that a scan was completed and saved to the configuration file (see Linux::DVB::DVBT::Config::read_dvb_ts_freqs($fname)).

Note: this will only work for scans completed with version 2.11 (and later) of this module.

Returns the discovered channel information as a HASH (see "scan()")

TUNING

is_busy()

Returns 0 is the currently selected adapter frontend is not busy; 1 if it is.

set_frontend(%params)

Tune the frontend to the specified frequency etc. HASH %params contains:

    'frequency'
    'inversion'
    'bandwidth'
    'code_rate_high'
    'code_rate_low'
    'modulation'
    'transmission'
    'guard_interval'
    'hierarchy'
    'timeout'
    'tsid'

(If you don't know what these parameters should be set to, then I recommend you just use the "select_channel($channel_name)" method)

Returns 0 if ok; error code otherwise

set_demux($video_pid, $audio_pid, $subtitle_pid, $teletext_pid)

Selects a particular video/audio stream (and optional subtitle and/or teletext streams) and sets the demultiplexer to those streams (ready for recording).

(If you don't know what these parameters should be set to, then I recommend you just use the "select_channel($channel_name)" method)

Returns 0 for success; error code otherwise.

select_channel($channel_name)

Tune the frontend & the demux based on $channel_name.

This method uses a "fuzzy" search to match the specified channel name with the name broadcast by the network. The case of the name is not important, and neither is whitespace. The search also checks for both numeric and name instances of a number (e.g. "1" and "one").

For example, the following are all equivalent and match with the broadcast channel name "BBC ONE":

    bbc1
    BbC One
    b b c    1  

Returns 0 if ok; error code otherwise

get_tuning_info()

Check to see if 'tuning' information has been set. If not, attempts to read from the config search path.

Returns a HASH ref of tuning information - i.e. it contains the complete information on all transponders (under the 'ts' field), and all programs (under the 'pr' field). [see "scan()" method for format].

Otherwise returns undef if no information is available.

get_channel_list()

Checks to see if 'channel_list' information has been set. If not, attempts to create a list based on the scan information.

NOTE that the created list will be the best attempt at ordering the channels based on the TSID & PNR which won't be pretty, but it'll be better than nothing!

Returns an ARRAY ref of channel_list information; otherwise returns undef. The array is sorted by logical channel number and contains HASHes of the form:

        {
                'channel'               => channel name (e.g. "BBC THREE") 
                'channel_num'   => the logical channel number (e.g. 7)
                'type'                  => radio or tv channel ('radio', 'tv' or 'hd-tv')
        }
signal_quality()

Measures the signal quality of the currently tuned transponder. Returns a HASH ref containing:

        {
                'ber'                                   => Bit error rate (32 bits)
                'snr'                                   => Signal to noise ratio (maximum is 0xffff)
                'strength'                              => Signal strength (maximum is 0xffff)
                'uncorrected_blocks'    => Number of uncorrected blocks (32 bits)
                'ok'                                    => flag set if no errors occured during the measurements
        }

Note that some tuner hardware may not support some (or any) of the above measurements.

tsid_signal_quality([$tsid])

Measures the signal quality of the specified transponder. Returns a HASH containing:

        {
                $tsid => {
                        'ber'                                   => Bit error rate (32 bits)
                        'snr'                                   => Signal to noise ratio (maximum is 0xffff)
                        'strength'                              => Signal strength (maximum is 0xffff)
                        'uncorrected_blocks'    => Number of uncorrected blocks (32 bits)
                        'ok'                                    => flag set if no errors occured during the measurements
                        'error'                                 => Set to an error string on error; otherwise undef
                }
        }

If no TSID is specified, then scans all transponders and returns the complete HASH.

Note that some tuner hardware may not support some (or any) of the above measurements.

RECORDING

record($file, $duration)

(New version that uses the underlying multiplex recording methods).

Streams the selected channel information (see "select_channel($channel_name)") into the file $file for $duration.

The duration may be specified either as an integer number of minutes, or in HH:MM format (for hours & minutes), or in HH:MM:SS format (for hours, minutes, seconds).

Note that (if possible) the method creates the directory path to the file if it doersn't already exist.

record_v1($file, $duration)

Old version 1.xxx style recording. Kept in case newer version does something that you weren't expecting. Note that this version will be phased out and removed in future releases.

Streams the selected channel information (see "select_channel($channel_name)") into the file $file for $duration.

The duration may be specified either as an integer number of minutes, or in HH:MM format (for hours & minutes), or in HH:MM:SS format (for hours, minutes, seconds).

Note that (if possible) the method creates the directory path to the file if it doersn't already exist.

EPG

epg()

Gathers the EPG information into a HASH using the previously tuned frontend and returns the EPG info. If the frontend is not yet tuned then the method attempts to use the tuning information (either from a previous scan or from reading the config files) to set up the frontend.

Note that you can safely run this method while recording; the EPG scan does not affect the demux or the frontend (once it has been set)

Returns an array:

        [0] = EPG HASH
        [1] = Dates HASH

EPG HASH format is:

    $channel_name =>
       $pid => {
                'pid'                   => program unique id (= $pid)
                'channel'               => channel name
                
                'date'                  => date
                'start'                 => start time
                'end'                   => end time
                'duration'              => duration
                
                'title'                 => title string (program/series/film title)
                'subtitle'              => Usually the epsiode name
                'text'                  => synopsis string
                'etext'                 => extra text (not usually used)
                'genre'                 => genre string
                
                'episode'               => episode number
                'num_episodes' => number of episodes

                'subtitle'              => this is a short program name (useful for saving as a filename)
                
                'tva_prog'              => TV Anytime program id
                'tva_series'    => TV Anytime series id
                
                'flags'                 => HASH ref to flags (see below)
        }

i.e. The information is keyed on channel name and program id (pid)

The genre string is formatted as:

    "Major category|genre/genre..."

For example:

    "Film|movie/drama (general)"

This allows for a simple regexp to extract the information (e.g. in a TV listings application you may want to only use the major category in the main view, then show the extra genre information in a more detailed view).

Note that the genre information is mostly correct (for films) but is not reliable. Most programs are tagged as 'show' (even some films!).

The flags HASH format is:

        # audio information
        'mono'                  => flag set if program is in mono
        'stereo'                => flag set if program is in stereo
        'dual-mono'             => flag set if program is in 2 channel mono
        'multi'                 => flag set if program is in multi-lingual, multi-channel audio
        'surround'              => flag set if program is in surround sound
        'he-aac'                => flag set if component descriptor indicates audio is in HE-ACC format
        
        # video information
        '4:3'                   => flag set if program is in 4:3 
        '16:9'                  => flag set if program is in 16:9 
        'hdtv'                  => flag set if program is in high definition 
        'h264'                  => flag set if component descriptor indicates video is in .H264 format
        
        'subtitles'             => flag set if subtitles (for the hard of hearing) are available for this program
                                
        'new'                   => flag set if description mentions that this is a new program/series

Note that (especially for DVB-T2 HD-TV channels) not all of the flags that should be set *are* set! It depends on the broadcaster.

Dates HASH format is:

    $channel_name => {
                'start_date'    => date of first program for this channel 
                'start'                 => start time of first program for this channel
                
                'end_date'              => date of last program for this channel 
                'end'                   => end time of last program for this channel
        }

i.e. The information is keyed on channel name

The dates HASH is created so that an existing EPG database can be updated by removing existing information for a channel between the indicated dates.

MULTIPLEX RECORDING

add_demux_filter($pid, $pid_type [, $tsid])

Adds a demultiplexer filter for the specified PID to allow that stream to be recorded.

Internally keeps track of the list of filters created (see "demux_filter_list()" for format of the list entries)

$pid_type is a string and should be one of:

        "video"
        "audio"
        "teletext"
        "subtitle"
        "other"

Optionally a tsid may be specified. This will be used if to tune the frontend if it has not yet been tuned.

Returns 0 for success; error code otherwise.

demux_filter_list()

Return the list of currently active demux filters.

Each filter entry in the list consists of a HASH ref of the form:

        'fd'            => file handle for this filter
        'tsid'          => Transponder ID
        'pid'           => Stream PID
        'pidtype'       => $pid_type,
close_demux_filters()

Closes any currently open demux filters (basically tidies up after finished recording)

multiplex_close()

Clears out the list of recordings for a multiplex. Also releases any demux filters.

multiplex_parse($chan_spec_aref, @args)

Helper function intended to be used to parse a program's arguments list (@ARGV). The arguments are parsed into the provided ARRAY ref ($chan_spec_aref) that can then be passed to "multiplex_select($chan_spec_aref, %options)" (see that method for a description of the $chan_spec_aref ARRAY).

The arguments define the set of streams (all from the same multiplex, or transponder) that are to be recorded at the same time into each file.

Each stream definition must start with a filename, followed by either channel names or pid numbers. Also, you must specify the duration of the stream. Finally, an offset time can be specified that delays the start of the stream (for example, if the start time of the programs to be recorded are staggered).

The list of recognised arguments is:

f|file

Filename

c|chan

Channel name

p|pid

PID number

lan|lang

"Language Specification"

out

"Output Specification"

len|duration

Recording duration (specified in HH:MM or HH:MM:SS format, or as minutes)

off|offset

Start offset (specified in HH:MM or HH:MM:SS format, or as minutes)

title

Title name (reserved for future use)

ev|event

Event id used for timeslipping (see "Timeslip Specification")

tslip|timeslip

Program start/end/both extended (see "Timeslip Specification")

max|max_timeslip

Maximum timeslip time (specified in HH:MM or HH:MM:SS format, or as minutes) (see "Timeslip Specification")

Output Specification

A file defined by channel name(s) may optionally also contain a language spec and an output spec:

The output spec determines which type of streams are included in the recording. By default, "video" and "audio" tracks are recorded. You can override this by specifying the output spec. For example, if you also want the subtitle track to be recorded, then you need to specify the output includes video, audio, and subtitles. This can be done either by specifying the types in full or by just their initials.

For example, any of the following specs define video, audio, and subtitles:

        "audio, video, subtitle"
        "a, v, s"
        "avs"

Note that, if the file format explicitly defines the type of streams required, then there is no need to specify an output spec. For example, specifying that the file format is mp3 will ensure that only the audio is recorded.

Language Specification

In a similar fashion, the language spec determines the audio streams to be recorded in the program. Normally, the default audio stream is included in the recorded file. If you want either an alternative audio track, or additional audio tracks, then you use the language spec to define them. The spec consists of a space seperated list of language names. If the spec contains a '+' then the audio streams are added to the default; otherwise the default audio is excluded and only those other audio tracks in the spec are recorded. Note that the specification order is important, audio streams from the language spec are matched with the audio details in the specified order. Once a stream has been skipped there is no back tracking (see the examples below for clarification).

For example, if a channel has the audio details: eng:601 eng:602 fra:603 deu:604 (i.e. 2 English tracks, 1 French track, 1 German) then

lang="+eng"

Results in the default audio (pid 601) and the next english track (pid 602) recorded

lang="fra"

Results in just the french track (pid 603) recorded

lang="eng fra"

Results in the second english (pid 602) and the french track (pid 603) recorded

lang="fra eng"

Results in an error. The english tracks have already been skipped to match the french track, and so will not be matched again.

Note that the output spec overrides the language spec. If the output does not include audio, then the language spec is ignored.

Timeslip Specification

Timeslip recording uses the now/next live EPG information to track the start and end of the program being recorded. This information is transmit by the broadcaster and (hopefully) is a correct reflection of the broadcast of the program. Using this feature should then allow recordings to be adjusted to account for late start of a program (for example, due to extended news or sports events).

To use the feature you MUST specify the event id of the program to be recorded. This information is the same event id that is gathered by the "epg()" function. By default, the timeslip will automatically extend the end of the recording by up to 1 hour (recording stops automatically when the now/next information indicates the real end of the program).

event=41140

Sets the event id to be 41140

Optionally you can specify a different maximum timeslip time using the 'max_timeslip' argument. Specify the time in minutes (or HH:MM or HH:MM:SS). Note that this has a different effect depending on the timeslip setting (which specifies the program 'edge'):

max_timeslip=2:00

Sets the maximum timslip time to be 2 hours (i.e. by default, the recording end can be extended by up to 2 hours)

Also, you can set the 'edge' of the program that is to be timeslipped using the timeslip parameter:

timeslip=end

Timeslips only the end of the recording. This means that the recording will record for the normal duration, and then check to see if the specified event (event_id) has finished broadcasting. If not, the recording continues until the program finishes OR the maximum timeslip duration has expired.

This is the default.

timeslip=start

Timeslips only the start of the recording. This means that the recording will not start until the event begins broadcasting. Once started, the specified duration will be recorded.

Note that this can mean that you miss a few seconds at the start of the program (which is why the default is to just extend the end of the recording).

timeslip=both

Performs both start and end recording timeslipping.

Examples

Example valid sets of arguments are:

file=f1.mpeg chan=bbc1 out=avs lang=+eng len=1:00 off=0:10

Record channel BBC1 into file f1.mpeg, include subtitles, add second English audio track, record for 1 hour, start recording 10 minutes from now

file=f2.mp3 chan=bbc2 len=0:30

Record channel BBC2 into file f2.mp3, audio only, record for 30 minutes

file=f3.ts pid=600 pid=601 len=0:30

Record pids 600 & 601 into file f3.ts, record for 30 minutes

multiplex_select($chan_spec_aref, %options)

Selects a set of streams based on the definitions in the chan spec ARRAY ref. The array contains hashes of:

        {
                'file'          => filename
                'chans'         => [ 
                        { 'chan' => channel name, 'lang' => lang spec, 'out' => output },
                        ... 
                ]
                'pids'          => [ stream pid, ... ]
                'offset'        => time
                'duration'      => time
        }

Each entry must contain a target filename, a recording duration, and either channel definitions or pid definitions. The channel definition list consists of HASHes containing a channel name, a language spec, and an output spec.

The language and output specs are as described in "multiplex_parse($chan_spec_aref, @args)"

The optional options hash consists of:

        {
                'tsid'                  => tsid
                'lang'                  => default lang spec
                'out'                   => default output spec
                'no-pid-check'  => when set, allows specification of any pids
        }

The TSID definition defines the transponder (multiplex) to use. Use this when pids define the streams rather than channel names and the pid value(s) may occur in multiple TSIDs.

If you define default language or output specs, these will be used in all file definitions unless that file definition has it's own output/language spec. For example, if you want all files to include subtitles you can specify it once as the default rather than for every file.

The method sets up the DVB demux filters to record each of the required streams. It also sets up a HASH of the settings, which may be read using "multiplex_info()". This hash being used in "multiplex_record(%multiplex_info)".

Setting the 'no-pid-check' allows the recording of pids that are not known to the module (i.e. not in the scan files). This is for experimental use.

multiplex_record_duration()

Returns the total recording duration (in seconds) of the currently spricied multiplex recordings.

Used for informational purposes.

multiplex_info()

Returns HASH of the currently defined multiplex filters. HASH is of the form:

  files => {
        $file => {
                'pids'  => [
                        {
                                'pid'   => Stream PID
                                'pidtype'       => pid type (i.e. 'audio', 'video', 'subtitle')
                        },
                        ...
                ]
                'offset' => offset time for this file
                'duration' => duration for this file

                'destfile'      => final written file name (set by L</multiplex_transcode(%multiplex_info)>)
                'warnings'      => [
                        ARRAY ref of list of warnings (set by L</multiplex_transcode(%multiplex_info)>)
                ],
                'errors'        => [
                        ARRAY ref of list of errors (set by L</multiplex_transcode(%multiplex_info)>)
                ],
                'lines' => [
                        ARRAY ref of lines of output from the transcode/demux operation(s) (set by L</multiplex_transcode(%multiplex_info)>)
                ],
        },
  },
  duration => maximum recording duration in seconds
  tsid => the multiplex id

where there is an entry for each file, each entry containing a recording duration (in seconds), an offset time (in seconds), and an array of pids that define the streams required for the file.

After recording, the multiplex info HASH 'pids' information also contains:

                'pids'  => [
                        {
                                'pid'   => Stream PID
                                'pidtype'       => pid type (i.e. 'audio', 'video', 'subtitle')

                                'pkts'          => Number of recorded packets
                                'errors'        => Transport stream error count
                                'overflows' => Count of DVBT buffer overflows during recording
                                'timeslip_start_secs' => Number of seconds the recording start has been slipped by
                                'timeslip_end_secs'   => Number of seconds the recording end has been slipped by
                        },
multiplex_record(%multiplex_info)

Records the selected streams into their files. Note that the recorded files will be the specified name, but with the extension set to '.ts'. You can optionally then call "multiplex_transcode(%multiplex_info)" to transcode the files into the requested file format.

multiplex_transcode(%multiplex_info)

Transcodes the recorded files into the requested formats (uses ffmpeg helper module).

If the destination file format is the same as the recorded format (i.e. transport file) then no transcoding is performed, but a check is made to ensure the file duration is correct.

Sets the following fields in the %multiplex_info HASH:

        $file => {

                ...

                'destfile'      => final written file name
                'warnings'      => [
                        ARRAY ref of list of warnings
                ],
                'errors'        => [
                        ARRAY ref of list of errors
                ],
                'lines' => [
                        ARRAY ref of lines of output from the transcode/demux operation(s)
                ],
        }

See Linux::DVB::DVBT::Ffmpeg::ts_transcode() for further details.

DEBUG UTILITIES

prt_data(@list)

Print out each item in the list, showing HASH hierarchies. Handles scalars, hashes (as an array), arrays, ref to scalar, ref to hash, ref to array, object.

Useful for debugging.

INTERNAL METHODS

hwinit()

Object internal method

Initialise the hardware (create dvb structure). Called once and sets the adpater & frontend number for this object.

If no adapter number has been specified yet then use the first device in the list.

log_error($error_message)

Object internal method

Add the error message to the error log. Get the log as an ARRAY ref via the 'errors()' method

dvb_closed()

Returns true if the DVB tuner has been closed (or failed to open).

ACKNOWLEDGEMENTS

Debugging

Special thanks to Thomas Rehn, not only for providing feedback on a number of latent bugs but also for his patience in re-running numerous test versions to gather the debug data I needed. Thanks Thomas.

Also, thanks to Arthur Gidlow for running various tests to debug a scanning issue.

Gerd Knorr for writing xawtv (see http://linux.bytesex.org/xawtv/)

Some of the C code used in this module is used directly from Gerd's libng. All other files are entirely written by me, or drastically modified from Gerd's original to (a) make the code more 'Perl friendly', (b) to reduce the amount of code compiled into the library to just those functions required by this module.

w_scan (see http://wirbel.htpc-forum.de/w_scan/index_en.html)

The country codes and frequency information used by "scan_from_country($iso3166)" are based on the information found in the w_scan program. I've used some of this information (only the DVB-T info) but I haven't copied (or used) any of the C code itself.

AUTHOR

Steve Price

Please report bugs using http://rt.cpan.org.

CONTRIBUTORS

Jean-Michel Masereel - Thanks for adding support for multi-language subtitles

BUGS

None that I know of!

FEATURES

The current release supports:

  • DVB-T2 support (i.e. HD TV). I've modified the libraries so that they correctly understand the DVB-T2 types used to transport the MPEG4 video. This has been tested using a PCTV 290e DVB-T/T2 usb stick (using the experimental drivers available at http://git.linuxtv.org/media_build.git). So now scanning and recording will work with HD channels.

    (For more information on using the PCTV 290e see http://stevekerrison.com/290e/).

  • Scan using country code (i.e. scanning through supported frequencies) as well as using a frequency file.

  • The module now stores the previous scan setting in the configuration files so that subsequent scans can be performed without needing to refer to a frequency file.

  • Timeslip recording so that the end (and/or start) of the recording is extended if the program broadcast is delayed.

  • Tuning to a channel based on "fuzzy" channel name (i.e. you can specify a channel with/without spaces, in any case, and with numerals or number names)

  • Transport stream recording (i.e. program record) with large file support

  • Electronic program guide. Builds the TV/radio listings as a HASH structure (which you can then store into a database, file etc and use to schedule your recordings)

  • Option to record all/any of the audio streams for a program (e.g. allows for descriptive audio for visually impaired)

  • Recording of any streams within a multiplex at the same time (i.e. multi-channel recording using a single DVB device)

  • Additional module providing wrappers to ffmpeg as "helper" programs to transcode recorded files (either during "normal" or "multiplex" recording).

FUTURE

Subsequent releases will include:

  • I'm looking into the option of writing the files directly as mpeg. Assuming I can work my way through the mpeg2 specification!

  • Support for event-driven applications (e.g. POE). I need to re-write some of the C to allow for event-driven hooks (and special select calls)

COPYRIGHT AND LICENSE

Copyright (C) 2008 by Steve Price

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.