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}