1 use std::convert::{Infallible, TryFrom};
2 use std::os::unix::io::AsRawFd;
3 
4 use nix::errno::Errno;
5 use thiserror::Error;
6 
7 use crate::bindings;
8 use crate::bindings::v4l2_enc_idx;
9 use crate::bindings::v4l2_encoder_cmd;
10 
11 #[doc(hidden)]
12 mod ioctl {
13     use crate::bindings::v4l2_enc_idx;
14     use crate::bindings::v4l2_encoder_cmd;
15 
16     nix::ioctl_read!(vidioc_g_enc_index, b'V', 76, v4l2_enc_idx);
17     nix::ioctl_readwrite!(vidioc_encoder_cmd, b'V', 77, v4l2_encoder_cmd);
18     nix::ioctl_readwrite!(vidioc_try_encoder_cmd, b'V', 78, v4l2_encoder_cmd);
19 }
20 
21 #[derive(Debug, Error)]
22 pub enum GEncIndexError {
23     #[error("ioctl error: {0}")]
24     IoctlError(Errno),
25 }
26 
27 impl From<GEncIndexError> for Errno {
from(err: GEncIndexError) -> Self28     fn from(err: GEncIndexError) -> Self {
29         match err {
30             GEncIndexError::IoctlError(e) => e,
31         }
32     }
33 }
34 
35 /// Safe wrapper around the `VIDIOC_G_ENC_INDEX` ioctl.
g_enc_index<O: From<v4l2_enc_idx>>(fd: &impl AsRawFd) -> Result<O, GEncIndexError>36 pub fn g_enc_index<O: From<v4l2_enc_idx>>(fd: &impl AsRawFd) -> Result<O, GEncIndexError> {
37     let mut enc_idx: v4l2_enc_idx = Default::default();
38 
39     match unsafe { ioctl::vidioc_g_enc_index(fd.as_raw_fd(), &mut enc_idx) } {
40         Ok(_) => Ok(O::from(enc_idx)),
41         Err(e) => Err(GEncIndexError::IoctlError(e)),
42     }
43 }
44 
45 #[derive(Debug, Clone, Copy)]
46 pub enum EncoderCommand {
47     Start,
48     Stop(bool),
49     Pause,
50     Resume,
51 }
52 
53 #[derive(Debug, Error)]
54 pub enum EncoderCmdError {
55     #[error("error while converting from v4l2_encoder_cmd")]
56     FromV4L2CommandConversionError,
57     #[error("drain already in progress")]
58     DrainInProgress,
59     #[error("command not supported by device")]
60     UnsupportedCommand,
61     #[error("ioctl error: {0}")]
62     IoctlError(Errno),
63 }
64 
65 impl From<EncoderCmdError> for Errno {
from(err: EncoderCmdError) -> Self66     fn from(err: EncoderCmdError) -> Self {
67         match err {
68             EncoderCmdError::FromV4L2CommandConversionError => Errno::EINVAL,
69             EncoderCmdError::DrainInProgress => Errno::EBUSY,
70             EncoderCmdError::UnsupportedCommand => Errno::EINVAL,
71             EncoderCmdError::IoctlError(e) => e,
72         }
73     }
74 }
75 
76 impl From<Errno> for EncoderCmdError {
from(error: Errno) -> Self77     fn from(error: Errno) -> Self {
78         match error {
79             Errno::EBUSY => EncoderCmdError::DrainInProgress,
80             Errno::EINVAL => EncoderCmdError::UnsupportedCommand,
81             e => EncoderCmdError::IoctlError(e),
82         }
83     }
84 }
85 
86 impl From<&EncoderCommand> for v4l2_encoder_cmd {
from(command: &EncoderCommand) -> Self87     fn from(command: &EncoderCommand) -> Self {
88         v4l2_encoder_cmd {
89             cmd: match command {
90                 EncoderCommand::Start => bindings::V4L2_ENC_CMD_START,
91                 EncoderCommand::Stop(_) => bindings::V4L2_ENC_CMD_STOP,
92                 EncoderCommand::Pause => bindings::V4L2_ENC_CMD_PAUSE,
93                 EncoderCommand::Resume => bindings::V4L2_ENC_CMD_RESUME,
94             },
95             flags: match &command {
96                 EncoderCommand::Stop(at_gop) if *at_gop => bindings::V4L2_ENC_CMD_STOP_AT_GOP_END,
97                 _ => 0,
98             },
99             ..Default::default()
100         }
101     }
102 }
103 
104 impl TryFrom<v4l2_encoder_cmd> for () {
105     type Error = Infallible;
106 
try_from(_: v4l2_encoder_cmd) -> Result<Self, Self::Error>107     fn try_from(_: v4l2_encoder_cmd) -> Result<Self, Self::Error> {
108         Ok(())
109     }
110 }
111 
112 /// Safe wrapper around the `VIDIOC_ENCODER_CMD` ioctl.
encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>( fd: &impl AsRawFd, command: I, ) -> Result<O, EncoderCmdError>113 pub fn encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>(
114     fd: &impl AsRawFd,
115     command: I,
116 ) -> Result<O, EncoderCmdError> {
117     let mut enc_cmd = command.into();
118 
119     match unsafe { ioctl::vidioc_encoder_cmd(fd.as_raw_fd(), &mut enc_cmd) } {
120         Ok(_) => Ok(
121             O::try_from(enc_cmd).map_err(|_| EncoderCmdError::FromV4L2CommandConversionError)?
122         ),
123         Err(e) => Err(e.into()),
124     }
125 }
126 
127 /// Safe wrapper around the `VIDIOC_TRY_ENCODER_CMD` ioctl.
try_encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>( fd: &impl AsRawFd, command: I, ) -> Result<O, EncoderCmdError>128 pub fn try_encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>(
129     fd: &impl AsRawFd,
130     command: I,
131 ) -> Result<O, EncoderCmdError> {
132     let mut enc_cmd = command.into();
133 
134     match unsafe { ioctl::vidioc_try_encoder_cmd(fd.as_raw_fd(), &mut enc_cmd) } {
135         Ok(_) => Ok(
136             O::try_from(enc_cmd).map_err(|_| EncoderCmdError::FromV4L2CommandConversionError)?
137         ),
138         Err(e) => Err(e.into()),
139     }
140 }
141