1
0
mirror of https://github.com/danog/libdvb.git synced 2024-11-30 04:19:00 +01:00

Add DMX API

This commit is contained in:
Daniil Gentili 2022-03-27 19:24:57 +02:00
parent 38f2db4e38
commit 983725c618
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
4 changed files with 272 additions and 61 deletions

View File

@ -1,7 +1,7 @@
[package] [package]
name = "libdvb-rs" name = "libdvb-rs"
version = "0.4.3" version = "0.4.4"
description = "Safer pure-Rust interface for DVB-API v5 devices in Linux" description = "Safer and feature-complete pure-Rust interface for DVB-API v5 devices in Linux"
authors = ["Cesbo Developers Team <info@cesbo.com>", "Daniil Gentili <daniil@daniil.it>"] authors = ["Cesbo Developers Team <info@cesbo.com>", "Daniil Gentili <daniil@daniil.it>"]
license = "MIT" license = "MIT"
readme = "README.md" readme = "README.md"

View File

@ -1 +1,183 @@
use {
anyhow::{Context, Result},
nix::{ioctl_write_int_bad, ioctl_none_bad, ioctl_write_ptr, request_code_none},
std::{
fs::{File, OpenOptions},
os::unix::{
fs::{OpenOptionsExt},
io::{AsRawFd, RawFd},
},
},
sys::*,
};
pub mod sys; pub mod sys;
/// A reference to the demux device and device information
#[derive(Debug)]
pub struct DmxDevice {
file: File,
}
impl AsRawFd for DmxDevice {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
impl DmxDevice {
fn open(adapter: u32, device: u32, is_write: bool) -> Result<Self> {
let path = format!("/dev/dvb/adapter{}/demux{}", adapter, device);
let file = OpenOptions::new()
.read(true)
.write(is_write)
.custom_flags(::nix::libc::O_NONBLOCK)
.open(&path)
.with_context(|| format!("DMX: failed to open device {}", &path))?;
Ok(DmxDevice {
file,
})
}
/// Attempts to open frontend device in read-only mode
#[inline]
pub fn open_ro(adapter: u32, device: u32) -> Result<Self> {
Self::open(adapter, device, false)
}
/// Attempts to open frontend device in read-write mode
#[inline]
pub fn open_rw(adapter: u32, device: u32) -> Result<Self> {
Self::open(adapter, device, true)
}
/// Attempts to set demux PES filter parameters.
/// By a PES filter is meant a filter that is based just on the packet identifier (PID),
/// i.e. no PES header or payload filtering capability is supported.
///
/// There is a flag field where it is possible to state whether a section should be CRC-checked,
/// whether the filter should be a “one-shot” filter, i.e. if the filtering operation should be stopped
/// after the first section is received, and whether the filtering operation should be started immediately
/// (without waiting for a DMX_START ioctl call).
pub fn set_pes_filter(&self, filter: &DmxPesFilterParams) -> Result<()> {
// DMX_SET_PES_FILTER
ioctl_write_ptr!(
#[inline]
ioctl_call,
b'o',
44,
DmxPesFilterParams
);
unsafe { ioctl_call(self.as_raw_fd(), filter as *const _) }.context("DMX: set PES filter")?;
Ok(())
}
/// Tries to add multiple PIDs to a transport stream filter previously set up with
/// set_pes_filter and output equal to DMX_OUT_TSDEMUX_TAP.
pub fn add_pid(&self, pid: u16) -> Result<()> {
// DMX_ADD_PID
ioctl_write_ptr!(
#[inline]
ioctl_call,
b'o',
51,
u16
);
unsafe { ioctl_call(self.as_raw_fd(), &pid as *const _) }.context("DMX: add PID")?;
Ok(())
}
/// This ioctl call allows to remove a PID when multiple PIDs are set on a transport stream filter,
/// e. g. a filter previously set up with output equal to DMX_OUT_TSDEMUX_TAP,
/// created via either set_pes_filter or add_pid.
pub fn remove_pid(&self, pid: u16) -> Result<()> {
// DMX_REMOVE_PID
ioctl_write_ptr!(
#[inline]
ioctl_call,
b'o',
52,
u16
);
unsafe { ioctl_call(self.as_raw_fd(), &pid as *const _) }.context("DMX: remove PID")?;
Ok(())
}
/// Attempts to set demux SCT filter parameters.
/// A timeout may be defined stating number of seconds to wait for a section to be loaded.
/// A value of 0 means that no timeout should be applied.
/// Finally there is a flag field where it is possible to state whether a section should be CRC-checked,
/// whether the filter should be a “one-shot” filter, i.e. if the filtering operation should be stopped
/// after the first section is received, and whether the filtering operation should be started immediately
/// (without waiting for a DMX_START ioctl call).
///
/// If a filter was previously set-up, this filter will be canceled, and the receive buffer will be flushed.
pub fn set_filter(&self, filter: &DmxSctFilterParams) -> Result<()> {
// DMX_SET_FILTER
ioctl_write_ptr!(
#[inline]
ioctl_call,
b'o',
43,
DmxSctFilterParams
);
unsafe { ioctl_call(self.as_raw_fd(), filter as *const _) }.context("DMX: set SCT filter")?;
Ok(())
}
/// Attempts to set the size of the circular buffer used for filtered data.
/// The default size is two maximum sized sections,
/// i.e. if this function is not called a buffer size of 2 * 4096 bytes will be used.
pub fn set_buffer_size(&self, size: u32) -> Result<()> {
// DMX_SET_BUFFER_SIZE
ioctl_write_int_bad!(
#[inline]
ioctl_call,
request_code_none!(b'o', 45)
);
unsafe { ioctl_call(self.as_raw_fd(), size as _) }.context("DMX: set buffer size")?;
Ok(())
}
/// Attempts to start the actual filtering operation defined via the ioctl calls set_filter or set_pes_filter.
pub fn start(&self) -> Result<()> {
// DMX_START
ioctl_none_bad!(
#[inline]
ioctl_call,
request_code_none!(b'o', 41)
);
unsafe { ioctl_call(self.as_raw_fd()) }.context("DMX: start")?;
Ok(())
}
/// Attempts to stop the actual filtering operation defined via the ioctl calls set_filter or set_pes_filter and started via start.
pub fn stop(&self) -> Result<()> {
// DMX_STOP
ioctl_none_bad!(
#[inline]
ioctl_call,
request_code_none!(b'o', 42)
);
unsafe { ioctl_call(self.as_raw_fd()) }.context("DMX: stop")?;
Ok(())
}
}

View File

@ -1,127 +1,154 @@
use { use bitflags::bitflags;
crate::ioctl::{ use strum::FromRepr;
IoctlInt,
io_none,
io_write,
},
};
pub use { pub use {
dmx_output::*, DmxOutput::*,
dmx_input::*, DmxInput::*,
dmx_ts_pes::*, DmxTsPes::*,
dmx_filter_flags::*,
}; };
/// Output for the demux /// Output for the demux
mod dmx_output { #[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, FromRepr)]
pub enum DmxOutput {
/// Streaming directly to decoder /// Streaming directly to decoder
pub const DMX_OUT_DECODER: u32 = 0; DMX_OUT_DECODER = 0,
/// Output going to a memory buffer (to be retrieved via the read command). /// Output going to a memory buffer (to be retrieved via the read command).
/// Delivers the stream output to the demux device on which the ioctl /// Delivers the stream output to the demux device on which the ioctl
/// is called. /// is called.
pub const DMX_OUT_TAP: u32 = 1; DMX_OUT_TAP = 1,
/// Output multiplexed into a new TS (to be retrieved by reading from the /// Output multiplexed into a new TS (to be retrieved by reading from the
/// logical DVR device). Routes output to the logical DVR device /// logical DVR device). Routes output to the logical DVR device
/// `/dev/dvb/adapter?/dvr?`, which delivers a TS multiplexed from all /// `/dev/dvb/adapter?/dvr?`, which delivers a TS multiplexed from all
/// filters for which DMX_OUT_TS_TAP was specified. /// filters for which DMX_OUT_TS_TAP was specified.
pub const DMX_OUT_TS_TAP: u32 = 2; DMX_OUT_TS_TAP = 2,
/// Like DMX_OUT_TS_TAP but retrieved from the DMX device. /// Like DMX_OUT_TS_TAP but retrieved from the DMX device.
pub const DMX_OUT_TSDEMUX_TAP: u32 = 3; DMX_OUT_TSDEMUX_TAP = 3
} }
/// Input from the demux /// Input from the demux
mod dmx_input { #[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, FromRepr)]
pub enum DmxInput {
/// Input from a front-end device /// Input from a front-end device
pub const DMX_IN_FRONTEND: u32 = 0; DMX_IN_FRONTEND = 0,
/// Input from the logical DVR device /// Input from the logical DVR device
pub const DMX_IN_DVR: u32 = 1; DMX_IN_DVR = 1
} }
/// type of the PES filter /// type of the PES filter
mod dmx_ts_pes { #[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, FromRepr)]
pub enum DmxTsPes {
/// first audio PID /// first audio PID
pub const DMX_PES_AUDIO0: u32 = 0; DMX_PES_AUDIO0 = 0,
/// first video PID /// first video PID
pub const DMX_PES_VIDEO0: u32 = 1; DMX_PES_VIDEO0 = 1,
/// first teletext PID /// first teletext PID
pub const DMX_PES_TELETEXT0: u32 = 2; DMX_PES_TELETEXT0 = 2,
/// first subtitle PID /// first subtitle PID
pub const DMX_PES_SUBTITLE0: u32 = 3; DMX_PES_SUBTITLE0 = 3,
/// first Program Clock Reference PID /// first Program Clock Reference PID
pub const DMX_PES_PCR0: u32 = 4; DMX_PES_PCR0 = 4,
/// second audio PID. /// second audio PID.
pub const DMX_PES_AUDIO1: u32 = 5; DMX_PES_AUDIO1 = 5,
/// second video PID. /// second video PID.
pub const DMX_PES_VIDEO1: u32 = 6; DMX_PES_VIDEO1 = 6,
/// second teletext PID. /// second teletext PID.
pub const DMX_PES_TELETEXT1: u32 = 7; DMX_PES_TELETEXT1 = 7,
/// second subtitle PID. /// second subtitle PID.
pub const DMX_PES_SUBTITLE1: u32 = 8; DMX_PES_SUBTITLE1 = 8,
/// second Program Clock Reference PID. /// second Program Clock Reference PID.
pub const DMX_PES_PCR1: u32 = 9; DMX_PES_PCR1 = 9,
/// third audio PID. /// third audio PID.
pub const DMX_PES_AUDIO2: u32 = 10; DMX_PES_AUDIO2 = 10,
/// third video PID. /// third video PID.
pub const DMX_PES_VIDEO2: u32 = 11; DMX_PES_VIDEO2 = 11,
/// third teletext PID. /// third teletext PID.
pub const DMX_PES_TELETEXT2: u32 = 12; DMX_PES_TELETEXT2 = 12,
/// third subtitle PID. /// third subtitle PID.
pub const DMX_PES_SUBTITLE2: u32 = 13; DMX_PES_SUBTITLE2 = 13,
/// third Program Clock Reference PID. /// third Program Clock Reference PID.
pub const DMX_PES_PCR2: u32 = 14; DMX_PES_PCR2 = 14,
/// fourth audio PID. /// fourth audio PID.
pub const DMX_PES_AUDIO3: u32 = 15; DMX_PES_AUDIO3 = 15,
/// fourth video PID. /// fourth video PID.
pub const DMX_PES_VIDEO3: u32 = 16; DMX_PES_VIDEO3 = 16,
/// fourth teletext PID. /// fourth teletext PID.
pub const DMX_PES_TELETEXT3: u32 = 17; DMX_PES_TELETEXT3 = 17,
/// fourth subtitle PID. /// fourth subtitle PID.
pub const DMX_PES_SUBTITLE3: u32 = 18; DMX_PES_SUBTITLE3 = 18,
/// fourth Program Clock Reference PID. /// fourth Program Clock Reference PID.
pub const DMX_PES_PCR3: u32 = 19; DMX_PES_PCR3 = 19,
/// any other PID. /// any other PID.
pub const DMX_PES_OTHER: u32 = 20; DMX_PES_OTHER = 20,
} }
/// Flags for the demux filter bitflags! {
mod dmx_filter_flags { /// Flags for the demux filter
/// Only deliver sections where the CRC check succeeded #[repr(C)]
pub const DMX_CHECK_CRC: u32 = 1; pub struct DmxFilterFlags : u32 {
/// Disable the section filter after one section has been delivered /// Only deliver sections where the CRC check succeeded
pub const DMX_ONESHOT: u32 = 2; const DMX_CHECK_CRC = 1;
/// Start filter immediately without requiring a `DMX_START` /// Disable the section filter after one section has been delivered
pub const DMX_IMMEDIATE_START: u32 = 4; const DMX_ONESHOT = 2;
/// Start filter immediately without requiring a `DMX_START`
const DMX_IMMEDIATE_START = 4;
}
} }
/// Specifies Packetized Elementary Stream (PES) filter parameters /// Specifies Packetized Elementary Stream (PES) filter parameters
#[repr(C)] #[repr(C)]
#[derive(Default, Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct DmxPesFilterParams { pub struct DmxPesFilterParams {
/// PID to be filtered. 8192 to pass all PID's /// PID to be filtered. 8192 to pass all PID's
pub pid: u16, pub pid: u16,
/// Demux input, as specified by `DMX_IN_*` /// Demux input, as specified by `DMX_IN_*`
pub input: u32, pub input: DmxInput,
/// Demux output, as specified by `DMX_OUT_*` /// Demux output, as specified by `DMX_OUT_*`
pub output: u32, pub output: DmxOutput,
/// Type of the pes filter, as specified by `DMX_PES_*` /// Type of the pes filter, as specified by `DMX_PES_*`
pub pes_type: u32, pub pes_type: DmxTsPes,
/// Demux PES flags /// Demux PES flags
pub flags: u32, pub flags: DmxFilterFlags,
} }
pub const DMX_FILTER_SIZE: usize = 16;
pub const DMX_START: IoctlInt = io_none(b'o', 41); /// Specifies demux section header filter parameters
pub const DMX_STOP: IoctlInt = io_none(b'o', 42); #[repr(C)]
pub const DMX_SET_PES_FILTER: IoctlInt = io_write::<DmxPesFilterParams>(b'o', 44); #[derive(Debug, Copy, Clone)]
pub const DMX_SET_BUFFER_SIZE: IoctlInt = io_none(b'o', 45); pub struct DmxFilter {
/// Bit array with bits to be matched at the section header
pub filter: [u8; DMX_FILTER_SIZE],
/// Bits that are valid at the filter bit array
pub mask: [u8; DMX_FILTER_SIZE],
/// Mode of match: if bit is zero, it will match if equal (positive match); if bit is one, it will match if the bit is negated.
pub mode: [u8; DMX_FILTER_SIZE],
}
/// Specifies Section header (SCT) filter parameters
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct DmxSctFilterParams {
/// PID to be filtered. 8192 to pass all PID's
pub pid: u16,
/// Section header filter, as defined by DmxFilter
pub filter: DmxFilter,
/// Maximum time to filter, in milliseconds
pub timeout: u32,
/// Extra flags for the section filter, as specified by DmxFilterFlags
pub flags: DmxFilterFlags
}

View File

@ -5,9 +5,11 @@ extern crate anyhow;
pub mod ca; pub mod ca;
pub mod fe; pub mod fe;
pub mod net; pub mod net;
pub mod dmx;
pub use { pub use {
ca::CaDevice, ca::CaDevice,
fe::{FeDevice, FeStatus}, fe::{FeDevice, FeStatus},
net::NetDevice, net::NetDevice,
dmx::DmxDevice,
}; };