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
use std::fs;
use std::path::{Path, PathBuf};

/// Returns a list of devices currently known to the system
///
/// # Example
///
/// ```
/// use v4l::context;
/// for dev in context::enum_devices() {
///     print!("{}{}", dev.index(), dev.name().unwrap());
/// }
/// ```
pub fn enum_devices() -> Vec<Node> {
    let mut devices = Vec::new();

    let entries = fs::read_dir("/dev");
    if let Ok(entries) = entries {
        for dentry in entries {
            let dentry = match dentry {
                Ok(dentry) => dentry,
                Err(_) => continue,
            };

            let file_name = dentry.file_name();
            let file_name = file_name.to_str().unwrap();

            if file_name.starts_with("video") {
                let node = Node::new(dentry.path());
                devices.push(node);
            }
        }
    }

    devices
}

/// Represents a video4linux device node
pub struct Node {
    /// Device node path
    path: PathBuf,
}

impl Node {
    /// Returns a device node observer by path
    ///
    /// The device is opened in read only mode.
    ///
    /// # Arguments
    ///
    /// * `path` - Node path (usually a character device or sysfs entry)
    ///
    /// # Example
    ///
    /// ```
    /// use v4l::context::Node;
    /// let node = Node::new("/dev/video0");
    /// ```
    pub fn new<P: AsRef<Path>>(path: P) -> Self {
        Node {
            path: PathBuf::from(path.as_ref()),
        }
    }

    /// Returns the absolute path of the device node
    pub fn path(&self) -> &Path {
        &self.path
    }

    /// Returns the index of the device node
    pub fn index(&self) -> usize {
        let file_name = self.path.file_name().unwrap();

        let mut index_str = String::new();
        for c in file_name.to_str().unwrap().chars() {
            if !c.is_ascii_digit() {
                continue;
            }

            index_str.push(c);
        }

        let index = index_str.parse::<usize>();
        index.unwrap()
    }

    /// Returns name of the device by parsing its sysfs entry
    pub fn name(&self) -> Option<String> {
        let index = self.index();
        let path = format!("{}{}{}", "/sys/class/video4linux/video", index, "/name");
        let name = fs::read_to_string(path);
        match name {
            Ok(name) => Some(name.trim().to_string()),
            Err(_) => None,
        }
    }
}