The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

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
	$dvb->scan_from_file('/usr/share/dvb/dvb-t/uk-Oxford') ;
	
	# 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) ;

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.

Example scripts have been provided in the package which illustrate the expected use of the package (and are useable programs in themeselves)

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
dvbt-epg

When run, this grabs the latest EPG information and updates a MySql database:

$ dvbt-epg
dvbt-record

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

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

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

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 experminets 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.

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)

channel_list - Channel numbering

Use this field to specify your preferred channel numbering.

You provide an ARRAY ref where the array contains HASHes of the form:

{
    'channel' => <channel name>
    'channel_num' => <channel number>
}

For example, you'd probably want 'BBC ONE' to have the channel number 1.

NOTE: When I've worked out how the logical channel numbering information is transmitted, then I'll automatically fill this in from the scan.

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 either 'die' (the default) or 'return' and when an error occurs, the 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. It is the application's responsibility to handle the errors (stored in "errors")

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

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.

CLASS METHODS

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

debug([$level])

Set new debug level. Returns setting.

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
}

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)

OBJECT DATA 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

OBJECT METHODS

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

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

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.

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'

(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, $teletext_pid)

Selects a particular video/audio stream (and optional teletext stream) 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.

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
		'channel'	=> channel name
		
		'date'		=> date
		'start'		=> start time
		'end'		=> end time
		'duration'	=> duration
		
		'title'		=> title string
		'text'		=> synopsis string
		'etext'		=> extra text (not usually used)
		'genre'		=> genre string
		
		'repeat'	=> repeat count
		'episode'	=> episode number
		'num_episodes' => number of episodes
	}

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)

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.

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

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

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",
        },
		....
    },
    
    '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.

get_tuning_info()

Object internal method

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 (see "scan()" method for format); otherwise returns undef

get_channel_list()

Object internal method

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 (see 'channel_list' field for format); otherwise returns undef

TODO: In a later release I'll work out how to use the logical channel number broadcast on the NIT

record($file, $duration)

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.

ACKNOWLEDGEMENTS

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.

AUTHOR

Steve Price

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

FUTURE

Subsequent releases will include:

  • 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)

  • Extraction of channel numbering from broadcast. I want to work out how to decode the LCN.

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.