libdvb_rs/dmx/
mod.rs

1use {
2    anyhow::{Context, Result},
3    nix::{ioctl_write_int_bad, ioctl_none_bad, ioctl_write_ptr, request_code_none},
4    std::{
5        fs::{File, OpenOptions},
6        os::unix::{
7            fs::{OpenOptionsExt},
8            io::{AsRawFd, RawFd},
9        },
10    },
11    sys::*,
12};
13
14
15pub mod sys;
16
17/// A reference to the demux device and device information
18#[derive(Debug)]
19pub struct DmxDevice {
20    file: File,
21}
22
23impl AsRawFd for DmxDevice {
24    #[inline]
25    fn as_raw_fd(&self) -> RawFd {
26        self.file.as_raw_fd()
27    }
28}
29
30impl DmxDevice {
31    fn open(adapter: u32, device: u32, is_write: bool) -> Result<Self> {
32        let path = format!("/dev/dvb/adapter{}/demux{}", adapter, device);
33        let file = OpenOptions::new()
34            .read(true)
35            .write(is_write)
36            .custom_flags(::nix::libc::O_NONBLOCK)
37            .open(&path)
38            .with_context(|| format!("DMX: failed to open device {}", &path))?;
39
40        Ok(DmxDevice {
41            file,
42        })
43    }
44
45    /// Attempts to open frontend device in read-only mode
46    #[inline]
47    pub fn open_ro(adapter: u32, device: u32) -> Result<Self> {
48        Self::open(adapter, device, false)
49    }
50
51    /// Attempts to open frontend device in read-write mode
52    #[inline]
53    pub fn open_rw(adapter: u32, device: u32) -> Result<Self> {
54        Self::open(adapter, device, true)
55    }
56
57    /// Attempts to set demux PES filter parameters.
58    /// By a PES filter is meant a filter that is based just on the packet identifier (PID),
59    /// i.e. no PES header or payload filtering capability is supported.
60    /// 
61    /// There is a flag field where it is possible to state whether a section should be CRC-checked,
62    /// whether the filter should be a “one-shot” filter, i.e. if the filtering operation should be stopped
63    /// after the first section is received, and whether the filtering operation should be started immediately
64    /// (without waiting for a DMX_START ioctl call).
65    pub fn set_pes_filter(&self, filter: &DmxPesFilterParams) -> Result<()> {
66        // DMX_SET_PES_FILTER
67        ioctl_write_ptr!(
68            #[inline]
69            ioctl_call,
70            b'o',
71            44,
72            DmxPesFilterParams
73        );
74
75        unsafe { ioctl_call(self.as_raw_fd(), filter as *const _) }.context("DMX: set PES filter")?;
76
77        Ok(())
78    }
79
80
81    /// Tries to add multiple PIDs to a transport stream filter previously set up with 
82    /// set_pes_filter and output equal to DMX_OUT_TSDEMUX_TAP.
83    pub fn add_pid(&self, pid: u16) -> Result<()> {
84        // DMX_ADD_PID
85        ioctl_write_ptr!(
86            #[inline]
87            ioctl_call,
88            b'o',
89            51,
90            u16
91        );
92
93        unsafe { ioctl_call(self.as_raw_fd(), &pid as *const _) }.context("DMX: add PID")?;
94
95        Ok(())
96    }
97
98    /// This ioctl call allows to remove a PID when multiple PIDs are set on a transport stream filter, 
99    /// e. g. a filter previously set up with output equal to DMX_OUT_TSDEMUX_TAP, 
100    /// created via either set_pes_filter or add_pid.
101    pub fn remove_pid(&self, pid: u16) -> Result<()> {
102        // DMX_REMOVE_PID
103        ioctl_write_ptr!(
104            #[inline]
105            ioctl_call,
106            b'o',
107            52,
108            u16
109        );
110
111        unsafe { ioctl_call(self.as_raw_fd(), &pid as *const _) }.context("DMX: remove PID")?;
112
113        Ok(())
114    }
115
116    /// Attempts to set demux SCT filter parameters.
117    /// A timeout may be defined stating number of seconds to wait for a section to be loaded.
118    /// A value of 0 means that no timeout should be applied.
119    /// Finally there is a flag field where it is possible to state whether a section should be CRC-checked,
120    /// whether the filter should be a “one-shot” filter, i.e. if the filtering operation should be stopped
121    /// after the first section is received, and whether the filtering operation should be started immediately
122    /// (without waiting for a DMX_START ioctl call).
123    /// 
124    /// If a filter was previously set-up, this filter will be canceled, and the receive buffer will be flushed.
125    pub fn set_filter(&self, filter: &DmxSctFilterParams) -> Result<()> {
126        // DMX_SET_FILTER
127        ioctl_write_ptr!(
128            #[inline]
129            ioctl_call,
130            b'o',
131            43,
132            DmxSctFilterParams
133        );
134
135        unsafe { ioctl_call(self.as_raw_fd(), filter as *const _) }.context("DMX: set SCT filter")?;
136
137        Ok(())
138    }
139
140    /// Attempts to set the size of the circular buffer used for filtered data.
141    /// The default size is two maximum sized sections, 
142    /// i.e. if this function is not called a buffer size of 2 * 4096 bytes will be used.
143    pub fn set_buffer_size(&self, size: u32) -> Result<()> {
144        // DMX_SET_BUFFER_SIZE
145        ioctl_write_int_bad!(
146            #[inline]
147            ioctl_call,
148            request_code_none!(b'o', 45)
149        );
150
151        unsafe { ioctl_call(self.as_raw_fd(), size as _) }.context("DMX: set buffer size")?;
152
153        Ok(())
154    }
155
156    /// Attempts to start the actual filtering operation defined via the ioctl calls set_filter or set_pes_filter.
157    pub fn start(&self) -> Result<()> {
158        // DMX_START
159        ioctl_none_bad!(
160            #[inline]
161            ioctl_call,
162            request_code_none!(b'o', 41)
163        );
164
165        unsafe { ioctl_call(self.as_raw_fd()) }.context("DMX: start")?;
166
167        Ok(())
168    }
169
170    /// Attempts to stop the actual filtering operation defined via the ioctl calls set_filter or set_pes_filter and started via start.
171    pub fn stop(&self) -> Result<()> {
172        // DMX_STOP
173        ioctl_none_bad!(
174            #[inline]
175            ioctl_call,
176            request_code_none!(b'o', 42)
177        );
178
179        unsafe { ioctl_call(self.as_raw_fd()) }.context("DMX: stop")?;
180
181        Ok(())
182    }
183}