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:
parent
38f2db4e38
commit
983725c618
@ -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"
|
||||||
|
182
src/dmx/mod.rs
182
src/dmx/mod.rs
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
145
src/dmx/sys.rs
145
src/dmx/sys.rs
@ -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
|
||||||
|
}
|
@ -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,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user