onix 0.1.0

Decode image files using V4L2
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
use crate::ioctl;
use crate::Media;
use bitflags::bitflags;
use core::ffi::CStr;
use core::fmt;
use core::mem;
use nix::{errno::Errno, libc::c_char};
use std::os::fd::AsRawFd;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum EntityFunction {
    // Analog TV IF-PLL entity functions
    IfVidDecoder = 0x2001,
    IfAudDecoder = 0x2002,

    // Audio entity functions
    AudioCapture = 0x3001,
    AudioPlayback = 0x3002,
    AudioMixer = 0x3003,

    // Processing entity functions
    ProcVideoComposer = 0x4001,
    ProcVideoPixelFormatter = 0x4002,
    ProcVideoPixelEncConv = 0x4003,
    ProcVideoLut = 0x4004,
    ProcVideoScaler = 0x4005,
    ProcVideoStatistics = 0x4006,
    ProcVideoEncoder = 0x4007,
    ProcVideoDecoder = 0x4008,
    ProcVideoIsp = 0x4009,

    // Switch and bridge entity functions
    VidMux = 0x5001,
    VidIfBridge = 0x5002,

    // Video decoder/encoder functions
    AtvDecoder = 0x20004,
    DvDecoder = 0x6001,
    DvEncoder = 0x6002,

    V4L = 0x10001,
    CameraSensor = 0x20001,
    Unknown,
}

bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    pub struct EntityFlags: u32 {
        const DEFAULT = 0b001;
        const CONNECTOR = 0b010;
    }
}

#[repr(C)]
pub struct Entity {
    id: u32,
    name: [c_char; 64],
    function: EntityFunction,
    flags: EntityFlags,
    reserved: [u32; 5],
}

impl fmt::Debug for Entity {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Entity")
            .field("id", &self.id)
            .field("name", &self.name())
            .field("function", &self.function)
            .field("flags", &self.flags)
            .finish()
    }
}

impl Entity {
    /// Unique ID for the entity. Do not expect that the ID will always be the same for each
    /// instance of the device. In other words, do not hardcode entity IDs in an application.
    pub fn id(&self) -> u32 {
        self.id
    }

    /// Entity name as an UTF-8 string. This name must be unique within the media topology.
    pub fn name(&self) -> &str {
        // This from_utf8_unchecked() call is safe, as the kernel will only ever give us UTF-8.
        unsafe {
            let c_str = CStr::from_ptr(self.name.as_ptr());
            std::str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Entity main function, see Media entity functions for details.
    pub fn function(&self) -> EntityFunction {
        self.function
    }

    pub fn flags(&self) -> EntityFlags {
        self.flags
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum InterfaceType {
    V4LVideo = 0x200,
    V4LVbi = 0x201,
    V4LRadio = 0x202,
    V4LSubdev = 0x203,
    V4LSwradio = 0x204,
    V4LTouch = 0x205,
    // TODO: DVB and ALSA
}

bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    pub struct InterfaceFlags: u32 {
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct InterfaceDevnode {
    pub major: u32,
    pub minor: u32,
}

#[repr(C)]
union InterfacePayload {
    devnode: InterfaceDevnode,
    raw: [u32; 16],
}

#[repr(C)]
pub struct Interface {
    id: u32,
    intf_type: InterfaceType,
    flags: InterfaceFlags,
    reserved: [u32; 9],
    payload: InterfacePayload,
}

impl fmt::Debug for Interface {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Interface")
            .field("id", &self.id)
            .field("type", &self.intf_type)
            .field("flags", &self.flags)
            .field("payload", &unsafe { self.payload.devnode })
            .finish()
    }
}

impl Interface {
    pub fn id(&self) -> u32 {
        self.id
    }

    pub fn intf_type(&self) -> InterfaceType {
        self.intf_type
    }

    pub fn flags(&self) -> InterfaceFlags {
        self.flags
    }

    pub fn devnode(&self) -> InterfaceDevnode {
        // Safety: devnode is the only possible payload.
        unsafe { self.payload.devnode }
    }
}

bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    #[derive(Default)]
    pub struct PadFlags: u32 {
        /// Input pad, relative to the entity. Input pads sink data and are targets of links.
        const SINK = 0b001;

        /// Output pad, relative to the entity. Output pads source data and are origins of links.
        const SOURCE = 0b010;

        /// If this flag is set and the pad is linked to any other pad, then at least one of those
        /// links must be enabled for the entity to be able to stream. There could be temporary
        /// reasons (e.g. device configuration dependent) for the pad to need enabled links even
        /// when this flag isn’t set; the absence of the flag doesn’t imply there is none.
        const MUST_CONNECT = 0b100;
    }
}

#[repr(C)]
pub struct Pad {
    id: u32,
    entity_id: u32,
    flags: PadFlags,
    index: u32,
    reserved: [u32; 4],
}

impl fmt::Debug for Pad {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Pad")
            .field("id", &self.id)
            .field("entity_id", &self.entity_id)
            .field("flags", &self.flags)
            .field("index", &self.index)
            .finish()
    }
}

impl Pad {
    pub fn id(&self) -> u32 {
        self.id
    }

    pub fn entity_id(&self) -> u32 {
        self.entity_id
    }

    pub fn flags(&self) -> PadFlags {
        self.flags
    }

    pub fn index(&self) -> u32 {
        self.index
    }
}

bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    #[derive(Default)]
    pub struct LinkFlags: u32 {
        /// The link is enabled and can be used to transfer media data. When two or more links
        /// target a sink pad, only one of them can be enabled at a time.
        const ENABLED = 0b001;

        /// The link enabled state can’t be modified at runtime. An immutable link is always
        /// enabled.
        const IMMUTABLE = 0b010;

        /// The link enabled state can be modified during streaming. This flag is set by drivers
        /// and is read-only for applications.
        const DYNAMIC = 0b100;

        /// The link is between two pads.
        const DATA_LINK = 0x00000000;

        /// The link is between an interface and an entity.
        const INTERFACE_LINK = 0x10000000;
    }
}

#[repr(C)]
pub struct Link {
    id: u32,
    source_id: u32,
    sink_id: u32,
    flags: LinkFlags,
    reserved: [u32; 6],
}

impl fmt::Debug for Link {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Link")
            .field("id", &self.id)
            .field("source_id", &self.source_id)
            .field("sink_id", &self.sink_id)
            .field("flags", &self.flags)
            .finish()
    }
}

impl Link {
    pub fn id(&self) -> u32 {
        self.id
    }

    pub fn source_id(&self) -> u32 {
        self.source_id
    }

    pub fn sink_id(&self) -> u32 {
        self.sink_id
    }

    pub fn flags(&self) -> LinkFlags {
        self.flags
    }
}

/// Represents the topology of a media device.
#[derive(Debug, Default)]
#[repr(C)]
pub struct Topology {
    topology_version: u64,

    num_entities: u32,
    reserved1: u32,
    ptr_entities: u64,

    num_interfaces: u32,
    reserved2: u32,
    ptr_interfaces: u64,

    num_pads: u32,
    reserved3: u32,
    ptr_pads: u64,

    num_links: u32,
    reserved4: u32,
    ptr_links: u64,
}

impl Drop for Topology {
    fn drop(&mut self) {
        Self::get_vec::<Entity>(self.ptr_entities, self.num_entities);
        Self::get_vec::<Interface>(self.ptr_interfaces, self.num_interfaces);
        Self::get_vec::<Pad>(self.ptr_pads, self.num_pads);
        Self::get_vec::<Link>(self.ptr_links, self.num_links);
    }
}

impl Topology {
    pub(crate) fn from_media(media: &Media) -> Result<Topology, Errno> {
        // Allocate a struct initialised with zeros.
        let mut data = Topology::default();

        // First we query the number of items we’ll receive.
        unsafe { ioctl::get_topology(media.as_raw_fd(), &mut data) }?;

        // Allocate the Vecs to hold them.
        let mut entities: Vec<Entity> = Vec::with_capacity(data.num_entities as usize);
        let mut interfaces: Vec<Interface> = Vec::with_capacity(data.num_interfaces as usize);
        let mut pads: Vec<Pad> = Vec::with_capacity(data.num_pads as usize);
        let mut links: Vec<Link> = Vec::with_capacity(data.num_links as usize);

        // Put them into the relevant fields in the struct.
        data.ptr_entities = entities.as_mut_ptr() as u64;
        data.ptr_interfaces = interfaces.as_mut_ptr() as u64;
        data.ptr_pads = pads.as_mut_ptr() as u64;
        data.ptr_links = links.as_mut_ptr() as u64;

        unsafe {
            // Then we can call the ioctl again, it will fill the Vecs.
            ioctl::get_topology(media.as_raw_fd(), &mut data)?;

            // Resize the Vecs…
            entities.set_len(data.num_entities as usize);
            interfaces.set_len(data.num_interfaces as usize);
            pads.set_len(data.num_pads as usize);
            links.set_len(data.num_links as usize);
        }

        // And now we can forget them, they’ll get recontructed on access.
        mem::forget(entities);
        mem::forget(interfaces);
        mem::forget(pads);
        mem::forget(links);

        // We’re done!
        Ok(data)
    }

    /// Returns the version of this topology.
    pub fn version(&self) -> u64 {
        self.topology_version
    }

    fn get_vec<T>(ptr: u64, num: u32) -> Vec<T> {
        // Safety: we always allocate the ptrs using Vec, so this is ok.
        unsafe { Vec::from_raw_parts(ptr as *mut T, num as usize, num as usize) }
    }

    fn get_slice<'a, T>(ptr: u64, num: u32) -> &'a [T] {
        // Safety: we always allocate the ptrs using Vec, so this is ok.
        unsafe { std::slice::from_raw_parts(ptr as *const T, num as usize) }
    }

    /// Returns the entities in this media topology.
    pub fn entities(&self) -> &[Entity] {
        Self::get_slice::<Entity>(self.ptr_entities, self.num_entities)
    }

    /// Returns the interfaces in this media topology.
    pub fn interfaces(&self) -> &[Interface] {
        Self::get_slice::<Interface>(self.ptr_interfaces, self.num_interfaces)
    }

    /// Returns the pads in this media topology.
    pub fn pads(&self) -> &[Pad] {
        Self::get_slice::<Pad>(self.ptr_pads, self.num_pads)
    }

    /// Returns the links in this media topology.
    pub fn links(&self) -> &[Link] {
        Self::get_slice::<Link>(self.ptr_links, self.num_links)
    }

    pub fn get_entity(&self, id: u32) -> Option<&Entity> {
        for entity in self.entities() {
            if entity.id == id {
                return Some(entity);
            }
        }
        None
    }

    pub fn get_interface(&self, id: u32) -> Option<&Interface> {
        for interface in self.interfaces() {
            if interface.id == id {
                return Some(interface);
            }
        }
        None
    }

    pub fn get_pad(&self, id: u32) -> Option<&Pad> {
        for pad in self.pads() {
            if pad.id == id {
                return Some(pad);
            }
        }
        None
    }

    pub fn get_pads_for_entity(&self, entity_id: u32) -> Vec<&Pad> {
        let mut pads = Vec::new();
        for pad in self.pads() {
            if pad.entity_id == entity_id {
                pads.push(pad);
            }
        }
        pads
    }

    pub fn get_link_by_source_id(&self, source_id: u32) -> Option<&Link> {
        for link in self.links() {
            if link.source_id == source_id {
                return Some(link);
            }
        }
        None
    }

    pub fn get_link_by_sink_id(&self, sink_id: u32) -> Option<&Link> {
        for link in self.links() {
            if link.sink_id == sink_id {
                return Some(link);
            }
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    macro_rules! assert_size (
        ($t:ty, $sz:expr) => (
            assert_eq!(::std::mem::size_of::<$t>(), $sz);
        );
    );

    #[test]
    fn size() {
        assert_size!(Entity, 96);
        assert_size!(InterfaceDevnode, 8);
        assert_size!(InterfacePayload, 64);
        assert_size!(Interface, 112);
        assert_size!(Pad, 32);
        assert_size!(Link, 40);
        assert_size!(Topology, 72);
    }
}