libdvb 0.5.0

Interface for DVB-API v5 devices in Linux
Documentation

libdvb

libdvb is an interface library for DVB API v5 devices in Linux.

Supports three types of delivery systems:

  • Satellite: DVB-S, DVB-S2
  • Terrestrial: DVB-T, DVB-T2, ATSC, ISDB-T
  • Cable: DVB-C
  • DiSEqC 1.0
  • DiSEqC 1.1
  • EN 50494 - Unicable I
  • EN 50607 - Unicable II

DVB-CI (EN 50221) support currently includes a runtime-neutral CiController, the link, transport and session layers, and Resource Manager, Application Information, Conditional Access Support, Host Control, Date-Time and high-level MMI resources, including CA PMT program selection from raw MPEG-TS PMT sections.

FeDevice

Frontend tuning uses the high-level TuneRequest enum, which lowers per-delivery-system parameters to a DVBv5 property command sequence.

Example DVB-S2 tune:

use libdvb::{
    DvbS2Tune,
    FeDevice,
    TuneRequest,
    fe::sys::{
        SecTone,
        SecVoltage,
    },
};

let fe = FeDevice::open_rw(0, 0)?;

// Optional: drive the SEC/DiSEqC switch and translate the transponder
// frequency to the intermediate frequency (11044 MHz transponder,
// 9750 MHz LNB local oscillator).
let frequency_khz = fe.use_diseqc(11044, DiseqcConfig::Dsl("t v".to_owned()))?;

let request = TuneRequest::DvbS2(DvbS2Tune {
    frequency_khz,
    symbolrate: 27500 * 1000,
    voltage: SecVoltage::V13,
    tone: SecTone::Off,
    ..Default::default()
});

fe.tune(&request)?;

The low-level interface is still available: TuneRequest::properties() builds the typed Vec<DtvProperty> command sequence, which can be applied with FeDevice::set_properties().

Frontend information is available through explicit accessors:

let fe = FeDevice::open_ro(0, 0)?;
println!("DVB API: {}", fe.api_version());
println!("Frontend: {}", fe.name());

print!("Delivery system:");
for v in fe.delivery_systems() {
    print!(" {}", v);
}
println!();

println!("Frequency range: {:?}", fe.frequency_range());
println!("Symbolrate range: {:?}", fe.symbolrate_range());
println!("Frontend capabilities: {:?}", fe.caps());

Frontend status:

let fe = FeDevice::open_ro(0, 0)?;
let mut status = FeStatus::default();
status.read(&fe)?;
println!("{}", status.to_status_string());

FeStatus also exposes parsed values via methods such as delivery_system(), modulation(), signal_strength_decibel(), signal_strength(), snr_decibel(), snr(), ber(), and unc().

Demux

DmxDevice opens /dev/dvb/adapterN/demuxM and supports PES filters, buffer sizing, and explicit start/stop:

use libdvb::dmx::{
    DmxDevice,
    sys::{
        DMX_IN_FRONTEND,
        DMX_OUT_TS_TAP,
        DMX_PES_OTHER,
        DmxFilterFlags,
        DmxPesFilterParams,
    },
};

let dmx = DmxDevice::open(0, 0)?;
let filter = DmxPesFilterParams {
    pid: 8192,
    input: DMX_IN_FRONTEND,
    output: DMX_OUT_TS_TAP,
    pes_type: DMX_PES_OTHER,
    flags: DmxFilterFlags::IMMEDIATE_START.bits(),
};

dmx.set_pes_filter(&filter)?;

DVR

DvrDevice opens /dev/dvb/adapterN/dvrM in blocking read-only mode. It implements Read and can resize the DVR buffer through the DVB DMX_SET_BUFFER_SIZE ioctl:

use std::io::Read;

use libdvb::DvrDevice;

let mut dvr = DvrDevice::open(0, 0)?;
dvr.set_buffer_size(100 * 188 * 1024)?;

let mut buf = vec![0; 188 * 1024];
let size = dvr.read(&mut buf)?;
println!("Read {} bytes", size);

NetDevice

Network interfaces are removed automatically when NetInterface is dropped. Use mac() to read the interface MAC address.

use libdvb::NetDevice;

let dev = NetDevice::open(0, 0)?;
let interface = dev.add_if(0, libdvb::net::sys::DVB_NET_FEEDTYPE_MPE)?;
println!("Interface: {}", interface);
println!("MAC: {}", interface.mac());

External CI (DigitalDevices / TBS)

SecDevice opens the CI adapter TS pipe (ciN node on DigitalDevices, secN on TBS) in non-blocking mode. It is control plane only: the TS data path uses the exposed file descriptors.

use libdvb::SecDevice;

let sec = SecDevice::open(1, 0)?;
sec.set_ci_bitrate(70)?; // MBit/s; TBS only, no-op for other vendors

let fd_in = sec.fd_in();   // write scrambled TS into the CAM
let fd_out = sec.fd_out(); // read descrambled TS from the CAM

CI

CiController manages multi-slot CAM insertion/removal, reset, CREATE_TC, transport polling, RCV and timeout recovery. It does not create a thread or own an event loop: integrate its file descriptor into the application runtime, drain poll_event() when readable and call tick() from a monotonic timer. A CAM reaches CamStatus::Ready after valid Application Information and CA Information replies; use caids() for the deduplicated slot list or session_caids() for one CA application:

use std::time::Instant;

use libdvb::{CaEvent, CiController};

let mut ci = CiController::open(0, 0)?;

// Call periodically (for example, every 100 ms).
ci.tick(Instant::now())?;

// Drain after each tick and from the CA descriptor readable callback.
while let Some(event) = ci.poll_event()? {
    match event {
        CaEvent::SlotStatusChanged { slot_id, new, .. } => {
            println!("CI slot {slot_id}: {new:?}");
        }
        CaEvent::CaInfo { slot_id, session_id, caids } => {
            println!("CI slot {slot_id}, CA session {session_id}: {caids:X?}");
        }
        event => println!("CI: {event:?}"),
    }
}

// A complete raw PMT section, including CRC32. The controller copies all
// data it needs, so the input buffer may be reused after this call.
let raw_pmt: &[u8] = get_raw_pmt_section();
let program_number = ci.set_program(raw_pmt)?;

// Later, withdraw the service by its PMT program_number.
ci.remove_program(program_number)?;

# Ok::<(), libdvb::error::Error>(())

File Descriptors

Demux, DVR, frontend, and network device handles open in blocking mode by default. The CA device opens in non-blocking mode as required by the CI transport. All device handles implement AsFd and AsRawFd, so callers can pass them to APIs that operate on borrowed or raw file descriptors.

Code Formatting

rustfmt --config "group_imports=StdExternalCrate,imports_granularity=Crate,imports_layout=Vertical,newline_style=Unix,spaces_around_ranges=true,struct_lit_single_line=true,use_field_init_shorthand=true"